From 1457a16bd549d9308f91d56793be2968629f4109 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Fri, 25 Jul 2025 03:34:28 -0400 Subject: [PATCH 01/14] Merge up to master --- src/k_kart.c | 18 ++++++++++++------ src/p_spec.c | 4 ++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index ce6d07536..d721d297d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3107,6 +3107,9 @@ fixed_t K_PlayerTripwireSpeedThreshold(const player_t *player) if (specialstageinfo.valid) required_speed = 3 * K_GetKartSpeed(player, false, false) / 2; // 150% + if (modeattacking && !(gametyperules & GTR_CATCHER)) + required_speed = 4 * K_GetKartSpeed(player, false, false); + UINT32 distance = K_GetItemRouletteDistance(player, 8); if (gametype == GT_RACE && M_NotFreePlay() && !modeattacking) @@ -13996,7 +13999,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground) { player->lastringboost = player->ringboost; UINT32 award = 5*player->ringboxaward + 10; - award = 23 * award / 20; // 115% Payout Increase + + if (!modeattacking) + award = 23 * award / 20; // 115% Payout Increase if (!K_ThunderDome()) award = 3 * award / 2; @@ -14052,15 +14057,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground) UINT8 maxtotal = accelPower*9 + weightPower*9; // Scale from base payout at 9/1 to max payout at 1/9. - award = Easing_InCubic(FRACUNIT*total/maxtotal, 13*award/10, 18*award/10); + award = Easing_InCubic(FRACUNIT*total/maxtotal, 10*award/10, 17*award/10); // And, because we don't have to give a damn about sandbagging, up the stakes the longer we progress! if (gametyperules & GTR_CIRCUIT) { - UINT8 maxgrade = 10; - UINT8 margin = min(player->gradingpointnum, maxgrade); - - award = Easing_Linear(FRACUNIT * margin / maxgrade, award, 2*award); + if (K_GetNumGradingPoints()) + award = Easing_Linear(FRACUNIT * player->gradingpointnum / K_GetNumGradingPoints(), award, 3*award/2); } } else @@ -16461,6 +16464,9 @@ UINT16 K_GetEXP(player_t *player) UINT16 exp = FixedRescale(player->gradingfactor, factormin, factormax, Easing_Linear, targetminexp, targetmaxexp)>>FRACBITS; + if (modeattacking) + exp = 100 * player->gradingpointnum / numgradingpoints; + // CONS_Printf("Player %s numgradingpoints=%d gradingpoint=%d targetminexp=%d targetmaxexp=%d factor=%.2f factormin=%.2f factormax=%.2f exp=%d\n", // player_names[player - players], numgradingpoints, player->gradingpointnum, targetminexp, targetmaxexp, FIXED_TO_FLOAT(player->gradingfactor), FIXED_TO_FLOAT(factormin), FIXED_TO_FLOAT(factormax), exp); diff --git a/src/p_spec.c b/src/p_spec.c index 930b374b9..53ec58120 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2048,14 +2048,14 @@ static void K_HandleLapIncrement(player_t *player) tic_t starthaste = starttime - leveltime; // How much time we had left to cross starthaste = TIMEATTACK_START - starthaste; // How much time we wasted before crossing - tic_t leniency = TICRATE*2; // How long we can take to cross with no penalty to amp payout + tic_t leniency = TICRATE*4; // How long we can take to cross with no penalty to amp payout if (starthaste <= leniency) starthaste = 0; else starthaste -= leniency; - fixed_t ampreward = Easing_OutQuart(starthaste*FRACUNIT/TIMEATTACK_START, 100*FRACUNIT, 0); + fixed_t ampreward = Easing_OutQuart(starthaste*FRACUNIT/TIMEATTACK_START, 60*FRACUNIT, 0); K_SpawnAmps(player, ampreward/FRACUNIT, player->mo); // And reset our time to 0. From 6d020fca0612e9f7f6437673c01242750effb7fa Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Fri, 25 Jul 2025 04:43:57 -0400 Subject: [PATCH 02/14] TA rebalance pass one fucktillion and two --- src/k_kart.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index d721d297d..94089d235 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -14044,26 +14044,30 @@ void K_MoveKartPlayer(player_t *player, boolean onground) // To try and help close this gap, we fudge Ring Box payouts to allow weaker characters // better access to things that make them go fast, without changing core handling. + UINT8 speed = player->kartspeed; UINT8 accel = 10-player->kartspeed; UINT8 weight = player->kartweight; // Relative stat power for bonus TA Ring Box awards. // AP 1, WP 2 = weight is worth twice what accel is. // 0 = stat not considered at all! - UINT8 accelPower = 0; + // UINT8 accelPower = 0; + UINT8 speedPower = 1; UINT8 weightPower = 4; - UINT8 total = accelPower*accel + weightPower*weight; - UINT8 maxtotal = accelPower*9 + weightPower*9; + UINT8 total = speedPower*speed + weightPower*weight; + UINT8 maxtotal = speedPower*9 + weightPower*9; + + UINT32 baseaward = award; // Scale from base payout at 9/1 to max payout at 1/9. - award = Easing_InCubic(FRACUNIT*total/maxtotal, 10*award/10, 17*award/10); + award += Easing_InCubic(FRACUNIT*total/maxtotal, 0, 7*baseaward/10); // And, because we don't have to give a damn about sandbagging, up the stakes the longer we progress! if (gametyperules & GTR_CIRCUIT) { if (K_GetNumGradingPoints()) - award = Easing_Linear(FRACUNIT * player->gradingpointnum / K_GetNumGradingPoints(), award, 3*award/2); + award += Easing_Linear(FRACUNIT * player->gradingpointnum / K_GetNumGradingPoints(), 0, baseaward/2); } } else From 0468f1f988647e5072776f91d5caa0edf5e6ed43 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Sat, 26 Jul 2025 15:31:55 -0400 Subject: [PATCH 03/14] Nudge some constants, startline startboost in TA --- src/k_kart.c | 4 ++-- src/p_spec.c | 19 +++++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 94089d235..c8a69d92b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -14053,7 +14053,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) // 0 = stat not considered at all! // UINT8 accelPower = 0; UINT8 speedPower = 1; - UINT8 weightPower = 4; + UINT8 weightPower = 6; UINT8 total = speedPower*speed + weightPower*weight; UINT8 maxtotal = speedPower*9 + weightPower*9; @@ -14061,7 +14061,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) UINT32 baseaward = award; // Scale from base payout at 9/1 to max payout at 1/9. - award += Easing_InCubic(FRACUNIT*total/maxtotal, 0, 7*baseaward/10); + award += Easing_InCubic(FRACUNIT*total/maxtotal, 0, 9*baseaward/10); // And, because we don't have to give a damn about sandbagging, up the stakes the longer we progress! if (gametyperules & GTR_CIRCUIT) diff --git a/src/p_spec.c b/src/p_spec.c index 53ec58120..da154872b 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2055,8 +2055,23 @@ static void K_HandleLapIncrement(player_t *player) else starthaste -= leniency; - fixed_t ampreward = Easing_OutQuart(starthaste*FRACUNIT/TIMEATTACK_START, 60*FRACUNIT, 0); - K_SpawnAmps(player, ampreward/FRACUNIT, player->mo); + // fixed_t ampreward = Easing_OutQuart(starthaste*FRACUNIT/TIMEATTACK_START, 60*FRACUNIT, 0); + // K_SpawnAmps(player, ampreward/FRACUNIT, player->mo); + + UINT8 baseboost = 125; + + player->startboost = Easing_OutQuart(starthaste*FRACUNIT/TIMEATTACK_START, baseboost, 0); + + if (player->startboost == baseboost) + { + K_SpawnDriftBoostExplosion(player, 4); + K_SpawnDriftElectricSparks(player, SKINCOLOR_SILVER, false); + } + else + { + K_SpawnDriftBoostExplosion(player, 3); + // K_SpawnDriftElectricSparks(player, SKINCOLOR_SILVER, false); + } // And reset our time to 0. starttime = leveltime; From 59ae7240f5fc85a9bdf8f20f77ff9f87733a28da Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 29 Jul 2025 19:34:18 -0400 Subject: [PATCH 04/14] Don't consider speed in TA Ring Box bonus --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index c8a69d92b..2a47de426 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -14052,7 +14052,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) // AP 1, WP 2 = weight is worth twice what accel is. // 0 = stat not considered at all! // UINT8 accelPower = 0; - UINT8 speedPower = 1; + UINT8 speedPower = 0; UINT8 weightPower = 6; UINT8 total = speedPower*speed + weightPower*weight; From e6091cc8b4284ebf19eab3df41fadac6bb0cd458 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Sat, 2 Aug 2025 22:36:25 -0400 Subject: [PATCH 05/14] Start condition rework --- src/k_kart.c | 142 ++++++++++++++++++++++++++++++++++++++------------- src/p_spec.c | 46 +---------------- 2 files changed, 109 insertions(+), 79 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 2a47de426..570597437 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -135,7 +135,7 @@ boolean K_DuelItemAlwaysSpawns(mapthing_t *mt) boolean K_InRaceDuel(void) { return ( - inDuel && + inDuel && (gametyperules & GTR_CIRCUIT) && !(mapheaderinfo[gamemap-1]->levelflags & LF_SECTIONRACE) && !specialstageinfo.valid && @@ -345,6 +345,7 @@ void K_TimerInit(void) if (G_TimeAttackStart()) { starttime = TIMEATTACK_START; // Longest permitted start. No half-laps in reverse. + rainbowstartavailable = true; // (Changed on finish line cross later, don't worry.) } @@ -3773,7 +3774,7 @@ static void K_GetKartBoostPower(player_t *player) // This one's a little special: we add extra top speed per tic of ringboost stored up, to allow for Ring Box to really rocket away. // (We compensate when decrementing ringboost to avoid runaway exponential scaling hell.) fixed_t rb = FixedDiv(player->ringboost * FRACUNIT, max(FRACUNIT, K_RingDurationBoost(player))); - fixed_t rp = ((9 - player->kartspeed) + (9 - player->kartweight)) * ((3*FRACUNIT/20)/16); + fixed_t rp = ((9 - player->kartspeed) + (9 - player->kartweight)) * ((3*FRACUNIT/20)/16); ADDBOOST( ringboost_base + FixedMul(FRACUNIT / 1750, rb) + rp, 4*FRACUNIT, @@ -3801,7 +3802,7 @@ static void K_GetKartBoostPower(player_t *player) // Even when not inputting a turn, drift prediction is hard. // Turn solver will sometimes need to slightly turn to stay "aligned". // Award full boost even if turn solver creates a fractional miniturn. - const INT16 inner_deadzone = KART_FULLTURN / 100; + const INT16 inner_deadzone = KART_FULLTURN / 100; INT16 steer_threshold = FixedMul((FRACUNIT * player->kartweight) / 9, max_steer_threshold)>>FRACBITS; @@ -4302,8 +4303,8 @@ void K_SpawnAmps(player_t *player, UINT8 amps, mobj_t *impact) UINT16 scaledamps = min(amps, amps * (10 + (9-player->kartspeed) - (9-player->kartweight)) / 10); // Debug print for scaledamps calculation // CONS_Printf("K_SpawnAmps: player=%s, amps=%d, kartspeed=%d, kartweight=%d, itemdistance=%d, itemdistmult=%0.2f, statscaledamps=%d, distscaledamps=%d\n", - // player_names[player-players], amps, player->kartspeed, player->kartweight, - // itemdistance, FixedToFloat(itemdistmult), + // player_names[player-players], amps, player->kartspeed, player->kartweight, + // itemdistance, FixedToFloat(itemdistmult), // min(amps, amps * (10 + (9-player->kartspeed) - (9-player->kartweight)) / 10), // FixedMul(scaledamps<>FRACBITS); scaledamps = FixedMul(scaledamps<>FRACBITS; @@ -4362,8 +4363,8 @@ void K_AwardPlayerAmps(player_t *player, UINT8 amps) if (oldamps/AMPLEVEL != player->amps/AMPLEVEL) { UINT8 amplevel = player->amps / AMPLEVEL; - static sfxenum_t bwips[7] = {sfx_mbs4c, - sfx_mbs4d, sfx_mbs4e, sfx_mbs4f, sfx_mbs50, + static sfxenum_t bwips[7] = {sfx_mbs4c, + sfx_mbs4d, sfx_mbs4e, sfx_mbs4f, sfx_mbs50, sfx_mbs51, sfx_mbs52}; amplevel = min(amplevel, 6); @@ -4427,7 +4428,7 @@ void K_CheckpointCrossAward(player_t *player) //CONS_Printf("player: %s factor: %.2f exp: %d\n", player_names[player-players], FIXED_TO_FLOAT(player->gradingfactor), player->exp); if (!player->cangrabitems) player->cangrabitems = 1; - + K_AwardPlayerRings(player, (player->bot ? 20 : 10), true); // Update Duel scoring. @@ -6672,7 +6673,7 @@ void K_SpawnFireworkTrail(mobj_t *mo) if (mo->player) dust->color = mo->player->skincolor; - else + else dust->color = mo->color; dust->colorized = true; @@ -9358,7 +9359,7 @@ static void K_UpdateTripwire(player_t *player) else CONS_Printf("airtime: %d, twLen: %d, twAirLen: %d\n", player->airtime, player->tripwireLeniency, player->tripwireAirLeniency); */ - + if (boostExists) { // If player is MOSTLY on the ground. @@ -9549,13 +9550,13 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) for (doubler = 0; doubler < 2; doubler++) { fixed_t heightOffset = player->mo->height + (24*player->mo->scale); - if (P_IsObjectFlipped(player->mo)) + if (P_IsObjectFlipped(player->mo)) { // This counteracts the offset added by K_FlipFromObject so it looks seamless from non-flipped. heightOffset += player->mo->height - FixedMul(player->mo->scale, player->mo->height); heightOffset *= P_MobjFlip(player->mo); // Fleep. } - + mobj_t *debtflag = P_SpawnMobj(player->mo->x + player->mo->momx, player->mo->y + player->mo->momy, player->mo->z + P_GetMobjZMovement(player->mo) + heightOffset, MT_THOK); @@ -9630,6 +9631,54 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_DropItems(player); } + if (G_TimeAttackStart() && !linecrossed && player->speed && leveltime > introtime) + { + linecrossed = leveltime; + /* + if (starttime > leveltime) // Overlong starts shouldn't reset time on cross + { + // Award some Amps for a fast start, to counterbalance Obvious Rainbow Driftboost + + tic_t starthaste = starttime - leveltime; // How much time we had left to cross + starthaste = TIMEATTACK_START - starthaste; // How much time we wasted before crossing + + tic_t leniency = TICRATE*4; // How long we can take to cross with no penalty to amp payout + + if (starthaste <= leniency) + starthaste = 0; + else + starthaste -= leniency; + + // fixed_t ampreward = Easing_OutQuart(starthaste*FRACUNIT/TIMEATTACK_START, 60*FRACUNIT, 0); + // K_SpawnAmps(player, ampreward/FRACUNIT, player->mo); + + UINT8 baseboost = 125; + + player->startboost = Easing_OutQuart(starthaste*FRACUNIT/TIMEATTACK_START, baseboost, 0); + + if (player->startboost == baseboost) + { + K_SpawnDriftBoostExplosion(player, 4); + K_SpawnDriftElectricSparks(player, SKINCOLOR_SILVER, false); + } + else + { + K_SpawnDriftBoostExplosion(player, 3); + // K_SpawnDriftElectricSparks(player, SKINCOLOR_SILVER, false); + } + + // And reset our time to 0. + starttime = leveltime; + } + */ + + starttime = leveltime; + + if (demo.recording) + demo_extradata[player-players] |= DXD_START; + Music_Stop("position"); + } + if (player->transfer) { if (player->fastfall) @@ -9667,7 +9716,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) sub = diff * (sub > 0 ? 1 : -1); player->mo->momz -= sub; } - + } else { @@ -10093,7 +10142,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->invincibilitytimer--; if (player->invincibilitytimer && K_PlayerScamPercentage(player, 1)) player->invincibilitytimer--; - + // Extra tripwire leniency for the end of invincibility if (player->invincibilitytimer <= 0) { player->tripwireLeniency = max( player->tripwireLeniency, TICRATE ); @@ -10104,7 +10153,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) { // freeze the stunned timer while baildrop is active // while retaining the value that was initially set - player->stunned++; + player->stunned++; mobj_t *pmo = player->mo; // particle spawn @@ -10174,7 +10223,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->amps > 0) K_DefensiveOverdrive(player); - P_StartQuakeFromMobj(7, 50 * player->mo->scale, 2048 * player->mo->scale, player->mo); + P_StartQuakeFromMobj(7, 50 * player->mo->scale, 2048 * player->mo->scale, player->mo); player->bailhitlag = false; } @@ -13272,6 +13321,9 @@ static void K_KartSpindashDust(mobj_t *parent) ); flip = P_MobjFlip(dust); + if (G_TimeAttackStart() && leveltime < starttime) + dust->scale = 3 * dust->scale / 2; + dust->momx = FixedMul(hmomentum, FINECOSINE(ang >> ANGLETOFINESHIFT)); dust->momy = FixedMul(hmomentum, FINESINE(ang >> ANGLETOFINESHIFT)); dust->momz = vmomentum * flip; @@ -13334,6 +13386,12 @@ static void K_KartSpindash(player_t *player) { fixed_t thrust = FixedMul(player->mo->scale, min(player->spindash, MAXCHARGETIME)*FRACUNIT/5); + if (G_TimeAttackStart() && leveltime < starttime) + { + thrust *= 2; + player->spindashspeed += FRACUNIT/2; + } + // Old behavior, before emergency zero-ring spindash /* if (gametyperules & GTR_CLOSERPLAYERS) @@ -13467,16 +13525,30 @@ static void K_KartSpindash(player_t *player) { UINT8 ringdropframes = 2 + (player->kartspeed + player->kartweight); boolean spawnOldEffect = true; + boolean normalsound = true; INT16 chargetime = MAXCHARGETIME - ++player->spindash; if (player->rings <= 0 && chargetime >= 0) // Desperation spindash { player->spindash++; + normalsound = false; if (!S_SoundPlaying(player->mo, sfx_kc38)) S_StartSound(player->mo, sfx_kc38); } + if (G_TimeAttackStart() && leveltime < starttime && chargetime >= 0) + { + if (player->spindash == 1) + { + S_ReducedVFXSound(player->mo, sfx_s3kab, player); + S_ReducedVFXSound(player->mo, sfx_s3k9c, player); + } + + normalsound = false; + player->spindash += 4; + } + if (player->spindash >= SPINDASHTHRUSTTIME) { K_KartSpindashDust(player->mo); @@ -13514,7 +13586,7 @@ static void K_KartSpindash(player_t *player) while ((soundcharge += ++add) < chargetime); - if (soundcharge == chargetime) + if (soundcharge == chargetime && normalsound) { if (spawnOldEffect == true) K_SpawnDashDustRelease(player); @@ -13728,7 +13800,7 @@ fixed_t K_PlayerBaseFriction(const player_t *player, fixed_t original) { INT16 myradius = FixedDiv(player->currentwaypoint->mobj->radius, mapobjectscale) / FRACUNIT; INT16 SMALL_WAYPOINT = 450; - + if (myradius < SMALL_WAYPOINT) errorfrict *= 2; } @@ -14039,7 +14111,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) // ...which nullifies a lot of designed advantages for accel types and high-weight racers. // // In addition, it's at Gear 3 Thunderdome speed, which can make it hard for heavies to - // take strong lines without brakedrifting. + // take strong lines without brakedrifting. // // To try and help close this gap, we fudge Ring Box payouts to allow weaker characters // better access to things that make them go fast, without changing core handling. @@ -14063,7 +14135,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) // Scale from base payout at 9/1 to max payout at 1/9. award += Easing_InCubic(FRACUNIT*total/maxtotal, 0, 9*baseaward/10); - // And, because we don't have to give a damn about sandbagging, up the stakes the longer we progress! + // And, because we don't have to give a damn about sandbagging, up the stakes the longer we progress! if (gametyperules & GTR_CIRCUIT) { if (K_GetNumGradingPoints()) @@ -14201,7 +14273,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) else if (!(player->instaWhipCharge >= INSTAWHIP_CHARGETIME && P_PlayerInPain(player))) // Allow reversal whip player->instaWhipCharge = 0; } - + if (player->cmd.buttons & BT_BAIL && (player->cmd.buttons & BT_RESPAWNMASK) != BT_RESPAWNMASK) { if (leveltime < introtime || (gametyperules & GTR_SPHERES)) @@ -14835,7 +14907,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) mobj_t *at1 = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_LIGHTNINGATTACK_VISUAL); mobj_t *at2 = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_LIGHTNINGATTACK_VISUAL); mobj_t *at3 = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_LIGHTNINGATTACK_VISUAL); - + P_SetMobjState(at1, S_THNG); P_SetMobjState(at2, S_THND); P_SetMobjState(at3, S_THNH); @@ -14846,7 +14918,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) P_SetTarget(&at1->target, player->mo); P_SetTarget(&at2->target, player->mo); P_SetTarget(&at3->target, player->mo); - + S_StartSound(player->mo, LIGHTNING_SOUND); } break; @@ -14902,7 +14974,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (player->curshield != KSHIELD_BUBBLE) { mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BUBBLESHIELD); - // MT_BUBBLESHIELD doesn't have MF_NOBLOCKMAP so we need to remove this manually. + // MT_BUBBLESHIELD doesn't have MF_NOBLOCKMAP so we need to remove this manually. // Otherwise if you roll a bubble shield while flipped, the visuals look too mismatched. shield->eflags &= ~MFE_VERTICALFLIP; P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); @@ -15086,14 +15158,14 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) { S_StartSound(player->mo, sfx_gsha7); - if (P_IsObjectOnGround(player->mo)) // facing angle blends w/ momentum angle for game-feel + if (P_IsObjectOnGround(player->mo)) // facing angle blends w/ momentum angle for game-feel { - P_Thrust(player->mo, player->mo->angle, 25*player->mo->scale); - P_Thrust(player->mo, K_MomentumAngle(player->mo), 25*player->mo->scale); + P_Thrust(player->mo, player->mo->angle, 25*player->mo->scale); + P_Thrust(player->mo, K_MomentumAngle(player->mo), 25*player->mo->scale); } else // air version is momentum angle only, reduces cheese, is twice as strong to compensate { - P_Thrust(player->mo, K_MomentumAngle(player->mo), 50*player->mo->scale); + P_Thrust(player->mo, K_MomentumAngle(player->mo), 50*player->mo->scale); } UINT8 numsparks = 8; @@ -16362,7 +16434,7 @@ boolean K_PlayerCanUseItem(player_t *player) return (player->mo->health > 0 && !player->spectator && !P_PlayerInPain(player) && !mapreset && leveltime > introtime); } -// === +// === // THE EXP ZONE // === @@ -16376,7 +16448,7 @@ static boolean K_IsValidOpponent(player_t *me, player_t *them) return false; if (G_SameTeam(me, them)) return false; - + return true; } @@ -16471,7 +16543,7 @@ UINT16 K_GetEXP(player_t *player) if (modeattacking) exp = 100 * player->gradingpointnum / numgradingpoints; - // CONS_Printf("Player %s numgradingpoints=%d gradingpoint=%d targetminexp=%d targetmaxexp=%d factor=%.2f factormin=%.2f factormax=%.2f exp=%d\n", + // CONS_Printf("Player %s numgradingpoints=%d gradingpoint=%d targetminexp=%d targetmaxexp=%d factor=%.2f factormin=%.2f factormax=%.2f exp=%d\n", // player_names[player - players], numgradingpoints, player->gradingpointnum, targetminexp, targetmaxexp, FIXED_TO_FLOAT(player->gradingfactor), FIXED_TO_FLOAT(factormin), FIXED_TO_FLOAT(factormax), exp); return exp; @@ -16485,7 +16557,7 @@ UINT32 K_GetNumGradingPoints(void) return numlaps * (1 + Obj_GetCheckpointCount()); } -// === +// === // END EXP ZONE // === @@ -16504,7 +16576,7 @@ boolean K_IsPickMeUpItem(mobjtype_t type) extern consvar_t cv_debugpickmeup; if (cv_debugpickmeup.value) return false; - + switch (type) { case MT_JAWZ: @@ -16683,7 +16755,7 @@ boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2, boolean allowHostile) if (!K_PickUp(victim->player, inflictor)) return false; - K_AddHitLag(victim, 3, false); + K_AddHitLag(victim, 3, false); P_RemoveMobj(inflictor); return true; @@ -16710,7 +16782,7 @@ fixed_t K_TeamComebackMultiplier(player_t *player) else theirdistance += K_GetItemRouletteDistance(&players[i], players[i].itemRoulette.playing); } - + fixed_t multiplier = FixedDiv(ourdistance, theirdistance); multiplier = min(multiplier, 3*FRACUNIT); multiplier = max(multiplier, FRACUNIT); diff --git a/src/p_spec.c b/src/p_spec.c index da154872b..319cf4d19 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2038,49 +2038,6 @@ static void K_HandleLapIncrement(player_t *player) K_UpdateAllPlayerPositions(); // P_DoPlayerExit calls this } - if (G_TimeAttackStart() && !linecrossed) - { - linecrossed = leveltime; - if (starttime > leveltime) // Overlong starts shouldn't reset time on cross - { - // Award some Amps for a fast start, to counterbalance Obvious Rainbow Driftboost - - tic_t starthaste = starttime - leveltime; // How much time we had left to cross - starthaste = TIMEATTACK_START - starthaste; // How much time we wasted before crossing - - tic_t leniency = TICRATE*4; // How long we can take to cross with no penalty to amp payout - - if (starthaste <= leniency) - starthaste = 0; - else - starthaste -= leniency; - - // fixed_t ampreward = Easing_OutQuart(starthaste*FRACUNIT/TIMEATTACK_START, 60*FRACUNIT, 0); - // K_SpawnAmps(player, ampreward/FRACUNIT, player->mo); - - UINT8 baseboost = 125; - - player->startboost = Easing_OutQuart(starthaste*FRACUNIT/TIMEATTACK_START, baseboost, 0); - - if (player->startboost == baseboost) - { - K_SpawnDriftBoostExplosion(player, 4); - K_SpawnDriftElectricSparks(player, SKINCOLOR_SILVER, false); - } - else - { - K_SpawnDriftBoostExplosion(player, 3); - // K_SpawnDriftElectricSparks(player, SKINCOLOR_SILVER, false); - } - - // And reset our time to 0. - starttime = leveltime; - } - if (demo.recording) - demo_extradata[player-players] |= DXD_START; - Music_Stop("position"); - } - if (rainbowstartavailable == true && player->mo->hitlag == 0) { if (K_InRaceDuel()) @@ -2097,7 +2054,8 @@ static void K_HandleLapIncrement(player_t *player) K_SpawnDriftBoostExplosion(player, 4); K_SpawnDriftElectricSparks(player, SKINCOLOR_SILVER, false); - K_SpawnAmps(player, (K_InRaceDuel()) ? 20 : 35, player->mo); + if (!G_TimeAttackStart()) + K_SpawnAmps(player, (K_InRaceDuel()) ? 20 : 35, player->mo); if (g_teamplay) { From fec835a5444a0cc571c2583e762aca3392658055 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Sun, 3 Aug 2025 17:47:10 -0400 Subject: [PATCH 06/14] Remove DXD_START, add demo header info for attack starts --- src/g_demo.cpp | 38 ++++++++++++++++++++++++++++++++------ src/g_demo.h | 5 +++-- src/k_kart.c | 3 +-- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/g_demo.cpp b/src/g_demo.cpp index 172309016..a769f59dd 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -118,7 +118,7 @@ size_t copy_fixed_buf(void* p, const void* s, size_t n) static char demoname[MAX_WADPATH]; static savebuffer_t demobuf = {0}; -static UINT8 *demotime_p, *demoinfo_p; +static UINT8 *demotime_p, *demoinfo_p, *demoattack_p; static UINT16 demoflags; boolean demosynced = true; // console warning message @@ -1249,6 +1249,9 @@ void G_ConsGhostTic(INT32 playernum) void G_GhostTicker(void) { demoghost *g,*p; + + tic_t fastforward = 0; + for (g = ghosts, p = NULL; g; g = g->next) { UINT16 ziptic; @@ -1258,10 +1261,11 @@ void G_GhostTicker(void) { continue; } - // Pause jhosts that cross until the timer starts. - if (g->linecrossed && leveltime < starttime && G_TimeAttackStart()) + if (g->attackstart != INT32_MAX && leveltime < starttime && leveltime >= g->attackstart && G_TimeAttackStart()) + { continue; + } readghosttic: #define follow g->mo->tracer @@ -1327,8 +1331,6 @@ fadeghost: g->p += g->sizes.skin_name + g->sizes.color_name; if (ziptic & DXD_WEAPONPREF) g->p++; // ditto - if (ziptic & DXD_START) - g->linecrossed = true; } else if (ziptic == DW_RNG) { @@ -1597,8 +1599,17 @@ skippedghosttic: I_Error("Ghost is not a record attack ghost GHOSTEND"); //@TODO lmao don't blow up like this // If the timer started, skip ahead until the ghost starts too. - if (starttime <= leveltime && !g->linecrossed && G_TimeAttackStart()) + if (!fastforward && linecrossed && g->attackstart != INT32_MAX && leveltime < g->attackstart && G_TimeAttackStart()) + { + fastforward = g->attackstart - leveltime; + g->attackstart = INT32_MAX; + } + + if (fastforward) + { + fastforward--; goto readghosttic; + } p = g; #undef follow @@ -2054,6 +2065,10 @@ void G_BeginRecording(void) demoinfo_p = demobuf.p; WRITEUINT32(demobuf.p, 0); + // If special attack-start timing applies, we need to know where to skip the ghost to + demoattack_p = demobuf.p; + WRITEUINT32(demobuf.p, INT32_MAX); + // Save netvar data CV_SaveDemoVars(&demobuf.p); @@ -2219,6 +2234,11 @@ void srb2::write_current_demo_end_marker() *(UINT32 *)demoinfo_p = demobuf.p - demobuf.buffer; } +void G_SetDemoAttackTiming(tic_t time) +{ + *(UINT32 *)demoattack_p = time; +} + void G_SetDemoTime(UINT32 ptime, UINT32 plap) { if (!demo.recording || !demotime_p) @@ -2573,6 +2593,7 @@ void G_LoadDemoInfo(menudemo_t *pdemo, boolean allownonmultiplayer) } extrainfo_p = info.buffer + READUINT32(info.p); // The extra UINT32 read is for a blank 4 bytes? + info.p += 4; // attack start // Pared down version of CV_LoadNetVars to find the kart speed pdemo->kartspeed = KARTSPEED_NORMAL; // Default to normal speed @@ -3072,6 +3093,7 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) } demobuf.p += 4; // Extrainfo location + demobuf.p += 4; // Attack start // ...*map* not loaded? if (!gamemap || (gamemap > nummapheaders) || !mapheaderinfo[gamemap-1] || mapheaderinfo[gamemap-1]->lumpnum == LUMPERROR) @@ -3487,6 +3509,7 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) } p += 4; // Extra data location reference + UINT32 attackstart = READUINT32(p); // net var data count = READUINT16(p); @@ -3578,6 +3601,8 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) gh->numskins = worknumskins; gh->skinlist = skinlist; + gh->attackstart = attackstart; + ghosts = gh; gh->version = ghostversion; @@ -3729,6 +3754,7 @@ staffbrief_t *G_GetStaffGhostBrief(UINT8 *buffer) } p += 4; // Extrainfo location marker + p += 4; // Attack start info // Ehhhh don't need ghostversion here (?) so I'll reuse the var here ghostversion = READUINT16(p); diff --git a/src/g_demo.h b/src/g_demo.h index 4ce05c2a3..39292d54e 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -159,7 +159,6 @@ extern UINT8 demo_writerng; #define DXD_NAME 0x08 // name changed #define DXD_COLOR 0x10 // color changed #define DXD_FOLLOWER 0x20 // follower was changed -#define DXD_START 0x40 // Crossed the line in TA #define DXD_ADDPLAYER (DXD_JOINDATA|DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER) @@ -203,7 +202,7 @@ struct demoghost { UINT8 fadein; UINT16 version; UINT8 numskins; - boolean linecrossed; + tic_t attackstart; boolean done; democharlist_t *skinlist; mobj_t oldmo, *mo; @@ -238,6 +237,8 @@ void G_DeferedPlayDemo(const char *demo); void G_SaveDemo(void); void G_ResetDemoRecording(void); +void G_SetDemoAttackTiming(tic_t time); + boolean G_CheckDemoTitleEntry(void); typedef enum diff --git a/src/k_kart.c b/src/k_kart.c index 570597437..ba26f506d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9673,9 +9673,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) */ starttime = leveltime; + G_SetDemoAttackTiming(leveltime); - if (demo.recording) - demo_extradata[player-players] |= DXD_START; Music_Stop("position"); } From 257384b42aebe51ccbeaa9d5626eb80e51b55877 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Mon, 4 Aug 2025 23:02:31 -0400 Subject: [PATCH 07/14] ONCE MORE TA rebalance --- src/k_kart.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index ba26f506d..dc8eec7f7 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -13388,7 +13388,7 @@ static void K_KartSpindash(player_t *player) if (G_TimeAttackStart() && leveltime < starttime) { thrust *= 2; - player->spindashspeed += FRACUNIT/2; + // player->spindashspeed += FRACUNIT/2; } // Old behavior, before emergency zero-ring spindash @@ -14122,17 +14122,16 @@ void K_MoveKartPlayer(player_t *player, boolean onground) // Relative stat power for bonus TA Ring Box awards. // AP 1, WP 2 = weight is worth twice what accel is. // 0 = stat not considered at all! - // UINT8 accelPower = 0; - UINT8 speedPower = 0; + UINT8 accelPower = 1; UINT8 weightPower = 6; - UINT8 total = speedPower*speed + weightPower*weight; - UINT8 maxtotal = speedPower*9 + weightPower*9; + UINT8 total = accelPower*accel + weightPower*weight; + UINT8 maxtotal = accelPower*9 + weightPower*9; UINT32 baseaward = award; // Scale from base payout at 9/1 to max payout at 1/9. - award += Easing_InCubic(FRACUNIT*total/maxtotal, 0, 9*baseaward/10); + award += Easing_Linear(FRACUNIT*total/maxtotal, 0, 11*baseaward/10); // And, because we don't have to give a damn about sandbagging, up the stakes the longer we progress! if (gametyperules & GTR_CIRCUIT) From 49c53182614cd7a81064adbabd2baf462c79654b Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 5 Aug 2025 21:16:12 -0400 Subject: [PATCH 08/14] Don't set demo attack timing in playback --- src/g_demo.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/g_demo.cpp b/src/g_demo.cpp index a769f59dd..89c35fc0e 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -2236,7 +2236,8 @@ void srb2::write_current_demo_end_marker() void G_SetDemoAttackTiming(tic_t time) { - *(UINT32 *)demoattack_p = time; + if (!demo.playback) + *(UINT32 *)demoattack_p = time; } void G_SetDemoTime(UINT32 ptime, UINT32 plap) From bf8511c495a41ba90fa0055e4d3667f468077edc Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Wed, 6 Aug 2025 16:48:20 -0400 Subject: [PATCH 09/14] Review fixup --- src/doomstat.h | 2 +- src/g_demo.cpp | 5 ++--- src/g_game.c | 4 ++-- src/k_kart.c | 6 +++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/doomstat.h b/src/doomstat.h index 9a8efea18..e585a5db3 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -884,7 +884,7 @@ extern boolean thwompsactive; extern UINT8 lastLowestLap; extern SINT8 spbplace; extern boolean rainbowstartavailable; -extern tic_t linecrossed; +extern tic_t attacktimingstarted; extern boolean inDuel; extern UINT8 overtimecheckpoints; diff --git a/src/g_demo.cpp b/src/g_demo.cpp index 89c35fc0e..7a3d91004 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -1250,12 +1250,11 @@ void G_GhostTicker(void) { demoghost *g,*p; - tic_t fastforward = 0; - for (g = ghosts, p = NULL; g; g = g->next) { UINT16 ziptic; UINT8 xziptic; + tic_t fastforward = 0; if (g->done) { @@ -1599,7 +1598,7 @@ skippedghosttic: I_Error("Ghost is not a record attack ghost GHOSTEND"); //@TODO lmao don't blow up like this // If the timer started, skip ahead until the ghost starts too. - if (!fastforward && linecrossed && g->attackstart != INT32_MAX && leveltime < g->attackstart && G_TimeAttackStart()) + if (!fastforward && attacktimingstarted && g->attackstart != INT32_MAX && leveltime < g->attackstart && G_TimeAttackStart()) { fastforward = g->attackstart - leveltime; g->attackstart = INT32_MAX; diff --git a/src/g_game.c b/src/g_game.c index e59199a08..ef2cb3218 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -314,7 +314,7 @@ boolean thwompsactive; // Thwomps activate on lap 2 UINT8 lastLowestLap; // Last lowest lap, for activating race lap executors SINT8 spbplace; // SPB exists, give the person behind better items boolean rainbowstartavailable; // Boolean, keeps track of if the rainbow start was gotten -tic_t linecrossed; // For Time Attack +tic_t attacktimingstarted; // For Time Attack boolean inDuel; // Boolean, keeps track of if it is a 1v1 UINT8 overtimecheckpoints; // Duel overtime speedups! @@ -5130,7 +5130,7 @@ void G_EndGame(void) return; } - if (gametype == GT_TUTORIAL && M_GameAboutToStart() && restoreMenu == NULL) + if (gametype == GT_TUTORIAL && M_GameAboutToStart() && restoreMenu == NULL) { // Playground Hack F_StartIntro(); diff --git a/src/k_kart.c b/src/k_kart.c index dc8eec7f7..bbabb5051 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -192,7 +192,7 @@ void K_TimerReset(void) memset(&g_musicfade, 0, sizeof g_musicfade); numbulbs = 1; inDuel = rainbowstartavailable = false; - linecrossed = 0; + attacktimingstarted = 0; overtimecheckpoints = 0; timelimitintics = extratimeintics = secretextratime = 0; g_pointlimit = 0; @@ -9631,9 +9631,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_DropItems(player); } - if (G_TimeAttackStart() && !linecrossed && player->speed && leveltime > introtime) + if (G_TimeAttackStart() && !attacktimingstarted && player->speed && leveltime > introtime) { - linecrossed = leveltime; + attacktimingstarted = leveltime; /* if (starttime > leveltime) // Overlong starts shouldn't reset time on cross { From a11acfaf0ea02606260195b09d928bc7cf4652da Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Wed, 6 Aug 2025 19:51:36 -0400 Subject: [PATCH 10/14] WIP: splits --- src/d_player.h | 7 +++++ src/g_demo.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++++--- src/g_demo.h | 4 +++ src/k_hud.cpp | 75 ++++++++++++++++++++++++++++++++++++++------------ src/k_kart.c | 12 ++++++++ 5 files changed, 151 insertions(+), 22 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index e73ff2e12..d9bc6d6b3 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -350,6 +350,13 @@ typedef enum khud_exp, khud_exptimer, + // Splits + khud_splittime, + khud_splitwin, + khud_splittimer, + khud_splitskin, + khud_splitcolor, + NUMKARTHUD } karthudtype_t; diff --git a/src/g_demo.cpp b/src/g_demo.cpp index 7a3d91004..71ef9ca38 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -118,7 +118,7 @@ size_t copy_fixed_buf(void* p, const void* s, size_t n) static char demoname[MAX_WADPATH]; static savebuffer_t demobuf = {0}; -static UINT8 *demotime_p, *demoinfo_p, *demoattack_p; +static UINT8 *demotime_p, *demoinfo_p, *demoattack_p, *demosplits_p; static UINT16 demoflags; boolean demosynced = true; // console warning message @@ -2068,6 +2068,13 @@ void G_BeginRecording(void) demoattack_p = demobuf.p; WRITEUINT32(demobuf.p, INT32_MAX); + demosplits_p = demobuf.p; + for (i = 0; i < MAXSPLITS; i++) + { + WRITEUINT32(demobuf.p, INT32_MAX); + } + + // Save netvar data CV_SaveDemoVars(&demobuf.p); @@ -2235,8 +2242,57 @@ void srb2::write_current_demo_end_marker() void G_SetDemoAttackTiming(tic_t time) { - if (!demo.playback) - *(UINT32 *)demoattack_p = time; + if (demo.playback) + return; + + *(UINT32 *)demoattack_p = time; +} + +void G_SetDemoCheckpointTiming(player_t *player, tic_t time, UINT8 checkpoint) +{ + if (demo.playback) + return; + if (checkpoint >= MAXSPLITS || checkpoint < 0) + return; + + UINT32 *splits = (UINT32 *)demosplits_p; + splits[checkpoint] = time; + + CONS_Printf("%d / %d\n", checkpoint, time); + + demoghost *g; + tic_t lowest = INT32_MAX; + UINT32 lowestskin = ((skin_t*)player->mo->skin) - skins; + UINT32 lowestcolor = player->skincolor; + for (g = ghosts; g; g = g->next) + { + if (lowest > g->splits[checkpoint]) + { + lowest = g->splits[checkpoint]; + lowestskin = ((skin_t*)g->mo->skin)-skins; + lowestcolor = g->mo->color; + + } + CONS_Printf("->%d\n", g->splits[checkpoint]); + } + + if (lowest != INT32_MAX) + { + player->karthud[khud_splittimer] = 1; + player->karthud[khud_splitskin] = lowestskin; + player->karthud[khud_splitcolor] = lowestcolor; + + if (lowest < time) + { + player->karthud[khud_splittime] = time - lowest; + player->karthud[khud_splitwin] = false; + } + else + { + player->karthud[khud_splittime] = lowest - time; + player->karthud[khud_splitwin] = true; + } + } } void G_SetDemoTime(UINT32 ptime, UINT32 plap) @@ -2594,6 +2650,8 @@ void G_LoadDemoInfo(menudemo_t *pdemo, boolean allownonmultiplayer) extrainfo_p = info.buffer + READUINT32(info.p); // The extra UINT32 read is for a blank 4 bytes? info.p += 4; // attack start + for (i = 0; i < MAXSPLITS; i++) + info.p += 4; // splits // Pared down version of CV_LoadNetVars to find the kart speed pdemo->kartspeed = KARTSPEED_NORMAL; // Default to normal speed @@ -3509,7 +3567,13 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) } p += 4; // Extra data location reference - UINT32 attackstart = READUINT32(p); + tic_t attackstart = READUINT32(p); + + UINT8 *splits = p; + for (i = 0; i < MAXSPLITS; i++) + { + p += 4; + } // net var data count = READUINT16(p); @@ -3602,6 +3666,7 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) gh->skinlist = skinlist; gh->attackstart = attackstart; + std::memcpy(gh->splits, splits, sizeof(tic_t) * MAXSPLITS); ghosts = gh; @@ -3755,6 +3820,8 @@ staffbrief_t *G_GetStaffGhostBrief(UINT8 *buffer) p += 4; // Extrainfo location marker p += 4; // Attack start info + for (i = 0; i < MAXSPLITS; i++) + p += 4; // splits // Ehhhh don't need ghostversion here (?) so I'll reuse the var here ghostversion = READUINT16(p); diff --git a/src/g_demo.h b/src/g_demo.h index 39292d54e..ab0bc4e68 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -168,6 +168,8 @@ extern UINT8 demo_writerng; #define DXD_PST_SPECTATING 0x02 #define DXD_PST_LEFT 0x03 +#define MAXSPLITS (200) + boolean G_CompatLevel(UINT16 level); // Record/playback tics @@ -203,6 +205,7 @@ struct demoghost { UINT16 version; UINT8 numskins; tic_t attackstart; + tic_t splits[MAXSPLITS]; boolean done; democharlist_t *skinlist; mobj_t oldmo, *mo; @@ -238,6 +241,7 @@ void G_SaveDemo(void); void G_ResetDemoRecording(void); void G_SetDemoAttackTiming(tic_t time); +void G_SetDemoCheckpointTiming(player_t *player, tic_t time, UINT8 checkpoint); boolean G_CheckDemoTitleEntry(void); diff --git a/src/k_hud.cpp b/src/k_hud.cpp index c3e444930..688b266bf 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -525,7 +525,7 @@ void K_LoadKartHUDGraphics(void) buffer[7] = '0'+((i) % 10); HU_UpdatePatch(&kp_overdrive[0][i], "%s", buffer); } - + sprintf(buffer, "bsOVRDxx"); for (i = 0; i < 32; i++) { @@ -2000,7 +2000,7 @@ static void K_drawKartItem(void) static void K_drawBackupItem(void) { bool tiny = r_splitscreen > 1; - patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw }; + patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw }; patch_t *localbg = (kp_itembg[2]); patch_t *localinv = kp_invincibility[((leveltime % (6*3)) / 3) + 7 + tiny]; INT32 fx = 0, fy = 0, fflags = 0, tx = 0, ty = 0; // final coords for hud and flags... @@ -3406,7 +3406,7 @@ static void K_drawKartDuelScores(void) { flags |= V_SNAPTOBOTTOM; flags &= ~V_SNAPTOTOP; - basey = BASEVIDHEIGHT - 40; + basey = BASEVIDHEIGHT - 40; } basex = BASEVIDWIDTH - 80; } @@ -3493,7 +3493,7 @@ static void K_drawKartDuelScores(void) V_DrawScaledPatch(basex-barheight+foeheight, basey, flags, kp_duel_4over); else V_DrawScaledPatch(basex, basey-barheight+foeheight, flags, kp_duel_over); - + if (!use4p) { V_DrawScaledPatch(basex, basey, flags, kp_duel_foe); @@ -3512,7 +3512,7 @@ static void K_drawKartDuelScores(void) } foenum.text("{}", foe->duelscore); - younum.text("{}", stplyr->duelscore); + younum.text("{}", stplyr->duelscore); // minirankings shamelessly copypasted because i know that shit works already // and SURELY we will never need to use this somewhere else, right? @@ -3526,7 +3526,7 @@ static void K_drawKartDuelScores(void) UINT8 drawme = draw ? (stplyr - players) : (foe - players); UINT16 drawx = basex + (draw ? youx : foex); UINT16 drawy = basey + (draw ? youy : foey); - + if (!playeringame[drawme] || players[drawme].spectator) continue; @@ -3790,8 +3790,8 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) if (R_GetViewNumber() == 1) { flags |= V_SNAPTOBOTTOM; - flags &= ~V_SNAPTOTOP; - basey = 170; + flags &= ~V_SNAPTOTOP; + basey = 170; } } } @@ -3817,7 +3817,7 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) return; using srb2::Draw; - srb2::Draw::Font scorefont = Draw::Font::kTimer; + srb2::Draw::Font scorefont = Draw::Font::kTimer; if (totalscore > 99) { @@ -3834,7 +3834,7 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) else { scorefont = Draw::Font::kZVote; - } + } } UINT32 youscore = stplyr->teamimportance; @@ -3870,7 +3870,7 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) if (teams_lastleveltime[vn] != leveltime) // Timing consistency { INT32 delta = abs(easedallyscore[vn] - allyscore); // how wrong is display score? - + if (scorechangecooldown[vn] == 0 && delta) { if (allyscore > easedallyscore[vn]) @@ -3886,9 +3886,9 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) enemycolor = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_WHITE, GTC_CACHE); } scorechangecooldown[vn] = TICRATE/delta; - } + } } - + if (!fromintermission) { // replace scores with eased scores @@ -4009,7 +4009,7 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) if (totalscore > 99) { enemynum.text("{:03}", enemyscore); - allynum.text("{:03}", allyscore); + allynum.text("{:03}", allyscore); } else { @@ -5856,13 +5856,13 @@ INT32 K_GetMinimapSplitFlags(const boolean usingProgressBar) #define ICON_DOT_RADIUS (10) // modified pick from blondedradio/RadioRacers (but there are like 57 things we don't want in the commit) -// (so gogo gadget copypaste, thanks for a good feature and saving me work i was supposed to do anyway) +// (so gogo gadget copypaste, thanks for a good feature and saving me work i was supposed to do anyway) static void K_DrawKartUFOTimer(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags) { fixed_t amnumxpos, amnumypos; INT32 amxpos, amypos; - if (exitcountdown || leveltime > g_battleufo.due || battleprisons) + if (exitcountdown || leveltime > g_battleufo.due || battleprisons) return; tic_t raw = g_battleufo.due - leveltime; @@ -5872,7 +5872,7 @@ static void K_DrawKartUFOTimer(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hud { flags |= (raw / (TICRATE/2) % 2) ? V_YELLOWMAP : 0; } - + if (countdown <= 10) { flags &= ~(V_HUDTRANS|V_HUDTRANSHALF); @@ -7758,9 +7758,48 @@ void K_drawKartHUD(void) using srb2::Draw; Draw::TextElement text = Draw::TextElement().parse(" Restart"); Draw(BASEVIDWIDTH - 19, 2) - .flags(flags | V_YELLOWMAP) + .flags(flags | V_ORANGEMAP) .align(Draw::Align::kRight) .text(text.string()); + + boolean debug_alwaysdraw = false; + + if (stplyr->karthud[khud_splittimer] || debug_alwaysdraw) + { + tic_t split = stplyr->karthud[khud_splittime]; + UINT32 skin = stplyr->karthud[khud_splitskin]; + UINT32 color = stplyr->karthud[khud_splitcolor]; + boolean ahead = stplyr->karthud[khud_splitwin]; + + // debug + if (!stplyr->karthud[khud_splittimer]) + { + ahead = !!((leveltime/17)%2); + split = leveltime; + skin = stplyr->skin; + color = stplyr->skincolor; + } + + UINT8 *skincolor = R_GetTranslationColormap(skin, static_cast(color), GTC_CACHE); + + UINT8 textcolor = ahead ? SKINCOLOR_SAPPHIRE : SKINCOLOR_KETCHUP; + + Draw row = Draw(BASEVIDWIDTH/2, BASEVIDHEIGHT/4).align(Draw::Align::kCenter) + .font(Draw::Font::kThinTimer).flags(V_30TRANS); + + // vibes offset + row.x(-32).colormap(skincolor).patch(R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap); + + Draw::TextElement text = Draw::TextElement( + std::string(ahead ? "-" : "+") + "{:02}'{:02}\"{:02}", + G_TicsToMinutes(split, true), + G_TicsToSeconds(split), + G_TicsToCentiseconds(split) + ); + + // vibes offset TWO + row.colormap(textcolor).colorize(textcolor).x(8).text(text); + } } else { diff --git a/src/k_kart.c b/src/k_kart.c index bbabb5051..a3996cfd2 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4422,6 +4422,11 @@ void K_CheckpointCrossAward(player_t *player) if (gametype != GT_RACE) return; + if (!demo.playback && G_TimeAttackStart()) + { + G_SetDemoCheckpointTiming(player, leveltime, player->gradingpointnum); + } + player->gradingfactor += K_GetGradingFactorAdjustment(player); player->gradingpointnum++; player->exp = K_GetEXP(player); @@ -9039,6 +9044,13 @@ void K_KartPlayerHUDUpdate(player_t *player) if (player->karthud[khud_trickcool]) player->karthud[khud_trickcool]--; + if (player->karthud[khud_splittimer]) + { + player->karthud[khud_splittimer]++; + if (player->karthud[khud_splittimer] > 2*TICRATE) + player->karthud[khud_splittimer] = 0; + } + if (player->positiondelay) player->positiondelay--; From 024ae0f8abe898eec4092ab3471b5b54232e916a Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Wed, 6 Aug 2025 22:09:06 -0400 Subject: [PATCH 11/14] More splits --- src/d_player.h | 1 + src/g_demo.cpp | 25 +++++++++++++++++-------- src/k_hud.cpp | 47 +++++++++++++++++++++++++++++++++++++---------- src/k_kart.c | 6 ++---- 4 files changed, 57 insertions(+), 22 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index d9bc6d6b3..b1faf5ee9 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -356,6 +356,7 @@ typedef enum khud_splittimer, khud_splitskin, khud_splitcolor, + khud_splitlast, NUMKARTHUD } karthudtype_t; diff --git a/src/g_demo.cpp b/src/g_demo.cpp index 71ef9ca38..d90e357b5 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -2258,8 +2258,6 @@ void G_SetDemoCheckpointTiming(player_t *player, tic_t time, UINT8 checkpoint) UINT32 *splits = (UINT32 *)demosplits_p; splits[checkpoint] = time; - CONS_Printf("%d / %d\n", checkpoint, time); - demoghost *g; tic_t lowest = INT32_MAX; UINT32 lowestskin = ((skin_t*)player->mo->skin) - skins; @@ -2273,25 +2271,36 @@ void G_SetDemoCheckpointTiming(player_t *player, tic_t time, UINT8 checkpoint) lowestcolor = g->mo->color; } - CONS_Printf("->%d\n", g->splits[checkpoint]); } if (lowest != INT32_MAX) { - player->karthud[khud_splittimer] = 1; + player->karthud[khud_splittimer] = 3*TICRATE; player->karthud[khud_splitskin] = lowestskin; player->karthud[khud_splitcolor] = lowestcolor; + player->karthud[khud_splittime] = (INT32)time - (INT32)lowest; if (lowest < time) { - player->karthud[khud_splittime] = time - lowest; - player->karthud[khud_splitwin] = false; + player->karthud[khud_splitwin] = -2; // behind and losing } else { - player->karthud[khud_splittime] = lowest - time; - player->karthud[khud_splitwin] = true; + player->karthud[khud_splitwin] = 2; // ahead and gaining } + + INT32 last = player->karthud[khud_splitlast]; + INT32 now = player->karthud[khud_splittime]; + + if (checkpoint != 0) + { + if (player->karthud[khud_splitwin] > 0 && now > last) + player->karthud[khud_splitwin] = 1; // ahead but losing + else if (player->karthud[khud_splitwin] < 0 && now < last) + player->karthud[khud_splitwin] = -1; // behind but gaining + } + + player->karthud[khud_splitlast] = player->karthud[khud_splittime]; } } diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 688b266bf..6306a64d2 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -7764,41 +7764,68 @@ void K_drawKartHUD(void) boolean debug_alwaysdraw = false; - if (stplyr->karthud[khud_splittimer] || debug_alwaysdraw) + if ( + ( + !stplyr->karthud[khud_lapanimation] && + stplyr->karthud[khud_splittimer] && + (stplyr->karthud[khud_splittimer] > TICRATE/3 || stplyr->karthud[khud_splittimer]%2 || cv_reducevfx.value) + ) + || debug_alwaysdraw + ) { - tic_t split = stplyr->karthud[khud_splittime]; - UINT32 skin = stplyr->karthud[khud_splitskin]; - UINT32 color = stplyr->karthud[khud_splitcolor]; - boolean ahead = stplyr->karthud[khud_splitwin]; + INT32 split = stplyr->karthud[khud_splittime]; + INT32 skin = stplyr->karthud[khud_splitskin]; + INT32 color = stplyr->karthud[khud_splitcolor]; + INT32 ahead = stplyr->karthud[khud_splitwin]; // debug if (!stplyr->karthud[khud_splittimer]) { - ahead = !!((leveltime/17)%2); + ahead = ((leveltime/17)%5) - 2; split = leveltime; skin = stplyr->skin; color = stplyr->skincolor; } + split = std::abs(split); + UINT8 *skincolor = R_GetTranslationColormap(skin, static_cast(color), GTC_CACHE); - UINT8 textcolor = ahead ? SKINCOLOR_SAPPHIRE : SKINCOLOR_KETCHUP; + UINT8 textcolor = SKINCOLOR_WHITE; + switch (ahead) + { + case 2: + textcolor = SKINCOLOR_SAPPHIRE; // leading and gaining + break; + case 1: + textcolor = SKINCOLOR_PIGEON; // leading and losing + break; + case -1: + textcolor = SKINCOLOR_RUBY; // trailing and gaining + break; + case -2: + textcolor = SKINCOLOR_CRIMSON; // trailing and losing + break; + } + Draw row = Draw(BASEVIDWIDTH/2, BASEVIDHEIGHT/4).align(Draw::Align::kCenter) .font(Draw::Font::kThinTimer).flags(V_30TRANS); + std::string arrow = (ahead == 1 || ahead == -2) ? "(" : ")"; + // vibes offset - row.x(-32).colormap(skincolor).patch(R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap); + row.x(-35).colormap(skincolor).patch(R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap); Draw::TextElement text = Draw::TextElement( - std::string(ahead ? "-" : "+") + "{:02}'{:02}\"{:02}", + std::string(ahead >= 0 ? "-" : "+") + " " + "{:02}'{:02}\"{:02} " + arrow, G_TicsToMinutes(split, true), G_TicsToSeconds(split), G_TicsToCentiseconds(split) ); // vibes offset TWO - row.colormap(textcolor).colorize(textcolor).x(8).text(text); + row.colormap(textcolor).colorize(textcolor).x(15).text(text); } } else diff --git a/src/k_kart.c b/src/k_kart.c index a3996cfd2..e81ba2471 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9044,11 +9044,9 @@ void K_KartPlayerHUDUpdate(player_t *player) if (player->karthud[khud_trickcool]) player->karthud[khud_trickcool]--; - if (player->karthud[khud_splittimer]) + if (player->karthud[khud_splittimer] && !player->karthud[khud_lapanimation]) { - player->karthud[khud_splittimer]++; - if (player->karthud[khud_splittimer] > 2*TICRATE) - player->karthud[khud_splittimer] = 0; + player->karthud[khud_splittimer]--; } if (player->positiondelay) From 5703fb2a0b5d46632eb9ff90e93d88b51654486f Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Thu, 7 Aug 2025 01:13:15 -0400 Subject: [PATCH 12/14] Adjust for start time when writing TA splits --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index e81ba2471..bee7eb759 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4424,7 +4424,7 @@ void K_CheckpointCrossAward(player_t *player) if (!demo.playback && G_TimeAttackStart()) { - G_SetDemoCheckpointTiming(player, leveltime, player->gradingpointnum); + G_SetDemoCheckpointTiming(player, leveltime - starttime, player->gradingpointnum); } player->gradingfactor += K_GetGradingFactorAdjustment(player); From 848c3d4f7a89b9dba15a71419eb1496a908cb067 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Thu, 7 Aug 2025 04:53:24 -0400 Subject: [PATCH 13/14] Read splits in G_DoPlayDemoEx (crash fix) --- src/g_demo.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/g_demo.cpp b/src/g_demo.cpp index d90e357b5..92223887e 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -3161,6 +3161,10 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) demobuf.p += 4; // Extrainfo location demobuf.p += 4; // Attack start + for (i = 0; i < MAXSPLITS; i++) + { + demobuf.p += 4; // Splits + } // ...*map* not loaded? if (!gamemap || (gamemap > nummapheaders) || !mapheaderinfo[gamemap-1] || mapheaderinfo[gamemap-1]->lumpnum == LUMPERROR) From 565207ec080c493b9b0cd2f0d2a248777793c9a4 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Thu, 7 Aug 2025 14:54:24 -0400 Subject: [PATCH 14/14] MAXSPLITS 200 to 32 --- src/g_demo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_demo.h b/src/g_demo.h index ab0bc4e68..61a6a17fc 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -168,7 +168,7 @@ extern UINT8 demo_writerng; #define DXD_PST_SPECTATING 0x02 #define DXD_PST_LEFT 0x03 -#define MAXSPLITS (200) +#define MAXSPLITS (32) boolean G_CompatLevel(UINT16 level);