Merge branch 'master' of https://git.do.srb2.org/KartKrew/Kart into seeecret-colors

This commit is contained in:
toaster 2023-06-28 12:48:50 +01:00
commit 4344221252
65 changed files with 2915 additions and 1130 deletions

View file

@ -146,6 +146,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
k_serverstats.c k_serverstats.c
k_zvote.c k_zvote.c
k_mapuser.c k_mapuser.c
k_powerup.cpp
) )
if(SRB2_CONFIG_ENABLE_WEBM_MOVIES) if(SRB2_CONFIG_ENABLE_WEBM_MOVIES)

View file

@ -3255,7 +3255,11 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
{ {
// Running the callback here would mean a very dumb infinite loop. // Running the callback here would mean a very dumb infinite loop.
// We'll manually handle this here by changing the msg type. // We'll manually handle this here by changing the msg type.
msg = KICK_MSG_VOTE_KICK; if (msg != KICK_MSG_BANNED && msg != KICK_MSG_CUSTOM_BAN)
{
// of course, don't take the teeth out of a ban
msg = KICK_MSG_VOTE_KICK;
}
K_MidVoteFinalize(FRACUNIT); // Vote succeeded, so the delay is normal. K_MidVoteFinalize(FRACUNIT); // Vote succeeded, so the delay is normal.
} }
else else

View file

@ -464,22 +464,12 @@ static void D_Display(void)
} }
break; break;
case GS_ENDING:
F_EndingDrawer();
HU_Erase();
HU_Drawer();
break;
case GS_CUTSCENE: case GS_CUTSCENE:
F_CutsceneDrawer(); F_CutsceneDrawer();
HU_Erase(); HU_Erase();
HU_Drawer(); HU_Drawer();
break; break;
case GS_GAMEEND:
F_GameEndDrawer();
break;
case GS_EVALUATION: case GS_EVALUATION:
F_GameEvaluationDrawer(); F_GameEvaluationDrawer();
HU_Erase(); HU_Erase();
@ -980,7 +970,6 @@ void D_ClearState(void)
SplitScreen_OnChange(); SplitScreen_OnChange();
cht_debug = 0; cht_debug = 0;
emeralds = 0;
memset(&luabanks, 0, sizeof(luabanks)); memset(&luabanks, 0, sizeof(luabanks));
// In case someone exits out at the same time they start a time attack run, // In case someone exits out at the same time they start a time attack run,

View file

@ -67,6 +67,7 @@
#include "k_vote.h" #include "k_vote.h"
#include "k_zvote.h" #include "k_zvote.h"
#include "k_bot.h" #include "k_bot.h"
#include "k_powerup.h"
#ifdef SRB2_CONFIG_ENABLE_WEBM_MOVIES #ifdef SRB2_CONFIG_ENABLE_WEBM_MOVIES
#include "m_avrecorder.h" #include "m_avrecorder.h"
@ -433,6 +434,8 @@ consvar_t cv_kartbot = CVAR_INIT ("bots", "0", CV_NETVAR, kartbot_cons_t, NULL);
consvar_t cv_karteliminatelast = CVAR_INIT ("eliminatelast", "Yes", CV_NETVAR|CV_CALL, CV_YesNo, KartEliminateLast_OnChange); consvar_t cv_karteliminatelast = CVAR_INIT ("eliminatelast", "Yes", CV_NETVAR|CV_CALL, CV_YesNo, KartEliminateLast_OnChange);
consvar_t cv_thunderdome = CVAR_INIT ("thunderdome", "Off", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_kartusepwrlv = CVAR_INIT ("usepwrlv", "Yes", CV_NETVAR, CV_YesNo, NULL); consvar_t cv_kartusepwrlv = CVAR_INIT ("usepwrlv", "Yes", CV_NETVAR, CV_YesNo, NULL);
static CV_PossibleValue_t kartdebugitem_cons_t[] = static CV_PossibleValue_t kartdebugitem_cons_t[] =
@ -440,6 +443,11 @@ static CV_PossibleValue_t kartdebugitem_cons_t[] =
#define FOREACH( name, n ) { n, #name } #define FOREACH( name, n ) { n, #name }
KART_ITEM_ITERATOR, KART_ITEM_ITERATOR,
#undef FOREACH #undef FOREACH
{POWERUP_SMONITOR, "SMonitor"},
{POWERUP_BARRIER, "Barrier"},
{POWERUP_BUMPER, "Bumper"},
{POWERUP_BADGE, "Badge"},
{POWERUP_SUPERFLICKY, "SuperFlicky"},
{0} {0}
}; };
consvar_t cv_kartdebugitem = CVAR_INIT ("debugitem", "None", CV_NETVAR|CV_CHEAT, kartdebugitem_cons_t, NULL); consvar_t cv_kartdebugitem = CVAR_INIT ("debugitem", "None", CV_NETVAR|CV_CHEAT, kartdebugitem_cons_t, NULL);
@ -2070,6 +2078,11 @@ void D_Cheat(INT32 playernum, INT32 cheat, ...)
COPY(WRITEUINT8, unsigned int); COPY(WRITEUINT8, unsigned int);
break; break;
case CHEAT_GIVEPOWERUP:
COPY(WRITEUINT8, unsigned int);
COPY(WRITEUINT16, unsigned int);
break;
case CHEAT_SCORE: case CHEAT_SCORE:
COPY(WRITEUINT32, UINT32); COPY(WRITEUINT32, UINT32);
break; break;
@ -5644,6 +5657,22 @@ static void Command_Mapmd5_f(void)
CONS_Printf(M_GetText("You must be in a level to use this.\n")); CONS_Printf(M_GetText("You must be in a level to use this.\n"));
} }
boolean G_GamestateUsesExitLevel(void)
{
if (demo.playback)
return false;
switch (gamestate)
{
case GS_LEVEL:
case GS_CREDITS:
return true;
default:
return false;
}
}
static void Command_ExitLevel_f(void) static void Command_ExitLevel_f(void)
{ {
if (!(server || (IsPlayerAdmin(consoleplayer)))) if (!(server || (IsPlayerAdmin(consoleplayer))))
@ -5654,7 +5683,7 @@ static void Command_ExitLevel_f(void)
{ {
CONS_Printf(M_GetText("This cannot be used without cheats enabled.\n")); CONS_Printf(M_GetText("This cannot be used without cheats enabled.\n"));
} }
else if (( gamestate != GS_LEVEL && gamestate != GS_CREDITS ) || demo.playback) else if (G_GamestateUsesExitLevel() == false)
{ {
CONS_Printf(M_GetText("You must be in a level to use this.\n")); CONS_Printf(M_GetText("You must be in a level to use this.\n"));
} }
@ -5680,6 +5709,9 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
return; return;
} }
if (G_GamestateUsesExitLevel() == false)
return;
G_ExitLevel(); G_ExitLevel();
} }
@ -6110,6 +6142,20 @@ static void Got_Cheat(UINT8 **cp, INT32 playernum)
break; break;
} }
case CHEAT_GIVEPOWERUP: {
UINT8 powerup = READUINT8(*cp);
UINT16 time = READUINT16(*cp);
// FIXME: we should have actual KITEM_ name array
const char *powerupname = cv_kartdebugitem.PossibleValue[
1 + NUMKARTITEMS + (powerup - FIRSTPOWERUP)].strvalue;
K_GivePowerUp(player, powerup, time);
CV_CheaterWarning(playernum, va("give powerup %s %d tics", powerupname, time));
break;
}
case CHEAT_SCORE: { case CHEAT_SCORE: {
UINT32 score = READUINT32(*cp); UINT32 score = READUINT32(*cp);
@ -6252,7 +6298,6 @@ void Command_ExitGame_f(void)
SplitScreen_OnChange(); SplitScreen_OnChange();
cht_debug = 0; cht_debug = 0;
emeralds = 0;
memset(&luabanks, 0, sizeof(luabanks)); memset(&luabanks, 0, sizeof(luabanks));
if (dirmenu) if (dirmenu)
@ -6417,7 +6462,18 @@ static void Command_KartGiveItem_f(void)
} }
} }
if (item < NUMKARTITEMS) if (item >= FIRSTPOWERUP)
{
INT32 amt;
if (ac > 2)
amt = atoi(COM_Argv(2));
else
amt = BATTLE_POWERUP_TIME;
D_Cheat(consoleplayer, CHEAT_GIVEPOWERUP, item, amt);
}
else if (item < NUMKARTITEMS)
{ {
INT32 amt; INT32 amt;

View file

@ -88,6 +88,7 @@ extern consvar_t cv_kartspeedometer;
extern consvar_t cv_kartvoices; extern consvar_t cv_kartvoices;
extern consvar_t cv_kartbot; extern consvar_t cv_kartbot;
extern consvar_t cv_karteliminatelast; extern consvar_t cv_karteliminatelast;
extern consvar_t cv_thunderdome;
extern consvar_t cv_kartusepwrlv; extern consvar_t cv_kartusepwrlv;
extern consvar_t cv_votetime; extern consvar_t cv_votetime;
@ -241,6 +242,7 @@ void WeaponPref_Parse(UINT8 **cp, INT32 playernum);
void D_SendPlayerConfig(UINT8 n); void D_SendPlayerConfig(UINT8 n);
void Command_ExitGame_f(void); void Command_ExitGame_f(void);
void Command_Retry_f(void); void Command_Retry_f(void);
boolean G_GamestateUsesExitLevel(void);
void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore
void D_MapChange(UINT16 pmapnum, INT32 pgametype, boolean pencoremode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pforcespecialstage); void D_MapChange(UINT16 pmapnum, INT32 pgametype, boolean pencoremode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pforcespecialstage);
void D_SetupVote(void); void D_SetupVote(void);

View file

@ -179,7 +179,16 @@ typedef enum
KRITEM_DUALJAWZ, KRITEM_DUALJAWZ,
KRITEM_TRIPLEGACHABOM, KRITEM_TRIPLEGACHABOM,
NUMKARTRESULTS NUMKARTRESULTS,
// Power-ups exist in the same enum as items so it's easy
// for paper items to be reused for them.
FIRSTPOWERUP,
POWERUP_SMONITOR = FIRSTPOWERUP,
POWERUP_BARRIER,
POWERUP_BUMPER,
POWERUP_BADGE,
POWERUP_SUPERFLICKY,
} kartitems_t; } kartitems_t;
typedef enum typedef enum
@ -192,6 +201,17 @@ typedef enum
NUMKARTSHIELDS NUMKARTSHIELDS
} kartshields_t; } kartshields_t;
typedef enum
{
KSM_BAR,
KSM_DOUBLEBAR,
KSM_TRIPLEBAR,
KSM_RING,
KSM_SEVEN,
KSM_JACKPOT,
KSM__MAX,
} kartslotmachine_t;
typedef enum typedef enum
{ {
KSPIN_THRUST = (1<<0), KSPIN_THRUST = (1<<0),
@ -410,6 +430,7 @@ struct itemroulette_t
tic_t elapsed; tic_t elapsed;
boolean eggman; boolean eggman;
boolean ringbox;
}; };
// enum for bot item priorities // enum for bot item priorities
@ -439,6 +460,11 @@ typedef struct {
boolean flip; boolean flip;
} sonicloopvars_t; } sonicloopvars_t;
// player_t struct for power-ups
struct powerupvars_t {
mobj_t *flickyController;
};
// player_t struct for all alternative viewpoint variables // player_t struct for all alternative viewpoint variables
struct altview_t struct altview_t
{ {
@ -756,6 +782,7 @@ struct player_t
mobj_t *sliptideZipIndicator; mobj_t *sliptideZipIndicator;
mobj_t *whip; mobj_t *whip;
mobj_t *hand; mobj_t *hand;
mobj_t *flickyAttacker;
UINT8 instaShieldCooldown; UINT8 instaShieldCooldown;
UINT8 guardCooldown; UINT8 guardCooldown;
@ -767,6 +794,9 @@ struct player_t
boolean markedfordeath; boolean markedfordeath;
UINT8 ringboxdelay; // Delay until Ring Box auto-activates
UINT8 ringboxaward; // Where did we stop?
fixed_t outrun; // Milky Way road effect fixed_t outrun; // Milky Way road effect
uint8_t public_key[PUBKEYLENGTH]; uint8_t public_key[PUBKEYLENGTH];
@ -777,6 +807,7 @@ struct player_t
sonicloopvars_t loop; sonicloopvars_t loop;
roundconditions_t roundconditions; roundconditions_t roundconditions;
powerupvars_t powerup;
}; };
// WARNING FOR ANYONE ABOUT TO ADD SOMETHING TO THE PLAYER STRUCT, G_PlayerReborn WANTS YOU TO SUFFER // WARNING FOR ANYONE ABOUT TO ADD SOMETHING TO THE PLAYER STRUCT, G_PlayerReborn WANTS YOU TO SUFFER

View file

@ -2395,7 +2395,7 @@ void readunlockable(MYFILE *f, INT32 num)
Z_Free(s); Z_Free(s);
} }
static void readcondition(UINT8 set, UINT32 id, char *word2) static void readcondition(UINT16 set, UINT32 id, char *word2)
{ {
INT32 i; INT32 i;
char *params[5]; // condition, requirement, extra info, extra info, stringvar char *params[5]; // condition, requirement, extra info, extra info, stringvar
@ -2585,6 +2585,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
} }
} }
else if ((offset=0) || fastcmp(params[0], "ADDON") else if ((offset=0) || fastcmp(params[0], "ADDON")
|| (++offset && fastcmp(params[0], "CREDITS"))
|| (++offset && fastcmp(params[0], "REPLAY")) || (++offset && fastcmp(params[0], "REPLAY"))
|| (++offset && fastcmp(params[0], "CRASH"))) || (++offset && fastcmp(params[0], "CRASH")))
{ {
@ -2861,13 +2862,13 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
M_AddRawCondition(set, (UINT8)id, ty, re, x1, x2, stringvar); M_AddRawCondition(set, (UINT8)id, ty, re, x1, x2, stringvar);
} }
void readconditionset(MYFILE *f, UINT8 setnum) void readconditionset(MYFILE *f, UINT16 setnum)
{ {
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
char *word = s; char *word = s;
char *word2; char *word2;
char *tmp; char *tmp;
UINT8 id; UINT16 id;
UINT8 previd = 0; UINT8 previd = 0;
M_ClearConditionSet(setnum); M_ClearConditionSet(setnum);
@ -2904,21 +2905,26 @@ void readconditionset(MYFILE *f, UINT8 setnum)
if (fastncmp(word, "CONDITION", 9)) if (fastncmp(word, "CONDITION", 9))
{ {
id = (UINT8)atoi(word + 9); id = atoi(word + 9);
if (id == 0) if (id == 0)
{ {
deh_warning("Condition set %d: unknown word '%s'", setnum+1, word); deh_warning("Condition set %d: unknown word '%s'", setnum+1, word);
continue; continue;
} }
else if (previd > id) if (previd > id)
{ {
// out of order conditions can cause problems, so enforce proper order // 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+1); deh_warning("Condition set %d: conditions are out of order, ignoring this line", setnum+1);
continue; continue;
} }
previd = id; if (id > UINT8_MAX)
{
deh_warning("Condition set %d: too many Condition# types, ignoring this line", setnum+1);
continue;
}
previd = (UINT8)id;
readcondition(setnum, id, word2); readcondition(setnum, (UINT8)id, word2);
} }
else else
deh_warning("Condition set %d: unknown word '%s'", setnum, word); deh_warning("Condition set %d: unknown word '%s'", setnum, word);
@ -3122,13 +3128,13 @@ void readmaincfg(MYFILE *f, boolean mainfile)
if (creditscutscene > 128) if (creditscutscene > 128)
creditscutscene = 128; creditscutscene = 128;
} }
else if (fastcmp(word, "USEBLACKROCK")) else if (fastcmp(word, "USESEAL"))
{ {
useBlackRock = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y'); useSeal = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y');
} }
else if (fastcmp(word, "LOOPTITLE")) else if (fastcmp(word, "LOOPTITLE"))
{ {
looptitle = (value || word2[0] == 'T' || word2[0] == 'Y'); looptitle = (value != 0 || word2[0] == 'T' || word2[0] == 'Y');
titlechanged = true; titlechanged = true;
} }
else if (fastcmp(word, "TITLEMAP")) else if (fastcmp(word, "TITLEMAP"))
@ -3139,7 +3145,7 @@ void readmaincfg(MYFILE *f, boolean mainfile)
} }
else if (fastcmp(word, "HIDETITLEPICS") || fastcmp(word, "TITLEPICSHIDE")) else if (fastcmp(word, "HIDETITLEPICS") || fastcmp(word, "TITLEPICSHIDE"))
{ {
hidetitlepics = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y'); hidetitlepics = (boolean)(value != 0 || word2[0] == 'T' || word2[0] == 'Y');
titlechanged = true; titlechanged = true;
} }
else if (fastcmp(word, "TITLEPICSMODE")) else if (fastcmp(word, "TITLEPICSMODE"))
@ -3367,14 +3373,6 @@ void readwipes(MYFILE *f)
else if (fastcmp(pword, "FINAL")) else if (fastcmp(pword, "FINAL"))
wipeoffset = wipe_evaluation_final; wipeoffset = wipe_evaluation_final;
} }
else if (fastncmp(word, "GAMEEND_", 8))
{
pword = word + 8;
if (fastcmp(pword, "TOBLACK"))
wipeoffset = wipe_gameend_toblack;
else if (fastcmp(pword, "FINAL"))
wipeoffset = wipe_gameend_final;
}
else if (fastncmp(word, "CEREMONY_", 9)) else if (fastncmp(word, "CEREMONY_", 9))
{ {
pword = word + 9; pword = word + 9;
@ -3440,6 +3438,15 @@ static void invalidateacrosscups(UINT16 map)
mapheaderinfo[map]->cup = NULL; mapheaderinfo[map]->cup = NULL;
} }
static char *MapNameOrRemoval(char *name)
{
if (name[0] == '\0'
|| (name[0] == '/' && name[1] == '\0'))
return NULL;
return Z_StrDup(name);
}
void readcupheader(MYFILE *f, cupheader_t *cup) void readcupheader(MYFILE *f, cupheader_t *cup)
{ {
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
@ -3541,8 +3548,12 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
break; break;
} }
cup->levellist[CUPCACHE_BONUS + cup->numbonus] = Z_StrDup(tmp); cup->levellist[CUPCACHE_BONUS + cup->numbonus] = MapNameOrRemoval(tmp);
cup->cachedlevels[CUPCACHE_BONUS + cup->numbonus] = NEXTMAP_INVALID; cup->cachedlevels[CUPCACHE_BONUS + cup->numbonus] = NEXTMAP_INVALID;
if (cup->levellist[CUPCACHE_BONUS + cup->numbonus] == NULL)
break;
cup->numbonus++; cup->numbonus++;
} while((tmp = strtok(NULL,",")) != NULL); } while((tmp = strtok(NULL,",")) != NULL);
} }
@ -3550,9 +3561,16 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
{ {
invalidateacrosscups(cup->cachedlevels[CUPCACHE_SPECIAL]); invalidateacrosscups(cup->cachedlevels[CUPCACHE_SPECIAL]);
Z_Free(cup->levellist[CUPCACHE_SPECIAL]); Z_Free(cup->levellist[CUPCACHE_SPECIAL]);
cup->levellist[CUPCACHE_SPECIAL] = Z_StrDup(word2); cup->levellist[CUPCACHE_SPECIAL] = MapNameOrRemoval(word2);
cup->cachedlevels[CUPCACHE_SPECIAL] = NEXTMAP_INVALID; cup->cachedlevels[CUPCACHE_SPECIAL] = NEXTMAP_INVALID;
} }
else if (fastcmp(word, "ALTPODIUM"))
{
invalidateacrosscups(cup->cachedlevels[CUPCACHE_PODIUM]);
Z_Free(cup->levellist[CUPCACHE_PODIUM]);
cup->levellist[CUPCACHE_PODIUM] = MapNameOrRemoval(word2);
cup->cachedlevels[CUPCACHE_PODIUM] = NEXTMAP_INVALID;
}
else if (fastcmp(word, "EMERALDNUM")) else if (fastcmp(word, "EMERALDNUM"))
{ {
if (i >= 0 && i <= 14) if (i >= 0 && i <= 14)
@ -3560,6 +3578,10 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
else else
deh_warning("%s Cup: invalid emerald number %d", cup->name, i); deh_warning("%s Cup: invalid emerald number %d", cup->name, i);
} }
else if (fastcmp(word, "PLAYCREDITS"))
{
cup->playcredits = (i != 0 || word2[0] == 'T' || word2[0] == 'Y');
}
else else
deh_warning("%s Cup: unknown word '%s'", cup->name, word); deh_warning("%s Cup: unknown word '%s'", cup->name, word);
} }

View file

@ -62,7 +62,7 @@ skincolornum_t get_skincolor(const char *word);
void readwipes(MYFILE *f); void readwipes(MYFILE *f);
void readmaincfg(MYFILE *f, boolean mainfile); void readmaincfg(MYFILE *f, boolean mainfile);
void readconditionset(MYFILE *f, UINT8 setnum); void readconditionset(MYFILE *f, UINT16 setnum);
void readunlockable(MYFILE *f, INT32 num); void readunlockable(MYFILE *f, INT32 num);
void reademblemdata(MYFILE *f, INT32 num); void reademblemdata(MYFILE *f, INT32 num);
void readsound(MYFILE *f, INT32 num); void readsound(MYFILE *f, INT32 num);

View file

@ -283,7 +283,6 @@ actionpointer_t actionpointers[] =
{{A_ChangeHeight}, "A_CHANGEHEIGHT"}, {{A_ChangeHeight}, "A_CHANGEHEIGHT"},
// SRB2Kart // SRB2Kart
{{A_ItemPop}, "A_ITEMPOP"},
{{A_JawzExplode}, "A_JAWZEXPLODE"}, {{A_JawzExplode}, "A_JAWZEXPLODE"},
{{A_SSMineSearch}, "A_SSMINESEARCH"}, {{A_SSMineSearch}, "A_SSMINESEARCH"},
{{A_SSMineExplode}, "A_SSMINEEXPLODE"}, {{A_SSMineExplode}, "A_SSMINEEXPLODE"},
@ -3223,7 +3222,20 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_RANDOMITEM10", "S_RANDOMITEM10",
"S_RANDOMITEM11", "S_RANDOMITEM11",
"S_RANDOMITEM12", "S_RANDOMITEM12",
"S_DEADRANDOMITEM",
// Ring Box
"S_RINGBOX1",
"S_RINGBOX2",
"S_RINGBOX3",
"S_RINGBOX4",
"S_RINGBOX5",
"S_RINGBOX6",
"S_RINGBOX7",
"S_RINGBOX8",
"S_RINGBOX9",
"S_RINGBOX10",
"S_RINGBOX11",
"S_RINGBOX12",
// Sphere Box (for Battle) // Sphere Box (for Battle)
"S_SPHEREBOX1", "S_SPHEREBOX1",
@ -3238,7 +3250,6 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_SPHEREBOX10", "S_SPHEREBOX10",
"S_SPHEREBOX11", "S_SPHEREBOX11",
"S_SPHEREBOX12", "S_SPHEREBOX12",
"S_DEADSPHEREBOX",
// Random Item Pop // Random Item Pop
"S_RANDOMITEMPOP1", "S_RANDOMITEMPOP1",
@ -4571,6 +4582,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_GACHABOM_EXPLOSION_4", "S_GACHABOM_EXPLOSION_4",
"S_GACHABOM_WAITING", "S_GACHABOM_WAITING",
"S_GACHABOM_RETURNING", "S_GACHABOM_RETURNING",
"S_SUPER_FLICKY",
}; };
// RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", // RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1",
@ -5697,6 +5710,9 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_LOOPENDPOINT", "MT_LOOPENDPOINT",
"MT_LOOPCENTERPOINT", "MT_LOOPCENTERPOINT",
"MT_SUPER_FLICKY",
"MT_SUPER_FLICKY_CONTROLLER",
}; };
const char *const MOBJFLAG_LIST[] = { const char *const MOBJFLAG_LIST[] = {
@ -6835,9 +6851,7 @@ struct int_const_s const INT_CONST[] = {
{"GS_MENU",GS_MENU}, {"GS_MENU",GS_MENU},
{"GS_CREDITS",GS_CREDITS}, {"GS_CREDITS",GS_CREDITS},
{"GS_EVALUATION",GS_EVALUATION}, {"GS_EVALUATION",GS_EVALUATION},
{"GS_GAMEEND",GS_GAMEEND},
{"GS_INTRO",GS_INTRO}, {"GS_INTRO",GS_INTRO},
{"GS_ENDING",GS_ENDING},
{"GS_CUTSCENE",GS_CUTSCENE}, {"GS_CUTSCENE",GS_CUTSCENE},
{"GS_DEDICATEDSERVER",GS_DEDICATEDSERVER}, {"GS_DEDICATEDSERVER",GS_DEDICATEDSERVER},
{"GS_WAITINGPLAYERS",GS_WAITINGPLAYERS}, {"GS_WAITINGPLAYERS",GS_WAITINGPLAYERS},
@ -6856,6 +6870,12 @@ struct int_const_s const INT_CONST[] = {
{"KRITEM_DUALJAWZ",KRITEM_DUALJAWZ}, {"KRITEM_DUALJAWZ",KRITEM_DUALJAWZ},
{"KRITEM_TRIPLEGACHABOM",KRITEM_TRIPLEGACHABOM}, {"KRITEM_TRIPLEGACHABOM",KRITEM_TRIPLEGACHABOM},
{"NUMKARTRESULTS",NUMKARTRESULTS}, {"NUMKARTRESULTS",NUMKARTRESULTS},
{"FIRSTPOWERUP",FIRSTPOWERUP},
{"POWERUP_SMONITOR",POWERUP_SMONITOR},
{"POWERUP_BARRIER",POWERUP_BARRIER},
{"POWERUP_BUMPER",POWERUP_BUMPER},
{"POWERUP_BADGE",POWERUP_BADGE},
{"POWERUP_SUPERFLICKY",POWERUP_SUPERFLICKY},
// kartshields_t // kartshields_t
{"KSHIELD_NONE",KSHIELD_NONE}, {"KSHIELD_NONE",KSHIELD_NONE},

View file

@ -453,7 +453,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
ignorelines(f); ignorelines(f);
} }
else if (i > 0 && i <= MAXCONDITIONSETS) else if (i > 0 && i <= MAXCONDITIONSETS)
readconditionset(f, (UINT8)(i-1)); readconditionset(f, (UINT16)(i-1));
else else
{ {
deh_warning("Condition set number %d out of range (1 - %d)", i, MAXCONDITIONSETS); deh_warning("Condition set number %d out of range (1 - %d)", i, MAXCONDITIONSETS);

View file

@ -383,7 +383,8 @@ struct customoption_t
#define CUPCACHE_BONUS MAXLEVELLIST #define CUPCACHE_BONUS MAXLEVELLIST
#define MAXBONUSLIST 2 #define MAXBONUSLIST 2
#define CUPCACHE_SPECIAL (CUPCACHE_BONUS+MAXBONUSLIST) #define CUPCACHE_SPECIAL (CUPCACHE_BONUS+MAXBONUSLIST)
#define CUPCACHE_MAX (CUPCACHE_SPECIAL+1) #define CUPCACHE_PODIUM (CUPCACHE_SPECIAL+1)
#define CUPCACHE_MAX (CUPCACHE_PODIUM+1)
#define MAXCUPNAME 16 // includes \0, for cleaner savedata #define MAXCUPNAME 16 // includes \0, for cleaner savedata
@ -401,6 +402,9 @@ struct cupheader_t
UINT8 numlevels; ///< Number of levels defined in levellist UINT8 numlevels; ///< Number of levels defined in levellist
UINT8 numbonus; ///< Number of bonus stages defined 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) UINT8 emeraldnum; ///< ID of Emerald to use for special stage (1-7 for Chaos Emeralds, 8-14 for Super Emeralds, 0 for no emerald)
boolean playcredits; ///< Play the credits?
cupwindata_t windata[4]; ///< Data for cup visitation cupwindata_t windata[4]; ///< Data for cup visitation
cupheader_t *next; ///< Next cup in linked list cupheader_t *next; ///< Next cup in linked list
}; };
@ -687,8 +691,6 @@ typedef enum
EMERALD_ALL = EMERALD_ALLCHAOS|EMERALD_ALLSUPER EMERALD_ALL = EMERALD_ALLCHAOS|EMERALD_ALLSUPER
} emeraldflags_t; } emeraldflags_t;
extern UINT16 emeralds;
#define ALLCHAOSEMERALDS(v) ((v & EMERALD_ALLCHAOS) == EMERALD_ALLCHAOS) #define ALLCHAOSEMERALDS(v) ((v & EMERALD_ALLCHAOS) == EMERALD_ALLCHAOS)
#define ALLSUPEREMERALDS(v) ((v & EMERALD_ALLSUPER) == EMERALD_ALLSUPER) #define ALLSUPEREMERALDS(v) ((v & EMERALD_ALLSUPER) == EMERALD_ALLSUPER)
#define ALLEMERALDS(v) ((v & EMERALD_ALL) == EMERALD_ALL) #define ALLEMERALDS(v) ((v & EMERALD_ALL) == EMERALD_ALL)
@ -744,7 +746,7 @@ extern INT32 flameseg;
extern UINT8 introtoplay; extern UINT8 introtoplay;
extern UINT8 creditscutscene; extern UINT8 creditscutscene;
extern UINT8 useBlackRock; extern UINT8 useSeal;
extern UINT8 use1upSound; extern UINT8 use1upSound;
extern UINT8 maxXtraLife; // Max extra lives from rings extern UINT8 maxXtraLife; // Max extra lives from rings

File diff suppressed because it is too large Load diff

View file

@ -33,7 +33,6 @@ boolean F_CutsceneResponder(event_t *ev);
boolean F_CreditResponder(event_t *ev); boolean F_CreditResponder(event_t *ev);
// Called by main loop. // Called by main loop.
void F_GameEndTicker(void);
void F_IntroTicker(void); void F_IntroTicker(void);
void F_TitleScreenTicker(boolean run); void F_TitleScreenTicker(boolean run);
void F_CutsceneTicker(void); void F_CutsceneTicker(void);
@ -41,7 +40,6 @@ void F_TitleDemoTicker(void);
void F_TextPromptTicker(void); void F_TextPromptTicker(void);
// Called by main loop. // Called by main loop.
void F_GameEndDrawer(void);
void F_IntroDrawer(void); void F_IntroDrawer(void);
void F_TitleScreenDrawer(void); void F_TitleScreenDrawer(void);
void F_SkyScroll(INT32 scrollxspeed, INT32 scrollyspeed, const char *patchname); void F_SkyScroll(INT32 scrollxspeed, INT32 scrollyspeed, const char *patchname);
@ -54,9 +52,6 @@ void F_GameEvaluationDrawer(void);
void F_StartGameEvaluation(void); void F_StartGameEvaluation(void);
void F_GameEvaluationTicker(void); void F_GameEvaluationTicker(void);
void F_EndingTicker(void);
void F_EndingDrawer(void);
void F_CreditTicker(void); void F_CreditTicker(void);
void F_CreditDrawer(void); void F_CreditDrawer(void);
@ -173,10 +168,8 @@ enum
wipe_menu_toblack, wipe_menu_toblack,
wipe_credits_toblack, wipe_credits_toblack,
wipe_evaluation_toblack, wipe_evaluation_toblack,
wipe_gameend_toblack,
wipe_ceremony_toblack, wipe_ceremony_toblack,
wipe_intro_toblack, wipe_intro_toblack,
wipe_ending_toblack,
wipe_cutscene_toblack, wipe_cutscene_toblack,
// Specialized wipes // Specialized wipes
@ -192,10 +185,8 @@ enum
wipe_menu_final, wipe_menu_final,
wipe_credits_final, wipe_credits_final,
wipe_evaluation_final, wipe_evaluation_final,
wipe_gameend_final,
wipe_ceremony_final, wipe_ceremony_final,
wipe_intro_final, wipe_intro_final,
wipe_ending_final,
wipe_cutscene_final, wipe_cutscene_final,
// custom intermissions // custom intermissions

View file

@ -63,10 +63,8 @@ UINT8 wipedefs[NUMWIPEDEFS] = {
1, // wipe_menu_toblack 1, // wipe_menu_toblack
99, // wipe_credits_toblack 99, // wipe_credits_toblack
0, // wipe_evaluation_toblack 0, // wipe_evaluation_toblack
0, // wipe_gameend_toblack
0, // wipe_ceremony_toblack 0, // wipe_ceremony_toblack
UINT8_MAX, // wipe_intro_toblack (hardcoded) UINT8_MAX, // wipe_intro_toblack (hardcoded)
99, // wipe_ending_toblack (hardcoded)
99, // wipe_cutscene_toblack (hardcoded) 99, // wipe_cutscene_toblack (hardcoded)
72, // wipe_encore_toinvert 72, // wipe_encore_toinvert
@ -80,10 +78,8 @@ UINT8 wipedefs[NUMWIPEDEFS] = {
1, // wipe_menu_final 1, // wipe_menu_final
99, // wipe_credits_final 99, // wipe_credits_final
0, // wipe_evaluation_final 0, // wipe_evaluation_final
0, // wipe_gameend_final
0, // wipe_ceremony_final 0, // wipe_ceremony_final
99, // wipe_intro_final (hardcoded) 99, // wipe_intro_final (hardcoded)
99, // wipe_ending_final (hardcoded)
99 // wipe_cutscene_final (hardcoded) 99 // wipe_cutscene_final (hardcoded)
}; };
@ -98,10 +94,8 @@ static boolean g_wipedef_toblack[NUMWIPEDEFS] = {
true, // wipe_menu_toblack true, // wipe_menu_toblack
true, // wipe_credits_toblack true, // wipe_credits_toblack
true, // wipe_evaluation_toblack true, // wipe_evaluation_toblack
true, // wipe_gameend_toblack
true, // wipe_ceremony_toblack true, // wipe_ceremony_toblack
true, // wipe_intro_toblack (hardcoded) true, // wipe_intro_toblack (hardcoded)
true, // wipe_ending_toblack (hardcoded)
true, // wipe_cutscene_toblack (hardcoded) true, // wipe_cutscene_toblack (hardcoded)
false, // wipe_encore_toinvert false, // wipe_encore_toinvert
@ -115,10 +109,8 @@ static boolean g_wipedef_toblack[NUMWIPEDEFS] = {
true, // wipe_menu_final true, // wipe_menu_final
true, // wipe_credits_final true, // wipe_credits_final
true, // wipe_evaluation_final true, // wipe_evaluation_final
true, // wipe_gameend_final
true, // wipe_ceremony_final true, // wipe_ceremony_final
true, // wipe_intro_final (hardcoded) true, // wipe_intro_final (hardcoded)
true, // wipe_ending_final (hardcoded)
true // wipe_cutscene_final (hardcoded) true // wipe_cutscene_final (hardcoded)
}; };
@ -133,10 +125,8 @@ static boolean g_wipedef_toinvert[NUMWIPEDEFS] = {
false, // wipe_menu_toblack false, // wipe_menu_toblack
false, // wipe_credits_toblack false, // wipe_credits_toblack
false, // wipe_evaluation_toblack false, // wipe_evaluation_toblack
false, // wipe_gameend_toblack
false, // wipe_ceremony_toblack false, // wipe_ceremony_toblack
false, // wipe_intro_toblack (hardcoded) false, // wipe_intro_toblack (hardcoded)
false, // wipe_ending_toblack (hardcoded)
false, // wipe_cutscene_toblack (hardcoded) false, // wipe_cutscene_toblack (hardcoded)
true, // wipe_encore_toinvert true, // wipe_encore_toinvert
@ -150,10 +140,8 @@ static boolean g_wipedef_toinvert[NUMWIPEDEFS] = {
false, // wipe_menu_final false, // wipe_menu_final
false, // wipe_credits_final false, // wipe_credits_final
false, // wipe_evaluation_final false, // wipe_evaluation_final
false, // wipe_gameend_final
false, // wipe_ceremony_final false, // wipe_ceremony_final
false, // wipe_intro_final (hardcoded) false, // wipe_intro_final (hardcoded)
false, // wipe_ending_final (hardcoded)
false // wipe_cutscene_final (hardcoded) false // wipe_cutscene_final (hardcoded)
}; };
@ -168,10 +156,8 @@ static boolean g_wipedef_towhite[NUMWIPEDEFS] = {
false, // wipe_menu_toblack false, // wipe_menu_toblack
false, // wipe_credits_toblack false, // wipe_credits_toblack
false, // wipe_evaluation_toblack false, // wipe_evaluation_toblack
false, // wipe_gameend_toblack
false, // wipe_ceremony_toblack false, // wipe_ceremony_toblack
false, // wipe_intro_toblack (hardcoded) false, // wipe_intro_toblack (hardcoded)
false, // wipe_ending_toblack (hardcoded)
false, // wipe_cutscene_toblack (hardcoded) false, // wipe_cutscene_toblack (hardcoded)
false, // wipe_encore_toinvert false, // wipe_encore_toinvert
@ -185,10 +171,8 @@ static boolean g_wipedef_towhite[NUMWIPEDEFS] = {
false, // wipe_menu_final false, // wipe_menu_final
false, // wipe_credits_final false, // wipe_credits_final
false, // wipe_evaluation_final false, // wipe_evaluation_final
false, // wipe_gameend_final
false, // wipe_ceremony_final false, // wipe_ceremony_final
false, // wipe_intro_final (hardcoded) false, // wipe_intro_final (hardcoded)
false, // wipe_ending_final (hardcoded)
false // wipe_cutscene_final (hardcoded) false // wipe_cutscene_final (hardcoded)
}; };
@ -203,10 +187,8 @@ static boolean g_wipedef_crossfade[NUMWIPEDEFS] = {
false, // wipe_menu_toblack false, // wipe_menu_toblack
false, // wipe_credits_toblack false, // wipe_credits_toblack
false, // wipe_evaluation_toblack false, // wipe_evaluation_toblack
false, // wipe_gameend_toblack
false, // wipe_ceremony_toblack false, // wipe_ceremony_toblack
false, // wipe_intro_toblack (hardcoded) false, // wipe_intro_toblack (hardcoded)
false, // wipe_ending_toblack (hardcoded)
false, // wipe_cutscene_toblack (hardcoded) false, // wipe_cutscene_toblack (hardcoded)
false, // wipe_encore_toinvert false, // wipe_encore_toinvert
@ -220,10 +202,8 @@ static boolean g_wipedef_crossfade[NUMWIPEDEFS] = {
true, // wipe_menu_final true, // wipe_menu_final
true, // wipe_credits_final true, // wipe_credits_final
true, // wipe_evaluation_final true, // wipe_evaluation_final
true, // wipe_gameend_final
true, // wipe_ceremony_final true, // wipe_ceremony_final
true, // wipe_intro_final (hardcoded) true, // wipe_intro_final (hardcoded)
true, // wipe_ending_final (hardcoded)
true // wipe_cutscene_final (hardcoded) true // wipe_cutscene_final (hardcoded)
}; };

View file

@ -140,8 +140,6 @@ boolean usedCheats = false; // Set when a "cheats on" is ever used.
UINT8 paused; UINT8 paused;
UINT8 modeattacking = ATTACKING_NONE; UINT8 modeattacking = ATTACKING_NONE;
boolean imcontinuing = false; boolean imcontinuing = false;
boolean runemeraldmanager = false;
UINT16 emeraldspawndelay = 60*TICRATE;
// menu demo things // menu demo things
UINT8 numDemos = 0; UINT8 numDemos = 0;
@ -205,7 +203,6 @@ static boolean retryingmodeattack = false;
UINT8 stagefailed; // Used for GEMS BONUS? Also to see if you beat the stage. UINT8 stagefailed; // Used for GEMS BONUS? Also to see if you beat the stage.
UINT16 emeralds;
INT32 luabanks[NUM_LUABANKS]; INT32 luabanks[NUM_LUABANKS];
// Temporary holding place for nights data for the current map // Temporary holding place for nights data for the current map
@ -264,7 +261,7 @@ UINT8 maxXtraLife = 2; // Max extra lives from rings
UINT8 introtoplay; UINT8 introtoplay;
UINT8 creditscutscene; UINT8 creditscutscene;
UINT8 useBlackRock = 1; UINT8 useSeal = 1;
// Emerald locations // Emerald locations
mobj_t *hunt1; mobj_t *hunt1;
@ -1785,7 +1782,7 @@ boolean G_Responder(event_t *ev)
return true; return true;
} }
} }
else if (gamestate == GS_CREDITS || gamestate == GS_ENDING) // todo: keep ending here? else if (gamestate == GS_CREDITS)
{ {
if (HU_Responder(ev)) if (HU_Responder(ev))
{ {
@ -1813,7 +1810,17 @@ boolean G_Responder(event_t *ev)
if (K_CeremonyResponder(ev)) if (K_CeremonyResponder(ev))
{ {
nextmap = NEXTMAP_TITLE; if (grandprixinfo.gp == true
&& grandprixinfo.cup != NULL
&& grandprixinfo.cup->playcredits == true)
{
nextmap = NEXTMAP_CREDITS;
}
else
{
nextmap = NEXTMAP_TITLE;
}
G_EndGame(); G_EndGame();
return true; return true;
} }
@ -1822,11 +1829,6 @@ boolean G_Responder(event_t *ev)
{ {
return true; return true;
} }
// Demo End
else if (gamestate == GS_GAMEEND)
{
return true;
}
else if (gamestate == GS_INTERMISSION || gamestate == GS_VOTING || gamestate == GS_EVALUATION) else if (gamestate == GS_INTERMISSION || gamestate == GS_VOTING || gamestate == GS_EVALUATION)
{ {
if (HU_Responder(ev)) if (HU_Responder(ev))
@ -2322,23 +2324,12 @@ void G_Ticker(boolean run)
F_IntroTicker(); F_IntroTicker();
break; break;
case GS_ENDING:
if (run)
F_EndingTicker();
HU_Ticker();
break;
case GS_CUTSCENE: case GS_CUTSCENE:
if (run) if (run)
F_CutsceneTicker(); F_CutsceneTicker();
HU_Ticker(); HU_Ticker();
break; break;
case GS_GAMEEND:
if (run)
F_GameEndTicker();
break;
case GS_EVALUATION: case GS_EVALUATION:
if (run) if (run)
F_GameEvaluationTicker(); F_GameEvaluationTicker();
@ -3387,10 +3378,6 @@ void G_ExitLevel(void)
// Don't save demos immediately here! Let standings write first // Don't save demos immediately here! Let standings write first
} }
else if (gamestate == GS_ENDING)
{
F_StartCredits();
}
else if (gamestate == GS_CREDITS) else if (gamestate == GS_CREDITS)
{ {
F_StartGameEvaluation(); F_StartGameEvaluation();
@ -4664,6 +4651,8 @@ void G_EndGame(void)
// Only do evaluation and credits in singleplayer contexts // Only do evaluation and credits in singleplayer contexts
if (!netgame && grandprixinfo.gp == true) if (!netgame && grandprixinfo.gp == true)
{ {
G_HandleSaveLevel(true);
if (nextmap == NEXTMAP_CEREMONY) // end game with ceremony if (nextmap == NEXTMAP_CEREMONY) // end game with ceremony
{ {
if (K_StartCeremony() == true) if (K_StartCeremony() == true)
@ -4736,7 +4725,15 @@ void G_LoadGameSettings(void)
} }
#define GD_VERSIONCHECK 0xBA5ED123 // Change every major version, as usual #define GD_VERSIONCHECK 0xBA5ED123 // Change every major version, as usual
#define GD_VERSIONMINOR 3 // Change every format update #define GD_VERSIONMINOR 4 // Change every format update
typedef enum
{
GDEVER_ADDON = 1,
GDEVER_CREDITS = 1<<1,
GDEVER_REPLAY = 1<<2,
GDEVER_SPECIAL = 1<<3,
} gdeverdone_t;
static const char *G_GameDataFolder(void) static const char *G_GameDataFolder(void)
{ {
@ -4860,9 +4857,21 @@ void G_LoadGameData(void)
gamedata->chaokeys = READUINT16(save.p); gamedata->chaokeys = READUINT16(save.p);
gamedata->everloadedaddon = (boolean)READUINT8(save.p); if (versionMinor >= 4)
gamedata->eversavedreplay = (boolean)READUINT8(save.p); {
gamedata->everseenspecial = (boolean)READUINT8(save.p); UINT32 everflags = READUINT32(save.p);
gamedata->everloadedaddon = !!(everflags & GDEVER_ADDON);
gamedata->everfinishedcredits = !!(everflags & GDEVER_CREDITS);
gamedata->eversavedreplay = !!(everflags & GDEVER_REPLAY);
gamedata->everseenspecial = !!(everflags & GDEVER_SPECIAL);
}
else
{
gamedata->everloadedaddon = (boolean)READUINT8(save.p);
gamedata->eversavedreplay = (boolean)READUINT8(save.p);
gamedata->everseenspecial = (boolean)READUINT8(save.p);
}
} }
else else
{ {
@ -5289,7 +5298,7 @@ void G_SaveGameData(void)
4+4+ 4+4+
(4*GDGT_MAX)+ (4*GDGT_MAX)+
4+1+2+2+ 4+1+2+2+
1+1+1+ 4+
4+ 4+
(MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+ (MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+
4+2); 4+2);
@ -5426,11 +5435,22 @@ void G_SaveGameData(void)
WRITEUINT16(save.p, gamedata->keyspending); // 2 WRITEUINT16(save.p, gamedata->keyspending); // 2
WRITEUINT16(save.p, gamedata->chaokeys); // 2 WRITEUINT16(save.p, gamedata->chaokeys); // 2
WRITEUINT8(save.p, gamedata->everloadedaddon); // 1 {
WRITEUINT8(save.p, gamedata->eversavedreplay); // 1 UINT32 everflags = 0;
WRITEUINT8(save.p, gamedata->everseenspecial); // 1
WRITEUINT32(save.p, quickncasehash(timeattackfolder, 64)); if (gamedata->everloadedaddon)
everflags |= GDEVER_ADDON;
if (gamedata->everfinishedcredits)
everflags |= GDEVER_CREDITS;
if (gamedata->eversavedreplay)
everflags |= GDEVER_REPLAY;
if (gamedata->everseenspecial)
everflags |= GDEVER_SPECIAL;
WRITEUINT32(save.p, everflags); // 4
}
WRITEUINT32(save.p, quickncasehash(timeattackfolder, 64)); // 4
// To save space, use one bit per collected/achieved/unlocked flag // To save space, use one bit per collected/achieved/unlocked flag
for (i = 0; i < MAXEMBLEMS;) // MAXEMBLEMS * 1; for (i = 0; i < MAXEMBLEMS;) // MAXEMBLEMS * 1;

View file

@ -35,12 +35,10 @@ typedef enum
GS_CREDITS, // credit sequence GS_CREDITS, // credit sequence
GS_EVALUATION, // Evaluation at the end of a game. GS_EVALUATION, // Evaluation at the end of a game.
GS_GAMEEND, // game end sequence - "did you get all those chaos emeralds?"
GS_CEREMONY, // RR: Podium sequence GS_CEREMONY, // RR: Podium sequence
// Hardcoded fades or other fading methods // Hardcoded fades or other fading methods
GS_INTRO, // introduction GS_INTRO, // introduction
GS_ENDING, // currently shared between bad and good endings
GS_CUTSCENE, // custom cutscene GS_CUTSCENE, // custom cutscene
// Not fadable // Not fadable

View file

@ -531,6 +531,7 @@ char sprnames[NUMSPRITES + 1][5] =
//SRB2kart Sprites (sort later) //SRB2kart Sprites (sort later)
"RNDM", // Random Item Box "RNDM", // Random Item Box
"SBOX", // Sphere Box (for Battle) "SBOX", // Sphere Box (for Battle)
"RBOX", // Ring Box
"RPOP", // Random Item Box Pop "RPOP", // Random Item Box Pop
"ITRI", // Item Box Debris "ITRI", // Item Box Debris
"ITPA", // Paper item backdrop "ITPA", // Paper item backdrop
@ -648,6 +649,7 @@ char sprnames[NUMSPRITES + 1][5] =
"ITMO", "ITMO",
"ITMI", "ITMI",
"ITMN", "ITMN",
"PWRB",
"WANT", "WANT",
"PBOM", // player bomb "PBOM", // player bomb
@ -815,6 +817,8 @@ char sprnames[NUMSPRITES + 1][5] =
"GBOM", "GBOM",
"GCHX", "GCHX",
"3DFR",
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
"VIEW", "VIEW",
}; };
@ -3895,7 +3899,19 @@ state_t states[NUMSTATES] =
{SPR_RNDM, 18|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM11}, // S_RANDOMITEM10 {SPR_RNDM, 18|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM11}, // S_RANDOMITEM10
{SPR_RNDM, 20|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM12}, // S_RANDOMITEM11 {SPR_RNDM, 20|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM12}, // S_RANDOMITEM11
{SPR_RNDM, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM1}, // S_RANDOMITEM12 {SPR_RNDM, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM1}, // S_RANDOMITEM12
{SPR_NULL, 0, 0, {A_ItemPop}, 0, 0, S_RANDOMITEM1}, // S_DEADRANDOMITEM
{SPR_RBOX, FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX2}, // S_RINGBOX1
{SPR_RBOX, 2|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX3}, // S_RINGBOX2
{SPR_RBOX, 4|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX4}, // S_RINGBOX3
{SPR_RBOX, 6|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX5}, // S_RINGBOX4
{SPR_RBOX, 8|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX6}, // S_RINGBOX5
{SPR_RBOX, 10|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX7}, // S_RINGBOX6
{SPR_RBOX, 12|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX8}, // S_RINGBOX7
{SPR_RBOX, 14|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX9}, // S_RINGBOX8
{SPR_RBOX, 16|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX10}, // S_RINGBOX9
{SPR_RBOX, 18|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX11}, // S_RINGBOX10
{SPR_RBOX, 20|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX12}, // S_RINGBOX11
{SPR_RBOX, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX1}, // S_RINGBOX12
{SPR_SBOX, FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX2}, // S_SPHEREBOX1 {SPR_SBOX, FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX2}, // S_SPHEREBOX1
{SPR_SBOX, 2|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX3}, // S_SPHEREBOX2 {SPR_SBOX, 2|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX3}, // S_SPHEREBOX2
@ -3909,7 +3925,6 @@ state_t states[NUMSTATES] =
{SPR_SBOX, 18|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX11}, // S_SPHEREBOX10 {SPR_SBOX, 18|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX11}, // S_SPHEREBOX10
{SPR_SBOX, 20|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX12}, // S_SPHEREBOX11 {SPR_SBOX, 20|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX12}, // S_SPHEREBOX11
{SPR_SBOX, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX1}, // S_SPHEREBOX12 {SPR_SBOX, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX1}, // S_SPHEREBOX12
{SPR_NULL, 0, 0, {A_ItemPop}, 1, 0, S_NULL}, // S_DEADSPHEREBOX
{SPR_RPOP, FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_RANDOMITEMPOP2}, // S_RANDOMITEMPOP1 {SPR_RPOP, FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_RANDOMITEMPOP2}, // S_RANDOMITEMPOP1
{SPR_RPOP, FF_FULLBRIGHT|1, 5, {NULL}, 0, 0, S_RANDOMITEMPOP3}, // S_RANDOMITEMPOP2 {SPR_RPOP, FF_FULLBRIGHT|1, 5, {NULL}, 0, 0, S_RANDOMITEMPOP3}, // S_RANDOMITEMPOP2
@ -5235,6 +5250,8 @@ state_t states[NUMSTATES] =
{SPR_GCHX, 6|FF_PAPERSPRITE|FF_ANIMATE|FF_REVERSEANIM, 14, {NULL}, 6, 2, S_GACHABOM_WAITING}, // S_GACHABOM_EXPLOSION_4 {SPR_GCHX, 6|FF_PAPERSPRITE|FF_ANIMATE|FF_REVERSEANIM, 14, {NULL}, 6, 2, S_GACHABOM_WAITING}, // S_GACHABOM_EXPLOSION_4
{SPR_GBOM, FF_INVERT, 8, {A_SetScale}, FRACUNIT, 0, S_GACHABOM_RETURNING}, // S_GACHABOM_WAITING {SPR_GBOM, FF_INVERT, 8, {A_SetScale}, FRACUNIT, 0, S_GACHABOM_RETURNING}, // S_GACHABOM_WAITING
{SPR_GBOM, FF_INVERT, -1, {A_SetScale}, FRACUNIT/2, 1, S_NULL}, // S_GACHABOM_RETURNING {SPR_GBOM, FF_INVERT, -1, {A_SetScale}, FRACUNIT/2, 1, S_NULL}, // S_GACHABOM_RETURNING
{SPR_3DFR, 1|FF_ANIMATE, -1, {NULL}, 2, 5, S_NULL}, // S_SUPER_FLICKY
}; };
mobjinfo_t mobjinfo[NUMMOBJTYPES] = mobjinfo_t mobjinfo[NUMMOBJTYPES] =
@ -8117,8 +8134,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL, // xdeathstate S_NULL, // xdeathstate
sfx_s3k9c, // deathsound sfx_s3k9c, // deathsound
0, // speed 0, // speed
72*FRACUNIT, // radius 80*FRACUNIT, // radius
72*FRACUNIT, // height 80*FRACUNIT, // height
0, // display offset 0, // display offset
16, // mass 16, // mass
0, // damage 0, // damage
@ -22394,7 +22411,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
// SRB2kart MT's // SRB2kart MT's
{ // MT_RANDOMITEM { // MT_RANDOMITEM
2000, // doomednum 2000, // doomednum
S_RANDOMITEM1, // spawnstate S_RINGBOX1, // spawnstate
1000, // spawnhealth 1000, // spawnhealth
S_NULL, // seestate S_NULL, // seestate
sfx_None, // seesound sfx_None, // seesound
@ -22405,7 +22422,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound sfx_None, // painsound
S_NULL, // meleestate S_NULL, // meleestate
S_NULL, // missilestate S_NULL, // missilestate
S_DEADRANDOMITEM, // deathstate S_NULL, // deathstate
S_NULL, // xdeathstate S_NULL, // xdeathstate
sfx_kc2e, // deathsound sfx_kc2e, // deathsound
60*FRACUNIT, // speed 60*FRACUNIT, // speed
@ -22432,7 +22449,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound sfx_None, // painsound
S_NULL, // meleestate S_NULL, // meleestate
S_NULL, // missilestate S_NULL, // missilestate
S_DEADSPHEREBOX, // deathstate S_NULL, // deathstate
S_NULL, // xdeathstate S_NULL, // xdeathstate
sfx_kc2e, // deathsound sfx_kc2e, // deathsound
60*FRACUNIT, // speed 60*FRACUNIT, // speed
@ -29794,6 +29811,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
{ // MT_SUPER_FLICKY
-1, // doomednum
S_SUPER_FLICKY, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
16*FRACUNIT, // radius
32*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
{ // MT_SUPER_FLICKY_CONTROLLER
-1, // doomednum
S_INVISIBLE, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
16*FRACUNIT, // radius
32*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY, // flags
S_NULL // raisestate
},
}; };
skincolor_t skincolors[MAXSKINCOLORS] = { skincolor_t skincolors[MAXSKINCOLORS] = {

View file

@ -277,7 +277,6 @@ enum actionnum
A_DRAGONWING, A_DRAGONWING,
A_DRAGONSEGMENT, A_DRAGONSEGMENT,
A_CHANGEHEIGHT, A_CHANGEHEIGHT,
A_ITEMPOP,
A_JAWZEXPLODE, A_JAWZEXPLODE,
A_SSMINESEARCH, A_SSMINESEARCH,
A_SSMINEEXPLODE, A_SSMINEEXPLODE,
@ -550,7 +549,6 @@ void A_ChangeHeight();
// //
// SRB2Kart // SRB2Kart
// //
void A_ItemPop();
void A_JawzExplode(); void A_JawzExplode();
void A_SSMineSearch(); void A_SSMineSearch();
void A_SSMineExplode(); void A_SSMineExplode();
@ -1084,6 +1082,7 @@ typedef enum sprite
// SRB2Kart // SRB2Kart
SPR_RNDM, // Random Item Box SPR_RNDM, // Random Item Box
SPR_SBOX, // Sphere Box (for Battle) SPR_SBOX, // Sphere Box (for Battle)
SPR_RBOX, // Ring Box
SPR_RPOP, // Random Item Box Pop SPR_RPOP, // Random Item Box Pop
SPR_ITRI, // Item Box Debris SPR_ITRI, // Item Box Debris
SPR_ITPA, // Paper item backdrop SPR_ITPA, // Paper item backdrop
@ -1201,6 +1200,7 @@ typedef enum sprite
SPR_ITMO, SPR_ITMO,
SPR_ITMI, SPR_ITMI,
SPR_ITMN, SPR_ITMN,
SPR_PWRB,
SPR_WANT, SPR_WANT,
SPR_PBOM, // player bomb SPR_PBOM, // player bomb
@ -1368,6 +1368,8 @@ typedef enum sprite
SPR_GBOM, SPR_GBOM,
SPR_GCHX, SPR_GCHX,
SPR_3DFR,
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
SPR_VIEW, SPR_VIEW,
@ -4302,7 +4304,20 @@ typedef enum state
S_RANDOMITEM10, S_RANDOMITEM10,
S_RANDOMITEM11, S_RANDOMITEM11,
S_RANDOMITEM12, S_RANDOMITEM12,
S_DEADRANDOMITEM,
// Ring Box
S_RINGBOX1,
S_RINGBOX2,
S_RINGBOX3,
S_RINGBOX4,
S_RINGBOX5,
S_RINGBOX6,
S_RINGBOX7,
S_RINGBOX8,
S_RINGBOX9,
S_RINGBOX10,
S_RINGBOX11,
S_RINGBOX12,
// Sphere Box (for Battle) // Sphere Box (for Battle)
S_SPHEREBOX1, S_SPHEREBOX1,
@ -4317,7 +4332,6 @@ typedef enum state
S_SPHEREBOX10, S_SPHEREBOX10,
S_SPHEREBOX11, S_SPHEREBOX11,
S_SPHEREBOX12, S_SPHEREBOX12,
S_DEADSPHEREBOX,
// Random Item Pop // Random Item Pop
S_RANDOMITEMPOP1, S_RANDOMITEMPOP1,
@ -5667,6 +5681,8 @@ typedef enum state
S_GACHABOM_WAITING, S_GACHABOM_WAITING,
S_GACHABOM_RETURNING, S_GACHABOM_RETURNING,
S_SUPER_FLICKY,
S_FIRSTFREESLOT, S_FIRSTFREESLOT,
S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1, S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1,
NUMSTATES NUMSTATES
@ -6812,6 +6828,9 @@ typedef enum mobj_type
MT_LOOPENDPOINT, MT_LOOPENDPOINT,
MT_LOOPCENTERPOINT, MT_LOOPCENTERPOINT,
MT_SUPER_FLICKY,
MT_SUPER_FLICKY_CONTROLLER,
MT_FIRSTFREESLOT, MT_FIRSTFREESLOT,
MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
NUMMOBJTYPES NUMMOBJTYPES

View file

@ -10,6 +10,7 @@ extern "C" {
#define BATTLE_SPAWN_INTERVAL (4*TICRATE) #define BATTLE_SPAWN_INTERVAL (4*TICRATE)
#define BATTLE_DESPAWN_TIME (15*TICRATE) #define BATTLE_DESPAWN_TIME (15*TICRATE)
#define BATTLE_POWERUP_TIME (20*TICRATE)
extern struct battleovertime extern struct battleovertime
{ {

View file

@ -16,6 +16,7 @@
#include "k_objects.h" #include "k_objects.h"
#include "k_roulette.h" #include "k_roulette.h"
#include "k_podium.h" #include "k_podium.h"
#include "k_powerup.h"
angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2) angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2)
{ {
@ -888,6 +889,8 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim)
P_PlayerRingBurst(victimPlayer, 5); P_PlayerRingBurst(victimPlayer, 5);
P_DamageMobj(victim, shield, attacker, 1, DMG_STUMBLE); // There's a special exception in P_DamageMobj for type==MT_INSTAWHIP P_DamageMobj(victim, shield, attacker, 1, DMG_STUMBLE); // There's a special exception in P_DamageMobj for type==MT_INSTAWHIP
K_DropPowerUps(victimPlayer);
angle_t thrangle = ANGLE_180 + R_PointToAngle2(victim->x, victim->y, shield->x, shield->y); angle_t thrangle = ANGLE_180 + R_PointToAngle2(victim->x, victim->y, shield->x, shield->y);
P_Thrust(victim, thrangle, FRACUNIT*10); P_Thrust(victim, thrangle, FRACUNIT*10);
@ -898,6 +901,19 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim)
} }
return false; return false;
} }
else if (victim->type == MT_SUPER_FLICKY)
{
if (Obj_IsSuperFlickyWhippable(victim))
{
K_AddHitLag(victim, victimHitlag, true);
K_AddHitLag(attacker, attackerHitlag, false);
shield->hitlag = attacker->hitlag;
Obj_WhipSuperFlicky(victim);
return true;
}
return false;
}
else else
{ {
if (victim->type == MT_ORBINAUT || victim->type == MT_JAWZ || victim->type == MT_GACHABOM if (victim->type == MT_ORBINAUT || victim->type == MT_JAWZ || victim->type == MT_GACHABOM

View file

@ -114,6 +114,7 @@ static patch_t *kp_wantedsplit;
static patch_t *kp_wantedreticle; static patch_t *kp_wantedreticle;
static patch_t *kp_itembg[4]; static patch_t *kp_itembg[4];
static patch_t *kp_ringbg[4];
static patch_t *kp_itemtimer[2]; static patch_t *kp_itemtimer[2];
static patch_t *kp_itemmulsticker[2]; static patch_t *kp_itemmulsticker[2];
static patch_t *kp_itemx; static patch_t *kp_itemx;
@ -142,6 +143,12 @@ static patch_t *kp_kitchensink[2];
static patch_t *kp_droptarget[2]; static patch_t *kp_droptarget[2];
static patch_t *kp_gardentop[2]; static patch_t *kp_gardentop[2];
static patch_t *kp_gachabom[2]; static patch_t *kp_gachabom[2];
static patch_t *kp_bar[2];
static patch_t *kp_doublebar[2];
static patch_t *kp_triplebar[2];
static patch_t *kp_slotring[2];
static patch_t *kp_seven[2];
static patch_t *kp_jackpot[2];
static patch_t *kp_check[6]; static patch_t *kp_check[6];
@ -443,6 +450,9 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_itemmulsticker[0], "K_ITMUL"); HU_UpdatePatch(&kp_itemmulsticker[0], "K_ITMUL");
HU_UpdatePatch(&kp_itemx, "K_ITX"); HU_UpdatePatch(&kp_itemx, "K_ITX");
HU_UpdatePatch(&kp_ringbg[0], "K_RBBG");
HU_UpdatePatch(&kp_ringbg[1], "K_SBBG");
HU_UpdatePatch(&kp_sadface[0], "K_ITSAD"); HU_UpdatePatch(&kp_sadface[0], "K_ITSAD");
HU_UpdatePatch(&kp_sneaker[0], "K_ITSHOE"); HU_UpdatePatch(&kp_sneaker[0], "K_ITSHOE");
HU_UpdatePatch(&kp_rocketsneaker[0], "K_ITRSHE"); HU_UpdatePatch(&kp_rocketsneaker[0], "K_ITRSHE");
@ -478,6 +488,12 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_droptarget[0], "K_ITDTRG"); HU_UpdatePatch(&kp_droptarget[0], "K_ITDTRG");
HU_UpdatePatch(&kp_gardentop[0], "K_ITGTOP"); HU_UpdatePatch(&kp_gardentop[0], "K_ITGTOP");
HU_UpdatePatch(&kp_gachabom[0], "K_ITGBOM"); HU_UpdatePatch(&kp_gachabom[0], "K_ITGBOM");
HU_UpdatePatch(&kp_bar[0], "K_RBBAR");
HU_UpdatePatch(&kp_doublebar[0], "K_RBBAR2");
HU_UpdatePatch(&kp_triplebar[0], "K_RBBAR3");
HU_UpdatePatch(&kp_slotring[0], "K_RBRING");
HU_UpdatePatch(&kp_seven[0], "K_RBSEV");
HU_UpdatePatch(&kp_jackpot[0], "K_RBJACK");
sprintf(buffer, "FSMFGxxx"); sprintf(buffer, "FSMFGxxx");
for (i = 0; i < 104; i++) for (i = 0; i < 104; i++)
@ -531,6 +547,12 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_droptarget[1], "K_ISDTRG"); HU_UpdatePatch(&kp_droptarget[1], "K_ISDTRG");
HU_UpdatePatch(&kp_gardentop[1], "K_ISGTOP"); HU_UpdatePatch(&kp_gardentop[1], "K_ISGTOP");
HU_UpdatePatch(&kp_gachabom[1], "K_ISGBOM"); HU_UpdatePatch(&kp_gachabom[1], "K_ISGBOM");
HU_UpdatePatch(&kp_bar[1], "K_SBBAR");
HU_UpdatePatch(&kp_doublebar[1], "K_SBBAR2");
HU_UpdatePatch(&kp_triplebar[1], "K_SBBAR3");
HU_UpdatePatch(&kp_slotring[1], "K_SBRING");
HU_UpdatePatch(&kp_seven[1], "K_SBSEV");
HU_UpdatePatch(&kp_jackpot[1], "K_SBJACK");
sprintf(buffer, "FSMFSxxx"); sprintf(buffer, "FSMFSxxx");
for (i = 0; i < 104; i++) for (i = 0; i < 104; i++)
@ -875,6 +897,23 @@ static patch_t *K_GetSmallStaticCachedItemPatch(kartitems_t item)
return K_GetCachedItemPatch(item, offset); return K_GetCachedItemPatch(item, offset);
} }
static patch_t *K_GetCachedSlotMachinePatch(INT32 item, UINT8 offset)
{
patch_t **kp[KSM__MAX] = {
kp_bar,
kp_doublebar,
kp_triplebar,
kp_slotring,
kp_seven,
kp_jackpot,
};
if (item >= 0 && item < KSM__MAX)
return kp[item][offset];
else
return NULL;
}
//} //}
INT32 ITEM_X, ITEM_Y; // Item Window INT32 ITEM_X, ITEM_Y; // Item Window
@ -1217,7 +1256,7 @@ static void K_drawKartItem(void)
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
{ {
const SINT8 indexOfs = i-1; const SINT8 indexOfs = i-1;
const size_t index = (stplyr->itemRoulette.index + indexOfs) % stplyr->itemRoulette.itemListLen; const size_t index = (stplyr->itemRoulette.itemListLen + (stplyr->itemRoulette.index + indexOfs)) % stplyr->itemRoulette.itemListLen;
const SINT8 result = stplyr->itemRoulette.itemList[index]; const SINT8 result = stplyr->itemRoulette.itemList[index];
const SINT8 item = K_ItemResultToType(result); const SINT8 item = K_ItemResultToType(result);
@ -1533,6 +1572,118 @@ static void K_drawKartItem(void)
} }
} }
static void K_drawKartSlotMachine(void)
{
// ITEM_X = BASEVIDWIDTH-50; // 270
// ITEM_Y = 24; // 24
// Why write V_DrawScaledPatch calls over and over when they're all the same?
// Set to 'no item' just in case.
const UINT8 offset = ((r_splitscreen > 1) ? 1 : 0);
patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw };
patch_t *localbg = offset ? kp_ringbg[1] : kp_ringbg[0];
// == SHITGARBAGE UNLIMITED 2: RISE OF MY ASS ==
// FIVE LAYERS OF BULLSHIT PER-PIXEL SHOVING BECAUSE THE PATCHES HAVE DIFFERENT OFFSETS
// IF YOU ARE HERE TO ADJUST THE RINGBOX HUD TURN OFF YOUR COMPUTER AND GO TO YOUR LOCAL PARK
INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags...
INT32 boxoffx = 0;
INT32 boxoffy = -6;
INT32 vstretch = 0;
INT32 hstretch = 3;
INT32 splitbsx, splitbsy = 0;
UINT16 localcolor[3] = { stplyr->skincolor };
SINT8 colormode[3] = { TC_RAINBOW };
fixed_t rouletteOffset = 0;
fixed_t rouletteSpace = SLOT_SPACING;
vector2_t rouletteCrop = {10, 10};
INT32 i;
if (stplyr->itemRoulette.itemListLen > 0)
{
// Init with item roulette stuff.
for (i = 0; i < 3; i++)
{
const SINT8 indexOfs = i-1;
const size_t index = (stplyr->itemRoulette.itemListLen + (stplyr->itemRoulette.index + indexOfs)) % stplyr->itemRoulette.itemListLen;
const SINT8 result = stplyr->itemRoulette.itemList[index];
localpatch[i] = K_GetCachedSlotMachinePatch(result, offset);
}
}
if (stplyr->itemRoulette.active == true)
{
rouletteOffset = K_GetSlotOffset(&stplyr->itemRoulette, rendertimefrac);
}
else
{
if (!stplyr->ringboxdelay)
return;
}
// pain and suffering defined below
if (offset)
{
boxoffx -= 4;
if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3...
{
fx = ITEM_X + 10;
fy = ITEM_Y + 10;
fflags = V_SNAPTOLEFT|V_SNAPTOTOP|V_SPLITSCREEN;
}
else // else, that means we're P2 or P4.
{
fx = ITEM2_X + 7;
fy = ITEM2_Y + 10;
fflags = V_SNAPTORIGHT|V_SNAPTOTOP|V_SPLITSCREEN;
}
rouletteSpace = SLOT_SPACING_SPLITSCREEN;
rouletteOffset = FixedMul(rouletteOffset, FixedDiv(SLOT_SPACING_SPLITSCREEN, SLOT_SPACING));
rouletteCrop.x = 16;
rouletteCrop.y = 13;
splitbsx = -6;
splitbsy = -6;
boxoffy += 2;
hstretch = 0;
}
else
{
fx = ITEM_X;
fy = ITEM_Y;
fflags = V_SNAPTOTOP|V_SNAPTOLEFT|V_SPLITSCREEN;
}
V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|fflags, localbg);
V_SetClipRect(
((fx + rouletteCrop.x + boxoffx + splitbsx) << FRACBITS), ((fy + rouletteCrop.y + boxoffy - vstretch + splitbsy) << FRACBITS),
rouletteSpace + (hstretch<<FRACBITS), rouletteSpace + (vstretch<<FRACBITS),
V_SLIDEIN|fflags
);
// item box has special layering, transparency, different sized patches, other fucked up shit
// ring box is evenly spaced and easy
rouletteOffset += rouletteSpace;
for (i = 0; i < 3; i++)
{
V_DrawFixedPatch(
((fx)<<FRACBITS), ((fy)<<FRACBITS) + rouletteOffset,
FRACUNIT, V_HUDTRANS|V_SLIDEIN|fflags,
localpatch[i], (localcolor[i] ? R_GetTranslationColormap(colormode[i], localcolor[i], GTC_CACHE) : NULL)
);
rouletteOffset -= rouletteSpace;
}
V_ClearClipRect();
}
void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT32 splitflags, UINT8 mode) void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT32 splitflags, UINT8 mode)
{ {
// TIME_X = BASEVIDWIDTH-124; // 196 // TIME_X = BASEVIDWIDTH-124; // 196
@ -4943,7 +5094,7 @@ static void K_drawDistributionDebugger(void)
return; return;
} }
K_FillItemRouletteData(stplyr, &rouletteData); K_FillItemRouletteData(stplyr, &rouletteData, false);
for (i = 0; i < rouletteData.itemListLen; i++) for (i = 0; i < rouletteData.itemListLen; i++)
{ {
@ -5200,7 +5351,16 @@ void K_drawKartHUD(void)
// Draw the item window // Draw the item window
if (LUA_HudEnabled(hud_item) && !freecam) if (LUA_HudEnabled(hud_item) && !freecam)
K_drawKartItem(); {
if (stplyr->itemRoulette.ringbox && stplyr->itemamount == 0 && stplyr->itemtype == 0)
{
K_drawKartSlotMachine();
}
else
{
K_drawKartItem();
}
}
if (demo.title) if (demo.title)
; ;
@ -5280,7 +5440,7 @@ void K_drawKartHUD(void)
{ {
if (gametyperules & GTR_CIRCUIT) if (gametyperules & GTR_CIRCUIT)
{ {
if (numlaps > 1) if (numlaps != 1)
{ {
K_drawKartLaps(); K_drawKartLaps();
gametypeinfoshown = true; gametypeinfoshown = true;

View file

@ -361,6 +361,7 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_kartvoices); CV_RegisterVar(&cv_kartvoices);
CV_RegisterVar(&cv_kartbot); CV_RegisterVar(&cv_kartbot);
CV_RegisterVar(&cv_karteliminatelast); CV_RegisterVar(&cv_karteliminatelast);
CV_RegisterVar(&cv_thunderdome);
CV_RegisterVar(&cv_kartusepwrlv); CV_RegisterVar(&cv_kartusepwrlv);
CV_RegisterVar(&cv_votetime); CV_RegisterVar(&cv_votetime);
CV_RegisterVar(&cv_botscanvote); CV_RegisterVar(&cv_botscanvote);
@ -6532,13 +6533,26 @@ SINT8 K_GetTotallyRandomResult(UINT8 useodds)
return i; return i;
} }
mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount) mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT16 amount)
{ {
mobj_t *drop = P_SpawnMobj(x, y, z, MT_FLOATINGITEM); mobj_t *drop = P_SpawnMobj(x, y, z, MT_FLOATINGITEM);
mobj_t *backdrop = P_SpawnMobjFromMobj(drop, 0, 0, 0, MT_OVERLAY);
P_SetTarget(&backdrop->target, drop); // FIXME: due to linkdraw sucking major ass, I was unable
P_SetMobjState(backdrop, S_ITEMBACKDROP); // to make a backdrop render behind dropped power-ups
// (which use a smaller sprite than normal items). So
// dropped power-ups have the backdrop baked into the
// sprite for now.
if (type < FIRSTPOWERUP)
{
mobj_t *backdrop = P_SpawnMobjFromMobj(drop, 0, 0, 0, MT_OVERLAY);
P_SetTarget(&backdrop->target, drop);
P_SetMobjState(backdrop, S_ITEMBACKDROP);
backdrop->dispoffset = 1;
P_SetTarget(&backdrop->tracer, drop);
backdrop->flags2 |= MF2_LINKDRAW;
}
P_SetScale(drop, drop->scale>>4); P_SetScale(drop, drop->scale>>4);
drop->destscale = (3*drop->destscale)/2; drop->destscale = (3*drop->destscale)/2;
@ -6587,9 +6601,6 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8
} }
drop->flags |= MF_NOCLIPTHING; drop->flags |= MF_NOCLIPTHING;
backdrop->dispoffset = 1;
P_SetTarget(&backdrop->tracer, drop);
backdrop->flags2 |= MF2_LINKDRAW;
if (gametyperules & GTR_CLOSERPLAYERS) if (gametyperules & GTR_CLOSERPLAYERS)
{ {
@ -6599,20 +6610,30 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8
return drop; return drop;
} }
void K_DropPaperItem(player_t *player, UINT8 itemtype, UINT16 itemamount)
{
if (!player->mo || P_MobjWasRemoved(player->mo))
{
return;
}
mobj_t *drop = K_CreatePaperItem(
player->mo->x, player->mo->y, player->mo->z + player->mo->height/2,
player->mo->angle + ANGLE_90, P_MobjFlip(player->mo),
itemtype, itemamount
);
K_FlipFromObject(drop, player->mo);
}
// For getting EXTRA hit! // For getting EXTRA hit!
void K_DropItems(player_t *player) void K_DropItems(player_t *player)
{ {
K_DropHnextList(player); K_DropHnextList(player);
if (player->mo && !P_MobjWasRemoved(player->mo) && player->itemamount > 0) if (player->itemamount > 0)
{ {
mobj_t *drop = K_CreatePaperItem( K_DropPaperItem(player, player->itemtype, player->itemamount);
player->mo->x, player->mo->y, player->mo->z + player->mo->height/2,
player->mo->angle + ANGLE_90, P_MobjFlip(player->mo),
player->itemtype, player->itemamount
);
K_FlipFromObject(drop, player->mo);
} }
K_StripItems(player); K_StripItems(player);
@ -8287,6 +8308,12 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->hand && P_MobjWasRemoved(player->hand)) if (player->hand && P_MobjWasRemoved(player->hand))
P_SetTarget(&player->hand, NULL); P_SetTarget(&player->hand, NULL);
if (player->flickyAttacker && P_MobjWasRemoved(player->flickyAttacker))
P_SetTarget(&player->flickyAttacker, NULL);
if (player->powerup.flickyController && P_MobjWasRemoved(player->powerup.flickyController))
P_SetTarget(&player->powerup.flickyController, NULL);
if (player->spectator == false) if (player->spectator == false)
{ {
K_KartEbrakeVisuals(player); K_KartEbrakeVisuals(player);
@ -10753,6 +10780,26 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
player->pflags &= ~PF_USERINGS; player->pflags &= ~PF_USERINGS;
} }
if (player->ringboxdelay)
{
player->ringboxdelay--;
if (player->ringboxdelay == 0)
{
// TODO
UINT32 behind = K_GetItemRouletteDistance(player, player->itemRoulette.playing);
UINT32 behindMulti = behind / 1000;
behindMulti = min(behindMulti, 20);
UINT32 award = 5*player->ringboxaward + 10;
if (player->ringboxaward > 2) // not a BAR
award = 3 * award / 2;
award = award * (behindMulti + 10) / 10;
K_AwardPlayerRings(player, award, true);
player->ringboxaward = 0;
}
}
if (player && player->mo && player->mo->health > 0 && !player->spectator && !P_PlayerInPain(player) && !mapreset && leveltime > introtime) if (player && player->mo && player->mo->health > 0 && !player->spectator && !P_PlayerInPain(player) && !mapreset && leveltime > introtime)
{ {
// First, the really specific, finicky items that function without the item being directly in your item slot. // First, the really specific, finicky items that function without the item being directly in your item slot.
@ -11988,8 +12035,17 @@ void K_UpdateMobjItemOverlay(mobj_t *part, SINT8 itemType, UINT8 itemCount)
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE; part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE;
break; break;
default: default:
part->sprite = SPR_ITEM; if (itemType >= FIRSTPOWERUP)
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|(itemType); {
part->sprite = SPR_PWRB;
// Not a papersprite. See K_CreatePaperItem for why.
part->frame = FF_FULLBRIGHT|(itemType - FIRSTPOWERUP);
}
else
{
part->sprite = SPR_ITEM;
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|(itemType);
}
break; break;
} }
} }

View file

@ -150,7 +150,8 @@ void K_SpawnDriftElectricSparks(player_t *player, int color, boolean shockwave);
void K_KartUpdatePosition(player_t *player); void K_KartUpdatePosition(player_t *player);
void K_UpdateAllPlayerPositions(void); void K_UpdateAllPlayerPositions(void);
SINT8 K_GetTotallyRandomResult(UINT8 useodds); SINT8 K_GetTotallyRandomResult(UINT8 useodds);
mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount); mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT16 amount);
void K_DropPaperItem(player_t *player, UINT8 itemtype, UINT16 itemamount);
void K_PopPlayerShield(player_t *player); void K_PopPlayerShield(player_t *player);
void K_DropItems(player_t *player); void K_DropItems(player_t *player);
void K_DropRocketSneaker(player_t *player); void K_DropRocketSneaker(player_t *player);

