Merge branch 'master' of https://git.do.srb2.org/KartKrew/Kart into save_p-unglobal-alt

# Conflicts:
#	src/g_demo.c
This commit is contained in:
toaster 2023-01-03 19:45:18 +00:00
commit 80d19a8458
65 changed files with 2114 additions and 1613 deletions

View file

@ -1,3 +1,5 @@
cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
set(CMAKE_BINARY_DIR "${BINARY_DIR}")
set(CMAKE_CURRENT_BINARY_DIR "${BINARY_DIR}")
@ -7,7 +9,23 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Mo
include(GitUtilities)
git_current_branch(SRB2_COMP_BRANCH)
git_summary(SRB2_COMP_REVISION)
git_working_tree_dirty(SRB2_COMP_UNCOMMITTED)
git_summary(revision)
string(REGEX REPLACE "([\"\\])" "\\\\\\1" SRB2_COMP_REVISION "${revision}")
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
set(CMAKE_BUILD_TYPE None)
endif()
# These build types enable optimizations of some kind by default.
set(optimized_build_types "MINSIZEREL;RELEASE;RELWITHDEBINFO")
string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type)
if("${build_type}" IN_LIST optimized_build_types)
set(SRB2_COMP_OPTIMIZED TRUE)
else()
set(SRB2_COMP_OPTIMIZED FALSE)
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/src/config.h")

View file

@ -154,7 +154,7 @@ add_custom_command(
# build time for accurate git information and before anything
# that needs it, obviously.
add_custom_target(_SRB2_reconf ALL
COMMAND ${CMAKE_COMMAND} -DGIT_EXECUTABLE=${GIT_EXECUTABLE} -DBINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}/.. -P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Comptime.cmake
COMMAND ${CMAKE_COMMAND} -DGIT_EXECUTABLE=${GIT_EXECUTABLE} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DBINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}/.. -P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Comptime.cmake
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.."
)
add_dependencies(SRB2SDL2 _SRB2_reconf)

View file

@ -11,6 +11,8 @@
#include "config.h"
const char *compbranch = SRB2_COMP_BRANCH;
const char *comprevision = SRB2_COMP_REVISION;
const char *comptype = CMAKE_BUILD_TYPE;
const int compoptimized = SRB2_COMP_OPTIMIZED;
#elif (defined(COMPVERSION))
#include "comptime.h"

View file

@ -20,6 +20,9 @@
#define COMPVERSION_UNCOMMITTED
#endif
#define CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}"
#cmakedefine01 SRB2_COMP_OPTIMIZED
#endif
/* Manually defined asset hashes for non-CMake builds

View file

@ -358,7 +358,22 @@ static void CON_SetupColormaps(void)
*memorysrc = (UINT8)(i & 0xFF); // remap each color to itself...
purplemap[0] = (UINT8)163;
yellowmap[0] = (UINT8)73;
yellowmap[1] = (UINT8)73;
yellowmap[3] = (UINT8)74;
yellowmap[6] = (UINT8)74;
yellowmap[7] = (UINT8)190;
yellowmap[8] = (UINT8)190;
yellowmap[10] = (UINT8)190;
yellowmap[12] = (UINT8)190;
yellowmap[14] = (UINT8)149;
yellowmap[15] = (UINT8)149;
yellowmap[16] = (UINT8)149;
yellowmap[21] = (UINT8)152;
yellowmap[23] = (UINT8)173;
yellowmap[24] = (UINT8)167;
greenmap[0] = (UINT8)98;
bluemap[0] = (UINT8)148;
redmap[0] = (UINT8)34; // battle

View file

@ -54,7 +54,6 @@
#include "k_pwrlv.h"
#include "k_bot.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "doomstat.h"
#include "s_sound.h" // sfx_syfail
#include "m_cond.h" // netUnlocked
@ -899,9 +898,6 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
UINT8 *p;
size_t mirror_length;
const char *httpurl = cv_httpsource.string;
UINT8 prefgametype = (cv_kartgametypepreference.value == -1)
? gametype
: cv_kartgametypepreference.value;
netbuffer->packettype = PT_SERVERINFO;
netbuffer->u.serverinfo._255 = 255;
@ -933,7 +929,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
else
netbuffer->u.serverinfo.refusereason = 0;
strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[prefgametype],
strncpy(netbuffer->u.serverinfo.gametypename, gametypes[gametype]->name,
sizeof netbuffer->u.serverinfo.gametypename);
netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
@ -2077,8 +2073,9 @@ static void CL_ConnectToServer(void)
Y_EndVote();
DEBFILE(va("waiting %d nodes\n", doomcom->numnodes));
M_ClearMenus(true);
G_SetGamestate(GS_WAITINGPLAYERS);
if (wipegamestate == GS_MENU)
M_ClearMenus(true);
wipegamestate = GS_WAITINGPLAYERS;
ClearAdminPlayers();
@ -3744,6 +3741,9 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
sprintf(player_names[newplayernum], "%s", skins[skinnum].realname);
SetPlayerSkinByNum(newplayernum, skinnum);
players[newplayernum].spectator = !(gametyperules & GTR_BOTS)
|| (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE);
if (netgame)
{
HU_AddChatText(va("\x82*Bot %d has been added to the game", newplayernum+1), false);

View file

@ -72,7 +72,6 @@
// SRB2Kart
#include "k_grandprix.h"
#include "k_boss.h"
#include "doomstat.h"
#include "m_random.h" // P_ClearRandom
#include "k_specialstage.h"
@ -963,12 +962,6 @@ void D_StartTitle(void)
// Reset GP
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
// Reset boss info
K_ResetBossInfo();
// Reset Special Stage
K_ResetSpecialStage();
// empty maptol so mario/etc sounds don't play in sound test when they shouldn't
maptol = 0;
@ -1200,13 +1193,14 @@ D_ConvertVersionNumbers (void)
//
void D_SRB2Main(void)
{
INT32 i, p;
INT32 i, j, p;
#ifdef DEVELOP
INT32 pstartmap = 1; // default to first loaded map (Test Run)
#else
INT32 pstartmap = 0; // default to random map (0 is not a valid map number)
#endif
boolean autostart = false;
INT32 newgametype = -1;
/* break the version string into version numbers, for netplay */
D_ConvertVersionNumbers();
@ -1519,11 +1513,6 @@ void D_SRB2Main(void)
CON_SetLoadingProgress(LOADED_HUINIT);
memset(timelimits, 0, sizeof(timelimits));
memset(pointlimits, 0, sizeof(pointlimits));
timelimits[GT_BATTLE] = 2;
D_RegisterServerCommands();
D_RegisterClientCommands(); // be sure that this is called before D_CheckNetGame
R_RegisterEngineStuff();
@ -1531,14 +1520,14 @@ void D_SRB2Main(void)
I_RegisterSysCommands();
M_Init();
//--------------------------------------------------------- CONFIG.CFG
M_FirstLoadConfig(); // WARNING : this do a "COM_BufExecute()"
// Load Profiles now that default controls have been defined
PR_LoadProfiles(); // load control profiles
M_Init();
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
VID_PrepareModeList(); // Regenerate Modelist according to cv_fullscreen
#endif
@ -1796,8 +1785,6 @@ void D_SRB2Main(void)
if (M_CheckParm("-gametype") && M_IsNextParm())
{
// from Command_Map_f
INT32 j;
INT16 newgametype = -1;
const char *sgametype = M_GetNextParm();
newgametype = G_GetGametypeByName(sgametype);
@ -1805,7 +1792,7 @@ void D_SRB2Main(void)
if (newgametype == -1) // reached end of the list with no match
{
j = atoi(sgametype); // assume they gave us a gametype number, which is okay too
if (j >= 0 && j < gametypecount)
if (j >= 0 && j < numgametypes)
newgametype = (INT16)j;
}
@ -1819,7 +1806,6 @@ void D_SRB2Main(void)
if (M_CheckParm("-skill") && M_IsNextParm())
{
INT32 j;
INT16 newskill = -1;
const char *sskill = M_GetNextParm();
@ -1872,11 +1858,20 @@ void D_SRB2Main(void)
if (grandprixinfo.gp == true && mapheaderinfo[pstartmap-1])
{
if (mapheaderinfo[pstartmap-1]->typeoflevel & TOL_SPECIAL)
if (newgametype == -1)
{
specialStage.active = true;
specialStage.encore = grandprixinfo.encore;
grandprixinfo.eventmode = GPEVENT_SPECIAL;
newgametype = G_GuessGametypeByTOL(mapheaderinfo[pstartmap-1]->typeoflevel);
if (newgametype != -1)
{
j = gametype;
G_SetGametype(newgametype);
D_GameTypeChanged(j);
}
if (gametyperules & (GTR_BOSS|GTR_CATCHER))
grandprixinfo.eventmode = GPEVENT_SPECIAL;
else if (gametype != GT_RACE)
grandprixinfo.eventmode = GPEVENT_BONUS;
}
G_SetUsedCheats();

View file

@ -57,7 +57,6 @@
#include "k_color.h"
#include "k_respawn.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_follower.h"
#include "doomstat.h"
#include "deh_tables.h"
@ -399,10 +398,6 @@ consvar_t cv_kartbumpers = CVAR_INIT ("battlebumpers", "3", CV_NETVAR, kartbumpe
consvar_t cv_kartfrantic = CVAR_INIT ("franticitems", "Off", CV_NETVAR|CV_CALL|CV_NOINIT, CV_OnOff, KartFrantic_OnChange);
static CV_PossibleValue_t kartencore_cons_t[] = {{-1, "Auto"}, {0, "Off"}, {1, "On"}, {0, NULL}};
consvar_t cv_kartencore = CVAR_INIT ("encore", "Auto", CV_NETVAR|CV_CALL|CV_NOINIT, kartencore_cons_t, KartEncore_OnChange);
static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}};
consvar_t cv_kartvoterulechanges = CVAR_INIT ("voterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL);
static CV_PossibleValue_t kartgametypepreference_cons_t[] = {{-1, "None"}, {GT_RACE, "Race"}, {GT_BATTLE, "Battle"}, {0, NULL}};
consvar_t cv_kartgametypepreference = CVAR_INIT ("gametypepreference", "None", CV_NETVAR, kartgametypepreference_cons_t, NULL);
static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Percentage"}, {2, "Kilometers"}, {3, "Miles"}, {4, "Fracunits"}, {0, NULL}};
consvar_t cv_kartspeedometer = CVAR_INIT ("speedometer", "Percentage", CV_SAVE, kartspeedometer_cons_t, NULL); // use tics in display
static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}};
@ -485,10 +480,6 @@ consvar_t cv_timelimit = CVAR_INIT ("timelimit", "None", CV_NETVAR|CV_CALL|CV_NO
static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {MAX_LAPS, "MAX"}, {0, "Map default"}, {0, NULL}};
consvar_t cv_numlaps = CVAR_INIT ("numlaps", "Map default", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT, numlaps_cons_t, NumLaps_OnChange);
// Point and time limits for every gametype
INT32 pointlimits[NUMGAMETYPES];
INT32 timelimits[NUMGAMETYPES];
consvar_t cv_forceskin = CVAR_INIT ("forceskin", "None", CV_NETVAR|CV_CALL|CV_CHEAT, NULL, ForceSkin_OnChange);
consvar_t cv_downloading = CVAR_INIT ("downloading", "On", 0, CV_OnOff, NULL);
@ -557,13 +548,11 @@ char timedemo_csv_id[256];
boolean timedemo_quit;
INT16 gametype = GT_RACE;
UINT32 gametyperules = 0;
INT16 gametypecount = GT_FIRSTFREESLOT;
INT16 numgametypes = GT_FIRSTFREESLOT;
boolean forceresetplayers = false;
boolean deferencoremode = false;
UINT8 splitscreen = 0;
boolean circuitmap = false;
INT32 adminplayers[MAXPLAYERS];
// Scheduled commands.
@ -2530,19 +2519,11 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r
CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d pencoremode=%d resetplayers=%d delay=%d skipprecutscene=%d\n",
mapnum, newgametype, pencoremode, resetplayers, delay, skipprecutscene);
if ((netgame || multiplayer) && !((gametype == newgametype) && (gametypedefaultrules[newgametype] & GTR_CAMPAIGN)))
if ((netgame || multiplayer) && (grandprixinfo.gp != false))
FLS = false;
// Too lazy to change the input value for every instance of this function.......
if (bossinfo.boss == true)
{
pencoremode = bossinfo.encore;
}
else if (specialStage.active == true)
{
pencoremode = specialStage.encore;
}
else if (grandprixinfo.gp == true)
if (grandprixinfo.gp == true)
{
pencoremode = grandprixinfo.encore;
}
@ -2596,14 +2577,13 @@ void D_SetupVote(void)
UINT8 buf[5*2]; // four UINT16 maps (at twice the width of a UINT8), and two gametypes
UINT8 *p = buf;
INT32 i;
UINT8 gt = (cv_kartgametypepreference.value == -1) ? gametype : cv_kartgametypepreference.value;
UINT8 secondgt = G_SometimesGetDifferentGametype(gt);
UINT8 secondgt = G_SometimesGetDifferentGametype();
INT16 votebuffer[4] = {-1,-1,-1,0};
if ((cv_kartencore.value == 1) && (gametypedefaultrules[gt] & GTR_CIRCUIT))
WRITEUINT8(p, (gt|VOTEMODIFIER_ENCORE));
if ((cv_kartencore.value == 1) && (gametyperules & GTR_ENCORE))
WRITEUINT8(p, (gametype|VOTEMODIFIER_ENCORE));
else
WRITEUINT8(p, gt);
WRITEUINT8(p, gametype);
WRITEUINT8(p, secondgt);
secondgt &= ~VOTEMODIFIER_ENCORE;
@ -2613,9 +2593,9 @@ void D_SetupVote(void)
if (i == 2) // sometimes a different gametype
m = G_RandMap(G_TOLFlag(secondgt), prevmap, ((secondgt != gametype) ? 2 : 0), 0, true, votebuffer);
else if (i >= 3) // unknown-random and formerly force-unknown MAP HELL
m = G_RandMap(G_TOLFlag(gt), prevmap, 0, (i-2), (i < 4), votebuffer);
m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, (i-2), (i < 4), votebuffer);
else
m = G_RandMap(G_TOLFlag(gt), prevmap, 0, 0, true, votebuffer);
m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, true, votebuffer);
if (i < 3)
votebuffer[i] = m;
WRITEUINT16(p, m);
@ -2758,13 +2738,7 @@ static void Command_Map_f(void)
if (option_gametype)
{
if (!multiplayer)
{
CONS_Printf(M_GetText(
"You can't switch gametypes in single player!\n"));
return;
}
else if (COM_Argc() < option_gametype + 2)/* no argument after? */
if (COM_Argc() < option_gametype + 2)/* no argument after? */
{
CONS_Alert(CONS_ERROR,
"No gametype name follows parameter '%s'.\n",
@ -2822,7 +2796,7 @@ static void Command_Map_f(void)
if (isdigit(gametypename[0]))
{
d = atoi(gametypename);
if (d >= 0 && d < gametypecount)
if (d >= 0 && d < numgametypes)
newgametype = d;
else
{
@ -2830,7 +2804,7 @@ static void Command_Map_f(void)
"Gametype number %d is out of range. Use a number between"
" 0 and %d inclusive. ...Or just use the name. :v\n",
d,
gametypecount-1);
numgametypes-1);
Z_Free(realmapname);
Z_Free(mapname);
return;
@ -2839,13 +2813,23 @@ static void Command_Map_f(void)
else
{
CONS_Alert(CONS_ERROR,
"'%s' is not a gametype.\n",
"'%s' is not a valid gametype.\n",
gametypename);
Z_Free(realmapname);
Z_Free(mapname);
return;
}
}
if (Playing() && netgame && (gametypes[newgametype]->rules & GTR_FORBIDMP))
{
CONS_Alert(CONS_ERROR,
"'%s' is not a net-compatible gametype.\n",
gametypename);
Z_Free(realmapname);
Z_Free(mapname);
return;
}
}
else if (!Playing())
{
@ -2853,7 +2837,15 @@ static void Command_Map_f(void)
if (mapheaderinfo[newmapnum-1])
{
// Let's just guess so we don't have to specify the gametype EVERY time...
newgametype = (mapheaderinfo[newmapnum-1]->typeoflevel & (TOL_BATTLE|TOL_BOSS)) ? GT_BATTLE : GT_RACE;
newgametype = G_GuessGametypeByTOL(mapheaderinfo[newmapnum-1]->typeoflevel);
if (newgametype == -1)
{
CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support any known gametype!\n"), realmapname, G_BuildMapName(newmapnum));
Z_Free(realmapname);
Z_Free(mapname);
return;
}
}
}
@ -2865,6 +2857,8 @@ static void Command_Map_f(void)
if (!M_SecretUnlocked(SECRET_ENCORE, false) && newencoremode == true && !usingcheats)
{
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
Z_Free(realmapname);
Z_Free(mapname);
return;
}
}
@ -2885,8 +2879,7 @@ static void Command_Map_f(void)
mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)
))
{
CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum),
(multiplayer ? gametype_cons_t[newgametype].strvalue : "Single Player"));
CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum), gametypes[newgametype]->name);
Z_Free(realmapname);
Z_Free(mapname);
return;
@ -2895,8 +2888,7 @@ static void Command_Map_f(void)
{
fromlevelselect =
( netgame || multiplayer ) &&
newgametype == gametype &&
gametypedefaultrules[newgametype] & GTR_CAMPAIGN;
grandprixinfo.gp != false;
}
}
@ -2952,35 +2944,18 @@ static void Command_Map_f(void)
grandprixinfo.eventmode = GPEVENT_NONE;
if (newgametype == GT_BATTLE)
if (gametypes[newgametype]->rules & (GTR_BOSS|GTR_CATCHER))
{
grandprixinfo.eventmode = GPEVENT_SPECIAL;
}
else if (newgametype != GT_RACE)
{
grandprixinfo.eventmode = GPEVENT_BONUS;
if (mapheaderinfo[newmapnum-1] &&
mapheaderinfo[newmapnum-1]->typeoflevel & TOL_BOSS)
{
bossinfo.boss = true;
bossinfo.encore = newencoremode;
}
else
{
bossinfo.boss = false;
K_ResetBossInfo();
}
}
else
if (!Playing())
{
if (mapheaderinfo[newmapnum-1] &&
mapheaderinfo[newmapnum-1]->typeoflevel & TOL_SPECIAL) // Special Stage
{
specialStage.active = true;
specialStage.encore = newencoremode;
grandprixinfo.eventmode = GPEVENT_SPECIAL;
}
else
{
specialStage.active = false;
}
multiplayer = true;
}
}
@ -3029,12 +3004,12 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
gametype = READUINT8(*cp);
G_SetGametype(gametype); // I fear putting that macro as an argument
if (gametype < 0 || gametype >= gametypecount)
if (gametype < 0 || gametype >= numgametypes)
gametype = lastgametype;
else if (gametype != lastgametype)
D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
if (!(gametyperules & GTR_CIRCUIT) && !bossinfo.boss)
if (!(gametyperules & GTR_ENCORE))
pencoremode = false;
skipprecutscene = ((flags & (1<<2)) != 0);
@ -3086,9 +3061,10 @@ static void Command_RandomMap(void)
{
INT32 oldmapnum;
INT32 newmapnum;
INT32 newgametype;
boolean newencoremode;
INT32 newgametype = (Playing() ? gametype : menugametype);
boolean newencore = false;
boolean newresetplayers;
size_t option_gametype;
if (client && !IsPlayerAdmin(consoleplayer))
{
@ -3096,13 +3072,69 @@ static void Command_RandomMap(void)
return;
}
if ((option_gametype = COM_CheckPartialParm("-g")))
{
const char *gametypename;
if (COM_Argc() < option_gametype + 2)/* no argument after? */
{
CONS_Alert(CONS_ERROR,
"No gametype name follows parameter '%s'.\n",
COM_Argv(option_gametype));
return;
}
// new gametype value
// use current one by default
gametypename = COM_Argv(option_gametype + 1);
newgametype = G_GetGametypeByName(gametypename);
if (newgametype == -1) // reached end of the list with no match
{
/* Did they give us a gametype number? That's okay too! */
if (isdigit(gametypename[0]))
{
INT16 d = atoi(gametypename);
if (d >= 0 && d < numgametypes)
newgametype = d;
else
{
CONS_Alert(CONS_ERROR,
"Gametype number %d is out of range. Use a number between"
" 0 and %d inclusive. ...Or just use the name. :v\n",
d,
numgametypes-1);
return;
}
}
else
{
CONS_Alert(CONS_ERROR,
"'%s' is not a valid gametype.\n",
gametypename);
return;
}
}
if (Playing() && netgame && (gametypes[newgametype]->rules & GTR_FORBIDMP))
{
CONS_Alert(CONS_ERROR,
"'%s' is not a net-compatible gametype.\n",
gametypename);
return;
}
}
// TODO: Handle singleplayer conditions.
// The existing ones are way too annoyingly complicated and "anti-cheat" for my tastes.
if (Playing())
{
newgametype = gametype;
newencoremode = encoremode;
if (cv_kartencore.value == 1 && (gametypes[newgametype]->rules & GTR_ENCORE))
{
newencore = true;
}
newresetplayers = false;
if (gamestate == GS_LEVEL)
@ -3116,14 +3148,12 @@ static void Command_RandomMap(void)
}
else
{
newgametype = cv_dummygametype.value; // Changed from cv_newgametype to match newmenus
newencoremode = false;
newresetplayers = true;
oldmapnum = -1;
}
newmapnum = G_RandMap(G_TOLFlag(newgametype), oldmapnum, 0, 0, false, NULL) + 1;
D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false);
D_MapChange(newmapnum, newgametype, newencore, newresetplayers, 0, false, false);
}
static void Command_RestartLevel(void)
@ -3742,7 +3772,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
// Clear player score and rings if a spectator.
if (players[playernum].spectator)
{
if (gametyperules & GTR_BUMPERS) // SRB2kart
if (gametyperules & GTR_POINTLIMIT) // SRB2kart
{
players[playernum].roundscore = 0;
K_CalculateBattleWanted();
@ -4774,16 +4804,13 @@ static void Command_Version_f(void)
else // 16-bit? 128-bit?
CONS_Printf("Bits Unknown ");
CONS_Printf("%s ", comptype);
// No ASM?
#ifdef NOASM
CONS_Printf("\x85" "NOASM " "\x80");
#endif
// Debug build
#ifdef _DEBUG
CONS_Printf("\x85" "DEBUG " "\x80");
#endif
// DEVELOP build
#if defined(TESTERS)
CONS_Printf("\x88" "TESTERS " "\x80");
@ -4812,15 +4839,9 @@ static void Command_ShowGametype_f(void)
{
const char *gametypestr = NULL;
if (!(netgame || multiplayer)) // print "Single player" instead of "Race"
{
CONS_Printf(M_GetText("Current gametype is %s\n"), "Single Player");
return;
}
// get name string for current gametype
if (gametype >= 0 && gametype < gametypecount)
gametypestr = Gametype_Names[gametype];
if (gametype >= 0 && gametype < numgametypes)
gametypestr = gametypes[gametype]->name;
if (gametypestr)
CONS_Printf(M_GetText("Current gametype is %s\n"), gametypestr);
@ -4999,10 +5020,10 @@ void D_GameTypeChanged(INT32 lastgametype)
{
const char *oldgt = NULL, *newgt = NULL;
if (lastgametype >= 0 && lastgametype < gametypecount)
oldgt = Gametype_Names[lastgametype];
if (gametype >= 0 && lastgametype < gametypecount)
newgt = Gametype_Names[gametype];
if (lastgametype >= 0 && lastgametype < numgametypes)
oldgt = gametypes[lastgametype]->name;
if (gametype >= 0 && gametype < numgametypes)
newgt = gametypes[gametype]->name;
if (oldgt && newgt)
CONS_Printf(M_GetText("Gametype was changed from %s to %s\n"), oldgt, newgt);
@ -5014,11 +5035,11 @@ void D_GameTypeChanged(INT32 lastgametype)
{
if (!cv_timelimit.changed) // user hasn't changed limits
{
CV_SetValue(&cv_timelimit, timelimits[gametype]);
CV_SetValue(&cv_timelimit, gametypes[gametype]->timelimit);
}
if (!cv_pointlimit.changed)
{
CV_SetValue(&cv_pointlimit, pointlimits[gametype]);
CV_SetValue(&cv_pointlimit, gametypes[gametype]->pointlimit);
}
}
@ -5313,11 +5334,29 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum)
// Strip illegal Encore flag.
if ((gt & VOTEMODIFIER_ENCORE)
&& !(gametypedefaultrules[(gt & ~VOTEMODIFIER_ENCORE)] & GTR_CIRCUIT))
&& !(gametypes[(gt & ~VOTEMODIFIER_ENCORE)]->rules & GTR_ENCORE))
{
gt &= ~VOTEMODIFIER_ENCORE;
}
if ((gt & ~VOTEMODIFIER_ENCORE) >= numgametypes)
{
gt &= ~VOTEMODIFIER_ENCORE;
if (server)
I_Error("Got_SetupVotecmd: Internal gametype ID %d not found (numgametypes = %d)", gt, numgametypes);
CONS_Alert(CONS_WARNING, M_GetText("Vote setup with bad gametype ID %d received from %s\n"), gt, player_names[playernum]);
return;
}
if ((secondgt & ~VOTEMODIFIER_ENCORE) >= numgametypes)
{
secondgt &= ~VOTEMODIFIER_ENCORE;
if (server)
I_Error("Got_SetupVotecmd: Internal second gametype ID %d not found (numgametypes = %d)", secondgt, numgametypes);
CONS_Alert(CONS_WARNING, M_GetText("Vote setup with bad second gametype ID %d received from %s\n"), secondgt, player_names[playernum]);
return;
}
for (i = 0; i < 4; i++)
{
tempvotelevels[i][0] = (UINT16)READUINT16(*cp);
@ -5333,11 +5372,11 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum)
// If third entry has an illelegal Encore flag... (illelegal!?)
if ((secondgt & VOTEMODIFIER_ENCORE)
&& !(gametypedefaultrules[(secondgt & ~VOTEMODIFIER_ENCORE)] & GTR_CIRCUIT))
&& !(gametypes[(secondgt & ~VOTEMODIFIER_ENCORE)]->rules & GTR_ENCORE))
{
secondgt &= ~VOTEMODIFIER_ENCORE;
// Apply it to the second entry instead, gametype permitting!
if (gametypedefaultrules[gt] & GTR_CIRCUIT)
if (gametypes[gt]->rules & GTR_ENCORE)
{
tempvotelevels[1][1] |= VOTEMODIFIER_ENCORE;
}
@ -5737,11 +5776,11 @@ void Command_Retry_f(void)
{
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
}
else if (grandprixinfo.gp == false && bossinfo.boss == false)
else if (grandprixinfo.gp == false)
{
CONS_Printf(M_GetText("This only works in singleplayer games.\n"));
}
else if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)
else if (grandprixinfo.eventmode == GPEVENT_BONUS)
{
CONS_Printf(M_GetText("You can't retry right now!\n"));
}

View file

@ -83,8 +83,6 @@ extern consvar_t cv_kartspeed;
extern consvar_t cv_kartbumpers;
extern consvar_t cv_kartfrantic;
extern consvar_t cv_kartencore;
extern consvar_t cv_kartvoterulechanges;
extern consvar_t cv_kartgametypepreference;
extern consvar_t cv_kartspeedometer;
extern consvar_t cv_kartvoices;
extern consvar_t cv_kartbot;

View file

@ -309,11 +309,16 @@ static inline int lib_getenum(lua_State *L)
}
else if (fastncmp("GT_", word, 3)) {
p = word;
for (i = 0; Gametype_ConstantNames[i]; i++)
if (fastcmp(p, Gametype_ConstantNames[i])) {
i = 0;
while (gametypes[i] != NULL)
{
if (fastcmp(p, gametypes[i]->constant))
{
lua_pushinteger(L, i);
return 1;
}
i++;
}
if (mathlib) return luaL_error(L, "gametype '%s' could not be found.\n", word);
return 0;
}

View file

@ -764,13 +764,13 @@ void readgametype(MYFILE *f, char *gtname)
char *tmp;
INT32 i, j;
INT16 newgtidx = 0;
gametype_t *newgametype = NULL;
UINT32 newgtrules = 0;
UINT32 newgttol = 0;
INT32 newgtpointlimit = 0;
INT32 newgttimelimit = 0;
INT16 newgtrankingstype = -1;
int newgtinttype = 0;
UINT8 newgtinttype = 0;
char gtconst[MAXLINELEN];
// Empty strings.
@ -821,12 +821,6 @@ void readgametype(MYFILE *f, char *gtname)
newgtpointlimit = (INT32)i;
else if (fastcmp(word, "DEFAULTTIMELIMIT"))
newgttimelimit = (INT32)i;
// Rankings type
else if (fastcmp(word, "RANKINGTYPE"))
{
// Case insensitive
newgtrankingstype = (int)get_number(word2);
}
// Intermission type
else if (fastcmp(word, "INTERMISSIONTYPE"))
{
@ -879,36 +873,54 @@ void readgametype(MYFILE *f, char *gtname)
Z_Free(word2lwr);
// Ran out of gametype slots
if (gametypecount == NUMGAMETYPEFREESLOTS)
if (numgametypes == GT_LASTFREESLOT)
{
CONS_Alert(CONS_WARNING, "Ran out of free gametype slots!\n");
I_Error("Out of Gametype Freeslots while allocating \"%s\"\nLoad less addons to fix this.", gtname);
}
if (gtname[0] == '\0')
{
deh_warning("Custom gametype must have a name");
return;
}
if (strlen(gtname) >= MAXGAMETYPELENGTH)
{
deh_warning("Custom gametype \"%s\"'s name must be %d long at most", gtname, MAXGAMETYPELENGTH-1);
return;
}
for (i = 0; i < numgametypes; i++)
if (fastcmp(gtname, gametypes[i]->name))
break;
if (i < numgametypes)
{
deh_warning("Custom gametype \"%s\"'s name is already in use", gtname);
return;
}
// Add the new gametype
newgtidx = G_AddGametype(newgtrules);
G_AddGametypeTOL(newgtidx, newgttol);
newgametype = Z_Calloc(sizeof (gametype_t), PU_STATIC, NULL);
if (!newgametype)
{
I_Error("Out of memory allocating gametype \"%s\"", gtname);
}
// Not covered by G_AddGametype alone.
if (newgtrankingstype == -1)
newgtrankingstype = newgtidx;
gametyperankings[newgtidx] = newgtrankingstype;
intermissiontypes[newgtidx] = newgtinttype;
pointlimits[newgtidx] = newgtpointlimit;
timelimits[newgtidx] = newgttimelimit;
// Write the new gametype name.
Gametype_Names[newgtidx] = Z_StrDup((const char *)gtname);
// Write the constant name.
if (gtconst[0] == '\0')
strncpy(gtconst, gtname, MAXLINELEN);
G_AddGametypeConstant(newgtidx, (const char *)gtconst);
// Update gametype_cons_t accordingly.
G_UpdateGametypeSelections();
newgametype->name = Z_StrDup((const char *)gtname);
newgametype->rules = newgtrules;
newgametype->constant = G_PrepareGametypeConstant((const char *)gtconst);
newgametype->tol = newgttol;
newgametype->intermission = newgtinttype;
newgametype->pointlimit = newgtpointlimit;
newgametype->timelimit = newgttimelimit;
CONS_Printf("Added gametype %s\n", Gametype_Names[newgtidx]);
gametypes[numgametypes++] = newgametype;
CONS_Printf("Added gametype %s\n", gtname);
}
void readlevelheader(MYFILE *f, char * name)
@ -1128,7 +1140,7 @@ void readlevelheader(MYFILE *f, char * name)
}
else if (fastcmp(word, "TYPEOFLEVEL"))
{
if (i) // it's just a number
if (i || isdigit(word2[0])) // it's just a number
mapheaderinfo[num]->typeoflevel = (UINT32)i;
else
{
@ -1267,12 +1279,12 @@ void readlevelheader(MYFILE *f, char * name)
else
mapheaderinfo[num]->menuflags &= ~LF2_NOTIMEATTACK;
}
else if (fastcmp(word, "VISITNEEDED"))
else if (fastcmp(word, "FINISHNEEDED"))
{
if (i || word2[0] == 'T' || word2[0] == 'Y')
mapheaderinfo[num]->menuflags |= LF2_VISITNEEDED;
mapheaderinfo[num]->menuflags |= LF2_FINISHNEEDED;
else
mapheaderinfo[num]->menuflags &= ~LF2_VISITNEEDED;
mapheaderinfo[num]->menuflags &= ~LF2_FINISHNEEDED;
}
else if (fastcmp(word, "GRAVITY"))
mapheaderinfo[num]->gravity = FLOAT_TO_FIXED(atof(word2));
@ -2267,6 +2279,8 @@ void readunlockable(MYFILE *f, INT32 num)
unlockables[num].type = SECRET_TIMEATTACK;
else if (fastcmp(word2, "BREAKTHECAPSULES"))
unlockables[num].type = SECRET_BREAKTHECAPSULES;
else if (fastcmp(word2, "SPECIALATTACK"))
unlockables[num].type = SECRET_SPECIALATTACK;
else if (fastcmp(word2, "SOUNDTEST"))
unlockables[num].type = SECRET_SOUNDTEST;
else if (fastcmp(word2, "ALTTITLE"))
@ -3670,7 +3684,7 @@ sfxenum_t get_sfx(const char *word)
return atoi(word);
if (fastncmp("GT_",word,3))
word += 3; // take off the GT_
for (i = 0; i < NUMGAMETYPES; i++)
for (i = 0; i < MAXGAMETYPES; i++)
if (fastcmp(word, Gametype_ConstantNames[i]+3))
return i;
deh_warning("Couldn't find gametype named 'GT_%s'",word);

View file

@ -5791,38 +5791,36 @@ const char *const PLAYERFLAG_LIST[] = {
};
const char *const GAMETYPERULE_LIST[] = {
"CAMPAIGN",
"RINGSLINGER",
"SPECTATORS",
"LIVES",
"TEAMS",
"FIRSTPERSON",
"CIRCUIT",
"BOTS",
"BUMPERS",
"SPHERES",
"CLOSERPLAYERS",
"BATTLESTARTS",
"PAPERITEMS",
"POWERSTONES",
"TEAMFLAGS",
"FRIENDLY",
"SPECIALSTAGES",
"EMERALDTOKENS",
"EMERALDHUNT",
"RACE",
"TAG",
"KARMA",
"ITEMARROWS",
"CAPSULES",
"CATCHER",
"ROLLINGSTART",
"SPECIALSTART",
"BOSS",
"POINTLIMIT",
"TIMELIMIT",
"OVERTIME",
"HURTMESSAGES",
"FRIENDLYFIRE",
"STARTCOUNTDOWN",
"HIDEFROZEN",
"BLINDFOLDED",
"RESPAWNDELAY",
"PITYSHIELD",
"DEATHPENALTY",
"NOSPECTATORSPAWN",
"DEATHMATCHSTARTS",
"SPAWNINVUL",
"SPAWNENEMIES",
"ALLOWEXIT",
"NOTITLECARD",
"CUTSCENES",
"ENCORE",
"TEAMS",
"NOTEAMS",
"TEAMSTARTS",
"NOMP",
"NOCUPSELECT",
NULL
};
@ -6305,7 +6303,7 @@ struct int_const_s const INT_CONST[] = {
{"LF2_HIDEINMENU",LF2_HIDEINMENU},
{"LF2_HIDEINSTATS",LF2_HIDEINSTATS},
{"LF2_NOTIMEATTACK",LF2_NOTIMEATTACK},
{"LF2_VISITNEEDED",LF2_VISITNEEDED},
{"LF2_FINISHNEEDED",LF2_FINISHNEEDED},
// Emeralds
{"EMERALD_CHAOS1",EMERALD_CHAOS1},
@ -6403,9 +6401,9 @@ struct int_const_s const INT_CONST[] = {
// Intermission types
{"int_none",int_none},
{"int_race",int_race},
{"int_battle",int_battle},
{"int_battletime", int_battletime},
{"int_time",int_time},
{"int_score",int_score},
{"int_scoreortimeattack", int_scoreortimeattack},
// Jingles (jingletype_t)
{"JT_NONE",JT_NONE},

View file

@ -499,8 +499,8 @@ void DRPC_UpdatePresence(void)
else
{
snprintf(detailstr, 48, "%s%s%s",
gametype_cons_t[gametype].strvalue,
(gametype == GT_RACE) ? va(" | %s", kartspeed_cons_t[gamespeed].strvalue) : "",
gametypes[gametype]->name,
(gametyperules & GTR_CIRCUIT) ? va(" | %s", kartspeed_cons_t[gamespeed].strvalue) : "",
(encoremode == true) ? " | Encore" : ""
);
discordPresence.details = detailstr;

View file

@ -664,7 +664,8 @@ UINT32 quickncasehash (const char *p, size_t n)
// Compile date and time and revision.
extern const char *compdate, *comptime, *comprevision, *compbranch;
extern int compuncommitted;
extern int compuncommitted, compoptimized;
extern const char *comptype;
// Disabled code and code under testing
// None of these that are disabled in the normal build are guaranteed to work perfectly

View file

@ -135,9 +135,9 @@ extern boolean usedCheats;
extern boolean imcontinuing; // Temporary flag while continuing
extern boolean metalrecording;
#define ATTACKING_NONE 0
#define ATTACKING_TIME 1
#define ATTACKING_CAPSULES 2
#define ATTACKING_NONE 0
#define ATTACKING_TIME 1
#define ATTACKING_LAP (1<<1)
extern UINT8 modeattacking;
// menu demo things
@ -151,15 +151,9 @@ extern boolean addedtogame; // true after the server has added you
// Only true if >1 player. netgame => multiplayer but not (multiplayer=>netgame)
extern boolean multiplayer;
extern INT16 gametype;
extern UINT32 gametyperules;
extern INT16 gametypecount;
extern UINT8 splitscreen;
extern int r_splitscreen;
extern boolean circuitmap; // Does this level have 'circuit mode'?
extern boolean fromlevelselect;
extern boolean forceresetplayers, deferencoremode;
@ -449,71 +443,97 @@ struct mapheader_t
#define LF_SECTIONRACE (1<<2) ///< Section race level
#define LF_SUBTRACTNUM (1<<3) ///< Use subtractive position number (for bright levels)
#define LF2_HIDEINMENU (1<<0) ///< Hide in the multiplayer menu
#define LF2_HIDEINSTATS (1<<1) ///< Hide in the statistics screen
#define LF2_NOTIMEATTACK (1<<2) ///< Hide this map in Time Attack modes
#define LF2_VISITNEEDED (1<<3) ///< Not available in Time Attack modes until you visit the level
#define LF2_HIDEINMENU (1<<0) ///< Hide in the multiplayer menu
#define LF2_HIDEINSTATS (1<<1) ///< Hide in the statistics screen
#define LF2_NOTIMEATTACK (1<<2) ///< Hide this map in Time Attack modes
#define LF2_FINISHNEEDED (1<<3) ///< Not available in Time Attack modes until you beat the level
extern mapheader_t** mapheaderinfo;
extern INT32 nummapheaders, mapallocsize;
// Gametypes
#define NUMGAMETYPEFREESLOTS 128
#define NUMGAMETYPEFREESLOTS (MAXGAMETYPES-GT_FIRSTFREESLOT)
#define MAXGAMETYPELENGTH 32
enum GameType
{
GT_RACE = 0,
GT_BATTLE,
GT_SPECIAL,
GT_VERSUS,
GT_FIRSTFREESLOT,
GT_LASTFREESLOT = GT_FIRSTFREESLOT + NUMGAMETYPEFREESLOTS - 1,
NUMGAMETYPES
GT_LASTFREESLOT = 127, // Previously (GT_FIRSTFREESLOT + NUMGAMETYPEFREESLOTS - 1) - it would be necessary to rewrite VOTEMODIFIER_ENCORE to go higher than this.
MAXGAMETYPES
};
// If you alter this list, update deh_tables.c, MISC_ChangeGameTypeMenu in m_menu.c, and Gametype_Names in g_game.c
// If you alter this list, update defaultgametypes and *gametypes in g_game.c
#define MAXTOL (1<<31)
#define NUMBASETOLNAMES (5)
#define NUMTOLNAMES (NUMBASETOLNAMES + NUMGAMETYPEFREESLOTS)
struct gametype_t
{
const char *name;
const char *constant;
UINT32 rules;
UINT32 tol;
UINT8 intermission;
INT32 pointlimit;
INT32 timelimit;
};
extern gametype_t *gametypes[MAXGAMETYPES+1];
extern INT16 numgametypes;
extern INT16 gametype;
// Gametype rules
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_CIRCUIT = 1, // Enables the finish line, laps, and the waypoint system.
GTR_BOTS = 1<<1, // Allows bots in this gametype. Combine with BotTiccmd hooks to make bots support your gametype.
// Battle gametype rules
GTR_BUMPERS = 1<<3, // Enables the bumper health system
GTR_SPHERES = 1<<4, // Replaces rings with blue spheres
GTR_PAPERITEMS = 1<<5, // Replaces item boxes with paper item spawners
GTR_WANTED = 1<<6, // unused
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_CAPSULES = 1<<9, // Enables the wanted anti-camping system
GTR_BATTLESTARTS = 1<<10, // Use Battle Mode start positions.
GTR_BUMPERS = 1<<2, // Enables the bumper health system
GTR_SPHERES = 1<<3, // Replaces rings with blue spheres
GTR_CLOSERPLAYERS = 1<<4, // Buffs spindash and draft power to bring everyone together, nerfs invincibility and grow to prevent excessive combos
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_BATTLESTARTS = 1<<5, // Use Battle Mode start positions.
GTR_PAPERITEMS = 1<<6, // Replaces item boxes with paper item spawners
GTR_POWERSTONES = 1<<7, // Battle Emerald collectables.
GTR_KARMA = 1<<8, // Enables the Karma system if you're out of bumpers
GTR_ITEMARROWS = 1<<9, // Show item box arrows above players
// 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
// Bonus gametype rules
GTR_CAPSULES = 1<<10, // Can enter Break The Capsules mode
GTR_CATCHER = 1<<11, // UFO Catcher (only works with GTR_CIRCUIT)
GTR_ROLLINGSTART = 1<<12, // Rolling start (only works with GTR_CIRCUIT)
GTR_SPECIALSTART = 1<<13, // White fade instant start
GTR_BOSS = 1<<14, // Boss intro and spawning
// Grand Prix rules
GTR_CAMPAIGN = 1<<17, // Handles cup-based progression
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.
// General purpose rules
GTR_POINTLIMIT = 1<<15, // Reaching point limit ends the round
GTR_TIMELIMIT = 1<<16, // Reaching time limit ends the round
GTR_OVERTIME = 1<<17, // Allow overtime behavior
GTR_ENCORE = 1<<18, // Alternate Encore mirroring, scripting, and texture remapping
GTR_NOCUPSELECT = 1<<20, // Your maps are not selected via cup. ...mutually exclusive with GTR_CAMPAIGN.
GTR_TEAMS = 1<<19, // Teams are forced on
GTR_NOTEAMS = 1<<20, // Teams are forced off
GTR_TEAMSTARTS = 1<<21, // Use team-based start positions
GTR_NOMP = 1<<22, // No multiplayer
GTR_NOCUPSELECT = 1<<23, // Your maps are not selected via cup.
// free: to and including 1<<31
};
// Remember to update GAMETYPERULE_LIST in deh_soc.c
// String names for gametypes
extern const char *Gametype_Names[NUMGAMETYPES];
extern const char *Gametype_ConstantNames[NUMGAMETYPES];
#define GTR_FORBIDMP (GTR_NOMP|GTR_CATCHER|GTR_BOSS)
// Point and time limits for every gametype
extern INT32 pointlimits[NUMGAMETYPES];
extern INT32 timelimits[NUMGAMETYPES];
// TODO: replace every instance
#define gametyperules (gametypes[gametype]->rules)
// TypeOfLevel things
enum TypeOfLevel
@ -527,9 +547,10 @@ enum TypeOfLevel
// Modifiers
TOL_TV = 0x0100 ///< Midnight Channel specific: draw TV like overlay on HUD
};
// Make sure to update TYPEOFLEVEL too
#define MAXTOL (1<<31)
#define NUMBASETOLNAMES (4)
#define NUMBASETOLNAMES (5)
#define NUMTOLNAMES (NUMBASETOLNAMES + NUMGAMETYPEFREESLOTS)
struct tolinfo_t

View file

@ -1889,6 +1889,52 @@ void F_StartTitleScreen(void)
F_CacheTitleScreen();
}
void F_VersionDrawer(void)
{
// An adapted thing from old menus - most games have version info on the title screen now...
INT32 texty = vid.height - 10*vid.dupy;
#define addtext(f, str) {\
V_DrawThinString(vid.dupx, texty, V_NOSCALESTART|f, str);\
texty -= 10*vid.dupy;\
}
if (customversionstring[0] != '\0')
{
addtext(V_ALLOWLOWERCASE, customversionstring);
addtext(0, "Mod version:");
}
else
{
// Development -- show revision / branch info
#if defined(TESTERS)
addtext(V_ALLOWLOWERCASE|V_SKYMAP, "Tester client");
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", compdate));
#elif defined(HOSTTESTERS)
addtext(V_ALLOWLOWERCASE|V_REDMAP, "Netgame host for testers");
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", compdate));
#elif defined(DEVELOP)
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, comprevision);
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, compbranch);
#else // Regular build
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", VERSIONSTRING));
#endif
if (compoptimized)
{
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s build", comptype));
}
else
{
addtext(V_ALLOWLOWERCASE|V_ORANGEMAP, va("%s build (no optimizations)", comptype));
}
if (compuncommitted)
{
addtext(V_REDMAP|V_STRINGDANCE, "! UNCOMMITTED CHANGES !");
}
}
#undef addtext
}
// (no longer) De-Demo'd Title Screen
void F_TitleScreenDrawer(void)
{
@ -1959,39 +2005,7 @@ void F_TitleScreenDrawer(void)
V_DrawFixedPatch(0, 0, FRACUNIT, 0, kts_copyright, NULL);
// An adapted thing from old menus - most games have version info on the title screen now...
{
INT32 texty = vid.height - 10*vid.dupy;
#define addtext(f, str) {\
V_DrawThinString(vid.dupx, texty, V_NOSCALESTART|f, str);\
texty -= 10*vid.dupy;\
}
if (customversionstring[0] != '\0')
{
addtext(V_ALLOWLOWERCASE, customversionstring);
addtext(0, "Mod version:");
}
else
{
// Development -- show revision / branch info
#if defined(TESTERS)
addtext(V_ALLOWLOWERCASE|V_SKYMAP, "Tester client");
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", compdate));
#elif defined(HOSTTESTERS)
addtext(V_ALLOWLOWERCASE|V_REDMAP, "Netgame host for testers");
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", compdate));
#elif defined(DEVELOP)
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, comprevision);
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, compbranch);
#else // Regular build
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", VERSIONSTRING));
#endif
if (compuncommitted)
addtext(V_REDMAP|V_STRINGDANCE, "! UNCOMMITTED CHANGES !");
}
#undef addtext
}
F_VersionDrawer();
break;
}

View file

@ -60,6 +60,8 @@ void F_EndingDrawer(void);
void F_CreditTicker(void);
void F_CreditDrawer(void);
void F_VersionDrawer(void);
void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean resetplayer);
void F_CutsceneDrawer(void);
void F_EndCutScene(void);

View file

@ -119,18 +119,14 @@ demoghost *ghosts = NULL;
#define DEMOVERSION 0x0007
#define DEMOHEADER "\xF0" "KartReplay" "\x0F"
#define DF_GHOST 0x01 // This demo contains ghost data too!
#define DF_TIMEATTACK 0x02 // This demo is from Time Attack and contains its final completion time & best lap!
#define DF_BREAKTHECAPSULES 0x04 // This demo is from Break the Capsules and contains its final completion time!
#define DF_ATTACKMASK 0x06 // This demo is from ??? attack and contains ???
#define DF_ATTACKMASK (ATTACKING_TIME|ATTACKING_LAP) // This demo contains time/lap data
// 0x08 free
#define DF_GHOST 0x08 // This demo contains ghost data too!
#define DF_NONETMP 0x10 // multiplayer but not netgame
#define DF_LUAVARS 0x20 // this demo contains extra lua vars
#define DF_ATTACKSHIFT 1
#define DF_ENCORE 0x40
#define DF_MULTIPLAYER 0x80 // This demo was recorded in multiplayer mode!
@ -2341,10 +2337,19 @@ void G_BeginRecording(void)
memset(name,0,sizeof(name));
demobuf.p = demobuf.buffer;
demoflags = DF_GHOST|(multiplayer ? DF_MULTIPLAYER : (modeattacking<<DF_ATTACKSHIFT));
if (multiplayer && !netgame)
demoflags |= DF_NONETMP;
demoflags = DF_GHOST;
if (multiplayer)
{
demoflags |= DF_MULTIPLAYER;
if (!netgame)
demoflags |= DF_NONETMP;
}
else
{
demoflags |= modeattacking;
}
if (encoremode)
demoflags |= DF_ENCORE;
@ -2375,7 +2380,9 @@ void G_BeginRecording(void)
M_Memcpy(demobuf.p, mapmd5, 16); demobuf.p += 16;
WRITEUINT8(demobuf.p, demoflags);
WRITEUINT8(demobuf.p, gametype & 0xFF);
WRITESTRINGN(demobuf.p, gametypes[gametype]->name, MAXGAMETYPELENGTH);
WRITEUINT8(demobuf.p, numlaps);
// file list
@ -2384,21 +2391,18 @@ void G_BeginRecording(void)
// character list
G_SaveDemoSkins(&demobuf.p);
switch ((demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT)
if ((demoflags & DF_ATTACKMASK))
{
case ATTACKING_NONE: // 0
break;
case ATTACKING_TIME: // 1
demotime_p = demobuf.p;
demotime_p = demobuf.p;
if (demoflags & ATTACKING_TIME)
WRITEUINT32(demobuf.p,UINT32_MAX); // time
if (demoflags & ATTACKING_LAP)
WRITEUINT32(demobuf.p,UINT32_MAX); // lap
break;
case ATTACKING_CAPSULES: // 2
demotime_p = demobuf.p;
WRITEUINT32(demobuf.p,UINT32_MAX); // time
break;
default: // 3
break;
}
else
{
demotime_p = NULL;
}
for (i = 0; i < PRNUMCLASS; i++)
@ -2602,18 +2606,15 @@ void G_SetDemoTime(UINT32 ptime, UINT32 plap)
{
if (!demo.recording || !demotime_p)
return;
if (demoflags & DF_TIMEATTACK)
if (demoflags & ATTACKING_TIME)
{
WRITEUINT32(demotime_p, ptime);
}
if (demoflags & ATTACKING_LAP)
{
WRITEUINT32(demotime_p, plap);
demotime_p = NULL;
}
else if (demoflags & DF_BREAKTHECAPSULES)
{
WRITEUINT32(demotime_p, ptime);
(void)plap;
demotime_p = NULL;
}
demotime_p = NULL;
}
// Returns bitfield:
@ -2624,7 +2625,8 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
{
UINT8 *buffer,*p;
UINT8 flags;
UINT32 oldtime, newtime, oldlap, newlap;
UINT32 oldtime = UINT32_MAX, newtime = UINT32_MAX;
UINT32 oldlap = UINT32_MAX, newlap = UINT32_MAX;
UINT16 oldversion;
size_t bufsize ATTRUNUSED;
UINT8 c;
@ -2654,23 +2656,22 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
SKIPSTRING(p); // gamemap
p += 16; // map md5
flags = READUINT8(p); // demoflags
p++; // gametype
SKIPSTRING(p); // gametype
p++; // numlaps
G_SkipDemoExtraFiles(&p);
G_SkipDemoSkins(&p);
aflags = flags & (DF_TIMEATTACK|DF_BREAKTHECAPSULES);
aflags = flags & DF_ATTACKMASK;
I_Assert(aflags);
if (flags & DF_TIMEATTACK)
uselaps = true; // get around uninitalized error
if (aflags & ATTACKING_LAP)
uselaps = true;
newtime = READUINT32(p);
if (aflags & ATTACKING_TIME)
newtime = READUINT32(p);
if (uselaps)
newlap = READUINT32(p);
else
newlap = UINT32_MAX;
Z_Free(buffer);
@ -2714,7 +2715,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
SKIPSTRING(p); // gamemap
p += 16; // mapmd5
flags = READUINT8(p);
p++; // gametype
SKIPSTRING(p); // gametype
p++; // numlaps
G_SkipDemoExtraFiles(&p);
if (!(flags & aflags))
@ -2726,11 +2727,10 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
G_SkipDemoSkins(&p);
oldtime = READUINT32(p);
if (flags & ATTACKING_TIME)
oldtime = READUINT32(p);
if (uselaps)
oldlap = READUINT32(p);
else
oldlap = 0;
Z_Free(buffer);
@ -2760,7 +2760,7 @@ void G_LoadDemoInfo(menudemo_t *pdemo)
UINT8 version, subversion, pdemoflags, worknumskins, skinid;
democharlist_t *skinlist = NULL;
UINT16 pdemoversion, count;
char mapname[MAXMAPLUMPNAME];
char mapname[MAXMAPLUMPNAME],gtname[MAXGAMETYPELENGTH];
INT32 i;
if (!FIL_ReadFile(pdemo->filepath, &infobuffer))
@ -2824,7 +2824,9 @@ void G_LoadDemoInfo(menudemo_t *pdemo)
return;
}
pdemo->gametype = READUINT8(info_p);
READSTRINGN(info_p, gtname, sizeof(gtname)); // gametype
pdemo->gametype = G_GetGametypeByName(gtname);
pdemo->numlaps = READUINT8(info_p);
pdemo->addonstatus = G_CheckDemoExtraFiles(&info_p, true);
@ -2936,9 +2938,11 @@ void G_DeferedPlayDemo(const char *name)
void G_DoPlayDemo(char *defdemoname)
{
UINT8 i, p, numslots = 0;
INT32 i;
UINT8 p, numslots = 0;
lumpnum_t l;
char color[MAXCOLORNAME+1],follower[17],mapname[MAXMAPLUMPNAME],*n,*pdemoname;
char color[MAXCOLORNAME+1],follower[17],mapname[MAXMAPLUMPNAME],gtname[MAXGAMETYPELENGTH];
char *n,*pdemoname;
UINT8 availabilities[MAXPLAYERS][MAXAVAILABILITY];
UINT8 version,subversion;
UINT32 randseed[PRNUMCLASS];
@ -2955,6 +2959,7 @@ void G_DoPlayDemo(char *defdemoname)
follower[16] = '\0';
color[MAXCOLORNAME] = '\0';
gtname[MAXGAMETYPELENGTH-1] = '\0';
// No demo name means we're restarting the current demo
if (defdemoname == NULL)
@ -3064,8 +3069,22 @@ void G_DoPlayDemo(char *defdemoname)
demobuf.p += 16; // mapmd5
demoflags = READUINT8(demobuf.p);
gametype = READUINT8(demobuf.p);
G_SetGametype(gametype);
READSTRINGN(demobuf.p, gtname, sizeof(gtname)); // gametype
i = G_GetGametypeByName(gtname);
if (i < 0)
{
snprintf(msg, 1024, M_GetText("%s is in a gametype that is not currently loaded and cannot be played.\n"), pdemoname);
CONS_Alert(CONS_ERROR, "%s", msg);
M_StartMessage(msg, NULL, MM_NOTHING);
Z_Free(pdemoname);
Z_Free(demobuf.buffer);
demo.playback = false;
demo.title = false;
return;
}
G_SetGametype(i);
numlaps = READUINT8(demobuf.p);
if (demo.title) // Titledemos should always play and ought to always be compatible with whatever wadlist is running.
@ -3138,7 +3157,7 @@ void G_DoPlayDemo(char *defdemoname)
return;
}
modeattacking = (demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT;
modeattacking = (demoflags & DF_ATTACKMASK);
multiplayer = !!(demoflags & DF_MULTIPLAYER);
demo.netgame = (multiplayer && !(demoflags & DF_NONETMP));
CON_ToggleOff();
@ -3146,21 +3165,10 @@ void G_DoPlayDemo(char *defdemoname)
hu_demotime = UINT32_MAX;
hu_demolap = UINT32_MAX;
switch (modeattacking)
{
case ATTACKING_NONE: // 0
break;
case ATTACKING_TIME: // 1
hu_demotime = READUINT32(demobuf.p);
hu_demolap = READUINT32(demobuf.p);
break;
case ATTACKING_CAPSULES: // 2
hu_demotime = READUINT32(demobuf.p);
break;
default: // 3
modeattacking = ATTACKING_NONE;
break;
}
if (modeattacking & ATTACKING_TIME)
hu_demotime = READUINT32(demobuf.p);
if (modeattacking & ATTACKING_LAP)
hu_demolap = READUINT32(demobuf.p);
// Random seed
for (i = 0; i < PRNUMCLASS; i++)
@ -3534,7 +3542,7 @@ void G_AddGhost(char *defdemoname)
return;
}
p++; // gametype
SKIPSTRING(p); // gametype
p++; // numlaps
G_SkipDemoExtraFiles(&p); // Don't wanna modify the file list for ghosts.
@ -3547,19 +3555,10 @@ void G_AddGhost(char *defdemoname)
return;
}
switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT)
{
case ATTACKING_NONE: // 0
break;
case ATTACKING_TIME: // 1
p += 8; // demo time, lap
break;
case ATTACKING_CAPSULES: // 2
p += 4; // demo time
break;
default: // 3
break;
}
if (flags & ATTACKING_TIME)
p += 4;
if (flags & ATTACKING_LAP)
p += 4;
for (i = 0; i < PRNUMCLASS; i++)
{
@ -3589,9 +3588,10 @@ void G_AddGhost(char *defdemoname)
p++; // player number - doesn't really need to be checked, TODO maybe support adding multiple players' ghosts at once
// any invalidating flags?
if ((READUINT8(p) & (DEMO_SPECTATOR|DEMO_BOT)) != 0)
i = READUINT8(p);
if ((i & (DEMO_SPECTATOR|DEMO_BOT)) != 0)
{
CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname);
CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot (spectator/bot)\n"), pdemoname);
Z_Free(skinlist);
Z_Free(pdemoname);
Z_Free(buffer);
@ -3602,6 +3602,8 @@ void G_AddGhost(char *defdemoname)
M_Memcpy(name, p, 16);
p += 16;
p += MAXAVAILABILITY;
// Skin
i = READUINT8(p);
if (i < worknumskins)
@ -3622,7 +3624,7 @@ void G_AddGhost(char *defdemoname)
if (READUINT8(p) != 0xFF)
{
CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname);
CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot (bad terminator)\n"), pdemoname);
Z_Free(skinlist);
Z_Free(pdemoname);
Z_Free(buffer);
@ -3757,25 +3759,16 @@ void G_UpdateStaffGhostName(lumpnum_t l)
goto fail; // we don't NEED to do it here, but whatever
}
p++; // Gametype
SKIPSTRING(p); // gametype
p++; // numlaps
G_SkipDemoExtraFiles(&p);
G_SkipDemoSkins(&p);
switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT)
{
case ATTACKING_NONE: // 0
break;
case ATTACKING_TIME: // 1
p += 8; // demo time, lap
break;
case ATTACKING_CAPSULES: // 2
p += 4; // demo time
break;
default: // 3
break;
}
if (flags & ATTACKING_TIME)
p += 4;
if (flags & ATTACKING_LAP)
p += 4;
for (i = 0; i < PRNUMCLASS; i++)
{

View file

@ -86,7 +86,7 @@ struct menudemo_t {
char title[65]; // Null-terminated for string prints
UINT16 map;
UINT8 addonstatus; // What do we need to do addon-wise to play this demo?
UINT8 gametype;
INT16 gametype;
SINT8 kartspeed; // Add OR DF_ENCORE for encore mode, idk
UINT8 numlaps;

View file

@ -317,9 +317,8 @@ typedef struct
{
INT16 *mapbuffer; // Pointer to zone memory
INT32 lastnummapheaders; // Reset if nummapheaders != this
UINT8 counttogametype; // Time to gametype change event
} randmaps_t;
static randmaps_t randmaps = {NULL, 0, 0};
static randmaps_t randmaps = {NULL, 0};
static void G_ResetRandMapBuffer(void)
{
@ -329,7 +328,6 @@ static void G_ResetRandMapBuffer(void)
randmaps.mapbuffer = Z_Malloc(randmaps.lastnummapheaders * sizeof(INT16), PU_STATIC, NULL);
for (i = 0; i < randmaps.lastnummapheaders; i++)
randmaps.mapbuffer[i] = -1;
//intentionally not resetting randmaps.counttogametype here
}
typedef struct joystickvector2_s
@ -517,13 +515,18 @@ static void G_UpdateRecordReplays(void)
players[consoleplayer].realtime = UINT32_MAX;
}
if (((mapheaderinfo[gamemap-1]->mainrecord->time == 0) || (players[consoleplayer].realtime < mapheaderinfo[gamemap-1]->mainrecord->time))
&& (players[consoleplayer].realtime < UINT32_MAX)) // DNF
if (modeattacking & ATTACKING_TIME)
{
mapheaderinfo[gamemap-1]->mainrecord->time = players[consoleplayer].realtime;
if (((mapheaderinfo[gamemap-1]->mainrecord->time == 0) || (players[consoleplayer].realtime < mapheaderinfo[gamemap-1]->mainrecord->time))
&& (players[consoleplayer].realtime < UINT32_MAX)) // DNF
mapheaderinfo[gamemap-1]->mainrecord->time = players[consoleplayer].realtime;
}
else
{
mapheaderinfo[gamemap-1]->mainrecord->time = 0;
}
if (modeattacking == ATTACKING_TIME)
if (modeattacking & ATTACKING_LAP)
{
if ((mapheaderinfo[gamemap-1]->mainrecord->lap == 0) || (bestlap < mapheaderinfo[gamemap-1]->mainrecord->lap))
mapheaderinfo[gamemap-1]->mainrecord->lap = bestlap;
@ -546,27 +549,32 @@ static void G_UpdateRecordReplays(void)
strcat(gpath, PATHSEP);
strcat(gpath, G_BuildMapName(gamemap));
snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, cv_chooseskin.string);
snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, cv_skin[0].string);
gpath = Z_StrDup(gpath);
if (FIL_FileExists(lastdemo))
if (modeattacking != ATTACKING_NONE && FIL_FileExists(lastdemo))
{
UINT8 *buf;
size_t len = FIL_ReadFile(lastdemo, &buf);
size_t len;
snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, cv_chooseskin.string);
if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
{ // Better time, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
gpath = Z_StrDup(gpath);
len = FIL_ReadFile(lastdemo, &buf);
if (modeattacking & ATTACKING_TIME)
{
snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, cv_skin[0].string);
if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
{ // Better time, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
}
}
if (modeattacking == ATTACKING_TIME)
if (modeattacking & ATTACKING_LAP)
{
snprintf(bestdemo, 255, "%s-%s-lap-best.lmp", gpath, cv_chooseskin.string);
snprintf(bestdemo, 255, "%s-%s-lap-best.lmp", gpath, cv_skin[0].string);
if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & (1<<1))
{ // Better lap time, save this demo.
if (FIL_FileExists(bestdemo))
@ -579,9 +587,9 @@ static void G_UpdateRecordReplays(void)
//CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo);
Z_Free(buf);
}
Z_Free(gpath);
Z_Free(gpath);
}
// Check emblems when level data is updated
if ((earnedEmblems = M_CheckLevelEmblems()))
@ -1409,7 +1417,7 @@ void G_StartTitleCard(void)
{
// The title card has been disabled for this map.
// Oh well.
if (!G_IsTitleCardAvailable() || demo.rewinding)
if (demo.rewinding || !G_IsTitleCardAvailable())
{
WipeStageTitle = false;
return;
@ -1424,9 +1432,9 @@ void G_StartTitleCard(void)
// play the sound
{
sfxenum_t kstart = sfx_kstart;
if (bossinfo.boss)
if (K_CheckBossIntro() == true)
kstart = sfx_ssa021;
else if (encoremode)
else if (encoremode == true)
kstart = sfx_ruby2;
S_StartSound(NULL, kstart);
}
@ -1473,11 +1481,17 @@ void G_PreLevelTitleCard(void)
//
boolean G_IsTitleCardAvailable(void)
{
#if 0
// Overwrites all other title card exceptions.
if (K_CheckBossIntro() == true)
return true;
// The current level has no name.
if (!mapheaderinfo[gamemap-1]->lvlttl[0])
return false;
#endif
// Instant white fade.
if (gametyperules & GTR_SPECIALSTART)
return false;
// The title card is available.
return true;
@ -2789,22 +2803,9 @@ mapthing_t *G_FindMapStart(INT32 playernum)
if (!playeringame[playernum])
return NULL;
// -- Spectators --
// Order in platform gametypes: Race->DM->CTF
// And, with deathmatch starts: DM->CTF->Race
if (players[playernum].spectator)
{
// In platform gametypes, spawn in Co-op starts first
// Overriden by GTR_BATTLESTARTS.
if (gametyperules & GTR_BATTLESTARTS && bossinfo.boss == false)
spawnpoint = G_FindBattleStartOrFallback(playernum);
else
spawnpoint = G_FindRaceStartOrFallback(playernum);
}
// -- Grand Prix / Time Attack --
// -- Time Attack --
// Order: Race->DM->CTF
else if (grandprixinfo.gp || modeattacking)
if (K_TimeAttackRules() == true)
spawnpoint = G_FindRaceStartOrFallback(playernum);
// -- CTF --
@ -2909,17 +2910,20 @@ void G_ExitLevel(void)
if (gamestate == GS_LEVEL)
{
UINT8 i;
boolean youlost = false;
if (bossinfo.boss == true)
boolean doretry = false;
if (!G_GametypeUsesLives())
; // never force a retry
else if (specialstageinfo.valid == true || (gametyperules & GTR_BOSS))
{
youlost = true;
doretry = true;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator && !players[i].bot)
{
if (players[i].bumpers > 0)
if (!K_IsPlayerLosing(&players[i]))
{
youlost = false;
doretry = false;
break;
}
}
@ -2927,10 +2931,10 @@ void G_ExitLevel(void)
}
else if (grandprixinfo.gp == true && grandprixinfo.eventmode == GPEVENT_NONE)
{
youlost = (grandprixinfo.wonround != true);
doretry = (grandprixinfo.wonround != true);
}
if (youlost)
if (doretry)
{
// You didn't win...
@ -2997,28 +3001,98 @@ void G_ExitLevel(void)
}
}
// See also the enum GameType in doomstat.h
const char *Gametype_Names[NUMGAMETYPES] =
static gametype_t defaultgametypes[] =
{
"Race", // GT_RACE
"Battle" // GT_BATTLE
// GT_RACE
{
"Race",
"GT_RACE",
GTR_CIRCUIT|GTR_BOTS|GTR_ENCORE,
TOL_RACE,
int_time,
0,
0,
},
// GT_BATTLE
{
"Battle",
"GT_BATTLE",
GTR_SPHERES|GTR_BUMPERS|GTR_PAPERITEMS|GTR_POWERSTONES|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_CLOSERPLAYERS,
TOL_BATTLE,
int_scoreortimeattack,
0,
2,
},
// GT_SPECIAL
{
"Special",
"GT_SPECIAL",
GTR_CATCHER|GTR_SPECIALSTART|GTR_ROLLINGSTART|GTR_CIRCUIT,
TOL_SPECIAL,
int_time,
0,
0,
},
// GT_VERSUS
{
"Versus",
"GT_VERSUS",
GTR_BOSS|GTR_SPHERES|GTR_BUMPERS|GTR_POINTLIMIT|GTR_CLOSERPLAYERS|GTR_NOCUPSELECT|GTR_ENCORE,
TOL_BOSS,
int_scoreortimeattack,
0,
0,
},
};
// For dehacked
const char *Gametype_ConstantNames[NUMGAMETYPES] =
gametype_t *gametypes[MAXGAMETYPES+1] =
{
"GT_RACE", // GT_RACE
"GT_BATTLE" // GT_BATTLE
&defaultgametypes[GT_RACE],
&defaultgametypes[GT_BATTLE],
&defaultgametypes[GT_SPECIAL],
&defaultgametypes[GT_VERSUS],
};
// Gametype rules
UINT32 gametypedefaultrules[NUMGAMETYPES] =
//
// G_GetGametypeByName
//
// Returns the number for the given gametype name string, or -1 if not valid.
//
INT32 G_GetGametypeByName(const char *gametypestr)
{
// Race
GTR_CAMPAIGN|GTR_CIRCUIT|GTR_BOTS,
// Battle
GTR_SPHERES|GTR_BUMPERS|GTR_PAPERITEMS|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME
};
INT32 i = 0;
while (gametypes[i] != NULL)
{
if (!stricmp(gametypestr, gametypes[i]->name))
return i;
i++;
}
return -1; // unknown gametype
}
//
// G_GuessGametypeByTOL
//
// Returns the first valid number for the given typeoflevel, or -1 if not valid.
//
INT32 G_GuessGametypeByTOL(UINT32 tol)
{
INT32 i = 0;
while (gametypes[i] != NULL)
{
if (tol & gametypes[i]->tol)
return i;
i++;
}
return -1; // unknown gametype
}
//
// G_SetGametype
@ -3027,41 +3101,26 @@ UINT32 gametypedefaultrules[NUMGAMETYPES] =
//
void G_SetGametype(INT16 gtype)
{
if (gtype < 0 || gtype > numgametypes)
{
I_Error("G_SetGametype: Bad gametype change %d (was %d/\"%s\")", gtype, gametype, gametypes[gametype]->name);
}
gametype = gtype;
gametyperules = gametypedefaultrules[gametype];
}
//
// G_AddGametype
//
// Add a gametype. Returns the new gametype number.
//
INT16 G_AddGametype(UINT32 rules)
{
INT16 newgtype = gametypecount;
gametypecount++;
// Set gametype rules.
gametypedefaultrules[newgtype] = rules;
Gametype_Names[newgtype] = "???";
// Update gametype_cons_t accordingly.
G_UpdateGametypeSelections();
return newgtype;
}
//
// G_AddGametypeConstant
// G_PrepareGametypeConstant
//
// Self-explanatory. Filters out "bad" characters.
//
void G_AddGametypeConstant(INT16 gtype, const char *newgtconst)
char *G_PrepareGametypeConstant(const char *newgtconst)
{
size_t r = 0; // read
size_t w = 0; // write
char *gtconst = Z_Calloc(strlen(newgtconst) + 4, PU_STATIC, NULL);
char *tmpconst = Z_Calloc(strlen(newgtconst) + 1, PU_STATIC, NULL);
size_t len = strlen(newgtconst);
char *gtconst = Z_Calloc(len + 4, PU_STATIC, NULL);
char *tmpconst = Z_Calloc(len + 1, PU_STATIC, NULL);
// Copy the gametype name.
strcpy(tmpconst, newgtconst);
@ -3121,42 +3180,10 @@ void G_AddGametypeConstant(INT16 gtype, const char *newgtconst)
// Free the temporary string.
Z_Free(tmpconst);
// Finally, set the constant string.
Gametype_ConstantNames[gtype] = gtconst;
// Finally, return the constant string.
return gtconst;
}
//
// G_UpdateGametypeSelections
//
// Updates gametype_cons_t.
//
void G_UpdateGametypeSelections(void)
{
INT32 i;
for (i = 0; i < gametypecount; i++)
{
gametype_cons_t[i].value = i;
gametype_cons_t[i].strvalue = Gametype_Names[i];
}
gametype_cons_t[NUMGAMETYPES].value = 0;
gametype_cons_t[NUMGAMETYPES].strvalue = NULL;
}
// Gametype rankings
INT16 gametyperankings[NUMGAMETYPES] =
{
GT_RACE,
GT_BATTLE,
};
// Gametype to TOL (Type Of Level)
UINT32 gametypetol[NUMGAMETYPES] =
{
TOL_RACE, // Race
TOL_BATTLE, // Battle
TOL_TV, // Midnight Channel effect
};
tolinfo_t TYPEOFLEVEL[NUMTOLNAMES] = {
{"RACE",TOL_RACE},
{"BATTLE",TOL_BATTLE},
@ -3183,51 +3210,6 @@ void G_AddTOL(UINT32 newtol, const char *tolname)
TYPEOFLEVEL[i].flag = newtol;
}
//
// G_AddGametypeTOL
//
// Assigns a type of level to a gametype.
//
void G_AddGametypeTOL(INT16 gtype, UINT32 newtol)
{
gametypetol[gtype] = newtol;
}
//
// G_GetGametypeByName
//
// Returns the number for the given gametype name string, or -1 if not valid.
//
INT32 G_GetGametypeByName(const char *gametypestr)
{
INT32 i;
for (i = 0; i < gametypecount; i++)
if (!stricmp(gametypestr, Gametype_Names[i]))
return i;
return -1; // unknown gametype
}
//
// G_IsSpecialStage
//
// Returns TRUE if
// the given map is a special stage.
//
boolean G_IsSpecialStage(INT32 mapnum)
{
mapnum--; // gamemap-based to 0 indexed
if (mapnum > nummapheaders || !mapheaderinfo[mapnum])
return false;
if (!mapheaderinfo[mapnum]->cup || mapheaderinfo[mapnum]->cup->cachedlevels[CUPCACHE_SPECIAL] != mapnum)
return false;
return true;
}
//
// G_GametypeUsesLives
//
@ -3240,13 +3222,7 @@ boolean G_GametypeUsesLives(void)
return false;
if ((grandprixinfo.gp == true) // In Grand Prix
&& (gametype == GT_RACE) // NOT in bonus round
&& grandprixinfo.eventmode == GPEVENT_NONE) // NOT in bonus
{
return true;
}
if (bossinfo.boss == true) // Fighting a boss?
&& grandprixinfo.eventmode != GPEVENT_BONUS) // NOT in bonus round
{
return true;
}
@ -3291,100 +3267,30 @@ boolean G_GametypeHasSpectators(void)
//
// G_SometimesGetDifferentGametype
//
// Oh, yeah, and we sometimes flip encore mode on here too.
// Because gametypes are no longer on the vote screen, all this does is sometimes flip encore mode.
// However, it remains a seperate function for long-term possibility.
//
INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype)
INT16 G_SometimesGetDifferentGametype(void)
{
// Most of the gametype references in this condition are intentionally not prefgametype.
// This is so a server CAN continue playing a gametype if they like the taste of it.
// The encore check needs prefgametype so can't use G_RaceGametype...
boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE, false) || encorescramble == 1)
&& ((gametyperules|gametypedefaultrules[prefgametype]) & GTR_CIRCUIT));
&& (gametyperules & GTR_ENCORE));
UINT8 encoremodifier = 0;
// -- the below is only necessary if you want to use randmaps.mapbuffer here
//if (randmaps.lastnummapheaders != nummapheaders)
//G_ResetRandMapBuffer();
if (encorepossible)
// FORCE to what was scrambled on intermission?
if (encorepossible && encorescramble != -1)
{
if (encorescramble != -1)
// FORCE to what was scrambled on intermission
if ((encorescramble != 0) != (cv_kartencore.value == 1))
{
encorepossible = (boolean)encorescramble; // FORCE to what was scrambled on intermission
}
else
{
switch (cv_kartvoterulechanges.value)
{
case 3: // always
encorepossible = true;
break;
case 2: // frequent
encorepossible = M_RandomChance(FRACUNIT>>1);
break;
case 1: // sometimes
encorepossible = M_RandomChance(FRACUNIT>>2);
break;
default:
break;
}
}
if (encorepossible != (cv_kartencore.value == 1))
encoremodifier = VOTEMODIFIER_ENCORE;
}
}
if (!cv_kartvoterulechanges.value) // never
return (gametype|encoremodifier);
if (randmaps.counttogametype > 0 && (cv_kartvoterulechanges.value != 3))
{
randmaps.counttogametype--;
return (gametype|encoremodifier);
}
switch (cv_kartvoterulechanges.value) // okay, we're having a gametype change! when's the next one, luv?
{
case 1: // sometimes
randmaps.counttogametype = 5; // per "cup"
break;
default:
// fallthrough - happens when clearing buffer, but needs a reasonable countdown if cvar is modified
case 2: // frequent
randmaps.counttogametype = 2; // ...every 1/2th-ish cup?
break;
}
// Only this response is prefgametype-based.
// todo custom gametypes
if (prefgametype == GT_BATTLE)
{
// Intentionally does not use encoremodifier!
if (cv_kartencore.value == 1)
return (GT_RACE|VOTEMODIFIER_ENCORE);
return (GT_RACE);
}
// This might appear wrong HERE, but the game will display the Encore possibility on the second voting choice instead.
return (GT_BATTLE|encoremodifier);
}
//
// G_GetGametypeColor
//
// Pretty and consistent ^u^
// See also M_GetGametypeColor (if that still exists).
//
UINT8 G_GetGametypeColor(INT16 gt)
{
if (modeattacking) // == ATTACKING_RECORD
return orangemap[0];
if (gt == GT_BATTLE)
return redmap[0];
if (gt == GT_RACE)
return skymap[0];
return 255; // FALLBACK
return (gametype|encoremodifier);
}
/** Get the typeoflevel flag needed to indicate support of a gametype.
@ -3394,7 +3300,9 @@ UINT8 G_GetGametypeColor(INT16 gt)
*/
UINT32 G_TOLFlag(INT32 pgametype)
{
return gametypetol[pgametype];
if (pgametype >= 0 && pgametype < numgametypes)
return gametypes[pgametype]->tol;
return 0;
}
INT16 G_GetFirstMapOfGametype(UINT8 pgametype)
@ -3405,7 +3313,7 @@ INT16 G_GetFirstMapOfGametype(UINT8 pgametype)
templevelsearch.cup = NULL;
templevelsearch.typeoflevel = G_TOLFlag(pgametype);
templevelsearch.cupmode = (!(gametypedefaultrules[pgametype] & GTR_NOCUPSELECT));
templevelsearch.cupmode = (!(gametypes[pgametype]->rules & GTR_NOCUPSELECT));
templevelsearch.timeattack = false;
templevelsearch.checklocked = true;
@ -3497,7 +3405,7 @@ tryagain:
if (!mapheaderinfo[ix] || mapheaderinfo[ix]->lumpnum == LUMPERROR)
continue;
if ((mapheaderinfo[ix]->typeoflevel & tolflags) != tolflags
if (!(mapheaderinfo[ix]->typeoflevel & tolflags)
|| ix == pprevmap
|| M_MapLocked(ix+1)
|| (usehellmaps != (mapheaderinfo[ix]->menuflags & LF2_HIDEINMENU))) // this is bad
@ -3716,7 +3624,6 @@ static void G_HandleSaveLevel(void)
static void G_GetNextMap(void)
{
boolean spec = G_IsSpecialStage(prevmap+1);
INT32 i;
// go to next level
@ -3733,7 +3640,7 @@ static void G_GetNextMap(void)
}
else
{
INT32 lastgametype = gametype;
INT32 lastgametype = gametype, newgametype = GT_RACE;
// 5 levels, 2 bonus stages: after rounds 2 and 4 (but flexible enough to accomodate other solutions)
UINT8 bonusmodulo = (grandprixinfo.cup->numlevels+1)/(grandprixinfo.cup->numbonus+1);
UINT8 bonusindex = (grandprixinfo.roundnum / bonusmodulo) - 1;
@ -3750,9 +3657,6 @@ static void G_GetNextMap(void)
G_SetGametype(GT_RACE);
if (gametype != lastgametype)
D_GameTypeChanged(lastgametype);
specialStage.active = false;
bossinfo.boss = false;
}
// Special stage
else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels)
@ -3773,11 +3677,11 @@ static void G_GetNextMap(void)
if (totaltotalring >= 50)
{
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_SPECIAL];
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]
&& mapheaderinfo[cupLevelNum]->typeoflevel & (TOL_SPECIAL|TOL_BOSS|TOL_BATTLE))
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
{
grandprixinfo.eventmode = GPEVENT_SPECIAL;
nextmap = cupLevelNum;
newgametype = G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel);
}
}
}
@ -3788,37 +3692,27 @@ static void G_GetNextMap(void)
// todo any other condition?
{
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_BONUS + bonusindex];
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]
&& mapheaderinfo[cupLevelNum]->typeoflevel & (TOL_BOSS|TOL_BATTLE))
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
{
grandprixinfo.eventmode = GPEVENT_BONUS;
nextmap = cupLevelNum;
newgametype = G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel);
}
}
}
if (newgametype == -1)
{
// Don't permit invalid changes.
grandprixinfo.eventmode = GPEVENT_NONE;
newgametype = gametype;
}
if (grandprixinfo.eventmode != GPEVENT_NONE)
{
// nextmap is set above
const INT32 newtol = mapheaderinfo[nextmap]->typeoflevel;
if (newtol & TOL_SPECIAL)
{
specialStage.active = true;
specialStage.encore = grandprixinfo.encore;
}
else //(if newtol & (TOL_BATTLE|TOL_BOSS)) -- safe to assume??
{
G_SetGametype(GT_BATTLE);
if (gametype != lastgametype)
D_GameTypeChanged(lastgametype);
if (newtol & TOL_BOSS)
{
K_ResetBossInfo();
bossinfo.boss = true;
bossinfo.encore = grandprixinfo.encore;
}
}
G_SetGametype(newgametype);
if (gametype != lastgametype)
D_GameTypeChanged(lastgametype);
}
else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map
{
@ -3842,10 +3736,6 @@ static void G_GetNextMap(void)
}
}
}
else if (bossinfo.boss == true)
{
nextmap = NEXTMAP_TITLE; // temporary
}
else
{
UINT32 tolflag = G_TOLFlag(gametype);
@ -3984,7 +3874,9 @@ static void G_GetNextMap(void)
if (nextmap == NEXTMAP_INVALID || (nextmap < NEXTMAP_SPECIAL && (nextmap >= nummapheaders || !mapheaderinfo[nextmap] || mapheaderinfo[nextmap]->lumpnum == LUMPERROR)))
I_Error("G_GetNextMap: Internal map ID %d not found (nummapheaders = %d)\n", nextmap, nummapheaders);
#if 0 // This is a surprise tool that will help us later.
if (!spec)
#endif //#if 0
lastmap = nextmap;
}
@ -4108,7 +4000,7 @@ void G_AfterIntermission(void)
G_HandleSaveLevel();
}
if ((gametyperules & GTR_CAMPAIGN) && mapheaderinfo[prevmap]->cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene.
if ((grandprixinfo.gp == true) && mapheaderinfo[prevmap]->cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene.
F_StartCustomCutscene(mapheaderinfo[prevmap]->cutscenenum-1, false, false);
else
{
@ -4245,7 +4137,7 @@ void G_EndGame(void)
}
// Only do evaluation and credits in singleplayer contexts
if (!netgame && (gametyperules & GTR_CAMPAIGN))
if (!netgame && grandprixinfo.gp == true)
{
if (nextmap == NEXTMAP_CEREMONY) // end game with ceremony
{
@ -4883,7 +4775,6 @@ cleanup:
void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ssplayers, boolean FLS)
{
UINT16 color = SKINCOLOR_NONE;
INT32 dogametype;
paused = false;
@ -4894,17 +4785,8 @@ void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ss
G_ResetRandMapBuffer();
if ((modeattacking == ATTACKING_CAPSULES) || (bossinfo.boss == true))
{
dogametype = GT_BATTLE;
}
else
{
dogametype = GT_RACE;
}
// this leave the actual game if needed
SV_StartSinglePlayerServer(dogametype, false);
SV_StartSinglePlayerServer(gametype, false);
if (splitscreen != ssplayers)
{
@ -5004,7 +4886,7 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr
automapactive = false;
imcontinuing = false;
if ((gametyperules & GTR_CAMPAIGN) && !skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking && !(marathonmode & MA_NOCUTSCENES)) // Start a custom cutscene.
if ((grandprixinfo.gp == true) && !skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking && !(marathonmode & MA_NOCUTSCENES)) // Start a custom cutscene.
F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer);
else
{

View file

@ -183,24 +183,17 @@ void G_SaveGame(UINT32 slot, INT16 mapnum);
void G_SaveGameOver(UINT32 slot, boolean modifylives);
extern UINT32 gametypedefaultrules[NUMGAMETYPES];
extern UINT32 gametypetol[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);
char *G_PrepareGametypeConstant(const char *newgtconst);
void G_AddTOL(UINT32 newtol, const char *tolname);
void G_AddGametypeTOL(INT16 gtype, UINT32 newtol);
INT32 G_GetGametypeByName(const char *gametypestr);
boolean G_IsSpecialStage(INT32 mapnum);
INT32 G_GuessGametypeByTOL(UINT32 tol);
boolean G_GametypeUsesLives(void);
boolean G_GametypeHasTeams(void);
boolean G_GametypeHasSpectators(void);
#define VOTEMODIFIER_ENCORE 0x80
INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype);
UINT8 G_GetGametypeColor(INT16 gt);
INT16 G_SometimesGetDifferentGametype(void);
void G_ExitLevel(void);
void G_NextLevel(void);
void G_Continue(void);

View file

@ -16,7 +16,7 @@
#include "hu_stuff.h"
#include "font.h"
#include "k_menu.h" // gametype_cons_t
#include "k_menu.h" // highlightflags
#include "m_cond.h" // emblems
#include "m_misc.h" // word jumping
@ -55,7 +55,8 @@
// SRB2Kart
#include "s_sound.h" // song credits
#include "k_kart.h"
#include "k_boss.h"
#include "k_battle.h"
#include "k_grandprix.h"
#include "k_color.h"
#include "k_hud.h"
#include "r_fps.h"
@ -2392,60 +2393,110 @@ static inline void HU_DrawSpectatorTicker(void)
static void HU_DrawRankings(void)
{
playersort_t tab[MAXPLAYERS];
INT32 i, j, scorelines, hilicol, numplayersingame = 0;
INT32 i, j, scorelines, numplayersingame = 0, hilicol = highlightflags;
boolean completed[MAXPLAYERS];
UINT32 whiteplayer = MAXPLAYERS;
boolean timedone = false, pointsdone = false;
V_DrawFadeScreen(0xFF00, 16); // A little more readable, and prevents cheating the fades under other circumstances.
if (modeattacking)
hilicol = V_ORANGEMAP;
else
hilicol = ((gametype == GT_RACE) ? V_SKYMAP : V_REDMAP);
// draw the current gametype in the lower right
if (modeattacking)
V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, "Record Attack");
else
V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, Gametype_Names[gametype]);
if (grandprixinfo.gp == true)
V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, "Grand Prix");
else if (battlecapsules)
V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, "Capsules");
else if (gametype >= 0 && gametype < numgametypes)
V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, gametypes[gametype]->name);
if ((gametyperules & (GTR_TIMELIMIT|GTR_POINTLIMIT)) && !bossinfo.boss)
// Left hand side
if (grandprixinfo.gp == true)
{
if ((gametyperules & GTR_TIMELIMIT) && timelimitintics > 0)
const char *roundstr = NULL;
V_DrawCenteredString(64, 8, 0, "ROUND");
switch (grandprixinfo.eventmode)
{
UINT32 timeval = (timelimitintics + starttime + 1 - leveltime);
if (timeval > timelimitintics+1)
timeval = timelimitintics+1;
timeval /= TICRATE;
case GPEVENT_BONUS:
roundstr = "BONUS";
break;
case GPEVENT_SPECIAL:
roundstr = "SPECIAL";
break;
default:
roundstr = va("%d", grandprixinfo.roundnum);
break;
}
V_DrawCenteredString(64, 16, hilicol, roundstr);
}
else if ((gametyperules & GTR_TIMELIMIT) && timelimitintics > 0)
{
UINT32 timeval = (timelimitintics + starttime + 1 - leveltime);
if (timeval > timelimitintics+1)
timeval = timelimitintics+1;
timeval /= TICRATE;
if (leveltime <= (timelimitintics + starttime))
{
V_DrawCenteredString(64, 8, 0, "TIME LEFT");
V_DrawCenteredString(64, 16, hilicol, va("%u", timeval));
}
// overtime
if (!players[consoleplayer].exiting && (leveltime > (timelimitintics + starttime + TICRATE/2)) && cv_overtime.value)
{
V_DrawCenteredString(64, 8, 0, "TIME LEFT");
V_DrawCenteredString(64, 16, hilicol, "OVERTIME");
}
if (leveltime <= (timelimitintics + starttime))
{
V_DrawCenteredString(64, 8, 0, "TIME LEFT");
V_DrawCenteredString(64, 16, hilicol, va("%u", timeval));
}
if ((gametyperules & GTR_POINTLIMIT) && cv_pointlimit.value > 0)
// overtime
if (!players[consoleplayer].exiting && (leveltime > (timelimitintics + starttime + TICRATE/2)) && cv_overtime.value)
{
V_DrawCenteredString(256, 8, 0, "POINT LIMIT");
V_DrawCenteredString(256, 16, hilicol, va("%d", cv_pointlimit.value));
V_DrawCenteredString(64, 8, 0, "TIME LEFT");
V_DrawCenteredString(64, 16, hilicol, "OVERTIME");
}
timedone = true;
}
else if ((gametyperules & GTR_POINTLIMIT) && cv_pointlimit.value > 0)
{
V_DrawCenteredString(64, 8, 0, "POINT LIMIT");
V_DrawCenteredString(64, 16, hilicol, va("%d", cv_pointlimit.value));
pointsdone = true;
}
else if (gametyperules & GTR_CIRCUIT)
{
V_DrawCenteredString(64, 8, 0, "LAPS");
V_DrawCenteredString(64, 16, hilicol, va("%d", numlaps));
}
// Right hand side
if (battlecapsules == true)
{
if (numtargets < maptargets)
{
V_DrawCenteredString(256, 8, 0, "CAPSULES");
V_DrawCenteredString(256, 16, hilicol, va("%d", maptargets - numtargets));
}
}
else
else if (!timedone && (gametyperules & GTR_TIMELIMIT) && timelimitintics > 0)
{
if (circuitmap)
UINT32 timeval = (timelimitintics + starttime + 1 - leveltime);
if (timeval > timelimitintics+1)
timeval = timelimitintics+1;
timeval /= TICRATE;
if (leveltime <= (timelimitintics + starttime))
{
V_DrawCenteredString(64, 8, 0, "LAP COUNT");
V_DrawCenteredString(64, 16, hilicol, va("%d", numlaps));
V_DrawCenteredString(256, 8, 0, "TIME LEFT");
V_DrawCenteredString(256, 16, hilicol, va("%u", timeval));
}
// overtime
if (!players[consoleplayer].exiting && (leveltime > (timelimitintics + starttime + TICRATE/2)) && cv_overtime.value)
{
V_DrawCenteredString(256, 8, 0, "TIME LEFT");
V_DrawCenteredString(256, 16, hilicol, "OVERTIME");
}
}
else if (!pointsdone && (gametyperules & GTR_POINTLIMIT) && cv_pointlimit.value > 0)
{
V_DrawCenteredString(256, 8, 0, "POINT LIMIT");
V_DrawCenteredString(256, 16, hilicol, va("%d", cv_pointlimit.value));
}
else if (gametyperules & GTR_CIRCUIT)
{
V_DrawCenteredString(256, 8, 0, "GAME SPEED");
V_DrawCenteredString(256, 16, hilicol, kartspeed_cons_t[1+gamespeed].strvalue);
}
@ -2494,13 +2545,12 @@ static void HU_DrawRankings(void)
if ((gametyperules & GTR_CIRCUIT))
{
if (circuitmap)
tab[scorelines].count = players[i].laps;
else
tab[scorelines].count = players[i].realtime;
tab[scorelines].count = players[i].laps;
}
else
{
tab[scorelines].count = players[i].roundscore;
}
scorelines++;

View file

@ -2,7 +2,6 @@
/// \brief SRB2Kart Battle Mode specific code
#include "k_battle.h"
#include "k_boss.h"
#include "k_kart.h"
#include "doomtype.h"
#include "doomdata.h"
@ -146,7 +145,7 @@ void K_CheckBumpers(void)
}
else if (numingame <= 1)
{
if (!battlecapsules)
if ((gametyperules & GTR_CAPSULES) && !battlecapsules)
{
// Reset map to turn on battle capsules
if (server)
@ -196,6 +195,11 @@ void K_CheckEmeralds(player_t *player)
{
UINT8 i;
if (!(gametyperules & GTR_POWERSTONES))
{
return;
}
if (!ALLCHAOSEMERALDS(player->emeralds))
{
return;
@ -221,6 +225,29 @@ void K_CheckEmeralds(player_t *player)
K_CheckBumpers();
}
UINT16 K_GetChaosEmeraldColor(UINT32 emeraldType)
{
switch (emeraldType)
{
case EMERALD_CHAOS1:
return SKINCOLOR_CHAOSEMERALD1;
case EMERALD_CHAOS2:
return SKINCOLOR_CHAOSEMERALD2;
case EMERALD_CHAOS3:
return SKINCOLOR_CHAOSEMERALD3;
case EMERALD_CHAOS4:
return SKINCOLOR_CHAOSEMERALD4;
case EMERALD_CHAOS5:
return SKINCOLOR_CHAOSEMERALD5;
case EMERALD_CHAOS6:
return SKINCOLOR_CHAOSEMERALD6;
case EMERALD_CHAOS7:
return SKINCOLOR_CHAOSEMERALD7;
default:
return SKINCOLOR_NONE;
}
}
mobj_t *K_SpawnChaosEmerald(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT32 emeraldType)
{
boolean validEmerald = true;
@ -240,25 +267,13 @@ mobj_t *K_SpawnChaosEmerald(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT
switch (emeraldType)
{
case EMERALD_CHAOS1:
emerald->color = SKINCOLOR_CHAOSEMERALD1;
break;
case EMERALD_CHAOS2:
emerald->color = SKINCOLOR_CHAOSEMERALD2;
break;
case EMERALD_CHAOS3:
emerald->color = SKINCOLOR_CHAOSEMERALD3;
break;
case EMERALD_CHAOS4:
emerald->color = SKINCOLOR_CHAOSEMERALD4;
break;
case EMERALD_CHAOS5:
emerald->color = SKINCOLOR_CHAOSEMERALD5;
break;
case EMERALD_CHAOS6:
emerald->color = SKINCOLOR_CHAOSEMERALD6;
break;
case EMERALD_CHAOS7:
emerald->color = SKINCOLOR_CHAOSEMERALD7;
emerald->color = K_GetChaosEmeraldColor(emeraldType);
break;
default:
CONS_Printf("Invalid emerald type %d\n", emeraldType);
@ -336,12 +351,17 @@ UINT8 K_NumEmeralds(player_t *player)
return num;
}
static inline boolean IsOnInterval(tic_t interval)
{
return ((leveltime - starttime) % interval) == 0;
}
void K_RunPaperItemSpawners(void)
{
const boolean overtime = (battleovertime.enabled >= 10*TICRATE);
tic_t interval = 8*TICRATE;
const tic_t interval = BATTLE_SPAWN_INTERVAL;
const boolean canmakeemeralds = true; //(!(battlecapsules || bossinfo.boss));
const boolean canmakeemeralds = (gametyperules & GTR_POWERSTONES);
UINT32 emeraldsSpawned = 0;
UINT32 firstUnspawnedEmerald = 0;
@ -352,7 +372,7 @@ void K_RunPaperItemSpawners(void)
UINT8 pcount = 0;
INT16 i;
if (battlecapsules || bossinfo.boss)
if (battlecapsules)
{
// Gametype uses paper items, but this specific expression doesn't
return;
@ -364,13 +384,7 @@ void K_RunPaperItemSpawners(void)
return;
}
if (overtime == true)
{
// Double frequency of items
interval /= 2;
}
if (((leveltime - starttime) % interval) != 0)
if (!IsOnInterval(interval))
{
return;
}
@ -546,7 +560,7 @@ void K_RunPaperItemSpawners(void)
}
else
{
if (gametyperules & GTR_SPHERES)
if ((gametyperules & GTR_SPHERES) && IsOnInterval(2 * interval))
{
drop = K_SpawnSphereBox(
spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip),
@ -789,7 +803,7 @@ void K_BattleInit(boolean singleplayercontext)
{
size_t i;
if ((gametyperules & GTR_CAPSULES) && singleplayercontext && !battlecapsules && !bossinfo.boss)
if ((gametyperules & GTR_CAPSULES) && singleplayercontext && !battlecapsules)
{
mapthing_t *mt = mapthings;
for (i = 0; i < nummapthings; i++, mt++)

View file

@ -8,6 +8,9 @@
extern "C" {
#endif
#define BATTLE_SPAWN_INTERVAL (4*TICRATE)
#define BATTLE_DESPAWN_TIME (15*TICRATE)
extern struct battleovertime
{
UINT16 enabled; ///< Has this been initalized yet?
@ -25,6 +28,7 @@ boolean K_IsPlayerWanted(player_t *player);
void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount);
void K_CheckBumpers(void);
void K_CheckEmeralds(player_t *player);
UINT16 K_GetChaosEmeraldColor(UINT32 emeraldType);
mobj_t *K_SpawnChaosEmerald(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT32 emeraldType);
mobj_t *K_SpawnSphereBox(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 amount);
void K_DropEmeraldsFromPlayer(player_t *player, UINT32 emeraldType);

View file

@ -23,7 +23,7 @@
struct bossinfo bossinfo;
/*--------------------------------------------------
void K_ClearBossInfo(void)
void K_ResetBossInfo(void)
See header file for description.
--------------------------------------------------*/
@ -32,6 +32,8 @@ void K_ResetBossInfo(void)
Z_Free(bossinfo.enemyname);
Z_Free(bossinfo.subtitle);
memset(&bossinfo, 0, sizeof(struct bossinfo));
bossinfo.barlen = BOSSHEALTHBARLEN;
bossinfo.titlesound = sfx_typri1;
}
/*--------------------------------------------------
@ -43,7 +45,7 @@ void K_BossInfoTicker(void)
{
UINT8 i;
if (bossinfo.boss == false)
if (bossinfo.valid == false)
return;
// Update healthbar data. (only if the hud is visible)
@ -55,7 +57,7 @@ void K_BossInfoTicker(void)
bossinfo.visualbar--;
// If the boss is dying, start shrinking the healthbar.
if (bossinfo.visualbar == 0)
bossinfo.barlen-= 2;
bossinfo.barlen -= 2;
}
// Less than the actual health?
else if (bossinfo.visualbar < bossinfo.healthbar)
@ -108,6 +110,18 @@ void K_BossInfoTicker(void)
void K_InitBossHealthBar(const char *enemyname, const char *subtitle, sfxenum_t titlesound, fixed_t pinchmagnitude, UINT8 divisions)
{
if (!(gametyperules & GTR_BOSS))
{
return;
}
bossinfo.valid = true;
if (!leveltime)
{
bossinfo.coolintro = true;
}
if (enemyname && enemyname[0])
{
Z_Free(bossinfo.enemyname);
@ -158,6 +172,9 @@ void K_InitBossHealthBar(const char *enemyname, const char *subtitle, sfxenum_t
void K_UpdateBossHealthBar(fixed_t magnitude, tic_t jitterlen)
{
if (bossinfo.valid == false)
return;
if (magnitude > FRACUNIT)
magnitude = FRACUNIT;
else if (magnitude < 0)
@ -177,6 +194,9 @@ void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean
{
UINT8 i;
if (bossinfo.valid == false)
return;
// First check whether the spot is already in the list and simply redeclaring weakness (for example, a vulnerable moment in the pattern).
for (i = 0; i < NUMWEAKSPOTS; i++)
if (bossinfo.weakspots[i].spot == spot)
@ -206,3 +226,16 @@ void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean
bossinfo.doweakspotsound = spottype;
}
}
/*--------------------------------------------------
boolean K_CheckBossIntro(void);
See header file for description.
--------------------------------------------------*/
boolean K_CheckBossIntro(void)
{
if (bossinfo.valid == false)
return false;
return bossinfo.coolintro;
}

View file

@ -43,7 +43,8 @@ struct weakspot_t
extern struct bossinfo
{
boolean boss; ///< If true, then we are fighting a boss
boolean valid; ///< If true, then data in this struct is valid
UINT8 healthbar; ///< Actual health bar fill amount
UINT8 visualbar; ///< Tracks above, but with delay
fixed_t visualdiv; ///< How far apart health bar divisions should appear
@ -52,7 +53,7 @@ extern struct bossinfo
UINT8 barlen; ///< The length of the bar (only reduced when a boss is deceased)
char *enemyname; ///< The name next to the bar
weakspot_t weakspots[NUMWEAKSPOTS]; ///< Array of weak spots (for minimap/object tracking)
boolean encore; ///< Copy of encore, just to make sure you can't cheat it with cvars
boolean coolintro; ///< Determines whether the map start(s/ed) with a boss-specific intro.
spottype_t doweakspotsound; ///< If nonzero, at least one weakspot was declared this tic
tic_t titleshow; ///< Show this many letters on the titlecard
sfxenum_t titlesound; ///< Sound to play when title typing
@ -116,6 +117,18 @@ void K_UpdateBossHealthBar(fixed_t magnitude, tic_t jitterlen);
void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean minimap);
/*--------------------------------------------------
boolean K_CheckBossIntro(void);
Checks whether the Versus-specific intro is playing for this map start.
Return:-
true if cool intro in action,
otherwise false.
--------------------------------------------------*/
boolean K_CheckBossIntro(void);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -27,7 +27,6 @@
#include "m_random.h"
#include "r_things.h" // numskins
#include "k_race.h" // finishBeamLine
#include "k_boss.h"
#include "m_perfstats.h"
@ -168,7 +167,7 @@ void K_UpdateMatchRaceBots(void)
}
}
if (difficulty == 0 || !(gametyperules & GTR_BOTS) || bossinfo.boss == true)
if (difficulty == 0 || !(gametyperules & GTR_BOTS))
{
wantedbots = 0;
}

View file

@ -324,6 +324,11 @@ tic_t K_MineExplodeAttack(mobj_t *actor, fixed_t size, boolean spin)
if (!spin)
{
if (minehitlag == 0)
{
minehitlag = actor->hitlag;
}
Obj_SpawnBrolyKi(actor, minehitlag);
return minehitlag;

View file

@ -11,7 +11,6 @@
/// \brief Grand Prix mode game logic & bot behaviors
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_specialstage.h"
#include "doomdef.h"
#include "d_player.h"
@ -339,7 +338,7 @@ void K_UpdateGrandPrixBots(void)
continue;
}
players[i].spectator = (grandprixinfo.eventmode != GPEVENT_NONE);
players[i].spectator = !(gametyperules & GTR_BOTS) || (grandprixinfo.eventmode != GPEVENT_NONE);
}
// Find the rival.
@ -529,7 +528,7 @@ void K_RetireBots(void)
UINT8 i;
if (grandprixinfo.gp == true
&& ((grandprixinfo.roundnum >= grandprixinfo.cup->numlevels)
&& ((grandprixinfo.cup != NULL && grandprixinfo.roundnum >= grandprixinfo.cup->numlevels)
|| grandprixinfo.eventmode != GPEVENT_NONE))
{
// No replacement.
@ -676,7 +675,7 @@ void K_PlayerLoseLife(player_t *player)
return;
}
if (player->spectator || player->exiting || player->bot || player->lives <= 0 || (player->pflags & PF_LOSTLIFE))
if (player->spectator || (player->exiting && !(player->pflags & PF_NOCONTEST)) || player->bot || player->lives <= 0 || (player->pflags & PF_LOSTLIFE))
{
return;
}
@ -703,24 +702,12 @@ void K_PlayerLoseLife(player_t *player)
--------------------------------------------------*/
boolean K_CanChangeRules(boolean allowdemos)
{
if (grandprixinfo.gp == true && grandprixinfo.roundnum > 0)
if (grandprixinfo.gp == true /*&& grandprixinfo.roundnum > 0*/)
{
// Don't cheat the rules of the GP!
return false;
}
if (bossinfo.boss == true)
{
// Don't cheat the boss!
return false;
}
if (specialStage.active == true)
{
// Don't cheat special stages!
return false;
}
if (marathonmode)
{
// Don't cheat the endurance challenge!

View file

@ -1863,7 +1863,7 @@ static boolean K_drawKartPositionFaces(void)
ranklines--;
i = ranklines;
if (gametype == GT_BATTLE || strank <= 2) // too close to the top, or playing battle, or a spectator? would have had (strank == -1) called out, but already caught by (strank <= 2)
if ((gametyperules & GTR_POINTLIMIT) || strank <= 2) // too close to the top, or playing battle, or a spectator? would have had (strank == -1) called out, but already caught by (strank <= 2)
{
if (i > 4) // could be both...
i = 4;
@ -1951,7 +1951,7 @@ static boolean K_drawKartPositionFaces(void)
if (i == strank)
V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]);
if (gametype == GT_BATTLE && players[rankplayer[i]].bumpers <= 0)
if ((gametyperules & GTR_BUMPERS) && players[rankplayer[i]].bumpers <= 0)
V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_ranknobumpers);
else
{
@ -1973,7 +1973,7 @@ static void K_drawBossHealthBar(void)
UINT8 i = 0, barstatus = 1, randlen = 0, darken = 0;
const INT32 startx = BASEVIDWIDTH - 23;
INT32 starty = BASEVIDHEIGHT - 25;
INT32 rolrand = 0;
INT32 rolrand = 0, randtemp = 0;
boolean randsign = false;
if (bossinfo.barlen <= 1)
@ -2019,7 +2019,9 @@ static void K_drawBossHealthBar(void)
barstatus = 2;
}
randlen = M_RandomKey(bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT)))+1;
randtemp = bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT));
if (randtemp > 0)
randlen = M_RandomKey(randtemp)+1;
randsign = M_RandomChance(FRACUNIT/2);
// Right wing.
@ -2034,7 +2036,9 @@ static void K_drawBossHealthBar(void)
randlen--;
if (!randlen)
{
randlen = M_RandomKey(bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT)))+1;
randtemp = bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT));
if (randtemp > 0)
randlen = M_RandomKey(randtemp)+1;
if (barstatus > 1)
{
rolrand = M_RandomKey(barstatus)+1;
@ -2265,7 +2269,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN
colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE);
V_DrawMappedPatch(x, y-4, 0, faceprefix[players[tab[i].num].skin][FACE_RANK], colormap);
/*if (gametype == GT_BATTLE && players[tab[i].num].bumpers > 0) -- not enough space for this
/*if ((gametyperules & GTR_BUMPERS) && players[tab[i].num].bumpers > 0) -- not enough space for this
{
INT32 bumperx = x+19;
V_DrawMappedPatch(bumperx-2, y-4, 0, kp_tinybumper[0], colormap);
@ -2280,7 +2284,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN
if (tab[i].num == whiteplayer)
V_DrawScaledPatch(x, y-4, 0, kp_facehighlight[(leveltime / 4) % 8]);
if (gametype == GT_BATTLE && players[tab[i].num].bumpers <= 0)
if ((gametyperules & GTR_BUMPERS) && players[tab[i].num].bumpers <= 0)
V_DrawScaledPatch(x-4, y-7, 0, kp_ranknobumpers);
else
{
@ -2291,7 +2295,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN
V_DrawScaledPatch(x-5, y+6, 0, kp_facenum[pos]);
}
if (gametype == GT_RACE)
if ((gametyperules & GTR_CIRCUIT))
{
#define timestring(time) va("%i'%02i\"%02i", G_TicsToMinutes(time, true), G_TicsToSeconds(time), G_TicsToCentiseconds(time))
if (scorelines >= 8)
@ -2300,7 +2304,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN
V_DrawRightAlignedThinString(x+rightoffset, y-1, hilicol|V_6WIDTHSPACE, timestring(players[tab[i].num].realtime));
else if (players[tab[i].num].pflags & PF_NOCONTEST)
V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, "NO CONTEST.");
else if (circuitmap)
else
V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, va("Lap %d", tab[i].count));
}
else
@ -2309,7 +2313,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN
V_DrawRightAlignedString(x+rightoffset, y, hilicol, timestring(players[tab[i].num].realtime));
else if (players[tab[i].num].pflags & PF_NOCONTEST)
V_DrawRightAlignedThinString(x+rightoffset, y-1, 0, "NO CONTEST.");
else if (circuitmap)
else
V_DrawRightAlignedString(x+rightoffset, y, 0, va("Lap %d", tab[i].count));
}
#undef timestring
@ -2326,40 +2330,13 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN
}
}
#define RINGANIM_FLIPFRAME (RINGANIM_NUMFRAMES/2)
static void K_drawKartLapsAndRings(void)
static void K_drawKartLaps(void)
{
const boolean uselives = G_GametypeUsesLives();
SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe];
INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN;
UINT8 rn[2];
INT32 ringflip = 0;
UINT8 *ringmap = NULL;
boolean colorring = false;
INT32 ringx = 0;
rn[0] = ((abs(stplyr->rings) / 10) % 10);
rn[1] = (abs(stplyr->rings) % 10);
if (stplyr->rings <= 0 && (leveltime/5 & 1)) // In debt
{
ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE);
colorring = true;
}
else if (stplyr->rings >= 20) // Maxed out
ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_YELLOW, GTC_CACHE);
if (stplyr->karthud[khud_ringframe] > RINGANIM_FLIPFRAME)
{
ringflip = V_FLIP;
ringanim_realframe = RINGANIM_NUMFRAMES-stplyr->karthud[khud_ringframe];
ringx += SHORT((r_splitscreen > 1) ? kp_smallring[ringanim_realframe]->width : kp_ring[ringanim_realframe]->width);
}
if (r_splitscreen > 1)
{
INT32 fx = 0, fy = 0, fr = 0;
INT32 fx = 0, fy = 0;
INT32 flipflag = 0;
// pain and suffering defined below
@ -2385,8 +2362,6 @@ static void K_drawKartLapsAndRings(void)
}
}
fr = fx;
// Laps
V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|V_SLIDEIN|splitflags|flipflag, kp_ringstickersplit[0]);
@ -2413,6 +2388,75 @@ static void K_drawKartLapsAndRings(void)
V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->laps) % 10]);
V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(numlaps) % 10]);
}
}
else
{
// Laps
V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_lapsticker);
V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", min(stplyr->laps, numlaps), numlaps));
}
}
#define RINGANIM_FLIPFRAME (RINGANIM_NUMFRAMES/2)
static void K_drawRingCounter(void)
{
const boolean uselives = G_GametypeUsesLives();
SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe];
INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN;
UINT8 rn[2];
INT32 ringflip = 0;
UINT8 *ringmap = NULL;
boolean colorring = false;
INT32 ringx = 0, fy = 0;
rn[0] = ((abs(stplyr->rings) / 10) % 10);
rn[1] = (abs(stplyr->rings) % 10);
if (stplyr->rings <= 0 && (leveltime/5 & 1)) // In debt
{
ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE);
colorring = true;
}
else if (stplyr->rings >= 20) // Maxed out
ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_YELLOW, GTC_CACHE);
if (stplyr->karthud[khud_ringframe] > RINGANIM_FLIPFRAME)
{
ringflip = V_FLIP;
ringanim_realframe = RINGANIM_NUMFRAMES-stplyr->karthud[khud_ringframe];
ringx += SHORT((r_splitscreen > 1) ? kp_smallring[ringanim_realframe]->width : kp_ring[ringanim_realframe]->width);
}
if (r_splitscreen > 1)
{
INT32 fx = 0, fr = 0;
INT32 flipflag = 0;
// pain and suffering defined below
if (r_splitscreen < 2) // don't change shit for THIS splitscreen.
{
fx = LAPS_X;
fy = LAPS_Y;
}
else
{
if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3...
{
fx = LAPS_X;
fy = LAPS_Y;
splitflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
}
else // else, that means we're P2 or P4.
{
fx = LAPS2_X;
fy = LAPS2_Y;
splitflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
flipflag = V_FLIP; // make the string right aligned and other shit
}
}
fr = fx;
// Rings
if (!uselives)
@ -2447,41 +2491,42 @@ static void K_drawKartLapsAndRings(void)
}
else
{
// Laps
V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_lapsticker);
V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", min(stplyr->laps, numlaps), numlaps));
fy = LAPS_Y-11;
if ((gametyperules & (GTR_BUMPERS|GTR_CIRCUIT)) == GTR_BUMPERS)
fy -= 4;
// Rings
if (!uselives)
V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringsticker[1]);
V_DrawScaledPatch(LAPS_X, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringsticker[1]);
else
V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringsticker[0]);
V_DrawScaledPatch(LAPS_X, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringsticker[0]);
V_DrawMappedPatch(LAPS_X+ringx+7, LAPS_Y-16, V_HUDTRANS|V_SLIDEIN|splitflags|ringflip, kp_ring[ringanim_realframe], (colorring ? ringmap : NULL));
V_DrawMappedPatch(LAPS_X+ringx+7, fy-5, V_HUDTRANS|V_SLIDEIN|splitflags|ringflip, kp_ring[ringanim_realframe], (colorring ? ringmap : NULL));
if (stplyr->rings < 0) // Draw the minus for ring debt
{
V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringdebtminus, ringmap);
V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[0]], ringmap);
V_DrawMappedPatch(LAPS_X+35, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[1]], ringmap);
V_DrawMappedPatch(LAPS_X+23, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringdebtminus, ringmap);
V_DrawMappedPatch(LAPS_X+29, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[0]], ringmap);
V_DrawMappedPatch(LAPS_X+35, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[1]], ringmap);
}
else
{
V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[0]], ringmap);
V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[1]], ringmap);
V_DrawMappedPatch(LAPS_X+23, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[0]], ringmap);
V_DrawMappedPatch(LAPS_X+29, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[1]], ringmap);
}
// SPB ring lock
if (stplyr->pflags & PF_RINGLOCK)
V_DrawScaledPatch(LAPS_X-5, LAPS_Y-28, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]);
V_DrawScaledPatch(LAPS_X-5, fy-17, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]);
// Lives
if (uselives)
{
UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE);
V_DrawMappedPatch(LAPS_X+46, LAPS_Y-16, V_HUDTRANS|V_SLIDEIN|splitflags, faceprefix[stplyr->skin][FACE_RANK], colormap);
V_DrawMappedPatch(LAPS_X+46, fy-5, V_HUDTRANS|V_SLIDEIN|splitflags, faceprefix[stplyr->skin][FACE_RANK], colormap);
if (stplyr->lives >= 0)
V_DrawScaledPatch(LAPS_X+63, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->lives % 10)]); // make sure this doesn't overflow OR underflow
V_DrawScaledPatch(LAPS_X+63, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->lives % 10)]); // make sure this doesn't overflow OR underflow
}
}
}
@ -2498,7 +2543,7 @@ static void K_drawKartAccessibilityIcons(INT32 fx)
if (r_splitscreen < 2) // adjust to speedometer height
{
if (gametype == GT_BATTLE)
if ((gametyperules & (GTR_BUMPERS|GTR_CIRCUIT)) == GTR_BUMPERS)
fy -= 4;
}
else
@ -2588,7 +2633,7 @@ static void K_drawKartSpeedometer(void)
numbers[1] = ((convSpeed / 10) % 10);
numbers[2] = (convSpeed % 10);
if (gametype == GT_BATTLE)
if ((gametyperules & (GTR_BUMPERS|GTR_CIRCUIT)) == GTR_BUMPERS)
battleoffset = -4;
V_DrawScaledPatch(LAPS_X, LAPS_Y-25 + battleoffset, V_HUDTRANS|V_SLIDEIN|splitflags, kp_speedometersticker);
@ -2793,14 +2838,15 @@ static void K_drawKartBumpersOrKarma(void)
else
V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumpersticker, colormap);
if (bossinfo.boss)
V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", stplyr->bumpers, maxbumper));
else // TODO BETTER HUD
if (gametyperules & GTR_KARMA) // TODO BETTER HUD
V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d %d", stplyr->bumpers, maxbumper, stplyr->overtimekarma / TICRATE));
else
V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", stplyr->bumpers, maxbumper));
}
}
}
#if 0
static void K_drawKartWanted(void)
{
UINT8 i, numwanted = 0;
@ -2875,6 +2921,7 @@ static void K_drawKartWanted(void)
}
}
}
#endif //if 0
static void K_drawKartPlayerCheck(void)
{
@ -3358,7 +3405,7 @@ static void K_drawKartNameTags(void)
c.z = viewz;
// Maybe shouldn't be handling this here... but the camera info is too good.
if (bossinfo.boss)
if (bossinfo.valid == true)
{
weakspotdraw_t weakspotdraw[NUMWEAKSPOTS];
UINT8 numdraw = 0;
@ -3810,7 +3857,7 @@ static void K_drawKartMinimap(void)
y -= SHORT(AutomapPic->topoffset);
// Draw the super item in Battle
if (gametype == GT_BATTLE && battleovertime.enabled)
if ((gametyperules & GTR_OVERTIME) && battleovertime.enabled)
{
if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1))
{
@ -3920,8 +3967,8 @@ static void K_drawKartMinimap(void)
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap, AutomapPic);
// Target reticule
if ((gametype == GT_RACE && players[i].position == spbplace)
|| (gametype == GT_BATTLE && K_IsPlayerWanted(&players[i])))
if (((gametyperules & GTR_CIRCUIT) && players[i].position == spbplace)
|| ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[i])))
{
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic);
}
@ -3979,7 +4026,7 @@ static void K_drawKartMinimap(void)
}
// ...but first, any boss targets.
if (bossinfo.boss)
if (bossinfo.valid == true)
{
for (i = 0; i < NUMWEAKSPOTS; i++)
{
@ -4047,8 +4094,8 @@ static void K_drawKartMinimap(void)
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap, AutomapPic);
// Target reticule
if ((gametype == GT_RACE && players[localplayers[i]].position == spbplace)
|| (gametype == GT_BATTLE && K_IsPlayerWanted(&players[localplayers[i]])))
if (((gametyperules & GTR_CIRCUIT) && players[localplayers[i]].position == spbplace)
|| ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[localplayers[i]])))
{
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic);
}
@ -4836,7 +4883,7 @@ void K_drawKartFreePlay(void)
if (!LUA_HudEnabled(hud_freeplay))
return;
if (modeattacking || grandprixinfo.gp || bossinfo.boss || stplyr->spectator)
if (modeattacking || grandprixinfo.gp || bossinfo.valid || stplyr->spectator)
return;
if (lt_exitticker < TICRATE/2)
@ -5024,10 +5071,10 @@ void K_drawKartHUD(void)
return;
}
battlefullscreen = ((gametyperules & (GTR_BUMPERS|GTR_KARMA)) == (GTR_BUMPERS|GTR_KARMA)
battlefullscreen = (!(gametyperules & GTR_CIRCUIT)
&& (stplyr->exiting
|| (stplyr->bumpers <= 0
&& stplyr->karmadelay > 0
|| ((gametyperules & GTR_BUMPERS) && (stplyr->bumpers <= 0)
&& ((gametyperules & GTR_KARMA) && (stplyr->karmadelay > 0))
&& !(stplyr->pflags & PF_ELIMINATED)
&& stplyr->playerstate == PST_LIVE)));
@ -5043,11 +5090,13 @@ void K_drawKartHUD(void)
K_drawKartNameTags();
// Draw WANTED status
#if 0
if (gametype == GT_BATTLE)
{
if (LUA_HudEnabled(hud_wanted))
K_drawKartWanted();
}
#endif
if (LUA_HudEnabled(hud_minimap))
K_drawKartMinimap();
@ -5094,32 +5143,26 @@ void K_drawKartHUD(void)
{
if (LUA_HudEnabled(hud_position))
{
if (bossinfo.boss)
if (bossinfo.valid)
{
K_drawBossHealthBar();
}
else if (gametype == GT_RACE) // Race-only elements (not currently gametyperuleable)
else if (freecam)
;
else if ((gametyperules & GTR_POWERSTONES))
{
if (!islonesome)
{
// Draw the numerical position
K_DrawKartPositionNum(stplyr->position);
}
}
else if (gametype == GT_BATTLE) // Battle-only (ditto)
{
if (!freecam && !battlecapsules)
{
if (!battlecapsules)
K_drawKartEmeralds();
}
}
else if (!islonesome)
K_DrawKartPositionNum(stplyr->position);
}
if (LUA_HudEnabled(hud_gametypeinfo))
{
if (gametyperules & GTR_CIRCUIT)
{
K_drawKartLapsAndRings();
K_drawKartLaps();
}
else if (gametyperules & GTR_BUMPERS)
{
@ -5141,8 +5184,12 @@ void K_drawKartHUD(void)
{
K_drawBlueSphereMeter();
}
else
{
K_drawRingCounter();
}
if (modeattacking && !bossinfo.boss)
if (modeattacking && !bossinfo.valid)
{
// Draw the input UI
if (LUA_HudEnabled(hud_position))
@ -5172,10 +5219,12 @@ void K_drawKartHUD(void)
}
// Race overlays
if (gametype == GT_RACE && !freecam)
if (!freecam)
{
if (stplyr->exiting)
K_drawKartFinish(true);
else if (!(gametyperules & GTR_CIRCUIT))
;
else if (stplyr->karthud[khud_lapanimation] && !r_splitscreen)
K_drawLapStartAnim();
}
@ -5187,7 +5236,7 @@ void K_drawKartHUD(void)
if (modeattacking || freecam) // everything after here is MP and debug only
return;
if (gametype == GT_BATTLE && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM *
if ((gametyperules & GTR_KARMA) && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM *
V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem);
// Draw FREE PLAY.

View file

@ -6,7 +6,6 @@
#include "k_kart.h"
#include "k_battle.h"
#include "k_boss.h"
#include "k_pwrlv.h"
#include "k_color.h"
#include "k_respawn.h"
@ -41,6 +40,7 @@
#include "k_follower.h"
#include "k_objects.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_specialstage.h"
#include "k_roulette.h"
@ -109,11 +109,43 @@ void K_TimerInit(void)
boolean domodeattack = ((modeattacking != ATTACKING_NONE)
|| (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE));
if (specialStage.active == true)
// Rooooooolllling staaaaaaart
if ((gametyperules & (GTR_ROLLINGSTART|GTR_CIRCUIT)) == (GTR_ROLLINGSTART|GTR_CIRCUIT))
{
S_StartSound(NULL, sfx_s25f);
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *player = NULL;
if (playeringame[i] == false)
{
continue;
}
player = &players[i];
if (player->spectator == true)
{
continue;
}
if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true)
{
continue;
}
// Rolling start? lol
P_InstaThrust(player->mo, player->mo->angle, K_GetKartSpeed(player, false, false));
}
}
if ((gametyperules & (GTR_CATCHER|GTR_CIRCUIT)) == (GTR_CATCHER|GTR_CIRCUIT))
{
K_InitSpecialStage();
}
else if (bossinfo.boss == false)
else if (K_CheckBossIntro() == true)
;
else
{
if (!domodeattack)
{
@ -152,7 +184,7 @@ void K_TimerInit(void)
K_BattleInit(domodeattack);
if ((gametyperules & GTR_TIMELIMIT) && !bossinfo.boss && !modeattacking)
if ((gametyperules & GTR_TIMELIMIT) && !modeattacking)
{
if (!K_CanChangeRules(true))
{
@ -162,7 +194,7 @@ void K_TimerInit(void)
}
else
{
timelimitintics = timelimits[gametype] * (60*TICRATE);
timelimitintics = gametypes[gametype]->timelimit * (60*TICRATE);
}
}
else
@ -310,8 +342,6 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_kartbumpers);
CV_RegisterVar(&cv_kartfrantic);
CV_RegisterVar(&cv_kartencore);
CV_RegisterVar(&cv_kartvoterulechanges);
CV_RegisterVar(&cv_kartgametypepreference);
CV_RegisterVar(&cv_kartspeedometer);
CV_RegisterVar(&cv_kartvoices);
CV_RegisterVar(&cv_kartbot);
@ -342,15 +372,21 @@ boolean K_IsPlayerLosing(player_t *player)
INT32 winningpos = 1;
UINT8 i, pcount = 0;
if (player->pflags & PF_NOCONTEST)
return true;
if (battlecapsules && numtargets == 0)
return true; // Didn't even TRY?
if (battlecapsules || bossinfo.boss)
if (battlecapsules || (gametyperules & GTR_BOSS))
return (player->bumpers <= 0); // anything short of DNF is COOL
if (player->position == 1)
return false;
if (specialstageinfo.valid == true)
return false; // anything short of DNF is COOL
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
@ -515,7 +551,7 @@ boolean K_TimeAttackRules(void)
UINT8 playing = 0;
UINT8 i;
if (specialStage.active == true)
if ((gametyperules & (GTR_CATCHER|GTR_CIRCUIT)) == (GTR_CATCHER|GTR_CIRCUIT))
{
// Kind of a hack -- Special Stages
// are expected to be 1-player, so
@ -1272,10 +1308,9 @@ static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed
player->draftpower += add;
}
if (gametype == GT_BATTLE)
if (gametyperules & GTR_CLOSERPLAYERS)
{
// TODO: gametyperules
// Double speed in Battle
// Double speed in smaller environments
player->draftpower += add;
}
}
@ -1304,8 +1339,7 @@ static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed
*/
static void K_UpdateDraft(player_t *player)
{
const boolean addUfo = ((specialStage.active == true)
&& (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false));
mobj_t *addUfo = K_GetPossibleSpecialTarget();
fixed_t topspd = K_GetKartSpeed(player, false, false);
fixed_t draftdistance;
@ -1335,9 +1369,8 @@ static void K_UpdateDraft(player_t *player)
minDist = 640 * player->mo->scale;
if (gametype == GT_BATTLE)
if (gametyperules & GTR_CLOSERPLAYERS)
{
// TODO: gametyperules
minDist /= 4;
draftdistance *= 2;
leniency *= 4;
@ -1346,10 +1379,10 @@ static void K_UpdateDraft(player_t *player)
// Not enough speed to draft.
if (player->speed >= 20 * player->mo->scale)
{
if (addUfo == true)
if (addUfo != NULL)
{
// Tether off of the UFO!
if (K_TryDraft(player, specialStage.ufo, minDist, draftdistance, leniency) == true)
if (K_TryDraft(player, addUfo, minDist, draftdistance, leniency) == true)
{
return; // Finished doing our draft.
}
@ -1403,11 +1436,11 @@ static void K_UpdateDraft(player_t *player)
fixed_t dist = P_AproxDistance(P_AproxDistance(victim->mo->x - player->mo->x, victim->mo->y - player->mo->y), victim->mo->z - player->mo->z);
K_DrawDraftCombiring(player, victim->mo, dist, draftdistance, true);
}
else if (addUfo == true)
else if (addUfo != NULL)
{
// kind of a hack to not have to mess with how lastdraft works
fixed_t dist = P_AproxDistance(P_AproxDistance(specialStage.ufo->x - player->mo->x, specialStage.ufo->y - player->mo->y), specialStage.ufo->z - player->mo->z);
K_DrawDraftCombiring(player, specialStage.ufo, dist, draftdistance, true);
fixed_t dist = P_AproxDistance(P_AproxDistance(addUfo->x - player->mo->x, addUfo->y - player->mo->y), addUfo->z - player->mo->z);
K_DrawDraftCombiring(player, addUfo, dist, draftdistance, true);
}
}
else // Remove draft speed boost.
@ -2454,7 +2487,7 @@ void K_PlayOvertakeSound(mobj_t *source)
{
boolean tasteful = (!source->player || !source->player->karthud[khud_voices]);
if (!gametype == GT_RACE) // Only in race
if (!(gametyperules & GTR_CIRCUIT)) // Only in race
return;
// 4 seconds from before race begins, 10 seconds afterwards
@ -2963,8 +2996,7 @@ fixed_t K_GetSpindashChargeSpeed(player_t *player)
// (can be higher than this value when overcharged)
const fixed_t val = (10*FRACUNIT/277) + (((player->kartspeed + player->kartweight) + 2) * FRACUNIT) / 45;
// TODO: gametyperules
return (gametype == GT_BATTLE) ? (4 * val) : val;
return (gametyperules & GTR_CLOSERPLAYERS) ? (4 * val) : val;
}
// sets boostpower, speedboost, accelboost, and handleboost to whatever we need it to be
@ -3099,9 +3131,8 @@ static void K_GetKartBoostPower(player_t *player)
// 30% - 44%, each point of speed adds 1.75%
fixed_t draftspeed = ((3*FRACUNIT)/10) + ((player->kartspeed-1) * ((7*FRACUNIT)/400));
if (gametype == GT_BATTLE)
if (gametyperules & GTR_CLOSERPLAYERS)
{
// TODO: gametyperules
draftspeed *= 2;
}
@ -3224,7 +3255,7 @@ fixed_t K_GetKartAccel(player_t *player)
k_accel += 17 * (9 - player->kartspeed); // 121 - 257
// karma bomb gets 2x acceleration
if (gametype == GT_BATTLE && player->bumpers <= 0)
if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0)
k_accel *= 2;
// Marble Garden Top gets 1200% accel
@ -3238,9 +3269,8 @@ UINT16 K_GetKartFlashing(player_t *player)
{
UINT16 tics = flashingtics;
if (gametype == GT_BATTLE)
if (gametyperules & GTR_BUMPERS)
{
// TODO: gametyperules
return 1;
}
@ -3303,7 +3333,8 @@ SINT8 K_GetForwardMove(player_t *player)
return 0;
}
if (player->sneakertimer || player->spindashboost)
if (player->sneakertimer || player->spindashboost
|| (((gametyperules & (GTR_ROLLINGSTART|GTR_CIRCUIT)) == (GTR_ROLLINGSTART|GTR_CIRCUIT)) && (leveltime < TICRATE/2)))
{
return MAXPLMOVE;
}
@ -3545,6 +3576,12 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN
UINT8 points = 1;
boolean trapItem = false;
if (!(gametyperules & GTR_POINTLIMIT))
{
// No points in this gametype.
return;
}
if (player == NULL || victim == NULL)
{
// Invalid player or victim
@ -3580,11 +3617,8 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN
}
}
if (gametyperules & GTR_POINTLIMIT)
{
P_AddPlayerScore(player, points);
K_SpawnBattlePoints(player, victim, points);
}
P_AddPlayerScore(player, points);
K_SpawnBattlePoints(player, victim, points);
}
void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type)
@ -4132,7 +4166,7 @@ void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers)
player->karmadelay = comebacktime;
if (bossinfo.boss)
if (gametyperules & GTR_BOSS)
{
P_DoTimeOver(player);
}
@ -5056,7 +5090,7 @@ void K_SpawnDraftDust(mobj_t *mo)
{
UINT8 leniency = (3*TICRATE)/4 + ((mo->player->kartweight-1) * (TICRATE/4));
if (gametype == GT_BATTLE)
if (gametyperules & GTR_CLOSERPLAYERS)
leniency *= 4;
ang = mo->player->drawangle;
@ -6183,17 +6217,48 @@ void K_DropHnextList(player_t *player, boolean keepshields)
}
}
SINT8 K_GetTotallyRandomResult(UINT8 useodds)
{
itemroulette_t rouletteData = {0};
INT32 spawnchance[NUMKARTRESULTS];
INT32 totalspawnchance = 0;
INT32 i;
memset(spawnchance, 0, sizeof (spawnchance));
K_FillItemRouletteData(NULL, &rouletteData);
for (i = 1; i < NUMKARTRESULTS; i++)
{
spawnchance[i] = (
totalspawnchance += K_KartGetItemOdds(NULL, &rouletteData, useodds, i)
);
}
if (totalspawnchance > 0)
{
totalspawnchance = P_RandomKey(PR_ITEM_ROULETTE, totalspawnchance);
for (i = 0; i < NUMKARTRESULTS && spawnchance[i] <= totalspawnchance; i++);
}
else
{
i = KITEM_SAD;
}
return i;
}
mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount)
{
mobj_t *drop = P_SpawnMobj(x, y, z, MT_FLOATINGITEM);
mobj_t *backdrop = P_SpawnMobjFromMobj(drop, 0, 0, 0, MT_OVERLAY);
P_SetTarget(&backdrop->target, drop);
P_SetMobjState(backdrop, S_ITEMBACKDROP);
P_SetScale(drop, drop->scale>>4);
drop->destscale = (3*drop->destscale)/2;
drop->angle = angle;
P_Thrust(drop,
FixedAngle(P_RandomFixed(PR_ITEM_ROULETTE) * 180) + angle,
@ -6205,62 +6270,31 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8
if (type == 0)
{
itemroulette_t rouletteData = {0};
UINT8 useodds = 0;
INT32 spawnchance[NUMKARTRESULTS];
INT32 totalspawnchance = 0;
INT32 i;
const SINT8 i = K_GetTotallyRandomResult(amount);
memset(spawnchance, 0, sizeof (spawnchance));
// TODO: this is bad!
// K_KartGetItemResult requires a player
// but item roulette will need rewritten to change this
useodds = amount;
const SINT8 newType = K_ItemResultToType(i);
const UINT8 newAmount = K_ItemResultToAmount(i);
K_FillItemRouletteData(NULL, &rouletteData);
for (i = 1; i < NUMKARTRESULTS; i++)
if (newAmount > 1)
{
spawnchance[i] = (
totalspawnchance += K_KartGetItemOdds(NULL, &rouletteData, useodds, i)
);
}
UINT8 j;
if (totalspawnchance > 0)
{
UINT8 newType;
UINT8 newAmount;
totalspawnchance = P_RandomKey(PR_ITEM_ROULETTE, totalspawnchance);
for (i = 0; i < NUMKARTRESULTS && spawnchance[i] <= totalspawnchance; i++);
// TODO: this is bad!
// K_KartGetItemResult requires a player
// but item roulette will need rewritten to change this
newType = K_ItemResultToType(i);
newAmount = K_ItemResultToAmount(i);
if (newAmount > 1)
for (j = 0; j < newAmount-1; j++)
{
UINT8 j;
for (j = 0; j < newAmount-1; j++)
{
K_CreatePaperItem(
x, y, z,
angle, flip,
newType, 1
);
}
K_CreatePaperItem(
x, y, z,
angle, flip,
newType, 1
);
}
}
drop->threshold = newType;
drop->movecount = 1;
}
else
{
drop->threshold = 1;
drop->movecount = 1;
}
drop->threshold = newType;
drop->movecount = 1;
}
else
{
@ -6273,6 +6307,11 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8
P_SetTarget(&backdrop->tracer, drop);
backdrop->flags2 |= MF2_LINKDRAW;
if (gametyperules & GTR_BUMPERS)
{
drop->fuse = BATTLE_DESPAWN_TIME;
}
return drop;
}
@ -6793,10 +6832,10 @@ mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
mobj_t *wtarg = NULL;
INT32 i;
if (specialStage.active == true)
if (specialstageinfo.valid == true)
{
// Always target the UFO.
return specialStage.ufo;
// Always target the UFO (but not the emerald)
return K_GetPossibleSpecialTarget();
}
for (i = 0; i < MAXPLAYERS; i++)
@ -7472,7 +7511,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
K_SpawnGrowShrinkParticles(player->mo, player->growshrinktimer);
}
if (gametype == GT_RACE && player->rings <= 0) // spawn ring debt indicator
if (!(gametyperules & GTR_SPHERES) && player->rings <= 0) // spawn ring debt indicator
{
mobj_t *debtflag = P_SpawnMobj(player->mo->x + player->mo->momx, player->mo->y + player->mo->momy,
player->mo->z + P_GetMobjZMovement(player->mo) + player->mo->height + (24*player->mo->scale), MT_THOK);
@ -7538,8 +7577,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
//CONS_Printf("cam: %d, dest: %d\n", player->karthud[khud_boostcam], player->karthud[khud_destboostcam]);
}
player->karthud[khud_timeovercam] = 0;
// Make ABSOLUTELY SURE that your flashing tics don't get set WHILE you're still in hit animations.
if (player->spinouttimer != 0 || player->wipeoutslow != 0)
{
@ -7790,7 +7827,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->eggmanexplode)
{
if (player->spectator || (gametype == GT_BATTLE && !player->bumpers))
if (player->spectator || ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0))
player->eggmanexplode = 0;
else
{
@ -8032,10 +8069,11 @@ void K_KartPlayerAfterThink(player_t *player)
mobj_t *ret = NULL;
if (specialStage.active == true && lastTargID == MAXPLAYERS)
if (specialstageinfo.valid == true
&& lastTargID == MAXPLAYERS)
{
// Aiming at the UFO.
lastTarg = specialStage.ufo;
// Aiming at the UFO (but never the emerald).
lastTarg = K_GetPossibleSpecialTarget();
}
else if ((lastTargID >= 0 && lastTargID <= MAXPLAYERS)
&& playeringame[lastTargID] == true)
@ -9332,15 +9370,18 @@ static INT32 K_FlameShieldMax(player_t *player)
UINT8 numplayers = 0;
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
if (gametyperules & GTR_CIRCUIT)
{
if (playeringame[i] && !players[i].spectator)
numplayers++;
if (players[i].position == 1)
disttofinish = players[i].distancetofinish;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator)
numplayers++;
if (players[i].position == 1)
disttofinish = players[i].distancetofinish;
}
}
if (numplayers <= 1 || gametype == GT_BATTLE)
if (numplayers <= 1)
{
return 16; // max when alone, for testing
// and when in battle, for chaos
@ -9594,8 +9635,7 @@ static void K_KartSpindash(player_t *player)
{
fixed_t thrust = FixedMul(player->mo->scale, player->spindash*FRACUNIT/5);
// TODO: gametyperules
if (gametype == GT_BATTLE)
if (gametyperules & GTR_CLOSERPLAYERS)
thrust *= 2;
// Give a bit of a boost depending on charge.
@ -10402,8 +10442,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
player->mo->destscale = FixedMul(player->mo->destscale, SHRINK_SCALE);
}
// TODO: gametyperules
player->growshrinktimer = max(player->growshrinktimer, (gametype == GT_BATTLE ? 8 : 12) * TICRATE);
player->growshrinktimer = max(player->growshrinktimer, ((gametyperules & GTR_CLOSERPLAYERS) ? 8 : 12) * TICRATE);
if (player->invincibilitytimer > 0)
{
@ -10561,8 +10600,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
if ((cmd->buttons & BT_ATTACK) && (player->pflags & PF_HOLDREADY))
{
// TODO: gametyperules
const INT32 incr = gametype == GT_BATTLE ? 4 : 2;
const INT32 incr = (gametyperules & GTR_CLOSERPLAYERS) ? 4 : 2;
if (player->flamedash == 0)
{
@ -10598,8 +10636,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
{
player->pflags |= PF_HOLDREADY;
// TODO: gametyperules
if (gametype != GT_BATTLE || leveltime % 6 == 0)
if (!(gametyperules & GTR_CLOSERPLAYERS) || leveltime % 6 == 0)
{
if (player->flamemeter > 0)
player->flamemeter--;
@ -10721,18 +10758,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
if (player->hyudorotimer > 0)
{
INT32 hyu = hyudorotime;
if (gametype == GT_RACE)
hyu *= 2; // double in race
if (leveltime & 1)
{
player->mo->renderflags |= RF_DONTDRAW;
}
else
{
if (player->hyudorotimer >= (TICRATE/2) && player->hyudorotimer <= hyu-(TICRATE/2))
if (player->hyudorotimer >= (TICRATE/2) && player->hyudorotimer <= hyudorotime-(TICRATE/2))
player->mo->renderflags &= ~K_GetPlayerDontDrawFlag(player);
else
player->mo->renderflags &= ~RF_DONTDRAW;
@ -10745,17 +10777,17 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
player->mo->renderflags &= ~RF_DONTDRAW;
}
if (gametype == GT_BATTLE && player->bumpers <= 0) // dead in match? you da bomb
if (!(gametyperules & GTR_BUMPERS) || player->bumpers > 0)
{
player->mo->renderflags &= ~(RF_TRANSMASK|RF_BRIGHTMASK);
}
else // dead in match? you da bomb
{
K_DropItems(player); //K_StripItems(player);
K_StripOther(player);
player->mo->renderflags |= RF_GHOSTLY;
player->flashing = player->karmadelay;
}
else if (gametype == GT_RACE || player->bumpers > 0)
{
player->mo->renderflags &= ~(RF_TRANSMASK|RF_BRIGHTMASK);
}
if (player->trickpanel == 1)
{
@ -10968,7 +11000,7 @@ void K_CheckSpectateStatus(void)
continue;
if (leveltime > (starttime + 20*TICRATE)) // DON'T allow if the match is 20 seconds in
return;
if (gametype == GT_RACE && players[i].laps >= 2) // DON'T allow if the race is at 2 laps
if ((gametyperules & GTR_CIRCUIT) && players[i].laps >= 2) // DON'T allow if the race is at 2 laps
return;
continue;
}
@ -11164,4 +11196,27 @@ void K_HandleDirectionalInfluence(player_t *player)
player->mo->momy = FixedMul(speed, finalY);
}
void K_UpdateMobjItemOverlay(mobj_t *part, SINT8 itemType, UINT8 itemCount)
{
switch (itemType)
{
case KITEM_ORBINAUT:
part->sprite = SPR_ITMO;
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetOrbinautItemFrame(itemCount);
break;
case KITEM_INVINCIBILITY:
part->sprite = SPR_ITMI;
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetInvincibilityItemFrame();
break;
case KITEM_SAD:
part->sprite = SPR_ITEM;
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE;
break;
default:
part->sprite = SPR_ITEM;
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|(itemType);
break;
}
}
//}

View file

@ -138,6 +138,7 @@ INT32 K_GetKartDriftSparkValueForStage(player_t *player, UINT8 stage);
void K_SpawnDriftBoostExplosion(player_t *player, int stage);
void K_SpawnDriftElectricSparks(player_t *player, int color, boolean shockwave);
void K_KartUpdatePosition(player_t *player);
SINT8 K_GetTotallyRandomResult(UINT8 useodds);
mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount);
void K_DropItems(player_t *player);
void K_DropRocketSneaker(player_t *player);
@ -197,6 +198,8 @@ fixed_t K_ItemScaleForPlayer(player_t *player);
void K_SetItemOut(player_t *player);
void K_UnsetItemOut(player_t *player);
void K_UpdateMobjItemOverlay(mobj_t *part, SINT8 itemType, UINT8 itemCount);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -119,7 +119,11 @@ struct menucolor_t {
extern menucolor_t *menucolorhead, *menucolortail;
extern CV_PossibleValue_t gametype_cons_t[];
extern INT16 menugametype;
void M_NextMenuGametype(UINT32 forbidden);
void M_PrevMenuGametype(UINT32 forbidden);
void M_HandleHostMenuGametype(INT32 choice);
void M_HandlePauseMenuGametype(INT32 choice);
//
// MENU TYPEDEFS
@ -417,6 +421,7 @@ extern menu_t MISC_StatisticsDef;
typedef enum
{
mpause_addons = 0,
mpause_changegametype,
mpause_switchmap,
mpause_restartmap,
mpause_tryagain,
@ -435,9 +440,6 @@ typedef enum
mpause_title,
} mpause_e;
extern menuitem_t PAUSE_GamemodesMenu[];
extern menu_t PAUSE_GamemodesDef;
extern menuitem_t PAUSE_PlaybackMenu[];
extern menu_t PAUSE_PlaybackMenuDef;
@ -696,7 +698,6 @@ extern struct cupgrid_s {
size_t cappages;
tic_t previewanim;
boolean grandprix; // Setup grand prix server after picking
boolean netgame; // Start the game in an actual server
} cupgrid;
typedef struct levelsearch_s {
@ -713,6 +714,7 @@ extern struct levellist_s {
UINT16 dest;
INT16 choosemap;
UINT8 newgametype;
UINT8 guessgt;
levelsearch_t levelsearch;
boolean netgame; // Start the game in an actual server
} levellist;
@ -773,7 +775,6 @@ void M_MPOptSelect(INT32 choice);
void M_MPOptSelectInit(INT32 choice);
void M_MPOptSelectTick(void);
boolean M_MPResetOpts(void);
extern consvar_t cv_dummygametype; // lazy hack to allow us to select the GT on the server host submenu
extern consvar_t cv_dummyip; // I HAVE
// HAVE YOUR IP ADDRESS (This just the hack Cvar we'll type into and then it apends itself to "connect" in the console for IP join)

View file

@ -89,12 +89,15 @@ menuitem_t PLAY_GamemodesMenu[] =
{IT_STRING | IT_CALL, "Race", "A contest to see who's the fastest of them all!",
NULL, {.routine = M_SetupRaceMenu}, 0, 0},
{IT_STRING | IT_CALL, "Battle", "It's last hedgehog standing in this free-for-all!",
{IT_STRING | IT_CALL, "Battle", "It's last kart standing in this free-for-all!",
"MENIMG00", {.routine = M_LevelSelectInit}, 0, GT_BATTLE},
{IT_STRING | IT_CALL, "Capsules", "Bust up all of the capsules in record time!",
NULL, {.routine = M_LevelSelectInit}, 1, GT_BATTLE},
{IT_STRING | IT_CALL, "Special", "Strike your target and secure the prize!",
NULL, {.routine = M_LevelSelectInit}, 1, GT_SPECIAL},
{IT_STRING | IT_CALL, "Back", NULL, NULL, {.routine = M_GoBack}, 0, 0},
};
@ -359,8 +362,8 @@ menuitem_t PLAY_MP_Host[] =
{IT_STRING | IT_CVAR, "Max. Players", "Set how many players can play at once. Others will spectate.",
NULL, {.cvar = &cv_maxplayers}, 0, 0},
{IT_STRING | IT_CVAR, "Gamemode", "Are we racing? Or perhaps battling?",
NULL, {.cvar = &cv_dummygametype}, 0, 0},
{IT_STRING | IT_KEYHANDLER, "Gamemode", "Choose the type of play on your server.",
NULL, {.routine = M_HandleHostMenuGametype}, 0, 0},
{IT_STRING | IT_CALL, "GO", "Select a map with the currently selected gamemode",
NULL, {.routine = M_MPSetupNetgameMapSelect}, 0, 0},
@ -1132,9 +1135,6 @@ menuitem_t OPTIONS_Server[] =
{IT_STRING | IT_CVAR, "Vote Timer", "Set how long players have to vote.",
NULL, {.cvar = &cv_votetime}, 0, 0},
{IT_STRING | IT_CVAR, "Vote Mode Change", "Set how often voting proposes a different gamemode.",
NULL, {.cvar = &cv_kartvoterulechanges}, 0, 0},
{IT_SPACE | IT_NOTHING, NULL, NULL,
NULL, {NULL}, 0, 0},
@ -1593,8 +1593,11 @@ menuitem_t PAUSE_Main[] =
{IT_STRING | IT_CALL, "ADDONS", "M_ICOADD",
NULL, {.routine = M_Addons}, 0, 0},
{IT_STRING | IT_SUBMENU, "CHANGE MAP", "M_ICOMAP",
NULL, {.submenu = &PAUSE_GamemodesDef}, 0, 0},
{IT_STRING | IT_KEYHANDLER, "GAMETYPE", "M_ICOGAM",
NULL, {.routine = M_HandlePauseMenuGametype}, 0, 0},
{IT_STRING | IT_CALL, "CHANGE MAP", "M_ICOMAP",
NULL, {.routine = M_LevelSelectInit}, 0, -1},
{IT_STRING | IT_CALL, "RESTART MAP", "M_ICORE",
NULL, {.routine = M_RestartMap}, 0, 0},
@ -1647,20 +1650,6 @@ menu_t PAUSE_MainDef = {
M_PauseInputs
};
// PAUSE : Map switching gametype selection (In case you want to pick from battle / race...)
menuitem_t PAUSE_GamemodesMenu[] =
{
{IT_STRING | IT_CALL, "Race", "Select which gamemode to choose a new map from.",
NULL, {.routine = M_LevelSelectInit}, 0, GT_RACE},
{IT_STRING | IT_CALL, "Battle", "Select which gamemode to choose a new map from.",
NULL, {.routine = M_LevelSelectInit}, 0, GT_BATTLE},
{IT_STRING | IT_CALL, "Back", NULL, NULL, {.routine = M_GoBack}, 0, 0},
};
menu_t PAUSE_GamemodesDef = KARTGAMEMODEMENU(PAUSE_GamemodesMenu, &PAUSE_MainDef);
// Replay popup menu
menuitem_t PAUSE_PlaybackMenu[] =
{

View file

@ -541,12 +541,7 @@ void M_Drawer(void)
}
else
{
#ifdef DEVELOP // Development -- show revision / branch info
V_DrawThinString(vid.dupx, vid.height - 20*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, compbranch);
V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, comprevision);
#else // Regular build
V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, va("%s", VERSIONSTRING));
#endif
F_VersionDrawer();
}
@ -1933,7 +1928,7 @@ static void M_DrawCupPreview(INT16 y, levelsearch_t *levelsearch)
V_DrawFill(0, y, BASEVIDWIDTH, 54, 31);
if (levelsearch->cup && !M_CupLocked(levelsearch->cup))
if (levelsearch->cup && maxlevels > 0)
{
add = (cupgrid.previewanim / 82) % maxlevels;
map = start;
@ -1979,16 +1974,18 @@ static void M_DrawCupPreview(INT16 y, levelsearch_t *levelsearch)
}
}
static void M_DrawCupTitle(INT16 y, cupheader_t *cup)
static void M_DrawCupTitle(INT16 y, levelsearch_t *levelsearch)
{
UINT8 temp = 0;
V_DrawScaledPatch(0, y, 0, W_CachePatchName("MENUHINT", PU_CACHE));
if (cup)
if (levelsearch->cup)
{
boolean unlocked = !M_CupLocked(cup);
boolean unlocked = (M_GetFirstLevelInList(&temp, levelsearch) != NEXTMAP_INVALID);
UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE);
patch_t *icon = W_CachePatchName(cup->icon, PU_CACHE);
const char *str = (unlocked ? va("%s Cup", cup->name) : "???");
patch_t *icon = W_CachePatchName(levelsearch->cup->icon, PU_CACHE);
const char *str = (unlocked ? va("%s Cup", levelsearch->cup->name) : "???");
INT16 offset = V_LSTitleLowStringWidth(str, 0) / 2;
V_DrawLSTitleLowString(BASEVIDWIDTH/2 - offset, y+6, 0, str);
@ -2002,32 +1999,35 @@ static void M_DrawCupTitle(INT16 y, cupheader_t *cup)
else
{
if (currentMenu == &PLAY_LevelSelectDef)
V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, y+6, 0, va("%s Mode", Gametype_Names[levellist.newgametype]));
{
UINT8 namedgt = (levellist.guessgt != MAXGAMETYPES) ? levellist.guessgt : levellist.newgametype;
V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, y+6, 0, va("%s Mode", gametypes[namedgt]->name));
}
}
}
void M_DrawCupSelect(void)
{
UINT8 i, j;
UINT8 i, j, temp = 0;
levelsearch_t templevelsearch = levellist.levelsearch; // full copy
templevelsearch.cup = cupgrid.builtgrid[CUPMENU_CURSORID];
for (i = 0; i < CUPMENU_COLUMNS; i++)
{
for (j = 0; j < CUPMENU_ROWS; j++)
{
size_t id = (i + (j * CUPMENU_COLUMNS)) + (cupgrid.pageno * (CUPMENU_COLUMNS * CUPMENU_ROWS));
cupheader_t *iconcup = cupgrid.builtgrid[id];
patch_t *patch = NULL;
INT16 x, y;
INT16 icony = 7;
if (!iconcup)
if (!cupgrid.builtgrid[id])
break;
/*if (iconcup->emeraldnum == 0)
templevelsearch.cup = cupgrid.builtgrid[id];
/*if (templevelsearch.cup->emeraldnum == 0)
patch = W_CachePatchName("CUPMON3A", PU_CACHE);
else*/ if (iconcup->emeraldnum > 7)
else*/ if (templevelsearch.cup->emeraldnum > 7)
{
patch = W_CachePatchName("CUPMON2A", PU_CACHE);
icony = 5;
@ -2040,14 +2040,14 @@ void M_DrawCupSelect(void)
V_DrawScaledPatch(x, y, 0, patch);
if (M_CupLocked(iconcup))
if (M_GetFirstLevelInList(&temp, &templevelsearch) == NEXTMAP_INVALID)
{
patch_t *st = W_CachePatchName(va("ICONST0%d", (cupgrid.previewanim % 4) + 1), PU_CACHE);
V_DrawScaledPatch(x + 8, y + icony, 0, st);
}
else
{
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName(iconcup->icon, PU_CACHE));
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName(templevelsearch.cup->icon, PU_CACHE));
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName("CUPBOX", PU_CACHE));
}
}
@ -2058,8 +2058,10 @@ void M_DrawCupSelect(void)
0, W_CachePatchName("CUPCURS", PU_CACHE)
);
templevelsearch.cup = cupgrid.builtgrid[CUPMENU_CURSORID];
M_DrawCupPreview(146 + (24*menutransition.tics), &templevelsearch);
M_DrawCupTitle(120 - (24*menutransition.tics), templevelsearch.cup);
M_DrawCupTitle(120 - (24*menutransition.tics), &templevelsearch);
}
static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map)
@ -2225,7 +2227,7 @@ void M_DrawLevelSelect(void)
map = M_GetNextLevelInList(map, &j, &levellist.levelsearch);
}
M_DrawCupTitle(tay, levellist.levelsearch.cup);
M_DrawCupTitle(tay, &levellist.levelsearch);
}
void M_DrawTimeAttack(void)
@ -2261,7 +2263,8 @@ void M_DrawTimeAttack(void)
laprec = mapheaderinfo[map]->mainrecord->lap;
}
if (levellist.newgametype != GT_BATTLE)
if ((gametypes[levellist.newgametype]->rules & GTR_CIRCUIT)
&& (mapheaderinfo[map]->numlaps != 1))
{
V_DrawRightAlignedString(rightedge-12, timeheight, highlightflags, "BEST LAP:");
K_drawKartTimestamp(laprec, 162+t, timeheight+6, 0, 2);
@ -2462,6 +2465,20 @@ void M_DrawMPHost(void)
}
break;
}
case IT_KEYHANDLER:
{
if (currentMenu->menuitems[i].itemaction.routine != M_HandleHostMenuGametype)
break;
w = V_ThinStringWidth(gametypes[menugametype]->name, V_6WIDTHSPACE);
V_DrawThinString(xp + 138 - w, yp, highlightflags|V_6WIDTHSPACE, gametypes[menugametype]->name);
if (i == itemOn)
{
V_DrawCharacter(xp + 138 - 10 - w - (skullAnimCounter/5), yp, '\x1C' | highlightflags, false); // left arrow
V_DrawCharacter(xp + 138 + 2 + (skullAnimCounter/5), yp, '\x1D' | highlightflags, false); // right arrow
}
break;
}
}
xp += 5;
@ -3680,27 +3697,19 @@ void M_DrawPause(void)
case IT_STRING:
{
patch_t *pp;
UINT8 *colormap = NULL;
if (i == itemOn)
if (i == itemOn && (i == mpause_restartmap || i == mpause_tryagain))
{
if (i == mpause_restartmap || i == mpause_tryagain)
{
pp = W_CachePatchName(
va("M_ICOR2%c", ('A'+(pausemenu.ticker & 1))),
PU_CACHE);
}
else
{
char iconame[9]; // 8 chars + \0
strcpy(iconame, currentMenu->menuitems[i].tooltip);
iconame[7] = '2'; // Yes this is a stupid hack. Replace the last character with a 2 when we're selecting this graphic.
pp = W_CachePatchName(iconame, PU_CACHE);
}
pp = W_CachePatchName(
va("M_ICOR2%c", ('A'+(pausemenu.ticker & 1))),
PU_CACHE);
}
else
{
pp = W_CachePatchName(currentMenu->menuitems[i].tooltip, PU_CACHE);
if (i == itemOn)
colormap = yellowmap;
}
// 294 - 261 = 33
@ -3711,7 +3720,7 @@ void M_DrawPause(void)
// This double ternary is awful, yes.
dypos = ypos + pausemenu.offset;
V_DrawFixedPatch( ((i == itemOn ? (294 - pausemenu.offset*2/3 * (dypos > 100 ? 1 : -1)) : 261) + offset) << FRACBITS, (dypos)*FRACUNIT, FRACUNIT, 0, pp, NULL);
V_DrawFixedPatch( ((i == itemOn ? (294 - pausemenu.offset*2/3 * (dypos > 100 ? 1 : -1)) : 261) + offset) << FRACBITS, (dypos)*FRACUNIT, FRACUNIT, 0, pp, colormap);
ypos += 50;
itemsdrawn++; // We drew that!
@ -3760,12 +3769,26 @@ void M_DrawPause(void)
word1[word1len] = '\0';
word2[word2len] = '\0';
// If there's no 2nd word, take this opportunity to center this line of text.
if (word1len)
V_DrawCenteredLSTitleHighString(220 + offset*2, 75 + (!word2len ? 10 : 0), 0, word1);
if (itemOn == mpause_changegametype)
{
INT32 w = V_LSTitleLowStringWidth(gametypes[menugametype]->name, 0)/2;
if (word2len)
V_DrawCenteredLSTitleLowString(220 + offset*2, 103, 0, word2);
if (word1len)
V_DrawCenteredLSTitleHighString(220 + offset*2, 75, 0, word1);
V_DrawLSTitleLowString(220-w + offset*2, 103, V_YELLOWMAP, gametypes[menugametype]->name);
V_DrawCharacter(220-w + offset*2 - 8 - (skullAnimCounter/5), 103+6, '\x1C' | V_YELLOWMAP, false); // left arrow
V_DrawCharacter(220+w + offset*2 + 4 + (skullAnimCounter/5), 103+6, '\x1D' | V_YELLOWMAP, false); // right arrow
}
else
{
// If there's no 2nd word, take this opportunity to center this line of text.
if (word1len)
V_DrawCenteredLSTitleHighString(220 + offset*2, 75 + (!word2len ? 10 : 0), 0, word1);
if (word2len)
V_DrawCenteredLSTitleLowString(220 + offset*2, 103, 0, word2);
}
}
tic_t playback_last_menu_interaction_leveltime = 0;
@ -3964,9 +3987,22 @@ static void M_DrawReplayHutReplayInfo(menudemo_t *demoref)
if (demoref->numlaps)
V_DrawThinString(x, y+9, V_SNAPTOTOP|V_ALLOWLOWERCASE, va("(%d laps)", demoref->numlaps));
V_DrawString(x, y+20, V_SNAPTOTOP|V_ALLOWLOWERCASE, demoref->gametype == GT_RACE ?
va("Race (%s speed)", kartspeed_cons_t[(demoref->kartspeed & ~DF_ENCORE) + 1].strvalue) :
"Battle Mode");
{
const char *gtstring;
if (demoref->gametype < 0)
{
gtstring = "Custom (not loaded)";
}
else
{
gtstring = gametypes[demoref->gametype]->name;
if ((gametypes[demoref->gametype]->rules & GTR_CIRCUIT))
gtstring = va("%s (%s)", gtstring, kartspeed_cons_t[(demoref->kartspeed & ~DF_ENCORE) + 1].strvalue);
}
V_DrawString(x, y+20, V_SNAPTOTOP|V_ALLOWLOWERCASE, gtstring);
}
if (!demoref->standings[0].ranking)
{
@ -3979,30 +4015,33 @@ static void M_DrawReplayHutReplayInfo(menudemo_t *demoref)
V_DrawThinString(x, y+29, V_SNAPTOTOP|highlightflags, "WINNER");
V_DrawString(x+38, y+30, V_SNAPTOTOP|V_ALLOWLOWERCASE, demoref->standings[0].name);
if (demoref->gametype == GT_RACE)
if (demoref->gametype >= 0)
{
V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "TIME");
}
else
{
V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "SCORE");
}
if (gametypes[demoref->gametype]->rules & GTR_POINTLIMIT)
{
V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "SCORE");
}
else
{
V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "TIME");
}
if (demoref->standings[0].timeorscore == (UINT32_MAX-1))
{
V_DrawThinString(x+32, y+39, V_SNAPTOTOP, "NO CONTEST");
}
else if (demoref->gametype == GT_RACE)
{
V_DrawRightAlignedString(x+84, y+40, V_SNAPTOTOP, va("%d'%02d\"%02d",
G_TicsToMinutes(demoref->standings[0].timeorscore, true),
G_TicsToSeconds(demoref->standings[0].timeorscore),
G_TicsToCentiseconds(demoref->standings[0].timeorscore)
));
}
else
{
V_DrawString(x+32, y+40, V_SNAPTOTOP, va("%d", demoref->standings[0].timeorscore));
if (demoref->standings[0].timeorscore == (UINT32_MAX-1))
{
V_DrawThinString(x+32, y+39, V_SNAPTOTOP, "NO CONTEST");
}
else if (gametypes[demoref->gametype]->rules & GTR_POINTLIMIT)
{
V_DrawString(x+32, y+40, V_SNAPTOTOP, va("%d", demoref->standings[0].timeorscore));
}
else
{
V_DrawRightAlignedString(x+84, y+40, V_SNAPTOTOP, va("%d'%02d\"%02d",
G_TicsToMinutes(demoref->standings[0].timeorscore, true),
G_TicsToSeconds(demoref->standings[0].timeorscore),
G_TicsToCentiseconds(demoref->standings[0].timeorscore)
));
}
}
// Character face!
@ -4197,14 +4236,16 @@ void M_DrawReplayStartMenu(void)
if (demoref->standings[i].timeorscore == UINT32_MAX-1)
V_DrawThinString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, "NO CONTEST");
else if (demoref->gametype == GT_RACE)
else if (demoref->gametype < 0)
;
else if (gametypes[demoref->gametype]->rules & GTR_POINTLIMIT)
V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", demoref->standings[i].timeorscore));
else
V_DrawRightAlignedString(BASEVIDWIDTH-40, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d'%02d\"%02d",
G_TicsToMinutes(demoref->standings[i].timeorscore, true),
G_TicsToSeconds(demoref->standings[i].timeorscore),
G_TicsToCentiseconds(demoref->standings[i].timeorscore)
));
else
V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", demoref->standings[i].timeorscore));
// Character face!
@ -4692,6 +4733,16 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
specialmap = btcmapcache;
break;
}
case SECRET_SPECIALATTACK:
{
static UINT16 sscmapcache = NEXTMAP_INVALID;
if (sscmapcache > nummapheaders)
{
sscmapcache = G_RandMap(G_TOLFlag(GT_SPECIAL), -1, 2, 0, false, NULL);
}
specialmap = sscmapcache;
break;
}
case SECRET_HARDSPEED:
{
static UINT16 hardmapcache = NEXTMAP_INVALID;

View file

@ -147,10 +147,6 @@ consvar_t cv_menujam_update = CVAR_INIT ("menujam_update", "Off", CV_SAVE, CV_On
static CV_PossibleValue_t menujam_cons_t[] = {{0, "menu"}, {1, "menu2"}, {2, "menu3"}, {0, NULL}};
static consvar_t cv_menujam = CVAR_INIT ("menujam", "0", CV_SAVE, menujam_cons_t, NULL);
// This gametype list is integral for many different reasons.
// When you add gametypes here, don't forget to update them in dehacked.c and doomstat.h!
CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1];
static CV_PossibleValue_t serversort_cons_t[] = {
{0,"Ping"},
{1,"AVG. Power Level"},
@ -189,18 +185,18 @@ static CV_PossibleValue_t dummyteam_cons_t[] = {{0, "Spectator"}, {1, "Red"}, {2
static CV_PossibleValue_t dummyspectate_cons_t[] = {{0, "Spectator"}, {1, "Playing"}, {0, NULL}};
static CV_PossibleValue_t dummyscramble_cons_t[] = {{0, "Random"}, {1, "Points"}, {0, NULL}};
static CV_PossibleValue_t dummystaff_cons_t[] = {{0, "MIN"}, {100, "MAX"}, {0, NULL}};
static CV_PossibleValue_t dummygametype_cons_t[] = {{0, "Race"}, {1, "Battle"}, {0, NULL}};
//static consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange);
static consvar_t cv_dummyteam = CVAR_INIT ("dummyteam", "Spectator", CV_HIDDEN, dummyteam_cons_t, NULL);
//static cv_dummyspectate = CVAR_INITconsvar_t ("dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL);
static consvar_t cv_dummyscramble = CVAR_INIT ("dummyscramble", "Random", CV_HIDDEN, dummyscramble_cons_t, NULL);
static consvar_t cv_dummystaff = CVAR_INIT ("dummystaff", "0", CV_HIDDEN|CV_CALL, dummystaff_cons_t, Dummystaff_OnChange);
consvar_t cv_dummygametype = CVAR_INIT ("dummygametype", "Race", CV_HIDDEN, dummygametype_cons_t, NULL);
consvar_t cv_dummyip = CVAR_INIT ("dummyip", "", CV_HIDDEN, NULL, NULL);
consvar_t cv_dummymenuplayer = CVAR_INIT ("dummymenuplayer", "P1", CV_HIDDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange);
consvar_t cv_dummyspectate = CVAR_INIT ("dummyspectate", "Spectator", CV_HIDDEN, dummyspectate_cons_t, NULL);
INT16 menugametype = GT_RACE;
consvar_t cv_dummyprofilename = CVAR_INIT ("dummyprofilename", "", CV_HIDDEN, NULL, NULL);
consvar_t cv_dummyprofileplayername = CVAR_INIT ("dummyprofileplayername", "", CV_HIDDEN, NULL, NULL);
consvar_t cv_dummyprofilekickstart = CVAR_INIT ("dummyprofilekickstart", "Off", CV_HIDDEN, CV_OnOff, NULL);
@ -1009,6 +1005,8 @@ void M_ClearMenus(boolean callexitmenufunc)
if (!menuactive)
return;
CON_ClearHUD();
if (currentMenu->quitroutine && callexitmenufunc && !currentMenu->quitroutine())
return; // we can't quit this menu (also used to set parameter from the menu)
@ -1713,7 +1711,6 @@ void M_Init(void)
CV_RegisterVar(&cv_dummyspectate);
CV_RegisterVar(&cv_dummyscramble);
CV_RegisterVar(&cv_dummystaff);
CV_RegisterVar(&cv_dummygametype);
CV_RegisterVar(&cv_dummyip);
CV_RegisterVar(&cv_dummyprofilename);
@ -3295,25 +3292,40 @@ void M_SetupGametypeMenu(INT32 choice)
PLAY_GamemodesDef.prevMenu = currentMenu;
// Battle and Capsules disabled
// Battle and Capsules (and Special) disabled
PLAY_GamemodesMenu[1].status = IT_DISABLED;
PLAY_GamemodesMenu[2].status = IT_DISABLED;
PLAY_GamemodesMenu[3].status = IT_DISABLED;
if (cv_splitplayers.value > 1)
{
// Re-add Battle
PLAY_GamemodesMenu[1].status = IT_STRING | IT_CALL;
}
else if (M_SecretUnlocked(SECRET_BREAKTHECAPSULES, true))
{
// Re-add Capsules
PLAY_GamemodesMenu[2].status = IT_STRING | IT_CALL;
}
else
{
// Only one non-Back entry, let's skip straight to Race.
M_SetupRaceMenu(-1);
return;
boolean anyunlocked = false;
if (M_SecretUnlocked(SECRET_BREAKTHECAPSULES, true))
{
// Re-add Capsules
PLAY_GamemodesMenu[2].status = IT_STRING | IT_CALL;
anyunlocked = true;
}
if (M_SecretUnlocked(SECRET_SPECIALATTACK, true))
{
// Re-add Special
PLAY_GamemodesMenu[3].status = IT_STRING | IT_CALL;
anyunlocked = true;
}
if (!anyunlocked)
{
// Only one non-Back entry, let's skip straight to Race.
M_SetupRaceMenu(-1);
return;
}
}
M_SetupNextMenu(&PLAY_GamemodesDef, false);
@ -3406,9 +3418,6 @@ boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch)
if (mapheaderinfo[mapnum]->lumpnum == LUMPERROR)
return false;
if (levelsearch->checklocked && M_MapLocked(mapnum+1))
return false; // not unlocked
// Check for TOL
if (!(mapheaderinfo[mapnum]->typeoflevel & levelsearch->typeoflevel))
return false;
@ -3427,6 +3436,19 @@ boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch)
&& mapheaderinfo[mapnum]->cup != levelsearch->cup)
return false;
// Finally, the most complex check: does the map have lock conditions?
if (levelsearch->checklocked)
{
// Check for completion
if ((mapheaderinfo[mapnum]->menuflags & LF2_FINISHNEEDED)
&& !(mapheaderinfo[mapnum]->mapvisited & MV_BEATEN))
return false;
// Check for unlock
if (M_MapLocked(mapnum+1))
return false;
}
// Survived our checks.
return true;
}
@ -3440,6 +3462,9 @@ UINT16 M_CountLevelsToShowInList(levelsearch_t *levelsearch)
if (levelsearch->cup)
{
if (levelsearch->checklocked && M_CupLocked(levelsearch->cup))
return 0;
for (i = 0; i < CUPCACHE_MAX; i++)
{
if (!M_CanShowLevelInList(levelsearch->cup->cachedlevels[i], levelsearch))
@ -3466,6 +3491,12 @@ UINT16 M_GetFirstLevelInList(UINT8 *i, levelsearch_t *levelsearch)
if (levelsearch->cup)
{
if (levelsearch->checklocked && M_CupLocked(levelsearch->cup))
{
*i = CUPCACHE_MAX;
return NEXTMAP_INVALID;
}
*i = 0;
mapnum = NEXTMAP_INVALID;
for (; *i < CUPCACHE_MAX; (*i)++)
@ -3530,20 +3561,39 @@ static void M_LevelSelectScrollDest(void)
}
// Builds the level list we'll be using from the gametype we're choosing and send us to the apropriate menu.
static void M_LevelListFromGametype(INT16 gt)
static boolean M_LevelListFromGametype(INT16 gt)
{
static boolean first = true;
if (first || gt != levellist.newgametype)
UINT8 temp = 0;
if (first || gt != levellist.newgametype || levellist.guessgt != MAXGAMETYPES)
{
if (first)
{
cupgrid.cappages = 0;
cupgrid.builtgrid = NULL;
}
levellist.newgametype = gt;
levellist.levelsearch.typeoflevel = G_TOLFlag(gt);
levellist.levelsearch.cupmode = (!(gametypedefaultrules[gt] & GTR_NOCUPSELECT));
if (levellist.levelsearch.timeattack == true && gt == GT_SPECIAL)
{
// Sneak in an extra.
levellist.levelsearch.typeoflevel |= G_TOLFlag(GT_VERSUS);
levellist.guessgt = gt;
}
else
{
levellist.guessgt = MAXGAMETYPES;
}
levellist.levelsearch.cupmode = (!(gametypes[gt]->rules & GTR_NOCUPSELECT));
levellist.levelsearch.cup = NULL;
first = false;
}
PLAY_CupSelectDef.prevMenu = currentMenu;
// Obviously go to Cup Select in gametypes that have cups.
// Use a really long level select in gametypes that don't use cups.
@ -3551,32 +3601,35 @@ static void M_LevelListFromGametype(INT16 gt)
{
levelsearch_t templevelsearch = levellist.levelsearch; // full copy
size_t currentid = 0, highestunlockedid = 0;
const size_t unitlen = sizeof(cupheader_t*) * (CUPMENU_COLUMNS * CUPMENU_ROWS);
const size_t pagelen = sizeof(cupheader_t*) * (CUPMENU_COLUMNS * CUPMENU_ROWS);
boolean foundany = false;
templevelsearch.cup = kartcupheaders;
templevelsearch.checklocked = false;
// Make sure there's valid cups before going to this menu.
#if 0
// Make sure there's valid cups before going to this menu. -- rip sweet prince
if (templevelsearch.cup == NULL)
I_Error("Can you really call this a racing game, I didn't recieve any Cups on my pillow or anything");
#endif
if (!cupgrid.builtgrid)
if (cupgrid.cappages == 0)
{
cupgrid.cappages = 2;
cupgrid.builtgrid = Z_Calloc(
cupgrid.cappages * unitlen,
cupgrid.cappages * pagelen,
PU_STATIC,
cupgrid.builtgrid);
NULL);
if (!cupgrid.builtgrid)
{
I_Error("M_LevelListFromGametype: Not enough memory to allocate builtgrid");
}
}
memset(cupgrid.builtgrid, 0, cupgrid.cappages * unitlen);
memset(cupgrid.builtgrid, 0, cupgrid.cappages * pagelen);
while (templevelsearch.cup)
{
templevelsearch.checklocked = false;
if (!M_CountLevelsToShowInList(&templevelsearch))
{
// No valid maps, skip.
@ -3584,10 +3637,12 @@ static void M_LevelListFromGametype(INT16 gt)
continue;
}
if ((currentid * sizeof(cupheader_t*)) >= cupgrid.cappages * unitlen)
foundany = true;
if ((currentid * sizeof(cupheader_t*)) >= cupgrid.cappages * pagelen)
{
// Double the size of the buffer, and clear the other stuff.
const size_t firstlen = cupgrid.cappages * unitlen;
const size_t firstlen = cupgrid.cappages * pagelen;
cupgrid.builtgrid = Z_Realloc(cupgrid.builtgrid,
firstlen * 2,
PU_STATIC, NULL);
@ -3603,7 +3658,8 @@ static void M_LevelListFromGametype(INT16 gt)
cupgrid.builtgrid[currentid] = templevelsearch.cup;
if (!M_CupLocked(templevelsearch.cup))
templevelsearch.checklocked = true;
if (M_GetFirstLevelInList(&temp, &templevelsearch) != NEXTMAP_INVALID)
{
highestunlockedid = currentid;
if (Playing() && mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->cup == templevelsearch.cup)
@ -3618,16 +3674,29 @@ static void M_LevelListFromGametype(INT16 gt)
templevelsearch.cup = templevelsearch.cup->next;
}
if (foundany == false)
{
return false;
}
cupgrid.numpages = (highestunlockedid / (CUPMENU_COLUMNS * CUPMENU_ROWS)) + 1;
if (cupgrid.pageno >= cupgrid.numpages)
{
cupgrid.pageno = 0;
}
PLAY_CupSelectDef.prevMenu = currentMenu;
PLAY_LevelSelectDef.prevMenu = &PLAY_CupSelectDef;
M_SetupNextMenu(&PLAY_CupSelectDef, false);
return;
return true;
}
// Okay, just a list of maps then.
if (M_GetFirstLevelInList(&temp, &levellist.levelsearch) == NEXTMAP_INVALID)
{
return false;
}
// Reset position properly if you go back & forth between gametypes
@ -3643,6 +3712,7 @@ static void M_LevelListFromGametype(INT16 gt)
PLAY_LevelSelectDef.prevMenu = currentMenu;
M_SetupNextMenu(&PLAY_LevelSelectDef, false);
return true;
}
// Init level select for use in local play using the last choice we made.
@ -3651,10 +3721,11 @@ static void M_LevelListFromGametype(INT16 gt)
void M_LevelSelectInit(INT32 choice)
{
INT32 gt = currentMenu->menuitems[itemOn].mvar2;
(void)choice;
// Make sure this is reset as we'll only be using this function for offline games!
cupgrid.netgame = false;
levellist.netgame = false;
levellist.levelsearch.checklocked = true;
@ -3677,7 +3748,111 @@ void M_LevelSelectInit(INT32 choice)
return;
}
M_LevelListFromGametype(currentMenu->menuitems[itemOn].mvar2);
if (gt == -1)
{
gt = menugametype;
}
if (!M_LevelListFromGametype(gt))
{
S_StartSound(NULL, sfx_s3kb2);
M_StartMessage(va("No levels available for\n%s Mode!\n\nPress (B)\n", gametypes[gt]->name), NULL, MM_NOTHING);
}
}
static void M_LevelSelected(INT16 add)
{
UINT8 i = 0;
INT16 map = M_GetFirstLevelInList(&i, &levellist.levelsearch);
while (add > 0)
{
map = M_GetNextLevelInList(map, &i, &levellist.levelsearch);
if (map >= nummapheaders)
{
break;
}
add--;
}
if (map >= nummapheaders)
{
// This shouldn't happen
return;
}
levellist.choosemap = map;
if (levellist.levelsearch.timeattack)
{
S_StartSound(NULL, sfx_s3k63);
if (levellist.guessgt != MAXGAMETYPES)
levellist.newgametype = G_GuessGametypeByTOL(levellist.levelsearch.typeoflevel);
PLAY_TimeAttackDef.lastOn = ta_start;
PLAY_TimeAttackDef.prevMenu = currentMenu;
M_SetupNextMenu(&PLAY_TimeAttackDef, false);
}
else
{
if (gamestate == GS_MENU)
{
UINT8 ssplayers = cv_splitplayers.value-1;
netgame = false;
multiplayer = true;
strncpy(connectedservername, cv_servername.string, MAXSERVERNAME);
// Still need to reset devmode
cht_debug = 0;
if (demo.playback)
G_StopDemo();
if (metalrecording)
G_StopMetalDemo();
/*if (levellist.choosemap == 0)
levellist.choosemap = G_RandMap(G_TOLFlag(levellist.newgametype), -1, 0, 0, false, NULL);*/
if (cv_maxconnections.value < ssplayers+1)
CV_SetValue(&cv_maxconnections, ssplayers+1);
if (splitscreen != ssplayers)
{
splitscreen = ssplayers;
SplitScreen_OnChange();
}
S_StartSound(NULL, sfx_s3k63);
paused = false;
// Early fadeout to let the sound finish playing
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame);
CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string);
CV_StealthSet(&cv_kartencore, (cv_dummygpencore.value == 1) ? "On" : "Auto");
CV_StealthSet(&cv_kartspeed, (cv_dummykartspeed.value == KARTSPEED_NORMAL) ? "Auto" : cv_dummykartspeed.string);
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false);
}
else
{
// directly do the map change
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false);
}
M_ClearMenus(true);
}
}
void M_CupSelectHandler(INT32 choice)
@ -3733,13 +3908,18 @@ void M_CupSelectHandler(INT32 choice)
if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/)
{
INT16 count;
cupheader_t *newcup = cupgrid.builtgrid[CUPMENU_CURSORID];
cupheader_t *oldcup = levellist.levelsearch.cup;
M_SetMenuDelay(pid);
levellist.levelsearch.cup = newcup;
count = M_CountLevelsToShowInList(&levellist.levelsearch);
if ((!newcup)
|| (M_CupLocked(newcup))
|| (newcup->cachedlevels[0] == NEXTMAP_INVALID))
|| (count <= 0)
|| (cupgrid.grandprix == true && newcup->cachedlevels[0] == NEXTMAP_INVALID))
{
S_StartSound(NULL, sfx_s3kb2);
return;
@ -3803,13 +3983,17 @@ void M_CupSelectHandler(INT32 choice)
M_ClearMenus(true);
}
else if (count == 1)
{
PLAY_TimeAttackDef.transitionID = currentMenu->transitionID+1;
M_LevelSelected(0);
}
else
{
// Keep cursor position if you select the same cup again, reset if it's a different cup
if (levellist.levelsearch.cup != newcup)
if (oldcup != newcup || levellist.cursor >= count)
{
levellist.cursor = 0;
levellist.levelsearch.cup = newcup;
}
M_LevelSelectScrollDest();
@ -3868,94 +4052,10 @@ void M_LevelSelectHandler(INT32 choice)
if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/)
{
UINT8 i = 0;
INT16 map = M_GetFirstLevelInList(&i, &levellist.levelsearch);
INT16 add = levellist.cursor;
M_SetMenuDelay(pid);
while (add > 0)
{
map = M_GetNextLevelInList(map, &i, &levellist.levelsearch);
if (map >= nummapheaders)
{
break;
}
add--;
}
if (map >= nummapheaders)
{
// This shouldn't happen
return;
}
levellist.choosemap = map;
if (levellist.levelsearch.timeattack)
{
M_SetupNextMenu(&PLAY_TimeAttackDef, false);
S_StartSound(NULL, sfx_s3k63);
}
else
{
if (gamestate == GS_MENU)
{
UINT8 ssplayers = cv_splitplayers.value-1;
netgame = false;
multiplayer = true;
strncpy(connectedservername, cv_servername.string, MAXSERVERNAME);
// Still need to reset devmode
cht_debug = 0;
if (demo.playback)
G_StopDemo();
if (metalrecording)
G_StopMetalDemo();
/*if (levellist.choosemap == 0)
levellist.choosemap = G_RandMap(G_TOLFlag(levellist.newgametype), -1, 0, 0, false, NULL);*/
if (cv_maxconnections.value < ssplayers+1)
CV_SetValue(&cv_maxconnections, ssplayers+1);
if (splitscreen != ssplayers)
{
splitscreen = ssplayers;
SplitScreen_OnChange();
}
S_StartSound(NULL, sfx_s3k63);
paused = false;
// Early fadeout to let the sound finish playing
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame);
CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string);
CV_StealthSet(&cv_kartencore, (cv_dummygpencore.value == 1) ? "On" : "Auto");
CV_StealthSet(&cv_kartspeed, (cv_dummykartspeed.value == KARTSPEED_NORMAL) ? "Auto" : cv_dummykartspeed.string);
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false);
}
else
{
// directly do the map change
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false);
}
M_ClearMenus(true);
}
PLAY_TimeAttackDef.transitionID = currentMenu->transitionID;
M_LevelSelected(levellist.cursor);
}
else if (M_MenuBackPressed(pid))
{
@ -4006,14 +4106,12 @@ void M_StartTimeAttack(INT32 choice)
(void)choice;
switch (levellist.newgametype)
modeattacking = ATTACKING_TIME;
if ((gametypes[levellist.newgametype]->rules & GTR_CIRCUIT)
&& (mapheaderinfo[levellist.choosemap]->numlaps != 1))
{
case GT_BATTLE:
modeattacking = ATTACKING_CAPSULES;
break;
default:
modeattacking = ATTACKING_TIME;
break;
modeattacking |= ATTACKING_LAP;
}
// Still need to reset devmode
@ -4078,6 +4176,7 @@ void M_MPOptSelectInit(INT32 choice)
{
INT16 arrcpy[3][3] = {{0,68,0}, {0,12,0}, {0,74,0}};
UINT8 i = 0, j = 0; // To copy the array into the struct
const UINT32 forbidden = GTR_FORBIDMP;
(void)choice;
@ -4088,6 +4187,10 @@ void M_MPOptSelectInit(INT32 choice)
for (j = 0; j < 3; j++)
mpmenu.modewinextend[i][j] = arrcpy[i][j]; // I miss Lua already
// Guarantee menugametype is good
M_NextMenuGametype(forbidden);
M_PrevMenuGametype(forbidden);
M_SetupNextMenu(&PLAY_MP_OptSelectDef, false);
}
@ -4119,41 +4222,93 @@ void M_MPHostInit(INT32 choice)
itemOn = mhost_go;
}
void M_NextMenuGametype(UINT32 forbidden)
{
const INT16 currentmenugametype = menugametype;
do
{
menugametype++;
if (menugametype >= numgametypes)
menugametype = 0;
if (!(gametypes[menugametype]->rules & forbidden))
break;
} while (menugametype != currentmenugametype);
}
void M_PrevMenuGametype(UINT32 forbidden)
{
const INT16 currentmenugametype = menugametype;
do
{
if (menugametype == 0)
menugametype = numgametypes;
menugametype--;
if (!(gametypes[menugametype]->rules & forbidden))
break;
} while (menugametype != currentmenugametype);
}
void M_HandleHostMenuGametype(INT32 choice)
{
const UINT8 pid = 0;
const UINT32 forbidden = GTR_FORBIDMP;
(void)choice;
if (M_MenuBackPressed(pid))
{
M_GoBack(0);
M_SetMenuDelay(pid);
return;
}
else if (menucmd[pid].dpad_lr > 0 || M_MenuConfirmPressed(pid))
{
M_NextMenuGametype(forbidden);
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
}
else if (menucmd[pid].dpad_lr < 0)
{
M_PrevMenuGametype(forbidden);
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
}
if (menucmd[pid].dpad_ud > 0)
{
M_NextOpt();
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
}
else if (menucmd[pid].dpad_ud < 0)
{
M_PrevOpt();
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
}
}
void M_MPSetupNetgameMapSelect(INT32 choice)
{
INT16 gt = GT_RACE;
(void)choice;
// Yep, we'll be starting a netgame.
levellist.netgame = true;
cupgrid.netgame = true;
// Make sure we reset those
levellist.levelsearch.timeattack = false;
levellist.levelsearch.checklocked = true;
cupgrid.grandprix = false;
// In case we ever want to add new gamemodes there somehow, have at it!
switch (cv_dummygametype.value)
{
case 1: // Battle
{
gt = GT_BATTLE;
break;
}
default:
{
gt = GT_RACE;
break;
}
}
// okay this is REALLY stupid but this fixes the host menu re-folding on itself when we go back.
mpmenu.modewinextend[0][0] = 1;
M_LevelListFromGametype(gt); // Setup the level select.
// (This will also automatically send us to the apropriate menu)
if (!M_LevelListFromGametype(menugametype))
{
S_StartSound(NULL, sfx_s3kb2);
M_StartMessage(va("No levels available for\n%s Mode!\n\nPress (B)\n", gametypes[menugametype]->name), NULL, MM_NOTHING);
}
}
// MULTIPLAYER JOIN BY IP
@ -6063,6 +6218,7 @@ void M_OpenPauseMenu(void)
// By default, disable anything sensitive:
PAUSE_Main[mpause_addons].status = IT_DISABLED;
PAUSE_Main[mpause_changegametype].status = IT_DISABLED;
PAUSE_Main[mpause_switchmap].status = IT_DISABLED;
PAUSE_Main[mpause_restartmap].status = IT_DISABLED;
PAUSE_Main[mpause_tryagain].status = IT_DISABLED;
@ -6084,14 +6240,10 @@ void M_OpenPauseMenu(void)
if (server || IsPlayerAdmin(consoleplayer))
{
PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_SUBMENU;
for (i = 0; i < PAUSE_GamemodesDef.numitems; i++)
{
if (PAUSE_GamemodesMenu[i].mvar2 != gametype)
continue;
PAUSE_GamemodesDef.lastOn = i;
break;
}
PAUSE_Main[mpause_changegametype].status = IT_STRING | IT_KEYHANDLER;
menugametype = gametype;
PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_CALL;
PAUSE_Main[mpause_restartmap].status = IT_STRING | IT_CALL;
PAUSE_Main[mpause_addons].status = IT_STRING | IT_CALL;
}
@ -6192,6 +6344,46 @@ boolean M_PauseInputs(INT32 ch)
return false;
}
// Change gametype
void M_HandlePauseMenuGametype(INT32 choice)
{
const UINT8 pid = 0;
const UINT32 forbidden = GTR_FORBIDMP;
(void)choice;
if (M_MenuConfirmPressed(pid))
{
if (menugametype != gametype)
{
M_ClearMenus(true);
COM_ImmedExecute(va("randommap -gt %s", gametypes[menugametype]->name));
return;
}
M_SetMenuDelay(pid);
S_StartSound(NULL, sfx_s3k7b);
}
else if (M_MenuExtraPressed(pid))
{
menugametype = gametype;
M_SetMenuDelay(pid);
S_StartSound(NULL, sfx_s3k7b);
}
else if (menucmd[pid].dpad_lr > 0)
{
M_NextMenuGametype(forbidden);
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
}
else if (menucmd[pid].dpad_lr < 0)
{
M_PrevMenuGametype(forbidden);
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
}
}
// Restart map
void M_RestartMap(INT32 choice)
{
@ -6269,7 +6461,7 @@ void M_EndGame(INT32 choice)
if (!Playing())
return;
M_StartMessage(M_GetText("Are you sure you want to return\nto the title screen?\nPress (A) to confirm or (B) to cancel\n"), FUNCPTRCAST(M_ExitGameResponse), MM_YESNO);
M_StartMessage(M_GetText("Are you sure you want to\nreturn to the menu?\nPress (A) to confirm or (B) to cancel\n"), FUNCPTRCAST(M_ExitGameResponse), MM_YESNO);
}
@ -7546,9 +7738,16 @@ void M_Statistics(INT32 choice)
if (!mapheaderinfo[i])
continue;
// Check for no visibility + legacy box
if (mapheaderinfo[i]->menuflags & (LF2_NOTIMEATTACK|LF2_HIDEINSTATS|LF2_HIDEINMENU))
continue;
// Check for completion
if ((mapheaderinfo[i]->menuflags & LF2_FINISHNEEDED)
&& !(mapheaderinfo[i]->mapvisited & MV_BEATEN))
continue;
// Check for unlock
if (M_MapLocked(i+1))
continue;

View file

@ -60,7 +60,7 @@ void Obj_DuelBombInit(mobj_t *bomb);
/* Broly Ki */
mobj_t *Obj_SpawnBrolyKi(mobj_t *source, tic_t duration);
void Obj_BrolyKiThink(mobj_t *ki);
boolean Obj_BrolyKiThink(mobj_t *ki);
/* Special Stage UFO */
waypoint_t *K_GetSpecialUFOWaypoint(mobj_t *ufo);

View file

@ -16,7 +16,6 @@
#include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems
#include "p_tick.h" // leveltime
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_profiles.h"
// Client-sided calculations done for Power Levels.
@ -38,7 +37,7 @@ SINT8 K_UsingPowerLevels(void)
{
SINT8 pt = PWRLV_DISABLED;
if (!cv_kartusepwrlv.value || !(netgame || (demo.playback && demo.netgame)) || grandprixinfo.gp == true || bossinfo.boss == true)
if (!cv_kartusepwrlv.value || !(netgame || (demo.playback && demo.netgame)) || grandprixinfo.gp == true)
{
return PWRLV_DISABLED;
}

View file

@ -400,7 +400,7 @@ static void K_DrawFinishLineBeamForLine(fixed_t offset, angle_t aiming, line_t *
void K_RunFinishLineBeam(void)
{
if (!(leveltime < starttime || rainbowstartavailable == true))
if ((gametyperules & GTR_ROLLINGSTART) || !(leveltime < starttime || rainbowstartavailable == true))
{
return;
}

View file

@ -175,14 +175,14 @@ void K_DoIngameRespawn(player_t *player)
mapthing_t *beststart = NULL;
UINT8 numstarts = 0;
if (gametype == GT_RACE)
{
numstarts = numcoopstarts;
}
else if (gametype == GT_BATTLE)
if (gametyperules & GTR_BATTLESTARTS)
{
numstarts = numdmstarts;
}
else
{
numstarts = numcoopstarts;
}
if (numstarts > 0)
{
@ -193,17 +193,13 @@ void K_DoIngameRespawn(player_t *player)
UINT32 dist = UINT32_MAX;
mapthing_t *checkstart = NULL;
if (gametype == GT_RACE)
{
checkstart = playerstarts[i];
}
else if (gametype == GT_BATTLE)
if (gametyperules & GTR_BATTLESTARTS)
{
checkstart = deathmatchstarts[i];
}
else
{
break;
checkstart = playerstarts[i];
}
dist = (UINT32)P_AproxDistance((player->mo->x >> FRACBITS) - checkstart->x,

View file

@ -176,6 +176,12 @@ static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] =
{ 0, 0, 0, 0 } // Gachabom x3
};
static kartitems_t K_KartItemReelSpecialEnd[] =
{
KITEM_SUPERRING,
KITEM_NONE
};
static kartitems_t K_KartItemReelTimeAttack[] =
{
KITEM_SNEAKER,
@ -359,7 +365,7 @@ static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers
return 0;
}
if (specialStage.active == true)
if (specialstageinfo.valid == true)
{
UINT32 ufoDis = K_GetSpecialUFODistance();
@ -506,7 +512,7 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette,
I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table
newOdds = K_KartItemOddsBattle[item-1][pos];
}
else if (specialStage.active == true)
else if (specialstageinfo.valid == true)
{
I_Assert(pos < 4); // Ditto
newOdds = K_KartItemOddsSpecial[item-1][pos];
@ -573,7 +579,7 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette,
return 0;
}
if (specialStage.active == false)
if (specialstageinfo.valid == false)
{
if (roulette->firstDist < ENDDIST*2 // No SPB when 1st is almost done
|| position == 1) // No SPB for 1st ever
@ -705,7 +711,7 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett
oddsValid[i] = false;
continue;
}
else if (specialStage.active == true && i > 3)
else if (specialstageinfo.valid == true && i > 3)
{
oddsValid[i] = false;
continue;
@ -734,7 +740,7 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett
}
else
{
if (specialStage.active == true) // Special Stages
if (specialstageinfo.valid == true) // Special Stages
{
SETUPDISTTABLE(0,2);
SETUPDISTTABLE(1,2);
@ -808,7 +814,7 @@ static boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulett
return false;
}
if (specialStage.active == true)
if (specialstageinfo.valid == true)
{
return false;
}
@ -904,7 +910,7 @@ static void K_InitRoulette(itemroulette_t *const roulette)
roulette->exiting++;
}
if (specialStage.active == true)
if (specialstageinfo.valid == true)
{
UINT32 dis = K_UndoMapScaling(players[i].distancetofinish);
if (dis < roulette->secondDist)
@ -926,7 +932,7 @@ static void K_InitRoulette(itemroulette_t *const roulette)
}
}
if (specialStage.active == true)
if (specialstageinfo.valid == true)
{
roulette->firstDist = K_UndoMapScaling(K_GetSpecialUFODistance());
}
@ -1113,8 +1119,19 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
}
// SPECIAL CASE No. 2:
// Use a special, pre-determined item reel for Time Attack / Free Play
if (bossinfo.boss == true)
// Use a special, pre-determined item reel for Time Attack / Free Play / End of Sealed Stars
if (specialstageinfo.valid)
{
if (K_GetPossibleSpecialTarget() == NULL)
{
for (i = 0; K_KartItemReelSpecialEnd[i] != KITEM_NONE; i++)
{
K_PushToRouletteItemList(roulette, K_KartItemReelSpecialEnd[i]);
}
return;
}
}
else if (gametyperules & GTR_BOSS)
{
for (i = 0; K_KartItemReelBoss[i] != KITEM_NONE; i++)
{
@ -1125,25 +1142,17 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
}
else if (K_TimeAttackRules() == true)
{
switch (gametype)
kartitems_t *presetlist = K_KartItemReelTimeAttack;
// If the objective is not to go fast, it's to cause serious damage.
if (gametyperules & GTR_CAPSULES)
{
case GT_RACE:
default:
{
for (i = 0; K_KartItemReelTimeAttack[i] != KITEM_NONE; i++)
{
K_PushToRouletteItemList(roulette, K_KartItemReelTimeAttack[i]);
}
break;
}
case GT_BATTLE:
{
for (i = 0; K_KartItemReelBreakTheCapsules[i] != KITEM_NONE; i++)
{
K_PushToRouletteItemList(roulette, K_KartItemReelBreakTheCapsules[i]);
}
break;
}
presetlist = K_KartItemReelBreakTheCapsules;
}
for (i = 0; presetlist[i] != KITEM_NONE; i++)
{
K_PushToRouletteItemList(roulette, presetlist[i]);
}
return;

View file

@ -22,7 +22,7 @@
#include "k_waypoint.h"
#include "k_objects.h"
struct specialStage specialStage;
struct specialstageinfo specialstageinfo;
/*--------------------------------------------------
void K_ResetSpecialStage(void)
@ -31,7 +31,8 @@ struct specialStage specialStage;
--------------------------------------------------*/
void K_ResetSpecialStage(void)
{
memset(&specialStage, 0, sizeof(struct specialStage));
memset(&specialstageinfo, 0, sizeof(struct specialstageinfo));
specialstageinfo.beamDist = UINT32_MAX;
}
/*--------------------------------------------------
@ -41,34 +42,15 @@ void K_ResetSpecialStage(void)
--------------------------------------------------*/
void K_InitSpecialStage(void)
{
INT32 i;
specialStage.beamDist = UINT32_MAX; // TODO: make proper value
P_SetTarget(&specialStage.ufo, Obj_CreateSpecialUFO());
for (i = 0; i < MAXPLAYERS; i++)
if ((gametyperules & (GTR_CATCHER|GTR_CIRCUIT)) != (GTR_CATCHER|GTR_CIRCUIT))
{
player_t *player = NULL;
if (playeringame[i] == false)
{
continue;
}
player = &players[i];
if (player->spectator == true)
{
continue;
}
if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true)
{
continue;
}
// Rolling start? lol
P_InstaThrust(player->mo, player->mo->angle, K_GetKartSpeed(player, false, false));
return;
}
specialstageinfo.valid = true;
specialstageinfo.beamDist = UINT32_MAX; // TODO: make proper value
P_SetTarget(&specialstageinfo.ufo, Obj_CreateSpecialUFO());
}
/*--------------------------------------------------
@ -88,15 +70,15 @@ static void K_MoveExitBeam(void)
moveDist = (8 * mapobjectscale) / FRACUNIT;
if (specialStage.beamDist <= moveDist)
if (specialstageinfo.beamDist <= moveDist)
{
specialStage.beamDist = 0;
specialstageinfo.beamDist = 0;
// TODO: Fail Special Stage
}
else
{
specialStage.beamDist -= moveDist;
specialstageinfo.beamDist -= moveDist;
}
// Find players who are now outside of the level.
@ -118,7 +100,7 @@ static void K_MoveExitBeam(void)
continue;
}
if (player->distancetofinish > specialStage.beamDist)
if (player->distancetofinish > specialstageinfo.beamDist)
{
P_DoTimeOver(player);
}
@ -132,10 +114,30 @@ static void K_MoveExitBeam(void)
--------------------------------------------------*/
void K_TickSpecialStage(void)
{
if (specialStage.active == false)
if (specialstageinfo.valid == false)
{
return;
}
if (P_MobjWasRemoved(specialstageinfo.ufo))
{
P_SetTarget(&specialstageinfo.ufo, NULL);
}
K_MoveExitBeam();
}
mobj_t *K_GetPossibleSpecialTarget(void)
{
if (specialstageinfo.valid == false)
return NULL;
if (specialstageinfo.ufo == NULL
|| P_MobjWasRemoved(specialstageinfo.ufo))
return NULL;
if (specialstageinfo.ufo->health <= 1) //UFOEmeraldChase(specialstageinfo.ufo)
return NULL;
return specialstageinfo.ufo;
}

View file

@ -20,14 +20,13 @@
extern "C" {
#endif
extern struct specialStage
extern struct specialstageinfo
{
boolean active; ///< If true, then we are in a special stage
boolean encore; ///< Copy of encore, just to make sure you can't cheat it with cvars
boolean valid; ///< If true, then data in this struct is valid
UINT32 beamDist; ///< Where the exit beam is.
mobj_t *ufo; ///< The Chaos Emerald capsule.
} specialStage;
} specialstageinfo;
/*--------------------------------------------------
void K_ResetSpecialStage(void);
@ -55,6 +54,18 @@ void K_InitSpecialStage(void);
void K_TickSpecialStage(void);
/*--------------------------------------------------
mobj_t *K_GetPossibleSpecialTarget(void)
Gets the global special stage target if valid
(for Jawz, tether, etc)
Return:-
Target or NULL
--------------------------------------------------*/
mobj_t *K_GetPossibleSpecialTarget(void);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -2971,15 +2971,16 @@ static int lib_gAddGametype(lua_State *L)
const char *k;
lua_Integer i;
gametype_t *newgametype = NULL;
const char *gtname = NULL;
const char *gtconst = NULL;
INT16 newgtidx = 0;
UINT32 newgtrules = 0;
UINT32 newgttol = 0;
INT32 newgtpointlimit = 0;
INT32 newgttimelimit = 0;
INT16 newgtrankingstype = -1;
int newgtinttype = 0;
UINT8 newgtinttype = 0;
INT16 j;
luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 1); // Clear out all other possible arguments, leaving only the first one.
@ -2988,8 +2989,10 @@ static int lib_gAddGametype(lua_State *L)
return luaL_error(L, "This function cannot be called from within a hook or coroutine!");
// Ran out of gametype slots
if (gametypecount == NUMGAMETYPEFREESLOTS)
return luaL_error(L, "Ran out of free gametype slots!");
if (numgametypes == GT_LASTFREESLOT)
{
I_Error("Out of Gametype Freeslots while allocating \"%s\"\nLoad less addons to fix this.", gtname);
}
#define FIELDERROR(f, e) luaL_error(L, "bad value for " LUA_QL(f) " in table passed to " LUA_QL("G_AddGametype") " (%s)", e);
#define TYPEERROR(f, t) FIELDERROR(f, va("%s expected, got %s", lua_typename(L, t), luaL_typename(L, -1)))
@ -3022,19 +3025,15 @@ static int lib_gAddGametype(lua_State *L)
if (!lua_isnumber(L, 3))
TYPEERROR("typeoflevel", LUA_TNUMBER)
newgttol = (UINT32)lua_tointeger(L, 3);
} else if (i == 5 || (k && fasticmp(k, "rankingtype"))) {
if (!lua_isnumber(L, 3))
TYPEERROR("rankingtype", LUA_TNUMBER)
newgtrankingstype = (INT16)lua_tointeger(L, 3);
} else if (i == 6 || (k && fasticmp(k, "intermissiontype"))) {
} else if (i == 5 || (k && fasticmp(k, "intermissiontype"))) {
if (!lua_isnumber(L, 3))
TYPEERROR("intermissiontype", LUA_TNUMBER)
newgtinttype = (int)lua_tointeger(L, 3);
} else if (i == 7 || (k && fasticmp(k, "defaultpointlimit"))) {
} else if (i == 6 || (k && fasticmp(k, "defaultpointlimit"))) {
if (!lua_isnumber(L, 3))
TYPEERROR("defaultpointlimit", LUA_TNUMBER)
newgtpointlimit = (INT32)lua_tointeger(L, 3);
} else if (i == 8 || (k && fasticmp(k, "defaulttimelimit"))) {
} else if (i == 7 || (k && fasticmp(k, "defaulttimelimit"))) {
if (!lua_isnumber(L, 3))
TYPEERROR("defaulttimelimit", LUA_TNUMBER)
newgttimelimit = (INT32)lua_tointeger(L, 3);
@ -3045,38 +3044,44 @@ static int lib_gAddGametype(lua_State *L)
#undef FIELDERROR
#undef TYPEERROR
if (gtname == NULL)
return luaL_error(L, "Custom gametype must have a name");
if (strlen(gtname) >= MAXGAMETYPELENGTH)
return luaL_error(L, "Custom gametype \"%s\"'s name must be %d long at most", gtname, MAXGAMETYPELENGTH-1);
for (j = 0; j < numgametypes; j++)
if (!strcmp(gtname, gametypes[j]->name))
break;
if (j < numgametypes)
return luaL_error(L, "Custom gametype \"%s\"'s name is already in use", gtname);
// pop gametype table
lua_pop(L, 1);
// Set defaults
if (gtname == NULL)
gtname = Z_StrDup("Unnamed gametype");
// Add the new gametype
newgtidx = G_AddGametype(newgtrules);
G_AddGametypeTOL(newgtidx, newgttol);
newgametype = Z_Calloc(sizeof (gametype_t), PU_STATIC, NULL);
if (!newgametype)
{
I_Error("Out of memory allocating gametype \"%s\"", gtname);
}
// Not covered by G_AddGametype alone.
if (newgtrankingstype == -1)
newgtrankingstype = newgtidx;
gametyperankings[newgtidx] = newgtrankingstype;
intermissiontypes[newgtidx] = newgtinttype;
pointlimits[newgtidx] = newgtpointlimit;
timelimits[newgtidx] = newgttimelimit;
// Write the new gametype name.
Gametype_Names[newgtidx] = gtname;
// Write the constant name.
if (gtconst == NULL)
gtconst = gtname;
G_AddGametypeConstant(newgtidx, gtconst);
// Update gametype_cons_t accordingly.
G_UpdateGametypeSelections();
newgametype->name = gtname;
newgametype->rules = newgtrules;
newgametype->constant = G_PrepareGametypeConstant(gtconst);
newgametype->tol = newgttol;
newgametype->intermission = newgtinttype;
newgametype->pointlimit = newgtpointlimit;
newgametype->timelimit = newgttimelimit;
gametypes[numgametypes++] = newgametype;
// done
CONS_Printf("Added gametype %s\n", Gametype_Names[newgtidx]);
CONS_Printf("Added gametype %s\n", gtname);
return 0;
}
@ -3284,15 +3289,6 @@ static int lib_gExitLevel(lua_State *L)
return 0;
}
static int lib_gIsSpecialStage(lua_State *L)
{
INT32 mapnum = luaL_optinteger(L, 1, gamemap);
//HUDSAFE
INLEVEL
lua_pushboolean(L, G_IsSpecialStage(mapnum));
return 1;
}
static int lib_gGametypeUsesLives(lua_State *L)
{
//HUDSAFE
@ -4101,7 +4097,6 @@ static luaL_Reg lib[] = {
{"G_DoReborn",lib_gDoReborn},
{"G_SetCustomExitVars",lib_gSetCustomExitVars},
{"G_ExitLevel",lib_gExitLevel},
{"G_IsSpecialStage",lib_gIsSpecialStage},
{"G_GametypeUsesLives",lib_gGametypeUsesLives},
{"G_GametypeHasTeams",lib_gGametypeHasTeams},
{"G_GametypeHasSpectators",lib_gGametypeHasSpectators},

View file

@ -162,9 +162,6 @@ int LUA_PushGlobals(lua_State *L, const char *word)
} else if (fastcmp(word,"maptol")) {
lua_pushinteger(L, maptol);
return 1;
} else if (fastcmp(word,"circuitmap")) {
lua_pushboolean(L, circuitmap);
return 1;
} else if (fastcmp(word,"stoppedclock")) {
lua_pushboolean(L, stoppedclock);
return 1;

View file

@ -570,7 +570,10 @@ static char *M_BuildConditionTitle(UINT16 map)
{
char *title, *ref;
if (M_MapLocked(map+1))
if (((mapheaderinfo[map]->menuflags & LF2_FINISHNEEDED)
// the following is intentionally not MV_BEATEN, just in case the title is for "Finish a round on X"
&& !(mapheaderinfo[map]->mapvisited & MV_VISITED))
|| M_MapLocked(map+1))
return Z_StrDup("???");
title = ref = G_BuildMapTitle(map+1);
@ -629,7 +632,7 @@ static const char *M_GetConditionString(condition_t *cn)
title = BUILDCONDITIONTITLE(cn->requirement);
work = va("%s %s%s",
(cn->type == UC_MAPVISITED) ? "Visit" : "Beat",
(cn->type == UC_MAPVISITED) ? "Visit" : "Finish a round on",
title,
(cn->type == UC_MAPENCORE) ? " in Encore Mode" : "");
Z_Free(title);

View file

@ -123,7 +123,8 @@ typedef enum
// Menu restrictions
SECRET_TIMEATTACK, // Permit Time attack
SECRET_BREAKTHECAPSULES, // Permit SP Capsules
SECRET_BREAKTHECAPSULES, // Permit SP Capsule attack
SECRET_SPECIALATTACK, // Permit Special attack (You're blue now!)
SECRET_SOUNDTEST, // Permit Sound Test
SECRET_ALTTITLE, // Permit alternate titlescreen

View file

@ -34,14 +34,16 @@ Obj_SpawnBrolyKi
( mobj_t * source,
tic_t duration)
{
mobj_t *x = P_SpawnMobjFromMobj(
source, 0, 0, 0, MT_BROLY);
mobj_t *x;
if (duration == 0)
if (duration <= 0)
{
return x;
return NULL;
}
x = P_SpawnMobjFromMobj(
source, 0, 0, 0, MT_BROLY);
// Shrink into center of source object.
x->z = (source->z + source->height / 2);
@ -61,12 +63,20 @@ Obj_SpawnBrolyKi
return x;
}
void
boolean
Obj_BrolyKiThink (mobj_t *x)
{
if (broly_duration(x) <= 0)
{
P_RemoveMobj(x);
return false;
}
const fixed_t
t = get_unit_linear(x),
n = Easing_OutSine(t, 0, broly_maxscale(x));
P_InstaScale(x, n);
return true;
}

View file

@ -549,7 +549,7 @@ static void SPBSeek(mobj_t *spb, mobj_t *bestMobj)
SetSPBSpeed(spb, xySpeed, zSpeed);
if (specialStage.active == false)
if (specialstageinfo.valid == false)
{
// see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh!
steerDist = 1536 * mapobjectscale;
@ -857,6 +857,27 @@ void Obj_SPBThink(mobj_t *spb)
ghost->colorized = true;
}
if (spb_nothink(spb) <= 1)
{
if (specialstageinfo.valid == true)
{
bestRank = 0;
if ((bestMobj = K_GetPossibleSpecialTarget()) == NULL)
{
// experimental - I think it's interesting IMO
Obj_MantaRingCreate(
spb,
spb_owner(spb),
NULL
);
spb->fuse = TICRATE/3;
spb_nothink(spb) = spb->fuse + 2;
}
}
}
if (spb_nothink(spb) > 0)
{
// Init values, don't think yet.
@ -874,15 +895,6 @@ void Obj_SPBThink(mobj_t *spb)
}
else
{
if (specialStage.active == true)
{
if (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false)
{
bestRank = 1;
bestMobj = specialStage.ufo;
}
}
// Find the player with the best rank
for (i = 0; i < MAXPLAYERS; i++)
{

View file

@ -239,9 +239,9 @@ static void UFOUpdateAngle(mobj_t *ufo)
waypoint_t *K_GetSpecialUFOWaypoint(mobj_t *ufo)
{
if ((ufo == NULL) && (specialStage.active == true))
if ((ufo == NULL) && (specialstageinfo.valid == true))
{
ufo = specialStage.ufo;
ufo = specialstageinfo.ufo;
}
if (ufo != NULL && P_MobjWasRemoved(ufo) == false
@ -281,10 +281,11 @@ static void UFOMove(mobj_t *ufo)
if (curWaypoint == NULL || destWaypoint == NULL)
{
// Waypoints aren't valid.
// Just stand still.
// Just go straight up.
// :japanese_ogre: : "Abrupt and funny is the funniest way to end the special stage anyways"
ufo->momx = 0;
ufo->momy = 0;
ufo->momz = 0;
ufo->momz = ufo_speed(ufo);
return;
}
@ -365,8 +366,23 @@ static void UFOMove(mobj_t *ufo)
if (reachedEnd == true)
{
CONS_Printf("You lost...\n");
ufo_waypoint(ufo) = -1; // Invalidate
UINT8 i;
// Invalidate UFO/emerald
ufo_waypoint(ufo) = -1;
ufo->flags &= ~(MF_SPECIAL|MF_PICKUPFROMBELOW);
// Disable player
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
players[i].pflags |= PF_NOCONTEST;
P_DoPlayerExit(&players[i]);
}
}
if (pathfindsuccess == true)
@ -591,6 +607,8 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN
ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW);
ufo->shadowscale = FRACUNIT/3;
P_LinedefExecute(LE_PINCHPHASE, ufo, NULL);
ufo_speed(ufo) += addSpeed; // Even more speed!
return true;
}
@ -655,7 +673,10 @@ void Obj_UFOPieceThink(mobj_t *piece)
fixed_t sc = FixedDiv(FixedDiv(ufo->ceilingz - stemZ, piece->scale), 15 * FRACUNIT);
UFOMoveTo(piece, ufo->x, ufo->y, stemZ);
piece->spriteyscale = sc;
if (sc > 0)
{
piece->spriteyscale = sc;
}
break;
}
default:
@ -820,11 +841,11 @@ mobj_t *Obj_CreateSpecialUFO(void)
UINT32 K_GetSpecialUFODistance(void)
{
if (specialStage.active == true)
if (specialstageinfo.valid == true)
{
if (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false)
if (specialstageinfo.ufo != NULL && P_MobjWasRemoved(specialstageinfo.ufo) == false)
{
return (UINT32)ufo_distancetofinish(specialStage.ufo);
return (UINT32)ufo_distancetofinish(specialstageinfo.ufo);
}
}

View file

@ -13053,7 +13053,7 @@ void A_ItemPop(mobj_t *actor)
}
// Here at mapload in battle?
if ((gametyperules & GTR_BUMPERS) && (actor->flags2 & MF2_BOSSNOTRAP))
if (!(gametyperules & GTR_CIRCUIT) && (actor->flags2 & MF2_BOSSNOTRAP))
{
numgotboxes++;

View file

@ -32,9 +32,9 @@
// SRB2kart
#include "k_kart.h"
#include "k_battle.h"
#include "k_specialstage.h"
#include "k_pwrlv.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_respawn.h"
#include "p_spec.h"
#include "k_objects.h"
@ -414,6 +414,8 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (toucher->hitlag > 0)
return;
P_LinedefExecute(LE_BOSSDEAD, toucher, NULL);
CONS_Printf("You win!\n");
break;
/*
@ -859,7 +861,7 @@ boolean P_CheckRacers(void)
// Check if all the players in the race have finished. If so, end the level.
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator || players[i].lives <= 0)
if (!playeringame[i] || players[i].spectator || (players[i].lives <= 0 && !players[i].exiting))
{
// Y'all aren't even playing
continue;
@ -893,7 +895,7 @@ boolean P_CheckRacers(void)
}
}
if (numPlaying <= 1)
if (numPlaying <= 1 || specialstageinfo.valid == true)
{
// Never do this without enough players.
eliminateLast = false;
@ -1097,7 +1099,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
{
P_SetTarget(&target->target, source);
if (gametyperules & GTR_BUMPERS)
if (!(gametyperules & GTR_CIRCUIT))
{
target->fuse = 2;
}
@ -2198,7 +2200,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
{
tic_t kinvextend;
if (gametype == GT_BATTLE)
if (gametyperules & GTR_CLOSERPLAYERS)
kinvextend = 2*TICRATE;
else
kinvextend = 5*TICRATE;

View file

@ -39,7 +39,6 @@
// SRB2kart
#include "k_kart.h"
#include "k_battle.h"
#include "k_boss.h"
#include "k_color.h"
#include "k_respawn.h"
#include "k_bot.h"
@ -4346,25 +4345,7 @@ static void P_RefreshItemCapsuleParts(mobj_t *mobj)
part->threshold = mobj->threshold;
part->movecount = mobj->movecount;
switch (itemType)
{
case KITEM_ORBINAUT:
part->sprite = SPR_ITMO;
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetOrbinautItemFrame(mobj->movecount);
break;
case KITEM_INVINCIBILITY:
part->sprite = SPR_ITMI;
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetInvincibilityItemFrame();
break;
case KITEM_SAD:
part->sprite = SPR_ITEM;
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE;
break;
default:
part->sprite = SPR_ITEM;
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|(itemType);
break;
}
K_UpdateMobjItemOverlay(part, itemType, mobj->movecount);
// update number frame
if (K_GetShieldFromItem(itemType) != KSHIELD_NONE) // shields don't stack, so don't show a number
@ -6163,7 +6144,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
mobj->color = mobj->target->color;
K_MatchGenericExtraFlags(mobj, mobj->target);
if ((gametype == GT_RACE || mobj->target->player->bumpers <= 0)
if ((!(gametyperules & GTR_BUMPERS) || mobj->target->player->bumpers <= 0)
#if 1 // Set to 0 to test without needing to host
|| (P_IsDisplayPlayer(mobj->target->player))
#endif
@ -6534,7 +6515,10 @@ static void P_MobjSceneryThink(mobj_t *mobj)
mobj->renderflags ^= RF_DONTDRAW;
break;
case MT_BROLY:
Obj_BrolyKiThink(mobj);
if (Obj_BrolyKiThink(mobj) == false)
{
return;
}
break;
case MT_VWREF:
case MT_VWREB:
@ -8362,7 +8346,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
statenum_t state = (mobj->state-states);
if (!mobj->target || !mobj->target->health || !mobj->target->player || mobj->target->player->spectator
|| (gametype == GT_RACE || mobj->target->player->bumpers))
|| (!(gametyperules & GTR_BUMPERS) || mobj->target->player->bumpers))
{
P_RemoveMobj(mobj);
return false;
@ -9366,7 +9350,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
{
if (gametyperules & GTR_PAPERITEMS)
{
if (battlecapsules == true || bossinfo.boss == true)
if (battlecapsules == true)
{
;
}
@ -9389,12 +9373,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
}
// FALLTHRU
case MT_SPHEREBOX:
if (gametype == GT_BATTLE && mobj->threshold == 70)
if (mobj->threshold == 70)
{
mobj->color = K_RainbowColor(leveltime);
mobj->colorized = true;
if (battleovertime.enabled)
if ((gametyperules & GTR_OVERTIME) && battleovertime.enabled)
{
angle_t ang = FixedAngle((leveltime % 360) << FRACBITS);
fixed_t z = battleovertime.z;
@ -9528,13 +9512,39 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s
P_RemoveMobj(mobj); // make sure they disappear
}
static boolean P_CanFlickerFuse(mobj_t *mobj)
{
switch (mobj->type)
{
case MT_SNAPPER_HEAD:
case MT_SNAPPER_LEG:
case MT_MINECARTSEG:
return true;
case MT_RANDOMITEM:
case MT_EGGMANITEM:
case MT_FALLINGROCK:
case MT_FLOATINGITEM:
if (mobj->fuse <= TICRATE)
{
return true;
}
break;
default:
break;
}
return false;
}
static boolean P_FuseThink(mobj_t *mobj)
{
if (mobj->type == MT_SNAPPER_HEAD || mobj->type == MT_SNAPPER_LEG || mobj->type == MT_MINECARTSEG)
mobj->renderflags ^= RF_DONTDRAW;
if (mobj->fuse <= TICRATE && (mobj->type == MT_RANDOMITEM || mobj->type == MT_EGGMANITEM || mobj->type == MT_FALLINGROCK))
if (P_CanFlickerFuse(mobj))
{
mobj->renderflags ^= RF_DONTDRAW;
}
mobj->fuse--;
@ -9582,7 +9592,7 @@ static boolean P_FuseThink(mobj_t *mobj)
{
;
}
else if ((gametyperules & GTR_BUMPERS) && (mobj->state == &states[S_INVISIBLE]))
else if (!(gametyperules & GTR_CIRCUIT) && (mobj->state == &states[S_INVISIBLE]))
{
break;
}
@ -11420,7 +11430,7 @@ void P_RespawnBattleBoxes(void)
{
thinker_t *th;
if (!(gametyperules & GTR_BUMPERS))
if (gametyperules & GTR_CIRCUIT)
return;
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
@ -11685,13 +11695,16 @@ void P_SpawnPlayer(INT32 playernum)
K_InitStumbleIndicator(p);
if (gametyperules & GTR_BUMPERS)
if (gametyperules & GTR_ITEMARROWS)
{
mobj_t *overheadarrow = P_SpawnMobj(mobj->x, mobj->y, mobj->z + mobj->height + 16*FRACUNIT, MT_PLAYERARROW);
P_SetTarget(&overheadarrow->target, mobj);
overheadarrow->renderflags |= RF_DONTDRAW;
P_SetScale(overheadarrow, mobj->destscale);
}
if (gametyperules & GTR_BUMPERS)
{
if (p->spectator)
{
// HEY! No being cheap...
@ -12075,7 +12088,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
// No bosses outside of a combat situation.
// (just in case we want boss arenas to do double duty as battle maps)
if (!bossinfo.boss && (mobjinfo[i].flags & MF_BOSS))
if (!(gametyperules & GTR_BOSS) && (mobjinfo[i].flags & MF_BOSS))
{
return false;
}
@ -12096,7 +12109,7 @@ static mobjtype_t P_GetMobjtypeSubstitute(mapthing_t *mthing, mobjtype_t i)
if ((i == MT_RING) && (gametyperules & GTR_SPHERES))
return MT_BLUESPHERE;
if ((i == MT_RANDOMITEM) && (gametyperules & (GTR_PAPERITEMS|GTR_CIRCUIT)) == (GTR_PAPERITEMS|GTR_CIRCUIT) && !bossinfo.boss)
if ((i == MT_RANDOMITEM) && (gametyperules & (GTR_PAPERITEMS|GTR_CIRCUIT)) == (GTR_PAPERITEMS|GTR_CIRCUIT))
return MT_PAPERITEMSPOT;
return i;

View file

@ -6820,7 +6820,6 @@ static void P_InitLevelSettings(void)
rflagpoint = bflagpoint = NULL;
// circuit, race and competition stuff
circuitmap = false;
numstarposts = 0;
timeinmap = 0;
@ -6842,7 +6841,7 @@ static void P_InitLevelSettings(void)
if (playeringame[i] && !players[i].spectator)
p++;
if (grandprixinfo.gp == false && bossinfo.boss == false)
if (grandprixinfo.gp == false)
players[i].lives = 3;
G_PlayerReborn(i, true);
@ -6852,39 +6851,27 @@ static void P_InitLevelSettings(void)
racecountdown = exitcountdown = exitfadestarted = 0;
curlap = bestlap = 0; // SRB2Kart
// SRB2Kart: map load variables
// Gamespeed and frantic items
gamespeed = KARTSPEED_EASY;
franticitems = false;
if (grandprixinfo.gp == true)
{
if ((gametyperules & GTR_BUMPERS))
{
gamespeed = KARTSPEED_EASY;
}
else
if (gametyperules & GTR_CIRCUIT)
{
gamespeed = grandprixinfo.gamespeed;
}
franticitems = false;
}
else if (bossinfo.boss)
{
gamespeed = KARTSPEED_EASY;
franticitems = false;
}
else if (modeattacking)
{
// Just play it safe and set everything
if ((gametyperules & GTR_BUMPERS))
gamespeed = KARTSPEED_EASY;
else
if (gametyperules & GTR_CIRCUIT)
{
gamespeed = KARTSPEED_HARD;
franticitems = false;
}
}
else
{
if ((gametyperules & GTR_BUMPERS))
gamespeed = KARTSPEED_EASY;
else
if (gametyperules & GTR_CIRCUIT)
{
if (cv_kartspeed.value == KARTSPEED_AUTO)
gamespeed = ((speedscramble == -1) ? KARTSPEED_NORMAL : (UINT8)speedscramble);
@ -6899,6 +6886,9 @@ static void P_InitLevelSettings(void)
memset(&battleovertime, 0, sizeof(struct battleovertime));
speedscramble = encorescramble = -1;
K_ResetSpecialStage();
K_ResetBossInfo();
}
#if 0
@ -7000,20 +6990,23 @@ static void P_LoadRecordGhosts(void)
gpath = Z_StrDup(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap)));
// Best Time ghost
if (cv_ghost_besttime.value)
if (modeattacking & ATTACKING_TIME)
{
for (i = 0; i < numskins; ++i)
if (cv_ghost_besttime.value)
{
if (cv_ghost_besttime.value == 1 && players[consoleplayer].skin != i)
continue;
for (i = 0; i < numskins; ++i)
{
if (cv_ghost_besttime.value == 1 && players[consoleplayer].skin != i)
continue;
if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, skins[i].name)))
G_AddGhost(va("%s-%s-time-best.lmp", gpath, skins[i].name));
if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, skins[i].name)))
G_AddGhost(va("%s-%s-time-best.lmp", gpath, skins[i].name));
}
}
}
// Best Lap ghost
if (modeattacking != ATTACKING_CAPSULES)
if (modeattacking & ATTACKING_LAP)
{
if (cv_ghost_bestlap.value)
{
@ -7220,7 +7213,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
// This is needed. Don't touch.
maptol = mapheaderinfo[gamemap-1]->typeoflevel;
gametyperules = gametypedefaultrules[gametype];
CON_Drawer(); // let the user know what we are going to do
I_FinishUpdate(); // page flip or blit buffer
@ -7334,13 +7326,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
}
G_ClearModeAttackRetryFlag();
}
/*
else if (rendermode != render_none && G_IsSpecialStage(gamemap))
{
P_RunSpecialStageWipe();
ranspecialwipe = 1;
}
*/
// Make sure all sounds are stopped before Z_FreeTags.
S_StopSounds();
@ -7375,7 +7360,20 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
S_Start();
}
levelfadecol = (encoremode ? 0 : 31);
if (gametyperules & GTR_SPECIALSTART)
{
if (ranspecialwipe != 2)
S_StartSound(NULL, sfx_s3kaf);
levelfadecol = 0;
}
else if (encoremode)
{
levelfadecol = 0;
}
else
{
levelfadecol = 31;
}
if (rendermode != render_none)
{
@ -7611,19 +7609,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
K_UpdateMatchRaceBots();
}
if (bossinfo.boss)
{
// Reset some pesky boss state that can't be handled elsewhere.
bossinfo.barlen = BOSSHEALTHBARLEN;
bossinfo.visualbar = 0;
Z_Free(bossinfo.enemyname);
Z_Free(bossinfo.subtitle);
bossinfo.enemyname = bossinfo.subtitle = NULL;
bossinfo.titleshow = 0;
bossinfo.titlesound = sfx_typri1;
memset(&(bossinfo.weakspots), 0, sizeof(weakspot_t)*NUMWEAKSPOTS);
}
if (!fromnetsave) // uglier hack
{ // to make a newly loaded level start on the second frame.
INT32 buf = gametic % BACKUPTICS;
@ -7769,7 +7754,7 @@ UINT8 P_InitMapData(boolean existingmapheaders)
for (i = 0; i < nummapheaders; ++i)
{
name = mapheaderinfo[i]->lumpname;
maplump = W_CheckNumForMap(name);
maplump = W_CheckNumForMap(name, (mapheaderinfo[i]->lumpnum == LUMPERROR));
// Always check for cup cache reassociations.
// (The core assumption is that cups < headers.)

View file

@ -43,6 +43,7 @@
// SRB2kart
#include "k_kart.h"
#include "k_specialstage.h"
#include "console.h" // CON_LogMessage
#include "k_respawn.h"
#include "k_terrain.h"
@ -1843,7 +1844,7 @@ static void K_HandleLapIncrement(player_t *player)
{
if (player)
{
if (leveltime < starttime)
if (leveltime < starttime && !(gametyperules & GTR_ROLLINGSTART))
{
// Will fault the player
K_DoIngameRespawn(player);
@ -1902,7 +1903,7 @@ static void K_HandleLapIncrement(player_t *player)
if (P_IsDisplayPlayer(player))
{
if (player->laps == numlaps) // final lap
if (numlaps > 1 && player->laps == numlaps) // final lap
S_StartSound(NULL, sfx_s3k68);
else if ((player->laps > 1) && (player->laps < numlaps)) // non-final lap
S_StartSound(NULL, sfx_s221);
@ -1925,6 +1926,14 @@ static void K_HandleLapIncrement(player_t *player)
// finished race exit setup
if (player->laps > numlaps)
{
if (specialstageinfo.valid == true)
{
// Don't permit a win just by sneaking ahead of the UFO/emerald.
if (!(specialstageinfo.ufo == NULL || P_MobjWasRemoved(specialstageinfo.ufo)))
{
player->pflags |= PF_NOCONTEST;
}
}
P_DoPlayerExit(player);
P_SetupSignExit(player);
}
@ -6662,12 +6671,6 @@ void P_SpawnSpecials(boolean fromnetsave)
break;
}
// SRB2Kart
case 2001: // Finish Line
if ((gametyperules & GTR_CIRCUIT))
circuitmap = true;
break;
default:
break;
}

View file

@ -361,7 +361,7 @@ static inline void P_RunThinkers(void)
if (gametyperules & GTR_PAPERITEMS)
K_RunPaperItemSpawners();
if ((gametyperules & GTR_BUMPERS) && battleovertime.enabled)
if ((gametyperules & GTR_OVERTIME) && battleovertime.enabled)
K_RunBattleOvertime();
}
@ -646,7 +646,7 @@ void P_Ticker(boolean run)
P_PlayerAfterThink(&players[i]);
// Bosses have a punchy start, so no position.
if (bossinfo.boss == true)
if (K_CheckBossIntro() == true)
{
if (leveltime == 3)
{
@ -719,7 +719,7 @@ void P_Ticker(boolean run)
K_TickSpecialStage();
if ((gametyperules & GTR_BUMPERS))
if ((gametyperules & GTR_POINTLIMIT))
{
if (wantedcalcdelay && --wantedcalcdelay <= 0)
K_CalculateBattleWanted();

View file

@ -54,6 +54,7 @@
#include "k_bot.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_specialstage.h"
#include "k_terrain.h" // K_SpawnSplashForMobj
#include "k_color.h"
#include "k_follower.h"
@ -501,7 +502,7 @@ INT32 P_GivePlayerRings(player_t *player, INT32 num_rings)
if (!player->mo)
return 0;
if ((gametyperules & GTR_BUMPERS)) // No rings in Battle Mode
if ((gametyperules & GTR_SPHERES)) // No rings in Battle Mode
return 0;
test = player->rings + num_rings;
@ -519,6 +520,9 @@ INT32 P_GivePlayerSpheres(player_t *player, INT32 num_spheres)
{
num_spheres += player->spheres;
if (!(gametyperules & GTR_SPHERES)) // No spheres in Race mode)
return 0;
// Not alive
if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0))
return 0;
@ -554,7 +558,7 @@ void P_GivePlayerLives(player_t *player, INT32 numlives)
// Adds to the player's score
void P_AddPlayerScore(player_t *player, UINT32 amount)
{
if (!((gametyperules & GTR_BUMPERS)))
if (!((gametyperules & GTR_POINTLIMIT)))
return;
if (player->exiting) // srb2kart
@ -720,6 +724,7 @@ boolean P_EndingMusic(player_t *player)
{
char buffer[9];
boolean looping = true;
boolean racetracks = !!(gametyperules & GTR_CIRCUIT);
INT32 bestlocalpos, test;
player_t *bestlocalplayer;
@ -773,7 +778,7 @@ boolean P_EndingMusic(player_t *player)
#undef getplayerpos
if ((gametyperules & GTR_CIRCUIT) && bestlocalpos == MAXPLAYERS+1)
if (racetracks == true && bestlocalpos == MAXPLAYERS+1)
sprintf(buffer, "k*fail"); // F-Zero death results theme
else
{
@ -787,9 +792,11 @@ boolean P_EndingMusic(player_t *player)
S_SpeedMusic(1.0f);
if ((gametyperules & GTR_CIRCUIT))
if (racetracks == true)
{
buffer[1] = 'r';
else if ((gametyperules & GTR_BUMPERS))
}
else
{
buffer[1] = 'b';
looping = false;
@ -828,7 +835,8 @@ void P_RestoreMusic(player_t *player)
return;
// Event - Level Start
if (bossinfo.boss == false && (leveltime < (starttime + (TICRATE/2)))) // see also where time overs are handled
if ((K_CheckBossIntro() == false)
&& (leveltime < (starttime + (TICRATE/2)))) // see also where time overs are handled
return;
{
@ -1303,7 +1311,16 @@ void P_DoPlayerExit(player_t *player)
P_EndingMusic(player);
if (P_CheckRacers() && !exitcountdown)
exitcountdown = raceexittime+1;
{
if (specialstageinfo.valid == true && losing == true)
{
exitcountdown = (5*TICRATE)/2;
}
else
{
exitcountdown = raceexittime+1;
}
}
}
else if ((gametyperules & GTR_BUMPERS)) // Battle Mode exiting
{
@ -2622,7 +2639,7 @@ static void P_DeathThink(player_t *player)
player->realtime = leveltime - starttime;
if (player == &players[consoleplayer])
{
if (player->spectator || !circuitmap)
if (player->spectator)
curlap = 0;
else if (curlap != UINT32_MAX)
curlap++; // This is too complicated to sync to realtime, just sorta hope for the best :V
@ -3044,7 +3061,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
return true;
}
if ((player->pflags & PF_NOCONTEST) && (gametyperules & GTR_CIRCUIT)) // 1 for momentum keep, 2 for turnaround
if ((player->pflags & PF_NOCONTEST) && (gametyperules & GTR_CIRCUIT) && player->karthud[khud_timeovercam] != 0) // 1 for momentum keep, 2 for turnaround
timeover = (player->karthud[khud_timeovercam] > 2*TICRATE ? 2 : 1);
else
timeover = 0;
@ -3612,7 +3629,7 @@ void P_DoTimeOver(player_t *player)
legitimateexit = true; // SRB2kart: losing a race is still seeing it through to the end :p
}
if (netgame && !player->bot && !bossinfo.boss)
if (netgame && !player->bot && !(gametyperules & GTR_BOSS))
{
CON_LogMessage(va(M_GetText("%s ran out of time.\n"), player_names[player-players]));
}
@ -3981,7 +3998,7 @@ void P_PlayerThink(player_t *player)
player->realtime = leveltime - starttime;
if (player == &players[consoleplayer])
{
if (player->spectator || !circuitmap)
if (player->spectator)
curlap = 0;
else if (curlap != UINT32_MAX)
curlap++; // This is too complicated to sync to realtime, just sorta hope for the best :V

View file

@ -30,7 +30,6 @@
#include "m_misc.h" // for tunes command
#include "m_cond.h" // for conditionsets
#include "lua_hook.h" // MusicChange hook
#include "k_boss.h" // bossinfo
#include "byteptr.h"
#ifdef HW3SOUND

View file

@ -597,7 +597,7 @@ void ST_runTitleCard(void)
// SRB2KART
// side Zig-Zag positions...
if (bossinfo.boss == true)
if (K_CheckBossIntro() == true)
{
// Handle name info...
if (bossinfo.enemyname)
@ -792,7 +792,7 @@ void ST_drawTitleCard(void)
if (lt_ticker < TTANIMSTART)
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
if (bossinfo.boss == true)
if (K_CheckBossIntro() == true)
{
// WARNING!
// https://twitter.com/matthewseiji/status/1485003284196716544
@ -802,7 +802,7 @@ void ST_drawTitleCard(void)
#define HITIME 15
patch_t *localwarn = (encoremode ? twarn2 : twarn);
INT32 transp = (lt_ticker+HITIME) % (LOTIME+HITIME);
boolean encorehack = (encoremode && lt_ticker <= PRELEVELTIME+4);
boolean encorehack = ((levelfadecol == 0) && lt_ticker <= PRELEVELTIME+4);
if ((localwarn->width > 0) && (lt_ticker + (HITIME-transp) <= lt_endtime))
{
@ -1253,15 +1253,15 @@ void ST_Drawer(void)
switch (demo.savemode)
{
case DSM_NOTSAVING:
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "(B) or (X): Save replay");
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|V_YELLOWMAP, "(B) or (X): Save replay");
break;
case DSM_WILLAUTOSAVE:
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "Replay will be saved. (Look Backward: Change title)");
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|V_YELLOWMAP, "Replay will be saved. (Look Backward: Change title)");
break;
case DSM_WILLSAVE:
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "Replay will be saved.");
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|V_YELLOWMAP, "Replay will be saved.");
break;
case DSM_TITLEENTRY:

View file

@ -111,6 +111,7 @@ TYPEDEF (textpage_t);
TYPEDEF (textprompt_t);
TYPEDEF (mappoint_t);
TYPEDEF (customoption_t);
TYPEDEF (gametype_t);
TYPEDEF (mapheader_t);
TYPEDEF (tolinfo_t);
TYPEDEF (cupheader_t);

View file

@ -585,16 +585,16 @@ void V_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 du
}
}
if (options & V_SLIDEIN)
if ((options & V_SLIDEIN))
{
const tic_t length = TICRATE/4;
tic_t timer = lt_exitticker;
if (bossinfo.boss == true)
if (K_CheckBossIntro() == true || G_IsTitleCardAvailable() == false)
{
if (leveltime <= 3)
if (leveltime <= 16)
timer = 0;
else
timer = leveltime-3;
timer = leveltime-16;
}
if (timer < length)

View file

@ -1309,12 +1309,12 @@ lumpnum_t W_CheckNumForLongName(const char *name)
// Look for valid map data through all added files in descendant order.
// Get a map marker for WADs, and a standalone WAD file lump inside PK3s.
lumpnum_t W_CheckNumForMap(const char *name)
lumpnum_t W_CheckNumForMap(const char *name, boolean checktofirst)
{
lumpnum_t check = INT16_MAX;
UINT32 uhash, hash = quickncasehash(name, LUMPNUMCACHENAME);
INT32 i;
UINT16 firstfile = (partadd_earliestfile == UINT16_MAX) ? 0 : partadd_earliestfile;
UINT16 firstfile = (checktofirst || (partadd_earliestfile == UINT16_MAX)) ? 0 : partadd_earliestfile;
// Check the lumpnumcache first. Loop backwards so that we check
// most recent entries first

View file

@ -171,7 +171,7 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump);
UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump);
UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump);
lumpnum_t W_CheckNumForMap(const char *name);
lumpnum_t W_CheckNumForMap(const char *name, boolean checktofirst);
lumpnum_t W_CheckNumForName(const char *name);
lumpnum_t W_CheckNumForLongName(const char *name);
lumpnum_t W_GetNumForName(const char *name); // like W_CheckNumForName but I_Error on LUMPERROR

View file

@ -98,7 +98,6 @@ static INT32 endtic = -1;
static INT32 sorttic = -1;
intertype_t intertype = int_none;
intertype_t intermissiontypes[NUMGAMETYPES];
static huddrawlist_h luahuddrawlist_intermission;
@ -210,7 +209,7 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
else
{
// set up the levelstring
if (bossinfo.boss == true && bossinfo.enemyname)
if (bossinfo.valid == true && bossinfo.enemyname)
{
snprintf(data.levelstring,
sizeof data.levelstring,
@ -335,7 +334,7 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
//
void Y_IntermissionDrawer(void)
{
INT32 i, whiteplayer = MAXPLAYERS, x = 4, hilicol = V_YELLOWMAP; // fallback
INT32 i, whiteplayer = MAXPLAYERS, x = 4, hilicol = highlightflags;
if (intertype == int_none || rendermode == render_none)
return;
@ -358,11 +357,6 @@ void Y_IntermissionDrawer(void)
if (!r_splitscreen)
whiteplayer = demo.playback ? displayplayers[0] : consoleplayer;
if (modeattacking)
hilicol = V_ORANGEMAP;
else
hilicol = ((intertype == int_race) ? V_SKYMAP : V_REDMAP);
if (sorttic != -1 && intertic > sorttic)
{
INT32 count = (intertic - sorttic);
@ -375,7 +369,7 @@ void Y_IntermissionDrawer(void)
x += (((16 - count) * vid.width) / (8 * vid.dupx));
}
if (intertype == int_race || intertype == int_battle || intertype == int_battletime)
if (intertype == int_time || intertype == int_score)
{
#define NUMFORNEWCOLUMN 8
INT32 y = 41, gutter = ((data.numplayers > NUMFORNEWCOLUMN) ? 0 : (BASEVIDWIDTH/2));
@ -398,7 +392,7 @@ void Y_IntermissionDrawer(void)
{
switch (intertype)
{
case int_battle:
case int_score:
timeheader = "SCORE";
break;
default:
@ -533,7 +527,7 @@ void Y_IntermissionDrawer(void)
V_DrawRightAlignedThinString(x+152+gutter, y-1, (data.numplayers > NUMFORNEWCOLUMN ? V_6WIDTHSPACE : 0), "NO CONTEST.");
else
{
if (intertype == int_race || intertype == int_battletime)
if (intertype == int_time)
{
snprintf(strtime, sizeof strtime, "%i'%02i\"%02i", G_TicsToMinutes(data.val[i], true),
G_TicsToSeconds(data.val[i]), G_TicsToCentiseconds(data.val[i]));
@ -575,7 +569,7 @@ skiptallydrawer:
if (!LUA_HudEnabled(hud_intermissionmessages))
return;
if (timer && grandprixinfo.gp == false && bossinfo.boss == false && !modeattacking)
if (timer && grandprixinfo.gp == false && !modeattacking)
{
char *string;
INT32 tickdown = (timer+1)/TICRATE;
@ -669,7 +663,7 @@ void Y_Ticker(void)
if (intertic < TICRATE || intertic & 1 || endtic != -1)
return;
if (intertype == int_race || intertype == int_battle || intertype == int_battletime)
if (intertype == int_time || intertype == int_score)
{
{
if (!data.rankingsmode && sorttic != -1 && (intertic >= sorttic + 8))
@ -751,29 +745,28 @@ void Y_Ticker(void)
//
void Y_DetermineIntermissionType(void)
{
// set to int_none initially
intertype = int_none;
if (gametype == GT_RACE)
intertype = int_race;
else if (gametype == GT_BATTLE)
// no intermission for GP events
if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)
{
if (grandprixinfo.gp == true && bossinfo.boss == false)
intertype = int_none;
else
{
UINT8 i = 0, nump = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
nump++;
}
intertype = (nump < 2 ? int_battletime : int_battle);
}
intertype = int_none;
return;
}
// set initially
intertype = gametypes[gametype]->intermission;
// special cases
if (intertype == int_scoreortimeattack)
{
UINT8 i = 0, nump = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
nump++;
}
intertype = (nump < 2 ? int_time : int_score);
}
else //if (intermissiontypes[gametype] != int_none)
intertype = intermissiontypes[gametype];
}
//
@ -830,9 +823,6 @@ void Y_StartIntermission(void)
sorttic = max((timer/2) - 2*TICRATE, 2*TICRATE);
}
if (intermissiontypes[gametype] != int_none)
intertype = intermissiontypes[gametype];
// We couldn't display the intermission even if we wanted to.
// But we still need to give the players their score bonuses, dummy.
//if (dedicated) return;
@ -841,23 +831,18 @@ void Y_StartIntermission(void)
if (prevmap >= nummapheaders || !mapheaderinfo[prevmap])
I_Error("Y_StartIntermission: Internal map ID %d not found (nummapheaders = %d)", prevmap, nummapheaders);
if (!(gametyperules & GTR_CIRCUIT) && (timer > 1))
S_ChangeMusicInternal("racent", true); // loop it
switch (intertype)
{
case int_battle:
case int_battletime:
case int_score:
{
if (timer > 1)
S_ChangeMusicInternal("racent", true); // loop it
// Calculate who won
if (intertype == int_battle)
{
Y_CalculateMatchData(0, Y_CompareScore);
break;
}
Y_CalculateMatchData(0, Y_CompareScore);
break;
}
// FALLTHRU
case int_race:
case int_time:
{
// Calculate who won
Y_CalculateMatchData(0, Y_CompareTime);
@ -1224,12 +1209,8 @@ void Y_VoteDrawer(void)
if (timer)
{
INT32 hilicol, tickdown = (timer+1)/TICRATE;
if (gametype == GT_RACE)
hilicol = V_SKYMAP;
else //if (gametype == GT_BATTLE)
hilicol = V_REDMAP;
V_DrawCenteredString(BASEVIDWIDTH/2, 188, hilicol,
INT32 tickdown = (timer+1)/TICRATE;
V_DrawCenteredString(BASEVIDWIDTH/2, 188, V_YELLOWMAP,
va("Vote ends in %d", tickdown));
}
}
@ -1569,9 +1550,9 @@ void Y_StartVote(void)
levelinfo[i].str[sizeof levelinfo[i].str - 1] = '\0';
// set up the gtc and gts
levelinfo[i].gtc = G_GetGametypeColor(votelevels[i][1]);
levelinfo[i].gtc = 73; // yellowmap[0] -- TODO rewrite vote screen
if (i == 2 && votelevels[i][1] != votelevels[0][1])
levelinfo[i].gts = Gametype_Names[votelevels[i][1]];
levelinfo[i].gts = gametypes[votelevels[i][1]]->name;
else
levelinfo[i].gts = NULL;
}

View file

@ -33,13 +33,12 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level);
typedef enum
{
int_none,
int_race, // Race
int_battle, // Battle (score-based)
int_battletime, // Battle (time-based)
int_time, // Always time
int_score, // Always score
int_scoreortimeattack, // Score unless 1P
} intertype_t;
extern intertype_t intertype;
extern intertype_t intermissiontypes[NUMGAMETYPES];
#ifdef __cplusplus
} // extern "C"