Custom gametype and gametyperules additions

Many new options have been added.

You can now specify "menucolor" when creating a gametype. This accepts a V_ colorchar flag (IE V_PURPLEMAP) and will be used to color hud elements in menus and on the HUD.

Encore is now controlled by GTR_ENCORE. This allows for custom modes that don't accept encore.

Itemodds are now controlled by GTR_RACEODDS and GTR_BATTLEODDS respectively. If neither is specified on gametype creation, completely random items will be rolled.

General GTR_FREEROAM adjustments
This commit is contained in:
NepDisk 2025-03-02 11:35:42 -05:00
parent 9458cbbaa2
commit b0bbf07bdb
14 changed files with 150 additions and 99 deletions

View file

@ -3134,7 +3134,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
else if (gametype != lastgametype)
D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
if (!(gametyperules & GTR_CIRCUIT) && !bossinfo.boss)
if (!(gametyperules & GTR_ENCORE) && !bossinfo.boss)
pencoremode = false;
skipprecutscene = ((flags & (1<<2)) != 0);

View file

@ -35,6 +35,7 @@
#include "fastcmp.h"
#include "lua_script.h" // Reluctantly included for LUA_EvalMath
#include "d_clisrv.h"
#include "v_video.h"
#ifdef HWRENDER
#include "hardware/hw_light.h"
@ -925,6 +926,7 @@ void readgametype(MYFILE *f, char *gtname)
INT32 newgtpointlimit = 0;
INT32 newgttimelimit = 0;
INT16 newgtrankingstype = -1;
INT32 newgtcolor = V_YELLOWMAP;
int newgtinttype = 0;
char gtconst[MAXLINELEN];
@ -1008,6 +1010,13 @@ void readgametype(MYFILE *f, char *gtname)
newgttol = tol;
}
}
// Menu Color
else if (fastcmp(word, "MENUCOLOR"))
{
INT32 color = (INT32)get_number(word2);
// Mask out other flags so they aren't passed into menu code.
newgtcolor = color & V_CHARCOLORMASK; // V_
}
// The SOC probably provided gametype rules as words,
// instead of using the RULES keyword.
// Like for example "NOSPECTATORSPAWN = TRUE".
@ -1051,6 +1060,7 @@ void readgametype(MYFILE *f, char *gtname)
intermissiontypes[newgtidx] = newgtinttype;
pointlimits[newgtidx] = newgtpointlimit;
timelimits[newgtidx] = newgttimelimit;
gametypecolor[newgtidx] = newgtcolor;
// Write the new gametype name.
Gametype_Names[newgtidx] = Z_StrDup((const char *)gtname);

View file

@ -296,38 +296,29 @@ const char *const ITEMFLAG_LIST[] = {
};
const char *const GAMETYPERULE_LIST[] = {
"CAMPAIGN",
"RINGSLINGER",
"SPECTATORS",
"LIVES",
"TEAMS",
"FIRSTPERSON",
"POWERSTONES",
"TEAMFLAGS",
"FRIENDLY",
"SPECIALSTAGES",
"EMERALDTOKENS",
"EMERALDHUNT",
"RACE",
"TAG",
"CIRCUIT",
"RACEODDS",
"BOTS",
"RINGS",
"BUMPERS",
"BATTLEODDS",
"PAPERITEMS",
"WANTED",
"KARMA",
"ITEMARROWS",
"ITEMBREAKER",
"BATTLESTARTS",
"POINTLIMIT",
"TIMELIMIT",
"OVERTIME",
"HURTMESSAGES",
"FRIENDLYFIRE",
"STARTCOUNTDOWN",
"HIDEFROZEN",
"BLINDFOLDED",
"RESPAWNDELAY",
"PITYSHIELD",
"DEATHPENALTY",
"NOSPECTATORSPAWN",
"DEATHMATCHSTARTS",
"SPAWNINVUL",
"SPAWNENEMIES",
"ALLOWEXIT",
"NOTITLECARD",
"CUTSCENES",
"TEAMS",
"NOTEAMS",
"TEAMSTARTS",
"\x01",
"LIVES",
"SPECIALBOTS",
"FREEROAM",
"ENCORE",
NULL
};

View file

