mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-04-28 04:51:42 +00:00
Merge branch 'master' into special-stages
This commit is contained in:
commit
82df346417
64 changed files with 4554 additions and 1343 deletions
|
|
@ -132,9 +132,12 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
|
|||
k_roulette.c
|
||||
)
|
||||
|
||||
if("${CMAKE_COMPILER_IS_GNUCC}" AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows" AND NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}" AND NOT "${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}")
|
||||
# On MinGW with internal libraries, link the standard library statically
|
||||
target_link_options(SRB2SDL2 PRIVATE "-static")
|
||||
if("${CMAKE_COMPILER_IS_GNUCC}" AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
|
||||
target_link_options(SRB2SDL2 PRIVATE "-Wl,--disable-dynamicbase")
|
||||
if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}" AND NOT "${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}")
|
||||
# On MinGW with internal libraries, link the standard library statically
|
||||
target_link_options(SRB2SDL2 PRIVATE "-static")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_compile_features(SRB2SDL2 PRIVATE c_std_11 cxx_std_17)
|
||||
|
|
@ -149,7 +152,11 @@ set(SRB2_CONFIG_USEASM OFF CACHE BOOL
|
|||
set(SRB2_CONFIG_YASM OFF CACHE BOOL
|
||||
"Use YASM in place of NASM.")
|
||||
set(SRB2_CONFIG_DEV_BUILD OFF CACHE BOOL
|
||||
"Compile a development build of SRB2Kart.")
|
||||
"Compile a development build of Dr Robotnik's Ring Racers.")
|
||||
set(SRB2_CONFIG_TESTERS OFF CACHE BOOL
|
||||
"Compile a build for testers.")
|
||||
set(SRB2_CONFIG_HOSTTESTERS OFF CACHE BOOL
|
||||
"Compile a build to host netgames for testers builds.")
|
||||
|
||||
add_subdirectory(blua)
|
||||
|
||||
|
|
@ -233,7 +240,7 @@ if(${SRB2_CONFIG_HAVE_ZLIB})
|
|||
set(SRB2_HAVE_ZLIB ON)
|
||||
target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_ZLIB)
|
||||
else()
|
||||
message(WARNING "You have specified that ZLIB is available but it was not found. SRB2Kart may not compile correctly.")
|
||||
message(WARNING "You have specified that ZLIB is available but it was not found. Dr Robotnik's Ring Racers may not compile correctly.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
@ -256,7 +263,7 @@ if(${SRB2_CONFIG_HAVE_PNG} AND ${SRB2_CONFIG_HAVE_ZLIB})
|
|||
target_compile_definitions(SRB2SDL2 PRIVATE -D_LARGEFILE64_SOURCE)
|
||||
target_sources(SRB2SDL2 PRIVATE apng.c)
|
||||
else()
|
||||
message(WARNING "You have specified that PNG is available but it was not found. SRB2Kart may not compile correctly.")
|
||||
message(WARNING "You have specified that PNG is available but it was not found. Dr Robotnik's Ring Racers may not compile correctly.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -482,6 +489,12 @@ target_compile_definitions(SRB2SDL2 PRIVATE -DCMAKECONFIG)
|
|||
if(SRB2_CONFIG_DEBUGMODE)
|
||||
target_compile_definitions(SRB2SDL2 PRIVATE -DZDEBUG -DPARANOIA -DRANGECHECK -DPACKETDROP)
|
||||
endif()
|
||||
if(SRB2_CONFIG_TESTERS)
|
||||
target_compile_definitions(SRB2SDL2 PRIVATE -DTESTERS)
|
||||
endif()
|
||||
if(SRB2_CONFIG_HOSTTESTERS)
|
||||
target_compile_definitions(SRB2SDL2 PRIVATE -DHOSTTESTERS)
|
||||
endif()
|
||||
if(SRB2_CONFIG_MOBJCONSISTANCY)
|
||||
target_compile_definitions(SRB2SDL2 PRIVATE -DMOBJCONSISTANCY)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ CV_PossibleValue_t CV_Natural[] = {{1, "MIN"}, {999999999, "MAX"}, {0, NULL}};
|
|||
#else
|
||||
#define VALUE "Off"
|
||||
#endif
|
||||
consvar_t cv_cheats = CVAR_INIT ("cheats", VALUE, CV_NETVAR|CV_CALL, CV_OnOff, CV_CheatsChanged);
|
||||
consvar_t cv_cheats = CVAR_INIT ("cheats", VALUE, CV_NETVAR|CV_CALL|CV_NOINIT, CV_OnOff, CV_CheatsChanged);
|
||||
#undef VALUE
|
||||
|
||||
// SRB2kart
|
||||
|
|
@ -1481,7 +1481,7 @@ boolean CV_CompleteValue(consvar_t *var, const char **valstrp, INT32 *intval)
|
|||
{
|
||||
v = R_SkinAvailable(valstr);
|
||||
|
||||
if (!R_SkinUsable(-1, v))
|
||||
if (!R_SkinUsable(-1, v, false))
|
||||
v = -1;
|
||||
|
||||
goto finish;
|
||||
|
|
@ -1966,13 +1966,13 @@ static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth)
|
|||
return;
|
||||
}
|
||||
|
||||
if (var == &cv_kartencore && !M_SecretUnlocked(SECRET_ENCORE))
|
||||
if (var == &cv_kartencore && !M_SecretUnlocked(SECRET_ENCORE, false))
|
||||
{
|
||||
CONS_Printf(M_GetText("You haven't unlocked Encore Mode yet!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (var == &cv_kartspeed && !M_SecretUnlocked(SECRET_HARDSPEED))
|
||||
if (var == &cv_kartspeed && !M_SecretUnlocked(SECRET_HARDSPEED, false))
|
||||
{
|
||||
if (!stricmp(value, "Hard") || atoi(value) >= KARTSPEED_HARD)
|
||||
{
|
||||
|
|
@ -1984,7 +1984,7 @@ static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth)
|
|||
if (var == &cv_forceskin)
|
||||
{
|
||||
INT32 skin = R_SkinAvailable(value);
|
||||
if ((stricmp(value, "None")) && ((skin == -1) || !R_SkinUsable(-1, skin)))
|
||||
if ((stricmp(value, "None")) && ((skin == -1) || !R_SkinUsable(-1, skin, false)))
|
||||
{
|
||||
CONS_Printf("Please provide a valid skin name (\"None\" disables).\n");
|
||||
return;
|
||||
|
|
@ -2109,7 +2109,7 @@ void CV_AddValue(consvar_t *var, INT32 increment)
|
|||
else if (newvalue >= numskins)
|
||||
newvalue = -1;
|
||||
} while ((oldvalue != newvalue)
|
||||
&& !(R_SkinUsable(-1, newvalue)));
|
||||
&& !(R_SkinUsable(-1, newvalue, false)));
|
||||
}
|
||||
else
|
||||
newvalue = var->value + increment;
|
||||
|
|
@ -2227,7 +2227,7 @@ void CV_AddValue(consvar_t *var, INT32 increment)
|
|||
|| var->PossibleValue == dummykartspeed_cons_t
|
||||
|| var->PossibleValue == gpdifficulty_cons_t)
|
||||
{
|
||||
if (!M_SecretUnlocked(SECRET_HARDSPEED))
|
||||
if (!M_SecretUnlocked(SECRET_HARDSPEED, false))
|
||||
{
|
||||
max = KARTSPEED_NORMAL+1;
|
||||
if (var->PossibleValue == kartspeed_cons_t)
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@
|
|||
#include "k_boss.h"
|
||||
#include "doomstat.h"
|
||||
#include "s_sound.h" // sfx_syfail
|
||||
#include "m_cond.h" // netUnlocked
|
||||
|
||||
// cl loading screen
|
||||
#include "v_video.h"
|
||||
|
|
@ -824,6 +825,8 @@ static boolean CL_SendJoin(void)
|
|||
for (; i < MAXSPLITSCREENPLAYERS; i++)
|
||||
strncpy(netbuffer->u.clientcfg.names[i], va("Player %c", 'A' + i), MAXPLAYERNAME);
|
||||
|
||||
memcpy(&netbuffer->u.clientcfg.availabilities, R_GetSkinAvailabilities(false), MAXAVAILABILITY*sizeof(UINT8));
|
||||
|
||||
return HSendPacket(servernode, false, 0, sizeof (clientconfig_pak));
|
||||
}
|
||||
|
||||
|
|
@ -3461,6 +3464,11 @@ void SV_ResetServer(void)
|
|||
|
||||
CV_RevertNetVars();
|
||||
|
||||
// Copy our unlocks to a place where net material can grab at/overwrite them safely.
|
||||
// (permits all unlocks in dedicated)
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
netUnlocked[i] = (dedicated || gamedata->unlocked[i]);
|
||||
|
||||
DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n");
|
||||
}
|
||||
|
||||
|
|
@ -3565,8 +3573,8 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
|
|||
return;
|
||||
}
|
||||
|
||||
node = (UINT8)READUINT8(*p);
|
||||
newplayernum = (UINT8)READUINT8(*p);
|
||||
node = READUINT8(*p);
|
||||
newplayernum = READUINT8(*p);
|
||||
|
||||
CONS_Debug(DBG_NETPLAY, "addplayer: %d %d\n", node, newplayernum);
|
||||
|
||||
|
|
@ -3587,8 +3595,13 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
|
|||
|
||||
READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME);
|
||||
|
||||
console = (UINT8)READUINT8(*p);
|
||||
splitscreenplayer = (UINT8)READUINT8(*p);
|
||||
console = READUINT8(*p);
|
||||
splitscreenplayer = READUINT8(*p);
|
||||
|
||||
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||
{
|
||||
newplayer->availabilities[i] = READUINT8(*p);
|
||||
}
|
||||
|
||||
// the server is creating my player
|
||||
if (node == mynode)
|
||||
|
|
@ -3690,7 +3703,7 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum)
|
|||
static void Got_AddBot(UINT8 **p, INT32 playernum)
|
||||
{
|
||||
INT16 newplayernum;
|
||||
UINT8 skinnum = 0;
|
||||
UINT8 i, skinnum = 0;
|
||||
UINT8 difficulty = DIFFICULTBOT;
|
||||
|
||||
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
|
||||
|
|
@ -3704,9 +3717,9 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
|||
return;
|
||||
}
|
||||
|
||||
newplayernum = (UINT8)READUINT8(*p);
|
||||
skinnum = (UINT8)READUINT8(*p);
|
||||
difficulty = (UINT8)READUINT8(*p);
|
||||
newplayernum = READUINT8(*p);
|
||||
skinnum = READUINT8(*p);
|
||||
difficulty = READUINT8(*p);
|
||||
|
||||
CONS_Debug(DBG_NETPLAY, "addbot: %d\n", newplayernum);
|
||||
|
||||
|
|
@ -3720,6 +3733,15 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
|||
|
||||
playernode[newplayernum] = servernode;
|
||||
|
||||
// todo find a way to have all auto unlocked for dedicated
|
||||
if (playeringame[0])
|
||||
{
|
||||
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||
{
|
||||
players[newplayernum].availabilities[i] = players[0].availabilities[i];
|
||||
}
|
||||
}
|
||||
|
||||
players[newplayernum].splitscreenindex = 0;
|
||||
players[newplayernum].bot = true;
|
||||
players[newplayernum].botvars.difficulty = difficulty;
|
||||
|
|
@ -3737,10 +3759,10 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
|||
LUA_HookInt(newplayernum, HOOK(PlayerJoin));
|
||||
}
|
||||
|
||||
static boolean SV_AddWaitingPlayers(SINT8 node, const char *name, const char *name2, const char *name3, const char *name4)
|
||||
static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities, const char *name, const char *name2, const char *name3, const char *name4)
|
||||
{
|
||||
INT32 n, newplayernum;
|
||||
UINT8 buf[4 + MAXPLAYERNAME];
|
||||
INT32 n, newplayernum, i;
|
||||
UINT8 buf[4 + MAXPLAYERNAME + MAXAVAILABILITY];
|
||||
UINT8 *buf_p = buf;
|
||||
boolean newplayer = false;
|
||||
|
||||
|
|
@ -3823,6 +3845,11 @@ static boolean SV_AddWaitingPlayers(SINT8 node, const char *name, const char *na
|
|||
WRITEUINT8(buf_p, nodetoplayer[node]); // consoleplayer
|
||||
WRITEUINT8(buf_p, playerpernode[node]); // splitscreen num
|
||||
|
||||
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||
{
|
||||
WRITEUINT8(buf_p, availabilities[i]);
|
||||
}
|
||||
|
||||
playerpernode[node]++;
|
||||
|
||||
SendNetXCmd(XD_ADDPLAYER, buf, buf_p - buf);
|
||||
|
|
@ -3886,9 +3913,10 @@ boolean SV_SpawnServer(void)
|
|||
// strictly speaking, i'm not convinced the following is necessary
|
||||
// but I'm not confident enough to remove it entirely in case it breaks something
|
||||
{
|
||||
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(false);
|
||||
SINT8 node = 0;
|
||||
for (; node < MAXNETNODES; node++)
|
||||
result |= SV_AddWaitingPlayers(node, cv_playername[0].zstring, cv_playername[1].zstring, cv_playername[2].zstring, cv_playername[3].zstring);
|
||||
result |= SV_AddWaitingPlayers(node, availabilitiesbuffer, cv_playername[0].zstring, cv_playername[1].zstring, cv_playername[2].zstring, cv_playername[3].zstring);
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
|
|
@ -3971,6 +3999,7 @@ static void HandleConnect(SINT8 node)
|
|||
{
|
||||
char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1];
|
||||
INT32 i;
|
||||
UINT8 availabilitiesbuffer[MAXAVAILABILITY];
|
||||
|
||||
// Sal: Dedicated mode is INCREDIBLY hacked together.
|
||||
// If a server filled out, then it'd overwrite the host and turn everyone into weird husks.....
|
||||
|
|
@ -4077,6 +4106,8 @@ static void HandleConnect(SINT8 node)
|
|||
}
|
||||
}
|
||||
|
||||
memcpy(availabilitiesbuffer, netbuffer->u.clientcfg.availabilities, sizeof(availabilitiesbuffer));
|
||||
|
||||
// client authorised to join
|
||||
nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]);
|
||||
if (!nodeingame[node])
|
||||
|
|
@ -4111,7 +4142,8 @@ static void HandleConnect(SINT8 node)
|
|||
SV_SendSaveGame(node, false); // send a complete game state
|
||||
DEBFILE("send savegame\n");
|
||||
}
|
||||
SV_AddWaitingPlayers(node, names[0], names[1], names[2], names[3]);
|
||||
|
||||
SV_AddWaitingPlayers(node, availabilitiesbuffer, names[0], names[1], names[2], names[3]);
|
||||
joindelay += cv_joindelay.value * TICRATE;
|
||||
player_joining = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -246,6 +246,7 @@ struct clientconfig_pak
|
|||
UINT8 localplayers; // number of splitscreen players
|
||||
UINT8 mode;
|
||||
char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME];
|
||||
UINT8 availabilities[MAXAVAILABILITY];
|
||||
} ATTRPACK;
|
||||
|
||||
#define SV_SPEEDMASK 0x03 // used to send kartspeed
|
||||
|
|
|
|||
14
src/d_main.c
14
src/d_main.c
|
|
@ -953,11 +953,6 @@ void D_StartTitle(void)
|
|||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
CL_ClearPlayer(i);
|
||||
|
||||
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
||||
{
|
||||
players[g_localplayers[i]].availabilities = R_GetSkinAvailabilities();
|
||||
}
|
||||
|
||||
splitscreen = 0;
|
||||
SplitScreen_OnChange();
|
||||
|
||||
|
|
@ -1151,6 +1146,10 @@ static void IdentifyVersion(void)
|
|||
#ifdef USE_PATCH_FILE
|
||||
D_AddFile(startupiwads, va(pandf,srb2waddir,PATCHNAME));
|
||||
#endif
|
||||
#define UNLOCKTESTING
|
||||
#if defined(DEVELOP) && defined(UNLOCKTESTING)
|
||||
D_AddFile(startupiwads, va(pandf,srb2waddir,"unlocks.pk3"));
|
||||
#endif
|
||||
////
|
||||
#undef TEXTURESNAME
|
||||
#undef MAPSNAME
|
||||
|
|
@ -1385,6 +1384,8 @@ void D_SRB2Main(void)
|
|||
Z_Init();
|
||||
CON_SetLoadingProgress(LOADED_ZINIT);
|
||||
|
||||
M_NewGameDataStruct();
|
||||
|
||||
// Do this up here so that WADs loaded through the command line can use ExecCfg
|
||||
COM_Init();
|
||||
|
||||
|
|
@ -1456,6 +1457,9 @@ void D_SRB2Main(void)
|
|||
#ifdef USE_PATCH_FILE
|
||||
mainwads++; // scripts.pk3
|
||||
#endif
|
||||
#ifdef UNLOCKTESTING
|
||||
mainwads++; // unlocks.pk3
|
||||
#endif
|
||||
|
||||
#endif //ifndef DEVELOP
|
||||
|
||||
|
|
|
|||
116
src/d_netcmd.c
116
src/d_netcmd.c
|
|
@ -383,12 +383,14 @@ consvar_t cv_items[NUMKARTRESULTS-1] = {
|
|||
CVAR_INIT ("kitchensink", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("droptarget", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("gardentop", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("gachabom", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("dualsneaker", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("triplesneaker", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("triplebanana", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("tripleorbinaut", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("quadorbinaut", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("dualjawz", "On", CV_NETVAR, CV_OnOff, NULL)
|
||||
CVAR_INIT ("dualjawz", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("triplegachabom", "On", CV_NETVAR, CV_OnOff, NULL)
|
||||
};
|
||||
|
||||
consvar_t cv_kartspeed = CVAR_INIT ("gamespeed", "Auto", CV_NETVAR|CV_CALL|CV_NOINIT, kartspeed_cons_t, KartSpeed_OnChange);
|
||||
|
|
@ -467,6 +469,7 @@ consvar_t cv_autobalance = CVAR_INIT ("autobalance", "Off", CV_SAVE|CV_NETVAR|CV
|
|||
consvar_t cv_teamscramble = CVAR_INIT ("teamscramble", "Off", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, teamscramble_cons_t, TeamScramble_OnChange);
|
||||
consvar_t cv_scrambleonchange = CVAR_INIT ("scrambleonchange", "Off", CV_SAVE|CV_NETVAR, teamscramble_cons_t, NULL);
|
||||
|
||||
consvar_t cv_alttitle = CVAR_INIT ("alttitle", "Off", CV_CALL|CV_NOSHOWHELP|CV_NOINIT|CV_SAVE, CV_OnOff, AltTitle_OnChange);
|
||||
consvar_t cv_itemfinder = CVAR_INIT ("itemfinder", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, ItemFinder_OnChange);
|
||||
|
||||
// Scoring type options
|
||||
|
|
@ -939,6 +942,7 @@ void D_RegisterClientCommands(void)
|
|||
CV_RegisterVar(&cv_mindelay);
|
||||
|
||||
// HUD
|
||||
CV_RegisterVar(&cv_alttitle);
|
||||
CV_RegisterVar(&cv_itemfinder);
|
||||
CV_RegisterVar(&cv_showinputjoy);
|
||||
|
||||
|
|
@ -1408,26 +1412,29 @@ boolean CanChangeSkinWhilePlaying(INT32 playernum)
|
|||
|
||||
static void ForceAllSkins(INT32 forcedskin)
|
||||
{
|
||||
INT32 i, j;
|
||||
INT32 i;
|
||||
|
||||
if (demo.playback)
|
||||
return; // DXD_SKIN should handle all changes for us
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
|
||||
SetPlayerSkinByNum(i, forcedskin);
|
||||
}
|
||||
|
||||
// If it's me (or my brother (or my sister (or my trusty pet dog))), set appropriate skin value in cv_skin
|
||||
if (dedicated) // But don't do this for dedicated servers, of course.
|
||||
if (dedicated)
|
||||
return;
|
||||
|
||||
// If it's me (or my brother (or my sister (or my trusty pet dog))), set appropriate skin value in cv_skin
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
if (!playeringame[g_localplayers[i]])
|
||||
continue;
|
||||
|
||||
for (j = 0; j <= splitscreen; j++)
|
||||
{
|
||||
if (i == g_localplayers[j])
|
||||
{
|
||||
CV_StealthSet(&cv_skin[j], skins[forcedskin].name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
CV_StealthSet(&cv_skin[i], skins[forcedskin].name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1478,6 +1485,8 @@ static void SendNameAndColor(UINT8 n)
|
|||
char buf[MAXPLAYERNAME+12];
|
||||
char *p;
|
||||
|
||||
UINT16 i = 0;
|
||||
|
||||
if (splitscreen < n)
|
||||
return; // can happen if skin4/color4/name4 changed
|
||||
|
||||
|
|
@ -1495,9 +1504,10 @@ static void SendNameAndColor(UINT8 n)
|
|||
CV_StealthSetValue(&cv_playercolor[n], skins[player->skin].prefcolor);
|
||||
else if (skincolors[atoi(cv_playercolor[n].defaultvalue)].accessible)
|
||||
CV_StealthSet(&cv_playercolor[n], cv_playercolor[n].defaultvalue);
|
||||
else {
|
||||
UINT16 i = 0;
|
||||
while (i<numskincolors && !skincolors[i].accessible) i++;
|
||||
else
|
||||
{
|
||||
while (i < numskincolors && !skincolors[i].accessible)
|
||||
i++;
|
||||
CV_StealthSetValue(&cv_playercolor[n], (i != numskincolors) ? i : SKINCOLOR_BLUE);
|
||||
}
|
||||
}
|
||||
|
|
@ -1514,8 +1524,6 @@ static void SendNameAndColor(UINT8 n)
|
|||
&& cv_followercolor[n].value == player->followercolor)
|
||||
return;
|
||||
|
||||
player->availabilities = R_GetSkinAvailabilities();
|
||||
|
||||
// We'll handle it later if we're not playing.
|
||||
if (!Playing())
|
||||
return;
|
||||
|
|
@ -1532,7 +1540,8 @@ static void SendNameAndColor(UINT8 n)
|
|||
|
||||
K_KartResetPlayerColor(player);
|
||||
|
||||
if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin))
|
||||
foundskin = R_SkinAvailable(cv_skin[n].string);
|
||||
if (foundskin != -1 && R_SkinUsable(playernum, foundskin, false))
|
||||
{
|
||||
SetPlayerSkin(playernum, cv_skin[n].string);
|
||||
CV_StealthSet(&cv_skin[n], skins[foundskin].name);
|
||||
|
|
@ -1550,6 +1559,8 @@ static void SendNameAndColor(UINT8 n)
|
|||
|
||||
// Update follower for local games:
|
||||
foundskin = K_FollowerAvailable(cv_follower[n].string);
|
||||
if (!K_FollowerUsable(foundskin))
|
||||
foundskin = -1;
|
||||
CV_StealthSet(&cv_follower[n], (foundskin == -1) ? "None" : followers[foundskin].name);
|
||||
cv_follower[n].value = foundskin;
|
||||
K_SetFollowerByNum(playernum, foundskin);
|
||||
|
|
@ -1577,14 +1588,14 @@ static void SendNameAndColor(UINT8 n)
|
|||
// check if player has the skin loaded (cv_skin may have
|
||||
// the name of a skin that was available in the previous game)
|
||||
cv_skin[n].value = R_SkinAvailable(cv_skin[n].string);
|
||||
if ((cv_skin[n].value < 0) || !R_SkinUsable(playernum, cv_skin[n].value))
|
||||
if ((cv_skin[n].value < 0) || !R_SkinUsable(playernum, cv_skin[n].value, false))
|
||||
{
|
||||
CV_StealthSet(&cv_skin[n], DEFAULTSKIN);
|
||||
cv_skin[n].value = 0;
|
||||
}
|
||||
|
||||
cv_follower[n].value = K_FollowerAvailable(cv_follower[n].string);
|
||||
if (cv_follower[n].value < 0)
|
||||
if (cv_follower[n].value < 0 || !K_FollowerUsable(cv_follower[n].value))
|
||||
{
|
||||
CV_StealthSet(&cv_follower[n], "None");
|
||||
cv_follower[n].value = -1;
|
||||
|
|
@ -1592,7 +1603,6 @@ static void SendNameAndColor(UINT8 n)
|
|||
|
||||
// Finally write out the complete packet and send it off.
|
||||
WRITESTRINGN(p, cv_playername[n].zstring, MAXPLAYERNAME);
|
||||
WRITEUINT32(p, (UINT32)player->availabilities);
|
||||
WRITEUINT16(p, (UINT16)cv_playercolor[n].value);
|
||||
WRITEUINT8(p, (UINT8)cv_skin[n].value);
|
||||
WRITEINT16(p, (INT16)cv_follower[n].value);
|
||||
|
|
@ -1634,11 +1644,9 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
|
|||
}
|
||||
|
||||
READSTRINGN(*cp, name, MAXPLAYERNAME);
|
||||
p->availabilities = READUINT32(*cp);
|
||||
color = READUINT16(*cp);
|
||||
skin = READUINT8(*cp);
|
||||
follower = READINT16(*cp);
|
||||
//CONS_Printf("Recieved follower id %d\n", follower);
|
||||
followercolor = READUINT16(*cp);
|
||||
|
||||
// set name
|
||||
|
|
@ -1659,56 +1667,20 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
|
|||
{
|
||||
boolean kick = false;
|
||||
|
||||
// team colors
|
||||
if (G_GametypeHasTeams())
|
||||
{
|
||||
if (p->ctfteam == 1 && p->skincolor != skincolor_redteam)
|
||||
kick = true;
|
||||
else if (p->ctfteam == 2 && p->skincolor != skincolor_blueteam)
|
||||
kick = true;
|
||||
}
|
||||
|
||||
// don't allow inaccessible colors
|
||||
if (skincolors[p->skincolor].accessible == false)
|
||||
kick = true;
|
||||
|
||||
// availabilities
|
||||
for (i = 0; i < MAXSKINS; i++)
|
||||
{
|
||||
UINT32 playerhasunlocked = (p->availabilities & (1 << i));
|
||||
boolean islocked = false;
|
||||
UINT8 j;
|
||||
|
||||
for (j = 0; j < MAXUNLOCKABLES; j++)
|
||||
{
|
||||
if (unlockables[j].type != SECRET_SKIN)
|
||||
continue;
|
||||
|
||||
if (unlockables[j].variable == i)
|
||||
{
|
||||
islocked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (islocked == false && playerhasunlocked == true)
|
||||
{
|
||||
// hacked client that enabled every bit
|
||||
kick = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (kick)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Illegal color change received from %s (team: %d), color: %d)\n"), player_names[playernum], p->ctfteam, p->skincolor);
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Illegal color change received from %s, color: %d)\n"), player_names[playernum], p->skincolor);
|
||||
SendKick(playernum, KICK_MSG_CON_FAIL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// set skin
|
||||
if (cv_forceskin.value >= 0 && (netgame || multiplayer)) // Server wants everyone to use the same player
|
||||
if (cv_forceskin.value >= 0 && K_CanChangeRules(true)) // Server wants everyone to use the same player
|
||||
{
|
||||
const INT32 forcedskin = cv_forceskin.value;
|
||||
SetPlayerSkinByNum(playernum, forcedskin);
|
||||
|
|
@ -2890,7 +2862,7 @@ static void Command_Map_f(void)
|
|||
{
|
||||
newencoremode = !newencoremode;
|
||||
|
||||
if (!M_SecretUnlocked(SECRET_ENCORE) && newencoremode == true && !usingcheats)
|
||||
if (!M_SecretUnlocked(SECRET_ENCORE, false) && newencoremode == true && !usingcheats)
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
|
||||
return;
|
||||
|
|
@ -4877,12 +4849,25 @@ FUNCNORETURN static ATTRNORETURN void Command_Quit_f(void)
|
|||
I_Quit();
|
||||
}
|
||||
|
||||
void AltTitle_OnChange(void)
|
||||
{
|
||||
if (!cv_alttitle.value)
|
||||
return; // it's fine.
|
||||
|
||||
if (!M_SecretUnlocked(SECRET_ALTTITLE, true))
|
||||
{
|
||||
CONS_Printf(M_GetText("You haven't earned this yet.\n"));
|
||||
CV_StealthSetValue(&cv_itemfinder, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ItemFinder_OnChange(void)
|
||||
{
|
||||
if (!cv_itemfinder.value)
|
||||
return; // it's fine.
|
||||
|
||||
if (!M_SecretUnlocked(SECRET_ITEMFINDER))
|
||||
if (!M_SecretUnlocked(SECRET_ITEMFINDER, true))
|
||||
{
|
||||
CONS_Printf(M_GetText("You haven't earned this yet.\n"));
|
||||
CV_StealthSetValue(&cv_itemfinder, 0);
|
||||
|
|
@ -5732,11 +5717,6 @@ void Command_ExitGame_f(void)
|
|||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
CL_ClearPlayer(i);
|
||||
|
||||
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
||||
{
|
||||
players[g_localplayers[i]].availabilities = R_GetSkinAvailabilities();
|
||||
}
|
||||
|
||||
splitscreen = 0;
|
||||
SplitScreen_OnChange();
|
||||
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ extern consvar_t cv_kartdebugnodes, cv_kartdebugcolorize, cv_kartdebugdirector;
|
|||
extern consvar_t cv_spbtest, cv_gptest, cv_reducevfx;
|
||||
extern consvar_t cv_kartdebugwaypoints, cv_kartdebugbotpredict;
|
||||
|
||||
extern consvar_t cv_itemfinder;
|
||||
extern consvar_t cv_alttitle, cv_itemfinder;
|
||||
|
||||
extern consvar_t cv_inttime, cv_advancemap;
|
||||
extern consvar_t cv_overtime;
|
||||
|
|
@ -238,6 +238,7 @@ boolean IsPlayerAdmin(INT32 playernum);
|
|||
void SetAdminPlayer(INT32 playernum);
|
||||
void ClearAdminPlayers(void);
|
||||
void RemoveAdminPlayer(INT32 playernum);
|
||||
void AltTitle_OnChange(void);
|
||||
void ItemFinder_OnChange(void);
|
||||
void D_SetPassword(const char *pw);
|
||||
|
||||
|
|
|
|||
|
|
@ -154,7 +154,8 @@ Run this macro, then #undef FOREACH afterward
|
|||
FOREACH (SUPERRING, 19),\
|
||||
FOREACH (KITCHENSINK, 20),\
|
||||
FOREACH (DROPTARGET, 21),\
|
||||
FOREACH (GARDENTOP, 22)
|
||||
FOREACH (GARDENTOP, 22),\
|
||||
FOREACH (GACHABOM, 23)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
|
@ -171,6 +172,7 @@ typedef enum
|
|||
KRITEM_TRIPLEORBINAUT,
|
||||
KRITEM_QUADORBINAUT,
|
||||
KRITEM_DUALJAWZ,
|
||||
KRITEM_TRIPLEGACHABOM,
|
||||
|
||||
NUMKARTRESULTS
|
||||
} kartitems_t;
|
||||
|
|
@ -421,7 +423,7 @@ struct player_t
|
|||
UINT16 skincolor;
|
||||
|
||||
INT32 skin;
|
||||
UINT32 availabilities;
|
||||
UINT8 availabilities[MAXAVAILABILITY];
|
||||
|
||||
UINT8 fakeskin; // ironman
|
||||
UINT8 lastfakeskin;
|
||||
|
|
@ -464,6 +466,7 @@ struct player_t
|
|||
UINT16 spinouttimer; // Spin-out from a banana peel or oil slick (was "pw_bananacam")
|
||||
UINT8 spinouttype; // Determines the mode of spinout/wipeout, see kartspinoutflags_t
|
||||
UINT8 instashield; // Instashield no-damage animation timer
|
||||
INT32 invulnhitlag; // Numbers of tics of hitlag added this tic for "potential" damage -- not real damage
|
||||
UINT8 wipeoutslow; // Timer before you slowdown when getting wiped out
|
||||
UINT8 justbumped; // Prevent players from endlessly bumping into each other
|
||||
UINT8 tumbleBounces;
|
||||
|
|
@ -613,7 +616,12 @@ struct player_t
|
|||
|
||||
INT16 lastsidehit, lastlinehit;
|
||||
|
||||
//UINT8 timeshit; // That's TIMES HIT, not TIME SHIT, you doofus! -- in memoriam
|
||||
// These track how many things tried to damage you, not
|
||||
// whether you actually took damage.
|
||||
UINT8 timeshit; // times hit this tic
|
||||
UINT8 timeshitprev; // times hit before
|
||||
// That's TIMES HIT, not TIME SHIT, you doofus! -- in memoriam
|
||||
// No longer in memoriam =P -jart
|
||||
|
||||
INT32 onconveyor; // You are on a conveyor belt if nonzero
|
||||
|
||||
|
|
|
|||
338
src/deh_soc.c
338
src/deh_soc.c
|
|
@ -131,11 +131,44 @@ static float searchfvalue(const char *s)
|
|||
#endif
|
||||
|
||||
// These are for clearing all of various things
|
||||
void clear_emblems(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < MAXEMBLEMS; ++i)
|
||||
{
|
||||
Z_Free(emblemlocations[i].level);
|
||||
emblemlocations[i].level = NULL;
|
||||
|
||||
Z_Free(emblemlocations[i].stringVar);
|
||||
emblemlocations[i].stringVar = NULL;
|
||||
}
|
||||
|
||||
memset(&emblemlocations, 0, sizeof(emblemlocations));
|
||||
numemblems = 0;
|
||||
}
|
||||
|
||||
void clear_unlockables(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < MAXUNLOCKABLES; ++i)
|
||||
{
|
||||
Z_Free(unlockables[i].stringVar);
|
||||
unlockables[i].stringVar = NULL;
|
||||
|
||||
Z_Free(unlockables[i].icon);
|
||||
unlockables[i].icon = NULL;
|
||||
}
|
||||
|
||||
memset(&unlockables, 0, sizeof(unlockables));
|
||||
}
|
||||
|
||||
void clear_conditionsets(void)
|
||||
{
|
||||
UINT8 i;
|
||||
for (i = 0; i < MAXCONDITIONSETS; ++i)
|
||||
M_ClearConditionSet(i+1);
|
||||
M_ClearConditionSet(i);
|
||||
}
|
||||
|
||||
void clear_levels(void)
|
||||
|
|
@ -1148,13 +1181,6 @@ void readlevelheader(MYFILE *f, char * name)
|
|||
mapheaderinfo[num]->encorepal = (UINT16)i;
|
||||
else if (fastcmp(word, "NUMLAPS"))
|
||||
mapheaderinfo[num]->numlaps = (UINT8)i;
|
||||
else if (fastcmp(word, "UNLOCKABLE"))
|
||||
{
|
||||
if (i >= 0 && i <= MAXUNLOCKABLES) // 0 for no unlock required, anything else requires something
|
||||
mapheaderinfo[num]->unlockrequired = (SINT8)i - 1;
|
||||
else
|
||||
deh_warning("Level header %d: invalid unlockable number %d", num, i);
|
||||
}
|
||||
else if (fastcmp(word, "SKYBOXSCALE"))
|
||||
mapheaderinfo[num]->skybox_scalex = mapheaderinfo[num]->skybox_scaley = mapheaderinfo[num]->skybox_scalez = (INT16)i;
|
||||
else if (fastcmp(word, "SKYBOXSCALEX"))
|
||||
|
|
@ -2098,14 +2124,6 @@ void reademblemdata(MYFILE *f, INT32 num)
|
|||
word2 = tmp += 2;
|
||||
value = atoi(word2); // used for numerical settings
|
||||
|
||||
// Up here to allow lowercase in hints
|
||||
if (fastcmp(word, "HINT"))
|
||||
{
|
||||
while ((tmp = strchr(word2, '\\')))
|
||||
*tmp = '\n';
|
||||
deh_strlcpy(emblemlocations[num-1].hint, word2, sizeof (emblemlocations[num-1].hint), va("Emblem %d: hint", num));
|
||||
continue;
|
||||
}
|
||||
strupr(word2);
|
||||
|
||||
if (fastcmp(word, "TYPE"))
|
||||
|
|
@ -2124,6 +2142,7 @@ void reademblemdata(MYFILE *f, INT32 num)
|
|||
else if (fastcmp(word, "MAPNAME"))
|
||||
{
|
||||
emblemlocations[num-1].level = Z_StrDup(word2);
|
||||
emblemlocations[num-1].levelCache = NEXTMAP_INVALID;
|
||||
}
|
||||
else if (fastcmp(word, "SPRITE"))
|
||||
{
|
||||
|
|
@ -2140,7 +2159,14 @@ void reademblemdata(MYFILE *f, INT32 num)
|
|||
else if (fastcmp(word, "COLOR"))
|
||||
emblemlocations[num-1].color = get_number(word2);
|
||||
else if (fastcmp(word, "VAR"))
|
||||
{
|
||||
Z_Free(emblemlocations[num-1].stringVar);
|
||||
emblemlocations[num-1].stringVar = Z_StrDup(word2);
|
||||
|
||||
emblemlocations[num-1].var = get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "FLAGS"))
|
||||
emblemlocations[num-1].flags = get_number(word2);
|
||||
else
|
||||
deh_warning("Emblem %d: unknown word '%s'", num, word);
|
||||
}
|
||||
|
|
@ -2165,88 +2191,6 @@ void reademblemdata(MYFILE *f, INT32 num)
|
|||
Z_Free(s);
|
||||
}
|
||||
|
||||
void readextraemblemdata(MYFILE *f, INT32 num)
|
||||
{
|
||||
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
||||
char *word = s;
|
||||
char *word2;
|
||||
char *tmp;
|
||||
INT32 value;
|
||||
|
||||
memset(&extraemblems[num-1], 0, sizeof(extraemblem_t));
|
||||
|
||||
do
|
||||
{
|
||||
if (myfgets(s, MAXLINELEN, f))
|
||||
{
|
||||
if (s[0] == '\n')
|
||||
break;
|
||||
|
||||
// First remove trailing newline, if there is one
|
||||
tmp = strchr(s, '\n');
|
||||
if (tmp)
|
||||
*tmp = '\0';
|
||||
|
||||
tmp = strchr(s, '#');
|
||||
if (tmp)
|
||||
*tmp = '\0';
|
||||
if (s == tmp)
|
||||
continue; // Skip comment lines, but don't break.
|
||||
|
||||
// Get the part before the " = "
|
||||
tmp = strchr(s, '=');
|
||||
if (tmp)
|
||||
*(tmp-1) = '\0';
|
||||
else
|
||||
break;
|
||||
strupr(word);
|
||||
|
||||
// Now get the part after
|
||||
word2 = tmp += 2;
|
||||
|
||||
value = atoi(word2); // used for numerical settings
|
||||
|
||||
if (fastcmp(word, "NAME"))
|
||||
deh_strlcpy(extraemblems[num-1].name, word2,
|
||||
sizeof (extraemblems[num-1].name), va("Extra emblem %d: name", num));
|
||||
else if (fastcmp(word, "OBJECTIVE"))
|
||||
deh_strlcpy(extraemblems[num-1].description, word2,
|
||||
sizeof (extraemblems[num-1].description), va("Extra emblem %d: objective", num));
|
||||
else if (fastcmp(word, "CONDITIONSET"))
|
||||
extraemblems[num-1].conditionset = (UINT8)value;
|
||||
else if (fastcmp(word, "SHOWCONDITIONSET"))
|
||||
extraemblems[num-1].showconditionset = (UINT8)value;
|
||||
else
|
||||
{
|
||||
strupr(word2);
|
||||
if (fastcmp(word, "SPRITE"))
|
||||
{
|
||||
if (word2[0] >= 'A' && word2[0] <= 'Z')
|
||||
value = word2[0];
|
||||
else
|
||||
value += 'A'-1;
|
||||
|
||||
if (value < 'A' || value > 'Z')
|
||||
deh_warning("Emblem %d: sprite must be from A - Z (1 - 26)", num);
|
||||
else
|
||||
extraemblems[num-1].sprite = (UINT8)value;
|
||||
}
|
||||
else if (fastcmp(word, "COLOR"))
|
||||
extraemblems[num-1].color = get_number(word2);
|
||||
else
|
||||
deh_warning("Extra emblem %d: unknown word '%s'", num, word);
|
||||
}
|
||||
}
|
||||
} while (!myfeof(f));
|
||||
|
||||
if (!extraemblems[num-1].sprite)
|
||||
extraemblems[num-1].sprite = 'C';
|
||||
if (!extraemblems[num-1].color)
|
||||
extraemblems[num-1].color = SKINCOLOR_RED;
|
||||
|
||||
Z_Free(s);
|
||||
}
|
||||
|
||||
void readunlockable(MYFILE *f, INT32 num)
|
||||
{
|
||||
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
||||
|
|
@ -2256,7 +2200,6 @@ void readunlockable(MYFILE *f, INT32 num)
|
|||
INT32 i;
|
||||
|
||||
memset(&unlockables[num], 0, sizeof(unlockable_t));
|
||||
unlockables[num].objective[0] = '/';
|
||||
|
||||
do
|
||||
{
|
||||
|
|
@ -2291,62 +2234,65 @@ void readunlockable(MYFILE *f, INT32 num)
|
|||
|
||||
if (fastcmp(word, "NAME"))
|
||||
deh_strlcpy(unlockables[num].name, word2,
|
||||
sizeof (unlockables[num].name), va("Unlockable %d: name", num));
|
||||
else if (fastcmp(word, "OBJECTIVE"))
|
||||
deh_strlcpy(unlockables[num].objective, word2,
|
||||
sizeof (unlockables[num].objective), va("Unlockable %d: objective", num));
|
||||
sizeof (unlockables[num].name), va("Unlockable %d: name", num+1));
|
||||
else
|
||||
{
|
||||
strupr(word2);
|
||||
|
||||
if (fastcmp(word, "CONDITIONSET"))
|
||||
unlockables[num].conditionset = (UINT8)i;
|
||||
else if (fastcmp(word, "SHOWCONDITIONSET"))
|
||||
unlockables[num].showconditionset = (UINT8)i;
|
||||
else if (fastcmp(word, "NOCECHO"))
|
||||
unlockables[num].nocecho = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
|
||||
else if (fastcmp(word, "NOCHECKLIST"))
|
||||
unlockables[num].nochecklist = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
|
||||
else if (fastcmp(word, "MAJORUNLOCK"))
|
||||
unlockables[num].majorunlock = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
|
||||
else if (fastcmp(word, "TYPE"))
|
||||
{
|
||||
if (fastcmp(word2, "NONE"))
|
||||
unlockables[num].type = SECRET_NONE;
|
||||
else if (fastcmp(word2, "HEADER"))
|
||||
unlockables[num].type = SECRET_HEADER;
|
||||
else if (fastcmp(word2, "EXTRAMEDAL"))
|
||||
unlockables[num].type = SECRET_EXTRAMEDAL;
|
||||
else if (fastcmp(word2, "CUP"))
|
||||
unlockables[num].type = SECRET_CUP;
|
||||
else if (fastcmp(word2, "MAP"))
|
||||
unlockables[num].type = SECRET_MAP;
|
||||
else if (fastcmp(word2, "SKIN"))
|
||||
unlockables[num].type = SECRET_SKIN;
|
||||
else if (fastcmp(word2, "WARP"))
|
||||
unlockables[num].type = SECRET_WARP;
|
||||
else if (fastcmp(word2, "LEVELSELECT"))
|
||||
unlockables[num].type = SECRET_LEVELSELECT;
|
||||
else if (fastcmp(word2, "FOLLOWER"))
|
||||
unlockables[num].type = SECRET_FOLLOWER;
|
||||
else if (fastcmp(word2, "HARDSPEED"))
|
||||
unlockables[num].type = SECRET_HARDSPEED;
|
||||
else if (fastcmp(word2, "ENCORE"))
|
||||
unlockables[num].type = SECRET_ENCORE;
|
||||
else if (fastcmp(word2, "LEGACYBOXRUMMAGE"))
|
||||
unlockables[num].type = SECRET_LEGACYBOXRUMMAGE;
|
||||
else if (fastcmp(word2, "TIMEATTACK"))
|
||||
unlockables[num].type = SECRET_TIMEATTACK;
|
||||
else if (fastcmp(word2, "BREAKTHECAPSULES"))
|
||||
unlockables[num].type = SECRET_BREAKTHECAPSULES;
|
||||
else if (fastcmp(word2, "SOUNDTEST"))
|
||||
unlockables[num].type = SECRET_SOUNDTEST;
|
||||
else if (fastcmp(word2, "CREDITS"))
|
||||
unlockables[num].type = SECRET_CREDITS;
|
||||
else if (fastcmp(word2, "ALTTITLE"))
|
||||
unlockables[num].type = SECRET_ALTTITLE;
|
||||
else if (fastcmp(word2, "ITEMFINDER"))
|
||||
unlockables[num].type = SECRET_ITEMFINDER;
|
||||
else if (fastcmp(word2, "EMBLEMHINTS"))
|
||||
unlockables[num].type = SECRET_EMBLEMHINTS;
|
||||
else if (fastcmp(word2, "ENCORE"))
|
||||
unlockables[num].type = SECRET_ENCORE;
|
||||
else if (fastcmp(word2, "HARDSPEED"))
|
||||
unlockables[num].type = SECRET_HARDSPEED;
|
||||
else if (fastcmp(word2, "HELLATTACK"))
|
||||
unlockables[num].type = SECRET_HELLATTACK;
|
||||
else if (fastcmp(word2, "PANDORA"))
|
||||
unlockables[num].type = SECRET_PANDORA;
|
||||
else
|
||||
unlockables[num].type = (INT16)i;
|
||||
unlockables[num].stringVarCache = -1;
|
||||
}
|
||||
else if (fastcmp(word, "VAR"))
|
||||
{
|
||||
// TODO: different field for level name string
|
||||
Z_Free(unlockables[num].stringVar);
|
||||
unlockables[num].stringVar = Z_StrDup(word2);
|
||||
unlockables[num].stringVarCache = -1;
|
||||
unlockables[num].variable = (INT16)i;
|
||||
}
|
||||
else if (fastcmp(word, "ICON"))
|
||||
{
|
||||
Z_Free(unlockables[num].icon);
|
||||
unlockables[num].icon = Z_StrDup(word2);
|
||||
}
|
||||
else if (fastcmp(word, "COLOR"))
|
||||
{
|
||||
unlockables[num].color = get_number(word2);
|
||||
}
|
||||
else
|
||||
deh_warning("Unlockable %d: unknown word '%s'", num+1, word);
|
||||
}
|
||||
|
|
@ -2383,7 +2329,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
|
|||
|
||||
if (!params[0])
|
||||
{
|
||||
deh_warning("condition line is empty for condition ID %d", id);
|
||||
deh_warning("condition line is empty for condition ID %d", id+1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2401,9 +2347,15 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
|
|||
re = atoi(params[1]);
|
||||
x1 = atoi(params[2]);
|
||||
|
||||
if (re < PWRLVRECORD_MIN || re > PWRLVRECORD_MAX)
|
||||
{
|
||||
deh_warning("Power level requirement %d out of range (%d - %d) for condition ID %d", re, PWRLVRECORD_MIN, PWRLVRECORD_MAX, id+1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (x1 < 0 || x1 >= PWRLV_NUMTYPES)
|
||||
{
|
||||
deh_warning("Power level type %d out of range (0 - %d) for condition ID %d", x1, PWRLV_NUMTYPES-1, id);
|
||||
deh_warning("Power level type %d out of range (0 - %d) for condition ID %d", x1, PWRLV_NUMTYPES-1, id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -2427,7 +2379,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
|
|||
|
||||
if (re >= nummapheaders)
|
||||
{
|
||||
deh_warning("Invalid level %s for condition ID %d", params[1], id);
|
||||
deh_warning("Invalid level %s for condition ID %d", params[1], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -2440,7 +2392,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
|
|||
|
||||
if (x1 >= nummapheaders)
|
||||
{
|
||||
deh_warning("Invalid level %s for condition ID %d", params[1], id);
|
||||
deh_warning("Invalid level %s for condition ID %d", params[1], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -2453,14 +2405,14 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
|
|||
// constrained by 32 bits
|
||||
if (re < 0 || re > 31)
|
||||
{
|
||||
deh_warning("Trigger ID %d out of range (0 - 31) for condition ID %d", re, id);
|
||||
deh_warning("Trigger ID %d out of range (0 - 31) for condition ID %d", re, id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "TOTALEMBLEMS"))
|
||||
else if (fastcmp(params[0], "TOTALMEDALS"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_TOTALEMBLEMS;
|
||||
ty = UC_TOTALMEDALS;
|
||||
re = atoi(params[1]);
|
||||
}
|
||||
else if (fastcmp(params[0], "EMBLEM"))
|
||||
|
|
@ -2471,19 +2423,19 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
|
|||
|
||||
if (re <= 0 || re > MAXEMBLEMS)
|
||||
{
|
||||
deh_warning("Emblem %d out of range (1 - %d) for condition ID %d", re, MAXEMBLEMS, id);
|
||||
deh_warning("Emblem %d out of range (1 - %d) for condition ID %d", re, MAXEMBLEMS, id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "EXTRAEMBLEM"))
|
||||
else if (fastcmp(params[0], "UNLOCKABLE"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_EXTRAEMBLEM;
|
||||
ty = UC_UNLOCKABLE;
|
||||
re = atoi(params[1]);
|
||||
|
||||
if (re <= 0 || re > MAXEXTRAEMBLEMS)
|
||||
if (re <= 0 || re > MAXUNLOCKABLES)
|
||||
{
|
||||
deh_warning("Extra emblem %d out of range (1 - %d) for condition ID %d", re, MAXEXTRAEMBLEMS, id);
|
||||
deh_warning("Unlockable %d out of range (1 - %d) for condition ID %d", re, MAXUNLOCKABLES, id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -2495,13 +2447,13 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
|
|||
|
||||
if (re <= 0 || re > MAXCONDITIONSETS)
|
||||
{
|
||||
deh_warning("Condition set %d out of range (1 - %d) for condition ID %d", re, MAXCONDITIONSETS, id);
|
||||
deh_warning("Condition set %d out of range (1 - %d) for condition ID %d", re, MAXCONDITIONSETS, id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
deh_warning("Invalid condition name %s for condition ID %d", params[0], id);
|
||||
deh_warning("Invalid condition name %s for condition ID %d", params[0], id+1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2554,13 +2506,13 @@ void readconditionset(MYFILE *f, UINT8 setnum)
|
|||
id = (UINT8)atoi(word + 9);
|
||||
if (id == 0)
|
||||
{
|
||||
deh_warning("Condition set %d: unknown word '%s'", setnum, word);
|
||||
deh_warning("Condition set %d: unknown word '%s'", setnum+1, word);
|
||||
continue;
|
||||
}
|
||||
else if (previd > id)
|
||||
{
|
||||
// out of order conditions can cause problems, so enforce proper order
|
||||
deh_warning("Condition set %d: conditions are out of order, ignoring this line", setnum);
|
||||
deh_warning("Condition set %d: conditions are out of order, ignoring this line", setnum+1);
|
||||
continue;
|
||||
}
|
||||
previd = id;
|
||||
|
|
@ -2575,13 +2527,14 @@ void readconditionset(MYFILE *f, UINT8 setnum)
|
|||
Z_Free(s);
|
||||
}
|
||||
|
||||
void readmaincfg(MYFILE *f)
|
||||
void readmaincfg(MYFILE *f, boolean mainfile)
|
||||
{
|
||||
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
||||
char *word = s;
|
||||
char *word2;
|
||||
char *tmp;
|
||||
INT32 value;
|
||||
boolean doClearLevels = false;
|
||||
|
||||
do
|
||||
{
|
||||
|
|
@ -2615,7 +2568,54 @@ void readmaincfg(MYFILE *f)
|
|||
|
||||
value = atoi(word2); // used for numerical settings
|
||||
|
||||
if (fastcmp(word, "EXECCFG"))
|
||||
if (fastcmp(word, "GAMEDATA"))
|
||||
{
|
||||
size_t filenamelen;
|
||||
|
||||
// Check the data filename so that mods
|
||||
// can't write arbitrary files.
|
||||
if (!GoodDataFileName(word2))
|
||||
I_Error("Maincfg: bad data file name '%s'\n", word2);
|
||||
|
||||
G_SaveGameData();
|
||||
strlcpy(gamedatafilename, word2, sizeof (gamedatafilename));
|
||||
strlwr(gamedatafilename);
|
||||
savemoddata = true;
|
||||
majormods = false;
|
||||
|
||||
// Also save a time attack folder
|
||||
filenamelen = strlen(gamedatafilename)-4; // Strip off the extension
|
||||
filenamelen = min(filenamelen, sizeof (timeattackfolder));
|
||||
strncpy(timeattackfolder, gamedatafilename, filenamelen);
|
||||
timeattackfolder[min(filenamelen, sizeof (timeattackfolder) - 1)] = '\0';
|
||||
|
||||
strcpy(savegamename, timeattackfolder);
|
||||
strlcat(savegamename, "%u.ssg", sizeof(savegamename));
|
||||
// can't use sprintf since there is %u in savegamename
|
||||
strcatbf(savegamename, srb2home, PATHSEP);
|
||||
|
||||
strcpy(liveeventbackup, va("live%s.bkp", timeattackfolder));
|
||||
strcatbf(liveeventbackup, srb2home, PATHSEP);
|
||||
|
||||
refreshdirmenu |= REFRESHDIR_GAMEDATA;
|
||||
gamedataadded = true;
|
||||
titlechanged = true;
|
||||
|
||||
clear_unlockables();
|
||||
clear_conditionsets();
|
||||
clear_emblems();
|
||||
//clear_levels();
|
||||
doClearLevels = true;
|
||||
}
|
||||
else if (!mainfile && !gamedataadded)
|
||||
{
|
||||
deh_warning("You must define a custom gamedata to use \"%s\"", word);
|
||||
}
|
||||
else if (fastcmp(word, "CLEARLEVELS"))
|
||||
{
|
||||
doClearLevels = (UINT8)(value == 0 || word2[0] == 'F' || word2[0] == 'N');
|
||||
}
|
||||
else if (fastcmp(word, "EXECCFG"))
|
||||
{
|
||||
if (strchr(word2, '.'))
|
||||
COM_BufAddText(va("exec %s\n", word2));
|
||||
|
|
@ -2800,40 +2800,6 @@ void readmaincfg(MYFILE *f)
|
|||
{
|
||||
maxXtraLife = (UINT8)get_number(word2);
|
||||
}
|
||||
|
||||
else if (fastcmp(word, "GAMEDATA"))
|
||||
{
|
||||
size_t filenamelen;
|
||||
|
||||
// Check the data filename so that mods
|
||||
// can't write arbitrary files.
|
||||
if (!GoodDataFileName(word2))
|
||||
I_Error("Maincfg: bad data file name '%s'\n", word2);
|
||||
|
||||
G_SaveGameData();
|
||||
strlcpy(gamedatafilename, word2, sizeof (gamedatafilename));
|
||||
strlwr(gamedatafilename);
|
||||
savemoddata = true;
|
||||
majormods = false;
|
||||
|
||||
// Also save a time attack folder
|
||||
filenamelen = strlen(gamedatafilename)-4; // Strip off the extension
|
||||
filenamelen = min(filenamelen, sizeof (timeattackfolder));
|
||||
strncpy(timeattackfolder, gamedatafilename, filenamelen);
|
||||
timeattackfolder[min(filenamelen, sizeof (timeattackfolder) - 1)] = '\0';
|
||||
|
||||
strcpy(savegamename, timeattackfolder);
|
||||
strlcat(savegamename, "%u.ssg", sizeof(savegamename));
|
||||
// can't use sprintf since there is %u in savegamename
|
||||
strcatbf(savegamename, srb2home, PATHSEP);
|
||||
|
||||
strcpy(liveeventbackup, va("live%s.bkp", timeattackfolder));
|
||||
strcatbf(liveeventbackup, srb2home, PATHSEP);
|
||||
|
||||
refreshdirmenu |= REFRESHDIR_GAMEDATA;
|
||||
gamedataadded = true;
|
||||
titlechanged = true;
|
||||
}
|
||||
else if (fastcmp(word, "RESETDATA"))
|
||||
{
|
||||
P_ResetData(value);
|
||||
|
|
@ -2858,6 +2824,11 @@ void readmaincfg(MYFILE *f)
|
|||
}
|
||||
} while (!myfeof(f));
|
||||
|
||||
if (doClearLevels)
|
||||
{
|
||||
clear_levels();
|
||||
}
|
||||
|
||||
Z_Free(s);
|
||||
}
|
||||
|
||||
|
|
@ -3154,13 +3125,6 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
|
|||
else
|
||||
deh_warning("%s Cup: invalid emerald number %d", cup->name, i);
|
||||
}
|
||||
else if (fastcmp(word, "UNLOCKABLE"))
|
||||
{
|
||||
if (i >= 0 && i <= MAXUNLOCKABLES) // 0 for no unlock required, anything else requires something
|
||||
cup->unlockrequired = (SINT8)i - 1;
|
||||
else
|
||||
deh_warning("%s Cup: invalid unlockable number %d", cup->name, i);
|
||||
}
|
||||
else
|
||||
deh_warning("%s Cup: unknown word '%s'", cup->name, word);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,10 +57,9 @@ sfxenum_t get_sfx(const char *word);
|
|||
skincolornum_t get_skincolor(const char *word);
|
||||
|
||||
void readwipes(MYFILE *f);
|
||||
void readmaincfg(MYFILE *f);
|
||||
void readmaincfg(MYFILE *f, boolean mainfile);
|
||||
void readconditionset(MYFILE *f, UINT8 setnum);
|
||||
void readunlockable(MYFILE *f, INT32 num);
|
||||
void readextraemblemdata(MYFILE *f, INT32 num);
|
||||
void reademblemdata(MYFILE *f, INT32 num);
|
||||
void readsound(MYFILE *f, INT32 num);
|
||||
void readframe(MYFILE *f, INT32 num);
|
||||
|
|
@ -77,6 +76,8 @@ void readlight(MYFILE *f, INT32 num);
|
|||
void readskincolor(MYFILE *f, INT32 num);
|
||||
void readthing(MYFILE *f, INT32 num);
|
||||
void readfreeslots(MYFILE *f);
|
||||
void clear_emblems(void);
|
||||
void clear_unlockables(void);
|
||||
void clear_levels(void);
|
||||
void clear_conditionsets(void);
|
||||
|
||||
|
|
|
|||
|
|
@ -5384,6 +5384,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
|
|||
"MT_SINK_SHIELD",
|
||||
"MT_SINKTRAIL",
|
||||
|
||||
"MT_GACHABOM",
|
||||
|
||||
"MT_DUELBOMB", // Duel mode bombs
|
||||
|
||||
"MT_BATTLEBUMPER", // Battle Mode bumper
|
||||
|
|
@ -6356,7 +6358,8 @@ struct int_const_s const INT_CONST[] = {
|
|||
{"SF_X2AWAYSOUND",SF_X2AWAYSOUND},
|
||||
|
||||
// Global emblem var flags
|
||||
// none in kart yet
|
||||
{"GE_NOTMEDAL", GE_NOTMEDAL},
|
||||
{"GE_TIMED", GE_TIMED},
|
||||
|
||||
// Map emblem var flags
|
||||
{"ME_ENCORE",ME_ENCORE},
|
||||
|
|
@ -6794,6 +6797,7 @@ struct int_const_s const INT_CONST[] = {
|
|||
{"KRITEM_TRIPLEORBINAUT",KRITEM_TRIPLEORBINAUT},
|
||||
{"KRITEM_QUADORBINAUT",KRITEM_QUADORBINAUT},
|
||||
{"KRITEM_DUALJAWZ",KRITEM_DUALJAWZ},
|
||||
{"KRITEM_TRIPLEGACHABOM",KRITEM_TRIPLEGACHABOM},
|
||||
{"NUMKARTRESULTS",NUMKARTRESULTS},
|
||||
|
||||
// kartshields_t
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
|
|||
else if (fastcmp(word, "MAINCFG"))
|
||||
{
|
||||
G_SetGameModified(multiplayer, true);
|
||||
readmaincfg(f);
|
||||
readmaincfg(f, mainfile);
|
||||
continue;
|
||||
}
|
||||
else if (fastcmp(word, "WIPES"))
|
||||
|
|
@ -278,32 +278,6 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
|
|||
}
|
||||
continue;
|
||||
}
|
||||
else if (fastcmp(word, "EXTRAEMBLEM"))
|
||||
{
|
||||
if (!mainfile && !gamedataadded)
|
||||
{
|
||||
deh_warning("You must define a custom gamedata to use \"%s\"", word);
|
||||
ignorelines(f);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!word2)
|
||||
i = numextraemblems + 1;
|
||||
|
||||
if (i > 0 && i <= MAXEXTRAEMBLEMS)
|
||||
{
|
||||
if (numextraemblems < i)
|
||||
numextraemblems = i;
|
||||
readextraemblemdata(f, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
deh_warning("Extra emblem number %d out of range (1 - %d)", i, MAXEXTRAEMBLEMS);
|
||||
ignorelines(f);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (word2)
|
||||
{
|
||||
if (fastcmp(word, "THING") || fastcmp(word, "MOBJ") || fastcmp(word, "OBJECT"))
|
||||
|
|
@ -479,7 +453,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
|
|||
ignorelines(f);
|
||||
}
|
||||
else if (i > 0 && i <= MAXCONDITIONSETS)
|
||||
readconditionset(f, (UINT8)i);
|
||||
readconditionset(f, (UINT8)(i-1));
|
||||
else
|
||||
{
|
||||
deh_warning("Condition set number %d out of range (1 - %d)", i, MAXCONDITIONSETS);
|
||||
|
|
@ -512,7 +486,6 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
|
|||
{
|
||||
cup = Z_Calloc(sizeof (cupheader_t), PU_STATIC, NULL);
|
||||
cup->id = numkartcupheaders;
|
||||
cup->unlockrequired = -1;
|
||||
deh_strlcpy(cup->name, word2,
|
||||
sizeof(cup->name), va("Cup header %s: name", word2));
|
||||
if (prev != NULL)
|
||||
|
|
@ -569,41 +542,6 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
|
|||
{
|
||||
deh_warning("Patch is only compatible with base SRB2.");
|
||||
}
|
||||
// Clear all data in certain locations (mostly for unlocks)
|
||||
// Unless you REALLY want to piss people off,
|
||||
// define a custom gamedata /before/ doing this!!
|
||||
// (then again, modifiedgame will prevent game data saving anyway)
|
||||
else if (fastcmp(word, "CLEAR"))
|
||||
{
|
||||
boolean clearall = (fastcmp(word2, "ALL"));
|
||||
|
||||
if (!mainfile && !gamedataadded)
|
||||
{
|
||||
deh_warning("You must define a custom gamedata to use \"%s\"", word);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (clearall || fastcmp(word2, "UNLOCKABLES"))
|
||||
memset(&unlockables, 0, sizeof(unlockables));
|
||||
|
||||
if (clearall || fastcmp(word2, "EMBLEMS"))
|
||||
{
|
||||
memset(&emblemlocations, 0, sizeof(emblemlocations));
|
||||
numemblems = 0;
|
||||
}
|
||||
|
||||
if (clearall || fastcmp(word2, "EXTRAEMBLEMS"))
|
||||
{
|
||||
memset(&extraemblems, 0, sizeof(extraemblems));
|
||||
numextraemblems = 0;
|
||||
}
|
||||
|
||||
if (clearall || fastcmp(word2, "CONDITIONSETS"))
|
||||
clear_conditionsets();
|
||||
|
||||
if (clearall || fastcmp(word2, "LEVELS"))
|
||||
clear_levels();
|
||||
}
|
||||
else
|
||||
deh_warning("Unknown word: %s", word);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -540,7 +540,9 @@ void DRPC_UpdatePresence(void)
|
|||
else
|
||||
{
|
||||
// Map name on tool tip
|
||||
snprintf(mapname, 48, "Map: %s", G_BuildMapTitle(gamemap));
|
||||
char *title = G_BuildMapTitle(gamemap);
|
||||
snprintf(mapname, 48, "Map: %s", title);
|
||||
Z_Free(title);
|
||||
discordPresence.largeImageText = mapname;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -201,7 +201,8 @@ extern char logfilename[1024];
|
|||
#define MAXGAMEPADS (MAXSPLITSCREENPLAYERS * 2) // Number of gamepads we'll be allowing
|
||||
|
||||
#define MAXSKINS UINT8_MAX
|
||||
#define SKINNAMESIZE 16 // Moved from r_skins.h as including that particular header causes issues later down the line.
|
||||
#define SKINNAMESIZE 16
|
||||
#define MAXAVAILABILITY ((MAXSKINS + 7)/8)
|
||||
|
||||
#define COLORRAMPSIZE 16
|
||||
#define MAXCOLORNAME 32
|
||||
|
|
|
|||
|
|
@ -186,8 +186,6 @@ extern INT32 postimgparam[MAXSPLITSCREENPLAYERS];
|
|||
extern INT32 viewwindowx, viewwindowy;
|
||||
extern INT32 viewwidth, scaledviewwidth;
|
||||
|
||||
extern boolean gamedataloaded;
|
||||
|
||||
// Player taking events, and displaying.
|
||||
extern INT32 consoleplayer;
|
||||
extern INT32 displayplayers[MAXSPLITSCREENPLAYERS];
|
||||
|
|
@ -357,7 +355,6 @@ struct cupheader_t
|
|||
UINT8 numlevels; ///< Number of levels defined in levellist
|
||||
UINT8 numbonus; ///< Number of bonus stages defined
|
||||
UINT8 emeraldnum; ///< ID of Emerald to use for special stage (1-7 for Chaos Emeralds, 8-14 for Super Emeralds, 0 for no emerald)
|
||||
SINT8 unlockrequired; ///< An unlockable is required to select this cup. -1 for no unlocking required.
|
||||
cupheader_t *next; ///< Next cup in linked list
|
||||
};
|
||||
|
||||
|
|
@ -393,7 +390,6 @@ struct mapheader_t
|
|||
// Selection metadata
|
||||
char keywords[33]; ///< Keywords separated by space to search for. 32 characters.
|
||||
|
||||
SINT8 unlockrequired; ///< Is an unlockable required to play this level? -1 if no.
|
||||
UINT8 levelselect; ///< Is this map available in the level select? If so, which map list is it available in?
|
||||
UINT16 menuflags; ///< LF2_flags: options that affect record attack menus
|
||||
|
||||
|
|
@ -540,9 +536,6 @@ struct tolinfo_t
|
|||
extern tolinfo_t TYPEOFLEVEL[NUMTOLNAMES];
|
||||
extern UINT32 lastcustomtol;
|
||||
|
||||
extern tic_t totalplaytime;
|
||||
extern UINT32 matchesplayed;
|
||||
|
||||
extern UINT8 stagefailed;
|
||||
|
||||
// Emeralds stored as bits to throw savegame hackers off.
|
||||
|
|
@ -580,10 +573,6 @@ extern INT32 luabanks[NUM_LUABANKS];
|
|||
|
||||
extern INT32 nummaprings; //keep track of spawned rings/coins
|
||||
|
||||
extern UINT32 token; ///< Number of tokens collected in a level
|
||||
extern UINT32 tokenlist; ///< List of tokens collected
|
||||
extern boolean gottoken; ///< Did you get a token? Used for end of act
|
||||
extern INT32 tokenbits; ///< Used for setting token bits
|
||||
extern UINT32 bluescore; ///< Blue Team Scores
|
||||
extern UINT32 redscore; ///< Red Team Scores
|
||||
|
||||
|
|
@ -684,8 +673,6 @@ extern INT16 votelevels[4][2];
|
|||
extern SINT8 votes[MAXPLAYERS];
|
||||
extern SINT8 pickedvote;
|
||||
|
||||
extern UINT32 timesBeaten; // # of times the game has been beaten.
|
||||
|
||||
// ===========================
|
||||
// Internal parameters, fixed.
|
||||
// ===========================
|
||||
|
|
|
|||
|
|
@ -866,7 +866,7 @@ boolean F_CreditResponder(event_t *event)
|
|||
return false;
|
||||
}
|
||||
|
||||
/*if (!(timesBeaten) && !(netgame || multiplayer) && !cht_debug)
|
||||
/*if (!(gamedata->timesBeaten) && !(netgame || multiplayer) && !cht_debug)
|
||||
return false;*/
|
||||
|
||||
if (key != KEY_ESCAPE && key != KEY_ENTER && key != KEY_BACKSPACE)
|
||||
|
|
@ -1024,31 +1024,6 @@ void F_GameEvaluationDrawer(void)
|
|||
|
||||
V_DrawCreditString((BASEVIDWIDTH - V_CreditStringWidth(endingtext))<<(FRACBITS-1), (BASEVIDHEIGHT-100)<<(FRACBITS-1), 0, endingtext);
|
||||
|
||||
#if 0 // the following looks like hot garbage the more unlockables we add, and we now have a lot of unlockables
|
||||
if (finalecount >= 5*TICRATE)
|
||||
{
|
||||
V_DrawString(8, 16, V_YELLOWMAP, "Unlocked:");
|
||||
|
||||
if (!usedCheats)
|
||||
{
|
||||
INT32 startcoord = 32;
|
||||
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
{
|
||||
if (unlockables[i].conditionset && unlockables[i].conditionset < MAXCONDITIONSETS
|
||||
&& unlockables[i].type && !unlockables[i].nocecho)
|
||||
{
|
||||
if (unlockables[i].unlocked)
|
||||
V_DrawString(8, startcoord, 0, unlockables[i].name);
|
||||
startcoord += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
V_DrawString(8, 96, V_YELLOWMAP, "Cheated games\ncan't unlock\nextras!");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (marathonmode)
|
||||
{
|
||||
const char *rtatext, *cuttext;
|
||||
|
|
@ -1101,11 +1076,9 @@ void F_GameEvaluationTicker(void)
|
|||
{
|
||||
if (!usedCheats)
|
||||
{
|
||||
++timesBeaten;
|
||||
|
||||
if (M_UpdateUnlockablesAndExtraEmblems())
|
||||
S_StartSound(NULL, sfx_s3k68);
|
||||
++gamedata->timesBeaten;
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
else
|
||||
|
|
@ -1611,7 +1584,7 @@ void F_EndingDrawer(void)
|
|||
//colset(linkmap, 164, 165, 169); -- the ideal purple colour to represent a clicked in-game link, but not worth it just for a soundtest-controlled secret
|
||||
V_DrawCenteredString(BASEVIDWIDTH/2, 8, V_ALLOWLOWERCASE|(trans<<V_ALPHASHIFT), str);
|
||||
V_DrawCharacter(32, BASEVIDHEIGHT-16, '>'|(trans<<V_ALPHASHIFT), false);
|
||||
V_DrawString(40, ((finalecount == STOPPINGPOINT-(20+TICRATE)) ? 1 : 0)+BASEVIDHEIGHT-16, ((timesBeaten || finalecount >= STOPPINGPOINT-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<<V_ALPHASHIFT), " [S] ===>");
|
||||
V_DrawString(40, ((finalecount == STOPPINGPOINT-(20+TICRATE)) ? 1 : 0)+BASEVIDHEIGHT-16, ((gamedata->timesBeaten || finalecount >= STOPPINGPOINT-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<<V_ALPHASHIFT), " [S] ===>");
|
||||
}
|
||||
|
||||
if (finalecount > STOPPINGPOINT-(20+(2*TICRATE)))
|
||||
|
|
@ -1804,7 +1777,14 @@ static void F_CacheTitleScreen(void)
|
|||
break; // idk do we still want this?
|
||||
|
||||
case TTMODE_RINGRACERS:
|
||||
kts_bumper = W_CachePatchName("KTSBUMPR1", PU_PATCH_LOWPRIORITY);
|
||||
if (!M_SecretUnlocked(SECRET_ALTTITLE, true))
|
||||
{
|
||||
CV_StealthSetValue(&cv_alttitle, 0);
|
||||
}
|
||||
|
||||
kts_bumper = W_CachePatchName(
|
||||
(cv_alttitle.value ? "KTSJUMPR1" : "KTSBUMPR1"),
|
||||
PU_PATCH_LOWPRIORITY);
|
||||
kts_eggman = W_CachePatchName("KTSEGG01", PU_PATCH_LOWPRIORITY);
|
||||
kts_tails = W_CachePatchName("KTSTAL01", PU_PATCH_LOWPRIORITY);
|
||||
kts_tails_tails = W_CachePatchName("KTSTAL02", PU_PATCH_LOWPRIORITY);
|
||||
|
|
|
|||
164
src/g_demo.c
164
src/g_demo.c
|
|
@ -212,47 +212,6 @@ void G_LoadMetal(UINT8 **buffer)
|
|||
metal_p = metalbuffer + READUINT32(*buffer);
|
||||
}
|
||||
|
||||
// Finds a skin with the closest stats if the expected skin doesn't exist.
|
||||
static INT32 GetSkinNumClosestToStats(UINT8 kartspeed, UINT8 kartweight, UINT32 flags)
|
||||
{
|
||||
INT32 i, closest_skin = 0;
|
||||
UINT8 closest_stats, stat_diff;
|
||||
boolean doflagcheck = true;
|
||||
UINT32 flagcheck = flags;
|
||||
|
||||
flaglessretry:
|
||||
closest_stats = UINT8_MAX;
|
||||
|
||||
for (i = 0; i < numskins; i++)
|
||||
{
|
||||
stat_diff = abs(skins[i].kartspeed - kartspeed) + abs(skins[i].kartweight - kartweight);
|
||||
if (doflagcheck && (skins[i].flags & flagcheck) != flagcheck)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (stat_diff < closest_stats)
|
||||
{
|
||||
closest_stats = stat_diff;
|
||||
closest_skin = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (stat_diff && (doflagcheck || closest_stats == UINT8_MAX))
|
||||
{
|
||||
// Just grab *any* SF_IRONMAN if we don't get it on the first pass.
|
||||
if ((flagcheck & SF_IRONMAN) && (flagcheck != SF_IRONMAN))
|
||||
{
|
||||
flagcheck = SF_IRONMAN;
|
||||
}
|
||||
|
||||
doflagcheck = false;
|
||||
|
||||
goto flaglessretry;
|
||||
}
|
||||
|
||||
return closest_skin;
|
||||
}
|
||||
|
||||
void G_ReadDemoExtraData(void)
|
||||
{
|
||||
INT32 p, extradata, i;
|
||||
|
|
@ -276,10 +235,8 @@ void G_ReadDemoExtraData(void)
|
|||
{
|
||||
extradata = READUINT8(demo_p);
|
||||
|
||||
if (extradata & DXD_PLAYSTATE)
|
||||
if (extradata & DXD_JOINDATA)
|
||||
{
|
||||
i = READUINT8(demo_p);
|
||||
|
||||
if (!playeringame[p])
|
||||
{
|
||||
CL_ClearPlayer(p);
|
||||
|
|
@ -288,14 +245,22 @@ void G_ReadDemoExtraData(void)
|
|||
players[p].spectator = true;
|
||||
}
|
||||
|
||||
if ((players[p].bot = !!(i & DXD_PST_ISBOT)))
|
||||
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||
{
|
||||
players[p].availabilities[i] = READUINT8(demo_p);
|
||||
}
|
||||
|
||||
players[p].bot = !!(READUINT8(demo_p));
|
||||
if (players[p].bot)
|
||||
{
|
||||
players[p].botvars.difficulty = READUINT8(demo_p);
|
||||
players[p].botvars.diffincrease = READUINT8(demo_p); // needed to avoid having to duplicate logic
|
||||
players[p].botvars.rival = (boolean)READUINT8(demo_p);
|
||||
|
||||
i &= ~DXD_PST_ISBOT;
|
||||
}
|
||||
}
|
||||
if (extradata & DXD_PLAYSTATE)
|
||||
{
|
||||
i = READUINT8(demo_p);
|
||||
|
||||
switch (i) {
|
||||
case DXD_PST_PLAYING:
|
||||
|
|
@ -334,14 +299,6 @@ void G_ReadDemoExtraData(void)
|
|||
K_CheckBumpers();
|
||||
P_CheckRacers();
|
||||
}
|
||||
if (extradata & DXD_RESPAWN)
|
||||
{
|
||||
if (players[p].mo)
|
||||
{
|
||||
// Is this how this should work..?
|
||||
K_DoIngameRespawn(&players[p]);
|
||||
}
|
||||
}
|
||||
if (extradata & DXD_SKIN)
|
||||
{
|
||||
UINT8 skinid;
|
||||
|
|
@ -397,6 +354,14 @@ void G_ReadDemoExtraData(void)
|
|||
}
|
||||
}
|
||||
}
|
||||
if (extradata & DXD_RESPAWN)
|
||||
{
|
||||
if (players[p].mo)
|
||||
{
|
||||
// Is this how this should work..?
|
||||
K_DoIngameRespawn(&players[p]);
|
||||
}
|
||||
}
|
||||
if (extradata & DXD_WEAPONPREF)
|
||||
{
|
||||
WeaponPref_Parse(&demo_p, p);
|
||||
|
|
@ -454,6 +419,21 @@ void G_WriteDemoExtraData(void)
|
|||
WRITEUINT8(demo_p, i);
|
||||
WRITEUINT8(demo_p, demo_extradata[i]);
|
||||
|
||||
if (demo_extradata[i] & DXD_JOINDATA)
|
||||
{
|
||||
for (j = 0; j < MAXAVAILABILITY; j++)
|
||||
{
|
||||
WRITEUINT8(demo_p, players[i].availabilities[i]);
|
||||
}
|
||||
|
||||
WRITEUINT8(demo_p, (UINT8)players[i].bot);
|
||||
if (players[i].bot)
|
||||
{
|
||||
WRITEUINT8(demo_p, players[i].botvars.difficulty);
|
||||
WRITEUINT8(demo_p, players[i].botvars.diffincrease); // needed to avoid having to duplicate logic
|
||||
WRITEUINT8(demo_p, (UINT8)players[i].botvars.rival);
|
||||
}
|
||||
}
|
||||
if (demo_extradata[i] & DXD_PLAYSTATE)
|
||||
{
|
||||
UINT8 pst = DXD_PST_PLAYING;
|
||||
|
|
@ -472,19 +452,7 @@ void G_WriteDemoExtraData(void)
|
|||
pst = DXD_PST_SPECTATING;
|
||||
}
|
||||
|
||||
if (players[i].bot)
|
||||
{
|
||||
pst |= DXD_PST_ISBOT;
|
||||
}
|
||||
|
||||
WRITEUINT8(demo_p, pst);
|
||||
|
||||
if (pst & DXD_PST_ISBOT)
|
||||
{
|
||||
WRITEUINT8(demo_p, players[i].botvars.difficulty);
|
||||
WRITEUINT8(demo_p, players[i].botvars.diffincrease); // needed to avoid having to duplicate logic
|
||||
WRITEUINT8(demo_p, (UINT8)players[i].botvars.rival);
|
||||
}
|
||||
}
|
||||
//if (demo_extradata[i] & DXD_RESPAWN) has no extra data
|
||||
if (demo_extradata[i] & DXD_SKIN)
|
||||
|
|
@ -1218,8 +1186,14 @@ void G_GhostTicker(void)
|
|||
if (ziptic == 0) // Only support player 0 info for now
|
||||
{
|
||||
ziptic = READUINT8(g->p);
|
||||
if (ziptic & DXD_JOINDATA)
|
||||
{
|
||||
g->p += MAXAVAILABILITY;
|
||||
if (READUINT8(g->p) != 0)
|
||||
I_Error("Ghost is not a record attack ghost (bot JOINDATA)");
|
||||
}
|
||||
if (ziptic & DXD_PLAYSTATE && READUINT8(g->p) != DXD_PST_PLAYING)
|
||||
I_Error("Ghost is not a record attack ghost PLAYSTATE"); //@TODO lmao don't blow up like this
|
||||
I_Error("Ghost is not a record attack ghost (has PLAYSTATE)");
|
||||
if (ziptic & DXD_SKIN)
|
||||
g->p++; // We _could_ read this info, but it shouldn't change anything in record attack...
|
||||
if (ziptic & DXD_COLOR)
|
||||
|
|
@ -2248,6 +2222,7 @@ static void G_SaveDemoSkins(UINT8 **pp)
|
|||
{
|
||||
char skin[16];
|
||||
UINT8 i;
|
||||
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(true);
|
||||
|
||||
WRITEUINT8((*pp), numskins);
|
||||
for (i = 0; i < numskins; i++)
|
||||
|
|
@ -2262,12 +2237,17 @@ static void G_SaveDemoSkins(UINT8 **pp)
|
|||
WRITEUINT8((*pp), skins[i].kartweight);
|
||||
WRITEUINT32((*pp), skins[i].flags);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||
{
|
||||
WRITEUINT8((*pp), availabilitiesbuffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static democharlist_t *G_LoadDemoSkins(UINT8 **pp, UINT8 *worknumskins, boolean getclosest)
|
||||
{
|
||||
char skin[17];
|
||||
UINT8 i;
|
||||
UINT8 i, byte, shif;
|
||||
democharlist_t *skinlist = NULL;
|
||||
|
||||
(*worknumskins) = READUINT8((*pp));
|
||||
|
|
@ -2300,7 +2280,7 @@ static democharlist_t *G_LoadDemoSkins(UINT8 **pp, UINT8 *worknumskins, boolean
|
|||
}
|
||||
else
|
||||
{
|
||||
result = GetSkinNumClosestToStats(skinlist[i].kartspeed, skinlist[i].kartweight, skinlist[i].flags);
|
||||
result = GetSkinNumClosestToStats(skinlist[i].kartspeed, skinlist[i].kartweight, skinlist[i].flags, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2310,6 +2290,24 @@ static democharlist_t *G_LoadDemoSkins(UINT8 **pp, UINT8 *worknumskins, boolean
|
|||
}
|
||||
}
|
||||
|
||||
for (byte = 0; byte < MAXAVAILABILITY; byte++)
|
||||
{
|
||||
UINT8 availabilitiesbuffer = READUINT8((*pp));
|
||||
|
||||
for (shif = 0; shif < 8; shif++)
|
||||
{
|
||||
i = (byte*8) + shif;
|
||||
|
||||
if (i >= (*worknumskins))
|
||||
break;
|
||||
|
||||
if (availabilitiesbuffer & (1 << shif))
|
||||
{
|
||||
skinlist[i].unlockrequired = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return skinlist;
|
||||
}
|
||||
|
||||
|
|
@ -2326,6 +2324,8 @@ static void G_SkipDemoSkins(UINT8 **pp)
|
|||
(*pp)++; // kartweight
|
||||
(*pp) += 4; // flags
|
||||
}
|
||||
|
||||
(*pp) += MAXAVAILABILITY;
|
||||
}
|
||||
|
||||
void G_BeginRecording(void)
|
||||
|
|
@ -2358,7 +2358,11 @@ void G_BeginRecording(void)
|
|||
|
||||
// Full replay title
|
||||
demo_p += 64;
|
||||
snprintf(demo.titlename, 64, "%s - %s", G_BuildMapTitle(gamemap), modeattacking ? "Record Attack" : connectedservername);
|
||||
{
|
||||
char *title = G_BuildMapTitle(gamemap);
|
||||
snprintf(demo.titlename, 64, "%s - %s", title, modeattacking ? "Record Attack" : connectedservername);
|
||||
Z_Free(title);
|
||||
}
|
||||
|
||||
// demo checksum
|
||||
demo_p += 16;
|
||||
|
|
@ -2443,6 +2447,11 @@ void G_BeginRecording(void)
|
|||
M_Memcpy(demo_p,name,16);
|
||||
demo_p += 16;
|
||||
|
||||
for (j = 0; j < MAXAVAILABILITY; j++)
|
||||
{
|
||||
WRITEUINT8(demo_p, player->availabilities[j]);
|
||||
}
|
||||
|
||||
// Skin (now index into demo.skinlist)
|
||||
WRITEUINT8(demo_p, player->skin);
|
||||
WRITEUINT8(demo_p, player->lastfakeskin);
|
||||
|
|
@ -2928,6 +2937,7 @@ void G_DoPlayDemo(char *defdemoname)
|
|||
UINT8 i, p, numslots = 0;
|
||||
lumpnum_t l;
|
||||
char color[MAXCOLORNAME+1],follower[17],mapname[MAXMAPLUMPNAME],*n,*pdemoname;
|
||||
UINT8 availabilities[MAXPLAYERS][MAXAVAILABILITY];
|
||||
UINT8 version,subversion;
|
||||
UINT32 randseed[PRNUMCLASS];
|
||||
char msg[1024];
|
||||
|
|
@ -3293,6 +3303,11 @@ void G_DoPlayDemo(char *defdemoname)
|
|||
M_Memcpy(player_names[p],demo_p,16);
|
||||
demo_p += 16;
|
||||
|
||||
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||
{
|
||||
availabilities[p][i] = READUINT8(demo_p);
|
||||
}
|
||||
|
||||
// Skin
|
||||
|
||||
i = READUINT8(demo_p);
|
||||
|
|
@ -3376,6 +3391,8 @@ void G_DoPlayDemo(char *defdemoname)
|
|||
|
||||
for (i = 0; i < numslots; i++)
|
||||
{
|
||||
UINT8 j;
|
||||
|
||||
p = slots[i];
|
||||
if (players[p].mo)
|
||||
{
|
||||
|
|
@ -3392,6 +3409,11 @@ void G_DoPlayDemo(char *defdemoname)
|
|||
players[p].kartweight = ghostext[p].kartweight = demo.skinlist[demo.currentskinid[p]].kartweight;
|
||||
players[p].charflags = ghostext[p].charflags = demo.skinlist[demo.currentskinid[p]].flags;
|
||||
players[p].lastfakeskin = lastfakeskin[p];
|
||||
|
||||
for (j = 0; j < MAXAVAILABILITY; j++)
|
||||
{
|
||||
players[p].availabilities[j] = availabilities[p][j];
|
||||
}
|
||||
}
|
||||
|
||||
demo.deferstart = true;
|
||||
|
|
|
|||
10
src/g_demo.h
10
src/g_demo.h
|
|
@ -33,6 +33,7 @@ struct democharlist_t {
|
|||
UINT8 kartspeed;
|
||||
UINT8 kartweight;
|
||||
UINT32 flags;
|
||||
boolean unlockrequired;
|
||||
};
|
||||
|
||||
// Publicly-accessible demo vars
|
||||
|
|
@ -119,20 +120,19 @@ typedef enum
|
|||
extern UINT8 demo_extradata[MAXPLAYERS];
|
||||
extern UINT8 demo_writerng;
|
||||
|
||||
#define DXD_PLAYSTATE 0x01 // state changed between playing, spectating, or not in-game
|
||||
#define DXD_RESPAWN 0x02 // "respawn" command in console
|
||||
#define DXD_JOINDATA 0x01 // join-specific data
|
||||
#define DXD_PLAYSTATE 0x02 // state changed between playing, spectating, or not in-game
|
||||
#define DXD_SKIN 0x04 // skin changed
|
||||
#define DXD_NAME 0x08 // name changed
|
||||
#define DXD_COLOR 0x10 // color changed
|
||||
#define DXD_FOLLOWER 0x20 // follower was changed
|
||||
#define DXD_WEAPONPREF 0x40 // netsynced playsim settings were changed
|
||||
#define DXD_RESPAWN 0x40 // "respawn" command in console
|
||||
#define DXD_WEAPONPREF 0x80 // netsynced playsim settings were changed
|
||||
|
||||
#define DXD_PST_PLAYING 0x01
|
||||
#define DXD_PST_SPECTATING 0x02
|
||||
#define DXD_PST_LEFT 0x03
|
||||
|
||||
#define DXD_PST_ISBOT 0x80 // extra flag
|
||||
|
||||
// Record/playback tics
|
||||
void G_ReadDemoExtraData(void);
|
||||
void G_WriteDemoExtraData(void);
|
||||
|
|
|
|||
230
src/g_game.c
230
src/g_game.c
|
|
@ -189,7 +189,8 @@ struct quake quake;
|
|||
|
||||
// Map Header Information
|
||||
mapheader_t** mapheaderinfo = {NULL};
|
||||
INT32 nummapheaders, mapallocsize = 0;
|
||||
INT32 nummapheaders = 0;
|
||||
INT32 mapallocsize = 0;
|
||||
|
||||
// Kart cup definitions
|
||||
cupheader_t *kartcupheaders = NULL;
|
||||
|
|
@ -203,14 +204,6 @@ UINT8 stagefailed; // Used for GEMS BONUS? Also to see if you beat the stage.
|
|||
|
||||
UINT16 emeralds;
|
||||
INT32 luabanks[NUM_LUABANKS];
|
||||
UINT32 token; // Number of tokens collected in a level
|
||||
UINT32 tokenlist; // List of tokens collected
|
||||
boolean gottoken; // Did you get a token? Used for end of act
|
||||
INT32 tokenbits; // Used for setting token bits
|
||||
|
||||
tic_t totalplaytime;
|
||||
UINT32 matchesplayed; // SRB2Kart
|
||||
boolean gamedataloaded = false;
|
||||
|
||||
// Temporary holding place for nights data for the current map
|
||||
//nightsdata_t ntemprecords;
|
||||
|
|
@ -340,9 +333,6 @@ static void G_ResetRandMapBuffer(void)
|
|||
//intentionally not resetting randmaps.counttogametype here
|
||||
}
|
||||
|
||||
// Grading
|
||||
UINT32 timesBeaten;
|
||||
|
||||
typedef struct joystickvector2_s
|
||||
{
|
||||
INT32 xaxis;
|
||||
|
|
@ -600,10 +590,7 @@ static void G_UpdateRecordReplays(void)
|
|||
if ((earnedEmblems = M_CheckLevelEmblems()))
|
||||
CONS_Printf(M_GetText("\x82" "Earned %hu medal%s for Record Attack records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
|
||||
|
||||
if (M_UpdateUnlockablesAndExtraEmblems())
|
||||
S_StartSound(NULL, sfx_ncitem);
|
||||
|
||||
// SRB2Kart - save here so you NEVER lose your earned times/medals.
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
|
||||
|
|
@ -2194,9 +2181,8 @@ static inline void G_PlayerFinishLevel(INT32 player)
|
|||
{
|
||||
if (legitimateexit && !demo.playback && !mapreset) // (yes you're allowed to unlock stuff this way when the game is modified)
|
||||
{
|
||||
matchesplayed++;
|
||||
if (M_UpdateUnlockablesAndExtraEmblems())
|
||||
S_StartSound(NULL, sfx_ncitem);
|
||||
gamedata->matchesplayed++;
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
|
||||
|
|
@ -2238,7 +2224,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
UINT8 latestlap;
|
||||
UINT16 skincolor;
|
||||
INT32 skin;
|
||||
UINT32 availabilities;
|
||||
UINT8 availabilities[MAXAVAILABILITY];
|
||||
UINT8 fakeskin;
|
||||
UINT8 lastfakeskin;
|
||||
|
||||
|
|
@ -2306,7 +2292,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
followercolor = players[player].followercolor;
|
||||
followerskin = players[player].followerskin;
|
||||
|
||||
availabilities = players[player].availabilities;
|
||||
memcpy(availabilities, players[player].availabilities, sizeof(availabilities));
|
||||
|
||||
followitem = players[player].followitem;
|
||||
|
||||
|
|
@ -2429,7 +2415,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
p->charflags = charflags;
|
||||
p->lastfakeskin = lastfakeskin;
|
||||
|
||||
p->availabilities = availabilities;
|
||||
memcpy(players[player].availabilities, availabilities, sizeof(availabilities));
|
||||
p->followitem = followitem;
|
||||
|
||||
p->starpostnum = starpostnum;
|
||||
|
|
@ -2918,7 +2904,7 @@ void G_AddPlayer(INT32 playernum)
|
|||
{
|
||||
player_t *p = &players[playernum];
|
||||
p->playerstate = PST_REBORN;
|
||||
demo_extradata[playernum] |= DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER; // Set everything
|
||||
demo_extradata[playernum] |= DXD_JOINDATA|DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER; // Set everything
|
||||
}
|
||||
|
||||
void G_ExitLevel(void)
|
||||
|
|
@ -3315,7 +3301,7 @@ INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype)
|
|||
// 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) || encorescramble == 1)
|
||||
boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE, false) || encorescramble == 1)
|
||||
&& ((gametyperules|gametypedefaultrules[prefgametype]) & GTR_CIRCUIT));
|
||||
UINT8 encoremodifier = 0;
|
||||
|
||||
|
|
@ -3418,23 +3404,27 @@ INT16 G_GetFirstMapOfGametype(UINT8 pgametype)
|
|||
{
|
||||
UINT8 i = 0;
|
||||
INT16 mapnum = NEXTMAP_INVALID;
|
||||
UINT32 tol = G_TOLFlag(pgametype);
|
||||
levelsearch_t templevelsearch;
|
||||
|
||||
levellist.cupmode = (!(gametypedefaultrules[pgametype] & GTR_NOCUPSELECT));
|
||||
levellist.timeattack = false;
|
||||
templevelsearch.cup = NULL;
|
||||
templevelsearch.typeoflevel = G_TOLFlag(pgametype);
|
||||
templevelsearch.cupmode = (!(gametypedefaultrules[pgametype] & GTR_NOCUPSELECT));
|
||||
templevelsearch.timeattack = false;
|
||||
templevelsearch.checklocked = true;
|
||||
|
||||
if (levellist.cupmode)
|
||||
if (templevelsearch.cupmode)
|
||||
{
|
||||
cupheader_t *cup = kartcupheaders;
|
||||
while (cup && mapnum >= nummapheaders)
|
||||
templevelsearch.cup = kartcupheaders;
|
||||
while (templevelsearch.cup && mapnum >= nummapheaders)
|
||||
{
|
||||
mapnum = M_GetFirstLevelInList(&i, tol, cup);
|
||||
mapnum = M_GetFirstLevelInList(&i, &templevelsearch);
|
||||
i = 0;
|
||||
templevelsearch.cup = templevelsearch.cup->next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mapnum = M_GetFirstLevelInList(&i, tol, NULL);
|
||||
mapnum = M_GetFirstLevelInList(&i, &templevelsearch);
|
||||
}
|
||||
|
||||
return mapnum;
|
||||
|
|
@ -3687,6 +3677,9 @@ static void G_UpdateVisited(void)
|
|||
|
||||
if ((earnedEmblems = M_CompletionEmblems()))
|
||||
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
|
||||
static boolean CanSaveLevel(INT32 mapnum)
|
||||
|
|
@ -3869,7 +3862,7 @@ static void G_GetNextMap(void)
|
|||
while (cup)
|
||||
{
|
||||
// Not unlocked? Grab the next result afterwards
|
||||
if (!marathonmode && cup->unlockrequired != -1 && !unlockables[cup->unlockrequired].unlocked)
|
||||
if (!marathonmode && M_CupLocked(cup))
|
||||
{
|
||||
cup = cup->next;
|
||||
gettingresult = 1;
|
||||
|
|
@ -4223,10 +4216,6 @@ static void G_DoContinued(void)
|
|||
// Reset score
|
||||
pl->score = 0;
|
||||
|
||||
// Allow tokens to come back
|
||||
tokenlist = 0;
|
||||
token = 0;
|
||||
|
||||
if (!(netgame || multiplayer || demo.playback || demo.recording || metalrecording || modeattacking) && !usedCheats && cursaveslot > 0)
|
||||
G_SaveGameOver((UINT32)cursaveslot, true);
|
||||
|
||||
|
|
@ -4310,7 +4299,8 @@ void G_LoadGameSettings(void)
|
|||
Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+2].strvalue = NULL;
|
||||
}
|
||||
|
||||
#define GD_VERSIONCHECK 0xBA5ED444 // Change every major version, as usual
|
||||
#define GD_VERSIONCHECK 0xBA5ED123 // Change every major version, as usual
|
||||
#define GD_VERSIONMINOR 0 // Change every format update
|
||||
|
||||
// G_LoadGameData
|
||||
// Loads the main data file, which stores information such as emblems found, etc.
|
||||
|
|
@ -4319,21 +4309,22 @@ void G_LoadGameData(void)
|
|||
size_t length;
|
||||
UINT32 i, j;
|
||||
UINT32 versionID;
|
||||
UINT8 versionMinor;
|
||||
UINT8 rtemp;
|
||||
|
||||
//For records
|
||||
UINT32 numgamedatamapheaders;
|
||||
|
||||
// Stop saving, until we successfully load it again.
|
||||
gamedataloaded = false;
|
||||
gamedata->loaded = false;
|
||||
|
||||
// Clear things so previously read gamedata doesn't transfer
|
||||
// to new gamedata
|
||||
G_ClearRecords(); // main and nights records
|
||||
G_ClearRecords(); // records
|
||||
M_ClearSecrets(); // emblems, unlocks, maps visited, etc
|
||||
|
||||
totalplaytime = 0; // total play time (separate from all)
|
||||
matchesplayed = 0; // SRB2Kart: matches played & finished
|
||||
gamedata->totalplaytime = 0; // total play time (separate from all)
|
||||
gamedata->matchesplayed = 0; // SRB2Kart: matches played & finished
|
||||
|
||||
if (M_CheckParm("-nodata"))
|
||||
{
|
||||
|
|
@ -4344,7 +4335,7 @@ void G_LoadGameData(void)
|
|||
if (M_CheckParm("-resetdata"))
|
||||
{
|
||||
// Don't load, but do save. (essentially, reset)
|
||||
gamedataloaded = true;
|
||||
gamedata->loaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -4352,7 +4343,7 @@ void G_LoadGameData(void)
|
|||
if (!length)
|
||||
{
|
||||
// No gamedata. We can save a new one.
|
||||
gamedataloaded = true;
|
||||
gamedata->loaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -4371,8 +4362,16 @@ void G_LoadGameData(void)
|
|||
I_Error("Game data is not for Ring Racers v2.0.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder);
|
||||
}
|
||||
|
||||
totalplaytime = READUINT32(save_p);
|
||||
matchesplayed = READUINT32(save_p);
|
||||
versionMinor = READUINT8(save_p);
|
||||
if (versionMinor > GD_VERSIONMINOR)
|
||||
{
|
||||
Z_Free(savebuffer);
|
||||
save_p = NULL;
|
||||
I_Error("Game data is from the future! (expected %d, got %d)", GD_VERSIONMINOR, versionMinor);
|
||||
}
|
||||
|
||||
gamedata->totalplaytime = READUINT32(save_p);
|
||||
gamedata->matchesplayed = READUINT32(save_p);
|
||||
|
||||
{
|
||||
// Quick & dirty hash for what mod this save file is for.
|
||||
|
|
@ -4391,32 +4390,49 @@ void G_LoadGameData(void)
|
|||
{
|
||||
rtemp = READUINT8(save_p);
|
||||
for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
|
||||
emblemlocations[j+i].collected = ((rtemp >> j) & 1);
|
||||
i += j;
|
||||
}
|
||||
for (i = 0; i < MAXEXTRAEMBLEMS;)
|
||||
{
|
||||
rtemp = READUINT8(save_p);
|
||||
for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
|
||||
extraemblems[j+i].collected = ((rtemp >> j) & 1);
|
||||
gamedata->collected[j+i] = ((rtemp >> j) & 1);
|
||||
i += j;
|
||||
}
|
||||
for (i = 0; i < MAXUNLOCKABLES;)
|
||||
{
|
||||
rtemp = READUINT8(save_p);
|
||||
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
|
||||
unlockables[j+i].unlocked = ((rtemp >> j) & 1);
|
||||
gamedata->unlocked[j+i] = ((rtemp >> j) & 1);
|
||||
i += j;
|
||||
}
|
||||
for (i = 0; i < MAXUNLOCKABLES;)
|
||||
{
|
||||
rtemp = READUINT8(save_p);
|
||||
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
|
||||
gamedata->unlockpending[j+i] = ((rtemp >> j) & 1);
|
||||
i += j;
|
||||
}
|
||||
for (i = 0; i < MAXCONDITIONSETS;)
|
||||
{
|
||||
rtemp = READUINT8(save_p);
|
||||
for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
|
||||
conditionSets[j+i].achieved = ((rtemp >> j) & 1);
|
||||
gamedata->achieved[j+i] = ((rtemp >> j) & 1);
|
||||
i += j;
|
||||
}
|
||||
|
||||
timesBeaten = READUINT32(save_p);
|
||||
gamedata->challengegridwidth = READUINT16(save_p);
|
||||
Z_Free(gamedata->challengegrid);
|
||||
if (gamedata->challengegridwidth)
|
||||
{
|
||||
gamedata->challengegrid = Z_Malloc(
|
||||
(gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT * sizeof(UINT8)),
|
||||
PU_STATIC, NULL);
|
||||
for (i = 0; i < (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT); i++)
|
||||
{
|
||||
gamedata->challengegrid[i] = READUINT8(save_p);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gamedata->challengegrid = NULL;
|
||||
}
|
||||
|
||||
gamedata->timesBeaten = READUINT32(save_p);
|
||||
|
||||
// Main records
|
||||
numgamedatamapheaders = READUINT32(save_p);
|
||||
|
|
@ -4468,10 +4484,10 @@ void G_LoadGameData(void)
|
|||
// It used to do this much earlier, but this would cause the gamedata to
|
||||
// save over itself when it I_Errors from the corruption landing point below,
|
||||
// which can accidentally delete players' legitimate data if the code ever has any tiny mistakes!
|
||||
gamedataloaded = true;
|
||||
gamedata->loaded = true;
|
||||
|
||||
// Silent update unlockables in case they're out of sync with conditions
|
||||
M_SilentUpdateUnlockablesAndEmblems();
|
||||
M_UpdateUnlockablesAndExtraEmblems(false);
|
||||
|
||||
return;
|
||||
|
||||
|
|
@ -4497,10 +4513,22 @@ void G_SaveGameData(void)
|
|||
INT32 i, j;
|
||||
UINT8 btemp;
|
||||
|
||||
if (!gamedataloaded)
|
||||
if (!gamedata->loaded)
|
||||
return; // If never loaded (-nodata), don't save
|
||||
|
||||
length = (4+4+4+1+(MAXEMBLEMS)+MAXEXTRAEMBLEMS+MAXUNLOCKABLES+MAXCONDITIONSETS+4+4);
|
||||
if (usedCheats)
|
||||
{
|
||||
#ifdef DEVELOP
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Cheats used - Gamedata will not be saved.\n"));
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
length = (4+1+4+4+1+(MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+4+4+2);
|
||||
if (gamedata->challengegrid)
|
||||
{
|
||||
length += gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT;
|
||||
}
|
||||
length += nummapheaders * (MAXMAPLUMPNAME+1+4+4);
|
||||
|
||||
save_p = savebuffer = (UINT8 *)malloc(length);
|
||||
|
|
@ -4510,18 +4538,12 @@ void G_SaveGameData(void)
|
|||
return;
|
||||
}
|
||||
|
||||
if (usedCheats)
|
||||
{
|
||||
free(savebuffer);
|
||||
save_p = savebuffer = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// Version test
|
||||
|
||||
WRITEUINT32(save_p, GD_VERSIONCHECK); // 4
|
||||
WRITEUINT32(save_p, totalplaytime); // 4
|
||||
WRITEUINT32(save_p, matchesplayed); // 4
|
||||
WRITEUINT8(save_p, GD_VERSIONMINOR); // 1
|
||||
WRITEUINT32(save_p, gamedata->totalplaytime); // 4
|
||||
WRITEUINT32(save_p, gamedata->matchesplayed); // 4
|
||||
WRITEUINT32(save_p, quickncasehash(timeattackfolder, 64));
|
||||
|
||||
// To save space, use one bit per collected/achieved/unlocked flag
|
||||
|
|
@ -4529,36 +4551,52 @@ void G_SaveGameData(void)
|
|||
{
|
||||
btemp = 0;
|
||||
for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
|
||||
btemp |= (emblemlocations[j+i].collected << j);
|
||||
WRITEUINT8(save_p, btemp);
|
||||
i += j;
|
||||
}
|
||||
for (i = 0; i < MAXEXTRAEMBLEMS;) // MAXEXTRAEMBLEMS * 1;
|
||||
{
|
||||
btemp = 0;
|
||||
for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
|
||||
btemp |= (extraemblems[j+i].collected << j);
|
||||
WRITEUINT8(save_p, btemp);
|
||||
i += j;
|
||||
}
|
||||
for (i = 0; i < MAXUNLOCKABLES;) // MAXUNLOCKABLES * 1;
|
||||
{
|
||||
btemp = 0;
|
||||
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
|
||||
btemp |= (unlockables[j+i].unlocked << j);
|
||||
WRITEUINT8(save_p, btemp);
|
||||
i += j;
|
||||
}
|
||||
for (i = 0; i < MAXCONDITIONSETS;) // MAXCONDITIONSETS * 1;
|
||||
{
|
||||
btemp = 0;
|
||||
for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
|
||||
btemp |= (conditionSets[j+i].achieved << j);
|
||||
btemp |= (gamedata->collected[j+i] << j);
|
||||
WRITEUINT8(save_p, btemp);
|
||||
i += j;
|
||||
}
|
||||
|
||||
WRITEUINT32(save_p, timesBeaten); // 4
|
||||
// MAXUNLOCKABLES * 2;
|
||||
for (i = 0; i < MAXUNLOCKABLES;)
|
||||
{
|
||||
btemp = 0;
|
||||
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
|
||||
btemp |= (gamedata->unlocked[j+i] << j);
|
||||
WRITEUINT8(save_p, btemp);
|
||||
i += j;
|
||||
}
|
||||
for (i = 0; i < MAXUNLOCKABLES;)
|
||||
{
|
||||
btemp = 0;
|
||||
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
|
||||
btemp |= (gamedata->unlockpending[j+i] << j);
|
||||
WRITEUINT8(save_p, btemp);
|
||||
i += j;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXCONDITIONSETS;) // MAXCONDITIONSETS * 1;
|
||||
{
|
||||
btemp = 0;
|
||||
for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
|
||||
btemp |= (gamedata->achieved[j+i] << j);
|
||||
WRITEUINT8(save_p, btemp);
|
||||
i += j;
|
||||
}
|
||||
|
||||
if (gamedata->challengegrid) // 2 + gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT
|
||||
{
|
||||
WRITEUINT16(save_p, gamedata->challengegridwidth);
|
||||
for (i = 0; i < (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT); i++)
|
||||
{
|
||||
WRITEUINT8(save_p, gamedata->challengegrid[i]);
|
||||
}
|
||||
}
|
||||
else // 2
|
||||
{
|
||||
WRITEUINT16(save_p, 0);
|
||||
}
|
||||
|
||||
WRITEUINT32(save_p, gamedata->timesBeaten); // 4
|
||||
|
||||
// Main records
|
||||
WRITEUINT32(save_p, nummapheaders); // 4
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ static char hu_tick;
|
|||
//-------------------------------------------
|
||||
|
||||
patch_t *missingpat;
|
||||
patch_t *blanklvl;
|
||||
patch_t *blanklvl, *nolvl;
|
||||
|
||||
// song credits
|
||||
static patch_t *songcreditbg;
|
||||
|
|
@ -186,6 +186,7 @@ void HU_LoadGraphics(void)
|
|||
Font_Load();
|
||||
|
||||
HU_UpdatePatch(&blanklvl, "BLANKLVL");
|
||||
HU_UpdatePatch(&nolvl, "M_NOLVL");
|
||||
|
||||
HU_UpdatePatch(&songcreditbg, "K_SONGCR");
|
||||
|
||||
|
|
|
|||
29
src/info.c
29
src/info.c
|
|
@ -7999,7 +7999,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
8, // reactiontime
|
||||
0, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
|
|
@ -24202,6 +24202,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_GACHABOM
|
||||
-1, // doomednum
|
||||
S_ORBINAUT1, // spawnstate
|
||||
7, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_tossed, // seesound
|
||||
8, // reactiontime
|
||||
sfx_s3k49, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_ORBINAUT_DEAD,// deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_s3k5d, // deathsound
|
||||
64*FRACUNIT, // speed
|
||||
24*FRACUNIT, // radius
|
||||
32*FRACUNIT, // height
|
||||
0, // display offset
|
||||
100, // mass
|
||||
1, // damage
|
||||
sfx_s3k96, // activesound
|
||||
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_DUELBOMB
|
||||
2050, // doomednum
|
||||
S_SPB1, // spawnstate
|
||||
|
|
|
|||
|
|
@ -6442,6 +6442,8 @@ typedef enum mobj_type
|
|||
MT_SINK_SHIELD,
|
||||
MT_SINKTRAIL,
|
||||
|
||||
MT_GACHABOM,
|
||||
|
||||
MT_DUELBOMB, // Duel mode bombs
|
||||
|
||||
MT_BATTLEBUMPER, // Battle Mode bumpers
|
||||
|
|
|
|||
|
|
@ -1558,6 +1558,11 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|||
case KITEM_FLAMESHIELD:
|
||||
K_BotItemFlame(player, cmd);
|
||||
break;
|
||||
/*
|
||||
case KITEM_GACHABOM:
|
||||
K_BotItemGachabom(player, cmd);
|
||||
break;
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -415,6 +415,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
|
|||
case MT_SPB:
|
||||
case MT_BUBBLESHIELDTRAP:
|
||||
case MT_DUELBOMB:
|
||||
case MT_GACHABOM:
|
||||
K_AddDodgeObject(thing, side, 20);
|
||||
break;
|
||||
case MT_SHRINK_GUN:
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
|
|||
else if (t2->type == MT_BANANA || t2->type == MT_BANANA_SHIELD
|
||||
|| t2->type == MT_ORBINAUT || t2->type == MT_ORBINAUT_SHIELD
|
||||
|| t2->type == MT_JAWZ || t2->type == MT_JAWZ_SHIELD
|
||||
|| t2->type == MT_BALLHOG)
|
||||
|| t2->type == MT_BALLHOG || t2->type == MT_GACHABOM)
|
||||
{
|
||||
// Other Item Damage
|
||||
angle_t bounceangle = K_GetCollideAngle(t1, t2);
|
||||
|
|
@ -204,7 +204,7 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2)
|
|||
static mobj_t *grenade;
|
||||
static fixed_t explodedist;
|
||||
static boolean explodespin;
|
||||
static tic_t minehitlag;
|
||||
static INT32 minehitlag;
|
||||
|
||||
static inline boolean PIT_SSMineChecks(mobj_t *thing)
|
||||
{
|
||||
|
|
@ -272,6 +272,9 @@ void K_DoMineSearch(mobj_t *actor, fixed_t size)
|
|||
|
||||
static inline BlockItReturn_t PIT_SSMineExplode(mobj_t *thing)
|
||||
{
|
||||
const INT32 oldhitlag = thing->hitlag;
|
||||
INT32 lagadded;
|
||||
|
||||
if (grenade == NULL || P_MobjWasRemoved(grenade))
|
||||
return BMIT_ABORT; // There's the possibility these can chain react onto themselves after they've already died if there are enough all in one spot
|
||||
|
||||
|
|
@ -283,9 +286,13 @@ static inline BlockItReturn_t PIT_SSMineExplode(mobj_t *thing)
|
|||
if (PIT_SSMineChecks(thing) == true)
|
||||
return BMIT_CONTINUE;
|
||||
|
||||
if (P_DamageMobj(thing, grenade, grenade->target, 1, (explodespin ? DMG_NORMAL : DMG_EXPLODE)))
|
||||
P_DamageMobj(thing, grenade, grenade->target, 1, (explodespin ? DMG_NORMAL : DMG_EXPLODE));
|
||||
|
||||
lagadded = (thing->hitlag - oldhitlag);
|
||||
|
||||
if (lagadded > 0)
|
||||
{
|
||||
minehitlag = thing->hitlag;
|
||||
minehitlag = lagadded;
|
||||
}
|
||||
|
||||
return BMIT_CONTINUE;
|
||||
|
|
@ -353,7 +360,8 @@ boolean K_MineCollide(mobj_t *t1, mobj_t *t2)
|
|||
}
|
||||
}
|
||||
else if (t2->type == MT_ORBINAUT || t2->type == MT_JAWZ
|
||||
|| t2->type == MT_ORBINAUT_SHIELD || t2->type == MT_JAWZ_SHIELD)
|
||||
|| t2->type == MT_ORBINAUT_SHIELD || t2->type == MT_JAWZ_SHIELD
|
||||
|| t2->type == MT_GACHABOM)
|
||||
{
|
||||
// Bomb death
|
||||
angle_t bounceangle = K_GetCollideAngle(t1, t2);
|
||||
|
|
@ -391,6 +399,8 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2)
|
|||
|
||||
if (t2->player)
|
||||
{
|
||||
const INT32 oldhitlag = t2->hitlag;
|
||||
|
||||
if (t2->player->flashing)
|
||||
return true;
|
||||
|
||||
|
|
@ -402,6 +412,7 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2)
|
|||
{
|
||||
// Melt item
|
||||
S_StartSound(t2, sfx_s3k43);
|
||||
K_SetHitLagForObjects(t2, t1, 3, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -409,13 +420,13 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2)
|
|||
P_DamageMobj(t2, t1, t1->target, 1, DMG_TUMBLE);
|
||||
}
|
||||
|
||||
t1->reactiontime = t2->hitlag;
|
||||
t1->reactiontime = (t2->hitlag - oldhitlag);
|
||||
P_KillMobj(t1, t2, t2, DMG_NORMAL);
|
||||
}
|
||||
else if (t2->type == MT_BANANA || t2->type == MT_BANANA_SHIELD
|
||||
|| t2->type == MT_ORBINAUT || t2->type == MT_ORBINAUT_SHIELD
|
||||
|| t2->type == MT_JAWZ || t2->type == MT_JAWZ_SHIELD
|
||||
|| t2->type == MT_BALLHOG)
|
||||
|| t2->type == MT_BALLHOG || t2->type == MT_GACHABOM)
|
||||
{
|
||||
// Other Item Damage
|
||||
angle_t bounceangle = K_GetCollideAngle(t1, t2);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "r_skins.h"
|
||||
#include "p_local.h"
|
||||
#include "p_mobj.h"
|
||||
#include "m_cond.h"
|
||||
|
||||
INT32 numfollowers = 0;
|
||||
follower_t followers[MAXSKINS];
|
||||
|
|
@ -38,6 +39,50 @@ INT32 K_FollowerAvailable(const char *name)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
INT32 K_FollowerUsable(INT32 followernum)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
boolean K_FollowerUsable(INT32 skinnum)
|
||||
{
|
||||
// Unlike R_SkinUsable, not netsynced.
|
||||
// Solely used to prevent an invalid value being sent over the wire.
|
||||
UINT8 i;
|
||||
INT32 fid;
|
||||
|
||||
if (skinnum == -1 || demo.playback)
|
||||
{
|
||||
// Simplifies things elsewhere, since there's already plenty of checks for less-than-0...
|
||||
return true;
|
||||
}
|
||||
|
||||
// Determine if this follower is supposed to be unlockable or not
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
{
|
||||
if (unlockables[i].type != SECRET_FOLLOWER)
|
||||
continue;
|
||||
|
||||
fid = M_UnlockableFollowerNum(&unlockables[i]);
|
||||
|
||||
if (fid != skinnum)
|
||||
continue;
|
||||
|
||||
// i is now the unlockable index, we can use this later
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == MAXUNLOCKABLES)
|
||||
{
|
||||
// Didn't trip anything, so we can use this follower.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use the unlockables table directly
|
||||
// DEFINITELY not M_CheckNetUnlockByID
|
||||
return (boolean)(gamedata->unlocked[i]);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
|
||||
|
||||
|
|
|
|||
|
|
@ -115,6 +115,22 @@ extern followercategory_t followercategories[MAXFOLLOWERCATEGORIES];
|
|||
INT32 K_FollowerAvailable(const char *name);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_FollowerUsable(INT32 followernum);
|
||||
|
||||
Check if a follower is usable or not.
|
||||
|
||||
Input Arguments:-
|
||||
skinnum - The follower's skin ID
|
||||
|
||||
Return:-
|
||||
true if it was a valid follower,
|
||||
otherwise false.
|
||||
--------------------------------------------------*/
|
||||
|
||||
boolean K_FollowerUsable(INT32 skinnum);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
|
||||
|
||||
|
|
|
|||
|
|
@ -231,9 +231,9 @@ void K_InitGrandPrixBots(void)
|
|||
// Rearrange usable bot skins list to prevent gaps for randomised selection
|
||||
for (i = 0; i < usableskins; i++)
|
||||
{
|
||||
if (!(grabskins[i] == MAXSKINS /*|| K_SkinLocked(grabskins[i])*/))
|
||||
if (!(grabskins[i] == MAXSKINS || !R_SkinUsable(-1, grabskins[i], true)))
|
||||
continue;
|
||||
while (usableskins > i && (grabskins[usableskins] == MAXSKINS /*|| K_SkinLocked(grabskins[i])*/))
|
||||
while (usableskins > i && (grabskins[usableskins] == MAXSKINS || !R_SkinUsable(-1, grabskins[usableskins], true)))
|
||||
{
|
||||
usableskins--;
|
||||
}
|
||||
|
|
@ -557,9 +557,9 @@ void K_RetireBots(void)
|
|||
// Rearrange usable bot skins list to prevent gaps for randomised selection
|
||||
for (i = 0; i < usableskins; i++)
|
||||
{
|
||||
if (!(grabskins[i] == MAXSKINS /*|| K_SkinLocked(grabskins[i])*/))
|
||||
if (!(grabskins[i] == MAXSKINS || !R_SkinUsable(-1, grabskins[i], true)))
|
||||
continue;
|
||||
while (usableskins > i && (grabskins[usableskins] == MAXSKINS /*|| K_SkinLocked(grabskins[i])*/))
|
||||
while (usableskins > i && (grabskins[usableskins] == MAXSKINS || !R_SkinUsable(-1, grabskins[usableskins], true)))
|
||||
usableskins--;
|
||||
grabskins[i] = grabskins[usableskins];
|
||||
grabskins[usableskins] = MAXSKINS;
|
||||
|
|
|
|||
383
src/k_hud.c
383
src/k_hud.c
|
|
@ -70,7 +70,7 @@ static patch_t *kp_racefinish[6];
|
|||
static patch_t *kp_positionnum[10][2][2]; // number, overlay or underlay, splitscreen
|
||||
|
||||
static patch_t *kp_facenum[MAXPLAYERS+1];
|
||||
static patch_t *kp_facehighlight[8];
|
||||
patch_t *kp_facehighlight[8];
|
||||
|
||||
static patch_t *kp_nocontestminimap;
|
||||
static patch_t *kp_spbminimap;
|
||||
|
|
@ -173,6 +173,11 @@ static patch_t *kp_bossret[4];
|
|||
|
||||
static patch_t *kp_trickcool[2];
|
||||
|
||||
static patch_t *kp_capsuletarget_arrow[2][2];
|
||||
static patch_t *kp_capsuletarget_icon[2];
|
||||
static patch_t *kp_capsuletarget_far[2];
|
||||
static patch_t *kp_capsuletarget_near[8];
|
||||
|
||||
void K_LoadKartHUDGraphics(void)
|
||||
{
|
||||
INT32 i, j, k;
|
||||
|
|
@ -644,6 +649,39 @@ void K_LoadKartHUDGraphics(void)
|
|||
buffer[7] = '0'+((i+1)%10);
|
||||
HU_UpdatePatch(&kp_bossret[i], "%s", buffer);
|
||||
}
|
||||
|
||||
sprintf(buffer, "HCAPARxx");
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
buffer[6] = 'A'+i;
|
||||
|
||||
for (j = 0; j < 2; j++)
|
||||
{
|
||||
buffer[7] = '0'+j;
|
||||
HU_UpdatePatch(&kp_capsuletarget_arrow[i][j], "%s", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(buffer, "HUDCAPCx");
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
buffer[7] = '0'+i;
|
||||
HU_UpdatePatch(&kp_capsuletarget_icon[i], "%s", buffer);
|
||||
}
|
||||
|
||||
sprintf(buffer, "HUDCAPBx");
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
buffer[7] = '0'+i;
|
||||
HU_UpdatePatch(&kp_capsuletarget_far[i], "%s", buffer);
|
||||
}
|
||||
|
||||
sprintf(buffer, "HUDCAPAx");
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
buffer[7] = '0'+i;
|
||||
HU_UpdatePatch(&kp_capsuletarget_near[i], "%s", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// For the item toggle menu
|
||||
|
|
@ -699,6 +737,9 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny)
|
|||
return (tiny ? "K_ISDTRG" : "K_ITDTRG");
|
||||
case KITEM_GARDENTOP:
|
||||
return (tiny ? "K_ISGTOP" : "K_ITGTOP");
|
||||
case KITEM_GACHABOM: // temp
|
||||
case KRITEM_TRIPLEGACHABOM: // temp
|
||||
return (tiny ? "K_ISSINK" : "K_ITSINK");
|
||||
case KRITEM_TRIPLEORBINAUT:
|
||||
return (tiny ? "K_ISORBN" : "K_ITORB3");
|
||||
case KRITEM_QUADORBINAUT:
|
||||
|
|
@ -735,6 +776,7 @@ static patch_t *K_GetCachedItemPatch(INT32 item, UINT8 offset)
|
|||
kp_kitchensink,
|
||||
kp_droptarget,
|
||||
kp_gardentop,
|
||||
kp_kitchensink, // temp
|
||||
};
|
||||
|
||||
if (item == KITEM_SAD || (item > KITEM_NONE && item < NUMKARTITEMS))
|
||||
|
|
@ -778,7 +820,7 @@ void K_ObjectTracking(trackingResult_t *result, vector3_t *point, boolean revers
|
|||
fixed_t fovDiff, fov, fovTangent, fg;
|
||||
|
||||
fixed_t h;
|
||||
INT32 da;
|
||||
INT32 da, dp;
|
||||
|
||||
UINT8 cameraNum = R_GetViewNumber();
|
||||
|
||||
|
|
@ -839,6 +881,7 @@ void K_ObjectTracking(trackingResult_t *result, vector3_t *point, boolean revers
|
|||
// Determine viewpoint factors.
|
||||
h = R_PointToDist2(point->x, point->y, viewx, viewy);
|
||||
da = AngleDeltaSigned(viewpointAngle, R_PointToAngle2(point->x, point->y, viewx, viewy));
|
||||
dp = AngleDeltaSigned(viewpointAiming, R_PointToAngle2(0, 0, h, viewz));
|
||||
|
||||
if (reverse)
|
||||
{
|
||||
|
|
@ -849,6 +892,10 @@ void K_ObjectTracking(trackingResult_t *result, vector3_t *point, boolean revers
|
|||
result->x = FixedMul(NEWTAN(da), fg);
|
||||
result->y = FixedMul((NEWTAN(viewpointAiming) - FixedDiv((viewz - point->z), 1 + FixedMul(NEWCOS(da), h))), fg);
|
||||
|
||||
result->angle = da;
|
||||
result->pitch = dp;
|
||||
result->fov = fg;
|
||||
|
||||
// Rotate for screen roll...
|
||||
if (viewpointRoll)
|
||||
{
|
||||
|
|
@ -1014,6 +1061,40 @@ static void K_initKartHUD(void)
|
|||
}
|
||||
}
|
||||
|
||||
void K_DrawMapThumbnail(INT32 x, INT32 y, INT32 width, UINT32 flags, UINT16 map, UINT8 *colormap)
|
||||
{
|
||||
patch_t *PictureOfLevel = NULL;
|
||||
|
||||
if (map >= nummapheaders || !mapheaderinfo[map])
|
||||
{
|
||||
PictureOfLevel = nolvl;
|
||||
}
|
||||
else if (!mapheaderinfo[map]->thumbnailPic)
|
||||
{
|
||||
PictureOfLevel = blanklvl;
|
||||
}
|
||||
else
|
||||
{
|
||||
PictureOfLevel = mapheaderinfo[map]->thumbnailPic;
|
||||
}
|
||||
|
||||
K_DrawLikeMapThumbnail(x, y, width, flags, PictureOfLevel, colormap);
|
||||
}
|
||||
|
||||
void K_DrawLikeMapThumbnail(INT32 x, INT32 y, INT32 width, UINT32 flags, patch_t *patch, UINT8 *colormap)
|
||||
{
|
||||
if (flags & V_FLIP)
|
||||
x += width;
|
||||
|
||||
V_DrawFixedPatch(
|
||||
x, y,
|
||||
FixedDiv(width, (320 << FRACBITS)),
|
||||
flags,
|
||||
patch,
|
||||
colormap
|
||||
);
|
||||
}
|
||||
|
||||
// see also MT_PLAYERARROW mobjthinker in p_mobj.c
|
||||
static void K_drawKartItem(void)
|
||||
{
|
||||
|
|
@ -1465,7 +1546,7 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UI
|
|||
static boolean canplaysound = true;
|
||||
tic_t timetoreach = emblem->var;
|
||||
|
||||
if (emblem->collected)
|
||||
if (gamedata->collected[(emblem-emblemlocations)])
|
||||
{
|
||||
emblempic[curemb] = W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_CACHE);
|
||||
emblemcol[curemb] = R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE);
|
||||
|
|
@ -3041,6 +3122,217 @@ static void K_DrawWeakSpot(weakspotdraw_t *ws)
|
|||
V_DrawFixedPatch(ws->x, ws->y, FRACUNIT, 0, kp_bossret[j+1], colormap);
|
||||
}
|
||||
|
||||
typedef struct capsuletracking_s
|
||||
{
|
||||
mobj_t *mobj;
|
||||
vector3_t point;
|
||||
fixed_t camDist;
|
||||
} capsuletracking_t;
|
||||
|
||||
static void K_DrawCapsuleTracking(capsuletracking_t *caps)
|
||||
{
|
||||
trackingResult_t result = {0};
|
||||
INT32 timer = 0;
|
||||
|
||||
K_ObjectTracking(&result, &caps->point, false);
|
||||
|
||||
if (result.onScreen == false)
|
||||
{
|
||||
// Off-screen, draw alongside the borders of the screen.
|
||||
// Probably the most complicated thing.
|
||||
|
||||
INT32 scrVal = 240;
|
||||
vector2_t screenSize = {0};
|
||||
|
||||
INT32 borderSize = 7;
|
||||
vector2_t borderWin = {0};
|
||||
vector2_t borderDir = {0};
|
||||
fixed_t borderLen = FRACUNIT;
|
||||
|
||||
vector2_t arrowDir = {0};
|
||||
|
||||
vector2_t arrowPos = {0};
|
||||
patch_t *arrowPatch = NULL;
|
||||
INT32 arrowFlags = 0;
|
||||
|
||||
vector2_t capsulePos = {0};
|
||||
patch_t *capsulePatch = NULL;
|
||||
|
||||
timer = (leveltime / 3);
|
||||
|
||||
screenSize.x = vid.width / vid.dupx;
|
||||
screenSize.y = vid.height / vid.dupy;
|
||||
|
||||
if (r_splitscreen >= 2)
|
||||
{
|
||||
// Half-wide screens
|
||||
screenSize.x >>= 1;
|
||||
borderSize >>= 1;
|
||||
}
|
||||
|
||||
if (r_splitscreen >= 1)
|
||||
{
|
||||
// Half-tall screens
|
||||
screenSize.y >>= 1;
|
||||
}
|
||||
|
||||
scrVal = max(screenSize.x, screenSize.y) - 80;
|
||||
|
||||
borderWin.x = screenSize.x - borderSize;
|
||||
borderWin.y = screenSize.y - borderSize;
|
||||
|
||||
arrowDir.x = 0;
|
||||
arrowDir.y = P_MobjFlip(caps->mobj) * FRACUNIT;
|
||||
|
||||
// Simply pointing towards the result doesn't work, so inaccurate hack...
|
||||
borderDir.x = FixedMul(
|
||||
FixedMul(
|
||||
FINESINE((-result.angle >> ANGLETOFINESHIFT) & FINEMASK),
|
||||
FINECOSINE((-result.pitch >> ANGLETOFINESHIFT) & FINEMASK)
|
||||
),
|
||||
result.fov
|
||||
);
|
||||
|
||||
borderDir.y = FixedMul(
|
||||
FINESINE((-result.pitch >> ANGLETOFINESHIFT) & FINEMASK),
|
||||
result.fov
|
||||
);
|
||||
|
||||
borderLen = R_PointToDist2(0, 0, borderDir.x, borderDir.y);
|
||||
|
||||
if (borderLen > 0)
|
||||
{
|
||||
borderDir.x = FixedDiv(borderDir.x, borderLen);
|
||||
borderDir.y = FixedDiv(borderDir.y, borderLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Eh just put it at the bottom.
|
||||
borderDir.x = 0;
|
||||
borderDir.y = FRACUNIT;
|
||||
}
|
||||
|
||||
capsulePatch = kp_capsuletarget_icon[timer & 1];
|
||||
|
||||
if (abs(borderDir.x) > abs(borderDir.y))
|
||||
{
|
||||
// Horizontal arrow
|
||||
arrowPatch = kp_capsuletarget_arrow[1][timer & 1];
|
||||
arrowDir.y = 0;
|
||||
|
||||
if (borderDir.x < 0)
|
||||
{
|
||||
// LEFT
|
||||
arrowDir.x = -FRACUNIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// RIGHT
|
||||
arrowDir.x = FRACUNIT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Vertical arrow
|
||||
arrowPatch = kp_capsuletarget_arrow[0][timer & 1];
|
||||
arrowDir.x = 0;
|
||||
|
||||
if (borderDir.y < 0)
|
||||
{
|
||||
// UP
|
||||
arrowDir.y = -FRACUNIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// DOWN
|
||||
arrowDir.y = FRACUNIT;
|
||||
}
|
||||
}
|
||||
|
||||
arrowPos.x = (screenSize.x >> 1) + FixedMul(scrVal, borderDir.x);
|
||||
arrowPos.y = (screenSize.y >> 1) + FixedMul(scrVal, borderDir.y);
|
||||
|
||||
arrowPos.x = min(max(arrowPos.x, borderSize), borderWin.x) * FRACUNIT;
|
||||
arrowPos.y = min(max(arrowPos.y, borderSize), borderWin.y) * FRACUNIT;
|
||||
|
||||
capsulePos.x = arrowPos.x - (arrowDir.x * 12);
|
||||
capsulePos.y = arrowPos.y - (arrowDir.y * 12);
|
||||
|
||||
arrowPos.x -= (arrowPatch->width << FRACBITS) >> 1;
|
||||
arrowPos.y -= (arrowPatch->height << FRACBITS) >> 1;
|
||||
|
||||
capsulePos.x -= (capsulePatch->width << FRACBITS) >> 1;
|
||||
capsulePos.y -= (capsulePatch->height << FRACBITS) >> 1;
|
||||
|
||||
if (arrowDir.x < 0)
|
||||
{
|
||||
arrowPos.x += arrowPatch->width << FRACBITS;
|
||||
arrowFlags |= V_FLIP;
|
||||
}
|
||||
|
||||
if (arrowDir.y < 0)
|
||||
{
|
||||
arrowPos.y += arrowPatch->height << FRACBITS;
|
||||
arrowFlags |= V_VFLIP;
|
||||
}
|
||||
|
||||
V_DrawFixedPatch(
|
||||
capsulePos.x, capsulePos.y,
|
||||
FRACUNIT,
|
||||
V_SPLITSCREEN,
|
||||
capsulePatch, NULL
|
||||
);
|
||||
|
||||
V_DrawFixedPatch(
|
||||
arrowPos.x, arrowPos.y,
|
||||
FRACUNIT,
|
||||
V_SPLITSCREEN | arrowFlags,
|
||||
arrowPatch, NULL
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw simple overlay.
|
||||
const fixed_t farDistance = 1280*mapobjectscale;
|
||||
boolean useNear = (caps->camDist < farDistance);
|
||||
|
||||
patch_t *capsulePatch = NULL;
|
||||
vector2_t capsulePos = {0};
|
||||
|
||||
boolean visible = P_CheckSight(stplyr->mo, caps->mobj);
|
||||
|
||||
if (visible == false && (leveltime & 1))
|
||||
{
|
||||
// Flicker when not visible.
|
||||
return;
|
||||
}
|
||||
|
||||
capsulePos.x = result.x;
|
||||
capsulePos.y = result.y;
|
||||
|
||||
if (useNear == true)
|
||||
{
|
||||
timer = (leveltime / 2);
|
||||
capsulePatch = kp_capsuletarget_near[timer % 8];
|
||||
}
|
||||
else
|
||||
{
|
||||
timer = (leveltime / 3);
|
||||
capsulePatch = kp_capsuletarget_far[timer & 1];
|
||||
}
|
||||
|
||||
capsulePos.x -= (capsulePatch->width << FRACBITS) >> 1;
|
||||
capsulePos.y -= (capsulePatch->height << FRACBITS) >> 1;
|
||||
|
||||
V_DrawFixedPatch(
|
||||
capsulePos.x, capsulePos.y,
|
||||
FRACUNIT,
|
||||
V_SPLITSCREEN,
|
||||
capsulePatch, NULL
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void K_drawKartNameTags(void)
|
||||
{
|
||||
const fixed_t maxdistance = 8192*mapobjectscale;
|
||||
|
|
@ -3049,7 +3341,7 @@ static void K_drawKartNameTags(void)
|
|||
UINT8 tobesorted[MAXPLAYERS];
|
||||
fixed_t sortdist[MAXPLAYERS];
|
||||
UINT8 sortlen = 0;
|
||||
UINT8 i, j;
|
||||
size_t i, j;
|
||||
|
||||
if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo))
|
||||
{
|
||||
|
|
@ -3140,6 +3432,85 @@ static void K_drawKartNameTags(void)
|
|||
}
|
||||
}
|
||||
|
||||
if (battlecapsules == true)
|
||||
{
|
||||
#define MAX_CAPSULE_HUD 32
|
||||
capsuletracking_t capsuleList[MAX_CAPSULE_HUD];
|
||||
size_t capsuleListLen = 0;
|
||||
|
||||
mobj_t *mobj = NULL;
|
||||
mobj_t *next = NULL;
|
||||
|
||||
for (mobj = kitemcap; mobj; mobj = next)
|
||||
{
|
||||
capsuletracking_t *caps = NULL;
|
||||
|
||||
next = mobj->itnext;
|
||||
|
||||
if (mobj->health <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mobj->type != MT_BATTLECAPSULE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
caps = &capsuleList[capsuleListLen];
|
||||
|
||||
caps->mobj = mobj;
|
||||
caps->point.x = R_InterpolateFixed(mobj->old_x, mobj->x);
|
||||
caps->point.y = R_InterpolateFixed(mobj->old_y, mobj->y);
|
||||
caps->point.z = R_InterpolateFixed(mobj->old_z, mobj->z);
|
||||
caps->point.z += (mobj->height >> 1);
|
||||
caps->camDist = R_PointToDist2(c.x, c.y, caps->point.x, caps->point.y);
|
||||
|
||||
capsuleListLen++;
|
||||
|
||||
if (capsuleListLen >= MAX_CAPSULE_HUD)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (capsuleListLen > 0)
|
||||
{
|
||||
// Sort by distance from camera.
|
||||
if (capsuleListLen > 1)
|
||||
{
|
||||
for (i = 0; i < capsuleListLen-1; i++)
|
||||
{
|
||||
size_t swap = i;
|
||||
|
||||
for (j = i + 1; j < capsuleListLen; j++)
|
||||
{
|
||||
capsuletracking_t *cj = &capsuleList[j];
|
||||
capsuletracking_t *cSwap = &capsuleList[swap];
|
||||
|
||||
if (cj->camDist > cSwap->camDist)
|
||||
{
|
||||
swap = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (swap != i)
|
||||
{
|
||||
capsuletracking_t temp = capsuleList[swap];
|
||||
capsuleList[swap] = capsuleList[i];
|
||||
capsuleList[i] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < capsuleListLen; i++)
|
||||
{
|
||||
K_DrawCapsuleTracking(&capsuleList[i]);
|
||||
}
|
||||
}
|
||||
#undef MAX_CAPSULE_HUD
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
player_t *ntplayer = &players[i];
|
||||
|
|
@ -3254,7 +3625,7 @@ static void K_drawKartNameTags(void)
|
|||
{
|
||||
if (!(demo.playback == true && demo.freecam == true))
|
||||
{
|
||||
for (j = 0; j <= r_splitscreen; j++)
|
||||
for (j = 0; j <= (unsigned)r_splitscreen; j++)
|
||||
{
|
||||
if (ntplayer == &players[displayplayers[j]])
|
||||
{
|
||||
|
|
@ -3262,7 +3633,7 @@ static void K_drawKartNameTags(void)
|
|||
}
|
||||
}
|
||||
|
||||
if (j <= r_splitscreen && j != cnum)
|
||||
if (j <= (unsigned)r_splitscreen && j != cnum)
|
||||
{
|
||||
localindicator = j;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ struct trackingResult_t
|
|||
fixed_t x, y;
|
||||
fixed_t scale;
|
||||
boolean onScreen;
|
||||
INT32 angle, pitch;
|
||||
fixed_t fov;
|
||||
};
|
||||
|
||||
void K_ObjectTracking(trackingResult_t *result, vector3_t *point, boolean reverse);
|
||||
|
|
@ -36,5 +38,9 @@ void K_drawKartHUD(void);
|
|||
void K_drawKartFreePlay(void);
|
||||
void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode);
|
||||
void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer, INT32 hilicol);
|
||||
void K_DrawMapThumbnail(INT32 x, INT32 y, INT32 width, UINT32 flags, UINT16 map, UINT8 *colormap);
|
||||
void K_DrawLikeMapThumbnail(INT32 x, INT32 y, INT32 width, UINT32 flags, patch_t *patch, UINT8 *colormap);
|
||||
|
||||
extern patch_t *kp_facehighlight[8];
|
||||
|
||||
#endif
|
||||
|
|
|
|||
59
src/k_kart.c
59
src/k_kart.c
|
|
@ -436,6 +436,9 @@ SINT8 K_ItemResultToType(SINT8 getitem)
|
|||
case KRITEM_DUALJAWZ:
|
||||
return KITEM_JAWZ;
|
||||
|
||||
case KRITEM_TRIPLEGACHABOM:
|
||||
return KITEM_GACHABOM;
|
||||
|
||||
default:
|
||||
I_Error("Bad item cooldown redirect for result %d\n", getitem);
|
||||
break;
|
||||
|
|
@ -456,6 +459,7 @@ UINT8 K_ItemResultToAmount(SINT8 getitem)
|
|||
case KRITEM_TRIPLESNEAKER:
|
||||
case KRITEM_TRIPLEBANANA:
|
||||
case KRITEM_TRIPLEORBINAUT:
|
||||
case KRITEM_TRIPLEGACHABOM:
|
||||
return 3;
|
||||
|
||||
case KRITEM_QUADORBINAUT:
|
||||
|
|
@ -629,6 +633,7 @@ fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against)
|
|||
break;
|
||||
case MT_ORBINAUT:
|
||||
case MT_ORBINAUT_SHIELD:
|
||||
case MT_GACHABOM:
|
||||
case MT_DUELBOMB:
|
||||
if (against->player)
|
||||
weight = K_PlayerWeight(against, NULL);
|
||||
|
|
@ -4428,7 +4433,7 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I
|
|||
finalscale = source->scale;
|
||||
}
|
||||
|
||||
if (dir == -1 && (type == MT_ORBINAUT || type == MT_BALLHOG))
|
||||
if (dir == -1 && (type == MT_ORBINAUT || type == MT_GACHABOM || type == MT_BALLHOG))
|
||||
{
|
||||
// Backwards nerfs
|
||||
finalspeed /= 8;
|
||||
|
|
@ -4485,6 +4490,7 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I
|
|||
switch (type)
|
||||
{
|
||||
case MT_ORBINAUT:
|
||||
case MT_GACHABOM:
|
||||
Obj_OrbinautThrown(th, finalspeed, dir);
|
||||
break;
|
||||
case MT_JAWZ:
|
||||
|
|
@ -5332,6 +5338,12 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing,
|
|||
dir = defaultDir;
|
||||
}
|
||||
|
||||
if (mapthing == MT_GACHABOM && dir > 0)
|
||||
{
|
||||
// This item is both a missile and not!
|
||||
missile = false;
|
||||
}
|
||||
|
||||
if (missile) // Shootables
|
||||
{
|
||||
if (dir < 0 && mapthing != MT_SPB && mapthing != MT_GARDENTOP)
|
||||
|
|
@ -5399,6 +5411,16 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing,
|
|||
mo->rollangle = FixedAngle(P_RandomRange(PR_DECORATION, -180, 180) << FRACBITS);
|
||||
}
|
||||
|
||||
if (mapthing == MT_GACHABOM)
|
||||
{
|
||||
// Set dropped flag
|
||||
mo->flags2 |= MF2_AMBUSH;
|
||||
mo->movecount = 2;
|
||||
P_SetMobjState(mo, mo->info->deathstate);
|
||||
mo->tics = -1;
|
||||
mo->color = player->skincolor;
|
||||
}
|
||||
|
||||
// this is the small graphic effect that plops in you when you throw an item:
|
||||
throwmo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_FIREDITEM);
|
||||
P_SetTarget(&throwmo->target, player->mo);
|
||||
|
|
@ -8103,6 +8125,32 @@ void K_KartPlayerAfterThink(player_t *player)
|
|||
{
|
||||
K_LookForRings(player->mo);
|
||||
}
|
||||
|
||||
if (player->invulnhitlag > 0)
|
||||
{
|
||||
// Hitlag from what would normally be damage but the
|
||||
// player was invulnerable.
|
||||
//
|
||||
// If we're constantly getting hit the same number of
|
||||
// times, we're probably standing on a damage floor.
|
||||
//
|
||||
// Checking if we're hit more than before ensures
|
||||
// that:
|
||||
//
|
||||
// 1) repeating damage doesn't count
|
||||
// 2) new damage sources still count
|
||||
|
||||
if (player->timeshit <= player->timeshitprev)
|
||||
{
|
||||
if (!P_MobjWasRemoved(player->mo))
|
||||
{
|
||||
player->mo->hitlag -= player->invulnhitlag;
|
||||
player->mo->eflags &= ~(MFE_DAMAGEHITLAG);
|
||||
}
|
||||
}
|
||||
|
||||
player->invulnhitlag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
@ -10623,6 +10671,15 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
K_UpdateHnextList(player, true);
|
||||
}
|
||||
break;
|
||||
case KITEM_GACHABOM:
|
||||
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
|
||||
{
|
||||
K_ThrowKartItem(player, true, MT_GACHABOM, 0, 0, 0);
|
||||
K_PlayAttackTaunt(player->mo);
|
||||
player->itemamount--;
|
||||
K_UpdateHnextList(player, false);
|
||||
}
|
||||
break;
|
||||
case KITEM_SAD:
|
||||
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO
|
||||
&& !player->sadtimer)
|
||||
|
|
|
|||
91
src/k_menu.h
91
src/k_menu.h
|
|
@ -398,12 +398,16 @@ extern menu_t EXTRAS_ReplayStartDef;
|
|||
extern menuitem_t PAUSE_Main[];
|
||||
extern menu_t PAUSE_MainDef;
|
||||
|
||||
// EXTRAS
|
||||
extern menuitem_t MISC_Manual[];
|
||||
extern menu_t MISC_ManualDef;
|
||||
|
||||
extern menuitem_t MISC_Addons[];
|
||||
extern menu_t MISC_AddonsDef;
|
||||
|
||||
// MANUAL
|
||||
extern menuitem_t MISC_Manual[];
|
||||
extern menu_t MISC_ManualDef;
|
||||
extern menuitem_t MISC_ChallengesStatsDummyMenu[];
|
||||
extern menu_t MISC_ChallengesDef;
|
||||
extern menu_t MISC_StatisticsDef;
|
||||
|
||||
// We'll need this since we're gonna have to dynamically enable and disable options depending on which state we're in.
|
||||
typedef enum
|
||||
|
|
@ -593,6 +597,9 @@ extern struct setup_chargrid_s {
|
|||
UINT8 numskins;
|
||||
} setup_chargrid[9][9];
|
||||
|
||||
extern UINT8 setup_followercategories[MAXFOLLOWERCATEGORIES][2];
|
||||
extern UINT8 setup_numfollowercategories;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CSSTEP_NONE = 0,
|
||||
|
|
@ -650,9 +657,9 @@ extern UINT8 setup_maxpage;
|
|||
#define CSEXPLOSIONS 48
|
||||
|
||||
extern struct setup_explosions_s {
|
||||
UINT8 x, y;
|
||||
INT16 x, y;
|
||||
UINT8 tics;
|
||||
UINT8 color;
|
||||
UINT16 color;
|
||||
} setup_explosions[CSEXPLOSIONS];
|
||||
|
||||
typedef enum
|
||||
|
|
@ -688,23 +695,28 @@ extern struct cupgrid_s {
|
|||
boolean netgame; // Start the game in an actual server
|
||||
} cupgrid;
|
||||
|
||||
typedef struct levelsearch_s {
|
||||
UINT32 typeoflevel;
|
||||
cupheader_t *cup;
|
||||
boolean timeattack;
|
||||
boolean cupmode;
|
||||
boolean checklocked;
|
||||
} levelsearch_t;
|
||||
|
||||
extern struct levellist_s {
|
||||
SINT8 cursor;
|
||||
UINT16 y;
|
||||
UINT16 dest;
|
||||
cupheader_t *selectedcup;
|
||||
INT16 choosemap;
|
||||
UINT8 newgametype;
|
||||
UINT32 typeoflevel;
|
||||
boolean cupmode;
|
||||
boolean timeattack; // Setup time attack menu after picking
|
||||
levelsearch_t levelsearch;
|
||||
boolean netgame; // Start the game in an actual server
|
||||
} levellist;
|
||||
|
||||
boolean M_CanShowLevelInList(INT16 mapnum, UINT32 tol, cupheader_t *cup);
|
||||
UINT16 M_CountLevelsToShowInList(UINT32 tol, cupheader_t *cup);
|
||||
UINT16 M_GetFirstLevelInList(UINT8 *i, UINT32 tol, cupheader_t *cup);
|
||||
UINT16 M_GetNextLevelInList(UINT16 map, UINT8 *i, UINT32 tol, cupheader_t *cup);
|
||||
boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch);
|
||||
UINT16 M_CountLevelsToShowInList(levelsearch_t *levelsearch);
|
||||
UINT16 M_GetFirstLevelInList(UINT8 *i, levelsearch_t *levelsearch);
|
||||
UINT16 M_GetNextLevelInList(UINT16 mapnum, UINT8 *i, levelsearch_t *levelsearch);
|
||||
|
||||
void M_LevelSelectInit(INT32 choice);
|
||||
void M_CupSelectHandler(INT32 choice);
|
||||
|
|
@ -1081,6 +1093,59 @@ void M_DrawReplayStartMenu(void);
|
|||
#define LOCATIONSTRING2 "Visit \x88SRB2.ORG/MODS\x80 to get & make addons!"
|
||||
void M_DrawAddons(void);
|
||||
|
||||
// Challenges menu:
|
||||
#define UNLOCKTIME 5
|
||||
#define MAXUNLOCKTIME TICRATE
|
||||
#define RIGHTUNLOCKSCROLL 3
|
||||
#define LEFTUNLOCKSCROLL (RIGHTUNLOCKSCROLL-1)
|
||||
|
||||
#define CC_TOTAL 0
|
||||
#define CC_UNLOCKED 1
|
||||
#define CC_TALLY 2
|
||||
#define CC_ANIM 3
|
||||
#define CC_MAX 4
|
||||
|
||||
// Keep track of some pause menu data for visual goodness.
|
||||
extern struct challengesmenu_s {
|
||||
|
||||
tic_t ticker; // How long the menu's been open for
|
||||
INT16 offset; // To make the icons move smoothly when we transition!
|
||||
|
||||
UINT8 currentunlock;
|
||||
char *unlockcondition;
|
||||
|
||||
tic_t unlockanim;
|
||||
|
||||
SINT8 row, hilix, focusx;
|
||||
UINT8 col, hiliy;
|
||||
|
||||
UINT8 *extradata;
|
||||
|
||||
boolean pending;
|
||||
boolean requestnew;
|
||||
|
||||
UINT8 unlockcount[CC_MAX];
|
||||
|
||||
UINT8 fade;
|
||||
} challengesmenu;
|
||||
|
||||
menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu);
|
||||
void M_Challenges(INT32 choice);
|
||||
void M_DrawChallenges(void);
|
||||
void M_ChallengesTick(void);
|
||||
boolean M_ChallengesInputs(INT32 ch);
|
||||
|
||||
extern struct statisticsmenu_s {
|
||||
INT32 location;
|
||||
INT32 nummaps;
|
||||
INT32 maxscroll;
|
||||
UINT16 *maplist;
|
||||
} statisticsmenu;
|
||||
|
||||
void M_Statistics(INT32 choice);
|
||||
void M_DrawStatistics(void);
|
||||
boolean M_StatisticsInputs(INT32 ch);
|
||||
|
||||
// These defines make it a little easier to make menus
|
||||
#define DEFAULTMENUSTYLE(source, prev, x, y)\
|
||||
{\
|
||||
|
|
|
|||
|
|
@ -1496,14 +1496,14 @@ menuitem_t EXTRAS_Main[] =
|
|||
{IT_STRING | IT_CALL, "Addons", "Add files to customize your experience.",
|
||||
NULL, {.routine = M_Addons}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Challenges", "View the requirements for some of the secret content you can unlock!",
|
||||
NULL, {.routine = M_Challenges}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Replay Hut", "Play the replays you've saved throughout your many races & battles!",
|
||||
NULL, {.routine = M_ReplayHut}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Statistics", "Look back on some of your greatest achievements such as your playtime and wins!",
|
||||
NULL, {NULL}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_TRANSTEXT, "Extras Checklist", "View the requirement for some of the secret content you can unlock!",
|
||||
NULL, {NULL}, 0, 0},
|
||||
NULL, {.routine = M_Statistics}, 0, 0},
|
||||
};
|
||||
|
||||
// the extras menu essentially reuses the options menu stuff
|
||||
|
|
@ -1744,3 +1744,39 @@ menu_t MISC_AddonsDef = {
|
|||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
// Challenges.
|
||||
menuitem_t MISC_ChallengesStatsDummyMenu[] =
|
||||
{
|
||||
{IT_STRING | IT_CALL, "Back", NULL, NULL, {.routine = M_GoBack}, 0, 0},
|
||||
};
|
||||
|
||||
menu_t MISC_ChallengesDef = {
|
||||
sizeof (MISC_ChallengesStatsDummyMenu)/sizeof (menuitem_t),
|
||||
&MainDef,
|
||||
0,
|
||||
MISC_ChallengesStatsDummyMenu,
|
||||
BASEVIDWIDTH/2, 32,
|
||||
0, 0,
|
||||
98, 0,
|
||||
M_DrawChallenges,
|
||||
M_ChallengesTick,
|
||||
NULL,
|
||||
NULL,
|
||||
M_ChallengesInputs,
|
||||
};
|
||||
|
||||
menu_t MISC_StatisticsDef = {
|
||||
sizeof (MISC_ChallengesStatsDummyMenu)/sizeof (menuitem_t),
|
||||
&MainDef,
|
||||
0,
|
||||
MISC_ChallengesStatsDummyMenu,
|
||||
280, 185,
|
||||
0, 0,
|
||||
98, 0,
|
||||
M_DrawStatistics,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
M_StatisticsInputs,
|
||||
};
|
||||
|
|
|
|||
849
src/k_menudraw.c
849
src/k_menudraw.c
File diff suppressed because it is too large
Load diff
966
src/k_menufunc.c
966
src/k_menufunc.c
File diff suppressed because it is too large
Load diff
|
|
@ -418,11 +418,7 @@ void K_CashInPowerLevels(void)
|
|||
{
|
||||
pr->powerlevels[powerType] = clientpowerlevels[i][powerType];
|
||||
|
||||
if (M_UpdateUnlockablesAndExtraEmblems())
|
||||
{
|
||||
S_StartSound(NULL, sfx_ncitem);
|
||||
}
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
}
|
||||
|
|
@ -642,11 +638,7 @@ void K_PlayerForfeit(UINT8 playerNum, boolean pointLoss)
|
|||
{
|
||||
pr->powerlevels[powerType] = yourPower + inc;
|
||||
|
||||
if (M_UpdateUnlockablesAndExtraEmblems())
|
||||
{
|
||||
S_StartSound(NULL, sfx_ncitem);
|
||||
}
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,12 +98,14 @@ static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
|
|||
{ 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink
|
||||
{ 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target
|
||||
{ 0, 0, 0, 3, 5, 0, 0, 0 }, // Garden Top
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 }, // Gachabom
|
||||
{ 0, 0, 2, 2, 2, 0, 0, 0 }, // Sneaker x2
|
||||
{ 0, 0, 0, 0, 4, 4, 4, 0 }, // Sneaker x3
|
||||
{ 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3
|
||||
{ 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3
|
||||
{ 0, 0, 0, 2, 0, 0, 0, 0 }, // Orbinaut x4
|
||||
{ 0, 0, 1, 2, 1, 0, 0, 0 } // Jawz x2
|
||||
{ 0, 0, 1, 2, 1, 0, 0, 0 }, // Jawz x2
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 } // Gachabom x3
|
||||
};
|
||||
|
||||
static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS-1][2] =
|
||||
|
|
@ -130,12 +132,14 @@ static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS-1][2] =
|
|||
{ 0, 0 }, // Kitchen Sink
|
||||
{ 2, 0 }, // Drop Target
|
||||
{ 4, 0 }, // Garden Top
|
||||
{ 0, 0 }, // Gachabom
|
||||
{ 0, 0 }, // Sneaker x2
|
||||
{ 0, 1 }, // Sneaker x3
|
||||
{ 0, 0 }, // Banana x3
|
||||
{ 2, 0 }, // Orbinaut x3
|
||||
{ 1, 1 }, // Orbinaut x4
|
||||
{ 5, 1 } // Jawz x2
|
||||
{ 5, 1 }, // Jawz x2
|
||||
{ 0, 0 } // Gachabom x3
|
||||
};
|
||||
|
||||
static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] =
|
||||
|
|
@ -179,13 +183,14 @@ static kartitems_t K_KartItemReelTimeAttack[] =
|
|||
|
||||
static kartitems_t K_KartItemReelBreakTheCapsules[] =
|
||||
{
|
||||
KRITEM_TRIPLEORBINAUT,
|
||||
KITEM_BANANA,
|
||||
KITEM_GACHABOM,
|
||||
KRITEM_TRIPLEGACHABOM,
|
||||
KITEM_NONE
|
||||
};
|
||||
|
||||
static kartitems_t K_KartItemReelBoss[] =
|
||||
{
|
||||
// FIXME: gachabom...?
|
||||
KITEM_ORBINAUT,
|
||||
KITEM_BANANA,
|
||||
KITEM_NONE
|
||||
|
|
|
|||
|
|
@ -2351,7 +2351,7 @@ static int lib_rSetPlayerSkin(lua_State *L)
|
|||
return luaL_error(L, "skin %s (argument 2) is not loaded", skinname);
|
||||
}
|
||||
|
||||
if (!R_SkinUsable(j, i))
|
||||
if (!R_SkinUsable(j, i, false))
|
||||
return luaL_error(L, "skin %d (argument 2) not usable - check with R_SkinUsable(player_t, skin) first.", i);
|
||||
SetPlayerSkinByNum(j, i);
|
||||
return 0;
|
||||
|
|
@ -2381,7 +2381,7 @@ static int lib_rSkinUsable(lua_State *L)
|
|||
return luaL_error(L, "skin %s (argument 2) is not loaded", skinname);
|
||||
}
|
||||
|
||||
lua_pushboolean(L, R_SkinUsable(j, i));
|
||||
lua_pushboolean(L, R_SkinUsable(j, i, false));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2449,8 +2449,6 @@ static int mapheaderinfo_get(lua_State *L)
|
|||
lua_pushinteger(L, header->palette);
|
||||
else if (fastcmp(field,"numlaps"))
|
||||
lua_pushinteger(L, header->numlaps);
|
||||
else if (fastcmp(field,"unlockrequired"))
|
||||
lua_pushinteger(L, header->unlockrequired);
|
||||
else if (fastcmp(field,"levelselect"))
|
||||
lua_pushinteger(L, header->levelselect);
|
||||
else if (fastcmp(field,"levelflags"))
|
||||
|
|
|
|||
|
|
@ -669,7 +669,7 @@ static int mobj_set(lua_State *L)
|
|||
for (i = 0; i < numskins; i++)
|
||||
if (fastcmp(skins[i].name, skin))
|
||||
{
|
||||
if (!mo->player || R_SkinUsable(mo->player-players, i))
|
||||
if (!mo->player || R_SkinUsable(mo->player-players, i, false))
|
||||
mo->skin = &skins[i];
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,6 +232,8 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->spinouttimer);
|
||||
else if (fastcmp(field,"instashield"))
|
||||
lua_pushinteger(L, plr->instashield);
|
||||
else if (fastcmp(field,"invulnhitlag"))
|
||||
lua_pushinteger(L, plr->invulnhitlag);
|
||||
else if (fastcmp(field,"wipeoutslow"))
|
||||
lua_pushinteger(L, plr->wipeoutslow);
|
||||
else if (fastcmp(field,"justbumped"))
|
||||
|
|
@ -408,8 +410,6 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->skincolor);
|
||||
else if (fastcmp(field,"skin"))
|
||||
lua_pushinteger(L, plr->skin);
|
||||
else if (fastcmp(field,"availabilities"))
|
||||
lua_pushinteger(L, plr->availabilities);
|
||||
else if (fastcmp(field,"fakeskin"))
|
||||
lua_pushinteger(L, plr->fakeskin);
|
||||
else if (fastcmp(field,"lastfakeskin"))
|
||||
|
|
@ -474,6 +474,10 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->lastsidehit);
|
||||
else if (fastcmp(field,"lastlinehit"))
|
||||
lua_pushinteger(L, plr->lastlinehit);
|
||||
else if (fastcmp(field,"timeshit"))
|
||||
lua_pushinteger(L, plr->timeshit);
|
||||
else if (fastcmp(field,"timeshitprev"))
|
||||
lua_pushinteger(L, plr->timeshitprev);
|
||||
else if (fastcmp(field,"onconveyor"))
|
||||
lua_pushinteger(L, plr->onconveyor);
|
||||
else if (fastcmp(field,"awayviewmobj"))
|
||||
|
|
@ -577,8 +581,6 @@ static int player_set(lua_State *L)
|
|||
}
|
||||
else if (fastcmp(field,"skin"))
|
||||
return NOSET;
|
||||
else if (fastcmp(field,"availabilities"))
|
||||
return NOSET;
|
||||
else if (fastcmp(field,"fakeskin"))
|
||||
return NOSET;
|
||||
else if (fastcmp(field,"lastfakeskin"))
|
||||
|
|
@ -608,6 +610,8 @@ static int player_set(lua_State *L)
|
|||
plr->spinouttimer = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"instashield"))
|
||||
plr->instashield = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"invulnhitlag"))
|
||||
plr->invulnhitlag = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"wipeoutslow"))
|
||||
plr->wipeoutslow = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"justbumped"))
|
||||
|
|
@ -834,6 +838,10 @@ static int player_set(lua_State *L)
|
|||
plr->lastsidehit = (INT16)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"lastlinehit"))
|
||||
plr->lastlinehit = (INT16)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"timeshit"))
|
||||
plr->timeshit = (UINT8)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"timeshitprev"))
|
||||
plr->timeshitprev = (UINT8)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"onconveyor"))
|
||||
plr->onconveyor = (INT32)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"awayviewmobj"))
|
||||
|
|
|
|||
|
|
@ -403,8 +403,6 @@ int LUA_WriteGlobals(lua_State *L, const char *word)
|
|||
skincolor_bluering = (UINT16)luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word, "emeralds"))
|
||||
emeralds = (UINT16)luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word, "token"))
|
||||
token = (UINT32)luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word, "gravity"))
|
||||
gravity = (fixed_t)luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word, "stoppedclock"))
|
||||
|
|
|
|||
|
|
@ -76,16 +76,16 @@ static UINT8 cheatf_warp(void)
|
|||
{
|
||||
if (!unlockables[i].conditionset)
|
||||
continue;
|
||||
if (!unlockables[i].unlocked)
|
||||
if (!gamedata->unlocked[i])
|
||||
{
|
||||
unlockables[i].unlocked = true;
|
||||
gamedata->unlocked[i] = true;
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
G_SaveGameData(); //G_SetUsedCheats();
|
||||
G_SetUsedCheats();
|
||||
S_StartSound(0, sfx_kc42);
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +111,7 @@ static UINT8 cheatf_devmode(void)
|
|||
// Just unlock all the things and turn on -debug and console devmode.
|
||||
G_SetUsedCheats();
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
unlockables[i].unlocked = true;
|
||||
gamedata->unlocked[i] = true;
|
||||
devparm = true;
|
||||
cht_debug |= 0x8000;
|
||||
|
||||
|
|
|
|||
1215
src/m_cond.c
1215
src/m_cond.c
File diff suppressed because it is too large
Load diff
184
src/m_cond.h
184
src/m_cond.h
|
|
@ -30,9 +30,9 @@ typedef enum
|
|||
UC_MAPENCORE, // MAPENCORE [map number]
|
||||
UC_MAPTIME, // MAPTIME [map number] [time to beat, tics]
|
||||
UC_TRIGGER, // TRIGGER [trigger number]
|
||||
UC_TOTALEMBLEMS, // TOTALEMBLEMS [number of emblems]
|
||||
UC_TOTALMEDALS, // TOTALMEDALS [number of emblems]
|
||||
UC_EMBLEM, // EMBLEM [emblem number]
|
||||
UC_EXTRAEMBLEM, // EXTRAEMBLEM [extra emblem number]
|
||||
UC_UNLOCKABLE, // UNLOCKABLE [unlockable number]
|
||||
UC_CONDITIONSET, // CONDITIONSET [condition set number]
|
||||
} conditiontype_t;
|
||||
|
||||
|
|
@ -52,97 +52,147 @@ struct conditionset_t
|
|||
{
|
||||
UINT32 numconditions; /// <- number of conditions.
|
||||
condition_t *condition; /// <- All conditionals to be checked.
|
||||
UINT8 achieved; /// <- Whether this conditional has been achieved already or not.
|
||||
/// (Conditional checking is skipped if true -- it's assumed you can't relock an unlockable)
|
||||
};
|
||||
|
||||
// Emblem information
|
||||
#define ET_GLOBAL 0 // Emblem with a position in space
|
||||
#define ET_MAP 1 // Beat the map
|
||||
#define ET_TIME 2 // Get the time
|
||||
#define ET_GLOBAL 0 // Emblem with a position in space
|
||||
#define ET_MAP 1 // Beat the map
|
||||
#define ET_TIME 2 // Get the time
|
||||
//#define ET_DEVTIME 3 // Time, but the value is tied to a Time Trial demo, not pre-defined
|
||||
|
||||
// Global emblem flags
|
||||
// (N/A to Kart yet)
|
||||
//#define GE_OH 1
|
||||
#define GE_NOTMEDAL 1 // Doesn't count towards number of medals
|
||||
#define GE_TIMED 2 // Disappears after var time
|
||||
|
||||
// Map emblem flags
|
||||
#define ME_ENCORE 1
|
||||
#define ME_ENCORE 1 // Achieve in Encore
|
||||
|
||||
struct emblem_t
|
||||
{
|
||||
UINT8 type; ///< Emblem type
|
||||
INT16 tag; ///< Tag of emblem mapthing
|
||||
char * level; ///< Level on which this emblem can be found.
|
||||
UINT8 sprite; ///< emblem sprite to use, 0 - 25
|
||||
UINT16 color; ///< skincolor to use
|
||||
INT32 var; ///< If needed, specifies information on the target amount to achieve (or target skin)
|
||||
char hint[110]; ///< Hint for emblem hints menu
|
||||
UINT8 collected; ///< Do you have this emblem?
|
||||
};
|
||||
struct extraemblem_t
|
||||
{
|
||||
char name[20]; ///< Name of the goal (used in the "emblem awarded" cecho)
|
||||
char description[40]; ///< Description of goal (used in statistics)
|
||||
UINT8 conditionset; ///< Condition set that awards this emblem.
|
||||
UINT8 showconditionset; ///< Condition set that shows this emblem.
|
||||
UINT8 sprite; ///< emblem sprite to use, 0 - 25
|
||||
UINT16 color; ///< skincolor to use
|
||||
UINT8 collected; ///< Do you have this emblem?
|
||||
UINT8 type; ///< Emblem type
|
||||
INT16 tag; ///< Tag of emblem mapthing
|
||||
char *level; ///< Level on which this emblem can be found.
|
||||
INT16 levelCache; ///< Stored G_MapNumber()+1 result
|
||||
UINT8 sprite; ///< emblem sprite to use, 0 - 25
|
||||
UINT16 color; ///< skincolor to use
|
||||
INT32 flags; ///< GE or ME constants
|
||||
INT32 var; ///< If needed, specifies extra information
|
||||
char *stringVar; ///< String version
|
||||
};
|
||||
|
||||
// Unlockable information
|
||||
struct unlockable_t
|
||||
{
|
||||
char name[64];
|
||||
char objective[64];
|
||||
char *icon;
|
||||
UINT16 color;
|
||||
UINT8 conditionset;
|
||||
UINT8 showconditionset;
|
||||
INT16 type;
|
||||
INT16 variable;
|
||||
UINT8 nocecho;
|
||||
UINT8 nochecklist;
|
||||
UINT8 unlocked;
|
||||
char *stringVar;
|
||||
INT16 stringVarCache;
|
||||
UINT8 majorunlock;
|
||||
};
|
||||
|
||||
#define SECRET_NONE 0 // Does nil. Use with levels locked by UnlockRequired
|
||||
#define SECRET_HEADER 1 // Does nothing on its own, just serves as a header for the menu
|
||||
typedef enum
|
||||
{
|
||||
SECRET_NONE = 0, // Does nil, useful as a default only
|
||||
|
||||
#define SECRET_SKIN 2 // Allow this character to be selected
|
||||
#define SECRET_WARP 3 // Selectable warp
|
||||
#define SECRET_LEVELSELECT 4 // Selectable level select
|
||||
// One step above bragging rights
|
||||
SECRET_EXTRAMEDAL, // Extra medal for your counter
|
||||
|
||||
#define SECRET_TIMEATTACK 5 // Enables Time Attack on the main menu
|
||||
#define SECRET_BREAKTHECAPSULES 6 // Enables Break the Capsules on the main menu
|
||||
#define SECRET_SOUNDTEST 7 // Sound Test
|
||||
#define SECRET_CREDITS 8 // Enables Credits
|
||||
// Level restrictions
|
||||
SECRET_CUP, // Permit access to entire cup (overrides SECRET_MAP)
|
||||
SECRET_MAP, // Permit access to single map
|
||||
|
||||
#define SECRET_ITEMFINDER 9 // Enables Item Finder/Emblem Radar
|
||||
#define SECRET_EMBLEMHINTS 10 // Enables Emblem Hints
|
||||
// Player restrictions
|
||||
SECRET_SKIN, // Permit this character
|
||||
SECRET_FOLLOWER, // Permit this follower
|
||||
|
||||
#define SECRET_ENCORE 11 // Enables Encore mode cvar
|
||||
#define SECRET_HARDSPEED 12 // Enables Hard gamespeed
|
||||
#define SECRET_HELLATTACK 13 // Map Hell in record attack
|
||||
// Difficulty restrictions
|
||||
SECRET_HARDSPEED, // Permit Hard gamespeed
|
||||
SECRET_ENCORE, // Permit Encore option
|
||||
SECRET_LEGACYBOXRUMMAGE, // Permit the Legacy Box for record attack, etc
|
||||
|
||||
#define SECRET_PANDORA 14 // Enables Pandora's Box
|
||||
// Menu restrictions
|
||||
SECRET_TIMEATTACK, // Permit Time attack
|
||||
SECRET_BREAKTHECAPSULES, // Permit SP Capsules
|
||||
SECRET_SOUNDTEST, // Permit Sound Test
|
||||
SECRET_ALTTITLE, // Permit alternate titlescreen
|
||||
|
||||
// Assist restrictions
|
||||
SECRET_ITEMFINDER, // Permit locating in-level secrets
|
||||
} secrettype_t;
|
||||
|
||||
// If you have more secrets than these variables allow in your game,
|
||||
// you seriously need to get a life.
|
||||
#define MAXCONDITIONSETS 128
|
||||
#define MAXCONDITIONSETS UINT8_MAX
|
||||
#define MAXEMBLEMS 512
|
||||
#define MAXEXTRAEMBLEMS 16
|
||||
#define MAXUNLOCKABLES 32
|
||||
#define MAXUNLOCKABLES MAXCONDITIONSETS
|
||||
|
||||
#define CHALLENGEGRIDHEIGHT 5
|
||||
#ifdef DEVELOP
|
||||
#define CHALLENGEGRIDLOOPWIDTH 3
|
||||
#else
|
||||
#define CHALLENGEGRIDLOOPWIDTH (BASEVIDWIDTH/16)
|
||||
#endif
|
||||
#define challengegridloops (gamedata->challengegridwidth >= CHALLENGEGRIDLOOPWIDTH)
|
||||
|
||||
// GAMEDATA STRUCTURE
|
||||
// Everything that would get saved in gamedata.dat
|
||||
struct gamedata_t
|
||||
{
|
||||
// WHENEVER OR NOT WE'RE READY TO SAVE
|
||||
boolean loaded;
|
||||
|
||||
// CONDITION SETS ACHIEVED
|
||||
boolean achieved[MAXCONDITIONSETS];
|
||||
|
||||
// EMBLEMS COLLECTED
|
||||
boolean collected[MAXEMBLEMS];
|
||||
|
||||
// UNLOCKABLES UNLOCKED
|
||||
boolean unlocked[MAXUNLOCKABLES];
|
||||
boolean unlockpending[MAXUNLOCKABLES];
|
||||
|
||||
// CHALLENGE GRID
|
||||
UINT16 challengegridwidth;
|
||||
UINT8 *challengegrid;
|
||||
|
||||
// # OF TIMES THE GAME HAS BEEN BEATEN
|
||||
UINT32 timesBeaten;
|
||||
|
||||
// PLAY TIME
|
||||
UINT32 totalplaytime;
|
||||
UINT32 matchesplayed;
|
||||
};
|
||||
|
||||
extern gamedata_t *gamedata;
|
||||
|
||||
// Netsynced functional alternative to gamedata->unlocked
|
||||
extern boolean netUnlocked[MAXUNLOCKABLES];
|
||||
|
||||
extern conditionset_t conditionSets[MAXCONDITIONSETS];
|
||||
extern emblem_t emblemlocations[MAXEMBLEMS];
|
||||
extern extraemblem_t extraemblems[MAXEXTRAEMBLEMS];
|
||||
extern unlockable_t unlockables[MAXUNLOCKABLES];
|
||||
|
||||
extern INT32 numemblems;
|
||||
extern INT32 numextraemblems;
|
||||
|
||||
extern UINT32 unlocktriggers;
|
||||
|
||||
void M_NewGameDataStruct(void);
|
||||
|
||||
// Challenges menu stuff
|
||||
void M_PopulateChallengeGrid(void);
|
||||
UINT8 *M_ChallengeGridExtraData(void);
|
||||
#define CHE_NONE 0
|
||||
#define CHE_HINT 1
|
||||
#define CHE_CONNECTEDLEFT (1<<1)
|
||||
#define CHE_CONNECTEDUP (1<<2)
|
||||
#define CHE_DONTDRAW (CHE_CONNECTEDLEFT|CHE_CONNECTEDUP)
|
||||
char *M_BuildConditionSetString(UINT8 unlockid);
|
||||
#define DESCRIPTIONWIDTH 170
|
||||
|
||||
// Condition set setup
|
||||
void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2);
|
||||
|
||||
|
|
@ -151,30 +201,36 @@ void M_ClearConditionSet(UINT8 set);
|
|||
void M_ClearSecrets(void);
|
||||
|
||||
// Updating conditions and unlockables
|
||||
void M_CheckUnlockConditions(void);
|
||||
UINT8 M_CheckCondition(condition_t *cn);
|
||||
UINT8 M_UpdateUnlockablesAndExtraEmblems(void);
|
||||
void M_SilentUpdateUnlockablesAndEmblems(void);
|
||||
boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud);
|
||||
UINT8 M_GetNextAchievedUnlock(void);
|
||||
UINT8 M_CheckLevelEmblems(void);
|
||||
UINT8 M_CompletionEmblems(void);
|
||||
|
||||
// Checking unlockable status
|
||||
UINT8 M_AnySecretUnlocked(void);
|
||||
UINT8 M_SecretUnlocked(INT32 type);
|
||||
UINT8 M_MapLocked(INT32 mapnum);
|
||||
INT32 M_CountEmblems(void);
|
||||
boolean M_CheckNetUnlockByID(UINT8 unlockid);
|
||||
boolean M_SecretUnlocked(INT32 type, boolean local);
|
||||
boolean M_CupLocked(cupheader_t *cup);
|
||||
boolean M_MapLocked(INT32 mapnum);
|
||||
INT32 M_CountMedals(boolean all, boolean extraonly);
|
||||
|
||||
// Emblem shit
|
||||
emblem_t *M_GetLevelEmblems(INT32 mapnum);
|
||||
skincolornum_t M_GetEmblemColor(emblem_t *em);
|
||||
const char *M_GetEmblemPatch(emblem_t *em, boolean big);
|
||||
skincolornum_t M_GetExtraEmblemColor(extraemblem_t *em);
|
||||
const char *M_GetExtraEmblemPatch(extraemblem_t *em, boolean big);
|
||||
|
||||
// If you're looking to compare stats for unlocks or what not, use these
|
||||
// They stop checking upon reaching the target number so they
|
||||
// should be (theoretically?) slightly faster.
|
||||
UINT8 M_GotEnoughEmblems(INT32 number);
|
||||
UINT8 M_GotEnoughMedals(INT32 number);
|
||||
UINT8 M_GotLowEnoughTime(INT32 tictime);
|
||||
|
||||
#define M_Achieved(a) ((a) >= MAXCONDITIONSETS || conditionSets[a].achieved)
|
||||
INT32 M_UnlockableSkinNum(unlockable_t *unlock);
|
||||
INT32 M_UnlockableFollowerNum(unlockable_t *unlock);
|
||||
cupheader_t *M_UnlockableCup(unlockable_t *unlock);
|
||||
UINT16 M_UnlockableMapNum(unlockable_t *unlock);
|
||||
|
||||
INT32 M_EmblemSkinNum(emblem_t *emblem);
|
||||
UINT16 M_EmblemMapNum(emblem_t *emblem);
|
||||
|
||||
#define M_Achieved(a) ((a) >= MAXCONDITIONSETS || gamedata->achieved[a])
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#include "doomdef.h"
|
||||
#include "doomtype.h"
|
||||
#include "doomstat.h" // totalplaytime
|
||||
#include "m_cond.h" // gamedata->totalplaytime
|
||||
|
||||
#include "m_random.h"
|
||||
#include "m_fixed.h"
|
||||
|
|
@ -372,5 +372,5 @@ void P_ClearRandom(UINT32 seed)
|
|||
*/
|
||||
UINT32 M_RandomizedSeed(void)
|
||||
{
|
||||
return ((totalplaytime & 0xFFFF) << 16) | M_RandomFixed();
|
||||
return ((gamedata->totalplaytime & 0xFFFF) << 16) | M_RandomFixed();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
|
|||
if (t2->player)
|
||||
{
|
||||
if ((t2->player->flashing > 0 && t2->hitlag == 0)
|
||||
&& !(t1->type == MT_ORBINAUT || t1->type == MT_JAWZ))
|
||||
&& !(t1->type == MT_ORBINAUT || t1->type == MT_JAWZ || t1->type == MT_GACHABOM))
|
||||
return true;
|
||||
|
||||
if (t2->player->hyudorotimer)
|
||||
|
|
@ -209,7 +209,7 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
|
|||
else if (t2->type == MT_ORBINAUT || t2->type == MT_JAWZ
|
||||
|| t2->type == MT_ORBINAUT_SHIELD || t2->type == MT_JAWZ_SHIELD
|
||||
|| t2->type == MT_BANANA || t2->type == MT_BANANA_SHIELD
|
||||
|| t2->type == MT_BALLHOG)
|
||||
|| t2->type == MT_BALLHOG || t2->type == MT_GACHABOM)
|
||||
{
|
||||
// Other Item Damage
|
||||
angle_t bounceangle = K_GetCollideAngle(t1, t2);
|
||||
|
|
|
|||
103
src/p_inter.c
103
src/p_inter.c
|
|
@ -157,6 +157,40 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon)
|
|||
return true;
|
||||
}
|
||||
|
||||
boolean P_CanPickupEmblem(player_t *player, INT32 emblemID)
|
||||
{
|
||||
if (emblemID < 0 || emblemID >= MAXEMBLEMS)
|
||||
{
|
||||
// Invalid emblem ID, can't pickup.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (demo.playback)
|
||||
{
|
||||
// Never collect emblems in replays.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player->bot)
|
||||
{
|
||||
// Your nefarious opponent puppy can't grab these for you.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean P_EmblemWasCollected(INT32 emblemID)
|
||||
{
|
||||
if (emblemID < 0 || emblemID >= numemblems)
|
||||
{
|
||||
// Invalid emblem ID, can't pickup.
|
||||
return true;
|
||||
}
|
||||
|
||||
return gamedata->collected[emblemID];
|
||||
}
|
||||
|
||||
/** Takes action based on a ::MF_SPECIAL thing touched by a player.
|
||||
* Actually, this just checks a few things (heights, toucher->player, no
|
||||
* objectplace, no dead or disappearing things)
|
||||
|
|
@ -508,12 +542,24 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
// Secret emblem thingy
|
||||
case MT_EMBLEM:
|
||||
{
|
||||
if (demo.playback || special->health > MAXEMBLEMS)
|
||||
boolean gotcollected = false;
|
||||
|
||||
if (!P_CanPickupEmblem(player, special->health - 1))
|
||||
return;
|
||||
|
||||
emblemlocations[special->health-1].collected = true;
|
||||
M_UpdateUnlockablesAndExtraEmblems();
|
||||
G_SaveGameData();
|
||||
if (P_IsLocalPlayer(player) && !gamedata->collected[special->health-1])
|
||||
{
|
||||
gamedata->collected[special->health-1] = gotcollected = true;
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
|
||||
if (netgame)
|
||||
{
|
||||
// Don't delete the object in netgames, just fade it.
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -962,7 +1008,8 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
|
|||
|| target->type == MT_BANANA || target->type == MT_BANANA_SHIELD
|
||||
|| target->type == MT_DROPTARGET || target->type == MT_DROPTARGET_SHIELD
|
||||
|| target->type == MT_EGGMANITEM || target->type == MT_EGGMANITEM_SHIELD
|
||||
|| target->type == MT_BALLHOG || target->type == MT_SPB)) // kart dead items
|
||||
|| target->type == MT_BALLHOG || target->type == MT_SPB
|
||||
|| target->type == MT_GACHABOM)) // kart dead items
|
||||
target->flags |= MF_NOGRAVITY; // Don't drop Tails 03-08-2000
|
||||
else
|
||||
target->flags &= ~MF_NOGRAVITY; // lose it if you for whatever reason have it, I'm looking at you shields
|
||||
|
|
@ -1975,7 +2022,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
if (!(target->flags & MF_SHOOTABLE))
|
||||
return false; // shouldn't happen...
|
||||
|
||||
if (!(damagetype & DMG_DEATHMASK) && target->hitlag > 0 && inflictor == NULL)
|
||||
if (!(damagetype & DMG_DEATHMASK) && (target->eflags & MFE_PAUSED))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1998,6 +2045,18 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
|
||||
if (player) // Player is the target
|
||||
{
|
||||
{
|
||||
const INT32 oldtimeshit = player->timeshit;
|
||||
|
||||
player->timeshit++;
|
||||
|
||||
// overflow prevention
|
||||
if (player->timeshit < oldtimeshit)
|
||||
{
|
||||
player->timeshit = oldtimeshit;
|
||||
}
|
||||
}
|
||||
|
||||
if (player->pflags & PF_GODMODE)
|
||||
return false;
|
||||
|
||||
|
|
@ -2031,6 +2090,9 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
// If not, then spawn the instashield effect instead.
|
||||
if (!force)
|
||||
{
|
||||
boolean invincible = true;
|
||||
sfxenum_t sfx = sfx_None;
|
||||
|
||||
if (gametyperules & GTR_BUMPERS)
|
||||
{
|
||||
if (player->bumpers <= 0 && player->karmadelay)
|
||||
|
|
@ -2050,8 +2112,35 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
}
|
||||
}
|
||||
|
||||
if (player->invincibilitytimer > 0 || K_IsBigger(target, inflictor) == true || player->hyudorotimer > 0)
|
||||
if (player->invincibilitytimer > 0)
|
||||
{
|
||||
sfx= sfx_invind;
|
||||
}
|
||||
else if (K_IsBigger(target, inflictor) == true)
|
||||
{
|
||||
sfx = sfx_grownd;
|
||||
}
|
||||
else if (player->hyudorotimer > 0)
|
||||
;
|
||||
else
|
||||
{
|
||||
invincible = false;
|
||||
}
|
||||
|
||||
if (invincible)
|
||||
{
|
||||
const INT32 oldhitlag = target->hitlag;
|
||||
|
||||
laglength = max(laglength / 2, 1);
|
||||
K_SetHitLagForObjects(target, inflictor, laglength, false);
|
||||
|
||||
player->invulnhitlag += (target->hitlag - oldhitlag);
|
||||
|
||||
if (player->timeshit > player->timeshitprev)
|
||||
{
|
||||
S_StartSound(target, sfx);
|
||||
}
|
||||
|
||||
// Full invulnerability
|
||||
K_DoInstashield(player);
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -546,6 +546,8 @@ void P_CheckPointLimit(void);
|
|||
boolean P_CheckRacers(void);
|
||||
|
||||
boolean P_CanPickupItem(player_t *player, UINT8 weapon);
|
||||
boolean P_CanPickupEmblem(player_t *player, INT32 emblemID);
|
||||
boolean P_EmblemWasCollected(INT32 emblemID);
|
||||
|
||||
//
|
||||
// P_SPEC
|
||||
|
|
|
|||
22
src/p_map.c
22
src/p_map.c
|
|
@ -277,6 +277,12 @@ P_DoSpringEx
|
|||
angle_t finalAngle,
|
||||
UINT16 starcolor)
|
||||
{
|
||||
if (object->eflags & MFE_SPRUNG)
|
||||
{
|
||||
// Object was already sprung this tic
|
||||
return;
|
||||
}
|
||||
|
||||
if (horizspeed < 0)
|
||||
{
|
||||
horizspeed = -(horizspeed);
|
||||
|
|
@ -402,7 +408,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
|
|||
return false;
|
||||
}
|
||||
|
||||
spring->flags &= ~(MF_SOLID|MF_SPECIAL); // De-solidify
|
||||
spring->flags |= MF_NOCLIPTHING; // De-solidify
|
||||
|
||||
if (spring->eflags & MFE_VERTICALFLIP)
|
||||
vertispeed *= -1;
|
||||
|
|
@ -445,7 +451,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
|
|||
spring->angle, starcolor);
|
||||
|
||||
// Re-solidify
|
||||
spring->flags |= (spring->info->flags & (MF_SPRING|MF_SPECIAL));
|
||||
spring->flags = (spring->flags & ~(MF_NOCLIPTHING)) | (spring->info->flags & (MF_NOCLIPTHING));
|
||||
|
||||
if (object->player)
|
||||
{
|
||||
|
|
@ -921,7 +927,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
// Bubble Shield reflect
|
||||
if (((thing->type == MT_BUBBLESHIELD && thing->target->player && thing->target->player->bubbleblowup)
|
||||
|| (thing->player && thing->player->bubbleblowup))
|
||||
&& (tm.thing->type == MT_ORBINAUT || tm.thing->type == MT_JAWZ
|
||||
&& (tm.thing->type == MT_ORBINAUT || tm.thing->type == MT_JAWZ || tm.thing->type == MT_GACHABOM
|
||||
|| tm.thing->type == MT_BANANA || tm.thing->type == MT_EGGMANITEM || tm.thing->type == MT_BALLHOG
|
||||
|| tm.thing->type == MT_SSMINE || tm.thing->type == MT_LANDMINE || tm.thing->type == MT_SINK
|
||||
|| tm.thing->type == MT_GARDENTOP
|
||||
|
|
@ -937,7 +943,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
}
|
||||
else if (((tm.thing->type == MT_BUBBLESHIELD && tm.thing->target->player && tm.thing->target->player->bubbleblowup)
|
||||
|| (tm.thing->player && tm.thing->player->bubbleblowup))
|
||||
&& (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ
|
||||
&& (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ || thing->type == MT_GACHABOM
|
||||
|| thing->type == MT_BANANA || thing->type == MT_EGGMANITEM || thing->type == MT_BALLHOG
|
||||
|| thing->type == MT_SSMINE || tm.thing->type == MT_LANDMINE || thing->type == MT_SINK
|
||||
|| thing->type == MT_GARDENTOP
|
||||
|
|
@ -958,7 +964,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
|
||||
// Droptarget reflect
|
||||
if ((thing->type == MT_DROPTARGET || thing->type == MT_DROPTARGET_SHIELD)
|
||||
&& (tm.thing->type == MT_ORBINAUT || tm.thing->type == MT_JAWZ
|
||||
&& (tm.thing->type == MT_ORBINAUT || tm.thing->type == MT_JAWZ || tm.thing->type == MT_GACHABOM
|
||||
|| tm.thing->type == MT_BANANA || tm.thing->type == MT_EGGMANITEM || tm.thing->type == MT_BALLHOG
|
||||
|| tm.thing->type == MT_SSMINE || tm.thing->type == MT_LANDMINE || tm.thing->type == MT_SINK
|
||||
|| tm.thing->type == MT_GARDENTOP
|
||||
|
|
@ -973,7 +979,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
return K_DropTargetCollide(thing, tm.thing) ? BMIT_CONTINUE : BMIT_ABORT;
|
||||
}
|
||||
else if ((tm.thing->type == MT_DROPTARGET || tm.thing->type == MT_DROPTARGET_SHIELD)
|
||||
&& (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ
|
||||
&& (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ || thing->type == MT_GACHABOM
|
||||
|| thing->type == MT_BANANA || thing->type == MT_EGGMANITEM || thing->type == MT_BALLHOG
|
||||
|| thing->type == MT_SSMINE || tm.thing->type == MT_LANDMINE || thing->type == MT_SINK
|
||||
|| thing->type == MT_GARDENTOP
|
||||
|
|
@ -993,7 +999,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
|| thing->type == MT_DROPTARGET_SHIELD || tm.thing->type == MT_DROPTARGET_SHIELD)
|
||||
return BMIT_CONTINUE;
|
||||
|
||||
if (tm.thing->type == MT_ORBINAUT || tm.thing->type == MT_JAWZ
|
||||
if (tm.thing->type == MT_ORBINAUT || tm.thing->type == MT_JAWZ || tm.thing->type == MT_GACHABOM
|
||||
|| tm.thing->type == MT_ORBINAUT_SHIELD || tm.thing->type == MT_JAWZ_SHIELD
|
||||
|| tm.thing->type == MT_GARDENTOP)
|
||||
{
|
||||
|
|
@ -1005,7 +1011,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
|
||||
return Obj_OrbinautJawzCollide(tm.thing, thing) ? BMIT_CONTINUE : BMIT_ABORT;
|
||||
}
|
||||
else if (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ
|
||||
else if (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ || thing->type == MT_GACHABOM
|
||||
|| thing->type == MT_ORBINAUT_SHIELD || thing->type == MT_JAWZ_SHIELD
|
||||
|| thing->type == MT_GARDENTOP)
|
||||
{
|
||||
|
|
|
|||
111
src/p_mobj.c
111
src/p_mobj.c
|
|
@ -1196,6 +1196,13 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
|
|||
case MT_BATTLEBUMPER:
|
||||
gravityadd /= 2;
|
||||
break;
|
||||
case MT_GACHABOM:
|
||||
if (!(mo->flags2 & MF2_AMBUSH))
|
||||
{
|
||||
// Use normal gravity, unless if it was tossed.
|
||||
break;
|
||||
}
|
||||
/*FALLTHRU*/
|
||||
case MT_BANANA:
|
||||
case MT_EGGMANITEM:
|
||||
case MT_SSMINE:
|
||||
|
|
@ -1743,7 +1750,7 @@ void P_XYMovement(mobj_t *mo)
|
|||
|
||||
//{ SRB2kart - Orbinaut, Ballhog
|
||||
// Bump sparks
|
||||
if (mo->type == MT_ORBINAUT || mo->type == MT_BALLHOG)
|
||||
if (mo->type == MT_ORBINAUT || mo->type == MT_BALLHOG || mo->type == MT_GACHABOM)
|
||||
{
|
||||
mobj_t *fx;
|
||||
fx = P_SpawnMobj(mo->x, mo->y, mo->z, MT_BUMP);
|
||||
|
|
@ -1757,6 +1764,7 @@ void P_XYMovement(mobj_t *mo)
|
|||
switch (mo->type)
|
||||
{
|
||||
case MT_ORBINAUT: // Orbinaut speed decreasing
|
||||
case MT_GACHABOM:
|
||||
case MT_GARDENTOP:
|
||||
if (mo->health > 1)
|
||||
{
|
||||
|
|
@ -5175,6 +5183,7 @@ boolean P_IsKartFieldItem(INT32 type)
|
|||
case MT_SINK:
|
||||
case MT_DROPTARGET:
|
||||
case MT_DUELBOMB:
|
||||
case MT_GACHABOM:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
|
@ -6669,6 +6678,7 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
|
|||
case MT_LANDMINE:
|
||||
//case MT_DROPTARGET:
|
||||
case MT_SPB:
|
||||
case MT_GACHABOM:
|
||||
if (P_IsObjectOnGround(mobj))
|
||||
{
|
||||
P_RemoveMobj(mobj);
|
||||
|
|
@ -6959,9 +6969,38 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
}
|
||||
break;
|
||||
case MT_EMBLEM:
|
||||
if (mobj->flags2 & MF2_NIGHTSPULL)
|
||||
P_NightsItemChase(mobj);
|
||||
{
|
||||
INT32 trans = 0;
|
||||
|
||||
mobj->frame &= ~FF_TRANSMASK;
|
||||
mobj->renderflags &= ~RF_TRANSMASK;
|
||||
|
||||
if (P_EmblemWasCollected(mobj->health - 1) || !P_CanPickupEmblem(&players[consoleplayer], mobj->health - 1))
|
||||
{
|
||||
trans = tr_trans50;
|
||||
mobj->renderflags |= (tr_trans50 << RF_TRANSSHIFT);
|
||||
}
|
||||
|
||||
if (mobj->reactiontime > 0
|
||||
&& leveltime > starttime)
|
||||
{
|
||||
INT32 diff = mobj->reactiontime - (signed)(leveltime - starttime);
|
||||
if (diff < 10)
|
||||
{
|
||||
if (diff <= 0)
|
||||
{
|
||||
P_RemoveMobj(mobj);
|
||||
return false;
|
||||
}
|
||||
|
||||
trans = max(trans, 10-diff);
|
||||
}
|
||||
}
|
||||
|
||||
mobj->renderflags |= (trans << RF_TRANSSHIFT);
|
||||
|
||||
break;
|
||||
}
|
||||
case MT_FLOATINGITEM:
|
||||
{
|
||||
mobj->pitch = mobj->roll = 0;
|
||||
|
|
@ -7069,6 +7108,39 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
}
|
||||
}
|
||||
break;
|
||||
case MT_GACHABOM:
|
||||
{
|
||||
if (mobj->flags2 & MF2_AMBUSH)
|
||||
{
|
||||
mobj->friction = ORIG_FRICTION/4;
|
||||
|
||||
if (mobj->momx || mobj->momy)
|
||||
{
|
||||
mobj_t *ghost = P_SpawnGhostMobj(mobj);
|
||||
|
||||
if (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player)
|
||||
{
|
||||
ghost->color = mobj->target->player->skincolor;
|
||||
ghost->colorized = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (P_IsObjectOnGround(mobj))
|
||||
{
|
||||
if (mobj->movecount > 1)
|
||||
{
|
||||
S_StartSound(mobj, mobj->info->activesound);
|
||||
mobj->momx = mobj->momy = 0;
|
||||
mobj->movecount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (mobj->threshold > 0)
|
||||
mobj->threshold--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* FALLTHRU */
|
||||
case MT_ORBINAUT:
|
||||
{
|
||||
Obj_OrbinautThink(mobj);
|
||||
|
|
@ -9624,6 +9696,7 @@ void P_MobjThinker(mobj_t *mobj)
|
|||
// Don't run any thinker code while in hitlag
|
||||
if (mobj->hitlag > 0)
|
||||
{
|
||||
mobj->eflags |= MFE_PAUSED;
|
||||
mobj->hitlag--;
|
||||
|
||||
if (mobj->type == MT_DROPTARGET && mobj->reactiontime > 0 && mobj->hitlag == 2)
|
||||
|
|
@ -9643,7 +9716,7 @@ void P_MobjThinker(mobj_t *mobj)
|
|||
return;
|
||||
}
|
||||
|
||||
mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG|MFE_JUSTBOUNCEDWALL|MFE_DAMAGEHITLAG|MFE_SLOPELAUNCHED);
|
||||
mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG|MFE_JUSTBOUNCEDWALL|MFE_DAMAGEHITLAG|MFE_SLOPELAUNCHED|MFE_PAUSED);
|
||||
|
||||
// sal: what the hell? is there any reason this isn't done, like, literally ANYWHERE else?
|
||||
P_SetTarget(&tm.floorthing, NULL);
|
||||
|
|
@ -9824,6 +9897,7 @@ void P_MobjThinker(mobj_t *mobj)
|
|||
|| mobj->type == MT_CANNONBALLDECOR
|
||||
|| mobj->type == MT_FALLINGROCK
|
||||
|| mobj->type == MT_ORBINAUT
|
||||
|| mobj->type == MT_GACHABOM
|
||||
|| mobj->type == MT_JAWZ
|
||||
|| (mobj->type == MT_DROPTARGET && mobj->reactiontime))
|
||||
{
|
||||
|
|
@ -10103,6 +10177,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing)
|
|||
case MT_ROCKETSNEAKER:
|
||||
case MT_SPB:
|
||||
case MT_DUELBOMB:
|
||||
case MT_GACHABOM:
|
||||
thing->shadowscale = 3*FRACUNIT/2;
|
||||
break;
|
||||
case MT_BANANA_SHIELD:
|
||||
|
|
@ -11424,7 +11499,7 @@ void P_RespawnSpecials(void)
|
|||
}
|
||||
else if (pcount > 1)
|
||||
{
|
||||
time = (120 - ((pcount-2) * 20)) * TICRATE;
|
||||
time = (120 * TICRATE) / (pcount - 1);
|
||||
|
||||
// If the map is longer or shorter than 3 laps, then adjust ring respawn to account for this.
|
||||
// 5 lap courses would have more retreaded ground, while 2 lap courses would have less.
|
||||
|
|
@ -12043,7 +12118,7 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj)
|
|||
|
||||
if (!emblem)
|
||||
{
|
||||
CONS_Debug(DBG_GAMELOGIC, "No map emblem for map %d with tag %d found!\n", gamemap, Tag_FGet(&mthing->tags));
|
||||
CONS_Alert(CONS_WARNING, "P_SetupEmblem: No map emblem for map %d with tag %d found!\n", gamemap, Tag_FGet(&mthing->tags));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -12056,24 +12131,13 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj)
|
|||
emcolor = M_GetEmblemColor(&emblemlocations[j]); // workaround for compiler complaint about bad function casting
|
||||
mobj->color = (UINT16)emcolor;
|
||||
|
||||
if (emblemlocations[j].collected)
|
||||
{
|
||||
P_UnsetThingPosition(mobj);
|
||||
mobj->flags |= MF_NOCLIP;
|
||||
mobj->flags &= ~MF_SPECIAL;
|
||||
mobj->flags |= MF_NOBLOCKMAP;
|
||||
mobj->frame |= (tr_trans50 << FF_TRANSSHIFT);
|
||||
P_SetThingPosition(mobj);
|
||||
}
|
||||
else
|
||||
{
|
||||
mobj->frame &= ~FF_TRANSMASK;
|
||||
mobj->frame &= ~FF_TRANSMASK;
|
||||
|
||||
if (emblemlocations[j].type == ET_GLOBAL)
|
||||
{
|
||||
mobj->reactiontime = emblemlocations[j].var;
|
||||
}
|
||||
if (emblemlocations[j].flags & GE_TIMED)
|
||||
{
|
||||
mobj->reactiontime = emblemlocations[j].var;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -12517,7 +12581,10 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
|
|||
case MT_EMBLEM:
|
||||
{
|
||||
if (!P_SetupEmblem(mthing, mobj))
|
||||
{
|
||||
P_RemoveMobj(mobj);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MT_SKYBOX:
|
||||
|
|
|
|||
18
src/p_mobj.h
18
src/p_mobj.h
|
|
@ -255,7 +255,8 @@ typedef enum
|
|||
MFE_DAMAGEHITLAG = 1<<13,
|
||||
// Slope physics sent you airborne
|
||||
MFE_SLOPELAUNCHED = 1<<14,
|
||||
// free: to and including 1<<15
|
||||
// Thinker is paused due to hitlag
|
||||
MFE_PAUSED = 1<<15,
|
||||
} mobjeflag_t;
|
||||
|
||||
//
|
||||
|
|
@ -275,14 +276,17 @@ struct mobj_t
|
|||
// List: thinker links.
|
||||
thinker_t thinker;
|
||||
|
||||
mobjtype_t type;
|
||||
const mobjinfo_t *info; // &mobjinfo[mobj->type]
|
||||
|
||||
// Info for drawing: position.
|
||||
fixed_t x, y, z;
|
||||
// --- Please make sure you keep the fields up to this
|
||||
// --- point in sync with degenmobj_t.
|
||||
|
||||
fixed_t old_x, old_y, old_z; // position interpolation
|
||||
fixed_t old_x2, old_y2, old_z2;
|
||||
|
||||
mobjtype_t type;
|
||||
const mobjinfo_t *info; // &mobjinfo[mobj->type]
|
||||
|
||||
// More list: links in sector (if needed)
|
||||
mobj_t *snext;
|
||||
mobj_t **sprev; // killough 8/11/98: change to ptr-to-ptr
|
||||
|
|
@ -428,14 +432,14 @@ struct precipmobj_t
|
|||
// List: thinker links.
|
||||
thinker_t thinker;
|
||||
|
||||
mobjtype_t type;
|
||||
const mobjinfo_t *info; // &mobjinfo[mobj->type]
|
||||
|
||||
// Info for drawing: position.
|
||||
fixed_t x, y, z;
|
||||
fixed_t old_x, old_y, old_z; // position interpolation
|
||||
fixed_t old_x2, old_y2, old_z2;
|
||||
|
||||
mobjtype_t type;
|
||||
const mobjinfo_t *info; // &mobjinfo[mobj->type]
|
||||
|
||||
// More list: links in sector (if needed)
|
||||
precipmobj_t *snext;
|
||||
precipmobj_t **sprev; // killough 8/11/98: change to ptr-to-ptr
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include "p_polyobj.h"
|
||||
#include "lua_script.h"
|
||||
#include "p_slopes.h"
|
||||
#include "m_cond.h" // netUnlocked
|
||||
|
||||
// SRB2Kart
|
||||
#include "k_battle.h"
|
||||
|
|
@ -150,7 +151,12 @@ static void P_NetArchivePlayers(void)
|
|||
|
||||
WRITEUINT8(save_p, players[i].skincolor);
|
||||
WRITEINT32(save_p, players[i].skin);
|
||||
WRITEUINT32(save_p, players[i].availabilities);
|
||||
|
||||
for (j = 0; j < MAXAVAILABILITY; j++)
|
||||
{
|
||||
WRITEUINT8(save_p, players[i].availabilities[j]);
|
||||
}
|
||||
|
||||
WRITEUINT8(save_p, players[i].fakeskin);
|
||||
WRITEUINT8(save_p, players[i].lastfakeskin);
|
||||
WRITEUINT32(save_p, players[i].score);
|
||||
|
|
@ -184,6 +190,9 @@ static void P_NetArchivePlayers(void)
|
|||
|
||||
WRITEINT32(save_p, players[i].onconveyor);
|
||||
|
||||
WRITEUINT8(save_p, players[i].timeshit);
|
||||
WRITEUINT8(save_p, players[i].timeshitprev);
|
||||
|
||||
WRITEUINT32(save_p, players[i].jointime);
|
||||
|
||||
WRITEUINT8(save_p, players[i].splitscreenindex);
|
||||
|
|
@ -260,6 +269,7 @@ static void P_NetArchivePlayers(void)
|
|||
WRITEUINT16(save_p, players[i].spinouttimer);
|
||||
WRITEUINT8(save_p, players[i].spinouttype);
|
||||
WRITEUINT8(save_p, players[i].instashield);
|
||||
WRITEINT32(save_p, players[i].invulnhitlag);
|
||||
WRITEUINT8(save_p, players[i].wipeoutslow);
|
||||
WRITEUINT8(save_p, players[i].justbumped);
|
||||
WRITEUINT8(save_p, players[i].tumbleBounces);
|
||||
|
|
@ -514,7 +524,12 @@ static void P_NetUnArchivePlayers(void)
|
|||
|
||||
players[i].skincolor = READUINT8(save_p);
|
||||
players[i].skin = READINT32(save_p);
|
||||
players[i].availabilities = READUINT32(save_p);
|
||||
|
||||
for (j = 0; j < MAXAVAILABILITY; j++)
|
||||
{
|
||||
players[i].availabilities[j] = READUINT8(save_p);
|
||||
}
|
||||
|
||||
players[i].fakeskin = READUINT8(save_p);
|
||||
players[i].lastfakeskin = READUINT8(save_p);
|
||||
players[i].score = READUINT32(save_p);
|
||||
|
|
@ -546,6 +561,9 @@ static void P_NetUnArchivePlayers(void)
|
|||
players[i].lastsidehit = READINT16(save_p);
|
||||
players[i].lastlinehit = READINT16(save_p);
|
||||
|
||||
players[i].timeshit = READUINT8(save_p);
|
||||
players[i].timeshitprev = READUINT8(save_p);
|
||||
|
||||
players[i].onconveyor = READINT32(save_p);
|
||||
|
||||
players[i].jointime = READUINT32(save_p);
|
||||
|
|
@ -604,6 +622,7 @@ static void P_NetUnArchivePlayers(void)
|
|||
players[i].spinouttimer = READUINT16(save_p);
|
||||
players[i].spinouttype = READUINT8(save_p);
|
||||
players[i].instashield = READUINT8(save_p);
|
||||
players[i].invulnhitlag = READINT32(save_p);
|
||||
players[i].wipeoutslow = READUINT8(save_p);
|
||||
players[i].justbumped = READUINT8(save_p);
|
||||
players[i].tumbleBounces = READUINT8(save_p);
|
||||
|
|
@ -4684,9 +4703,6 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride)
|
|||
//lastmapsaved = gamemap;
|
||||
lastmaploaded = gamemap;
|
||||
|
||||
tokenlist = 0;
|
||||
token = 0;
|
||||
|
||||
savedata.emeralds = READUINT16(save_p)-357;
|
||||
|
||||
READSTRINGN(save_p, testname, sizeof(testname));
|
||||
|
|
@ -4705,7 +4721,7 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride)
|
|||
|
||||
static void P_NetArchiveMisc(boolean resending)
|
||||
{
|
||||
size_t i;
|
||||
size_t i, j;
|
||||
|
||||
WRITEUINT32(save_p, ARCHIVEBLOCK_MISC);
|
||||
|
||||
|
|
@ -4726,7 +4742,14 @@ static void P_NetArchiveMisc(boolean resending)
|
|||
WRITEUINT32(save_p, pig);
|
||||
}
|
||||
|
||||
WRITEUINT32(save_p, tokenlist);
|
||||
for (i = 0; i < MAXUNLOCKABLES;)
|
||||
{
|
||||
UINT8 btemp = 0;
|
||||
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
|
||||
btemp |= (netUnlocked[j+i] << j);
|
||||
WRITEUINT8(save_p, btemp);
|
||||
i += j;
|
||||
}
|
||||
|
||||
WRITEUINT8(save_p, encoremode);
|
||||
|
||||
|
|
@ -4755,7 +4778,6 @@ static void P_NetArchiveMisc(boolean resending)
|
|||
WRITEUINT8(save_p, globools);
|
||||
}
|
||||
|
||||
WRITEUINT32(save_p, token);
|
||||
WRITEUINT32(save_p, bluescore);
|
||||
WRITEUINT32(save_p, redscore);
|
||||
|
||||
|
|
@ -4851,8 +4873,9 @@ static void P_NetArchiveMisc(boolean resending)
|
|||
|
||||
static inline boolean P_NetUnArchiveMisc(boolean reloading)
|
||||
{
|
||||
size_t i;
|
||||
size_t i, j;
|
||||
size_t numTasks;
|
||||
UINT8 *old_save_p;
|
||||
|
||||
if (READUINT32(save_p) != ARCHIVEBLOCK_MISC)
|
||||
I_Error("Bad $$$.sav at archive block Misc");
|
||||
|
|
@ -4885,16 +4908,27 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
|
|||
}
|
||||
}
|
||||
|
||||
tokenlist = READUINT32(save_p);
|
||||
for (i = 0; i < MAXUNLOCKABLES;)
|
||||
{
|
||||
UINT8 rtemp = READUINT8(save_p);
|
||||
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
|
||||
netUnlocked[j+i] = ((rtemp >> j) & 1);
|
||||
i += j;
|
||||
}
|
||||
|
||||
encoremode = (boolean)READUINT8(save_p);
|
||||
|
||||
// FIXME: save_p should not be global!!!
|
||||
old_save_p = save_p;
|
||||
|
||||
if (!P_LoadLevel(true, reloading))
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, M_GetText("Can't load the level!\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
save_p = old_save_p;
|
||||
|
||||
// get the time
|
||||
leveltime = READUINT32(save_p);
|
||||
lastmap = READINT16(save_p);
|
||||
|
|
@ -4918,7 +4952,6 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
|
|||
stoppedclock = !!(globools & (1<<1));
|
||||
}
|
||||
|
||||
token = READUINT32(save_p);
|
||||
bluescore = READUINT32(save_p);
|
||||
redscore = READUINT32(save_p);
|
||||
|
||||
|
|
|
|||
|
|
@ -400,7 +400,6 @@ static void P_ClearSingleMapHeaderInfo(INT16 num)
|
|||
mapheaderinfo[num]->palette = UINT16_MAX;
|
||||
mapheaderinfo[num]->encorepal = UINT16_MAX;
|
||||
mapheaderinfo[num]->numlaps = NUMLAPS_DEFAULT;
|
||||
mapheaderinfo[num]->unlockrequired = -1;
|
||||
mapheaderinfo[num]->levelselect = 0;
|
||||
mapheaderinfo[num]->levelflags = 0;
|
||||
mapheaderinfo[num]->menuflags = 0;
|
||||
|
|
@ -6807,7 +6806,6 @@ static void P_InitLevelSettings(void)
|
|||
modulothing = 0;
|
||||
|
||||
// special stage tokens, emeralds, and ring total
|
||||
tokenbits = 0;
|
||||
runemeraldmanager = false;
|
||||
emeraldspawndelay = 60*TICRATE;
|
||||
|
||||
|
|
@ -7564,10 +7562,13 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
nextmapoverride = 0;
|
||||
skipstats = 0;
|
||||
|
||||
if (!(netgame || multiplayer || demo.playback) && !majormods)
|
||||
if (!demo.playback)
|
||||
{
|
||||
mapheaderinfo[gamemap-1]->mapvisited |= MV_VISITED;
|
||||
else if (!demo.playback)
|
||||
mapheaderinfo[gamemap-1]->mapvisited |= MV_MP; // you want to record that you've been there this session, but not permanently
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
|
||||
G_AddMapToBuffer(gamemap-1);
|
||||
|
||||
|
|
@ -8037,8 +8038,8 @@ UINT16 P_PartialAddWadFile(const char *wadfilename)
|
|||
//
|
||||
// look for skins
|
||||
//
|
||||
R_AddSkins(wadnum); // faB: wadfile index in wadfiles[]
|
||||
R_PatchSkins(wadnum); // toast: PATCH PATCH
|
||||
R_AddSkins(wadnum, false); // faB: wadfile index in wadfiles[]
|
||||
R_PatchSkins(wadnum, false); // toast: PATCH PATCH
|
||||
|
||||
//
|
||||
// edit music defs
|
||||
|
|
|
|||
11
src/p_spec.c
11
src/p_spec.c
|
|
@ -1495,14 +1495,14 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
|
|||
{ // An unlockable itself must be unlocked!
|
||||
INT32 unlockid = triggerline->args[1];
|
||||
|
||||
if ((modifiedgame && !savemoddata) || (netgame || multiplayer))
|
||||
if (modifiedgame && !savemoddata)
|
||||
return false;
|
||||
else if (unlockid < 0 || unlockid >= MAXUNLOCKABLES) // limited by unlockable count
|
||||
else if (unlockid <= 0 || unlockid > MAXUNLOCKABLES) // limited by unlockable count
|
||||
{
|
||||
CONS_Debug(DBG_GAMELOGIC, "Unlockable check (sidedef %hu): bad unlockable ID %d\n", triggerline->sidenum[0], unlockid);
|
||||
return false;
|
||||
}
|
||||
else if (!(unlockables[unlockid-1].unlocked))
|
||||
else if (!(M_CheckNetUnlockByID(unlockid)))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
|
@ -2752,7 +2752,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
|
|||
break;
|
||||
|
||||
case 441: // Trigger unlockable
|
||||
if ((!modifiedgame || savemoddata) && !(netgame || multiplayer))
|
||||
if (!(demo.playback || netgame || multiplayer))
|
||||
{
|
||||
INT32 trigid = line->args[0];
|
||||
|
||||
|
|
@ -2763,9 +2763,8 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
|
|||
unlocktriggers |= 1 << trigid;
|
||||
|
||||
// Unlocked something?
|
||||
if (M_UpdateUnlockablesAndExtraEmblems())
|
||||
if (M_UpdateUnlockablesAndExtraEmblems(true))
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k68);
|
||||
G_SaveGameData(); // only save if unlocked something
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "st_stuff.h"
|
||||
#include "p_polyobj.h"
|
||||
#include "m_random.h"
|
||||
#include "m_cond.h" // gamedata->playtime
|
||||
#include "lua_script.h"
|
||||
#include "lua_hook.h"
|
||||
#include "m_perfstats.h"
|
||||
|
|
@ -628,7 +629,7 @@ void P_Ticker(boolean run)
|
|||
|
||||
// Keep track of how long they've been playing!
|
||||
if (!demo.playback) // Don't increment if a demo is playing.
|
||||
totalplaytime++;
|
||||
gamedata->totalplaytime++;
|
||||
|
||||
// formality so kitemcap gets updated properly each frame.
|
||||
P_RunKartItems();
|
||||
|
|
|
|||
|
|
@ -4341,6 +4341,12 @@ void P_PlayerAfterThink(player_t *player)
|
|||
// so a lag value of 1 is exactly attached to the player.
|
||||
K_HandleFollower(player);
|
||||
|
||||
if (P_MobjWasRemoved(player->mo) || (player->mo->eflags & MFE_PAUSED) == 0)
|
||||
{
|
||||
player->timeshitprev = player->timeshit;
|
||||
player->timeshit = 0;
|
||||
}
|
||||
|
||||
|
||||
if (K_PlayerUsesBotMovement(player))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -821,7 +821,7 @@ struct patch_t
|
|||
};
|
||||
|
||||
extern patch_t *missingpat;
|
||||
extern patch_t *blanklvl;
|
||||
extern patch_t *blanklvl, *nolvl;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma pack(1)
|
||||
|
|
|
|||
284
src/r_skins.c
284
src/r_skins.c
|
|
@ -32,6 +32,7 @@
|
|||
#if 0
|
||||
#include "k_kart.h" // K_KartResetPlayerColor
|
||||
#endif
|
||||
#include "k_grandprix.h" // K_CanChangeRules
|
||||
#ifdef HWRENDER
|
||||
#include "hardware/hw_md2.h"
|
||||
#endif
|
||||
|
|
@ -150,36 +151,52 @@ void R_InitSkins(void)
|
|||
|
||||
for (i = 0; i < numwadfiles; i++)
|
||||
{
|
||||
R_AddSkins((UINT16)i);
|
||||
R_PatchSkins((UINT16)i);
|
||||
R_AddSkins((UINT16)i, true);
|
||||
R_PatchSkins((UINT16)i, true);
|
||||
R_LoadSpriteInfoLumps(i, wadfiles[i]->numlumps);
|
||||
}
|
||||
ST_ReloadSkinFaceGraphics();
|
||||
}
|
||||
|
||||
UINT32 R_GetSkinAvailabilities(void)
|
||||
UINT8 *R_GetSkinAvailabilities(boolean demolock)
|
||||
{
|
||||
UINT8 i;
|
||||
UINT32 response = 0;
|
||||
UINT8 i, shif, byte;
|
||||
INT32 skinid;
|
||||
static UINT8 responsebuffer[MAXAVAILABILITY];
|
||||
|
||||
memset(&responsebuffer, 0, sizeof(responsebuffer));
|
||||
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
{
|
||||
if (unlockables[i].type == SECRET_SKIN && unlockables[i].unlocked)
|
||||
{
|
||||
UINT8 s = min(unlockables[i].variable, MAXSKINS);
|
||||
response |= (1 << s);
|
||||
}
|
||||
if (unlockables[i].type != SECRET_SKIN)
|
||||
continue;
|
||||
|
||||
// NEVER EVER EVER M_CheckNetUnlockByID
|
||||
if (gamedata->unlocked[i] != true && !demolock)
|
||||
continue;
|
||||
|
||||
skinid = M_UnlockableSkinNum(&unlockables[i]);
|
||||
|
||||
if (skinid < 0 || skinid >= MAXSKINS)
|
||||
continue;
|
||||
|
||||
shif = (skinid % 8);
|
||||
byte = (skinid / 8);
|
||||
|
||||
responsebuffer[byte] |= (1 << shif);
|
||||
}
|
||||
|
||||
return response;
|
||||
return responsebuffer;
|
||||
}
|
||||
|
||||
// returns true if available in circumstances, otherwise nope
|
||||
// warning don't use with an invalid skinnum other than -1 which always returns true
|
||||
boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
|
||||
boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins)
|
||||
{
|
||||
boolean needsunlocked = false;
|
||||
boolean useplayerstruct = (Playing() && playernum != -1);
|
||||
UINT8 i;
|
||||
INT32 skinid;
|
||||
|
||||
if (skinnum == -1)
|
||||
{
|
||||
|
|
@ -187,55 +204,55 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (modeattacking)
|
||||
{
|
||||
// If you have someone else's run, you should be able to take a look
|
||||
return true;
|
||||
}
|
||||
|
||||
if (netgame && (cv_forceskin.value == skinnum))
|
||||
if (K_CanChangeRules(true) && (cv_forceskin.value == skinnum))
|
||||
{
|
||||
// Being forced to play as this character by the server
|
||||
return true;
|
||||
}
|
||||
|
||||
if (metalrecording)
|
||||
{
|
||||
// Recording a Metal Sonic race
|
||||
const INT32 metalskin = R_SkinAvailable("metalsonic");
|
||||
return (skinnum == metalskin);
|
||||
}
|
||||
|
||||
// Determine if this character is supposed to be unlockable or not
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
if (useplayerstruct && demo.playback)
|
||||
{
|
||||
if (unlockables[i].type == SECRET_SKIN && unlockables[i].variable == skinnum)
|
||||
if (!demoskins)
|
||||
skinnum = demo.skinlist[skinnum].mapping;
|
||||
needsunlocked = demo.skinlist[skinnum].unlockrequired;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
{
|
||||
if (unlockables[i].type != SECRET_SKIN)
|
||||
continue;
|
||||
|
||||
skinid = M_UnlockableSkinNum(&unlockables[i]);
|
||||
|
||||
if (skinid != skinnum)
|
||||
continue;
|
||||
|
||||
// i is now the unlockable index, we can use this later
|
||||
needsunlocked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsunlocked == true)
|
||||
{
|
||||
// You can use this character IF you have it unlocked.
|
||||
if ((netgame || multiplayer) && playernum != -1)
|
||||
{
|
||||
// Use the netgame synchronized unlocks.
|
||||
return (boolean)(!(players[playernum].availabilities & (1 << skinnum)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the unlockables table directly
|
||||
return (boolean)(unlockables[i].unlocked);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (needsunlocked == false)
|
||||
{
|
||||
// Didn't trip anything, so we can use this character.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ok, you can use this character IF you have it unlocked.
|
||||
if (useplayerstruct)
|
||||
{
|
||||
// Use the netgame synchronized unlocks.
|
||||
UINT8 shif = (skinnum % 8);
|
||||
UINT8 byte = (skinnum / 8);
|
||||
return !!(players[playernum].availabilities[byte] & (1 << shif));
|
||||
}
|
||||
|
||||
// Use the unlockables table directly
|
||||
// NOTE: M_CheckNetUnlockByID would be correct in many circumstances... but not all. TODO figure out how to discern.
|
||||
return (boolean)(gamedata->unlocked[i]);
|
||||
}
|
||||
|
||||
// returns true if the skin name is found (loaded from pwad)
|
||||
|
|
@ -253,15 +270,79 @@ INT32 R_SkinAvailable(const char *name)
|
|||
return -1;
|
||||
}
|
||||
|
||||
// Auxillary function that actually sets the skin
|
||||
static void SetSkin(player_t *player, INT32 skinnum)
|
||||
{
|
||||
skin_t *skin = &skins[skinnum];
|
||||
|
||||
player->skin = skinnum;
|
||||
|
||||
player->followitem = skin->followitem;
|
||||
|
||||
player->kartspeed = skin->kartspeed;
|
||||
player->kartweight = skin->kartweight;
|
||||
player->charflags = skin->flags;
|
||||
|
||||
#if 0
|
||||
if (!CV_CheatsEnabled() && !(netgame || multiplayer || demo.playback))
|
||||
{
|
||||
for (i = 0; i <= r_splitscreen; i++)
|
||||
{
|
||||
if (playernum == g_localplayers[i])
|
||||
{
|
||||
CV_StealthSetValue(&cv_playercolor[i], skin->prefcolor);
|
||||
}
|
||||
}
|
||||
|
||||
player->skincolor = skin->prefcolor;
|
||||
K_KartResetPlayerColor(player);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (player->followmobj)
|
||||
{
|
||||
P_RemoveMobj(player->followmobj);
|
||||
P_SetTarget(&player->followmobj, NULL);
|
||||
}
|
||||
|
||||
if (player->mo)
|
||||
{
|
||||
player->mo->skin = skin;
|
||||
P_SetScale(player->mo, player->mo->scale);
|
||||
P_SetPlayerMobjState(player->mo, player->mo->state-states); // Prevent visual errors when switching between skins with differing number of frames
|
||||
}
|
||||
|
||||
// for replays: We have changed our skin mid-game; let the game know so it can do the same in the replay!
|
||||
demo_extradata[(player-players)] |= DXD_SKIN;
|
||||
}
|
||||
|
||||
// Gets the player to the first usuable skin in the game.
|
||||
// (If your mod locked them all, then you kinda stupid)
|
||||
static INT32 GetPlayerDefaultSkin(INT32 playernum)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < numskins; i++)
|
||||
{
|
||||
if (R_SkinUsable(playernum, i, false))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
I_Error("All characters are locked!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// network code calls this when a 'skin change' is received
|
||||
void SetPlayerSkin(INT32 playernum, const char *skinname)
|
||||
{
|
||||
INT32 i = R_SkinAvailable(skinname);
|
||||
player_t *player = &players[playernum];
|
||||
|
||||
if ((i != -1) && R_SkinUsable(playernum, i))
|
||||
if ((i != -1) && R_SkinUsable(playernum, i, false))
|
||||
{
|
||||
SetPlayerSkinByNum(playernum, i);
|
||||
SetSkin(player, i);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -270,7 +351,7 @@ void SetPlayerSkin(INT32 playernum, const char *skinname)
|
|||
else if(server || IsPlayerAdmin(consoleplayer))
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) skin '%s' not found\n"), playernum, player_names[playernum], skinname);
|
||||
|
||||
SetPlayerSkinByNum(playernum, 0);
|
||||
SetSkin(player, GetPlayerDefaultSkin(playernum));
|
||||
}
|
||||
|
||||
// Same as SetPlayerSkin, but uses the skin #.
|
||||
|
|
@ -278,53 +359,10 @@ void SetPlayerSkin(INT32 playernum, const char *skinname)
|
|||
void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
|
||||
{
|
||||
player_t *player = &players[playernum];
|
||||
skin_t *skin = &skins[skinnum];
|
||||
//UINT16 newcolor = 0;
|
||||
//UINT8 i;
|
||||
|
||||
if (skinnum >= 0 && skinnum < numskins && R_SkinUsable(playernum, skinnum)) // Make sure it exists!
|
||||
if (skinnum >= 0 && skinnum < numskins && R_SkinUsable(playernum, skinnum, false)) // Make sure it exists!
|
||||
{
|
||||
player->skin = skinnum;
|
||||
|
||||
player->charflags = (UINT32)skin->flags;
|
||||
|
||||
player->followitem = skin->followitem;
|
||||
|
||||
player->kartspeed = skin->kartspeed;
|
||||
player->kartweight = skin->kartweight;
|
||||
|
||||
#if 0
|
||||
if (!CV_CheatsEnabled() && !(netgame || multiplayer || demo.playback))
|
||||
{
|
||||
for (i = 0; i <= r_splitscreen; i++)
|
||||
{
|
||||
if (playernum == g_localplayers[i])
|
||||
{
|
||||
CV_StealthSetValue(&cv_playercolor[i], skin->prefcolor);
|
||||
}
|
||||
}
|
||||
|
||||
player->skincolor = newcolor = skin->prefcolor;
|
||||
K_KartResetPlayerColor(player);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (player->followmobj)
|
||||
{
|
||||
P_RemoveMobj(player->followmobj);
|
||||
P_SetTarget(&player->followmobj, NULL);
|
||||
}
|
||||
|
||||
if (player->mo)
|
||||
{
|
||||
player->mo->skin = skin;
|
||||
P_SetScale(player->mo, player->mo->scale);
|
||||
P_SetPlayerMobjState(player->mo, player->mo->state-states); // Prevent visual errors when switching between skins with differing number of frames
|
||||
}
|
||||
|
||||
// for replays: We have changed our skin mid-game; let the game know so it can do the same in the replay!
|
||||
demo_extradata[playernum] |= DXD_SKIN;
|
||||
|
||||
SetSkin(player, skinnum);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -333,7 +371,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
|
|||
else if(server || IsPlayerAdmin(consoleplayer))
|
||||
CONS_Alert(CONS_WARNING, "Player %d (%s) skin %d not found\n", playernum, player_names[playernum], skinnum);
|
||||
|
||||
SetPlayerSkinByNum(playernum, 0); // not found, put in the default skin
|
||||
SetSkin(player, GetPlayerDefaultSkin(playernum)); // not found put the eggman skin
|
||||
}
|
||||
|
||||
// Set mo skin but not player_t skin, for ironman
|
||||
|
|
@ -383,11 +421,14 @@ void SetRandomFakePlayerSkin(player_t* player, boolean fast)
|
|||
}
|
||||
else if (skins[i].flags & SF_IRONMAN)
|
||||
continue;
|
||||
/*if (K_SkinLocked(i))
|
||||
continue;*/
|
||||
if (!R_SkinUsable(player-players, i, true))
|
||||
continue;
|
||||
grabskins[usableskins++] = i;
|
||||
}
|
||||
|
||||
if (!usableskins)
|
||||
I_Error("SetRandomFakePlayerSkin: No valid skins to pick from!?");
|
||||
|
||||
i = grabskins[P_RandomKey(PR_RANDOMSKIN, usableskins)];
|
||||
|
||||
SetFakePlayerSkin(player, i);
|
||||
|
|
@ -466,6 +507,51 @@ void ClearFakePlayerSkin(player_t* player)
|
|||
player->fakeskin = MAXSKINS;
|
||||
}
|
||||
|
||||
// Finds a skin with the closest stats if the expected skin doesn't exist.
|
||||
INT32 GetSkinNumClosestToStats(UINT8 kartspeed, UINT8 kartweight, UINT32 flags, boolean unlock)
|
||||
{
|
||||
INT32 i, closest_skin = 0;
|
||||
UINT8 closest_stats, stat_diff;
|
||||
boolean doflagcheck = true;
|
||||
UINT32 flagcheck = flags;
|
||||
|
||||
flaglessretry:
|
||||
closest_stats = stat_diff = UINT8_MAX;
|
||||
|
||||
for (i = 0; i < numskins; i++)
|
||||
{
|
||||
if (!unlock && !R_SkinUsable(-1, i, false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
stat_diff = abs(skins[i].kartspeed - kartspeed) + abs(skins[i].kartweight - kartweight);
|
||||
if (doflagcheck && (skins[i].flags & flagcheck) != flagcheck)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (stat_diff < closest_stats)
|
||||
{
|
||||
closest_stats = stat_diff;
|
||||
closest_skin = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (stat_diff && (doflagcheck || closest_stats == UINT8_MAX))
|
||||
{
|
||||
// Just grab *any* SF_IRONMAN if we don't get it on the first pass.
|
||||
if ((flagcheck & SF_IRONMAN) && (flagcheck != SF_IRONMAN))
|
||||
{
|
||||
flagcheck = SF_IRONMAN;
|
||||
}
|
||||
|
||||
doflagcheck = false;
|
||||
|
||||
goto flaglessretry;
|
||||
}
|
||||
|
||||
return closest_skin;
|
||||
}
|
||||
|
||||
//
|
||||
// Add skins from a pwad, each skin preceded by 'S_SKIN' marker
|
||||
//
|
||||
|
|
@ -659,7 +745,7 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value)
|
|||
//
|
||||
// Find skin sprites, sounds & optional status bar face, & add them
|
||||
//
|
||||
void R_AddSkins(UINT16 wadnum)
|
||||
void R_AddSkins(UINT16 wadnum, boolean mainfile)
|
||||
{
|
||||
UINT16 lump, lastlump = 0;
|
||||
char *buf;
|
||||
|
|
@ -807,7 +893,8 @@ next_token:
|
|||
|
||||
R_FlushTranslationColormapCache();
|
||||
|
||||
CONS_Printf(M_GetText("Added skin '%s'\n"), skin->name);
|
||||
if (mainfile == false)
|
||||
CONS_Printf(M_GetText("Added skin '%s'\n"), skin->name);
|
||||
|
||||
#ifdef SKINVALUES
|
||||
skin_cons_t[numskins].value = numskins;
|
||||
|
|
@ -831,7 +918,7 @@ next_token:
|
|||
//
|
||||
// Patch skin sprites
|
||||
//
|
||||
void R_PatchSkins(UINT16 wadnum)
|
||||
void R_PatchSkins(UINT16 wadnum, boolean mainfile)
|
||||
{
|
||||
UINT16 lump, lastlump = 0;
|
||||
char *buf;
|
||||
|
|
@ -973,7 +1060,8 @@ next_token:
|
|||
|
||||
R_FlushTranslationColormapCache();
|
||||
|
||||
CONS_Printf(M_GetText("Patched skin '%s'\n"), skin->name);
|
||||
if (mainfile == false)
|
||||
CONS_Printf(M_GetText("Patched skin '%s'\n"), skin->name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,11 +84,14 @@ void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002
|
|||
void SetFakePlayerSkin(player_t* player, INT32 skinnum);
|
||||
void SetRandomFakePlayerSkin(player_t* player, boolean fast);
|
||||
void ClearFakePlayerSkin(player_t* player);
|
||||
boolean R_SkinUsable(INT32 playernum, INT32 skinnum);
|
||||
UINT32 R_GetSkinAvailabilities(void);
|
||||
boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins);
|
||||
INT32 GetSkinNumClosestToStats(UINT8 kartspeed, UINT8 kartweight, UINT32 flags, boolean unlock);
|
||||
|
||||
UINT8 *R_GetSkinAvailabilities(boolean demolock);
|
||||
INT32 R_SkinAvailable(const char *name);
|
||||
void R_PatchSkins(UINT16 wadnum);
|
||||
void R_AddSkins(UINT16 wadnum);
|
||||
|
||||
void R_PatchSkins(UINT16 wadnum, boolean mainfile);
|
||||
void R_AddSkins(UINT16 wadnum, boolean mainfile);
|
||||
|
||||
UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player);
|
||||
|
||||
|
|
|
|||
|
|
@ -1148,6 +1148,10 @@ sfxinfo_t S_sfx[NUMSFX] =
|
|||
{"pass15", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
|
||||
{"pass16", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
|
||||
|
||||
// SRB2Kart - Blocked damage
|
||||
{"grownd", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SF_X8AWAYSOUND
|
||||
{"invind", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SF_X8AWAYSOUND
|
||||
|
||||
// SRB2Kart - Engine sounds
|
||||
// Engine class A
|
||||
{"krta00", false, 48, 65, -1, NULL, 0, -1, -1, LUMPERROR, ""},
|
||||
|
|
|
|||
|
|
@ -1212,6 +1212,10 @@ typedef enum
|
|||
sfx_pass15,
|
||||
sfx_pass16,
|
||||
|
||||
// Blocked damage SFX
|
||||
sfx_grownd,
|
||||
sfx_invind,
|
||||
|
||||
// Next up: UNIQUE ENGINE SOUNDS! Hoooooo boy...
|
||||
// Engine class A - Low Speed, Low Weight
|
||||
sfx_krta00,
|
||||
|
|
|
|||
|
|
@ -197,8 +197,8 @@ TYPEDEF (aatree_t);
|
|||
TYPEDEF (condition_t);
|
||||
TYPEDEF (conditionset_t);
|
||||
TYPEDEF (emblem_t);
|
||||
TYPEDEF (extraemblem_t);
|
||||
TYPEDEF (unlockable_t);
|
||||
TYPEDEF (gamedata_t);
|
||||
|
||||
// m_dllist.h
|
||||
TYPEDEF (mdllistitem_t);
|
||||
|
|
|
|||
118
src/y_inter.c
118
src/y_inter.c
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
#include "m_random.h" // M_RandomKey
|
||||
#include "g_input.h" // G_PlayerInputDown
|
||||
#include "k_hud.h" // K_DrawMapThumbnail
|
||||
#include "k_battle.h"
|
||||
#include "k_boss.h"
|
||||
#include "k_pwrlv.h"
|
||||
|
|
@ -1006,34 +1007,11 @@ void Y_VoteDrawer(void)
|
|||
y = (200-height)/2;
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
const char *str;
|
||||
patch_t *pic;
|
||||
UINT8 j, color;
|
||||
|
||||
if (i == 3)
|
||||
{
|
||||
str = "RANDOM";
|
||||
pic = randomlvl;
|
||||
}
|
||||
else
|
||||
{
|
||||
str = levelinfo[i].str;
|
||||
|
||||
pic = NULL;
|
||||
|
||||
if (mapheaderinfo[votelevels[i][0]])
|
||||
{
|
||||
pic = mapheaderinfo[votelevels[i][0]]->thumbnailPic;
|
||||
}
|
||||
|
||||
if (!pic)
|
||||
{
|
||||
pic = blanklvl;
|
||||
}
|
||||
}
|
||||
|
||||
if (selected[i])
|
||||
{
|
||||
const char *str;
|
||||
UINT8 sizeadd = selected[i];
|
||||
|
||||
for (j = 0; j <= splitscreen; j++) // another loop for drawing the selection backgrounds in the right order, grumble grumble..
|
||||
|
|
@ -1093,11 +1071,30 @@ void Y_VoteDrawer(void)
|
|||
sizeadd--;
|
||||
}
|
||||
|
||||
if (!levelinfo[i].encore)
|
||||
V_DrawSmallScaledPatch(BASEVIDWIDTH-100, y, V_SNAPTORIGHT, pic);
|
||||
if (i == 3)
|
||||
{
|
||||
str = "RANDOM";
|
||||
K_DrawLikeMapThumbnail(
|
||||
(BASEVIDWIDTH-100)<<FRACBITS, (y)<<FRACBITS,
|
||||
80<<FRACBITS,
|
||||
V_SNAPTORIGHT|(levelinfo[i].encore ? V_FLIP : 0),
|
||||
randomlvl,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
V_DrawFixedPatch((BASEVIDWIDTH-20)<<FRACBITS, (y)<<FRACBITS, FRACUNIT/2, V_FLIP|V_SNAPTORIGHT, pic, 0);
|
||||
str = levelinfo[i].str;
|
||||
K_DrawMapThumbnail(
|
||||
(BASEVIDWIDTH-100)<<FRACBITS, (y)<<FRACBITS,
|
||||
80<<FRACBITS,
|
||||
V_SNAPTORIGHT|(levelinfo[i].encore ? V_FLIP : 0),
|
||||
votelevels[i][0],
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (levelinfo[i].encore)
|
||||
{
|
||||
V_DrawFixedPatch((BASEVIDWIDTH-60)<<FRACBITS, ((y+25)<<FRACBITS) - (rubyheight<<1), FRACUNIT, V_SNAPTORIGHT, rubyicon, NULL);
|
||||
}
|
||||
|
||||
|
|
@ -1117,11 +1114,28 @@ void Y_VoteDrawer(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!levelinfo[i].encore)
|
||||
V_DrawTinyScaledPatch(BASEVIDWIDTH-60, y, V_SNAPTORIGHT, pic);
|
||||
if (i == 3)
|
||||
{
|
||||
K_DrawLikeMapThumbnail(
|
||||
(BASEVIDWIDTH-60)<<FRACBITS, (y)<<FRACBITS,
|
||||
40<<FRACBITS,
|
||||
V_SNAPTORIGHT|(levelinfo[i].encore ? V_FLIP : 0),
|
||||
randomlvl,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
V_DrawFixedPatch((BASEVIDWIDTH-20)<<FRACBITS, y<<FRACBITS, FRACUNIT/4, V_FLIP|V_SNAPTORIGHT, pic, 0);
|
||||
K_DrawMapThumbnail(
|
||||
(BASEVIDWIDTH-60)<<FRACBITS, (y)<<FRACBITS,
|
||||
40<<FRACBITS,
|
||||
V_SNAPTORIGHT|(levelinfo[i].encore ? V_FLIP : 0),
|
||||
votelevels[i][0],
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (levelinfo[i].encore)
|
||||
{
|
||||
V_DrawFixedPatch((BASEVIDWIDTH-40)<<FRACBITS, (y<<FRACBITS) + (25<<(FRACBITS-1)) - rubyheight, FRACUNIT/2, V_SNAPTORIGHT, rubyicon, NULL);
|
||||
}
|
||||
|
||||
|
|
@ -1146,27 +1160,6 @@ void Y_VoteDrawer(void)
|
|||
|
||||
if ((playeringame[i] && !players[i].spectator) && votes[i] != -1)
|
||||
{
|
||||
patch_t *pic;
|
||||
|
||||
if (votes[i] >= 3 && (i != pickedvote || voteendtic == -1))
|
||||
{
|
||||
pic = randomlvl;
|
||||
}
|
||||
else
|
||||
{
|
||||
pic = NULL;
|
||||
|
||||
if (mapheaderinfo[votelevels[votes[i]][0]])
|
||||
{
|
||||
pic = mapheaderinfo[votelevels[votes[i]][0]]->thumbnailPic;
|
||||
}
|
||||
|
||||
if (!pic)
|
||||
{
|
||||
pic = blanklvl;
|
||||
}
|
||||
}
|
||||
|
||||
if (!timer && i == voteclient.ranim)
|
||||
{
|
||||
V_DrawScaledPatch(x-18, y+9, V_SNAPTOLEFT, cursor);
|
||||
|
|
@ -1176,11 +1169,28 @@ void Y_VoteDrawer(void)
|
|||
V_DrawFill(x-1, y-1, 42, 27, levelinfo[votes[i]].gtc|V_SNAPTOLEFT);
|
||||
}
|
||||
|
||||
if (!levelinfo[votes[i]].encore)
|
||||
V_DrawTinyScaledPatch(x, y, V_SNAPTOLEFT, pic);
|
||||
if (votes[i] >= 3 && (i != pickedvote || voteendtic == -1))
|
||||
{
|
||||
K_DrawLikeMapThumbnail(
|
||||
(x)<<FRACBITS, (y)<<FRACBITS,
|
||||
40<<FRACBITS,
|
||||
V_SNAPTOLEFT|(levelinfo[votes[i]].encore ? V_FLIP : 0),
|
||||
randomlvl,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
V_DrawFixedPatch((x+40)<<FRACBITS, (y)<<FRACBITS, FRACUNIT/4, V_SNAPTOLEFT|V_FLIP, pic, 0);
|
||||
K_DrawMapThumbnail(
|
||||
(x)<<FRACBITS, (y)<<FRACBITS,
|
||||
40<<FRACBITS,
|
||||
V_SNAPTOLEFT|(levelinfo[votes[i]].encore ? V_FLIP : 0),
|
||||
votelevels[votes[i]][0],
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (levelinfo[votes[i]].encore)
|
||||
{
|
||||
V_DrawFixedPatch((x+20)<<FRACBITS, (y<<FRACBITS) + (25<<(FRACBITS-1)) - rubyheight, FRACUNIT/2, V_SNAPTOLEFT, rubyicon, NULL);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue