Add "Lunatic" and "Maniac" modes
DUDE TOUHOU LMAO
Jokes aside:
* Lunatic = Master difficulty with modifications to make the races
significantly more difficult:
* Introduces a "lunaticmode" boolean to grandprixinfo;
the demoversion has been upped to 0x0010 due to this
* The bot modifier is, at MINIMUM, 2.0, making them aggressive as hell;
Rival bots use a 2.5 modifier
* Bump Spark is always off in this mode
* RUNNERAUGMENT results have their distances significantly shortened;
if a rival bot takes the lead, this distance is shortened even FURTHER
so they don't frontrun against the human player endlessly
* Alt. Invinc shows up earlier as a sort of "mercy" for human players;
it would otherwise not show up until the race was effectively over
* Maniac = Nightmare difficulty (Master at Expert speed)
with Lunatic's changes
This commit is contained in:
parent
271cec1907
commit
474a59ab7c
13 changed files with 157 additions and 18 deletions
|
|
@ -97,6 +97,8 @@ CV_PossibleValue_t gpdifficulty_cons_t[] = {
|
|||
{KARTSPEED_EXPERT, "Expert"},
|
||||
{KARTGP_MASTER, "Master"},
|
||||
{KARTGP_NIGHTMARE, "Nightmare"},
|
||||
{KARTGP_LUNATIC, "Lunatic"},
|
||||
{KARTGP_MANIAC, "Maniac"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -187,6 +187,8 @@ extern CV_PossibleValue_t CV_Natural[];
|
|||
#define KARTSPEED_EXPERT 3
|
||||
#define KARTGP_MASTER 4 // Not a speed setting, gives hard speed with maxed out bots
|
||||
#define KARTGP_NIGHTMARE 5 // Not a speed setting, gives expert speed with maxed out bots
|
||||
#define KARTGP_LUNATIC 6 // Not a speed setting; Master difficulty with some... modifications.
|
||||
#define KARTGP_MANIAC 7 // Not a speed setting; Nightmare difficulty with some... modifications.
|
||||
extern CV_PossibleValue_t kartspeed_cons_t[], gpdifficulty_cons_t[];
|
||||
|
||||
extern consvar_t cv_execversion;
|
||||
|
|
|
|||
|
|
@ -1957,6 +1957,7 @@ void D_SRB2Main(void)
|
|||
grandprixinfo.gamespeed = KARTSPEED_HARD;
|
||||
grandprixinfo.encore = false;
|
||||
grandprixinfo.masterbots = false;
|
||||
grandprixinfo.lunaticmode = false;
|
||||
|
||||
grandprixinfo.gp = true;
|
||||
grandprixinfo.roundnum = 0;
|
||||
|
|
@ -2121,7 +2122,7 @@ void D_SRB2Main(void)
|
|||
if (!gpdifficulty_cons_t[j].strvalue) // reached end of the list with no match
|
||||
{
|
||||
j = atoi(sskill); // assume they gave us a skill number, which is okay too
|
||||
if (j >= KARTSPEED_EASY && j <= KARTGP_NIGHTMARE)
|
||||
if (j >= KARTSPEED_EASY && j <= KARTGP_MANIAC)
|
||||
newskill = (INT16)j;
|
||||
}
|
||||
|
||||
|
|
@ -2137,6 +2138,18 @@ void D_SRB2Main(void)
|
|||
grandprixinfo.masterbots = true;
|
||||
newskill = KARTSPEED_EXPERT;
|
||||
}
|
||||
else if (newskill == KARTGP_LUNATIC)
|
||||
{
|
||||
grandprixinfo.masterbots = true;
|
||||
grandprixinfo.lunaticmode = true;
|
||||
newskill = KARTSPEED_HARD;
|
||||
}
|
||||
else if (newskill == KARTGP_MANIAC)
|
||||
{
|
||||
grandprixinfo.masterbots = true;
|
||||
grandprixinfo.lunaticmode = true;
|
||||
newskill = KARTSPEED_EXPERT;
|
||||
}
|
||||
|
||||
grandprixinfo.gamespeed = newskill;
|
||||
}
|
||||
|
|
@ -2148,6 +2161,14 @@ void D_SRB2Main(void)
|
|||
{
|
||||
newskill = KARTSPEED_EXPERT;
|
||||
}
|
||||
else if (newskill == KARTGP_LUNATIC)
|
||||
{
|
||||
newskill = KARTSPEED_HARD;
|
||||
}
|
||||
else if (newskill == KARTGP_MANIAC)
|
||||
{
|
||||
newskill = KARTSPEED_EXPERT;
|
||||
}
|
||||
|
||||
if (newskill != -1)
|
||||
CV_SetValue(&cv_kartspeed, newskill);
|
||||
|
|
|
|||
|
|
@ -3554,6 +3554,7 @@ static void Command_Map_f(void)
|
|||
{
|
||||
grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value);
|
||||
grandprixinfo.masterbots = false;
|
||||
grandprixinfo.lunaticmode = false;
|
||||
|
||||
if (option_skill)
|
||||
{
|
||||
|
|
@ -3573,7 +3574,7 @@ static void Command_Map_f(void)
|
|||
if (!gpdifficulty_cons_t[j].strvalue) // reached end of the list with no match
|
||||
{
|
||||
INT32 num = atoi(COM_Argv(option_skill + 1)); // assume they gave us a skill number, which is okay too
|
||||
if (num >= KARTSPEED_EASY && num <= KARTGP_NIGHTMARE)
|
||||
if (num >= KARTSPEED_EASY && num <= KARTGP_MANIAC)
|
||||
newskill = (INT16)num;
|
||||
}
|
||||
|
||||
|
|
@ -3589,6 +3590,18 @@ static void Command_Map_f(void)
|
|||
grandprixinfo.gamespeed = KARTSPEED_EXPERT;
|
||||
grandprixinfo.masterbots = true;
|
||||
}
|
||||
else if (newskill == KARTGP_LUNATIC)
|
||||
{
|
||||
grandprixinfo.gamespeed = KARTSPEED_HARD;
|
||||
grandprixinfo.masterbots = true;
|
||||
grandprixinfo.lunaticmode = true;
|
||||
}
|
||||
else if (newskill == KARTGP_MANIAC)
|
||||
{
|
||||
grandprixinfo.gamespeed = KARTSPEED_EXPERT;
|
||||
grandprixinfo.masterbots = true;
|
||||
grandprixinfo.lunaticmode = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
grandprixinfo.gamespeed = newskill;
|
||||
|
|
@ -8483,6 +8496,13 @@ static void KartBumpSpark_OnChange(void)
|
|||
return;
|
||||
}
|
||||
|
||||
if (grandprixinfo.lunaticmode)
|
||||
{
|
||||
CONS_Printf("No, no - lunatics don't need Bump Spark!\n");
|
||||
bumpsparkactive = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (leveltime < starttime)
|
||||
{
|
||||
CONS_Printf(M_GetText("Bump spark type has been changed to \"%s\".\n"), cv_kartbumpspark.string);
|
||||
|
|
|
|||
15
src/g_demo.c
15
src/g_demo.c
|
|
@ -110,7 +110,7 @@ demoghost *ghosts = NULL;
|
|||
// DEMO RECORDING
|
||||
//
|
||||
|
||||
#define DEMOVERSION 0x000F
|
||||
#define DEMOVERSION 0x0010
|
||||
#define DEMOHEADER "\xF0" "BlanReplay" "\x0F"
|
||||
|
||||
#define DF_GHOST 0x01 // This demo contains ghost data too!
|
||||
|
|
@ -268,6 +268,7 @@ typedef struct
|
|||
struct // DF_GRANDPRIX
|
||||
{
|
||||
boolean masterbots;
|
||||
boolean lunaticmode;
|
||||
UINT8 gamespeed;
|
||||
UINT8 eventmode;
|
||||
} grandprix;
|
||||
|
|
@ -704,6 +705,7 @@ static headerstatus_e G_ReadDemoHeader(UINT8 *dp, demoheader_t *header)
|
|||
boolean rapreset = true; // + extended serverinfo length
|
||||
boolean dubs = true; // Multiple voices
|
||||
boolean availabilities = true; // Store player availabilities
|
||||
boolean gplunatic = true; // Grand Prix: Lunatic mode
|
||||
INT32 i;
|
||||
|
||||
// these may not be present in old demo formats, so initialize them
|
||||
|
|
@ -745,6 +747,9 @@ static headerstatus_e G_ReadDemoHeader(UINT8 *dp, demoheader_t *header)
|
|||
/* FALLTHRU */
|
||||
case 0x000E:
|
||||
availabilities = false;
|
||||
/* FALLTHRU */
|
||||
case 0x000F:
|
||||
gplunatic = false;
|
||||
break;
|
||||
|
||||
default: // too old, cannot support.
|
||||
|
|
@ -754,7 +759,7 @@ static headerstatus_e G_ReadDemoHeader(UINT8 *dp, demoheader_t *header)
|
|||
else if (!memcmp(startdp, "\xF0" "KartReplay" "\x0F", 12))
|
||||
{
|
||||
dubs = rapreset = raflag = false;
|
||||
serverinfo = availabilities = false;
|
||||
gplunatic = serverinfo = availabilities = false;
|
||||
switch (header->demoversion)
|
||||
{
|
||||
case 0x0001: // SRB2Kart 1.0.x (only staff ghosts supported)
|
||||
|
|
@ -929,6 +934,10 @@ skipfiles:
|
|||
{
|
||||
header->grandprix.gamespeed = READUINT8(dp);
|
||||
header->grandprix.masterbots = READUINT8(dp) != 0;
|
||||
|
||||
if (gplunatic)
|
||||
header->grandprix.lunaticmode = READUINT8(dp) != 0;
|
||||
|
||||
header->grandprix.eventmode = READUINT8(dp);
|
||||
}
|
||||
|
||||
|
|
@ -2957,6 +2966,7 @@ void G_BeginRecording(void)
|
|||
{
|
||||
WRITEUINT8(demobuf.p, grandprixinfo.gamespeed);
|
||||
WRITEUINT8(demobuf.p, grandprixinfo.masterbots == true);
|
||||
WRITEUINT8(demobuf.p, grandprixinfo.lunaticmode == true);
|
||||
WRITEUINT8(demobuf.p, grandprixinfo.eventmode);
|
||||
}
|
||||
|
||||
|
|
@ -3800,6 +3810,7 @@ void G_DoPlayDemo(char *defdemoname)
|
|||
grandprixinfo.gp = true;
|
||||
grandprixinfo.gamespeed = header.grandprix.gamespeed;
|
||||
grandprixinfo.masterbots = header.grandprix.masterbots;
|
||||
grandprixinfo.lunaticmode = header.grandprix.lunaticmode;
|
||||
grandprixinfo.eventmode = header.grandprix.eventmode;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -578,6 +578,10 @@ fixed_t K_BotMapModifier(void)
|
|||
// fuck it we ball
|
||||
//return 5*FRACUNIT/10;
|
||||
// ...with a bit of customization
|
||||
|
||||
if (grandprixinfo.lunaticmode == true)
|
||||
return std::max(static_cast<fixed_t>(BASELUNATICSPEEDMOD), K_TrackModifierMax());
|
||||
|
||||
return K_TrackModifierMax();
|
||||
|
||||
#if 0
|
||||
|
|
@ -689,13 +693,12 @@ fixed_t K_BotRubberband(const player_t *player)
|
|||
);
|
||||
|
||||
// +/- x0.35
|
||||
const fixed_t rubberStretchiness = FixedMul(
|
||||
FixedDiv(
|
||||
35 * FRACUNIT / 100,
|
||||
K_GetKartGameSpeedScalar(gamespeed)
|
||||
),
|
||||
K_BotMapModifier()
|
||||
);
|
||||
const fixed_t rubberStretchiness =
|
||||
FixedMul(FixedDiv(35 * FRACUNIT / 100, K_GetKartGameSpeedScalar(gamespeed)),
|
||||
((grandprixinfo.lunaticmode == true) && (player->botvars.rival == true))
|
||||
? std::max(BASELUNATICRIVALSPEEDMOD,
|
||||
K_BotMapModifier()) // The rival is faster on Lunatic mode.
|
||||
: K_BotMapModifier());
|
||||
|
||||
// Lv. 1: x0.4 min
|
||||
// Lv. MAX: x0.85 min
|
||||
|
|
|
|||
|
|
@ -57,6 +57,12 @@ extern consvar_t cv_botdrifting;
|
|||
|
||||
#define MAXDRIFTSKILL (FRACUNIT/2)
|
||||
|
||||
// Minimum bot complexity for Lunatic mode.
|
||||
#define BASELUNATICSPEEDMOD (2 * FRACUNIT)
|
||||
|
||||
// 2.5
|
||||
#define BASELUNATICRIVALSPEEDMOD (5 * FRACUNIT / 2)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DRIFTSTATE_AUTO,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ extern struct grandprixinfo
|
|||
UINT8 gamespeed; ///< Copy of gamespeed, just to make sure you can't cheat it with cvars
|
||||
boolean encore; ///< Ditto, but for encore mode
|
||||
boolean masterbots; ///< If true, all bots should be max difficulty (Master Mode)
|
||||
boolean lunaticmode; ///< If true, make this GP especially difficult (Lunatic Mode)
|
||||
boolean initalize; ///< If true, we need to initialize a new session.
|
||||
boolean wonround; ///< If false, then we retry the map instead of going to the next.
|
||||
UINT8 eventmode; ///< See GPEVENT_ constants
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "k_kart.h"
|
||||
#include "k_waypoint.h"
|
||||
#include "k_director.h"
|
||||
#include "k_grandprix.h"
|
||||
#include "k_cluster.hpp"
|
||||
#include "k_itemlist.hpp"
|
||||
#include "k_items.h"
|
||||
|
|
@ -621,6 +622,8 @@ void K_SetIndirectItemCooldown(tic_t cooldown)
|
|||
}
|
||||
}
|
||||
|
||||
#define LUNATIC_RUNNERAUG_CRUNCHER (29127) // FRACUNIT / 2.25
|
||||
|
||||
static INT32 GetItemOdds(kartroulette_t *roulette, kartresult_t *result, UINT8 *forceme)
|
||||
{
|
||||
INT32 newodds;
|
||||
|
|
@ -791,8 +794,23 @@ static INT32 GetItemOdds(kartroulette_t *roulette, kartresult_t *result, UINT8 *
|
|||
if ((flags & KRF_RUNNERAUGMENT) && (result->augcvar[aug_idx] != NULL))
|
||||
{
|
||||
// These odds get stronger as 1st's frontrun increases.
|
||||
const INT32 distFromStart = max(secondToFirst - result->augcvar[aug_idx]->value, 0);
|
||||
const INT32 distRange = (7 * result->augcvar[aug_idx]->value / 2) - result->augcvar[aug_idx]->value;
|
||||
INT32 runner_distval = result->augcvar[aug_idx]->value;
|
||||
|
||||
if (grandprixinfo.lunaticmode == true)
|
||||
{
|
||||
// Lunatic Mode: Divide this distance by 2.25 for
|
||||
// aggressive frontrun prevention.
|
||||
runner_distval = FixedMul(runner_distval, LUNATIC_RUNNERAUG_CRUNCHER);
|
||||
}
|
||||
|
||||
if (roulette->rival_frontrunner == true)
|
||||
{
|
||||
// The rival is frontrunning. Be *especially* vicious against them!
|
||||
runner_distval = (runner_distval / 2);
|
||||
}
|
||||
|
||||
const INT32 distFromStart = max(secondToFirst - runner_distval, 0);
|
||||
const INT32 distRange = (7 * runner_distval / 2) - runner_distval;
|
||||
const INT32 mulMax = 24;
|
||||
|
||||
INT32 multiplier = (distFromStart * mulMax) / distRange;
|
||||
|
|
@ -861,6 +879,8 @@ static INT32 GetItemOdds(kartroulette_t *roulette, kartresult_t *result, UINT8 *
|
|||
return newodds;
|
||||
}
|
||||
|
||||
#undef LUNATIC_RUNNERAUG_CRUNCHER
|
||||
|
||||
void K_KartGetItemOdds(kartroulette_t *roulette, INT32 outodds[static MAXKARTRESULTS])
|
||||
{
|
||||
// Reset forceme
|
||||
|
|
@ -883,6 +903,7 @@ UINT8 K_FindUseodds(const player_t *player, fixed_t mashed, UINT32 pdis, UINT8 b
|
|||
UINT8 distlen = 0;
|
||||
boolean oddsvalid[MAXODDS];
|
||||
boolean rivalodds = false;
|
||||
boolean rivalrunner = false;
|
||||
|
||||
// Unused now, oops :V
|
||||
(void)bestbumper;
|
||||
|
|
@ -893,6 +914,12 @@ UINT8 K_FindUseodds(const player_t *player, fixed_t mashed, UINT32 pdis, UINT8 b
|
|||
rivalodds = true;
|
||||
}
|
||||
|
||||
if (player->bot && player->botvars.rival && player->position <= 1)
|
||||
{
|
||||
// A rival is frontrunning!
|
||||
rivalrunner = true;
|
||||
}
|
||||
|
||||
INT32 itemodds[MAXKARTRESULTS];
|
||||
kartroulette_t roulette = {
|
||||
.pdis = pdis,
|
||||
|
|
@ -904,6 +931,7 @@ UINT8 K_FindUseodds(const player_t *player, fixed_t mashed, UINT32 pdis, UINT8 b
|
|||
.spbrush = spbrush,
|
||||
.bot = player->bot,
|
||||
.rival = rivalodds,
|
||||
.rival_frontrunner = rivalrunner,
|
||||
.inBottom = K_IsPlayerLosing(player),
|
||||
};
|
||||
|
||||
|
|
@ -1466,7 +1494,8 @@ void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
|
|||
.mashed = mashed,
|
||||
.spbrush = spbrush,
|
||||
.bot = player->bot,
|
||||
.rival = player->bot && player->botvars.rival,
|
||||
.rival = ((player->bot && player->botvars.rival) || (K_IsAltShrunk(player))),
|
||||
.rival_frontrunner = ((player->bot && player->botvars.rival) && (player->position <= 1)),
|
||||
.inBottom = K_IsPlayerLosing(player),
|
||||
};
|
||||
|
||||
|
|
@ -1535,7 +1564,16 @@ void K_SetPlayerItemCooldown(player_t *player, tic_t timer, boolean force)
|
|||
INT32 KO_AltInvinOdds(INT32 odds, const kartroulette_t *roulette, const kartresult_t *result, UINT8 *forceme)
|
||||
{
|
||||
(void)result;
|
||||
odds = K_KartGetInvincibilityOdds(roulette->clusterDist);
|
||||
|
||||
UINT32 cdist = roulette->clusterDist;
|
||||
|
||||
if (grandprixinfo.lunaticmode)
|
||||
{
|
||||
// I'm tired, boss.
|
||||
cdist = (9 * cdist / 5);
|
||||
}
|
||||
|
||||
odds = K_KartGetInvincibilityOdds(cdist);
|
||||
|
||||
// Special case: if you're SERIOUSLY far behind before the cooldown finishes, ignore it and start forcing,
|
||||
if (odds >= INVFORCEODDS)
|
||||
|
|
@ -1565,12 +1603,26 @@ INT32 KO_SPBRaceOdds(INT32 odds, const kartroulette_t *roulette, const kartresul
|
|||
odds = 0;
|
||||
}
|
||||
|
||||
UINT32 raceforce_pdis = roulette->pdis;
|
||||
|
||||
if (grandprixinfo.lunaticmode == true)
|
||||
{
|
||||
// Multiply by 2.25
|
||||
raceforce_pdis = FixedMul(raceforce_pdis, 9 * FRACUNIT / 2);
|
||||
}
|
||||
|
||||
if (roulette->rival_frontrunner == true)
|
||||
{
|
||||
// Frontrunning rival? Multiply by 1.5
|
||||
raceforce_pdis = FixedMul(raceforce_pdis, 3 * FRACUNIT / 2);
|
||||
}
|
||||
|
||||
// No forced SPB in 1v1s, it has to be randomly rolled
|
||||
if (roulette->pingame <= 2)
|
||||
{
|
||||
*forceme = 0;
|
||||
}
|
||||
else if (K_RaceForceSPB(roulette->playerpos, roulette->pdis)
|
||||
else if (K_RaceForceSPB(roulette->playerpos, raceforce_pdis)
|
||||
&& spbplace == -1 && K_GetKartResult("selfpropelledbomb")->cooldown == 0)
|
||||
{
|
||||
// Force SPB onto 2nd if they get too far behind.
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ struct kartroulette_t
|
|||
boolean spbrush;
|
||||
boolean bot;
|
||||
boolean rival;
|
||||
boolean rival_frontrunner; // Is a Rival bot frontrunning?
|
||||
boolean inBottom;
|
||||
|
||||
// output: which results are being forced into a player's item slot for one reason or another. Higher value = higher priority.
|
||||
|
|
|
|||
10
src/k_kart.c
10
src/k_kart.c
|
|
@ -2532,7 +2532,15 @@ UINT16 K_GetInvincibilityTime(player_t *player)
|
|||
distmul = LEGACYALTINVINMUL;
|
||||
}
|
||||
|
||||
fixed_t clustermul = K_InvincibilityEasing(FixedMul(player->distancefromcluster,distmul));
|
||||
UINT32 cdist = player->distancefromcluster;
|
||||
|
||||
if (grandprixinfo.lunaticmode)
|
||||
{
|
||||
// I'm tired, boss.
|
||||
cdist = (9 * cdist / 5);
|
||||
}
|
||||
|
||||
fixed_t clustermul = K_InvincibilityEasing(FixedMul(cdist,distmul));
|
||||
UINT16 invintics = FixedMul(BASEINVINTIME, clustermul);
|
||||
|
||||
return max(MININVINTIME, invintics);
|
||||
|
|
|
|||
10
src/m_menu.c
10
src/m_menu.c
|
|
@ -6259,6 +6259,16 @@ INT32 MR_StartGrandPrix(INT32 choice)
|
|||
grandprixinfo.gamespeed = KARTSPEED_EXPERT;
|
||||
grandprixinfo.masterbots = true;
|
||||
break;
|
||||
case KARTGP_LUNATIC:
|
||||
grandprixinfo.gamespeed = KARTSPEED_HARD;
|
||||
grandprixinfo.lunaticmode = true;
|
||||
grandprixinfo.masterbots = true;
|
||||
break;
|
||||
case KARTGP_MANIAC:
|
||||
grandprixinfo.gamespeed = KARTSPEED_EXPERT;
|
||||
grandprixinfo.lunaticmode = true;
|
||||
grandprixinfo.masterbots = true;
|
||||
break;
|
||||
default:
|
||||
CONS_Alert(CONS_WARNING, "Invalid GP difficulty\n");
|
||||
grandprixinfo.gamespeed = KARTSPEED_NORMAL;
|
||||
|
|
|
|||
|
|
@ -8230,7 +8230,9 @@ static void P_InitLevelSettings(boolean reloadinggamestate)
|
|||
if (cv_itemlist.value)
|
||||
itemlistactive = true;
|
||||
|
||||
bumpsparkactive = (UINT8)cv_kartbumpspark.value;
|
||||
// Lunatics don't need Bump Spark!
|
||||
if (grandprixinfo.lunaticmode == false)
|
||||
bumpsparkactive = (UINT8)cv_kartbumpspark.value;
|
||||
|
||||
antibumptime = (tic_t)cv_kartantibump.value * TICRATE;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue