diff --git a/src/d_clisrv.c b/src/d_clisrv.c index fac79bcab..d59435aa3 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3738,8 +3738,6 @@ static void Got_AddPlayer(const UINT8 **p, INT32 playernum) CONS_Debug(DBG_NETPLAY, "addplayer: %d %d\n", node, newplayernum); - //G_SpectatePlayerOnJoin(newplayernum); -- caused desyncs in this spot :( - if (newplayernum+1 > doomcom->numslots) doomcom->numslots = (INT16)(newplayernum+1); @@ -3793,6 +3791,13 @@ static void Got_AddPlayer(const UINT8 **p, INT32 playernum) players[newplayernum].splitscreenindex = splitscreenplayer; players[newplayernum].bot = false; + // Previously called at the top of this function, commented as + // "caused desyncs in this spot :(". But we can't do this in + // G_PlayerReborn, since that only runs for level contexts and + // allows people to party-crash the vote screen even when + // maxplayers is too low for them. Let's try it here...? + G_SpectatePlayerOnJoin(newplayernum); + if (node == mynode && splitscreenplayer == 0) S_AttemptToRestoreMusic(); // Earliest viable point @@ -5397,7 +5402,7 @@ static void FuzzTiccmd(ticcmd_t* target) { target->buttons |= BT_ACCELERATE; target->buttons &= ~BT_LOOKBACK; - target->buttons &= ~BT_RESPAWN; + target->buttons &= ~BT_BAIL; target->buttons &= ~BT_BRAKE; } } diff --git a/src/d_player.h b/src/d_player.h index 5cb2f7afc..cf7788101 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -135,11 +135,12 @@ typedef enum typedef enum { - PF2_SELFMUTE = 1<<1, - PF2_SELFDEAFEN = 1<<2, - PF2_SERVERMUTE = 1<<3, - PF2_SERVERDEAFEN = 1<<4, - PF2_STRICTFASTFALL = 1<<5, + PF2_SELFMUTE = 1<<1, + PF2_SELFDEAFEN = 1<<2, + PF2_SERVERMUTE = 1<<3, + PF2_SERVERDEAFEN = 1<<4, + PF2_STRICTFASTFALL = 1<<5, + PF2_ALWAYSDAMAGED = 1<<6, } pflags2_t; typedef enum @@ -430,6 +431,8 @@ struct botvars_t tic_t rouletteTimeout; // If it takes too long to decide, try lowering priority until we find something valid. angle_t predictionError; // How bad is our momentum angle relative to where we're trying to go? + angle_t recentDeflection; // How long have we been going straight? (See k_bot.h) + angle_t lastAngle; }; // player_t struct for round-specific condition tracking @@ -738,7 +741,7 @@ struct player_t UINT8 tumbleBounces; UINT16 tumbleHeight; // In *mobjscaled* fracunits, or mfu, not raw fu UINT16 stunned; // Number of tics during which rings cannot be picked up - UINT8 stunnedCombo; // Number of hits sustained while stunned, reduces consecutive stun penalties + mobj_t *flybot; // One Flybot767 circling the player while stunned UINT8 justDI; // Turn-lockout timer to briefly prevent unintended turning after DI, resets when actionable or no input boolean flipDI; // Bananas flip the DI direction. Was a bug, but it made bananas much more interesting. @@ -1079,6 +1082,10 @@ struct player_t UINT16 progressivethrust; // When getting beat up in GTR_BUMPERS, speed up the longer you've been out of control. UINT8 ringvisualwarning; // Check with > 1, not >= 1! Set when put in debt, counts down and holds at 1 when still in debt. + UINT32 bailcharge; + UINT32 baildrop; + boolean bailquake; + boolean analoginput; // Has an input been recorded that requires analog usage? For input display. boolean markedfordeath; diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index f0c5f4f28..2ac3bb07a 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -32,12 +32,13 @@ typedef enum BT_BRAKE = 1<<3, // Brake BT_ATTACK = 1<<4, // Use Item BT_LOOKBACK = 1<<5, // Look Backward - BT_RESPAWN = 1<<6, // Respawn + BT_BAIL = 1<<6, // Bail BT_VOTE = 1<<7, // Vote BT_SPINDASH = 1<<8, // Spindash BT_EBRAKEMASK = (BT_ACCELERATE|BT_BRAKE), BT_SPINDASHMASK = (BT_ACCELERATE|BT_BRAKE|BT_DRIFT), + BT_RESPAWNMASK = (BT_EBRAKEMASK|BT_BAIL), // free: 1<<9 to 1<<12 diff --git a/src/deh_tables.c b/src/deh_tables.c index cd479a80c..a880a1766 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -1574,6 +1574,13 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_BLOCKRING", "S_BLOCKBODY", + "S_BAIL", + "S_BAIB1", + "S_BAIB2", + "S_BAIB3", + "S_BAIC", + "S_BAILCHARGE", + "S_AMPRING", "S_AMPBODY", "S_AMPAURA", @@ -3564,6 +3571,10 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_BLOCKRING", "MT_BLOCKBODY", + "MT_BAIL", + "MT_BAILCHARGE", + "MT_BAILSPARKLE", + "MT_AMPRING", "MT_AMPBODY", "MT_AMPAURA", @@ -5048,11 +5059,12 @@ struct int_const_s const INT_CONST[] = { {"BT_BRAKE",BT_BRAKE}, {"BT_ATTACK",BT_ATTACK}, {"BT_LOOKBACK",BT_LOOKBACK}, - {"BT_RESPAWN",BT_RESPAWN}, + {"BT_BAIL",BT_BAIL}, {"BT_VOTE",BT_VOTE}, {"BT_SPINDASH",BT_SPINDASH}, // Real button now, but triggers the macro same as always. {"BT_EBRAKEMASK",BT_EBRAKEMASK}, // Macro button {"BT_SPINDASHMASK",BT_SPINDASHMASK}, // Macro button + {"BT_RESPAWNMASK",BT_RESPAWNMASK}, // Macro button {"BT_LUAA",BT_LUAA}, // Lua customizable {"BT_LUAB",BT_LUAB}, // Lua customizable {"BT_LUAC",BT_LUAC}, // Lua customizable diff --git a/src/doomdef.h b/src/doomdef.h index 3ff5c274b..2842e4d80 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -747,9 +747,9 @@ extern int // Exp #define EXP_STABLERATE 3*FRACUNIT/10 // how low is your placement before losing XP? 4*FRACUNIT/10 = top 40% of race will gain #define EXP_POWER 3*FRACUNIT/100 // adjust to change overall xp volatility -#define MINEXP 25 // The min value target -#define TARGETEXP 120 // Used for grading ... -#define MAXEXP 120 // The max value displayed by the hud and in the tally screen and GP results screen +#define EXP_MIN 25 // The min value target +#define EXP_TARGET 120 // Used for grading ... +#define EXP_MAX 120 // The max value displayed by the hud and in the tally screen and GP results screen #ifdef __cplusplus } // extern "C" diff --git a/src/doomstat.h b/src/doomstat.h index 97c58c441..748b0773e 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -198,6 +198,8 @@ typedef enum GRADE_S } gp_rank_e; +#define SEALED_STAR_ENTRY (370*FRACUNIT/400) + struct cupwindata_t { UINT8 best_placement; diff --git a/src/g_build_ticcmd.cpp b/src/g_build_ticcmd.cpp index f32c1f509..cfd962e28 100644 --- a/src/g_build_ticcmd.cpp +++ b/src/g_build_ticcmd.cpp @@ -404,10 +404,7 @@ class TiccmdBuilder map(gc_item, BT_ATTACK); // fire map(gc_lookback, BT_LOOKBACK); // rear view - if (!modeattacking) - { - map(gc_respawn, BT_RESPAWN | (freecam() ? 0 : BT_EBRAKEMASK)); // respawn - } + map(gc_bail, BT_BAIL); // bail map(gc_vote, BT_VOTE); // mp general function button // lua buttons a thru c diff --git a/src/g_game.c b/src/g_game.c index cb6ea7697..bd3acb7f0 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2313,7 +2313,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) jointime = players[player].jointime; if (jointime <= 1) { - G_SpectatePlayerOnJoin(player); + // Now called in Got_AddPlayer. In case of weirdness, break glass. + // G_SpectatePlayerOnJoin(player); betweenmaps = true; } @@ -4381,7 +4382,7 @@ void G_GetNextMap(void) && grandprixinfo.gamespeed >= KARTSPEED_NORMAL) { // On A rank pace? Then you get a chance for S rank! - permitrank = (K_CalculateGPGrade(&grandprixinfo.rank) >= GRADE_A); + permitrank = (K_CalculateGPPercent(&grandprixinfo.rank) >= SEALED_STAR_ENTRY); // If you're on Master, a win floats you to rank-restricted levels for free. // (This is a different class of challenge!) diff --git a/src/g_input.h b/src/g_input.h index d2f2d535c..7991923e8 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -123,7 +123,7 @@ typedef enum gc_lookback = gc_b, gc_spindash = gc_c, gc_brake = gc_x, - gc_respawn = gc_y, + gc_bail = gc_y, gc_vote = gc_z, gc_item = gc_l, gc_drift = gc_r, diff --git a/src/hud/input-display.cpp b/src/hud/input-display.cpp index c3dab0a53..14678c87a 100644 --- a/src/hud/input-display.cpp +++ b/src/hud/input-display.cpp @@ -113,7 +113,7 @@ void K_DrawInputDisplay(float x, float y, INT32 flags, char mode, UINT8 pid, boo box.patch(but('B', gc_b, BT_LOOKBACK)); box.patch(but('C', gc_c, BT_SPINDASH)); box.patch(but('X', gc_x, BT_BRAKE)); - box.patch(but('Y', gc_y, BT_RESPAWN)); + box.patch(but('Y', gc_y, BT_BAIL)); box.patch(but('Z', gc_z, BT_VOTE)); box.patch(but('L', gc_l, BT_ATTACK)); box.patch(but('R', gc_r, BT_DRIFT)); diff --git a/src/hud/spectator.cpp b/src/hud/spectator.cpp index f0606378a..512f64d6a 100644 --- a/src/hud/spectator.cpp +++ b/src/hud/spectator.cpp @@ -198,7 +198,7 @@ void K_drawSpectatorHUD(boolean director) } else { - bool press = D_LocalTiccmd(viewnum)->buttons & BT_RESPAWN; + bool press = D_LocalTiccmd(viewnum)->buttons & BT_BAIL; const char* label = (press && I_GetTime() % 16 < 8) ? "> <" : ">< "; list.insert({{label, press ? "" : ""}, {"Exit", ""}}); diff --git a/src/info.c b/src/info.c index 690b5e5c7..93f6bddae 100644 --- a/src/info.c +++ b/src/info.c @@ -334,6 +334,11 @@ char sprnames[NUMSPRITES + 1][5] = "GRNG", // Guard ring "GBDY", // Guard body + "BAIL", // Bail charge + "BAIB", // Bail after effect + "BAIC", // Bail sparkle + "TECH", // Bail tech charge + "TRC1", // Charge aura "TRC2", // Charge fall "TRC3", // Charge flicker/sparks @@ -2160,6 +2165,16 @@ state_t states[NUMSTATES] = {SPR_GRNG, FF_FULLBRIGHT|FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_BLOCKRING {SPR_GBDY, FF_FULLBRIGHT|FF_ANIMATE|0, -1, {NULL}, 4, 2, S_NULL}, // S_BLOCKBODY + // why can we not use actions on spawn? I'd love to fix it but I imagine all sorts of crazy pain if I change something fundamental like that + {SPR_BAIL, FF_FULLBRIGHT|FF_ANIMATE|0, 9, {NULL}, 8, 1, S_BAIB1}, // S_BAIL + {SPR_BAIB, 0, 0, {A_PlaySound}, sfx_gshb2, 2, S_BAIB2}, // S_BAIB1 + {SPR_BAIB, 0, 0, {A_PlaySound}, sfx_gshbd, 2, S_BAIB3}, // S_BAIB2 + {SPR_BAIB, FF_FULLBRIGHT|FF_ANIMATE|0, 10, {NULL}, 9, 1, S_NULL}, // S_BAIB3 + + {SPR_BAIC, FF_FULLBRIGHT|FF_ANIMATE|0, 11, {NULL}, 10, 1, S_NULL}, // S_BAIC + + {SPR_TECH, 1, -1, {NULL}, 41, 1, S_NULL}, // S_BAILCHARGE + {SPR_AMPB, FF_FULLBRIGHT|FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_AMPRING {SPR_AMPC, FF_FULLBRIGHT|FF_ANIMATE|0, -1, {NULL}, 4, 2, S_NULL}, // S_AMPBODY {SPR_AMPD, FF_FULLBRIGHT|FF_ANIMATE|0, -1, {NULL}, 4, 2, S_NULL}, // S_AMPAURA @@ -13745,6 +13760,87 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_BAIL + -1, // doomednum + S_BAIL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 67*FRACUNIT, // radius + 67*FRACUNIT, // height + 1, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_BAILCHARGE + -1, // doomednum + S_BAILCHARGE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 67*FRACUNIT, // radius + 67*FRACUNIT, // height + 1, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_BAILSPARKLE + -1, // doomednum + S_BAIC, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 67*FRACUNIT, // radius + 67*FRACUNIT, // height + 1, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_DONTENCOREMAP|MF_SCENERY, // flags + S_NULL // raisestate + }, + { // MT_AMPRING -1, // doomednum S_AMPRING, // spawnstate diff --git a/src/info.h b/src/info.h index f704e4426..996b721e2 100644 --- a/src/info.h +++ b/src/info.h @@ -875,6 +875,11 @@ typedef enum sprite SPR_GRNG, // Guard ring SPR_GBDY, // Guard body + SPR_BAIL, // Bail charge + SPR_BAIB, // Bail after effect + SPR_BAIC, // Bail sparkle + SPR_TECH, // Bail tech charge + SPR_TRC1, // Charge aura SPR_TRC2, // Charge fall SPR_TRC3, // Charge flicker/sparks @@ -2629,6 +2634,13 @@ typedef enum state S_BLOCKRING, S_BLOCKBODY, + S_BAIL, + S_BAIB1, + S_BAIB2, + S_BAIB3, + S_BAIC, + S_BAILCHARGE, + S_AMPRING, S_AMPBODY, S_AMPAURA, @@ -4646,6 +4658,10 @@ typedef enum mobj_type MT_BLOCKRING, MT_BLOCKBODY, + MT_BAIL, + MT_BAILCHARGE, + MT_BAILSPARKLE, + MT_AMPRING, MT_AMPBODY, MT_AMPAURA, diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 3918a2e6b..1d827138a 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -586,7 +586,7 @@ const botcontroller_t *K_GetBotController(const mobj_t *mobj) fixed_t K_BotMapModifier(void) { // fuck it we ball - // return 10*FRACUNIT/10; + return 5*FRACUNIT/10; constexpr INT32 complexity_scale = 10000; fixed_t modifier_max = (10 * FRACUNIT / 10) - FRACUNIT; @@ -690,9 +690,9 @@ fixed_t K_BotRubberband(const player_t *player) // Allow the status quo to assert itself a bit. Bots get most of their speed from their // mechanics adjustments, not from items, so kill some bot speed if they've got bad EXP. - if (player->gradingfactor < FRACUNIT && !(player->botvars.rival)) + if (player->gradingfactor < FRACUNIT && !(player->botvars.rival) && player->botvars.difficulty > 1) { - UINT8 levelreduce = 3; // How much to drop the "effective level" of bots that are consistently behind + UINT8 levelreduce = std::min(3, player->botvars.difficulty); // How much to drop the "effective level" of bots that are consistently behind expreduce = Easing_Linear((K_EffectiveGradingFactor(player) - MINGRADINGFACTOR) * 2, levelreduce*FRACUNIT, 0); } @@ -823,12 +823,23 @@ fixed_t K_BotRubberband(const player_t *player) fixed_t K_UpdateRubberband(player_t *player) { fixed_t dest = K_BotRubberband(player); + + fixed_t deflect = player->botvars.recentDeflection; + if (deflect > BOTMAXDEFLECTION) + deflect = BOTMAXDEFLECTION; + + dest = FixedMul(dest, Easing_Linear( + FixedDiv(deflect, BOTMAXDEFLECTION), + BOTSTRAIGHTSPEED, + BOTTURNSPEED + )); + fixed_t ret = player->botvars.rubberband; - UINT8 ease_soften = 8; + UINT8 ease_soften = (ret > dest) ? 3 : 8; if (player->botvars.bumpslow && dest > ret) - ease_soften *= 10; + ease_soften = 80; // Ease into the new value. ret += (dest - player->botvars.rubberband) / ease_soften; @@ -1792,7 +1803,7 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd) if (K_TryRingShooter(player, botController) == true && player->botvars.respawnconfirm >= BOTRESPAWNCONFIRM) { // We want to respawn. Simply hold Y and stop here! - cmd->buttons |= (BT_RESPAWN | BT_EBRAKEMASK); + cmd->buttons |= BT_RESPAWNMASK; return; } @@ -2123,6 +2134,21 @@ void K_UpdateBotGameplayVars(player_t *player) } } + angle_t mangle = K_MomentumAngleEx(player->mo, 5*mapobjectscale); // magic threshold + angle_t langle = player->botvars.lastAngle; + angle_t dangle = 0; + if (mangle >= langle) + dangle = mangle - langle; + else + dangle = langle - mangle; + // Writing this made me move my tongue around in my mouth + + UINT32 smo = BOTANGLESAMPLES - 1; + + player->botvars.recentDeflection = (smo * player->botvars.recentDeflection / BOTANGLESAMPLES) + (dangle / BOTANGLESAMPLES); + + player->botvars.lastAngle = mangle; + const botcontroller_t *botController = K_GetBotController(player->mo); if (K_TryRingShooter(player, botController) == true) { diff --git a/src/k_bot.h b/src/k_bot.h index 6044bbd92..e58e4e8be 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -35,7 +35,7 @@ extern "C" { // How many tics in a row do you need to turn in this direction before we'll let you turn. // Made it as small as possible without making it look like the bots are twitching constantly. -#define BOTTURNCONFIRM 4 +#define BOTTURNCONFIRM 1 // How many tics with only one spindash-viable condition before we'll let you spindash. #define BOTSPINDASHCONFIRM (4*TICRATE) @@ -46,6 +46,11 @@ extern "C" { // How long it takes for a Lv.1 bot to decide to pick an item. #define BOT_ITEM_DECISION_TIME (2*TICRATE) +#define BOTSTRAIGHTSPEED (80*FRACUNIT/100) // How fast we move when at 0 deflection. +#define BOTTURNSPEED (100*FRACUNIT/100) // How fast we move when at MAXDEFLECTION deflection. +#define BOTANGLESAMPLES (TICRATE) // Time period to average over. Higher values produce lower peaks that last longer. +#define BOTMAXDEFLECTION (ANG1*3) // Measured in "degrees per tic" here, use debugbots. + // Point for bots to aim for struct botprediction_t { diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 0de68598c..0392e0133 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -885,18 +885,28 @@ boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2) thing = oldthing; P_SetTarget(&g_tm.thing, oldg_tm.thing);*/ + boolean hit = false; + if (K_KartBouncing(t2, t1->target) == true) { if (t2->player && t1->target && t1->target->player) { - K_PvPTouchDamage(t2, t1->target); + hit = K_PvPTouchDamage(t2, t1->target); } // Don't play from t1 else it gets cut out... for some reason. S_StartSound(t2, sfx_s3k44); } - return true; + if (hit && (gametyperules & GTR_BUMPERS)) + { + K_PopBubbleShield(t1->target->player); + return false; + } + else + { + return true; + } } if (K_BubbleShieldCanReflect(t1, t2)) @@ -1158,6 +1168,27 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) return false; } + + boolean guard1 = K_PlayerGuard(t1->player); + boolean guard2 = K_PlayerGuard(t2->player); + + // Bubble Shield physically extends past guard when inflated, + // makes some sense to suppress this behavior + if (t1->player->bubbleblowup) + guard1 = false; + if (t2->player->bubbleblowup) + guard2 = false; + + if (guard1 && guard2) + K_DoPowerClash(t1, t2); + else if (guard1) + K_DoGuardBreak(t1, t2); + else if (guard2) + K_DoGuardBreak(t2, t1); + + if (guard1 || guard2) + return false; + // Clash instead of damage if both parties have any of these conditions auto canClash = [](mobj_t *t1, mobj_t *t2) { diff --git a/src/k_follower.c b/src/k_follower.c index aee769b95..b4ed0bf84 100644 --- a/src/k_follower.c +++ b/src/k_follower.c @@ -546,7 +546,7 @@ void K_HandleFollower(player_t *player) } // Sal: Turn the follower around when looking backwards. - if ( player->cmd.buttons & BT_LOOKBACK ) + if (K_GetKartButtons(player) & BT_LOOKBACK) { destAngle += ANGLE_180; } diff --git a/src/k_hitlag.c b/src/k_hitlag.c index 9110f1845..ca891eb04 100644 --- a/src/k_hitlag.c +++ b/src/k_hitlag.c @@ -34,6 +34,9 @@ void K_AddHitLag(mobj_t *mo, INT32 tics, boolean fromDamage) return; } + if (mo->player && mo->player->overshield) + tics = min(tics, 3); + mo->hitlag += tics; mo->hitlag = min(mo->hitlag, MAXHITLAGTICS); diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 1bf954bec..a5e25edbf 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -4876,7 +4876,7 @@ static void K_drawKartPlayerCheck(void) return; } - if (stplyr->cmd.buttons & BT_LOOKBACK) + if (K_GetKartButtons(stplyr) & BT_LOOKBACK) { return; } @@ -7095,7 +7095,8 @@ static void K_DrawBotDebugger(void) V_DrawSmallString(8, 66, 0, va("Complexity: %d", K_GetTrackComplexity())); V_DrawSmallString(8, 70, 0, va("Bot modifier: %.2f", FixedToFloat(K_BotMapModifier()))); - V_DrawSmallString(8, 76, 0, va("Prediction error: %d", bot->botvars.predictionError)); + V_DrawSmallString(8, 76, 0, va("Prediction error: %.2fdeg", FIXED_TO_FLOAT(FixedDiv(bot->botvars.predictionError, ANG1)))); + V_DrawSmallString(8, 80, 0, va("Recent deflection: %.2fdeg", FIXED_TO_FLOAT(FixedDiv(bot->botvars.recentDeflection, ANG1)))); } static void K_DrawGPRankDebugger(void) @@ -7119,6 +7120,7 @@ static void K_DrawGPRankDebugger(void) } grade = K_CalculateGPGrade(&grandprixinfo.rank); + fixed_t percent = K_CalculateGPPercent(&grandprixinfo.rank); V_DrawThinString(0, 0, V_SNAPTOTOP|V_SNAPTOLEFT, va("POS: %d / %d", grandprixinfo.rank.position, RANK_NEUTRAL_POSITION)); @@ -7132,6 +7134,8 @@ static void K_DrawGPRankDebugger(void) va("RINGS: %d / %d", grandprixinfo.rank.rings, grandprixinfo.rank.totalRings)); V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT, va("EMERALD: %s", (grandprixinfo.rank.specialWon == true) ? "YES" : "NO")); + V_DrawThinString(0, 70, V_SNAPTOTOP|V_SNAPTOLEFT, + va("PERCENT: %.2f", FixedToFloat(percent))); switch (grade) { @@ -7517,7 +7521,7 @@ void K_drawKartHUD(void) if (ta) { using srb2::Draw; - Draw::TextElement text = Draw::TextElement().parse(" Restart"); + Draw::TextElement text = Draw::TextElement().parse(" Restart"); Draw(BASEVIDWIDTH - 19, 2) .flags(flags | V_YELLOWMAP) .align(Draw::Align::kRight) diff --git a/src/k_kart.c b/src/k_kart.c index 9caf07617..bcf9747b8 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -72,13 +72,17 @@ // comeback is Battle Mode's karma comeback, also bool // mapreset is set when enough players fill an empty server -static void K_PopBubbleShield(player_t *player) +void K_PopBubbleShield(player_t *player) { + if (player->curshield != KSHIELD_BUBBLE) + return; + S_StartSound(player->mo, sfx_kc31); K_StripItems(player); K_AddHitLag(player->mo, 4, false); vector3_t offset = { 0, 0, 0 }; - K_SpawnSingleHitLagSpark(player->mo, &offset, player->mo->scale*2, 4, 0, player->skincolor); + K_SpawnSingleHitLagSpark(player->mo, &offset, player->mo->scale*2, 4, 0, player->skincolor); + player->bubbledrag = false; } boolean K_ThunderDome(void) @@ -1179,20 +1183,6 @@ boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2) K_SpawnBumpForObjs(mobj1, mobj2); - if (mobj1->type == MT_PLAYER && mobj2->type == MT_PLAYER - && !mobj1->player->powerupVFXTimer && !mobj2->player->powerupVFXTimer) - { - boolean guard1 = K_PlayerGuard(mobj1->player); - boolean guard2 = K_PlayerGuard(mobj2->player); - - if (guard1 && guard2) - K_DoPowerClash(mobj1, mobj2); - else if (guard1) - K_DoGuardBreak(mobj1, mobj2); - else if (guard2) - K_DoGuardBreak(mobj2, mobj1); - } - K_PlayerJustBumped(mobj1->player); K_PlayerJustBumped(mobj2->player); @@ -1607,13 +1597,16 @@ static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed // How much this increments every tic biases toward acceleration! (min speed gets 1.5% per tic, max speed gets 0.5% per tic) if (player->draftpower < FRACUNIT) { - fixed_t add = (FRACUNIT/200) + ((9 - player->kartspeed) * ((3*FRACUNIT)/1600));; + fixed_t add = (FRACUNIT/200) + ((9 - player->kartspeed) * ((3*FRACUNIT)/1600)); player->draftpower += add; - if (player->bot && (player->botvars.rival || cv_levelskull.value)) + if (player->bot) { // Double speed for the rival! - player->draftpower += add; + if (player->botvars.rival || cv_levelskull.value) + player->draftpower += add; + else if (dest->player->bot) // Reduce bot gluts. + player->draftpower -= 3*add/4; } if (gametyperules & GTR_CLOSERPLAYERS) @@ -4066,8 +4059,16 @@ boolean K_KartKickstart(const player_t *player) UINT16 K_GetKartButtons(const player_t *player) { - return (player->cmd.buttons | - (K_KartKickstart(player) ? BT_ACCELERATE : 0)); + UINT16 buttons = player->cmd.buttons; + if ((buttons & BT_RESPAWNMASK) == BT_RESPAWNMASK) + { + buttons &= ~BT_LOOKBACK; + } + if (K_KartKickstart(player)) + { + buttons = buttons | BT_ACCELERATE; + } + return buttons; } SINT8 K_GetForwardMove(const player_t *player) @@ -4553,12 +4554,16 @@ void K_DoPowerClash(mobj_t *t1, mobj_t *t2) { UINT8 lag1 = 5; UINT8 lag2 = 5; + boolean stripbubble = (gametyperules & GTR_BUMPERS); + // short-circuit instashield for vfx visibility if (t1->player) { t1->player->instashield = 1; t1->player->speedpunt += 20; lag1 -= min(lag1, t1->player->speedpunt/10); + if (stripbubble && t1->player->curshield == KSHIELD_BUBBLE) + K_PopBubbleShield(t1->player); } if (t2->player) @@ -4566,6 +4571,8 @@ void K_DoPowerClash(mobj_t *t1, mobj_t *t2) { t2->player->instashield = 1; t2->player->speedpunt += 20; lag2 -= min(lag1, t2->player->speedpunt/10); + if (stripbubble && t2->player->curshield == KSHIELD_BUBBLE) + K_PopBubbleShield(t2->player); } S_StartSound(t1, sfx_parry); @@ -4603,7 +4610,9 @@ void K_DoGuardBreak(mobj_t *t1, mobj_t *t2) { angle_t thrangle = R_PointToAngle2(t2->x, t2->y, t1->x, t1->y); P_Thrust(t1, thrangle, 7*mapobjectscale); + t1->player->pflags2 |= PF2_ALWAYSDAMAGED; P_DamageMobj(t1, t2, t2, 1, DMG_TUMBLE); + t1->player->pflags2 &= ~PF2_ALWAYSDAMAGED; clash = P_SpawnMobj((t1->x/2) + (t2->x/2), (t1->y/2) + (t2->y/2), (t1->z/2) + (t2->z/2), MT_GUARDBREAK); @@ -5180,6 +5189,8 @@ void K_UpdateWavedashIndicator(player_t *player) { mobj_t *mobj = NULL; + player->vortexBoost = 0; + if (player == NULL) { return; @@ -5193,7 +5204,6 @@ void K_UpdateWavedashIndicator(player_t *player) if (player->wavedashIndicator == NULL || P_MobjWasRemoved(player->wavedashIndicator) == true) { K_InitWavedashIndicator(player); - player->vortexBoost = 0; return; } @@ -7768,6 +7778,30 @@ void K_PopPlayerShield(player_t *player) K_UnsetItemOut(player); } +static void K_DeleteHnextList(player_t *player) +{ + mobj_t *work = player->mo, *nextwork; + + if (work == NULL || P_MobjWasRemoved(work)) + { + return; + } + + nextwork = work->hnext; + + while ((work = nextwork) && !(work == NULL || P_MobjWasRemoved(work))) + { + nextwork = work->hnext; + + if (!work->health) + continue; // taking care of itself + + K_SpawnLandMineExplosion(work, player->skincolor, player->mo->hitlag); + + P_RemoveMobj(work); + } +} + void K_DropHnextList(player_t *player) { mobj_t *work = player->mo, *nextwork, *dropwork; @@ -9734,23 +9768,12 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) && P_IsObjectOnGround(player->mo) ) { - // MEGA FUCKING HACK BECAUSE P_SAVEG MOBJS ARE FULL - // Would updating player_saveflags to 32 bits have any negative consequences? - // For now, player->stunned 16th bit is a flag to determine whether the flybots were spawned - // timer counts down at triple speed while spindashing - player->stunned = (player->stunned & 0x8000) | max(0, (player->stunned & 0x7FFF) - (player->spindash ? 3 : 1)); + player->stunned = max(0, player->stunned - (player->spindash ? 3 : 1)); - // when timer reaches 0, reset the flag and stun combo counter - if ((player->stunned & 0x7FFF) == 0) + // if the flybots aren't spawned, spawn them now! + if (player->stunned != 0 && P_MobjWasRemoved(player->flybot)) { - player->stunned = 0; - player->stunnedCombo = 0; - } - // otherwise if the flybots aren't spawned, spawn them now! - else if ((player->stunned & 0x8000) == 0) - { - player->stunned |= 0x8000; Obj_SpawnFlybotsForPlayer(player); } } @@ -9785,7 +9808,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) // UINT16 oldringboost = player->ringboost; - if (player->superring == 0 || player->stunned) + if (!player->baildrop && (player->superring == 0 || player->stunned)) player->ringboost -= max((player->ringboost / roller), 1); else if (K_LegacyRingboost(player)) player->ringboost--; @@ -9976,6 +9999,51 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } } + if (player->baildrop) + { + if (player->stunned & 0x8000) + player->stunned = 0x8000 | BAILSTUN; + else + player->stunned = BAILSTUN; + + mobj_t *pmo = player->mo; + // particle spawn + #define BAILSPARKLE_MAXBAIL 61 // amount of bail rings needed for max sparkle spawn frequency + UINT32 baildropinversefreq = BAILSPARKLE_MAXBAIL - min(player->baildrop, BAILSPARKLE_MAXBAIL-6); + UINT32 baildropmodulo = baildropinversefreq *5/3 /10; + if ((leveltime % (1+baildropmodulo)) == 0) + { + mobj_t *sparkle = P_SpawnMobj(pmo->x + (P_RandomRange(PR_DECORATION, -40,40) * pmo->scale), + pmo->y + (P_RandomRange(PR_DECORATION, -40,40) * pmo->scale), + pmo->z + (pmo->height/2) + (P_RandomRange(PR_DECORATION, -40,40) * pmo->scale), + MT_BAILSPARKLE); + + sparkle->scale = pmo->scale; + sparkle->angle = pmo->angle; + sparkle->momx = 3*pmo->momx/4; + sparkle->momy = 3*pmo->momy/4; + sparkle->momz = 3*P_GetMobjZMovement(pmo)/4; + K_MatchGenericExtraFlags(sparkle, pmo); + sparkle->renderflags = (pmo->renderflags & ~RF_TRANSMASK);//|RF_TRANS20|RF_ADD; + } + + if ((player->baildrop % BAIL_DROPFREQUENCY) == 0) + { + P_FlingBurst(player, K_MomentumAngle(pmo), MT_FLINGRING, 10*TICRATE, FRACUNIT, player->baildrop/BAIL_DROPFREQUENCY); + S_StartSound(pmo, sfx_gshad); + } + + player->baildrop--; + if (player->baildrop == 0) + player->ringboost /= 3; + } + + if (player->bailquake && !player->mo->hitlag) // quake as soon as we leave hitlag + { + P_StartQuakeFromMobj(7, 50 * player->mo->scale, 2048 * player->mo->scale, player->mo); + player->bailquake = false; + } + // The precise ordering of start-of-level made me want to cut my head off, // so let's try this instead. Whatever! if (leveltime <= starttime || player->gradingpointnum == 0) @@ -10057,7 +10125,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_RemoveGrowShrink(player); } - if (player->respawn.state != RESPAWNST_MOVE && (player->cmd.buttons & BT_RESPAWN) == BT_RESPAWN) + if (player->respawn.state != RESPAWNST_MOVE && (player->cmd.buttons & BT_RESPAWNMASK) == BT_RESPAWNMASK) { player->finalfailsafe++; // Decremented by ringshooter to "freeze" this timer // Part-way through the auto-respawn timer, you can tap Ring Shooter to respawn early @@ -10143,7 +10211,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->nextringaward >= ringrate) { - if (player->instaWhipCharge) + if (player->instaWhipCharge || player->baildrop || player->bailcharge) { // Store award rings to do diabolical horseshit with later. player->nextringaward = ringrate; @@ -10309,6 +10377,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->powerupVFXTimer > 0) { player->powerupVFXTimer--; + if (player->powerupVFXTimer == 0) + player->mo->flags &= ~MF_NOCLIPTHING; } if (player->dotrickfx && !player->mo->hitlag) @@ -13883,6 +13953,106 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } + if ((player->cmd.buttons & BT_BAIL) && (player->cmd.buttons & BT_RESPAWNMASK) != BT_RESPAWNMASK && ((player->itemtype && player->itemamount) || (player->rings > 0) || player->superring > 0 || player->pickuprings > 0 || player->itemRoulette.active)) + { + boolean grounded = P_IsObjectOnGround(player->mo); + onground && player->tumbleBounces == 0 ? player->bailcharge += 2 : player->bailcharge++; // charge twice as fast on the ground + if ((P_PlayerInPain(player) && player->bailcharge == 1) || (grounded && P_PlayerInPain(player) && player->bailcharge == 2)) // this is brittle .. + { + mobj_t *bail = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_BAILCHARGE); + S_StartSound(bail, sfx_gshb9); // I tried to use info.c, but you can't play sounds on mobjspawn via A_PlaySound + S_StartSound(bail, sfx_kc4e); + P_SetTarget(&bail->target, player->mo); + bail->renderflags |= RF_FULLBRIGHT; // set fullbright here, were gonna animate frames in the thinker and it saves us from setting FF_FULLBRIGHT every frame + } + } + else + { + player->bailcharge = 0; + } + + if ((!P_PlayerInPain(player) && player->bailcharge >= 5) || player->bailcharge >= BAIL_MAXCHARGE) + { + player->bailcharge = 0; + + mobj_t *bail = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_BAIL); + P_SetTarget(&bail->target, player->mo); + + UINT32 debtrings = 20; + if (player->rings < 0) + { + debtrings -= player->rings; + player->rings = 0; + } + + UINT32 totalrings = player->rings + player->superring + player->pickuprings; + if (BAIL_CREDIT_DEBTRINGS) + totalrings += debtrings; + totalrings = max(totalrings, 0); + UINT32 bailboost = FixedInt(FixedMul(totalrings*FRACUNIT, BAIL_BOOST)); + UINT32 baildrop = FixedInt(FixedMul((totalrings)*FRACUNIT, BAIL_DROP)); + + if (player->itemRoulette.active) + { + player->itemRoulette.active = false; + } + + K_PopPlayerShield(player); + K_DeleteHnextList(player); + K_DropItems(player); + + player->itemamount = 0; + player->itemtype = 0; + + /* + if (player->itemamount) + { + K_DropPaperItem(player, player->itemtype, player->itemamount); + player->itemtype = player->itemamount = 0; + } + */ + + player->rings = -20; + player->superring = 0; + player->pickuprings = 0; + player->ringboxaward = 0; + player->ringboxdelay = 0; + + player->superringdisplay = 0; + player->superringalert = 0; + player->superringpeak = 0; + + player->counterdash += TICRATE/8; + + player->ringboost += bailboost * (3+K_GetKartRingPower(player, true)); + player->baildrop = baildrop * BAIL_DROPFREQUENCY + 1; + + K_AddHitLag(player->mo, TICRATE/4, false); + player->bailquake = true; // set for a one time quake effect as soon as hitlag ends + + if (P_PlayerInPain(player)) + { + player->spinouttimer = 0; + player->spinouttype = 0; + player->tumbleBounces = 0; + player->pflags &= ~PF_TUMBLELASTBOUNCE; + player->mo->rollangle = 0; + P_ResetPitchRoll(player->mo); + } + + INT32 fls = K_GetEffectiveFollowerSkin(player); + if (player->follower && fls >= 0 && fls < numfollowers) + { + const follower_t *fl = &followers[fls]; + S_StartSound(NULL, fl->hornsound); + } + + if (player->amps > 0) + K_DefensiveOverdrive(player); + + S_StartSound(player->mo, sfx_kc33); + } + if (player && player->mo && K_PlayerCanUseItem(player)) { // First, the really specific, finicky items that function without the item being directly in your item slot. @@ -14551,7 +14721,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) S_StartSound(player->mo, sfx_s3k75); player->bubbleblowup++; - player->bubblecool = player->bubbleblowup*4; + player->bubblecool = player->bubbleblowup * (gametyperules & GTR_BUMPERS ? 6 : 4); if (player->bubbleblowup > bubbletime*2) { @@ -15960,111 +16130,115 @@ boolean K_PlayerCanUseItem(player_t *player) return (player->mo->health > 0 && !player->spectator && !P_PlayerInPain(player) && !mapreset && leveltime > introtime); } -fixed_t K_GetGradingFactorAdjustment(player_t *player) +// === +// THE EXP ZONE +// === + +static boolean K_IsValidOpponent(player_t *me, player_t *them) { - fixed_t power = EXP_POWER; // adjust to change overall xp volatility - const fixed_t stablerate = EXP_STABLERATE; // how low is your placement before losing XP? 4*FRACUNIT/10 = top 40% of race will gain - fixed_t result = 0; + UINT8 i = (them - players); + + if (!playeringame[i] || players[i].spectator) + return false; + if (me == them) + return false; + if (G_SameTeam(me, them)) + return false; + + return true; +} + +static UINT8 K_Opponents(player_t *player) +{ + UINT8 opponents = 0; // players we are competing against + + for (UINT8 i = 0; i < MAXPLAYERS; i++) + { + if (K_IsValidOpponent(player, &players[i])) + opponents++; + } + + return opponents; +} + +static fixed_t K_GradingFactorPower(player_t *player) +{ + fixed_t power = EXP_POWER; // adjust to change overall exp volatility + UINT8 opponents = K_Opponents(player); if (g_teamplay) power = 3 * power / 4; - INT32 live_players = 0; // players we are competing against + if (opponents < 8) + power += (8 - opponents) * power/4; + return power; +} + +static fixed_t K_GradingFactorGainPerWin(player_t *player) +{ + return K_GradingFactorPower(player); +} + +static fixed_t K_GradingFactorDrainPerCheckpoint(player_t *player) +{ + // EXP_STABLERATE: How low do you have to place before losing gradingfactor? 4*FRACUNIT/10 = top 40% of race gains, 60% loses. + UINT8 opponents = K_Opponents(player); + fixed_t power = K_GradingFactorPower(player); + return FixedMul(power, FixedMul(opponents*FRACUNIT, FRACUNIT - EXP_STABLERATE)); +} + +fixed_t K_GetGradingFactorAdjustment(player_t *player) +{ + fixed_t result = 0; + + // Increase gradingfactor for each player you're beating... for (INT32 i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || players[i].spectator || player == players+i) + if (!K_IsValidOpponent(player, &players[i])) continue; - if (G_SameTeam(player, &players[i]) == true) - { - // You don't win/lose against your teammates. - continue; - } - - live_players++; - } - - if (live_players < 8) - { - power += (8 - live_players) * power/4; - } - - // Increase XP for each player you're beating... - for (INT32 i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || player == players+i) - continue; - - if (G_SameTeam(player, &players[i]) == true) - { - // You don't win/lose against your teammates. - continue; - } - if (player->position < players[i].position) - result += power; + result += K_GradingFactorGainPerWin(player); } - // ...then take all of the XP you could possibly have earned, + // ...then take all of the gradingfactor you could possibly have earned, // and lose it proportional to the stable rate. If you're below - // the stable threshold, this results in you losing XP. - result -= FixedMul(power, FixedMul(live_players*FRACUNIT, FRACUNIT - stablerate)); + // the stable threshold, this results in you losing gradingfactor + result -= K_GradingFactorDrainPerCheckpoint(player); return result; } -fixed_t K_GetGradingFactorMinMax(UINT32 gradingpointnum, boolean max) +fixed_t K_GetGradingFactorMinMax(player_t *player, boolean max) { - // Create a dummy player structure for the theoretical last-place player - player_t dummy_player; - memset(&dummy_player, 0, sizeof(player_t)); - dummy_player.gradingfactor = FRACUNIT; // Start at 1.0 + fixed_t factor = FRACUNIT; // Starting gradingfactor + UINT8 opponents = K_Opponents(player); + UINT8 winning = (max) ? opponents : 0; - if (G_GametypeHasTeams()) + for (UINT8 i = 0; i < player->gradingpointnum; i++) // For each gradingpoint you've reached... { - const UINT8 orange_count = G_CountTeam(TEAM_ORANGE); - const UINT8 blue_count = G_CountTeam(TEAM_BLUE); - if (orange_count <= blue_count) - { - dummy_player.team = TEAM_ORANGE; - } - else - { - dummy_player.team = TEAM_BLUE; - } - dummy_player.position = max ? 0 : D_NumPlayersInRace() + 1; // Ensures that all enemy players are counted, and our dummy won't overlap + for (UINT8 j = 0; j < winning; j++) + factor += K_GradingFactorGainPerWin(player); // If max, increase gradingfactor for each player you could have been beating. + factor -= K_GradingFactorDrainPerCheckpoint(player); // Then, drain like usual. } - else - { - dummy_player.position = max ? 1 : D_NumPlayersInRace(); - } - - // Apply the adjustment for each grading point - for (UINT32 i = 0; i < gradingpointnum; i++) - { - dummy_player.gradingfactor += K_GetGradingFactorAdjustment(&dummy_player); - } - return dummy_player.gradingfactor; + + return factor; } UINT16 K_GetEXP(player_t *player) { UINT32 numgradingpoints = K_GetNumGradingPoints(); - UINT16 targetminexp = (MINEXP*player->gradingpointnum/max(1,numgradingpoints)); // about what a last place player should be at this stage of the race - UINT16 targetexp = (MAXEXP*player->gradingpointnum/max(1,numgradingpoints)); // about what a 1.0 factor should be at this stage of the race - fixed_t factormin = K_GetGradingFactorMinMax(player->gradingpointnum, false); - fixed_t factormax = K_GetGradingFactorMinMax(player->gradingpointnum, true); - fixed_t clampedfactor = max(factormin, min(factormax, player->gradingfactor)); - fixed_t range = factormax - factormin; - fixed_t normalizedfactor = FixedDiv(clampedfactor - factormin, range); - fixed_t easedexp = Easing_Linear(normalizedfactor, targetminexp, targetexp); - // fixed_t easedexp = Easing_Linear(normalizedfactor, MINEXP*FRACUNIT, MAXEXP*FRACUNIT); - UINT16 exp = easedexp; - // CONS_Printf("Player %s numgradingpoints=%d targetminexp=%d targetexp=%d factormin=%.2f factormax=%.2f clampedfactor=%.2f normalizedfactor=%.2f easedexp=%d\n", - // player_names[player - players], numgradingpoints, targetminexp, targetexp, FIXED_TO_FLOAT(factormin), FIXED_TO_FLOAT(factormax), - // FIXED_TO_FLOAT(clampedfactor), FIXED_TO_FLOAT(normalizedfactor), easedexp); - // UINT16 exp = (player->gradingfactor*100)>>FRACBITS; + fixed_t targetminexp = (EXP_MIN*player->gradingpointnum<gradingpointnum<gradingfactor, factormin, factormax, Easing_Linear, targetminexp, targetmaxexp)>>FRACBITS; + + // 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; } @@ -16076,6 +16250,10 @@ UINT32 K_GetNumGradingPoints(void) return numlaps * (1 + Obj_GetCheckpointCount()); } +// === +// END EXP ZONE +// === + void K_BotHitPenalty(player_t *player) { if (K_PlayerUsesBotMovement(player)) @@ -16301,4 +16479,70 @@ fixed_t K_TeamComebackMultiplier(player_t *player) return multiplier; } +void K_ApplyStun(player_t *player, mobj_t *inflictor, mobj_t *source, ATTRUNUSED INT32 damage, ATTRUNUSED UINT8 damagetype) +{ + #define BASE_STUN_TICS_MIN (4 * TICRATE) + #define BASE_STUN_TICS_MAX (10 * TICRATE) + #define MAX_STUN_REDUCTION (FRACUNIT/2) + #define STUN_REDUCTION_DISTANCE (20000) + INT32 stunTics = 0; + UINT8 numPlayers = 0; + UINT8 i; + + // calculate the number of players playing + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator) + { + numPlayers++; + } + } + + // calculate base stun tics + stunTics = Easing_Linear((player->kartweight - 1) * FRACUNIT / 8, BASE_STUN_TICS_MAX, BASE_STUN_TICS_MIN); + + // reduce stun in games with more than 8 players + if (numPlayers > 8) + { + stunTics -= 6 * (numPlayers - 8); + } + + // 1/3 stun values in battle + if (gametyperules & GTR_SPHERES) + { + stunTics /= 3; + } + + if (source && source->player) + { + // hits scored by players apply full stun + ; + } + else if (inflictor && (P_IsKartItem(inflictor->type) || P_IsKartFieldItem(inflictor->type))) + { + // items not thrown by a player apply half stun + stunTics /= 2; + } + else + { + // all other hazards apply 1/4 stun + stunTics /= 4; + } + + UINT32 dist = K_GetItemRouletteDistance(player, D_NumPlayersInRace()); + if (dist > STUN_REDUCTION_DISTANCE) + dist = STUN_REDUCTION_DISTANCE; + + fixed_t distfactor = FixedDiv(dist, STUN_REDUCTION_DISTANCE); // 0-1 as you approach STUN_REDUCTION_DISTANCE + fixed_t stunfactor = Easing_Linear(distfactor, FRACUNIT, MAX_STUN_REDUCTION); + stunTics = FixedMul(stunTics*FRACUNIT, stunfactor)/FRACUNIT; + + player->stunned = max(stunTics, 0); + + #undef BASE_STUN_TICS_MIN + #undef BASE_STUN_TICS_MAX + #undef MAX_STUN_REDUCTION + #undef STUN_REDUCTION_DISTANCE +} + //} diff --git a/src/k_kart.h b/src/k_kart.h index ff269dbb9..e454ec0a1 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -44,6 +44,13 @@ Make sure this matches the actual number of states #define INSTAWHIP_TETHERBLOCK (TICRATE*4) #define PUNISHWINDOW (7*TICRATE/10) +#define BAIL_MAXCHARGE (84) // tics to bail when in painstate nad in air, on ground is half, if you touch this, also update Obj_BailChargeThink synced animation logic +#define BAIL_DROP (FRACUNIT) +#define BAIL_BOOST (FRACUNIT) +#define BAIL_CREDIT_DEBTRINGS (true) +#define BAIL_DROPFREQUENCY (2) +#define BAILSTUN (TICRATE*10) + #define MAXCOMBOTHRUST (mapobjectscale*20) #define MAXCOMBOFLOAT (mapobjectscale*10) #define MINCOMBOTHRUST (mapobjectscale*2) @@ -111,6 +118,8 @@ Make sure this matches the actual number of states angle_t K_ReflectAngle(angle_t angle, angle_t against, fixed_t maxspeed, fixed_t yourspeed); +void K_PopBubbleShield(player_t *player); + boolean K_IsDuelItem(mobjtype_t type); boolean K_DuelItemAlwaysSpawns(mapthing_t *mt); boolean K_InRaceDuel(void); @@ -325,7 +334,7 @@ boolean K_ThunderDome(void); boolean K_PlayerCanUseItem(player_t *player); fixed_t K_GetGradingFactorAdjustment(player_t *player); -fixed_t K_GetGradingFactorMinMax(UINT32 gradingpointnum, boolean max); +fixed_t K_GetGradingFactorMinMax(player_t *player, boolean max); UINT16 K_GetEXP(player_t *player); UINT32 K_GetNumGradingPoints(void); @@ -340,6 +349,8 @@ boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2, boolean allowHostile); fixed_t K_TeamComebackMultiplier(player_t *player); +void K_ApplyStun(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 3631441b0..64da0fecd 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -465,8 +465,8 @@ boolean M_Responder(event_t *ev) // Special mid-game input behaviours if (Playing() && !demo.playback) { - // Quick Retry (Y in modeattacking) - if (modeattacking && G_PlayerInputDown(0, gc_respawn, splitscreen + 1) == true) + // Quick Retry (Z in modeattacking) + if (modeattacking && G_PlayerInputDown(0, gc_vote, splitscreen + 1) == true) { M_TryAgain(0); return true; diff --git a/src/k_objects.h b/src/k_objects.h index f5c36da12..03240a089 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -153,6 +153,10 @@ void Obj_ChargeFallThink(mobj_t *charge); void Obj_ChargeReleaseThink(mobj_t *release); void Obj_ChargeExtraThink(mobj_t *extra); +/* Bail VFX */ +void Obj_BailThink(mobj_t *aura); +void Obj_BailChargeThink(mobj_t *aura); + /* Ring Shooter */ boolean Obj_RingShooterThinker(mobj_t *mo); boolean Obj_PlayerRingShooterFreeze(const player_t *player); @@ -440,6 +444,7 @@ void Obj_DestroyedKartParticleLanding(mobj_t *part); void Obj_SpawnFlybotsForPlayer(player_t *player); void Obj_FlybotThink(mobj_t *flybot); void Obj_FlybotDeath(mobj_t *flybot); +void Obj_FlybotRemoved(mobj_t *flybot); /* Pulley */ void Obj_PulleyThink(mobj_t *root); diff --git a/src/k_podium.cpp b/src/k_podium.cpp index d3ee64205..2af6afb35 100644 --- a/src/k_podium.cpp +++ b/src/k_podium.cpp @@ -139,7 +139,7 @@ void podiumData_s::Init(void) constexpr INT32 numRaces = 5; for (INT32 i = 0; i < rank.numPlayers; i++) { - rank.totalPoints += numRaces * K_CalculateGPRankPoints(MAXEXP, i+1, rank.totalPlayers); + rank.totalPoints += numRaces * K_CalculateGPRankPoints(EXP_MAX, i+1, rank.totalPlayers); } rank.totalRings = numRaces * rank.numPlayers * 20; @@ -180,7 +180,7 @@ void podiumData_s::Init(void) } default: { - lvl->totalExp = TARGETEXP; + lvl->totalExp = EXP_TARGET; texp += lvl->totalExp * rank.numPlayers; break; } @@ -203,7 +203,7 @@ void podiumData_s::Init(void) dta->rings = M_RandomRange(0, 20); rgs += dta->rings; - dta->exp = M_RandomRange(MINEXP, MAXEXP); + dta->exp = M_RandomRange(EXP_MIN, EXP_MAX); pexp += dta->exp; } @@ -727,8 +727,8 @@ void podiumData_s::Draw(void) // Colorize the crystal, just like we do for hud skincolornum_t overlaycolor = SKINCOLOR_MUSTARD; fixed_t stablerateinverse = FRACUNIT - EXP_STABLERATE; - INT16 exp_range = MAXEXP-MINEXP; - INT16 exp_offset = dta->exp-MINEXP; + INT16 exp_range = EXP_MAX-EXP_MIN; + INT16 exp_offset = dta->exp-EXP_MIN; fixed_t factor = (exp_offset*FRACUNIT) / exp_range; // 0.0 to 1.0 in fixed // amount of blue is how much factor is above EXP_STABLERATE, and amount of red is how much factor is below // assume that EXP_STABLERATE is within 0.0 to 1.0 in fixed @@ -892,9 +892,9 @@ void podiumData_s::Draw(void) .patch("K_STEXP"); // Colorize the crystal for the totals, just like we do for in race hud - fixed_t extraexpfactor = (MAXEXP*FRACUNIT) / TARGETEXP; + fixed_t extraexpfactor = (EXP_MAX*FRACUNIT) / EXP_TARGET; INT16 totalExpMax = FixedMul(rank.totalExp*FRACUNIT, extraexpfactor) / FRACUNIT; // im just going to calculate it from target lol - INT16 totalExpMin = rank.numPlayers*MINEXP; + INT16 totalExpMin = rank.numPlayers*EXP_MIN; skincolornum_t overlaycolor = SKINCOLOR_MUSTARD; fixed_t stablerateinverse = FRACUNIT - EXP_STABLERATE; INT16 exp_range = totalExpMax-totalExpMin; diff --git a/src/k_powerup.cpp b/src/k_powerup.cpp index 2efd94b84..38ece8439 100644 --- a/src/k_powerup.cpp +++ b/src/k_powerup.cpp @@ -65,9 +65,10 @@ void K_GivePowerUp(player_t* player, kartitems_t powerup, tic_t time) } S_StartSound(NULL, sfx_gsha7l); - player->flashing = 2*TICRATE; + player->flashing = 3*TICRATE; player->mo->hitlag += BATTLE_POWERUP_VFX_TIME; player->powerupVFXTimer = BATTLE_POWERUP_VFX_TIME; + player->mo->flags |= MF_NOCLIPTHING; Obj_SpawnPowerUpSpinner(player->mo, powerup, BATTLE_POWERUP_VFX_TIME); g_darkness.start = leveltime; diff --git a/src/k_rank.cpp b/src/k_rank.cpp index c2e222158..f36e4f40a 100644 --- a/src/k_rank.cpp +++ b/src/k_rank.cpp @@ -322,7 +322,7 @@ void gpRank_t::Init(void) // (Should this account for all coop players?) for (i = 0; i < numHumans; i++) { - totalPoints += grandprixinfo.cup->numlevels * K_CalculateGPRankPoints(MAXEXP, i+1, totalPlayers); + totalPoints += grandprixinfo.cup->numlevels * K_CalculateGPRankPoints(EXP_MAX, i+1, totalPlayers); } totalRings = grandprixinfo.cup->numlevels * numHumans * 20; @@ -332,7 +332,7 @@ void gpRank_t::Init(void) const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[i]; if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum] != NULL) { - exp += TARGETEXP; + exp += EXP_TARGET; } } @@ -372,7 +372,7 @@ void gpRank_t::Rejigger(UINT16 removedmap, UINT16 removedgt, UINT16 addedmap, UI { for (i = 0; i < numPlayers; i++) { - deltaPoints += K_CalculateGPRankPoints(MAXEXP, i + 1, totalPlayers); + deltaPoints += K_CalculateGPRankPoints(EXP_MAX, i + 1, totalPlayers); } if (addedgt == GT_RACE) totalPoints += deltaPoints; @@ -391,7 +391,7 @@ void gpRank_t::Rejigger(UINT16 removedmap, UINT16 removedgt, UINT16 addedmap, UI { if (removedgt == GT_RACE) { - deltaExp -= TARGETEXP; + deltaExp -= EXP_TARGET; } if ((gametypes[removedgt]->rules & GTR_SPHERES) == 0) { @@ -408,7 +408,7 @@ void gpRank_t::Rejigger(UINT16 removedmap, UINT16 removedgt, UINT16 addedmap, UI { if (addedgt == GT_RACE) { - deltaExp += TARGETEXP; + deltaExp += EXP_TARGET; } if ((gametypes[addedgt]->rules & GTR_SPHERES) == 0) { @@ -492,7 +492,7 @@ void gpRank_t::Update(void) lvl->time = UINT32_MAX; - lvl->totalExp = TARGETEXP; + lvl->totalExp = EXP_TARGET; lvl->totalPrisons = maptargets; UINT8 i; @@ -548,13 +548,10 @@ void K_UpdateGPRank(gpRank_t *rankData) rankData->Update(); } -/*-------------------------------------------------- - gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) - - See header file for description. ---------------------------------------------------*/ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) { + INT32 retGrade = GRADE_E; + { extern consvar_t cv_debugrank; @@ -564,6 +561,8 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) } } + fixed_t percent = K_CalculateGPPercent(rankData); + static const fixed_t gradePercents[GRADE_A] = { 7*FRACUNIT/20, // D: 35% or higher 10*FRACUNIT/20, // C: 50% or higher @@ -571,8 +570,31 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) 17*FRACUNIT/20 // A: 85% or higher }; - INT32 retGrade = GRADE_E; + for (retGrade = GRADE_E; retGrade < GRADE_A; retGrade++) + { + if (percent < gradePercents[retGrade]) + { + break; + } + } + if (rankData->specialWon == true) + { + // Winning the Special Stage gives you + // a free grade increase. + retGrade++; + } + + return static_cast(retGrade); +} + +/*-------------------------------------------------- + gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) + + See header file for description. +--------------------------------------------------*/ +fixed_t K_CalculateGPPercent(gpRank_t *rankData) +{ rankData->scorePosition = 0; rankData->scoreGPPoints = 0; rankData->scoreExp = 0; @@ -625,22 +647,8 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) rankData->scoreContinues; const fixed_t percent = FixedDiv(rankData->scoreTotal, total); - for (retGrade = GRADE_E; retGrade < GRADE_A; retGrade++) - { - if (percent < gradePercents[retGrade]) - { - break; - } - } - if (rankData->specialWon == true) - { - // Winning the Special Stage gives you - // a free grade increase. - retGrade++; - } - - return static_cast(retGrade); + return percent; } /*-------------------------------------------------- diff --git a/src/k_rank.h b/src/k_rank.h index aa28727e3..943cb40c0 100644 --- a/src/k_rank.h +++ b/src/k_rank.h @@ -159,6 +159,7 @@ void K_UpdateGPRank(gpRank_t *rankData); --------------------------------------------------*/ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData); +fixed_t K_CalculateGPPercent(gpRank_t *rankData); /*-------------------------------------------------- diff --git a/src/k_tally.cpp b/src/k_tally.cpp index f8e91b46c..ba17f4153 100644 --- a/src/k_tally.cpp +++ b/src/k_tally.cpp @@ -347,7 +347,7 @@ void level_tally_t::Init(player_t *player) if (player->exp) { exp = player->exp; - totalExp = TARGETEXP; + totalExp = EXP_TARGET; } } diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 1a7d199c0..552742d4b 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -262,8 +262,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->tumbleHeight); else if (fastcmp(field,"stunned")) lua_pushinteger(L, plr->stunned); - else if (fastcmp(field,"stunnedcombo")) - lua_pushinteger(L, plr->stunnedCombo); + else if (fastcmp(field,"flybot")) + LUA_PushUserdata(L, plr->flybot, META_MOBJ); else if (fastcmp(field,"justdi")) lua_pushinteger(L, plr->justDI); else if (fastcmp(field,"flipdi")) @@ -284,6 +284,12 @@ static int player_get(lua_State *L) lua_pushboolean(L, plr->progressivethrust); else if (fastcmp(field,"ringvisualwarning")) lua_pushboolean(L, plr->ringvisualwarning); + else if (fastcmp(field,"bailcharge")) + lua_pushinteger(L, plr->bailcharge); + else if (fastcmp(field,"baildrop")) + lua_pushinteger(L, plr->baildrop); + else if (fastcmp(field,"bailquake")) + lua_pushboolean(L, plr->bailquake); else if (fastcmp(field,"dotrickfx")) lua_pushboolean(L, plr->dotrickfx); else if (fastcmp(field,"stingfx")) @@ -896,8 +902,13 @@ static int player_set(lua_State *L) plr->tumbleHeight = luaL_checkinteger(L, 3); else if (fastcmp(field,"stunned")) plr->stunned = luaL_checkinteger(L, 3); - else if (fastcmp(field,"stunnedcombo")) - plr->stunnedCombo = luaL_checkinteger(L, 3); + else if (fastcmp(field,"flybot")) + { + mobj_t *mo = NULL; + if (!lua_isnil(L, 3)) + mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); + P_SetTarget(&plr->flybot, mo); + } else if (fastcmp(field,"justdi")) plr->justDI = luaL_checkinteger(L, 3); else if (fastcmp(field,"flipdi")) @@ -910,6 +921,12 @@ static int player_set(lua_State *L) plr->progressivethrust = luaL_checkboolean(L, 3); else if (fastcmp(field,"ringvisualwarning")) plr->ringvisualwarning = luaL_checkboolean(L, 3); + else if (fastcmp(field,"bailcharge")) + plr->bailcharge = luaL_checkinteger(L, 3); + else if (fastcmp(field,"baildrop")) + plr->baildrop = luaL_checkinteger(L, 3); + else if (fastcmp(field,"bailquake")) + plr->bailquake = luaL_checkboolean(L, 3); else if (fastcmp(field,"analoginput")) plr->analoginput = luaL_checkboolean(L, 3); else if (fastcmp(field,"transfer")) diff --git a/src/m_easing.c b/src/m_easing.c index ef48abe51..e1e225ecd 100644 --- a/src/m_easing.c +++ b/src/m_easing.c @@ -430,3 +430,93 @@ const char *easing_funcnames[EASE_MAX] = #undef COMMA #undef EASINGFUNC + +// ================== +// FEATURE RESCALING +// ================== + +/*-------------------------------------------------- + fixed_t Rescale(fixed_t value, fixed_t inmin, fixed_t inmax, easingfunc_t easing_func, fixed_t outmin, fixed_t outmax) + + Rescales a feature value from [min, max] to [start, end] using + a custom easing function pointer. + + Input Arguments:- + value - The input value to rescale + inmin - Minimum value of the input range + inmax - Maximum value of the input range + easing_func - Pointer to the easing function to use + outmin - Start value of the output range + outmax - End value of the output range + + Return:- + The rescaled value using the specified easing function. +--------------------------------------------------*/ +fixed_t FixedRescale(fixed_t value, fixed_t inmin, fixed_t inmax, easingfunc_t easing_func, fixed_t outmin, fixed_t outmax) +{ + // Handle edge case where min == max + if (inmin == inmax) + return outmin; + + // Clamp the input value to the range + max(inmin, min(inmax, value)); + + // Normalize the value to [0, FRACUNIT] range + fixed_t t = FixedDiv(value - inmin, inmax - inmin); + + // Apply the easing function if provided + if (easing_func != NULL) + { + return easing_func(t, outmin, outmax); + } + + // Fallback to linear if no function provided + return Easing_Linear(t, outmin, outmax); +} + +/*-------------------------------------------------- + INT16 IntRescale(INT16 value, INT16 inmin, INT16 inmax, easingfunc_t easing_func, INT16 outmin, INT16 outmax) + + Rescales a feature value from [min, max] to [start, end] using + a custom easing function pointer. + Can only take in up to INT16 because it uses fixed_t internally + + Input Arguments:- + value - The input value to rescale + inmin - Minimum value of the input range + inmax - Maximum value of the input range + easing_func - Pointer to the easing function to use + outmin - Start value of the output range + outmax - End value of the output range + + Return:- + The rescaled value using the specified easing function. +--------------------------------------------------*/ +INT16 IntRescale(INT16 value, INT16 inmin, INT16 inmax, easingfunc_t easing_func, INT16 outmin, INT16 outmax) +{ + // Handle edge case where min == max + if (inmin == inmax) + return outmin; + + // Clamp the input value to the range + max(inmin, min(inmax, value)); + + // Conversion shit + value = value<>FRACBITS; + } + + // Fallback to linear if no function provided + return Easing_Linear(t, outmin, outmax)>>FRACBITS; +} diff --git a/src/m_easing.h b/src/m_easing.h index 3685ed192..bae0029c6 100644 --- a/src/m_easing.h +++ b/src/m_easing.h @@ -105,6 +105,9 @@ EASINGFUNC(InOutBackParameterized) /* Easing_InOutBackParameterized */ #undef EASINGFUNC +fixed_t FixedRescale(fixed_t value, fixed_t inmin, fixed_t inmax, easingfunc_t easing_func, fixed_t outmin, fixed_t outmax); +INT16 IntRescale(INT16 value, INT16 inmin, INT16 inmax, easingfunc_t easing_func, INT16 outmin, INT16 outmax); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/menus/options-profiles-edit-controls.c b/src/menus/options-profiles-edit-controls.c index 628035f3a..1fc1b914e 100644 --- a/src/menus/options-profiles-edit-controls.c +++ b/src/menus/options-profiles-edit-controls.c @@ -39,10 +39,10 @@ menuitem_t OPTIONS_ProfileControls[] = { {IT_CONTROL, "Brake / Go back", "Brake / Go back", "TLB_X", {.routine = M_ProfileSetControl}, gc_x, 0}, - {IT_CONTROL, "Respawn", "Respawn", + {IT_CONTROL, "Ring Bail", "Ring Bail / Burst", "TLB_Y", {.routine = M_ProfileSetControl}, gc_y, 0}, - {IT_CONTROL, "Action", "Multiplayer quick-chat / quick-vote", + {IT_CONTROL, "Action", "Quick-vote / Quick-chat / Time Attack Quick Restart", "TLB_Z", {.routine = M_ProfileSetControl}, gc_z, 0}, {IT_CONTROL, "Use Item", "Use item", diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 8d339d8f4..7a9cb347e 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -66,6 +66,7 @@ target_sources(SRB2SDL2 PRIVATE flame-shield.cpp stone-shoe.cpp exp.c + bail.c ) add_subdirectory(versus) diff --git a/src/objects/bail.c b/src/objects/bail.c new file mode 100644 index 000000000..4d3d6824d --- /dev/null +++ b/src/objects/bail.c @@ -0,0 +1,80 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by AJ "Tyron" Martinez. +// Copyright (C) 2025 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file bail.c +/// \brief Charge VFX code. + +#include "../doomdef.h" +#include "../info.h" +#include "../k_objects.h" +#include "../info.h" +#include "../k_kart.h" +#include "../p_local.h" +#include "../s_sound.h" + +void Obj_BailThink (mobj_t *aura) +{ + if (P_MobjWasRemoved(aura->target) + || aura->target->health == 0 + || aura->target->destscale <= 1 // sealed star fall out + || !aura->target->player + || P_PlayerInPain(aura->target->player)) // if you got hit when starting to bail, cancel the VFX + { + P_RemoveMobj(aura); + } + else + { + mobj_t *mo = aura->target; + + aura->flags &= ~(MF_NOCLIPTHING); + P_MoveOrigin(aura, mo->x, mo->y, mo->z + mo->height/2); + aura->flags |= MF_NOCLIPTHING; + + fixed_t baseScale = 12*mo->scale/10; + + P_SetScale(aura, baseScale); + + // make target player invisible during the effect, like the retro games + if (aura->state == &states[S_BAIL]) + mo->renderflags |= RF_DONTDRAW; + } +} + +void Obj_BailChargeThink (mobj_t *aura) +{ + if (P_MobjWasRemoved(aura->target) + || aura->target->health == 0 + || aura->target->destscale <= 1 // sealed star fall out + || !aura->target->player + || !aura->target->player->bailcharge) + { + P_RemoveMobj(aura); + } + else + { + mobj_t *mo = aura->target; + player_t *player = mo->player; + + // Follow player + aura->flags &= ~(MF_NOCLIPTHING); + P_MoveOrigin(aura, mo->x, mo->y, mo->z + mo->height/2); + aura->flags |= MF_NOCLIPTHING; + // aura->color = mo->color; + + aura->frame = ((player->bailcharge-1)/2); // By syncing the frame with the charge timer here + + fixed_t baseScale = 13*mo->scale/10; + + P_SetScale(aura, baseScale); + + mobj_t *ghost = P_SpawnGhostMobj(aura); + ghost->renderflags = (ghost->renderflags & ~RF_TRANSMASK)|RF_ADD; + ghost->fuse = 3; + } +} \ No newline at end of file diff --git a/src/objects/flybot767.c b/src/objects/flybot767.c index 790276387..32293aeec 100644 --- a/src/objects/flybot767.c +++ b/src/objects/flybot767.c @@ -43,6 +43,7 @@ void Obj_SpawnFlybotsForPlayer(player_t *player) { UINT8 i; mobj_t *mo = player->mo; + mobj_t *hprev = mo; fixed_t radius = mo->radius; for (i = 0; i < FLYBOT_QUANTITY; i++) @@ -61,6 +62,17 @@ void Obj_SpawnFlybotsForPlayer(player_t *player) flybot->movedir = flybot->old_angle = flybot->angle = angle + ANGLE_90; flybot->old_z = SetFlybotZ(flybot); flybot->renderflags |= (i * RF_DONTDRAW); + + if (hprev->player) + { + P_SetTarget(&player->flybot, flybot); + } + else + { + P_SetTarget(&hprev->hnext, flybot); + P_SetTarget(&flybot->hprev, hprev); + } + hprev = flybot; } } @@ -80,11 +92,18 @@ void Obj_FlybotThink(mobj_t *flybot) if (mo->player) { - if (((stunned = mo->player->stunned & 0x7FFF) == 0) || (mo->player->playerstate == PST_DEAD)) + if (((stunned = mo->player->stunned) == 0) || (mo->player->playerstate == PST_DEAD)) { P_KillMobj(flybot, NULL, NULL, 0); return; } + + // If player is spindashing, spin faster to hint that stun is going down faster + else if (mo->player->spindash) + { + speed *= 2; + flybot->movedir += FLYBOT_BOB_FREQUENCY*2; + } } flybot->frame = flybot->frame & ~FF_TRANSMASK; @@ -120,6 +139,11 @@ void Obj_FlybotDeath(mobj_t *flybot) if (!P_MobjWasRemoved(mo)) { + if (mo->player && (flybot == mo->player->flybot)) + { + P_SetTarget(&mo->player->flybot, NULL); + } + mom.x = mo->momx; mom.y = mo->momy; mom.z = mo->momz; @@ -140,3 +164,12 @@ void Obj_FlybotDeath(mobj_t *flybot) angle += ANGLE_90; } } + +void Obj_FlybotRemoved(mobj_t *flybot) +{ + mobj_t *mo = flybot->target; + if (!P_MobjWasRemoved(mo) && mo->player && (flybot == mo->player->flybot)) + { + P_SetTarget(&mo->player->flybot, NULL); + } +} diff --git a/src/objects/ring-shooter.c b/src/objects/ring-shooter.c index 83613d3c5..2f731a7cd 100644 --- a/src/objects/ring-shooter.c +++ b/src/objects/ring-shooter.c @@ -668,7 +668,7 @@ boolean Obj_PlayerRingShooterFreeze(const player_t *player) const mobj_t *base = player->ringShooter; if (AllowRingShooter(player) == true - && (player->cmd.buttons & BT_RESPAWN) == BT_RESPAWN + && (player->cmd.buttons & BT_RESPAWNMASK) == BT_RESPAWNMASK && P_MobjWasRemoved(base) == false) { return (rs_base_canceled(base) == 0); @@ -682,7 +682,7 @@ void Obj_RingShooterInput(player_t *player) mobj_t *const base = player->ringShooter; if (AllowRingShooter(player) == true - && (player->cmd.buttons & BT_RESPAWN) == BT_RESPAWN) + && (player->cmd.buttons & BT_RESPAWNMASK) == BT_RESPAWNMASK) { // "Freeze" final-failsafe timer if we're eligible to ringshooter, but don't reset it. if (player->finalfailsafe) diff --git a/src/p_enemy.c b/src/p_enemy.c index 3006b7369..03d823eb7 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3503,7 +3503,7 @@ void A_AttractChase(mobj_t *actor) if (actor->extravalue1 && actor->type != MT_EMERALD) // SRB2Kart { - if (!actor->target || P_MobjWasRemoved(actor->target) || !actor->target->player) + if (!actor->target || P_MobjWasRemoved(actor->target) || !actor->target->player || actor->target->player->baildrop || actor->target->player->bailcharge) { P_RemoveMobj(actor); return; @@ -12206,6 +12206,8 @@ void A_BallhogExplode(mobj_t *actor) mo2->destscale = mo2->scale; P_SetTarget(&mo2->target, actor->target); S_StartSound(mo2, actor->info->deathsound); + + actor->fuse = 1; return; } diff --git a/src/p_inter.c b/src/p_inter.c index 04a05ce4c..9af619404 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -723,6 +723,17 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->extravalue1) return; + // No picking up rings while SPB is targetting you + if (player->pflags & PF_RINGLOCK) + return; + + // Prepping instawhip? Don't ruin it by collecting rings + if (player->instaWhipCharge) + return; + + if (player->baildrop || player->bailcharge) + return; + // Don't immediately pick up spilled rings if (special->threshold > 0 || P_PlayerInPain(player) || player->spindash) // player->spindash: Otherwise, players can pick up rings that are thrown out of them from invinc spindash penalty return; @@ -3041,7 +3052,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da UINT8 type = (damagetype & DMG_TYPEMASK); const boolean hardhit = (type == DMG_EXPLODE || type == DMG_KARMA || type == DMG_TUMBLE); // This damage type can do evil stuff like ALWAYS combo INT16 ringburst = 5; - UINT16 stunTics = 0; // Check if the player is allowed to be damaged! // If not, then spawn the instashield effect instead. @@ -3094,6 +3104,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da invincible = false; } + if (player->pflags2 && PF2_ALWAYSDAMAGED) + { + invincible = false; + clash = false; + } + // TODO: doing this from P_DamageMobj limits punting to objects that damage the player. // And it may be kind of yucky. // But this is easier than accounting for every condition in PIT_CheckThing! @@ -3181,6 +3197,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } } + if (inflictor && !P_MobjWasRemoved(inflictor) && inflictor->momx == 0 && inflictor->momy == 0 && inflictor->momz == 0) + { + // Probably a map hazard. + allowcombo = false; + } + if (allowcombo == false && (target->eflags & MFE_PAUSED)) { return false; @@ -3395,18 +3417,22 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da K_PopPlayerShield(player); } - if (!(gametyperules & GTR_SPHERES) && player->tripwireLeniency) + boolean downgraded = false; + + if (!(gametyperules & GTR_SPHERES) && player->tripwireLeniency && !P_PlayerInPain(player)) { switch (type) { case DMG_EXPLODE: type = DMG_TUMBLE; + downgraded = true; break; case DMG_TUMBLE: softenTumble = true; break; case DMG_NORMAL: case DMG_WIPEOUT: + downgraded = true; type = DMG_STUMBLE; player->ringburst += 5; // THERE IS SIMPLY NO HOPE AT THIS POINT K_PopPlayerShield(player); @@ -3453,7 +3479,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da ringburst = 0; } - if (type != DMG_STUMBLE && type != DMG_WHUMBLE) + if ((type != DMG_STUMBLE && type != DMG_WHUMBLE) || (type == DMG_STUMBLE && downgraded)) { if (type != DMG_STING) player->flashing = K_GetKartFlashing(player); @@ -3488,26 +3514,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } // Apply stun! - // Feel free to move these calculations higher up if different damage sources should apply variable stun in future - #define MIN_STUNTICS (4 * TICRATE) - #define MAX_STUNTICS (10 * TICRATE) - stunTics = Easing_Linear((player->kartweight - 1) * FRACUNIT / 8, MAX_STUNTICS, MIN_STUNTICS); - stunTics >>= player->stunnedCombo; // consecutive hits add half as much stun as the previous hit - - // 1/3 base stun values in battle - if (gametyperules & GTR_SPHERES) + if (type != DMG_STING) { - stunTics /= 3; + K_ApplyStun(player, inflictor, source, damage, damagetype); } - if (player->stunnedCombo < UINT8_MAX) - { - player->stunnedCombo++; - } - player->stunned = (player->stunned & 0x8000) | min(0x7FFF, (player->stunned & 0x7FFF) + stunTics); - #undef MIN_STUNTICS - #undef MAX_STUNTICS - K_DefensiveOverdrive(target->player); } } @@ -3612,7 +3623,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da #define RING_LAYER_SIDE_SIZE (3) #define RING_LAYER_SIZE (RING_LAYER_SIDE_SIZE * 2) -static void P_FlingBurst +void P_FlingBurst ( player_t *player, angle_t fa, mobjtype_t objType, diff --git a/src/p_local.h b/src/p_local.h index af4f722bf..6524dd69c 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -549,6 +549,7 @@ void P_RampConstant(const BasicFF_t *FFInfo, INT32 Start, INT32 End); void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source); boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); +void P_FlingBurst(player_t *player, angle_t fa, mobjtype_t objType, tic_t objFuse, fixed_t objScale, INT32 i); void P_PlayerRingBurst(player_t *player, INT32 num_rings); /// \todo better fit in p_user.c void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck); diff --git a/src/p_mobj.c b/src/p_mobj.c index da3e08bc6..389ae45ed 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8923,6 +8923,16 @@ static boolean P_MobjRegularThink(mobj_t *mobj) Obj_BlockBodyThink(mobj); break; } + case MT_BAIL: + { + Obj_BailThink(mobj); + break; + } + case MT_BAILCHARGE: + { + Obj_BailChargeThink(mobj); + break; + } case MT_AMPRING: { Obj_AmpRingThink(mobj); @@ -11855,6 +11865,11 @@ void P_RemoveMobj(mobj_t *mobj) Obj_UnlinkRocks(mobj); break; } + case MT_FLYBOT767: + { + Obj_FlybotRemoved(mobj); + break; + } default: { break; diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 8e6bcc6e8..040370163 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -72,8 +72,7 @@ static savebuffer_t *current_savebuffer; #define ARCHIVEBLOCK_WAYPOINTS 0x7F46498F #define ARCHIVEBLOCK_RNG 0x7FAAB5BD -// Note: This cannot be bigger -// than an UINT16 (for now) +// Note: This cannot have more than 32 entries typedef enum { AWAYVIEW = 0x0001, @@ -93,6 +92,7 @@ typedef enum BARRIER = 0x4000, BALLHOGRETICULE = 0x8000, STONESHOE = 0x10000, + FLYBOT = 0x20000, } player_saveflags; static inline void P_ArchivePlayer(savebuffer_t *save) @@ -368,6 +368,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (players[i].stoneShoe) flags |= STONESHOE; + if (players[i].flybot) + flags |= FLYBOT; + WRITEUINT32(save->p, flags); if (flags & SKYBOXVIEW) @@ -418,6 +421,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (flags & STONESHOE) WRITEUINT32(save->p, players[i].stoneShoe->mobjnum); + if (flags & FLYBOT) + WRITEUINT32(save->p, players[i].flybot->mobjnum); + WRITEUINT32(save->p, (UINT32)players[i].followitem); WRITEUINT32(save->p, players[i].charflags); @@ -464,7 +470,6 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].tumbleBounces); WRITEUINT16(save->p, players[i].tumbleHeight); WRITEUINT16(save->p, players[i].stunned); - WRITEUINT8(save->p, players[i].stunnedCombo); WRITEUINT8(save->p, players[i].justDI); WRITEUINT8(save->p, players[i].flipDI); @@ -667,6 +672,10 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT16(save->p, players[i].progressivethrust); WRITEUINT8(save->p, players[i].ringvisualwarning); + WRITEUINT32(save->p, players[i].bailcharge); + WRITEUINT32(save->p, players[i].baildrop); + WRITEUINT8(save->p, players[i].bailquake); + WRITEUINT8(save->p, players[i].analoginput); WRITEUINT8(save->p, players[i].markedfordeath); @@ -756,7 +765,10 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT32(save->p, players[i].botvars.spindashconfirm); WRITEUINT32(save->p, players[i].botvars.respawnconfirm); WRITEUINT8(save->p, players[i].botvars.roulettePriority); - WRITEUINT32(save->p, players[i].botvars.rouletteTimeout); + WRITEINT32(save->p, players[i].botvars.rouletteTimeout); + WRITEUINT32(save->p, players[i].botvars.predictionError); + WRITEUINT32(save->p, players[i].botvars.recentDeflection); + WRITEUINT32(save->p, players[i].botvars.lastAngle); // itemroulette_t WRITEUINT8(save->p, players[i].itemRoulette.active); @@ -1070,6 +1082,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) if (flags & STONESHOE) players[i].stoneShoe = (mobj_t *)(size_t)READUINT32(save->p); + if (flags & FLYBOT) + players[i].flybot = (mobj_t *)(size_t)READUINT32(save->p); + players[i].followitem = (mobjtype_t)READUINT32(save->p); //SetPlayerSkinByNum(i, players[i].skin); @@ -1117,7 +1132,6 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].tumbleBounces = READUINT8(save->p); players[i].tumbleHeight = READUINT16(save->p); players[i].stunned = READUINT16(save->p); - players[i].stunnedCombo = READUINT8(save->p); players[i].justDI = READUINT8(save->p); players[i].flipDI = (boolean)READUINT8(save->p); @@ -1319,6 +1333,10 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].progressivethrust = READUINT16(save->p); players[i].ringvisualwarning = READUINT8(save->p); + players[i].bailcharge = READUINT32(save->p); + players[i].baildrop = READUINT32(save->p); + players[i].bailquake = READUINT8(save->p); + players[i].analoginput = READUINT8(save->p); players[i].markedfordeath = READUINT8(save->p); @@ -1409,6 +1427,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].botvars.respawnconfirm = READUINT32(save->p); players[i].botvars.roulettePriority = READUINT8(save->p); players[i].botvars.rouletteTimeout = READUINT32(save->p); + players[i].botvars.predictionError = READUINT32(save->p); + players[i].botvars.recentDeflection = READUINT32(save->p); + players[i].botvars.lastAngle = READUINT32(save->p); // itemroulette_t players[i].itemRoulette.active = (boolean)READUINT8(save->p); @@ -6226,6 +6247,11 @@ static void P_RelinkPointers(void) if (!RelinkMobj(&players[i].stoneShoe)) CONS_Debug(DBG_GAMELOGIC, "stoneShoe not found on player %d\n", i); } + if (players[i].flybot) + { + if (!RelinkMobj(&players[i].flybot)) + CONS_Debug(DBG_GAMELOGIC, "flybot not found on player %d\n", i); + } } } diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 9a781ac12..b6bdc8c65 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -8698,7 +8698,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) if (G_IsModeAttackRetrying() && !demo.playback) { nowtime = lastwipetic; - while (G_PlayerInputDown(0, gc_respawn, splitscreen + 1) == true) + while (G_PlayerInputDown(0, gc_vote, splitscreen + 1) == true) { while (!((nowtime = I_GetTime()) - lastwipetic)) { diff --git a/src/p_slopes.c b/src/p_slopes.c index 7ae2567bd..a64ebfaa0 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -1206,11 +1206,15 @@ void P_ButteredSlope(mobj_t *mo) // Let's get the gravity strength for the object... thrust = FixedMul(thrust, abs(P_GetMobjGravity(mo))); - if (mo->friction != ORIG_FRICTION) + fixed_t basefriction = ORIG_FRICTION; + if (mo->player) + basefriction = K_PlayerBaseFriction(mo->player, ORIG_FRICTION); + + if (mo->friction != basefriction && basefriction != 0) { // ... and its friction against the ground for good measure. // (divided by original friction to keep behaviour for normal slopes the same) - thrust = FixedMul(thrust, FixedDiv(mo->friction, ORIG_FRICTION)); + thrust = FixedMul(thrust, FixedDiv(mo->friction, basefriction)); // Sal: Also consider movefactor of players. // We want ice to make slopes *really* funnel you in a specific direction. diff --git a/src/p_spec.c b/src/p_spec.c index ca923d809..e4f3285b6 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -9635,7 +9635,7 @@ void P_DoQuakeOffset(UINT8 view, mappoint_t *viewPos, mappoint_t *offset) quake = quake->next; } - // Add level-based effects. + // Add level-based effects if (P_MobjWasRemoved(viewer->mo) == false && viewer->speed > viewer->mo->scale && P_IsObjectOnGround(viewer->mo) == true) @@ -9664,9 +9664,9 @@ void P_DoQuakeOffset(UINT8 view, mappoint_t *viewPos, mappoint_t *offset) maxShake = FixedMul(mapheaderinfo[gamemap-1]->cameraHeight, mapobjectscale) * 3 / 4; // For 2p SPLITSCREEN SPECIFICALLY: - // The view is pretty narrow, so move it back 1/4th of the way towards default camera height. + // The view is pretty narrow, so move it back 3/20 of the way towards default camera height. else - maxShake = FixedMul((mapheaderinfo[gamemap-1]->cameraHeight*3 + cv_cam_height[view].value)/4, mapobjectscale) * 3 / 4; + maxShake = FixedMul((mapheaderinfo[gamemap-1]->cameraHeight*17 + cv_cam_height[view].value*3)/20, mapobjectscale) * 3 / 4; } if (battle) diff --git a/src/p_user.c b/src/p_user.c index 74a4be388..80169dee6 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3067,7 +3067,7 @@ void P_DemoCameraMovement(camera_t *cam, UINT8 num) } // if you hold Y, you will lock on to displayplayer. (The last player you were ""f12-ing"") - if (cam->freecam && cmd->buttons & BT_RESPAWN) + if (cam->freecam && cmd->buttons & BT_BAIL) { lastp = &players[displayplayers[0]]; // Fun fact, I was trying displayplayers[0]->mo as if it was Lua like an absolute idiot. cam->angle = R_PointToAngle2(cam->x, cam->y, lastp->mo->x, lastp->mo->y); @@ -3369,7 +3369,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (P_CameraThinker(player, thiscam, resetcalled)) return true; - lookback = ( player->cmd.buttons & BT_LOOKBACK ); + lookback = K_GetKartButtons(player) & BT_LOOKBACK; camspeed = cv_cam_speed[num].value; camstill = cv_cam_still[num].value || player->seasaw; // RR: seasaws lock the camera so that it isn't disorienting. @@ -3384,10 +3384,10 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall camheight = FixedMul(mapheaderinfo[gamemap-1]->cameraHeight, cameraScale); // For 2p SPLITSCREEN SPECIFICALLY: - // The view is pretty narrow, so move it back 1/4th of the way towards default camera height. + // The view is pretty narrow, so move it back 3/20 of the way towards default camera height. else { // CONS_Printf( "Camera values: %f / %f / %f \n", FixedToFloat(mapheaderinfo[gamemap-1]->cameraHeight), FixedToFloat(cv_cam_height[num].value), FixedToFloat(cameraScale) ); - camheight = FixedMul((mapheaderinfo[gamemap-1]->cameraHeight*3 + cv_cam_height[num].value)/4, cameraScale); + camheight = FixedMul((mapheaderinfo[gamemap-1]->cameraHeight*17 + cv_cam_height[num].value*3)/20, cameraScale); } } @@ -4564,9 +4564,7 @@ void P_PlayerThink(player_t *player) // Strength counts up to diminish fade. if (player->flashing && player->flashing < UINT16_MAX && - (player->spectator || !P_PlayerInPain(player)) && - // Battle: flashing tics do not decrease in the air - (!(gametyperules & GTR_BUMPERS) || P_IsObjectOnGround(player->mo))) + (player->spectator || !P_PlayerInPain(player))) { player->flashing--; } diff --git a/src/sounds.c b/src/sounds.c index a14aa31b5..bb8c55633 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -778,7 +778,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"kc30", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc31", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc32", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, - {"kc33", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc33", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, //x8away {"kc34", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc35", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc36", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, @@ -805,7 +805,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"kc4b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc4c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Pop-shot"}, {"kc4d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Power up"}, - {"kc4e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc4e", false, 64, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, //x8away {"kc4f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc50", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc51", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, ""}, @@ -1432,18 +1432,18 @@ sfxinfo_t S_sfx[NUMSFX] = {"gshaf", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"gshb0", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"gshb1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, - {"gshb2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"gshb2", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, //x8away {"gshb3", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"gshb4", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"gshb5", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"gshb6", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"gshb7", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"gshb8", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, - {"gshb9", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"gshb9", false, 64, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, //x4away {"gshba", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"gshbb", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"gshbc", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, - {"gshbd", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"gshbd", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, //x8away {"gshbe", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"gshbf", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"gshc0a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, diff --git a/src/y_inter.cpp b/src/y_inter.cpp index 0f966e2e9..449324357 100644 --- a/src/y_inter.cpp +++ b/src/y_inter.cpp @@ -400,14 +400,14 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) if (roundqueue.position == roundqueue.size-1) { // On A rank pace? Then you get a chance for S rank! - gp_rank_e rankforline = K_CalculateGPGrade(&grandprixinfo.rank); + fixed_t rankforline = K_CalculateGPPercent(&grandprixinfo.rank); - data.showrank = (rankforline >= GRADE_A); + data.showrank = (rankforline >= SEALED_STAR_ENTRY); data.linemeter = - (std::min(rankforline, GRADE_A) + (std::min(rankforline, SEALED_STAR_ENTRY) * (2 * TICRATE) - ) / GRADE_A; + ) / SEALED_STAR_ENTRY; // G_NextMap will float you to rank-restricted stages on Master wins. // Fudge the rank display.