View file

@ -126,6 +126,9 @@ void M_PrevMenuGametype(UINT32 forbidden);
void M_HandleHostMenuGametype(INT32 choice); void M_HandleHostMenuGametype(INT32 choice);
void M_HandlePauseMenuGametype(INT32 choice); void M_HandlePauseMenuGametype(INT32 choice);
extern UINT32 menucallvote; // not midVoteType_e to prevent #include k_zvote
void M_HandlePauseMenuCallVote(INT32 choice);
// //
// MENU TYPEDEFS // MENU TYPEDEFS
// //
@ -415,6 +418,8 @@ extern menu_t EXTRAS_EggTVDef;
extern menuitem_t PAUSE_Main[]; extern menuitem_t PAUSE_Main[];
extern menu_t PAUSE_MainDef; extern menu_t PAUSE_MainDef;
extern menu_t PAUSE_KickHandlerDef;
// EXTRAS // EXTRAS
extern menuitem_t MISC_Manual[]; extern menuitem_t MISC_Manual[];
extern menu_t MISC_ManualDef; extern menu_t MISC_ManualDef;
@ -447,6 +452,8 @@ typedef enum
#ifdef HAVE_DISCORDRPC #ifdef HAVE_DISCORDRPC
mpause_discordrequests, mpause_discordrequests,
#endif #endif
mpause_admin,
mpause_callvote,
mpause_continue, mpause_continue,
mpause_spectate, mpause_spectate,
@ -1052,6 +1059,7 @@ typedef enum
extras_eggtv, extras_eggtv,
extras_stereo, extras_stereo,
extras_password, extras_password,
extras_credits,
} extras_e; } extras_e;
void M_InitExtras(INT32 choice); // init for the struct void M_InitExtras(INT32 choice); // init for the struct
@ -1082,6 +1090,15 @@ void M_QuitPauseMenu(INT32 choice);
boolean M_PauseInputs(INT32 ch); boolean M_PauseInputs(INT32 ch);
void M_PauseTick(void); void M_PauseTick(void);
extern struct playerkickmenu_s {
tic_t ticker;
UINT8 player;
UINT8 poke;
boolean adminpowered;
} playerkickmenu;
void M_KickHandler(INT32 choice);
extern consvar_t cv_dummymenuplayer; extern consvar_t cv_dummymenuplayer;
extern consvar_t cv_dummyspectator; extern consvar_t cv_dummyspectator;
@ -1154,6 +1171,7 @@ void M_DrawMPServerBrowser(void);
// Pause menu: // Pause menu:
void M_DrawPause(void); void M_DrawPause(void);
void M_DrawKickHandler(void);
// Replay Playback // Replay Playback
void M_DrawPlaybackMenu(void); void M_DrawPlaybackMenu(void);

View file