@ -493,34 +493,37 @@ enum GameTypeRules
{
// Race rules
GTR_CIRCUIT = 1, // Enables the finish line, laps, and the waypoint system.
GTR_BOTS = 1<<2, // Allows bots in this gametype. Combine with BotTiccmd hooks to make bots support your gametype.
GTR_RINGS = 1<<3, // Allow Rings in this gametype.
GTR_RACEODDS = 1<<2, // ItemOdds used in this mode are for racing.
GTR_BOTS = 1<<3, // Allows bots in this gametype. Combine with BotTiccmd hooks to make bots support your gametype.
GTR_RINGS = 1<<4, // Allow Rings in this gametype.
// Battle gametype rules
GTR_BUMPERS = 1<<4, // Enables the bumper health system
GTR_PAPERITEMS = 1<<5, // Replaces item boxes with paper item spawners
GTR_WANTED = 1<<6, // Enables the wanted anti-camping system
GTR_KARMA = 1<<7, // Enables the Karma system if you're out of bumpers
GTR_ITEMARROWS = 1<<8, // Show item box arrows above players
GTR_ITEMBREAKER = 1<<9, // Enables the use of Item Breaker in this Gamemode
GTR_BATTLESTARTS = 1<<10, // Use Battle Mode start positions.
GTR_BUMPERS = 1<<5, // Enables the bumper health system
GTR_BATTLEODDS = 1<<6, // ItemOdds used in this mode are for battling.
GTR_PAPERITEMS = 1<<7, // Replaces item boxes with paper item spawners
GTR_WANTED = 1<<8, // Enables the wanted anti-camping system
GTR_KARMA = 1<<9, // Enables the Karma system if you're out of bumpers
GTR_ITEMARROWS = 1<<10, // Show item box arrows above players
GTR_ITEMBREAKER = 1<<11, // Enables the use of Item Breaker in this Gamemode
GTR_BATTLESTARTS = 1<<12, // Use Battle Mode start positions.
GTR_POINTLIMIT = 1<<11, // Reaching point limit ends the round
GTR_TIMELIMIT = 1<<12, // Reaching time limit ends the round
GTR_OVERTIME = 1<<13, // Allow overtime behavior
GTR_POINTLIMIT = 1<<13, // Reaching point limit ends the round
GTR_TIMELIMIT = 1<<14, // Reaching time limit ends the round
GTR_OVERTIME = 1<<15, // Allow overtime behavior
// Custom gametype rules
GTR_TEAMS = 1<<14, // Teams are forced on
GTR_NOTEAMS = 1<<15, // Teams are forced off
GTR_TEAMSTARTS = 1<<16, // Use team-based start positions
GTR_TEAMS = 1<<16, // Teams are forced on
GTR_NOTEAMS = 1<<17, // Teams are forced off
GTR_TEAMSTARTS = 1<<18, // Use team-based start positions
// Grand Prix rules
//Free = 1<<17,
GTR_LIVES = 1<<18, // Lives system, players are forced to spectate during Game Over.
GTR_SPECIALBOTS = 1<<19, // Bot difficulty gets stronger between rounds, and the rival system is enabled.
//Free = 1<<19,
GTR_LIVES = 1<<20, // Lives system, players are forced to spectate during Game Over.
GTR_SPECIALBOTS = 1<<21, // Bot difficulty gets stronger between rounds, and the rival system is enabled.
// Misc
GTR_FREEROAM = 1<<20, // Disables Countdown timer and control lock at the start of levels.
GTR_FREEROAM = 1<<22, // Disables Countdown timer and control lock at the start of levels.
GTR_ENCORE = 1<<23, // Enable Encore mode.
// free: to and including 1<<31
};

View file

@ -3150,9 +3150,9 @@ const char *Gametype_ConstantNames[NUMGAMETYPES] =
UINT32 gametypedefaultrules[NUMGAMETYPES] =
{
// Race
GTR_CIRCUIT|GTR_BOTS|GTR_RINGS,
GTR_CIRCUIT|GTR_BOTS|GTR_RINGS|GTR_ENCORE|GTR_RACEODDS,
// Battle
GTR_BUMPERS|GTR_RINGS|GTR_KARMA|GTR_WANTED|GTR_ITEMARROWS|GTR_ITEMBREAKER|GTR_BATTLESTARTS|GTR_TIMELIMIT
GTR_BUMPERS|GTR_RINGS|GTR_KARMA|GTR_WANTED|GTR_ITEMARROWS|GTR_ITEMBREAKER|GTR_BATTLESTARTS|GTR_TIMELIMIT|GTR_BATTLEODDS
};
//
@ -3277,6 +3277,12 @@ void G_UpdateGametypeSelections(void)
gametype_cons_t[NUMGAMETYPES].strvalue = NULL;
}
INT32 gametypecolor[NUMGAMETYPES] =
{
V_SKYMAP,
V_REDMAP,
};
// Gametype rankings
INT16 gametyperankings[NUMGAMETYPES] =
{
@ -3513,13 +3519,10 @@ UINT8 G_GetGametypeColor(INT16 gt)
|| gamestate == GS_TIMEATTACK)
return orangemap[0];
if (gt == GT_BATTLE)
return redmap[0];
if (gametypecolor[gt])
return *V_GetStringColormap(gametypecolor[gt]);
if (gt == GT_RACE)
return skymap[0];
return 255; // FALLBACK
return yellowmap[0]; // FALLBACK
}
/** Get the typeoflevel flag needed to indicate support of a gametype.

View file

@ -206,12 +206,14 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives);
extern UINT32 gametypedefaultrules[NUMGAMETYPES];
extern UINT32 gametypetol[NUMGAMETYPES];
extern INT32 gametypecolor[NUMGAMETYPES];
extern INT16 gametyperankings[NUMGAMETYPES];
void G_SetGametype(INT16 gametype);
INT16 G_AddGametype(UINT32 rules);
void G_AddGametypeConstant(INT16 gtype, const char *newgtconst);
void G_UpdateGametypeSelections(void);
void G_SetGametypeColor(INT16 gtype,INT32 color);
void G_AddTOL(UINT32 newtol, const char *tolname);
void G_AddGametypeTOL(INT16 gtype, UINT32 newtol);
INT32 G_GetGametypeByName(const char *gametypestr);

View file

@ -2465,7 +2465,12 @@ static void HU_DrawRankings(void)
else if (modeattacking)
hilicol = V_ORANGEMAP;
else
hilicol = ((gametype == GT_RACE) ? V_SKYMAP : V_REDMAP);
{
if (gametypecolor[gametype])
hilicol = gametypecolor[gametype];
else
hilicol = V_YELLOWMAP;
}
// draw the current gametype in the lower right
if (modeattacking)

View file

@ -4475,7 +4475,7 @@ void K_drawKartHUD(void)
}
// Race overlays
if (gametype == GT_RACE && !freecam)
if ((gametyperules & GTR_CIRCUIT) && !freecam)
{
if (stplyr->exiting)
K_drawKartFinish();

View file

@ -608,16 +608,20 @@ INT32 K_KartGetItemOdds(
*/
(void)bot;
if (gametype == GT_BATTLE)
if (gametyperules & GTR_BATTLEODDS)
{
I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table
newodds = K_KartItemOddsBattle[item-1][pos];
}
else
else if (gametyperules & GTR_RACEODDS)
{
I_Assert(pos < 8); // Ditto
newodds = K_KartItemOddsRace[item-1][pos];
}
else
{
newodds = 0;
}
// Base multiplication to ALL item odds to simulate fractional precision
newodds *= 4;
@ -825,7 +829,7 @@ INT32 K_KartGetLegacyItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spb
if (!KartItemCVars[item-1]->value && !modeattacking)
return 0;
if (gametype == GT_BATTLE)
if (gametyperules & GTR_BATTLEODDS)
newodds = K_KartItemOddsBattle[item-1][pos];
else
newodds = K_KartItemOddsRace[item-1][pos];
@ -1000,7 +1004,7 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum
UINT8 j;
boolean available = false;
if (gametype == GT_BATTLE && i > 1)
if ((gametyperules & GTR_BATTLEODDS) && i > 1)
{
oddsvalid[i] = false;
break;
@ -1028,7 +1032,7 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum
for (i = num; i; --i) \
disttable[distlen++] = odds;
if (gametype == GT_BATTLE) // Battle Mode
if (gametyperules & GTR_BATTLEODDS) // Battle Mode
{
if (player->roulettetype == 1 && oddsvalid[1] == true)
{
@ -1046,7 +1050,7 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum
}
}
}
else
else if (gametyperules & GTR_RACEODDS)
{
SETUPDISTTABLE(0,1);
SETUPDISTTABLE(1,1);
@ -1104,7 +1108,7 @@ INT32 K_FindLegacyUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT32
INT32 j;
boolean available = false;
if (gametype == GT_BATTLE && i > 1)
if ((gametyperules & GTR_BATTLEODDS) && i > 1)
{
oddsvalid[i] = false;
break;
@ -1239,7 +1243,7 @@ INT32 K_FindLegacyUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT32
for (i = num; i; --i) \
disttable[distlen++] = odds;
if (gametype == GT_BATTLE) // Battle Mode
if (gametyperules & GTR_BATTLEODDS) // Battle Mode
{
if (player->roulettetype == 1 && oddsvalid[1] == true)
{
@ -1331,7 +1335,7 @@ INT32 K_GetRollingRouletteItem(player_t *player)
roulette_size = 0;
if (gametype == GT_BATTLE)
if (gametyperules & GTR_BATTLEODDS)
{
odds_row = K_KartItemOddsBattle[0];
odds_row_size = sizeof K_KartItemOddsBattle[0];
@ -1495,12 +1499,28 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
}
// SPECIAL CASE No. 3:
// Record Attack / alone mashing behavior
if (modeattacking || pingame == 1)
// This Gametype never specified an odds type. Roll something random please!
if (!(gametyperules & GTR_RACEODDS) && !(gametyperules & GTR_BATTLEODDS))
{
if (gametype == GT_RACE)
SINT8 itemroll = P_RandomRange(KITEM_SNEAKER, NUMKARTITEMS - 1);
K_KartGetItemResult(player, itemroll);
player->karthud[khud_itemblink] = TICRATE;
player->karthud[khud_itemblinkmode] = 0;
player->itemroulette = 0;
player->roulettetype = 0;
if (P_IsDisplayPlayer(player))
S_StartSound(NULL, sfx_itrolf);
return;
}
// SPECIAL CASE No. 4:
// Record Attack / alone mashing behavior
if ((modeattacking || pingame == 1) && ((gametyperules & GTR_RACEODDS) || (gametyperules & GTR_BATTLEODDS)))
{
if ((gametyperules & GTR_RACEODDS))
{
if (mashed && (modeattacking || (cv_superring.value && (K_RingsActive() == true)))) // ANY mashed value? You get rings.
if (mashed && ((K_RingsActive() == true) && (modeattacking || cv_superring.value))) // ANY mashed value? You get rings.
{
K_KartGetItemResult(player, KITEM_SUPERRING);
player->karthud[khud_itemblinkmode] = 1;
@ -1518,7 +1538,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
S_StartSound(NULL, sfx_itrolf);
}
}
else if (gametype == GT_BATTLE)
else if (gametyperules & GTR_BATTLEODDS)
{
if (mashed && (bossinfo.boss || cv_banana.value) && !itembreaker) // ANY mashed value? You get a banana.
{
@ -1534,16 +1554,6 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
if (P_IsDisplayPlayer(player))
S_StartSound(NULL, sfx_itrolf);
}
else
{
if (modeattacking || cv_tripleorbinaut.value) // Waited patiently? You get Orbinaut x3!
K_KartGetItemResult(player, KRITEM_TRIPLEORBINAUT);
else // Default to sad if nothing's enabled...
K_KartGetItemResult(player, KITEM_SAD);
player->karthud[khud_itemblinkmode] = 0;
if (P_IsDisplayPlayer(player))
S_StartSound(NULL, sfx_itrolf);
}
}
player->karthud[khud_itemblink] = TICRATE;
@ -1552,7 +1562,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
return;
}
// SPECIAL CASE No. 4:
// SPECIAL CASE No. 5:
// Being in ring debt occasionally forces Super Ring on you if you mashed
if ((K_RingsActive() == true) && mashed && player->rings < 0 && cv_superring.value)
{
@ -1572,7 +1582,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
if (!(numbosswaypoints > 0))
{
// SPECIAL CASE No. 5:
// SPECIAL CASE No. 6:
// Force SPB onto 2nd if they get too far behind
if ((gametyperules & GTR_CIRCUIT) && player->position == 2 && pdis > SPBFORCEDIST
&& spbplace == -1 && !indirectitemcooldown && !dontforcespb
@ -6523,7 +6533,7 @@ void K_KartPlayerHUDUpdate(player_t *player)
if (player->karthud[khud_cardanimation] < 0)
player->karthud[khud_cardanimation] = 0;
}
else if (gametype == GT_RACE && player->exiting)
else if ((gametyperules & GTR_CIRCUIT) && player->exiting)
{
if (player->karthud[khud_cardanimation] < 2*TICRATE)
player->karthud[khud_cardanimation]++;
@ -9764,9 +9774,6 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
{
INT32 hyu = hyudorotime;
if (gametype == GT_RACE)
hyu *= 2; // double in race
if (leveltime & 1)
{
player->mo->renderflags |= RF_DONTDRAW;

View file

@ -36,6 +36,7 @@
#include "m_menu.h" // Player Setup menu color stuff
#include "p_spec.h" // P_StartQuake
#include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision
#include "v_video.h"
#include "lua_script.h"
#include "lua_libs.h"
@ -3049,6 +3050,7 @@ static int lib_gAddGametype(lua_State *L)
INT32 newgtpointlimit = 0;
INT32 newgttimelimit = 0;
INT16 newgtrankingstype = -1;
INT32 newgtcolor = V_YELLOWMAP;
int newgtinttype = 0;
luaL_checktype(L, 1, LUA_TTABLE);
@ -3108,6 +3110,14 @@ static int lib_gAddGametype(lua_State *L)
if (!lua_isnumber(L, 3))
TYPEERROR("defaulttimelimit", LUA_TNUMBER)
newgttimelimit = (INT32)lua_tointeger(L, 3);
} else if (i == 9 || (k && fasticmp(k, "menucolor"))) {
if (!lua_isnumber(L, 3))
TYPEERROR("menucolor", LUA_TNUMBER)
{
INT32 color = (INT32)lua_tointeger(L, 3);
// Mask out other flags so they aren't passed into menu code.
newgtcolor = color & V_CHARCOLORMASK; // V_
}
}
lua_pop(L, 1);
}
@ -3133,6 +3143,7 @@ static int lib_gAddGametype(lua_State *L)
intermissiontypes[newgtidx] = newgtinttype;
pointlimits[newgtidx] = newgtpointlimit;
timelimits[newgtidx] = newgttimelimit;
gametypecolor[newgtidx] = newgtcolor;
// Write the new gametype name.
Gametype_Names[newgtidx] = gtname;

View file

@ -1728,13 +1728,19 @@ inline static void M_GetGametypeColor(void)
if (gt == GT_BATTLE || levellistmode == LLM_BOSS)
{
highlightflags = V_REDMAP;
highlightflags = gametypecolor[gt];
warningflags = V_ORANGEMAP;
return;
}
if (gt == GT_RACE)
{
highlightflags = V_SKYMAP;
highlightflags = gametypecolor[gt];
return;
}
if (gametypecolor[gt])
{
highlightflags = gametypecolor[gt];
return;
}

View file

@ -28,6 +28,7 @@
#include "i_video.h"
#include "r_fps.h"
#include "r_main.h"
#include "f_finale.h"
// Object place
#include "m_cheat.h"
@ -775,7 +776,7 @@ void P_Ticker(boolean run)
// Plays the music after the starting countdown.
else
{
if (!(gametyperules & GTR_FREEROAM))
if (!(gametyperules & GTR_FREEROAM) && !(titlemapinaction == TITLEMAP_RUNNING))
{
if (leveltime == starttime-(3*TICRATE))
{
@ -791,7 +792,7 @@ void P_Ticker(boolean run)
}
}
if (leveltime < starttime) // SRB2Kart
if (!(gametyperules & GTR_FREEROAM) && leveltime < starttime) // SRB2Kart
S_ChangeMusicInternal((encoremode ? "estart" : "kstart"), false); // yes this will be spammed otherwise encore and some stuff WILL overwrite it
else if (leveltime == starttime) // The GO! sound stops the level start ambience
S_StopMusic();

View file

@ -2564,10 +2564,14 @@ void S_InitLevelMusic(boolean fromnetsave)
S_StopMusic(); // Starting ambience should always be restarted, if playing.
if (leveltime < (starttime + (TICRATE/2))) // SRB2Kart
if (!(gametyperules & GTR_FREEROAM) && leveltime < (starttime + (TICRATE/2))) // SRB2Kart
{
S_ChangeMusicEx((encoremode ? "estart" : "kstart"), 0, false, mapmusposition, 0, 0);
}
else if (!(gametyperules & GTR_FREEROAM) && leveltime < (starttime + (TICRATE/2)))
{
S_ChangeMusicEx(mapmusname, 0, false, mapmusposition, 0, 0);
}
S_ResetMusicStack();
music_stack_noposition = false;

View file

@ -556,7 +556,12 @@ void Y_IntermissionDrawer(void)
else if (modeattacking)
hilicol = V_ORANGEMAP;
else
hilicol = ((intertype == int_race) ? V_SKYMAP : V_REDMAP);
{
if (gametypecolor[gametype])
hilicol = gametypecolor[gametype];
else
hilicol = V_YELLOWMAP;
}
if (sorttic != -1 && intertic > sorttic)
{
@ -1444,10 +1449,13 @@ void Y_VoteDrawer(void)
INT32 hilicol, tickdown = (timer+1)/TICRATE;
if (cons_menuhighlight.value)
hilicol = cons_menuhighlight.value;
else if (gametype == GT_RACE)
hilicol = V_SKYMAP;
else //if (gametype == GT_BATTLE)
hilicol = V_REDMAP;
else
{
if (gametypecolor[gametype])
hilicol = gametypecolor[gametype];
else
hilicol = V_YELLOWMAP;
}
V_DrawCenteredString(BASEVIDWIDTH/2, 188, hilicol,
va("Vote ends in %d", tickdown));
}