diff --git a/src/d_player.h b/src/d_player.h index f2edadeee..039ec2390 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -418,6 +418,7 @@ struct botvars_t UINT8 difficulty; // Bot's difficulty setting INT16 diffincrease; // In GP: bot difficulty will increase this much next round boolean rival; // If true, they're the GP rival + boolean foe; // If true, in contention for top X // All entries above persist between rounds and must be recorded in demos diff --git a/src/g_demo.cpp b/src/g_demo.cpp index 172309016..17e095e29 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -321,6 +321,7 @@ void G_ReadDemoExtraData(void) players[p].botvars.difficulty = READUINT8(demobuf.p); players[p].botvars.diffincrease = READINT16(demobuf.p); // needed to avoid having to duplicate logic players[p].botvars.rival = (boolean)READUINT8(demobuf.p); + players[p].botvars.foe = (boolean)READUINT8(demobuf.p); } } if (extradata & DXD_PLAYSTATE) @@ -497,6 +498,7 @@ void G_WriteDemoExtraData(void) WRITEUINT8(demobuf.p, players[i].botvars.difficulty); WRITEINT16(demobuf.p, players[i].botvars.diffincrease); // needed to avoid having to duplicate logic WRITEUINT8(demobuf.p, (UINT8)players[i].botvars.rival); + WRITEUINT8(demobuf.p, (UINT8)players[i].botvars.foe); } } if (demo_extradata[i] & DXD_PLAYSTATE) @@ -2111,6 +2113,7 @@ void G_BeginRecording(void) WRITEUINT8(demobuf.p, player->botvars.difficulty); WRITEINT16(demobuf.p, player->botvars.diffincrease); // needed to avoid having to duplicate logic WRITEUINT8(demobuf.p, (UINT8)player->botvars.rival); + WRITEUINT8(demobuf.p, (UINT8)player->botvars.foe); } // Name @@ -3222,6 +3225,7 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) players[p].botvars.difficulty = READUINT8(demobuf.p); players[p].botvars.diffincrease = READINT16(demobuf.p); // needed to avoid having to duplicate logic players[p].botvars.rival = (boolean)READUINT8(demobuf.p); + players[p].botvars.foe = (boolean)READUINT8(demobuf.p); } K_UpdateShrinkCheat(&players[p]); diff --git a/src/g_game.c b/src/g_game.c index e59199a08..1b5b5208f 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2271,6 +2271,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) INT16 botdiffincrease; boolean botrival; + boolean botfoe; boolean cangrabitems; @@ -2381,6 +2382,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) botdiffincrease = players[player].botvars.diffincrease; botrival = players[player].botvars.rival; + botfoe = players[player].botvars.foe; totalring = players[player].totalring; xtralife = players[player].xtralife; @@ -2641,6 +2643,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->spheres = spheres; p->botvars.diffincrease = botdiffincrease; p->botvars.rival = botrival; + p->botvars.foe = botfoe; p->xtralife = xtralife; // SRB2kart diff --git a/src/k_bot.cpp b/src/k_bot.cpp index bb4cb3e5c..d8da81725 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -408,6 +408,8 @@ void K_UpdateMatchRaceBots(void) clear_bots(wantedbots); } + K_AssignFoes(); + // We should have enough bots now :) #ifdef HAVE_DISCORDRPC @@ -615,7 +617,7 @@ fixed_t K_BotMapModifier(void) --------------------------------------------------*/ static UINT32 K_BotRubberbandDistance(const player_t *player) { - const UINT32 spacing = FixedDiv(640 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; + UINT32 spacing = FixedDiv(640 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; const UINT8 portpriority = player - players; UINT8 pos = 1; UINT8 i; @@ -626,6 +628,11 @@ static UINT32 K_BotRubberbandDistance(const player_t *player) return 0; } + /* + if (player->botvars.foe) + spacing /= 2; + */ + for (i = 0; i < MAXPLAYERS; i++) { if (i == portpriority) @@ -649,6 +656,11 @@ static UINT32 K_BotRubberbandDistance(const player_t *player) continue; } + if (player->botvars.foe && !players[i].botvars.foe) + { + continue; + } + // First check difficulty levels, then score, then settle it with port priority! if (player->botvars.difficulty < players[i].botvars.difficulty) { @@ -694,6 +706,8 @@ fixed_t K_BotRubberband(const player_t *player) { UINT8 levelreduce = std::min(3, player->botvars.difficulty/4); // How much to drop the "effective level" of bots that are consistently behind expreduce = Easing_Linear((K_EffectiveGradingFactor(player) - MINGRADINGFACTOR) * 2, levelreduce*FRACUNIT, 0); + if (player->botvars.foe) + expreduce /= 2; } fixed_t difficultyEase = (((player->botvars.difficulty - 1) * FRACUNIT) - expreduce) / (MAXBOTDIFFICULTY - 1); @@ -805,7 +819,13 @@ fixed_t K_BotRubberband(const player_t *player) scaled_dist = FixedDiv(scaled_dist, mapobjectscale); } - constexpr UINT32 END_DIST = 2048 * 14; + UINT32 END_DIST = 2048 * 14; + + if (K_EffectiveGradingFactor(player) <= FRACUNIT) + { + END_DIST = Easing_Linear((K_EffectiveGradingFactor(player) - MINGRADINGFACTOR) * 2, END_DIST * 2, END_DIST); + } + if (scaled_dist < END_DIST) { // At the end of tracks, start slowing down. @@ -823,7 +843,7 @@ 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; @@ -2146,7 +2166,7 @@ void K_UpdateBotGameplayVars(player_t *player) 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); diff --git a/src/k_botitem.cpp b/src/k_botitem.cpp index 360c80c31..70cd7eb29 100644 --- a/src/k_botitem.cpp +++ b/src/k_botitem.cpp @@ -299,7 +299,7 @@ static boolean K_RivalBotAggression(const player_t *bot, const player_t *target) return false; } - if (bot->botvars.rival == false && !cv_levelskull.value) + if (!(bot->botvars.rival || bot->botvars.foe) && !cv_levelskull.value) { // Not the rival, we aren't self-aware. return false; diff --git a/src/k_grandprix.cpp b/src/k_grandprix.cpp index d990dd94a..041d092f8 100644 --- a/src/k_grandprix.cpp +++ b/src/k_grandprix.cpp @@ -98,6 +98,113 @@ static UINT8 K_GetOffsetStartingDifficulty(const UINT8 startingdifficulty, UINT8 return startingdifficulty - offset; } +/*-------------------------------------------------- + static INT16 K_RivalScore(player_t *bot) + + Creates a "rival score" for a bot, used to determine which bot is the + most deserving of the rival status. + + Input Arguments:- + bot - Player to check. + + Return:- + "Rival score" value. +--------------------------------------------------*/ +static INT16 K_RivalScore(player_t *bot) +{ + const UINT16 difficulty = bot->botvars.difficulty; + const UINT16 score = bot->score; + SINT8 roundnum = 1, roundsleft = 1; + UINT16 lowestscore = UINT16_MAX; + UINT8 lowestdifficulty = MAXBOTDIFFICULTY; + UINT8 i; + + if (grandprixinfo.cup != NULL && roundqueue.size > 0) + { + roundnum = roundqueue.roundnum; + roundsleft = grandprixinfo.cup->numlevels - roundnum; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + { + continue; + } + + if (players[i].score < lowestscore) + { + lowestscore = players[i].score; + } + + if (players[i].bot == true && players[i].botvars.difficulty < lowestdifficulty) + { + lowestdifficulty = players[i].botvars.difficulty; + } + } + + // In the early game, difficulty is more important. + // This will try to influence the higher difficulty bots to get rival more often & get even more points. + // However, when we're running low on matches left, we need to focus more on raw score! + + return ((difficulty - lowestdifficulty) * roundsleft) + ((score - lowestscore) * roundnum); +} + +static boolean CompareRivals(player_t *a, player_t *b) +{ + if (a == NULL) + return false; + if (b == NULL) + return true; + + if (K_RivalScore(a) != K_RivalScore(b)) + { + // Rival Score is HIGH when bots are strong. Sort them first! + return (K_RivalScore(a) > K_RivalScore(b)); + } + + // Fuck it + return a > b; +} + +void K_AssignFoes(void) +{ + std::vector bots; + boolean addedplayer = false; + + for (UINT8 i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] == false) + continue; + + player_t *player = &players[i]; + + if (!player->spectator && player->bot) + { + addedplayer = true; + bots.push_back(player); + player->botvars.foe = false; + } + } + + // Why the fuck is this blowing up sometimes + if (!addedplayer) + return; + + std::stable_sort(bots.begin(), bots.end(), CompareRivals); + + UINT8 i = 0; + for (auto &bot : bots) + { + if (bot != NULL) + bot->botvars.foe = true; + + i++; + if (i > 2) + break; + } +} + /*-------------------------------------------------- void K_InitGrandPrixBots(void) @@ -254,6 +361,8 @@ void K_InitGrandPrixBots(void) { break; } + if (i <= 2) + players[newplayernum-1].botvars.foe = true; } } @@ -289,64 +398,13 @@ void K_LoadGrandPrixSaveGame(void) K_SetBot(i, savedata.bots[i].skin, savedata.bots[i].difficulty, BOT_STYLE_NORMAL); players[i].botvars.rival = savedata.bots[i].rival; + players[i].botvars.foe = savedata.bots[i].foe; players[i].score = savedata.bots[i].score; players[i].spectator = K_BotDefaultSpectator(); } } -/*-------------------------------------------------- - static INT16 K_RivalScore(player_t *bot) - - Creates a "rival score" for a bot, used to determine which bot is the - most deserving of the rival status. - - Input Arguments:- - bot - Player to check. - - Return:- - "Rival score" value. ---------------------------------------------------*/ -static INT16 K_RivalScore(player_t *bot) -{ - const UINT16 difficulty = bot->botvars.difficulty; - const UINT16 score = bot->score; - SINT8 roundnum = 1, roundsleft = 1; - UINT16 lowestscore = UINT16_MAX; - UINT8 lowestdifficulty = MAXBOTDIFFICULTY; - UINT8 i; - - if (grandprixinfo.cup != NULL && roundqueue.size > 0) - { - roundnum = roundqueue.roundnum; - roundsleft = grandprixinfo.cup->numlevels - roundnum; - } - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - { - continue; - } - - if (players[i].score < lowestscore) - { - lowestscore = players[i].score; - } - - if (players[i].bot == true && players[i].botvars.difficulty < lowestdifficulty) - { - lowestdifficulty = players[i].botvars.difficulty; - } - } - - // In the early game, difficulty is more important. - // This will try to influence the higher difficulty bots to get rival more often & get even more points. - // However, when we're running low on matches left, we need to focus more on raw score! - - return ((difficulty - lowestdifficulty) * roundsleft) + ((score - lowestscore) * roundnum); -} - /*-------------------------------------------------- void K_UpdateGrandPrixBots(void) @@ -379,6 +437,8 @@ void K_UpdateGrandPrixBots(void) return; } + K_AssignFoes(); + // Find the rival. for (i = 0; i < MAXPLAYERS; i++) { @@ -639,7 +699,7 @@ void K_IncreaseBotDifficulty(player_t *bot) // RELAXED MODE: // Continues don't drop bot difficulty, because we always advance. // Bots will still level up from standard advancement; we need a - // much steeper rank nudge to keep difficulty at the right level. + // much steeper rank nudge to keep difficulty at the right level. if (grandprixinfo.gamespeed == KARTSPEED_EASY) { switch(averageRank) @@ -689,8 +749,8 @@ static boolean CompareJoiners(player_t *a, player_t *b) return (a->spectatewait < b->spectatewait); } - // They are equals, so just randomize - return (P_Random(PR_BOTS) & 1); + // Fuck it + return a > b; } static boolean CompareReplacements(player_t *a, player_t *b) @@ -707,8 +767,8 @@ static boolean CompareReplacements(player_t *a, player_t *b) return (a->position < b->position); } - // They are equals, so just randomize - return (P_Random(PR_BOTS) & 1); + // Fuck it + return a > b; } /*-------------------------------------------------- diff --git a/src/k_grandprix.h b/src/k_grandprix.h index 8b62187dc..b3e1620bd 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -200,6 +200,8 @@ boolean K_CanChangeRules(boolean allowdemos); boolean K_BotDefaultSpectator(void); +void K_AssignFoes(void); + #ifdef __cplusplus } // extern "C" diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 89a5e7fb6..5ad635659 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 { @@ -5346,8 +5346,10 @@ static void K_DrawCPUTagForPlayer(fixed_t x, fixed_t y, player_t *p, UINT32 flag K_DrawNameTagItemSpy(barx, bary, p, flags); } + UINT8 *foecol = R_GetTranslationColormap(TC_RAINBOW, static_cast(SKINCOLOR_RED), GTC_CACHE); + UINT8 blink = ((leveltime / 7) & 1); - V_DrawFixedPatch(x, y, FRACUNIT, flags, kp_cpu[blink], NULL); + V_DrawFixedPatch(x, y, FRACUNIT, flags, kp_cpu[blink], (p->botvars.foe) ? foecol : NULL); } static void K_DrawNameTagForPlayer(fixed_t x, fixed_t y, player_t *p, UINT32 flags) @@ -5856,13 +5858,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 +5874,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); @@ -7314,7 +7316,7 @@ static void K_DrawBotDebugger(void) V_DrawSmallString(8, 14, 0, va("Difficulty: %d / %d", bot->botvars.difficulty, MAXBOTDIFFICULTY)); V_DrawSmallString(8, 18, 0, va("Difficulty increase: %d", bot->botvars.diffincrease)); - V_DrawSmallString(8, 22, 0, va("Rival: %d", (UINT8)(bot->botvars.rival == true))); + V_DrawSmallString(8, 22, 0, va("Rival / Foe: %d / %d", (UINT8)(bot->botvars.rival == true), (UINT8)(bot->botvars.foe == true))); V_DrawSmallString(8, 26, 0, va("Rubberbanding: %.02f", FIXED_TO_FLOAT(bot->botvars.rubberband) * 100.0f)); V_DrawSmallString(8, 32, 0, va("Item delay: %d", bot->botvars.itemdelay)); diff --git a/src/k_kart.c b/src/k_kart.c index 9aa3f45da..216c182bd 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 && @@ -1624,6 +1624,8 @@ static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed // Double speed for the rival! if (player->botvars.rival || cv_levelskull.value) player->draftpower += add; + else if (player->botvars.foe) + player->draftpower += add/2; else if (dest->player->bot) // Reduce bot gluts. player->draftpower -= 3*add/4; } @@ -3557,6 +3559,10 @@ static fixed_t K_RingDurationBoost(const player_t *player) // x2.0 for Rival ret *= 2; } + else if (player->botvars.foe) + { + ret = 3 * ret / 2; + } } return ret; @@ -3773,7 +3779,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 +3807,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; @@ -3979,6 +3985,11 @@ fixed_t K_GetKartSpeed(const player_t *player, boolean doboostpower, boolean dor // +10% top speed for the rival finalspeed = FixedMul(finalspeed, 11*FRACUNIT/10); } + else if (player->bot && player->botvars.foe) + { + // +5% for foes + finalspeed = FixedMul(finalspeed, 21*FRACUNIT/20); + } } } @@ -4302,8 +4313,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 +4373,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 +4438,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 +6683,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 +9369,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 +9560,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); @@ -9667,7 +9678,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) sub = diff * (sub > 0 ? 1 : -1); player->mo->momz -= sub; } - + } else { @@ -10093,7 +10104,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 +10115,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 +10185,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; } @@ -13728,7 +13739,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; } @@ -14037,7 +14048,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. @@ -14057,7 +14068,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, 13*award/10, 18*award/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) { UINT8 maxgrade = 10; @@ -14197,7 +14208,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)) @@ -14831,7 +14842,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); @@ -14842,7 +14853,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; @@ -14898,7 +14909,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)); @@ -15082,14 +15093,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; @@ -16358,7 +16369,7 @@ boolean K_PlayerCanUseItem(player_t *player) return (player->mo->health > 0 && !player->spectator && !P_PlayerInPain(player) && !mapreset && leveltime > introtime); } -// === +// === // THE EXP ZONE // === @@ -16372,7 +16383,7 @@ static boolean K_IsValidOpponent(player_t *me, player_t *them) return false; if (G_SameTeam(me, them)) return false; - + return true; } @@ -16464,7 +16475,7 @@ UINT16 K_GetEXP(player_t *player) UINT16 exp = FixedRescale(player->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", + // 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; @@ -16478,7 +16489,7 @@ UINT32 K_GetNumGradingPoints(void) return numlaps * (1 + Obj_GetCheckpointCount()); } -// === +// === // END EXP ZONE // === @@ -16497,7 +16508,7 @@ boolean K_IsPickMeUpItem(mobjtype_t type) extern consvar_t cv_debugpickmeup; if (cv_debugpickmeup.value) return false; - + switch (type) { case MT_JAWZ: @@ -16676,7 +16687,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; @@ -16703,7 +16714,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_saveg.cpp b/src/p_saveg.cpp index e2a7fd530..36d681d4d 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -138,6 +138,7 @@ static inline void P_ArchivePlayer(savebuffer_t *save) WRITEUINT8(save->p, players[i].botvars.difficulty); WRITEUINT8(save->p, (UINT8)players[i].botvars.rival); + WRITEUINT8(save->p, (UINT8)players[i].botvars.foe); WRITEUINT32(save->p, players[i].score); } @@ -195,6 +196,7 @@ static boolean P_UnArchivePlayer(savebuffer_t *save) savedata.bots[pid].difficulty = READUINT8(save->p); savedata.bots[pid].rival = (boolean)READUINT8(save->p); + savedata.bots[pid].foe = (boolean)READUINT8(save->p); savedata.bots[pid].score = READUINT32(save->p); } @@ -765,6 +767,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].botvars.difficulty); WRITEUINT8(save->p, players[i].botvars.diffincrease); WRITEUINT8(save->p, players[i].botvars.rival); + WRITEUINT8(save->p, players[i].botvars.foe); WRITEFIXED(save->p, players[i].botvars.rubberband); WRITEUINT8(save->p, players[i].botvars.bumpslow); WRITEUINT32(save->p, players[i].botvars.itemdelay); @@ -1430,6 +1433,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].botvars.difficulty = READUINT8(save->p); players[i].botvars.diffincrease = READUINT8(save->p); players[i].botvars.rival = (boolean)READUINT8(save->p); + players[i].botvars.foe = (boolean)READUINT8(save->p); players[i].botvars.rubberband = READFIXED(save->p); players[i].botvars.bumpslow = READUINT8(save->p); players[i].botvars.itemdelay = READUINT32(save->p); diff --git a/src/p_saveg.h b/src/p_saveg.h index f0beb35e3..1848550ad 100644 --- a/src/p_saveg.h +++ b/src/p_saveg.h @@ -46,6 +46,7 @@ struct savedata_bot_s UINT8 skin; UINT8 difficulty; boolean rival; + boolean foe; UINT32 score; };