@ -52,6 +52,7 @@
#include "doomstat.h" // MAXSPLITSCREENPLAYERS #include "doomstat.h" // MAXSPLITSCREENPLAYERS
#include "k_grandprix.h" // K_CanChangeRules #include "k_grandprix.h" // K_CanChangeRules
#include "k_rank.h" // K_GetGradeColor #include "k_rank.h" // K_GetGradeColor
#include "k_zvote.h" // K_GetMidVoteLabel
#include "y_inter.h" // Y_RoundQueueDrawer #include "y_inter.h" // Y_RoundQueueDrawer
@ -3111,7 +3112,7 @@ void M_DrawMPHost(void)
} }
break; break;
} }
case IT_KEYHANDLER: case IT_ARROWS:
{ {
if (currentMenu->menuitems[i].itemaction.routine != M_HandleHostMenuGametype) if (currentMenu->menuitems[i].itemaction.routine != M_HandleHostMenuGametype)
break; break;
@ -4308,11 +4309,6 @@ void M_DrawPause(void)
INT16 arrxpos = 150 + 2*offset; // To draw the background arrow. INT16 arrxpos = 150 + 2*offset; // To draw the background arrow.
INT16 j = 0; INT16 j = 0;
char word1[MAXSTRINGLENGTH];
INT16 word1len = 0;
char word2[MAXSTRINGLENGTH];
INT16 word2len = 0;
boolean sok = false;
patch_t *vertbg = W_CachePatchName("M_STRIPV", PU_CACHE); patch_t *vertbg = W_CachePatchName("M_STRIPV", PU_CACHE);
patch_t *arrstart = W_CachePatchName("M_PTIP", PU_CACHE); patch_t *arrstart = W_CachePatchName("M_PTIP", PU_CACHE);
@ -4410,54 +4406,107 @@ void M_DrawPause(void)
} }
// Draw the string! // Draw the string!
// ...but first get what we need to get.
while (currentMenu->menuitems[itemOn].text[j] && j < MAXSTRINGLENGTH)
{
char c = currentMenu->menuitems[itemOn].text[j];
if (c == ' ' && !sok) const char *maintext = NULL;
{ const char *selectabletext = NULL;
sok = true; INT32 mainflags = V_YELLOWMAP, selectableflags = 0;
j++;
continue; // We don't care about this :moyai:
}
if (sok)
{
word2[word2len] = c;
word2len++;
}
else
{
word1[word1len] = c;
word1len++;
}
j++;
}
word1[word1len] = '\0';
word2[word2len] = '\0';
if (itemOn == mpause_changegametype) if (itemOn == mpause_changegametype)
{ {
INT32 w = V_LSTitleLowStringWidth(gametypes[menugametype]->name, 0)/2; selectabletext = gametypes[menugametype]->name;
}
else if (itemOn == mpause_callvote)
{
selectabletext = K_GetMidVoteLabel(menucallvote);
if (word1len) if (K_MinimalCheckNewMidVote(menucallvote) == false)
V_DrawCenteredLSTitleHighString(220 + offset*2, 75, 0, word1); {
if (g_midVote.active == true)
{
maintext = "ACTIVE...";
}
else if (g_midVote.delay > 0)
{
if (g_midVote.delay != 1)
maintext = va("%u", ((g_midVote.delay - 1) / TICRATE) + 1);
}
else if (K_PlayerIDAllowedInMidVote(consoleplayer) == false)
{
maintext = "SPECTATING";
}
else
{
maintext = "INVALID!?";
}
V_DrawLSTitleLowString(220-w + offset*2, 103, V_YELLOWMAP, gametypes[menugametype]->name); if (maintext != NULL)
V_DrawCharacter(220-w + offset*2 - 8 - (skullAnimCounter/5), 103+6, '\x1C' | V_YELLOWMAP, false); // left arrow selectableflags |= V_MODULATE;
V_DrawCharacter(220+w + offset*2 + 4 + (skullAnimCounter/5), 103+6, '\x1D' | V_YELLOWMAP, false); // right arrow }
} }
else else
{ {
maintext = currentMenu->menuitems[itemOn].text;
mainflags = 0;
}
if (selectabletext != NULL)
{
// We have a selection. Let's show the full menu text on top, and the choice below.
if (currentMenu->menuitems[itemOn].text)
V_DrawCenteredLSTitleHighString(220 + offset*2, 75, selectableflags, currentMenu->menuitems[itemOn].text);
selectableflags |= V_YELLOWMAP;
INT32 w = V_LSTitleLowStringWidth(selectabletext, selectableflags)/2;
V_DrawLSTitleLowString(220-w + offset*2, 103, selectableflags, selectabletext);
V_DrawCharacter(220-w + offset*2 - 8 - (skullAnimCounter/5), 103+6, '\x1C' | selectableflags, false); // left arrow
V_DrawCharacter(220+w + offset*2 + (skullAnimCounter/5), 103+6, '\x1D' | selectableflags, false); // right arrow
}
if (maintext != NULL)
{
// This is a regular menu option. Try to break it onto two lines.
char word1[MAXSTRINGLENGTH];
INT16 word1len = 0;
char word2[MAXSTRINGLENGTH];
INT16 word2len = 0;
boolean sok = false;
while (maintext[j] && j < MAXSTRINGLENGTH)
{
if (maintext[j] == ' ' && !sok)
{
sok = true;
j++;
continue; // We don't care about this :moyai:
}
if (sok)
{
word2[word2len] = maintext[j];
word2len++;
}
else
{
word1[word1len] = maintext[j];
word1len++;
}
j++;
}
word1[word1len] = '\0';
word2[word2len] = '\0';
// If there's no 2nd word, take this opportunity to center this line of text. // If there's no 2nd word, take this opportunity to center this line of text.
if (word1len) if (word1len)
V_DrawCenteredLSTitleHighString(220 + offset*2, 75 + (!word2len ? 10 : 0), 0, word1); V_DrawCenteredLSTitleHighString(220 + offset*2, 75 + (!word2len ? 10 : 0), mainflags, word1);
if (word2len) if (word2len)
V_DrawCenteredLSTitleLowString(220 + offset*2, 103, 0, word2); V_DrawCenteredLSTitleLowString(220 + offset*2, 103, mainflags, word2);
} }
if (gamestate != GS_INTERMISSION && roundqueue.size > 0) if (gamestate != GS_INTERMISSION && roundqueue.size > 0)
@ -4539,6 +4588,106 @@ void M_DrawPause(void)
} }
} }
void M_DrawKickHandler(void)
{
// fake round queue drawer simply to make release
INT32 x = 29 + 4, y = 70, returny = y;
INT32 pokeamount = (playerkickmenu.poke & 1) ? -playerkickmenu.poke/2 : playerkickmenu.poke/2;
INT32 x2 = x + pokeamount - 9 - 8;
boolean datarightofcolumn = false;
patch_t *resbar = W_CachePatchName("R_RESBAR", PU_CACHE); // Results bars for players
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
V_DrawMappedPatch(
x, y,
(playeringame[i] == true)
? ((players[i].spectator == true) ? V_TRANSLUCENT : 0)
: V_MODULATE,
resbar, NULL
);
V_DrawRightAlignedThinString(
x+13, y-2,
((i == playerkickmenu.player)
? highlightflags
: 0
),
va("%u", i)
);
if (playeringame[i] == true)
{
if (players[i].skincolor != SKINCOLOR_NONE)
{
UINT8 *charcolormap;
if ((players[i].pflags & PF_NOCONTEST) && players[i].bot)
{
// RETIRED !!
charcolormap = R_GetTranslationColormap(TC_DEFAULT, players[i].skincolor, GTC_CACHE);
V_DrawMappedPatch(x+14, y-5, 0, W_CachePatchName("MINIDEAD", PU_CACHE), charcolormap);
}
else
{
charcolormap = R_GetTranslationColormap(players[i].skin, players[i].skincolor, GTC_CACHE);
V_DrawMappedPatch(x+14, y-5, 0, faceprefix[players[i].skin][FACE_MINIMAP], charcolormap);
}
}
V_DrawThinString(
x+27, y-2,
(
P_IsMachineLocalPlayer(&players[i])
? highlightflags
: 0
)|V_ALLOWLOWERCASE|V_6WIDTHSPACE,
player_names[i]
);
V_DrawRightAlignedThinString(
x+118, y-2,
V_ALLOWLOWERCASE|V_6WIDTHSPACE,
(players[i].spectator) ? "SPECTATOR" : "PLAYING"
);
}
if (i == playerkickmenu.player)
{
V_DrawScaledPatch(
x2, y-1,
(datarightofcolumn ? V_FLIP : 0),
W_CachePatchName("M_CURSOR", PU_CACHE)
);
}
y += 13;
if (i == (MAXPLAYERS-1)/2)
{
x = 169 - 4;
y = returny;
datarightofcolumn = true;
x2 = x + 118 + 9 + 8 + 4 - pokeamount;
}
}
//V_DrawFill(32 + (playerkickmenu.player & 8), 32 + (playerkickmenu.player & 7)*8, 8, 8, playeringame[playerkickmenu.player] ? 0 : 16);
V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL);
V_DrawCenteredThinString(
BASEVIDWIDTH/2, 12,
V_ALLOWLOWERCASE|V_6WIDTHSPACE,
(playerkickmenu.adminpowered)
? "You are using ""\x85""Admin Tools""\x80"", ""\x83""(A)""\x80"" to kick and ""\x84""(C)""\x80"" to ban"
: K_GetMidVoteLabel(menucallvote)
);
}
void M_DrawPlaybackMenu(void) void M_DrawPlaybackMenu(void)
{ {
INT16 i; INT16 i;

View file

@ -209,7 +209,6 @@ static boolean M_GamestateCanOpenMenu(void)
{ {
case GS_INTRO: case GS_INTRO:
case GS_CUTSCENE: case GS_CUTSCENE:
case GS_GAMEEND:
case GS_CREDITS: case GS_CREDITS:
case GS_EVALUATION: case GS_EVALUATION:
case GS_CEREMONY: case GS_CEREMONY:

View file

@ -2,6 +2,8 @@
#ifndef k_objects_H #ifndef k_objects_H
#define k_objects_H #define k_objects_H
#include "doomdef.h"
#include "m_fixed.h"
#include "tables.h" // angle_t #include "tables.h" // angle_t
#include "taglist.h" #include "taglist.h"
@ -135,6 +137,7 @@ void Obj_RandomItemVisuals(mobj_t *mobj);
boolean Obj_RandomItemSpawnIn(mobj_t *mobj); boolean Obj_RandomItemSpawnIn(mobj_t *mobj);
fixed_t Obj_RandomItemScale(fixed_t oldScale); fixed_t Obj_RandomItemScale(fixed_t oldScale);
void Obj_RandomItemSpawn(mobj_t *mobj); void Obj_RandomItemSpawn(mobj_t *mobj);
#define RINGBOX_TIME (70)
/* Gachabom Rebound */ /* Gachabom Rebound */
void Obj_GachaBomReboundThink(mobj_t *mobj); void Obj_GachaBomReboundThink(mobj_t *mobj);
@ -143,6 +146,21 @@ void Obj_SpawnGachaBomRebound(mobj_t *source, mobj_t *target);
/* Servant Hand */ /* Servant Hand */
void Obj_ServantHandHandling(player_t *player); void Obj_ServantHandHandling(player_t *player);
/* Super Flicky Controller */
void Obj_SpawnSuperFlickySwarm(player_t *owner, tic_t time);
void Obj_SuperFlickyControllerThink(mobj_t *controller);
void Obj_EndSuperFlickySwarm(mobj_t *controller);
void Obj_ExtendSuperFlickySwarm(mobj_t *controller, tic_t time);
tic_t Obj_SuperFlickySwarmTime(const mobj_t *controller);
/* Super Flicky */
void Obj_SuperFlickyThink(mobj_t *flicky);
void Obj_WhipSuperFlicky(mobj_t *flicky);
void Obj_BlockSuperFlicky(mobj_t *flicky);
void Obj_SuperFlickyPlayerCollide(mobj_t *flicky, mobj_t *player);
void Obj_SuperFlickyLanding(mobj_t *flicky);
boolean Obj_IsSuperFlickyWhippable(const mobj_t *flicky);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -235,16 +235,25 @@ void K_UpdatePodiumWaypoints(player_t *const player)
--------------------------------------------------*/ --------------------------------------------------*/
boolean K_StartCeremony(void) boolean K_StartCeremony(void)
{ {
INT32 podiumMapNum = nummapheaders;
INT32 i;
if (grandprixinfo.gp == false) if (grandprixinfo.gp == false)
{ {
return false; return false;
} }
if (podiummap INT32 i;
&& ((podiumMapNum = G_MapNumber(podiummap)) < nummapheaders) INT32 podiumMapNum = NEXTMAP_INVALID;
if (grandprixinfo.cup != NULL
&& grandprixinfo.cup->cachedlevels[CUPCACHE_PODIUM] != NEXTMAP_INVALID)
{
podiumMapNum = grandprixinfo.cup->cachedlevels[CUPCACHE_PODIUM];
}
else if (podiummap)
{
podiumMapNum = G_MapNumber(podiummap);
}
if (podiumMapNum < nummapheaders
&& mapheaderinfo[podiumMapNum] && mapheaderinfo[podiumMapNum]
&& mapheaderinfo[podiumMapNum]->lumpnum != LUMPERROR) && mapheaderinfo[podiumMapNum]->lumpnum != LUMPERROR)
{ {

50
src/k_powerup.cpp Normal file
View file

@ -0,0 +1,50 @@
/// \brief Battle mode power-up code
#include "k_kart.h"
#include "k_objects.h"
#include "k_powerup.h"
tic_t K_PowerUpRemaining(const player_t* player, kartitems_t powerup)
{
switch (powerup)
{
case POWERUP_SUPERFLICKY:
return Obj_SuperFlickySwarmTime(player->powerup.flickyController);
default:
return 0u;
}
}
void K_GivePowerUp(player_t* player, kartitems_t powerup, tic_t time)
{
switch (powerup)
{
case POWERUP_SUPERFLICKY:
if (K_PowerUpRemaining(player, POWERUP_SUPERFLICKY))
{
Obj_ExtendSuperFlickySwarm(player->powerup.flickyController, time);
}
else
{
Obj_SpawnSuperFlickySwarm(player, time);
}
break;
default:
break;
}
}
void K_DropPowerUps(player_t* player)
{
if (K_PowerUpRemaining(player, POWERUP_SUPERFLICKY))
{
mobj_t* swarm = player->powerup.flickyController;
// Be sure to measure the remaining time before ending the power-up
K_DropPaperItem(player, POWERUP_SUPERFLICKY, Obj_SuperFlickySwarmTime(swarm));
Obj_EndSuperFlickySwarm(swarm);
}
}

19
src/k_powerup.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef __K_POWERUP__
#define __K_POWERUP__
#include "doomtype.h"
#include "d_player.h"
#ifdef __cplusplus
extern "C" {
#endif
tic_t K_PowerUpRemaining(const player_t *player, kartitems_t powerup);
void K_GivePowerUp(player_t *player, kartitems_t powerup, tic_t timer);
void K_DropPowerUps(player_t *player);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // __K_POWERUP__

View file

@ -218,6 +218,17 @@ static kartitems_t K_KartItemReelBoss[] =
KITEM_NONE KITEM_NONE
}; };
static kartslotmachine_t K_KartItemReelRingBox[] =
{
KSM_BAR,
KSM_DOUBLEBAR,
KSM_TRIPLEBAR,
KSM_RING,
KSM_SEVEN,
KSM_JACKPOT,
KSM__MAX
};
/*-------------------------------------------------- /*--------------------------------------------------
boolean K_ItemEnabled(kartitems_t item) boolean K_ItemEnabled(kartitems_t item)
@ -438,7 +449,7 @@ static UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers)
Return:- Return:-
The player's finalized item distance. The player's finalized item distance.
--------------------------------------------------*/ --------------------------------------------------*/
static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers) UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers)
{ {
UINT32 pdis = 0; UINT32 pdis = 0;
@ -1052,6 +1063,7 @@ static void K_InitRoulette(itemroulette_t *const roulette)
roulette->active = true; roulette->active = true;
roulette->eggman = false; roulette->eggman = false;
roulette->ringbox = false;
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
@ -1104,19 +1116,20 @@ static void K_InitRoulette(itemroulette_t *const roulette)
} }
/*-------------------------------------------------- /*--------------------------------------------------
static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t item) static void K_PushToRouletteItemList(itemroulette_t *const roulette, INT32 item)
Pushes a new item to the end of the item Pushes a new item to the end of the item
roulette's item list. roulette's item list. Also accepts slot machine
values instead of items.
Input Arguments:- Input Arguments:-
roulette - The item roulette data to modify. roulette - The item roulette data to modify.
item - The item to push to the list. item - The item / slot machine index to push to the list.
Return:- Return:-
N/A N/A
--------------------------------------------------*/ --------------------------------------------------*/
static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t item) static void K_PushToRouletteItemList(itemroulette_t *const roulette, INT32 item)
{ {
#ifdef ITEM_LIST_SIZE #ifdef ITEM_LIST_SIZE
if (roulette->itemListLen >= ITEM_LIST_SIZE) if (roulette->itemListLen >= ITEM_LIST_SIZE)
@ -1253,11 +1266,11 @@ static void K_CalculateRouletteSpeed(itemroulette_t *const roulette)
} }
/*-------------------------------------------------- /*--------------------------------------------------
void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette) void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox)
See header file for description. See header file for description.
--------------------------------------------------*/ --------------------------------------------------*/
void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette) void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox)
{ {
UINT32 spawnChance[NUMKARTRESULTS] = {0}; UINT32 spawnChance[NUMKARTRESULTS] = {0};
UINT32 totalSpawnChance = 0; UINT32 totalSpawnChance = 0;
@ -1276,6 +1289,20 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
K_CalculateRouletteSpeed(roulette); K_CalculateRouletteSpeed(roulette);
} }
if (ringbox == true)
{
// If this is being invoked by a Ring Box, it should literally never produce items.
kartslotmachine_t *presetlist = K_KartItemReelRingBox;
roulette->ringbox = true;
for (i = 0; presetlist[i] != KSM__MAX; i++)
{
K_PushToRouletteItemList(roulette, presetlist[i]);
}
return;
}
// SPECIAL CASE No. 1: // SPECIAL CASE No. 1:
// Give only the debug item if specified // Give only the debug item if specified
if (cv_kartdebugitem.value != KITEM_NONE) if (cv_kartdebugitem.value != KITEM_NONE)
@ -1412,12 +1439,12 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
See header file for description. See header file for description.
--------------------------------------------------*/ --------------------------------------------------*/
void K_StartItemRoulette(player_t *const player) void K_StartItemRoulette(player_t *const player, boolean ringbox)
{ {
itemroulette_t *const roulette = &player->itemRoulette; itemroulette_t *const roulette = &player->itemRoulette;
size_t i; size_t i;
K_FillItemRouletteData(player, roulette); K_FillItemRouletteData(player, roulette, ringbox);
if (K_PlayerUsesBotMovement(player) == true) if (K_PlayerUsesBotMovement(player) == true)
{ {
@ -1444,7 +1471,7 @@ void K_StartItemRoulette(player_t *const player)
void K_StartEggmanRoulette(player_t *const player) void K_StartEggmanRoulette(player_t *const player)
{ {
itemroulette_t *const roulette = &player->itemRoulette; itemroulette_t *const roulette = &player->itemRoulette;
K_StartItemRoulette(player); K_StartItemRoulette(player, false);
roulette->eggman = true; roulette->eggman = true;
} }
@ -1461,6 +1488,19 @@ fixed_t K_GetRouletteOffset(itemroulette_t *const roulette, fixed_t renderDelta)
return FixedMul(FixedDiv(midTic - curTic, ((roulette->speed + 1) << FRACBITS)), ROULETTE_SPACING); return FixedMul(FixedDiv(midTic - curTic, ((roulette->speed + 1) << FRACBITS)), ROULETTE_SPACING);
} }
/*--------------------------------------------------
fixed_t K_GetSlotOffset(itemroulette_t *const roulette, fixed_t renderDelta)
See header file for description.
--------------------------------------------------*/
fixed_t K_GetSlotOffset(itemroulette_t *const roulette, fixed_t renderDelta)
{
const fixed_t curTic = (roulette->tics << FRACBITS) - renderDelta;
const fixed_t midTic = roulette->speed * (FRACUNIT >> 1);
return FixedMul(FixedDiv(midTic - curTic, ((roulette->speed + 1) << FRACBITS)), SLOT_SPACING);
}
/*-------------------------------------------------- /*--------------------------------------------------
static void K_KartGetItemResult(player_t *const player, kartitems_t getitem) static void K_KartGetItemResult(player_t *const player, kartitems_t getitem)
@ -1572,9 +1612,17 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd)
// And one more nudge for the remaining delay. // And one more nudge for the remaining delay.
roulette->tics = (roulette->tics + fudgedDelay) % roulette->speed; roulette->tics = (roulette->tics + fudgedDelay) % roulette->speed;
kartitems_t finalItem = roulette->itemList[ roulette->index ]; INT32 finalItem = roulette->itemList[ roulette->index ];
K_KartGetItemResult(player, finalItem); if (roulette->ringbox == true)
{
player->ringboxdelay = TICRATE;
player->ringboxaward = finalItem;
}
else
{
K_KartGetItemResult(player, finalItem);
}
player->karthud[khud_itemblink] = TICRATE; player->karthud[khud_itemblink] = TICRATE;
player->karthud[khud_itemblinkmode] = 0; player->karthud[khud_itemblinkmode] = 0;
@ -1582,7 +1630,10 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd)
if (P_IsDisplayPlayer(player) && !demo.freecam) if (P_IsDisplayPlayer(player) && !demo.freecam)
{ {
S_StartSound(NULL, sfx_itrolf); if (roulette->ringbox)
S_StartSound(NULL, sfx_s245);
else
S_StartSound(NULL, sfx_itrolf);
} }
} }
@ -1603,7 +1654,10 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd)
if (P_IsDisplayPlayer(player) && !demo.freecam) if (P_IsDisplayPlayer(player) && !demo.freecam)
{ {
S_StartSound(NULL, sfx_itrol1 + roulette->sound); if (roulette->ringbox)
S_StartSound(NULL, sfx_s240);
else
S_StartSound(NULL, sfx_itrol1 + roulette->sound);
} }
} }
else else

View file

@ -23,6 +23,9 @@ extern "C" {
#define ROULETTE_SPACING (36 << FRACBITS) #define ROULETTE_SPACING (36 << FRACBITS)
#define ROULETTE_SPACING_SPLITSCREEN (16 << FRACBITS) #define ROULETTE_SPACING_SPLITSCREEN (16 << FRACBITS)
#define SLOT_SPACING (40 << FRACBITS)
#define SLOT_SPACING_SPLITSCREEN (22 << FRACBITS)
/*-------------------------------------------------- /*--------------------------------------------------
boolean K_ItemEnabled(kartitems_t item); boolean K_ItemEnabled(kartitems_t item);
@ -98,7 +101,7 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette,
/*-------------------------------------------------- /*--------------------------------------------------
void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette); void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox);
Fills out the item roulette struct when it is Fills out the item roulette struct when it is
initially created. This function needs to be initially created. This function needs to be
@ -109,16 +112,17 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette,
player - The player this roulette data is for. player - The player this roulette data is for.
Can be NULL for generic use. Can be NULL for generic use.
roulette - The roulette data struct to fill out. roulette - The roulette data struct to fill out.
ringbox - Is this roulette fill triggered by a just-respawned Ring Box?
Return:- Return:-
N/A N/A
--------------------------------------------------*/ --------------------------------------------------*/
void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette); void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox);
/*-------------------------------------------------- /*--------------------------------------------------
void K_StartItemRoulette(player_t *const player); void K_StartItemRoulette(player_t *const player, boolean ringbox);
Starts the item roulette sequence for a player. Starts the item roulette sequence for a player.
This stage can only be used by gameplay, thus This stage can only be used by gameplay, thus
@ -126,12 +130,13 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
Input Arguments:- Input Arguments:-
player - The player to start the item roulette for. player - The player to start the item roulette for.
ringbox - Is this roulette being started from a just-respawned Ring Box?
Return:- Return:-
N/A N/A
--------------------------------------------------*/ --------------------------------------------------*/
void K_StartItemRoulette(player_t *const player); void K_StartItemRoulette(player_t *const player, boolean ringbox);
/*-------------------------------------------------- /*--------------------------------------------------
@ -169,6 +174,23 @@ void K_StartEggmanRoulette(player_t *const player);
fixed_t K_GetRouletteOffset(itemroulette_t *const roulette, fixed_t renderDelta); fixed_t K_GetRouletteOffset(itemroulette_t *const roulette, fixed_t renderDelta);
/*--------------------------------------------------
fixed_t K_GetSlotOffset(itemroulette_t *const roulette, fixed_t renderDelta);
Gets the Y offset, for use in the slot HUD.
A separate function since it is used both by the
HUD itself, as well as when confirming an item.
Input Arguments:-
roulette - The roulette we are drawing for.
renderDelta - Fractional tic delta, when used for HUD.
Return:-
The Y offset when drawing the item.
--------------------------------------------------*/
fixed_t K_GetSlotOffset(itemroulette_t *const roulette, fixed_t renderDelta);
/*-------------------------------------------------- /*--------------------------------------------------
void K_KartItemRoulette(player_t *const player, ticcmd_t *cmd); void K_KartItemRoulette(player_t *const player, ticcmd_t *cmd);
@ -186,6 +208,8 @@ fixed_t K_GetRouletteOffset(itemroulette_t *const roulette, fixed_t renderDelta)
void K_KartItemRoulette(player_t *const player, ticcmd_t *cmd); void K_KartItemRoulette(player_t *const player, ticcmd_t *cmd);
UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -57,10 +57,7 @@ static void K_MidVoteKick(void)
return; return;
} }
if (server) SendKick(g_midVote.victim - players, KICK_MSG_VOTE_KICK);
{
SendKick(g_midVote.victim - players, KICK_MSG_VOTE_KICK);
}
} }
/*-------------------------------------------------- /*--------------------------------------------------
@ -70,7 +67,29 @@ static void K_MidVoteKick(void)
--------------------------------------------------*/ --------------------------------------------------*/
static void K_MidVoteRockTheVote(void) static void K_MidVoteRockTheVote(void)
{ {
G_ExitLevel(); if (G_GamestateUsesExitLevel() == false)
{
return;
}
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
/*--------------------------------------------------
static void K_MidVoteRunItBack(void)
MVT_RUNITBACK's success function.
--------------------------------------------------*/
static void K_MidVoteRunItBack(void)
{
boolean newencore = false;
if (cv_kartencore.value != 0)
{
newencore = (cv_kartencore.value == 1) || encoremode;
}
D_MapChange(gamemap, gametype, newencore, false, 0, false, false);
} }
static midVoteTypeDef_t g_midVoteTypeDefs[MVT__MAX] = static midVoteTypeDef_t g_midVoteTypeDefs[MVT__MAX] =
@ -85,25 +104,24 @@ static midVoteTypeDef_t g_midVoteTypeDefs[MVT__MAX] =
{ // MVT_RTV { // MVT_RTV
"RTV", "RTV",
"Skip Level?", "Skip Level?",
CVAR_INIT ("zvote_rtv_allowed", "No", CV_SAVE|CV_NETVAR, CV_YesNo, NULL), CVAR_INIT ("zvote_rtv_allowed", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL),
K_MidVoteRockTheVote K_MidVoteRockTheVote
}, },
{ // MVT_RUNITBACK
"RUNITBACK",
"Redo Level?",
CVAR_INIT ("zvote_runitback_allowed", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL),
K_MidVoteRunItBack
},
}; };
/*-------------------------------------------------- /*--------------------------------------------------
static boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType)
Specifies whenever or not a vote type is intended See header file for description.
to specify a "victim", or a player that would be
negatively affected by the vote.
Input Arguments:-
voteType - The vote type to check.
Return:-
true if it uses a victim, otherwise false.
--------------------------------------------------*/ --------------------------------------------------*/
static boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType)
{ {
switch (voteType) switch (voteType)
{ {
@ -125,9 +143,6 @@ static boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType)
--------------------------------------------------*/ --------------------------------------------------*/
static void Command_CallVote(void) static void Command_CallVote(void)
{ {
UINT8 buf[MAXTEXTCMD];
UINT8 *buf_p = buf;
size_t numArgs = 0; size_t numArgs = 0;
const char *voteTypeStr = NULL; const char *voteTypeStr = NULL;
@ -136,8 +151,6 @@ static void Command_CallVote(void)
const char *voteVariableStr = NULL; const char *voteVariableStr = NULL;
INT32 voteVariable = 0; INT32 voteVariable = 0;
player_t *victim = NULL;
INT32 i = INT32_MAX; INT32 i = INT32_MAX;
if (netgame == false) if (netgame == false)
@ -184,11 +197,26 @@ static void Command_CallVote(void)
break; break;
} }
} }
}
}
if (voteVariable >= 0 && voteVariable < MAXPLAYERS) K_SendCallMidVote(voteType, voteVariable);
{ }
victim = &players[voteVariable];
} /*--------------------------------------------------
void K_SendCallMidVote(midVoteType_e voteType, INT32 voteVariable)
See header file for description.
--------------------------------------------------*/
void K_SendCallMidVote(midVoteType_e voteType, INT32 voteVariable)
{
player_t *victim = NULL;
if (K_MidVoteTypeUsesVictim(voteType) == true)
{
if (voteVariable >= 0 && voteVariable < MAXPLAYERS)
{
victim = &players[voteVariable];
} }
} }
@ -198,6 +226,9 @@ static void Command_CallVote(void)
return; return;
} }
UINT8 buf[MAXTEXTCMD];
UINT8 *buf_p = buf;
WRITEUINT8(buf_p, voteType); WRITEUINT8(buf_p, voteType);
WRITEINT32(buf_p, voteVariable); WRITEINT32(buf_p, voteVariable);
@ -341,6 +372,51 @@ boolean K_AnyMidVotesAllowed(void)
return false; return false;
} }
/*--------------------------------------------------
midVoteType_e K_GetNextCallableMidVote(INT32 seed, boolean backwards)
See header file for description.
--------------------------------------------------*/
midVoteType_e K_GetNextAllowedMidVote(midVoteType_e seed, boolean backwards)
{
if (seed >= MVT__MAX)
seed = 0;
midVoteType_e i = seed;
if (backwards)
{
do
{
if (i <= 0)
i = MVT__MAX;
i--;
if (g_midVoteTypeDefs[i].cv_allowed.value != 0)
return i;
}
while (i != seed);
}
else
{
do
{
i++;
if (i >= MVT__MAX)
i = 0;
if (g_midVoteTypeDefs[i].cv_allowed.value != 0)
return i;
}
while (i != seed);
}
return MVT__MAX;
}
/*-------------------------------------------------- /*--------------------------------------------------
boolean K_PlayerIDAllowedInMidVote(const UINT8 id) boolean K_PlayerIDAllowedInMidVote(const UINT8 id)
@ -468,6 +544,46 @@ UINT8 K_CountMidVotes(void)
return voteCount; return voteCount;
} }
/*--------------------------------------------------
boolean K_MinimalCheckNewMidVote(midVoteType_e type)
See header file for description.
--------------------------------------------------*/
boolean K_MinimalCheckNewMidVote(midVoteType_e type)
{
if (g_midVote.active == true)
{
// Don't allow another vote if one is already running.
return false;
}
if (g_midVote.delay > 0)
{
// Don't allow another vote if one has recently just ran.
return false;
}
if (type < 0 || type >= MVT__MAX)
{
// Invalid range.
return false;
}
if (g_midVoteTypeDefs[type].cv_allowed.value == 0)
{
// These types of votes aren't allowed on this server.
return false;
}
if (K_PlayerIDAllowedInMidVote(consoleplayer) == false)
{
// Invalid calling player.
return false;
}
return true;
}
/*-------------------------------------------------- /*--------------------------------------------------
boolean K_AllowNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim) boolean K_AllowNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim)
@ -630,7 +746,11 @@ void K_MidVoteFinalize(fixed_t delayMul)
--------------------------------------------------*/ --------------------------------------------------*/
void K_MidVoteSuccess(void) void K_MidVoteSuccess(void)
{ {
if (g_midVoteTypeDefs[ g_midVote.type ].callback != NULL) if (
server == true
&& demo.playback == false
&& g_midVoteTypeDefs[ g_midVote.type ].callback != NULL
)
{ {
g_midVoteTypeDefs[ g_midVote.type ].callback(); g_midVoteTypeDefs[ g_midVote.type ].callback();
} }
@ -823,6 +943,25 @@ void K_UpdateMidVotePatches(void)
HU_UpdatePatch(&g_zBarEnds[1][1][1], "TLBXB0"); HU_UpdatePatch(&g_zBarEnds[1][1][1], "TLBXB0");
} }
/*--------------------------------------------------
const char *K_GetMidVoteLabel(midVoteType_e i)
See header file for description.
--------------------------------------------------*/
const char *K_GetMidVoteLabel(midVoteType_e i)
{
if (
i < 0
|| i >= MVT__MAX
|| g_midVoteTypeDefs[i].label == NULL)
{
return "N/A";
}
return g_midVoteTypeDefs[i].label;
}
/*-------------------------------------------------- /*--------------------------------------------------
static void K_DrawMidVoteBar(fixed_t x, fixed_t y, INT32 flags, fixed_t fill, skincolornum_t color, boolean flipped) static void K_DrawMidVoteBar(fixed_t x, fixed_t y, INT32 flags, fixed_t fill, skincolornum_t color, boolean flipped)
@ -1006,11 +1145,13 @@ void K_DrawMidVote(void)
(id & 1) (id & 1)
); );
const char *label = K_GetMidVoteLabel(g_midVote.type);
// Vote main label // Vote main label
strWidth = V__OneScaleStringWidth( strWidth = V__OneScaleStringWidth(
FRACUNIT, FRACUNIT,
V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN,
KART_FONT, g_midVoteTypeDefs[g_midVote.type].label KART_FONT, label
); );
V__DrawOneScaleString( V__DrawOneScaleString(
@ -1018,7 +1159,7 @@ void K_DrawMidVote(void)
y - (18 * FRACUNIT), y - (18 * FRACUNIT),
FRACUNIT, FRACUNIT,
V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, NULL, V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, NULL,
KART_FONT, g_midVoteTypeDefs[g_midVote.type].label KART_FONT, label
); );
// Vote extra text // Vote extra text

View file

@ -28,6 +28,7 @@ typedef enum
{ {
MVT_KICK, // Kick another player in the server MVT_KICK, // Kick another player in the server
MVT_RTV, // Exit level early MVT_RTV, // Exit level early
MVT_RUNITBACK, // Restart level fresh
MVT__MAX, // Total number of vote types MVT__MAX, // Total number of vote types
} midVoteType_e; } midVoteType_e;
@ -63,6 +64,39 @@ struct midVote_t
extern midVote_t g_midVote; extern midVote_t g_midVote;
/*--------------------------------------------------
boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType)
Specifies whenever or not a vote type is intended
to specify a "victim", or a player that would be
negatively affected by the vote.
Input Arguments:-
voteType - The vote type to check.
Return:-
true if it uses a victim, otherwise false.
--------------------------------------------------*/
boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType);
/*--------------------------------------------------
void K_SendCallMidVote(midVoteType_e voteType, INT32 voteVariable)
Prepares and sends net packet for calling a midvote.
Input Arguments:-
voteType - The type of vote a local player is trying to call.
variable - Extra arguments for the vote type.
Return:-
N/A
--------------------------------------------------*/
void K_SendCallMidVote(midVoteType_e voteType, INT32 voteVariable);
/*-------------------------------------------------- /*--------------------------------------------------
void K_RegisterMidVoteCVars(void); void K_RegisterMidVoteCVars(void);
@ -99,6 +133,22 @@ void K_ResetMidVote(void);
boolean K_AnyMidVotesAllowed(void); boolean K_AnyMidVotesAllowed(void);
/*--------------------------------------------------
midVoteType_e K_GetNextCallableMidVote(midVoteType_e seed, boolean backwards)
Gets the next enabled Z-vote type in the list.
Input Arguments:-
seed - position in the list to start with
backwards - if true, traverses list in reverse order
Return:-
next Z-vote id if any vote types are enabled, otherwise MVT__MAX.
--------------------------------------------------*/
midVoteType_e K_GetNextAllowedMidVote(midVoteType_e seed, boolean backwards);
/*-------------------------------------------------- /*--------------------------------------------------
boolean K_PlayerIDAllowedInMidVote(const UINT8 id); boolean K_PlayerIDAllowedInMidVote(const UINT8 id);
@ -170,6 +220,18 @@ boolean K_PlayerIDMidVoted(const UINT8 id);
UINT8 K_CountMidVotes(void); UINT8 K_CountMidVotes(void);
/*--------------------------------------------------
boolean K_MinimalCheckNewMidVote(midVoteType_e type)
Returns if the variables given are a valid state for
pause menu Z-vote flow.
Input Arguments:-
type - The type of vote they're trying to call.
--------------------------------------------------*/
boolean K_MinimalCheckNewMidVote(midVoteType_e type);
/*-------------------------------------------------- /*--------------------------------------------------
boolean K_AllowNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim); boolean K_AllowNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim);
@ -263,6 +325,19 @@ void K_TickMidVote(void);
void K_UpdateMidVotePatches(void); void K_UpdateMidVotePatches(void);
/*--------------------------------------------------
const char *K_GetMidVoteLabel(midVoteType_e i)
Input Arguments:-
i - id in the list to retrieve label for
Return:-
label associated with that id, or a sensible default (not NULL)
--------------------------------------------------*/
const char *K_GetMidVoteLabel(midVoteType_e i);
/*-------------------------------------------------- /*--------------------------------------------------
void K_DrawMidVote(void); void K_DrawMidVote(void);

View file

@ -36,6 +36,7 @@
#include "p_spec.h" // P_StartQuake #include "p_spec.h" // P_StartQuake
#include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision #include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision
#include "hu_stuff.h" // for the cecho #include "hu_stuff.h" // for the cecho
#include "k_powerup.h"
#include "lua_script.h" #include "lua_script.h"
#include "lua_libs.h" #include "lua_libs.h"
@ -3845,6 +3846,40 @@ static int lib_kAddHitLag(lua_State *L)
} }
static int lib_kPowerUpRemaining(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
kartitems_t powerup = luaL_checkinteger(L, 2);
//HUDSAFE
if (!player)
return LUA_ErrInvalid(L, "player_t");
lua_pushinteger(L, K_PowerUpRemaining(player, powerup));
return 1;
}
static int lib_kGivePowerUp(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
kartitems_t powerup = luaL_checkinteger(L, 2);
tic_t time = (tic_t)luaL_checkinteger(L, 3);
NOHUD
if (!player)
return LUA_ErrInvalid(L, "player_t");
K_GivePowerUp(player, powerup, time);
return 0;
}
static int lib_kDropPowerUps(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
NOHUD
if (!player)
return LUA_ErrInvalid(L, "player_t");
K_DropPowerUps(player);
return 0;
}
static int lib_kInitBossHealthBar(lua_State *L) static int lib_kInitBossHealthBar(lua_State *L)
{ {
const char *enemyname = luaL_checkstring(L, 1); const char *enemyname = luaL_checkstring(L, 1);
@ -4167,6 +4202,11 @@ static luaL_Reg lib[] = {
{"K_GetCollideAngle",lib_kGetCollideAngle}, {"K_GetCollideAngle",lib_kGetCollideAngle},
{"K_AddHitLag",lib_kAddHitLag}, {"K_AddHitLag",lib_kAddHitLag},
// k_powerup
{"K_PowerUpRemaining",lib_kPowerUpRemaining},
{"K_GivePowerUp",lib_kGivePowerUp},
{"K_DropPowerUps",lib_kDropPowerUps},
// k_boss // k_boss
{"K_InitBossHealthBar", lib_kInitBossHealthBar}, {"K_InitBossHealthBar", lib_kInitBossHealthBar},
{"K_UpdateBossHealthBar", lib_kUpdateBossHealthBar}, {"K_UpdateBossHealthBar", lib_kUpdateBossHealthBar},

View file

@ -251,6 +251,10 @@ static int player_get(lua_State *L)
lua_pushboolean(L, plr->flipDI); lua_pushboolean(L, plr->flipDI);
else if (fastcmp(field,"markedfordeath")) else if (fastcmp(field,"markedfordeath"))
lua_pushboolean(L, plr->markedfordeath); lua_pushboolean(L, plr->markedfordeath);
else if (fastcmp(field,"ringboxdelay"))
lua_pushinteger(L, plr->ringboxdelay);
else if (fastcmp(field,"ringboxaward"))
lua_pushinteger(L, plr->ringboxaward);
else if (fastcmp(field,"drift")) else if (fastcmp(field,"drift"))
lua_pushinteger(L, plr->drift); lua_pushinteger(L, plr->drift);
else if (fastcmp(field,"driftcharge")) else if (fastcmp(field,"driftcharge"))
@ -651,6 +655,10 @@ static int player_set(lua_State *L)
plr->flipDI = luaL_checkboolean(L, 3); plr->flipDI = luaL_checkboolean(L, 3);
else if (fastcmp(field,"markedfordeath")) else if (fastcmp(field,"markedfordeath"))
plr->markedfordeath = luaL_checkboolean(L, 3); plr->markedfordeath = luaL_checkboolean(L, 3);
else if (fastcmp(field,"ringboxdelay"))
plr->ringboxdelay = luaL_checkinteger(L, 3);
else if (fastcmp(field,"ringboxaward"))
plr->ringboxaward = luaL_checkinteger(L, 3);
else if (fastcmp(field,"drift")) else if (fastcmp(field,"drift"))
plr->drift = luaL_checkinteger(L, 3); plr->drift = luaL_checkinteger(L, 3);
else if (fastcmp(field,"driftcharge")) else if (fastcmp(field,"driftcharge"))

View file

@ -394,8 +394,6 @@ int LUA_WriteGlobals(lua_State *L, const char *word)
skincolor_redring = (UINT16)luaL_checkinteger(L, 2); skincolor_redring = (UINT16)luaL_checkinteger(L, 2);
else if (fastcmp(word, "skincolor_bluering")) else if (fastcmp(word, "skincolor_bluering"))
skincolor_bluering = (UINT16)luaL_checkinteger(L, 2); skincolor_bluering = (UINT16)luaL_checkinteger(L, 2);
else if (fastcmp(word, "emeralds"))
emeralds = (UINT16)luaL_checkinteger(L, 2);
else if (fastcmp(word, "gravity")) else if (fastcmp(word, "gravity"))
gravity = (fixed_t)luaL_checkinteger(L, 2); gravity = (fixed_t)luaL_checkinteger(L, 2);
else if (fastcmp(word, "stoppedclock")) else if (fastcmp(word, "stoppedclock"))

View file

@ -469,29 +469,6 @@ void Command_Savecheckpoint_f(void)
} }
} }
// Like M_GetAllEmeralds() but for console devmode junkies.
/*
void Command_Getallemeralds_f(void)
{
REQUIRE_CHEATS;
REQUIRE_SINGLEPLAYER;
emeralds = EMERALD_ALL;
CONS_Printf(M_GetText("You now have all 7 emeralds.\n"));
}
void Command_Resetemeralds_f(void)
{
REQUIRE_CHEATS;
REQUIRE_SINGLEPLAYER;
emeralds = 0;
CONS_Printf(M_GetText("Emeralds reset to zero.\n"));
}
*/
// //
// Devmode // Devmode
// //

View file

@ -39,6 +39,7 @@ typedef enum {
CHEAT_SCORE, CHEAT_SCORE,
CHEAT_ANGLE, CHEAT_ANGLE,
CHEAT_RESPAWNAT, CHEAT_RESPAWNAT,
CHEAT_GIVEPOWERUP,
NUMBER_OF_CHEATS NUMBER_OF_CHEATS
} cheat_t; } cheat_t;

View file

@ -610,6 +610,7 @@ void M_ClearStats(void)
gamedata->timesBeaten = 0; gamedata->timesBeaten = 0;
gamedata->everloadedaddon = false; gamedata->everloadedaddon = false;
gamedata->everfinishedcredits = false;
gamedata->eversavedreplay = false; gamedata->eversavedreplay = false;
gamedata->everseenspecial = false; gamedata->everseenspecial = false;
gamedata->evercrashed = false; gamedata->evercrashed = false;
@ -738,6 +739,43 @@ boolean M_NotFreePlay(player_t *player)
return false; return false;
} }
UINT16 M_CheckCupEmeralds(UINT8 difficulty)
{
if (difficulty == 0)
return 0;
if (difficulty >= KARTGP_MAX)
difficulty = KARTGP_MASTER;
cupheader_t *cup;
UINT16 ret = 0, seen = 0;
for (cup = kartcupheaders; cup; cup = cup->next)
{
// Does it not *have* an emerald?
if (cup->emeraldnum == 0 || cup->emeraldnum > 14)
continue;
UINT16 emerald = 1<<(cup->emeraldnum-1);
// Only count the first reference.
if (seen & emerald)
continue;
// We've seen it, prevent future repetitions.
seen |= emerald;
// Did you actually get it?
if (cup->windata[difficulty].got_emerald == false)
continue;
// Wa hoo !
ret |= emerald;
}
return ret;
}
// See also M_GetConditionString // See also M_GetConditionString
boolean M_CheckCondition(condition_t *cn, player_t *player) boolean M_CheckCondition(condition_t *cn, player_t *player)
{ {
@ -791,30 +829,12 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
case UC_ALLSUPER: case UC_ALLSUPER:
case UC_ALLEMERALDS: case UC_ALLEMERALDS:
{ {
cupheader_t *cup;
UINT16 ret = 0; UINT16 ret = 0;
UINT8 i;
if (gamestate == GS_LEVEL) if (gamestate == GS_LEVEL)
return false; // this one could be laggy with many cups available return false; // this one could be laggy with many cups available
for (cup = kartcupheaders; cup; cup = cup->next) ret = M_CheckCupEmeralds(cn->requirement);
{
if (cup->emeraldnum == 0)
continue;
i = cn->requirement;
for (i = cn->requirement; i < KARTGP_MAX; i++)
{
if (cup->windata[i].got_emerald == true)
break;
}
if (i == KARTGP_MAX)
continue;
ret |= 1<<(cup->emeraldnum-1);
}
if (cn->type == UC_ALLCHAOS) if (cn->type == UC_ALLCHAOS)
return ALLCHAOSEMERALDS(ret); return ALLCHAOSEMERALDS(ret);
@ -835,6 +855,8 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
case UC_ADDON: case UC_ADDON:
return ((gamedata->everloadedaddon == true) return ((gamedata->everloadedaddon == true)
&& M_SecretUnlocked(SECRET_ADDONS, true)); && M_SecretUnlocked(SECRET_ADDONS, true));
case UC_CREDITS:
return (gamedata->everfinishedcredits == true);
case UC_REPLAY: case UC_REPLAY:
return (gamedata->eversavedreplay == true); return (gamedata->eversavedreplay == true);
case UC_CRASH: case UC_CRASH:
@ -1289,6 +1311,8 @@ static const char *M_GetConditionString(condition_t *cn)
if (!M_SecretUnlocked(SECRET_ADDONS, true)) if (!M_SecretUnlocked(SECRET_ADDONS, true))
return NULL; return NULL;
return "load a custom addon into \"Dr. Robotnik's Ring Racers\""; return "load a custom addon into \"Dr. Robotnik's Ring Racers\"";
case UC_CREDITS:
return "watch the developer credits all the way from start to finish";
case UC_REPLAY: case UC_REPLAY:
return "save a replay after finishing a round"; return "save a replay after finishing a round";
case UC_CRASH: case UC_CRASH:

View file

@ -52,6 +52,7 @@ typedef enum
UC_CONDITIONSET, // CONDITIONSET [condition set number] UC_CONDITIONSET, // CONDITIONSET [condition set number]
UC_ADDON, // Ever loaded a custom file? UC_ADDON, // Ever loaded a custom file?
UC_CREDITS, // Finish watching the credits
UC_REPLAY, // Save a replay UC_REPLAY, // Save a replay
UC_CRASH, // Hee ho ! UC_CRASH, // Hee ho !
@ -288,6 +289,7 @@ struct gamedata_t
// SPECIFIC SPECIAL EVENTS // SPECIFIC SPECIAL EVENTS
boolean everloadedaddon; boolean everloadedaddon;
boolean everfinishedcredits;
boolean eversavedreplay; boolean eversavedreplay;
boolean everseenspecial; boolean everseenspecial;
boolean evercrashed; boolean evercrashed;
@ -341,6 +343,7 @@ void M_ClearSecrets(void);
void M_ClearStats(void); void M_ClearStats(void);
boolean M_NotFreePlay(player_t *player); boolean M_NotFreePlay(player_t *player);
UINT16 M_CheckCupEmeralds(UINT8 difficulty);
// Updating conditions and unlockables // Updating conditions and unlockables
boolean M_ConditionInterpret(const char *password); boolean M_ConditionInterpret(const char *password);

View file

@ -5,6 +5,15 @@
#include "../m_cond.h" #include "../m_cond.h"
#include "../m_cheat.h" #include "../m_cheat.h"
#include "../s_sound.h" #include "../s_sound.h"
#include "../f_finale.h"
static void M_Credits(INT32 choice)
{
(void)choice;
restoreMenu = currentMenu;
M_ClearMenus(true);
F_StartCredits();
}
menuitem_t EXTRAS_Main[] = menuitem_t EXTRAS_Main[] =
{ {
@ -32,6 +41,9 @@ menuitem_t EXTRAS_Main[] =
{IT_STRING | IT_CVAR | IT_CV_STRING, "Password", "If you don't know any passwords, come back later!", {IT_STRING | IT_CVAR | IT_CV_STRING, "Password", "If you don't know any passwords, come back later!",
NULL, {.cvar = &cv_dummyextraspassword}, 0, 0}, NULL, {.cvar = &cv_dummyextraspassword}, 0, 0},
{IT_STRING | IT_CALL, "Credits", "It's important to know who makes the video games you play.",
NULL, {.routine = M_Credits}, 0, 0},
}; };
// the extras menu essentially reuses the options menu stuff // the extras menu essentially reuses the options menu stuff

View file

@ -485,7 +485,6 @@ void M_StartTimeAttack(INT32 choice)
// Still need to reset devmode // Still need to reset devmode
cht_debug = 0; cht_debug = 0;
emeralds = 0;
if (demo.playback) if (demo.playback)
G_StopDemo(); G_StopDemo();

View file

@ -80,8 +80,8 @@ void M_MPOptSelectInit(INT32 choice)
memcpy(&mpmenu.modewinextend, &arrcpy, sizeof(mpmenu.modewinextend)); memcpy(&mpmenu.modewinextend, &arrcpy, sizeof(mpmenu.modewinextend));
// Guarantee menugametype is good // Guarantee menugametype is good
M_NextMenuGametype(forbidden);
M_PrevMenuGametype(forbidden); M_PrevMenuGametype(forbidden);
M_NextMenuGametype(forbidden);
if (choice != -1) if (choice != -1)
{ {

View file

@ -18,7 +18,7 @@ menuitem_t PLAY_MP_Host[] =
{IT_STRING | IT_CVAR, "Max. Players", "Set how many players can play at once. Others will spectate.", {IT_STRING | IT_CVAR, "Max. Players", "Set how many players can play at once. Others will spectate.",
NULL, {.cvar = &cv_maxplayers}, 0, 0}, NULL, {.cvar = &cv_maxplayers}, 0, 0},
{IT_STRING | IT_KEYHANDLER, "Gamemode", "Choose the type of play on your server.", {IT_STRING | IT_ARROWS, "Gamemode", "Choose the type of play on your server.",
NULL, {.routine = M_HandleHostMenuGametype}, 0, 0}, NULL, {.routine = M_HandleHostMenuGametype}, 0, 0},
{IT_STRING | IT_CALL, "GO", "Select a map with the currently selected gamemode", {IT_STRING | IT_CALL, "GO", "Select a map with the currently selected gamemode",
@ -54,41 +54,21 @@ void M_MPHostInit(INT32 choice)
void M_HandleHostMenuGametype(INT32 choice) void M_HandleHostMenuGametype(INT32 choice)
{ {
const UINT8 pid = 0;
const UINT32 forbidden = GTR_FORBIDMP; const UINT32 forbidden = GTR_FORBIDMP;
(void)choice; if (choice > 0)
if (M_MenuBackPressed(pid))
{
M_GoBack(0);
M_SetMenuDelay(pid);
return;
}
else if (menucmd[pid].dpad_lr > 0 || M_MenuConfirmPressed(pid))
{ {
M_NextMenuGametype(forbidden); M_NextMenuGametype(forbidden);
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
} }
else if (menucmd[pid].dpad_lr < 0) else if (choice == -1)
{
menugametype = GT_RACE;
M_PrevMenuGametype(forbidden);
M_NextMenuGametype(forbidden);
}
else
{ {
M_PrevMenuGametype(forbidden); M_PrevMenuGametype(forbidden);
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
}
if (menucmd[pid].dpad_ud > 0)
{
M_NextOpt();
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
}
else if (menucmd[pid].dpad_ud < 0)
{
M_PrevOpt();
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
} }
} }

View file

@ -8,6 +8,7 @@ target_sources(SRB2SDL2 PRIVATE
discord-requests.c discord-requests.c
message-box.c message-box.c
pause-game.c pause-game.c
pause-kick.c
pause-replay.c pause-replay.c
virtual-keyboard.c virtual-keyboard.c
) )

View file

@ -155,7 +155,8 @@ static void M_StartCup(UINT8 entry)
NULL, MM_NOTHING, NULL, NULL NULL, MM_NOTHING, NULL, NULL
); );
G_HandleSaveLevel(true); if (FIL_FileExists(gpbackup))
remove(gpbackup);
return; return;
} }

View file

@ -266,7 +266,7 @@ boolean M_LevelListFromGametype(INT16 gt)
G_GetBackupCupData( G_GetBackupCupData(
cupgrid.grandprix == true cupgrid.grandprix == true
|| cv_splitplayers.value <= 1 && cv_splitplayers.value <= 1
); );
templevelsearch.cup = kartcupheaders; templevelsearch.cup = kartcupheaders;

View file

@ -5,6 +5,7 @@
#include "../../k_grandprix.h" // K_CanChangeRules #include "../../k_grandprix.h" // K_CanChangeRules
#include "../../m_cond.h" #include "../../m_cond.h"
#include "../../s_sound.h" #include "../../s_sound.h"
#include "../../k_zvote.h"
#ifdef HAVE_DISCORDRPC #ifdef HAVE_DISCORDRPC
#include "../../discord.h" #include "../../discord.h"
@ -22,7 +23,7 @@ menuitem_t PAUSE_Main[] =
{IT_STRING | IT_CALL, "STEREO MODE", "M_ICOSTM", {IT_STRING | IT_CALL, "STEREO MODE", "M_ICOSTM",
NULL, {.routine = M_SoundTest}, 0, 0}, NULL, {.routine = M_SoundTest}, 0, 0},
{IT_STRING | IT_KEYHANDLER, "GAMETYPE", "M_ICOGAM", {IT_STRING | IT_ARROWS, "GAMETYPE", "M_ICOGAM",
NULL, {.routine = M_HandlePauseMenuGametype}, 0, 0}, NULL, {.routine = M_HandlePauseMenuGametype}, 0, 0},
{IT_STRING | IT_CALL, "CHANGE MAP", "M_ICOMAP", {IT_STRING | IT_CALL, "CHANGE MAP", "M_ICOMAP",
@ -39,6 +40,12 @@ menuitem_t PAUSE_Main[] =
NULL, {.routine = M_DiscordRequests}, 0, 0}, NULL, {.routine = M_DiscordRequests}, 0, 0},
#endif #endif
{IT_STRING | IT_ARROWS, "ADMIN TOOLS", "M_ICOADM",
NULL, {.routine = M_KickHandler}, 0, 0},
{IT_STRING | IT_ARROWS, "CALL VOTE", "M_ICOVOT",
NULL, {.routine = M_HandlePauseMenuCallVote}, 0, 0},
{IT_STRING | IT_CALL, "RESUME GAME", "M_ICOUNP", {IT_STRING | IT_CALL, "RESUME GAME", "M_ICOUNP",
NULL, {.routine = M_QuitPauseMenu}, 0, 0}, NULL, {.routine = M_QuitPauseMenu}, 0, 0},
@ -71,7 +78,7 @@ menu_t PAUSE_MainDef = {
PAUSE_Main, PAUSE_Main,
0, 0, 0, 0,
0, 0, 0, 0,
0, MBF_SOUNDLESS,
NULL, NULL,
1, 10, // For transition with some menus! 1, 10, // For transition with some menus!
M_DrawPause, M_DrawPause,
@ -123,6 +130,8 @@ void M_OpenPauseMenu(void)
PAUSE_Main[mpause_switchmap].status = IT_DISABLED; PAUSE_Main[mpause_switchmap].status = IT_DISABLED;
PAUSE_Main[mpause_restartmap].status = IT_DISABLED; PAUSE_Main[mpause_restartmap].status = IT_DISABLED;
PAUSE_Main[mpause_tryagain].status = IT_DISABLED; PAUSE_Main[mpause_tryagain].status = IT_DISABLED;
PAUSE_Main[mpause_callvote].status = IT_DISABLED;
PAUSE_Main[mpause_admin].status = IT_DISABLED;
#ifdef HAVE_DISCORDRPC #ifdef HAVE_DISCORDRPC
PAUSE_Main[mpause_discordrequests].status = IT_DISABLED; PAUSE_Main[mpause_discordrequests].status = IT_DISABLED;
#endif #endif
@ -146,7 +155,7 @@ void M_OpenPauseMenu(void)
if (server || IsPlayerAdmin(consoleplayer)) if (server || IsPlayerAdmin(consoleplayer))
{ {
PAUSE_Main[mpause_changegametype].status = IT_STRING | IT_KEYHANDLER; PAUSE_Main[mpause_changegametype].status = IT_STRING | IT_ARROWS;
menugametype = gametype; menugametype = gametype;
PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_CALL; PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_CALL;
@ -156,12 +165,21 @@ void M_OpenPauseMenu(void)
{ {
PAUSE_Main[mpause_addons].status = IT_STRING | IT_CALL; PAUSE_Main[mpause_addons].status = IT_STRING | IT_CALL;
} }
if (netgame)
{
PAUSE_Main[mpause_admin].status = IT_STRING | IT_CALL;
}
} }
} }
else if (!netgame && !demo.playback) else if (!netgame && !demo.playback)
{ {
boolean retryallowed = (modeattacking != ATTACKING_NONE); boolean retryallowed = (modeattacking != ATTACKING_NONE);
if (G_GametypeUsesLives()) if (
retryallowed == false
&& gamestate == GS_LEVEL
&& G_GametypeUsesLives()
)
{ {
for (i = 0; i <= splitscreen; i++) for (i = 0; i <= splitscreen; i++)
{ {
@ -178,6 +196,18 @@ void M_OpenPauseMenu(void)
} }
} }
if (netgame) // && (PAUSE_Main[mpause_admin].status == IT_DISABLED))
{
menucallvote = K_GetNextAllowedMidVote(menucallvote, true);
if (menucallvote != MVT__MAX)
{
menucallvote = K_GetNextAllowedMidVote(menucallvote, false);
PAUSE_Main[mpause_callvote].status = IT_STRING | IT_ARROWS;
}
}
if (G_GametypeHasSpectators()) if (G_GametypeHasSpectators())
{ {
if (splitscreen) if (splitscreen)
@ -267,12 +297,9 @@ boolean M_PauseInputs(INT32 ch)
// Change gametype // Change gametype
void M_HandlePauseMenuGametype(INT32 choice) void M_HandlePauseMenuGametype(INT32 choice)
{ {
const UINT8 pid = 0;
const UINT32 forbidden = GTR_FORBIDMP; const UINT32 forbidden = GTR_FORBIDMP;
(void)choice; if (choice == 2)
if (M_MenuConfirmPressed(pid))
{ {
if (menugametype != gametype) if (menugametype != gametype)
{ {
@ -281,27 +308,60 @@ void M_HandlePauseMenuGametype(INT32 choice)
return; return;
} }
M_SetMenuDelay(pid);
S_StartSound(NULL, sfx_s3k7b); S_StartSound(NULL, sfx_s3k7b);
return;
} }
else if (M_MenuExtraPressed(pid))
if (choice == -1)
{ {
menugametype = gametype; menugametype = gametype;
M_SetMenuDelay(pid);
S_StartSound(NULL, sfx_s3k7b); S_StartSound(NULL, sfx_s3k7b);
return;
} }
else if (menucmd[pid].dpad_lr > 0)
{ if (choice == 0)
M_NextMenuGametype(forbidden);
S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
}
else if (menucmd[pid].dpad_lr < 0)
{ {
M_PrevMenuGametype(forbidden); M_PrevMenuGametype(forbidden);
S_StartSound(NULL, sfx_s3k5b); S_StartSound(NULL, sfx_s3k5b);
M_SetMenuDelay(pid);
} }
else
{
M_NextMenuGametype(forbidden);
S_StartSound(NULL, sfx_s3k5b);
}
}
// Call vote
UINT32 menucallvote = MVT__MAX;
void M_HandlePauseMenuCallVote(INT32 choice)
{
if (choice == 2)
{
if (K_MinimalCheckNewMidVote(menucallvote) == false)
{
// Invalid.
S_StartSound(NULL, sfx_s3k7b);
}
else if (K_MidVoteTypeUsesVictim(menucallvote) == true)
{
S_StartSound(NULL, sfx_s3k5b);
M_KickHandler(-1);
}
else
{
// Bog standard and victimless, let's send it on its way!
M_ClearMenus(true);
K_SendCallMidVote(menucallvote, 0);
return;
}
return;
}
menucallvote = K_GetNextAllowedMidVote(menucallvote, (choice == 0));
S_StartSound(NULL, sfx_s3k5b);
} }
// Restart map // Restart map

View file

@ -0,0 +1,135 @@
/// \file menus/transient/pause-kick.c
/// \brief Player Kick menu
#include "../../k_menu.h"
#include "../../s_sound.h"
#include "../../p_local.h"
#include "../../k_zvote.h"
struct playerkickmenu_s playerkickmenu;
static void M_PlayerKickHandler(INT32 choice)
{
const UINT8 pid = 0;
UINT8 kicktype = UINT8_MAX;
(void)choice;
if (menucmd[pid].dpad_lr != 0) // symmetrical in this case
{
S_StartSound(NULL, sfx_s3k5b);
playerkickmenu.player = ((playerkickmenu.player + 8) % MAXPLAYERS);
M_SetMenuDelay(pid);
}
else if (menucmd[pid].dpad_ud > 0)
{
S_StartSound(NULL, sfx_s3k5b);
playerkickmenu.player = ((playerkickmenu.player + 1) & 7) + (playerkickmenu.player & 8);
M_SetMenuDelay(pid);
}
else if (menucmd[pid].dpad_ud < 0)
{
S_StartSound(NULL, sfx_s3k5b);
playerkickmenu.player = ((playerkickmenu.player + 7) & 7) + (playerkickmenu.player & 8);
M_SetMenuDelay(pid);
}
else if (M_MenuBackPressed(pid))
{
M_GoBack(0);
M_SetMenuDelay(pid);
}
else if (M_MenuExtraPressed(pid) && playerkickmenu.adminpowered)
{
kicktype = KICK_MSG_BANNED;
}
else if (M_MenuConfirmPressed(pid))
{
kicktype = KICK_MSG_KICKED;
}
if (kicktype != UINT8_MAX)
{
M_SetMenuDelay(pid);
if (
playeringame[playerkickmenu.player]
&& P_IsMachineLocalPlayer(&players[playerkickmenu.player]) == false
&& playerkickmenu.player != serverplayer
)
{
if (playerkickmenu.adminpowered)
{
if (consoleplayer == serverplayer || IsPlayerAdmin(consoleplayer))
{
playerkickmenu.poke = (kicktype == KICK_MSG_BANNED) ? 16 : 12;
SendKick(playerkickmenu.player, kicktype);
return;
}
}
else if (
K_MinimalCheckNewMidVote(menucallvote) == true
#ifndef DEVELOP
&& IsPlayerAdmin(playerkickmenu.player) == false
#endif
)
{
M_ClearMenus(true);
K_SendCallMidVote(menucallvote, playerkickmenu.player);
return;
}
}
playerkickmenu.poke = 8;
S_StartSound(NULL, sfx_s3k7b);
}
}
static menuitem_t PAUSE_KickHandler[] =
{
{IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, {.routine = M_PlayerKickHandler}, 0, 0},
};
static void M_KickHandlerTick(void)
{
playerkickmenu.ticker++;
if (playerkickmenu.poke)
playerkickmenu.poke--;
}
menu_t PAUSE_KickHandlerDef = {
sizeof(PAUSE_KickHandler) / sizeof(menuitem_t),
&PAUSE_MainDef,
0,
PAUSE_KickHandler,
0, 0,
0, 0,
0,
NULL,
0, 0,
M_DrawKickHandler,
M_KickHandlerTick,
NULL,
NULL,
NULL,
};
void M_KickHandler(INT32 choice)
{
playerkickmenu.adminpowered = (choice >= 0);
PAUSE_KickHandlerDef.prevMenu = currentMenu;
M_SetupNextMenu(&PAUSE_KickHandlerDef, true);
}

View file

@ -21,4 +21,5 @@ target_sources(SRB2SDL2 PRIVATE
block.c block.c
gachabom-rebound.cpp gachabom-rebound.cpp
servant-hand.c servant-hand.c
super-flicky.cpp
) )

View file

@ -102,6 +102,29 @@ void Obj_RandomItemVisuals(mobj_t *mobj)
ItemBoxBob(mobj); ItemBoxBob(mobj);
ItemBoxScaling(mobj); ItemBoxScaling(mobj);
item_vfxtimer(mobj)++; item_vfxtimer(mobj)++;
if (mobj->type != MT_RANDOMITEM)
return;
// Respawn flow, documented by a dumb asshole:
// P_TouchSpecialThing -> P_ItemPop sets fuse, NOCLIPTHING and DONTDRAW.
// P_FuseThink does visual flicker, and when fuse is 0, unsets NOCLIPTHING/DONTDRAW/etc...
// ...unless it's a map-start box from Battle, in which case it does nothing and waits for
// P_RespawnBattleBoxes to trigger the effect instead, since Battle boxes don't respawn until
// the player's cleared out a good portion of the map.
//
// Then extraval1 starts ticking up and triggers the transformation from Ringbox to Random Item.
if (mobj->fuse == 0 && !(mobj->flags & MF_NOCLIPTHING) && !cv_thunderdome.value)
{
mobj->extravalue1++;
if (mobj->extravalue1 == RINGBOX_TIME)
{
// Sync the position in RINGBOX and RANDOMITEM animations.
statenum_t animDelta = mobj->state - states - S_RINGBOX1;
P_SetMobjState(mobj, S_RANDOMITEM1 + (animDelta%12));
}
}
} }
boolean Obj_RandomItemSpawnIn(mobj_t *mobj) boolean Obj_RandomItemSpawnIn(mobj_t *mobj)
@ -116,6 +139,7 @@ boolean Obj_RandomItemSpawnIn(mobj_t *mobj)
} }
else else
{ {
// Spawn a battle monitor in your place and Fucking Die
mobj_t *paperspawner = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_PAPERITEMSPOT); mobj_t *paperspawner = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_PAPERITEMSPOT);
paperspawner->spawnpoint = mobj->spawnpoint; paperspawner->spawnpoint = mobj->spawnpoint;
mobj->spawnpoint->mobj = paperspawner; mobj->spawnpoint->mobj = paperspawner;
@ -148,6 +172,14 @@ void Obj_RandomItemSpawn(mobj_t *mobj)
{ {
item_vfxtimer(mobj) = P_RandomRange(PR_DECORATION, 0, SCALE_TIME - 1); item_vfxtimer(mobj) = P_RandomRange(PR_DECORATION, 0, SCALE_TIME - 1);
// Respawns are handled by P_RespawnBattleBoxes,
// but we do need to start MT_RANDOMITEMs in the right state...
if (mobj->type == MT_RANDOMITEM && (gametyperules & GTR_BUMPERS))
{
mobj->extravalue1 = RINGBOX_TIME;
P_SetMobjState(mobj, S_RANDOMITEM1);
}
mobj->destscale = Obj_RandomItemScale(mobj->destscale); mobj->destscale = Obj_RandomItemScale(mobj->destscale);
P_SetScale(mobj, mobj->destscale); P_SetScale(mobj, mobj->destscale);
} }

View file

@ -0,0 +1,769 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2023 by James R.
// Copyright (C) 2023 by Kart Krew
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \brief Super Flicky power-up, hunts other players
#include "../d_player.h"
#include "../doomdef.h"
#include "../g_game.h"
#include "../k_battle.h"
#include "../k_kart.h"
#include "../k_objects.h"
#include "../k_respawn.h"
#include "../m_fixed.h"
#include "../m_random.h"
#include "../p_local.h"
#include "../r_main.h"
#include "../s_sound.h"
#include "../tables.h"
#define flicky_controller(o) ((o)->target)
#define flicky_chasing(o) ((o)->tracer)
#define flicky_next(o) ((o)->hnext)
#define flicky_next_target(o) ((o)->hprev)
#define flicky_phase(o) ((o)->threshold)
#define flicky_delay(o) ((o)->movecount)
#define flicky_mode(o) ((o)->extravalue1)
#define flicky_fly(o) ((o)->extravalue2)
#define controller_source(o) ((o)->target)
#define controller_chasing(o) ((o)->tracer)
#define controller_flicky(o) ((o)->hnext)
#define controller_mode(o) ((o)->movecount)
#define controller_zofs(o) ((o)->sprzoff)
#define controller_expiry(o) ((o)->fuse)
namespace
{
constexpr tic_t kOrbitSpeed = 2*TICRATE;
constexpr int kOrbitSpacing = ANGLE_90;
// Multiples of player radius
constexpr fixed_t kOrbitRadiusInitial = 32*FRACUNIT;
constexpr fixed_t kOrbitRadius = 2*FRACUNIT;
constexpr int kDescendHeight = 256;
constexpr int kDescendSmoothing = 16;
constexpr int kSearchRadius = 1920;
constexpr int kFlightRadius = 1280;
constexpr int kPeckingRadius = 256;
constexpr int kFlightSpeed = 2;
constexpr int kPeckingSpeed = 8;
constexpr fixed_t kRebound = 8*FRACUNIT/9;
constexpr tic_t kDelay = 8;
constexpr tic_t kStunTime = 5*TICRATE;
constexpr tic_t kBlockTime = 1*TICRATE;
constexpr int kRiseTime = 1*TICRATE;
constexpr int kRiseSpeed = 4;
// TODO: skincolor must be updated to 2.2 palette
constexpr skincolornum_t kSuperStart = SKINCOLOR_SUPERGOLD1;
constexpr skincolornum_t kSuperEnd = SKINCOLOR_SUPERGOLD5;
// copied from objects/hyudoro.c
void
sine_bob
( mobj_t * hyu,
angle_t a,
fixed_t sineofs)
{
const fixed_t kBobHeight = 4 * mapobjectscale;
// slightly modified from objects/hyudoro.c
hyu->sprzoff = FixedMul(kBobHeight,
sineofs + FINESINE(a >> ANGLETOFINESHIFT));
}
void
bob_in_place
( mobj_t * hyu,
INT32 phase,
INT32 bob_speed)
{
sine_bob(hyu,
((leveltime + phase) & (bob_speed - 1)) *
(ANGLE_MAX / bob_speed), -(3*FRACUNIT/4));
}
struct Flicky;
struct Controller : mobj_t
{
enum class Mode : int
{
kDescend,
kOrbit,
kEnRoute,
kAttached,
kReturning,
};
mobj_t* source() const { return controller_source(this); }
void source(mobj_t* n) { P_SetTarget(&controller_source(this), n); }
mobj_t* chasing() const { return controller_chasing(this); }
void chasing(mobj_t* n) { P_SetTarget(&controller_chasing(this), n); }
Flicky* flicky() const;
void flicky(Flicky* n);
Mode mode() const { return static_cast<Mode>(controller_mode(this)); }
void mode(Mode n) { controller_mode(this) = static_cast<int>(n); }
fixed_t zofs() const { return controller_zofs(this); }
void zofs(fixed_t n) { controller_zofs(this) = n; }
tic_t expiry() const { return controller_expiry(this); }
void expiry(tic_t n) { controller_expiry(this) = n; }
static Controller* spawn(player_t* player, tic_t time)
{
Controller* x = static_cast<Controller*>(P_SpawnMobjFromMobjUnscaled(
player->mo,
0,
0,
kDescendHeight * mapobjectscale,
MT_SUPER_FLICKY_CONTROLLER
));
x->source(player->mo);
x->mode(Mode::kDescend);
x->zofs(0);
x->expiry(leveltime + time);
P_SetTarget(&player->powerup.flickyController, x);
S_StartSound(x, sfx_s3k46);
return x;
}
bool valid() { return !P_MobjWasRemoved(source()); }
tic_t time_remaining() const { return expiry() - leveltime; }
tic_t powerup_remaining() const { return ending() ? 0u : time_remaining() - kRiseTime; }
bool ending() const { return time_remaining() <= kRiseTime; }
void descend()
{
fixed_t head = P_GetMobjHead(source());
fixed_t tz = head;
if (mode() == Mode::kDescend)
{
tz = z - ((z - head) / kDescendSmoothing);
if ((tz - head) < mapobjectscale)
{
mode(Mode::kOrbit);
tz = head;
}
}
z = tz + zofs();
if (ending())
{
zofs(zofs() + (kRiseSpeed * mapobjectscale * P_MobjFlip(this)));
}
}
void expand()
{
fixed_t n = FixedMul(kOrbitRadiusInitial, ((z - P_GetMobjHead(source())) / kDescendHeight));
radius = FixedMul(FixedMul(kOrbitRadius, source()->radius), FRACUNIT + n);
}
void end()
{
// +1 in case flicky already thunk this tic
expiry(leveltime + kRiseTime + 1);
}
void search();
};
struct Flicky : mobj_t
{
enum class Mode : int
{
kReserved,
kHunting,
kStunned,
kWeak,
};
enum class Fly : int
{
kNormal,
kZoom,
kSlow,
};
Controller* controller() const { return static_cast<Controller*>(flicky_controller(this)); }
void controller(Controller* n) { P_SetTarget(&flicky_controller(this), n); }
mobj_t* chasing() const { return flicky_chasing(this); }
void chasing(mobj_t* n) { P_SetTarget(&flicky_chasing(this), n); }
Flicky* next() const { return static_cast<Flicky*>(flicky_next(this)); }
void next(Flicky* n) { P_SetTarget(&flicky_next(this), n); }
mobj_t* next_target() const { return flicky_next_target(this); }
void next_target(mobj_t* n) { P_SetTarget(&flicky_next_target(this), n); }
int phase() const { return flicky_phase(this); }
void phase(int n) { flicky_phase(this) = n; }
int delay() const { return flicky_delay(this); }
void delay(int n) { flicky_delay(this) = n; }
Mode mode() const { return static_cast<Mode>(flicky_mode(this)); }
void mode(Mode n) { flicky_mode(this) = static_cast<int>(n); }
Fly fly() const { return static_cast<Fly>(flicky_fly(this)); }
void fly(Fly n) { flicky_fly(this) = static_cast<int>(n); }
mobj_t* source() const { return controller()->source(); }
static void spawn(Controller* controller, int phase)
{
Flicky* x = static_cast<Flicky*>(P_SpawnMobjFromMobj(controller, 0, 0, 0, MT_SUPER_FLICKY));
x->controller(controller);
x->phase(phase);
x->delay(0);
x->mode(Mode::kReserved);
x->light_up(true);
x->next(controller->flicky());
controller->flicky(x);
}
static skincolornum_t super_color()
{
return static_cast<skincolornum_t>(kSuperStart + ((leveltime / 4) % ((kSuperEnd - kSuperStart) + 1)));
}
bool valid() const { return !P_MobjWasRemoved(controller()) && controller()->valid(); }
bool stunned() const { return mode() == Mode::kStunned || mode() == Mode::kWeak; }
void light_up(bool n)
{
if (n)
{
renderflags |= RF_FULLBRIGHT;
color = super_color();
}
else
{
renderflags &= ~(RF_FULLBRIGHT);
color = source()->player ? source()->player->skincolor : source()->color;
}
}
void animate()
{
P_InstaScale(this, source()->scale * (chasing() ? 2 : 1));
if (color >= kSuperStart && color <= kSuperEnd)
{
color = super_color();
}
bob_in_place(this, phase() * 8, 32);
}
void refocus()
{
if (controller()->ending())
{
if (controller()->time_remaining() == kRiseTime)
{
light_up(false);
rise();
}
return;
}
if (delay() > 0)
{
delay(delay() - 1);
}
else
{
if (chasing() != next_target())
{
chasing(next_target());
mode(Mode::kHunting);
S_StartSound(this, sfx_fhurt2);
}
if (stunned())
{
light_up(true);
flags = info->flags;
mode(Mode::kHunting);
S_StartSound(this, sfx_s3k9f);
}
}
}
angle_t orbit_angle() const { return controller()->angle + (phase() * kOrbitSpacing); }
vector3_t orbit_position() const
{
return {
source()->x + FixedMul(FCOS(orbit_angle()), controller()->radius),
source()->y + FixedMul(FSIN(orbit_angle()), controller()->radius),
controller()->z
};
}
void orbit()
{
vector3_t pos = orbit_position();
P_MoveOrigin(this, pos.x, pos.y, pos.z);
momx = 0;
momy = 0;
momz = 0;
angle = orbit_angle() + ANGLE_90; // face direction of orbit
}
// copied from objects/spb.c
void spawn_speed_lines(angle_t direction)
{
if (mode() != Mode::kHunting)
{
return;
}
mobj_t *fast = P_SpawnMobjFromMobjUnscaled(
this,
P_RandomRange(PR_DECORATION, -24, 24) * mapobjectscale,
P_RandomRange(PR_DECORATION, -24, 24) * mapobjectscale,
(height / 2) + (P_RandomRange(PR_DECORATION, -24, 24) * mapobjectscale),
MT_FASTLINE
);
P_SetTarget(&fast->target, this);
fast->angle = direction;
fast->color = source()->color;
fast->colorized = true;
fast->renderflags |= RF_ADD;
K_MatchGenericExtraFlags(fast, this);
}
void chase()
{
if (controller()->ending())
{
return;
}
vector3_t pos = chasing() ? vector3_t{chasing()->x, chasing()->y, P_GetMobjFeet(chasing())} : orbit_position();
angle_t th = R_PointToAngle2(x, y, pos.x, pos.y);
momz = (pos.z - z) / kDescendSmoothing;
angle = K_MomentumAngleReal(this);
angle_t d = AngleDelta(th, angle);
fixed_t dist = FixedHypot(x - pos.x, y - pos.y);
const Fly oldFly = fly();
if (d < ANGLE_11hh && dist < kPeckingRadius * mapobjectscale)
{
// Drastically speed up when about to intersect
P_Thrust(this, th, kPeckingSpeed * mapobjectscale);
fly(Fly::kZoom);
}
else
{
P_Thrust(this, th, kFlightSpeed * mapobjectscale);
fly(Fly::kNormal);
}
if (d > ANGLE_45 && dist > kFlightRadius * mapobjectscale)
{
// Cut momentum when too far outside of intended trajectory
momx = FixedMul(momx, kRebound);
momy = FixedMul(momy, kRebound);
spawn_speed_lines(th);
fly(Fly::kSlow);
}
else
{
spawn_speed_lines(angle);
}
// Returning to owner
if (!chasing())
{
if (AngleDelta(th, R_PointToAngle2(x + momx, y + momy, pos.x, pos.y)) > ANG1)
{
mode(Mode::kReserved);
}
else
{
P_InstaThrust(this, th, FixedHypot(momx, momy));
}
}
if (fly() != oldFly)
{
switch (fly())
{
case Fly::kNormal:
break;
case Fly::kZoom:
S_StartSound(this, sfx_fbird);
break;
case Fly::kSlow:
S_StartSound(this, sfx_fbost1);
break;
}
}
}
void rise()
{
P_SetObjectMomZ(this, kRiseSpeed * FRACUNIT, false);
}
void damage(mobj_t* mobj)
{
if (!mobj->player)
{
return;
}
if (mobj != chasing())
{
return;
}
if (mode() != Mode::kHunting)
{
return;
}
if (P_DamageMobj(mobj, this, source(), 1, DMG_NORMAL))
{
P_InstaThrust(mobj, K_MomentumAngleReal(this), FixedHypot(momx, momy));
K_StumblePlayer(mobj->player);
mobj->player->spinouttimer = 1; // need invulnerability for one tic
P_SetTarget(&mobj->player->flickyAttacker, this);
controller()->mode(Controller::Mode::kAttached);
}
S_StartSound(this, sfx_supflk);
}
void reflect()
{
momx = -(momx);
momy = -(momy);
}
void nerf()
{
light_up(false);
flags &= ~(MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT);
}
void whip()
{
reflect();
P_SetObjectMomZ(this, 8*FRACUNIT, false);
nerf();
mode(Mode::kStunned);
delay(kStunTime);
S_StartSound(this, sfx_fhurt1);
}
void block()
{
reflect();
mode(Mode::kStunned);
delay(kBlockTime);
}
void land()
{
flags |= MF_NOGRAVITY;
mode(Mode::kWeak);
}
};
Flicky* Controller::flicky() const
{
return static_cast<Flicky*>(controller_flicky(this));
}
void Controller::flicky(Flicky* n)
{
P_SetTarget(&controller_flicky(this), n);
}
void Controller::search()
{
if (ending())
{
return;
}
fixed_t nearestDistance = INT32_MAX;
mobj_t* nearestMobj = nullptr;
mobj_t* origin = chasing() ? chasing() : source();
for (int i = 0; i < MAXPLAYERS; ++i)
{
player_t* player = &players[i];
mobj_t* mobj = player->mo;
if (!playeringame[i] || P_MobjWasRemoved(mobj))
{
continue;
}
// Do not retarget existing target or owner.
if (mobj == chasing() || mobj == source())
{
continue;
}
// Target is already being hunted.
if (player->flickyAttacker)
{
continue;
}
if (player->respawn.state != RESPAWNST_NONE)
{
continue;
}
fixed_t dist = FixedHypot(origin->x - mobj->x, origin->y - mobj->y);
if (dist < kSearchRadius * mapobjectscale && dist < nearestDistance)
{
nearestDistance = dist;
nearestMobj = mobj;
}
}
if (nearestMobj)
{
if (chasing() && flicky())
{
// Detach flicky from swarm. This one keeps its previous target.
flicky(flicky()->next());
}
chasing(nearestMobj);
mode(Mode::kEnRoute);
// Update entire swarm
for (Flicky* x = flicky(); x; x = x->next())
{
x->next_target(chasing());
x->delay(x->phase() * kDelay);
}
}
}
}; // namespace
void Obj_SpawnSuperFlickySwarm(player_t* owner, tic_t time)
{
Controller* controller = Controller::spawn(owner, time);
Flicky::spawn(controller, 0);
Flicky::spawn(controller, 1);
Flicky::spawn(controller, 2);
Flicky::spawn(controller, 3);
}
void Obj_SuperFlickyThink(mobj_t* mobj)
{
Flicky* x = static_cast<Flicky*>(mobj);
if (!x->valid())
{
P_RemoveMobj(x);
return;
}
x->animate();
x->refocus();
switch (x->mode())
{
case Flicky::Mode::kReserved:
x->orbit();
break;
case Flicky::Mode::kHunting:
x->chase();
break;
case Flicky::Mode::kStunned:
break;
case Flicky::Mode::kWeak:
x->chase();
break;
}
}
void Obj_SuperFlickyControllerThink(mobj_t* mobj)
{
Controller* x = static_cast<Controller*>(mobj);
if (!x->valid())
{
P_RemoveMobj(x);
return;
}
if (x->time_remaining() <= 1)
{
P_RemoveMobj(x);
return;
}
x->angle += ANGLE_MAX / kOrbitSpeed;
switch (x->mode())
{
case Controller::Mode::kDescend:
x->descend();
x->expand();
break;
case Controller::Mode::kOrbit:
x->descend();
x->expand();
x->search();
break;
case Controller::Mode::kEnRoute:
break;
case Controller::Mode::kAttached:
x->search();
break;
case Controller::Mode::kReturning:
x->descend();
break;
}
}
void Obj_WhipSuperFlicky(mobj_t* t1)
{
Flicky* x = static_cast<Flicky*>(t1);
if (x->valid())
{
x->whip();
}
}
void Obj_BlockSuperFlicky(mobj_t* t1)
{
Flicky* x = static_cast<Flicky*>(t1);
if (x->valid())
{
x->block();
}
}
void Obj_SuperFlickyPlayerCollide(mobj_t* t1, mobj_t* t2)
{
Flicky* x = static_cast<Flicky*>(t1);
if (x->valid())
{
x->damage(t2);
}
}
void Obj_SuperFlickyLanding(mobj_t* mobj)
{
Flicky* x = static_cast<Flicky*>(mobj);
if (x->valid())
{
x->land();
}
}
void Obj_EndSuperFlickySwarm(mobj_t* mobj)
{
Controller* x = static_cast<Controller*>(mobj);
if (x->valid())
{
x->end();
}
}
void Obj_ExtendSuperFlickySwarm(mobj_t* mobj, tic_t time)
{
Controller* x = static_cast<Controller*>(mobj);
x->expiry(x->expiry() + time);
}
tic_t Obj_SuperFlickySwarmTime(const mobj_t* mobj)
{
const Controller* x = static_cast<const Controller*>(mobj);
return x ? x->powerup_remaining() : 0u;
}
boolean Obj_IsSuperFlickyWhippable(const mobj_t* mobj)
{
const Flicky* x = static_cast<const Flicky*>(mobj);
return !x->stunned();
}

View file

@ -312,7 +312,6 @@ void A_ChangeHeight(mobj_t *actor);
// //
// SRB2Kart // SRB2Kart
// //
void A_ItemPop(mobj_t *actor);
void A_JawzExplode(mobj_t *actor); void A_JawzExplode(mobj_t *actor);
void A_SSMineSearch(mobj_t *actor); void A_SSMineSearch(mobj_t *actor);
void A_SSMineExplode(mobj_t *actor); void A_SSMineExplode(mobj_t *actor);
@ -12984,54 +12983,6 @@ void A_ChangeHeight(mobj_t *actor)
// SRB2Kart // SRB2Kart
// //
void A_ItemPop(mobj_t *actor)
{
INT32 locvar1 = var1;
if (LUA_CallAction(A_ITEMPOP, actor))
return;
if (!(actor->target && actor->target->player))
{
if (cht_debug && !(actor->target && actor->target->player))
CONS_Printf("ERROR: Powerup has no target!\n");
return;
}
// de-solidify
P_UnsetThingPosition(actor);
actor->flags &= ~MF_SOLID;
actor->flags |= MF_NOCLIP;
P_SetThingPosition(actor);
// RF_DONTDRAW will flicker as the object's fuse gets
// closer to running out (see P_FuseThink)
actor->renderflags |= RF_DONTDRAW|RF_TRANS50;
actor->color = SKINCOLOR_GREY;
actor->colorized = true;
Obj_SpawnItemDebrisEffects(actor, actor->target);
if (locvar1 == 1)
{
P_GivePlayerSpheres(actor->target->player, actor->extravalue1);
}
else if (locvar1 == 0)
{
K_StartItemRoulette(actor->target->player);
}
// Here at mapload in battle?
if (!(gametyperules & GTR_CIRCUIT) && (actor->flags2 & MF2_BOSSNOTRAP))
{
numgotboxes++;
// do not flicker back in just yet, handled by
// P_RespawnBattleBoxes eventually
P_SetMobjState(actor, S_INVISIBLE);
}
}
void A_JawzExplode(mobj_t *actor) void A_JawzExplode(mobj_t *actor)
{ {
INT32 shrapnel = 2; INT32 shrapnel = 2;

View file

@ -41,6 +41,7 @@
#include "k_roulette.h" #include "k_roulette.h"
#include "k_boss.h" #include "k_boss.h"
#include "acs/interface.h" #include "acs/interface.h"
#include "k_powerup.h"
// CTF player names // CTF player names
#define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : ""
@ -140,6 +141,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon)
// Item slot already taken up // Item slot already taken up
if (player->itemRoulette.active == true if (player->itemRoulette.active == true
|| player->ringboxdelay > 0
|| (weapon != 3 && player->itemamount) || (weapon != 3 && player->itemamount)
|| (player->pflags & PF_ITEMOUT)) || (player->pflags & PF_ITEMOUT))
return false; return false;
@ -186,6 +188,61 @@ boolean P_EmblemWasCollected(INT32 emblemID)
return gamedata->collected[emblemID]; return gamedata->collected[emblemID];
} }
static void P_ItemPop(mobj_t *actor)
{
/*
INT32 locvar1 = var1;
if (LUA_CallAction(A_ITEMPOP, actor))
return;
if (!(actor->target && actor->target->player))
{
if (cht_debug && !(actor->target && actor->target->player))
CONS_Printf("ERROR: Powerup has no target!\n");
return;
}
*/
P_SetMobjState(actor, S_RINGBOX1);
actor->extravalue1 = 0;
// de-solidify
actor->flags |= MF_NOCLIPTHING;
// RF_DONTDRAW will flicker as the object's fuse gets
// closer to running out (see P_FuseThink)
actor->renderflags |= RF_DONTDRAW|RF_TRANS50;
actor->color = SKINCOLOR_GREY;
actor->colorized = true;
Obj_SpawnItemDebrisEffects(actor, actor->target);
/*
if (locvar1 == 1)
{
P_GivePlayerSpheres(actor->target->player, actor->extravalue1);
}
else if (locvar1 == 0)
{
if (actor->extravalue1 >= TICRATE)
K_StartItemRoulette(actor->target->player, false);
else
K_StartItemRoulette(actor->target->player, true);
}
*/
// Here at mapload in battle?
if (!(gametyperules & GTR_CIRCUIT) && (actor->flags2 & MF2_BOSSNOTRAP))
{
numgotboxes++;
// do not flicker back in just yet, handled by
// P_RespawnBattleBoxes eventually
P_SetMobjState(actor, S_INVISIBLE);
}
}
/** Takes action based on a ::MF_SPECIAL thing touched by a player. /** Takes action based on a ::MF_SPECIAL thing touched by a player.
* Actually, this just checks a few things (heights, toucher->player, no * Actually, this just checks a few things (heights, toucher->player, no
* objectplace, no dead or disappearing things) * objectplace, no dead or disappearing things)
@ -276,14 +333,21 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
P_InstaThrust(player->mo, player->mo->angle, 20<<FRACBITS); P_InstaThrust(player->mo, player->mo->angle, 20<<FRACBITS);
return; return;
case MT_FLOATINGITEM: // SRB2Kart case MT_FLOATINGITEM: // SRB2Kart
if (!P_CanPickupItem(player, 3) || (player->itemamount && player->itemtype != special->threshold)) if (special->threshold >= FIRSTPOWERUP)
return; {
K_GivePowerUp(player, special->threshold, special->movecount);
player->itemtype = special->threshold; }
if ((UINT16)(player->itemamount) + special->movecount > 255)
player->itemamount = 255;
else else
player->itemamount += special->movecount; {
if (!P_CanPickupItem(player, 3) || (player->itemamount && player->itemtype != special->threshold))
return;
player->itemtype = special->threshold;
if ((UINT16)(player->itemamount) + special->movecount > 255)
player->itemamount = 255;
else
player->itemamount += special->movecount;
}
S_StartSound(special, special->info->deathsound); S_StartSound(special, special->info->deathsound);
@ -300,7 +364,13 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
special->momx = special->momy = special->momz = 0; special->momx = special->momy = special->momz = 0;
P_SetTarget(&special->target, toucher); P_SetTarget(&special->target, toucher);
P_KillMobj(special, toucher, toucher, DMG_NORMAL); // P_KillMobj(special, toucher, toucher, DMG_NORMAL);
if (special->extravalue1 >= RINGBOX_TIME)
K_StartItemRoulette(player, false);
else
K_StartItemRoulette(player, true);
P_ItemPop(special);
special->fuse = TICRATE;
return; return;
case MT_SPHEREBOX: case MT_SPHEREBOX:
if (!P_CanPickupItem(player, 0)) if (!P_CanPickupItem(player, 0))
@ -308,7 +378,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
special->momx = special->momy = special->momz = 0; special->momx = special->momy = special->momz = 0;
P_SetTarget(&special->target, toucher); P_SetTarget(&special->target, toucher);
P_KillMobj(special, toucher, toucher, DMG_NORMAL); // P_KillMobj(special, toucher, toucher, DMG_NORMAL);
P_ItemPop(special);
P_GivePlayerSpheres(player, special->extravalue1);
return; return;
case MT_ITEMCAPSULE: case MT_ITEMCAPSULE:
if (special->scale < special->extravalue1) // don't break it while it's respawning if (special->scale < special->extravalue1) // don't break it while it's respawning
@ -434,7 +506,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (special->fuse || !P_CanPickupItem(player, 1)) if (special->fuse || !P_CanPickupItem(player, 1))
return; return;
K_StartItemRoulette(player); K_StartItemRoulette(player, false);
// Karma fireworks // Karma fireworks
/*for (i = 0; i < 5; i++) /*for (i = 0; i < 5; i++)
@ -584,6 +656,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
Obj_PlayerUsedRingShooter(special, player); Obj_PlayerUsedRingShooter(special, player);
return; return;
case MT_SUPER_FLICKY:
Obj_SuperFlickyPlayerCollide(special, toucher);
return;
default: // SOC or script pickup default: // SOC or script pickup
P_SetTarget(&special->target, toucher); P_SetTarget(&special->target, toucher);
break; break;
@ -2258,8 +2334,16 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (clash) if (clash)
{ {
player->spheres = max(player->spheres - 10, 0); player->spheres = max(player->spheres - 10, 0);
if (inflictor) if (inflictor)
{
K_DoPowerClash(target, inflictor); K_DoPowerClash(target, inflictor);
if (inflictor->type == MT_SUPER_FLICKY)
{
Obj_BlockSuperFlicky(inflictor);
}
}
else if (source) else if (source)
K_DoPowerClash(target, source); K_DoPowerClash(target, source);
} }

View file

@ -2464,6 +2464,12 @@ boolean P_ZMovement(mobj_t *mo)
return false; return false;
} }
} }
else if (mo->type == MT_SUPER_FLICKY)
{
mom.z = -mom.z;
Obj_SuperFlickyLanding(mo);
}
else if (mo->type == MT_DRIFTCLIP) else if (mo->type == MT_DRIFTCLIP)
{ {
mom.z = -mom.z/2; mom.z = -mom.z/2;
@ -6684,6 +6690,14 @@ static void P_MobjSceneryThink(mobj_t *mobj)
case MT_INSTAWHIP_RECHARGE: case MT_INSTAWHIP_RECHARGE:
Obj_InstaWhipRechargeThink(mobj); Obj_InstaWhipRechargeThink(mobj);
if (P_MobjWasRemoved(mobj))
{
return;
}
break;
case MT_SUPER_FLICKY_CONTROLLER:
Obj_SuperFlickyControllerThink(mobj);
if (P_MobjWasRemoved(mobj)) if (P_MobjWasRemoved(mobj))
{ {
return; return;
@ -7197,29 +7211,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
} }
} }
switch (mobj->threshold) if (mobj->threshold == KITEM_SPB || mobj->threshold == KITEM_SHRINK)
{ {
case KITEM_ORBINAUT: K_SetItemCooldown(mobj->threshold, 20*TICRATE);
mobj->sprite = SPR_ITMO;
mobj->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetOrbinautItemFrame(mobj->movecount);
break;
case KITEM_INVINCIBILITY:
mobj->sprite = SPR_ITMI;
mobj->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetInvincibilityItemFrame();
break;
case KITEM_SAD:
mobj->sprite = SPR_ITEM;
mobj->frame = FF_FULLBRIGHT|FF_PAPERSPRITE;
break;
case KITEM_SPB:
case KITEM_SHRINK:
K_SetItemCooldown(mobj->threshold, 20*TICRATE);
/* FALLTHRU */
default:
mobj->sprite = SPR_ITEM;
mobj->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|(mobj->threshold);
break;
} }
K_UpdateMobjItemOverlay(mobj, mobj->threshold, mobj->movecount);
break; break;
} }
case MT_ITEMCAPSULE: case MT_ITEMCAPSULE:
@ -9533,6 +9530,14 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
case MT_GACHABOM_REBOUND: case MT_GACHABOM_REBOUND:
Obj_GachaBomReboundThink(mobj); Obj_GachaBomReboundThink(mobj);
if (P_MobjWasRemoved(mobj))
{
return false;
}
break;
case MT_SUPER_FLICKY:
Obj_SuperFlickyThink(mobj);
if (P_MobjWasRemoved(mobj)) if (P_MobjWasRemoved(mobj))
{ {
return false; return false;
@ -9718,7 +9723,7 @@ static boolean P_FuseThink(mobj_t *mobj)
case MT_RANDOMITEM: case MT_RANDOMITEM:
if (mobj->flags2 & MF2_DONTRESPAWN) if (mobj->flags2 & MF2_DONTRESPAWN)
{ {
; P_RemoveMobj(mobj);
} }
else if (!(gametyperules & GTR_CIRCUIT) && (mobj->state == &states[S_INVISIBLE])) else if (!(gametyperules & GTR_CIRCUIT) && (mobj->state == &states[S_INVISIBLE]))
{ {
@ -9726,22 +9731,10 @@ static boolean P_FuseThink(mobj_t *mobj)
} }
else else
{ {
mobj_t *newmobj; mobj->flags &= ~MF_NOCLIPTHING;
mobj->renderflags &= ~(RF_DONTDRAW|RF_TRANSMASK);
// Respawn from mapthing if you have one!
if (mobj->spawnpoint)
{
P_SpawnMapThing(mobj->spawnpoint);
newmobj = mobj->spawnpoint->mobj; // this is set to the new mobj in P_SpawnMapThing
}
else
newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->type);
// Transfer flags2 (strongbox, objectflip, bossnotrap)
newmobj->flags2 = mobj->flags2;
} }
P_RemoveMobj(mobj); // make sure they disappear
return false; return false;
case MT_ITEMCAPSULE: case MT_ITEMCAPSULE:
if (mobj->spawnpoint) if (mobj->spawnpoint)
@ -11592,11 +11585,11 @@ void P_RespawnBattleBoxes(void)
if (box->type != MT_RANDOMITEM if (box->type != MT_RANDOMITEM
|| (box->flags2 & MF2_DONTRESPAWN) || (box->flags2 & MF2_DONTRESPAWN)
|| box->health > 0 || !(box->flags & MF_NOCLIPTHING)
|| box->fuse) || box->fuse)
continue; // only popped items continue; // only popped items
box->fuse = TICRATE; // flicker back in (A_ItemPop preps this effect) box->fuse = TICRATE; // flicker back in
P_SetMobjState(box, box->info->raisestate); P_SetMobjState(box, box->info->raisestate);
if (numgotboxes > 0) if (numgotboxes > 0)

View file

@ -566,8 +566,6 @@ extern INT32 modulothing;
#define MAXHUNTEMERALDS 64 #define MAXHUNTEMERALDS 64
extern mapthing_t *huntemeralds[MAXHUNTEMERALDS]; extern mapthing_t *huntemeralds[MAXHUNTEMERALDS];
extern INT32 numhuntemeralds; extern INT32 numhuntemeralds;
extern boolean runemeraldmanager;
extern UINT16 emeraldspawndelay;
extern INT32 numstarposts; extern INT32 numstarposts;
extern UINT16 bossdisabled; extern UINT16 bossdisabled;
extern boolean stoppedclock; extern boolean stoppedclock;

View file

@ -79,6 +79,8 @@ typedef enum
RINGSHOOTER = 0x0100, RINGSHOOTER = 0x0100,
WHIP = 0x0200, WHIP = 0x0200,
HAND = 0x0400, HAND = 0x0400,
FLICKYATTACKER = 0x0800,
FLICKYCONTROLLER = 0x1000,
} player_saveflags; } player_saveflags;
static inline void P_ArchivePlayer(savebuffer_t *save) static inline void P_ArchivePlayer(savebuffer_t *save)
@ -319,6 +321,12 @@ static void P_NetArchivePlayers(savebuffer_t *save)
if (players[i].ringShooter) if (players[i].ringShooter)
flags |= RINGSHOOTER; flags |= RINGSHOOTER;
if (players[i].flickyAttacker)
flags |= FLICKYATTACKER;
if (players[i].powerup.flickyController)
flags |= FLICKYCONTROLLER;
WRITEUINT16(save->p, flags); WRITEUINT16(save->p, flags);
if (flags & SKYBOXVIEW) if (flags & SKYBOXVIEW)
@ -351,6 +359,12 @@ static void P_NetArchivePlayers(savebuffer_t *save)
if (flags & RINGSHOOTER) if (flags & RINGSHOOTER)
WRITEUINT32(save->p, players[i].ringShooter->mobjnum); WRITEUINT32(save->p, players[i].ringShooter->mobjnum);
if (flags & FLICKYATTACKER)
WRITEUINT32(save->p, players[i].flickyAttacker->mobjnum);
if (flags & FLICKYCONTROLLER)
WRITEUINT32(save->p, players[i].powerup.flickyController->mobjnum);
WRITEUINT32(save->p, (UINT32)players[i].followitem); WRITEUINT32(save->p, (UINT32)players[i].followitem);
WRITEUINT32(save->p, players[i].charflags); WRITEUINT32(save->p, players[i].charflags);
@ -527,6 +541,8 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT8(save->p, players[i].markedfordeath); WRITEUINT8(save->p, players[i].markedfordeath);
WRITEUINT8(save->p, players[i].ringboxdelay);
WRITEUINT8(save->p, players[i].ringboxaward);
WRITEFIXED(save->p, players[i].outrun); WRITEFIXED(save->p, players[i].outrun);
// respawnvars_t // respawnvars_t
@ -600,6 +616,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT32(save->p, players[i].itemRoulette.tics); WRITEUINT32(save->p, players[i].itemRoulette.tics);
WRITEUINT32(save->p, players[i].itemRoulette.elapsed); WRITEUINT32(save->p, players[i].itemRoulette.elapsed);
WRITEUINT8(save->p, players[i].itemRoulette.eggman); WRITEUINT8(save->p, players[i].itemRoulette.eggman);
WRITEUINT8(save->p, players[i].itemRoulette.ringbox);
// sonicloopsvars_t // sonicloopsvars_t
WRITEFIXED(save->p, players[i].loop.radius); WRITEFIXED(save->p, players[i].loop.radius);
@ -754,6 +771,12 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
if (flags & RINGSHOOTER) if (flags & RINGSHOOTER)
players[i].ringShooter = (mobj_t *)(size_t)READUINT32(save->p); players[i].ringShooter = (mobj_t *)(size_t)READUINT32(save->p);
if (flags & FLICKYATTACKER)
players[i].flickyAttacker = (mobj_t *)(size_t)READUINT32(save->p);
if (flags & FLICKYCONTROLLER)
players[i].powerup.flickyController = (mobj_t *)(size_t)READUINT32(save->p);
players[i].followitem = (mobjtype_t)READUINT32(save->p); players[i].followitem = (mobjtype_t)READUINT32(save->p);
//SetPlayerSkinByNum(i, players[i].skin); //SetPlayerSkinByNum(i, players[i].skin);
@ -931,6 +954,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].markedfordeath = READUINT8(save->p); players[i].markedfordeath = READUINT8(save->p);
players[i].ringboxdelay = READUINT8(save->p);
players[i].ringboxaward = READUINT8(save->p);
players[i].outrun = READFIXED(save->p); players[i].outrun = READFIXED(save->p);
// respawnvars_t // respawnvars_t
@ -1015,6 +1040,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].itemRoulette.tics = (tic_t)READUINT32(save->p); players[i].itemRoulette.tics = (tic_t)READUINT32(save->p);
players[i].itemRoulette.elapsed = (tic_t)READUINT32(save->p); players[i].itemRoulette.elapsed = (tic_t)READUINT32(save->p);
players[i].itemRoulette.eggman = (boolean)READUINT8(save->p); players[i].itemRoulette.eggman = (boolean)READUINT8(save->p);
players[i].itemRoulette.ringbox = (boolean)READUINT8(save->p);
// sonicloopsvars_t // sonicloopsvars_t
players[i].loop.radius = READFIXED(save->p); players[i].loop.radius = READFIXED(save->p);
@ -5258,6 +5284,20 @@ static void P_RelinkPointers(void)
if (!P_SetTarget(&players[i].ringShooter, P_FindNewPosition(temp))) if (!P_SetTarget(&players[i].ringShooter, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "ringShooter not found on player %d\n", i); CONS_Debug(DBG_GAMELOGIC, "ringShooter not found on player %d\n", i);
} }
if (players[i].flickyAttacker)
{
temp = (UINT32)(size_t)players[i].flickyAttacker;
players[i].flickyAttacker = NULL;
if (!P_SetTarget(&players[i].flickyAttacker, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "flickyAttacker not found on player %d\n", i);
}
if (players[i].powerup.flickyController)
{
temp = (UINT32)(size_t)players[i].powerup.flickyController;
players[i].powerup.flickyController = NULL;
if (!P_SetTarget(&players[i].powerup.flickyController, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "powerup.flickyController not found on player %d\n", i);
}
} }
} }
@ -5622,7 +5662,6 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
WRITESINT8(save->p, g_pickedVote); WRITESINT8(save->p, g_pickedVote);
WRITEUINT16(save->p, emeralds);
{ {
UINT8 globools = 0; UINT8 globools = 0;
if (stagefailed) if (stagefailed)
@ -5796,7 +5835,6 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
g_pickedVote = READSINT8(save->p); g_pickedVote = READSINT8(save->p);
emeralds = READUINT16(save->p);
{ {
UINT8 globools = READUINT8(save->p); UINT8 globools = READUINT8(save->p);
stagefailed = !!(globools & 1); stagefailed = !!(globools & 1);

View file

@ -7351,10 +7351,6 @@ static void P_InitLevelSettings(void)
K_TimerReset(); K_TimerReset();
// special stage tokens, emeralds, and ring total
runemeraldmanager = false;
emeraldspawndelay = 60*TICRATE;
nummaprings = 0; nummaprings = 0;
nummapboxes = numgotboxes = 0; nummapboxes = numgotboxes = 0;
maptargets = numtargets = 0; maptargets = numtargets = 0;
@ -8362,7 +8358,7 @@ void P_PostLoadLevel(void)
P_RunCachedActions(); P_RunCachedActions();
G_HandleSaveLevel(gamestate == GS_CEREMONY); G_HandleSaveLevel(false);
if (marathonmode & MA_INGAME) if (marathonmode & MA_INGAME)
{ {

View file

@ -4647,11 +4647,12 @@ void P_SetupSignExit(player_t *player, boolean tie)
thinker_t *think; thinker_t *think;
INT32 numfound = 0; INT32 numfound = 0;
angle_t bestAngle = K_MomentumAngle(player->mo) + ANGLE_180; if (player->position != 1
|| (gametyperules & GTR_SPECIALSTART))
if (player->position != 1)
return; return;
angle_t bestAngle = K_MomentumAngle(player->mo) + ANGLE_180;
for (; node; node = node->m_thinglist_next) for (; node; node = node->m_thinglist_next)
{ {
thing = node->m_thing; thing = node->m_thing;

View file

@ -342,7 +342,6 @@ void P_GiveEmerald(boolean spawnObj)
UINT8 em = P_GetNextEmerald(); UINT8 em = P_GetNextEmerald();
S_StartSound(NULL, sfx_cgot); // Got the emerald! S_StartSound(NULL, sfx_cgot); // Got the emerald!
emeralds |= (1 << em);
stagefailed = false; stagefailed = false;
if (spawnObj) if (spawnObj)
@ -714,12 +713,50 @@ void P_PlayVictorySound(mobj_t *source)
void P_EndingMusic(void) void P_EndingMusic(void)
{ {
const char *jingle = NULL; const char *jingle = NULL;
boolean nointer = false;
UINT8 bestPos = UINT8_MAX; UINT8 bestPos = UINT8_MAX;
player_t *bestPlayer = NULL; player_t *bestPlayer = NULL;
SINT8 i; SINT8 i;
// See G_DoCompleted and Y_DetermineIntermissionType
boolean nointer = ((modeattacking && (players[consoleplayer].pflags & PF_NOCONTEST))
|| (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE));
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i]
|| players[i].spectator)
continue;
// Battle powerstone win
if ((gametyperules & GTR_POWERSTONES)
&& ALLCHAOSEMERALDS(players[i].emeralds))
break;
// Special round?
if (((gametyperules & GTR_SPECIALSTART)
|| (grandprixinfo.gp == true
&& grandprixinfo.eventmode == GPEVENT_SPECIAL)
) == false)
continue;
// Any player has completed well?
if (!players[i].exiting
|| players[i].bot
|| K_IsPlayerLosing(&players[i]))
continue;
// Special win
break;
}
// Event - Emerald Finish
if (i != MAXPLAYERS)
{
jingle = "EMRLD";
goto skippingposition;
}
// Event - Level Finish // Event - Level Finish
// Check for if this is valid or not // Check for if this is valid or not
for (i = 0; i <= r_splitscreen; i++) for (i = 0; i <= r_splitscreen; i++)
@ -756,10 +793,6 @@ void P_EndingMusic(void)
} }
} }
// See G_DoCompleted and Y_DetermineIntermissionType
nointer = ((modeattacking && (players[consoleplayer].pflags & PF_NOCONTEST))
|| (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE));
if (bestPlayer == NULL) if (bestPlayer == NULL)
{ {
// No jingle for you // No jingle for you
@ -798,6 +831,8 @@ void P_EndingMusic(void)
} }
} }
skippingposition:
if (nointer == true) if (nointer == true)
{ {
// Do not set "racent" in G_Ticker // Do not set "racent" in G_Ticker

View file

@ -1189,6 +1189,13 @@ sfxinfo_t S_sfx[NUMSFX] =
{"iwhp", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Instawhip attack {"iwhp", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Instawhip attack
{"gbrk", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Guard break! {"gbrk", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Guard break!
// Super Flicky Power-Up
{"supflk", false, 255, 32, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Woodpecking - SF_NOINTERRUPT
{"fbost1", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Slowing down
{"fbird", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Speeding up
{"fhurt1", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Whipped
{"fhurt2", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Hunting
// SRB2Kart - Engine sounds // SRB2Kart - Engine sounds
// Engine class A // Engine class A
{"krta00", false, 48, 65, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"krta00", false, 48, 65, -1, NULL, 0, -1, -1, LUMPERROR, ""},

View file

@ -1258,6 +1258,12 @@ typedef enum
sfx_iwhp, sfx_iwhp,
sfx_gbrk, sfx_gbrk,
sfx_supflk,
sfx_fbost1,
sfx_fbird,
sfx_fhurt1,
sfx_fhurt2,
// Next up: UNIQUE ENGINE SOUNDS! Hoooooo boy... // Next up: UNIQUE ENGINE SOUNDS! Hoooooo boy...
// Engine class A - Low Speed, Low Weight // Engine class A - Low Speed, Low Weight
sfx_krta00, sfx_krta00,

View file

@ -47,6 +47,7 @@ TYPEDEF (botvars_t);
TYPEDEF (roundconditions_t); TYPEDEF (roundconditions_t);
TYPEDEF (skybox_t); TYPEDEF (skybox_t);
TYPEDEF (itemroulette_t); TYPEDEF (itemroulette_t);
TYPEDEF (powerupvars_t);
TYPEDEF (altview_t); TYPEDEF (altview_t);
TYPEDEF (player_t); TYPEDEF (player_t);

View file

@ -2286,7 +2286,7 @@ void V_DrawStringScaled(
break; break;
case LSHI_FONT: case LSHI_FONT:
case LSLOW_FONT: case LSLOW_FONT:
spacew = 16; spacew = 10;
break; break;
case OPPRF_FONT: case OPPRF_FONT:
spacew = 5; spacew = 5;
@ -2579,7 +2579,7 @@ fixed_t V_StringScaledWidth(
break; break;
case LSHI_FONT: case LSHI_FONT:
case LSLOW_FONT: case LSLOW_FONT:
spacew = 16; spacew = 10;
break; break;
case OPPRF_FONT: case OPPRF_FONT:
spacew = 5; spacew = 5;