mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-01-11 01:02:16 +00:00
Merge branch 'conditions-cascading' into 'master'
Conditions Cascading Closes #366 See merge request KartKrew/Kart!1053
This commit is contained in:
commit
b9bbb6cb8a
69 changed files with 3137 additions and 691 deletions
|
|
@ -1173,6 +1173,7 @@ bool CallFunc_HaveUnlockableTrigger(ACSVM::Thread *thread, const ACSVM::Word *ar
|
|||
{
|
||||
UINT8 id = 0;
|
||||
bool unlocked = false;
|
||||
auto info = &static_cast<Thread *>(thread)->info;
|
||||
|
||||
(void)argC;
|
||||
|
||||
|
|
@ -1182,9 +1183,11 @@ bool CallFunc_HaveUnlockableTrigger(ACSVM::Thread *thread, const ACSVM::Word *ar
|
|||
{
|
||||
CONS_Printf("Bad unlockable trigger ID %d\n", id);
|
||||
}
|
||||
else
|
||||
else if ((info != NULL)
|
||||
&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
|
||||
&& (info->mo->player != NULL))
|
||||
{
|
||||
unlocked = (unlocktriggers & (1 << id));
|
||||
unlocked = (info->mo->player->roundconditions.unlocktriggers & (1 << id));
|
||||
}
|
||||
|
||||
thread->dataStk.push(unlocked);
|
||||
|
|
@ -1354,7 +1357,7 @@ bool CallFunc_BreakTheCapsules(ACSVM::Thread *thread, const ACSVM::Word *argV, A
|
|||
(void)argV;
|
||||
(void)argC;
|
||||
|
||||
thread->dataStk.push(battlecapsules);
|
||||
thread->dataStk.push(battleprisons);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2242,6 +2242,11 @@ void CV_AddValue(consvar_t *var, INT32 increment)
|
|||
if (var->PossibleValue == kartspeed_cons_t)
|
||||
max++; // Accommodate KARTSPEED_AUTO
|
||||
}
|
||||
else if (var->PossibleValue == gpdifficulty_cons_t
|
||||
&& !M_SecretUnlocked(SECRET_MASTERMODE, false))
|
||||
{
|
||||
max = KARTSPEED_HARD+1;
|
||||
}
|
||||
}
|
||||
#ifdef PARANOIA
|
||||
if (currentindice == -1)
|
||||
|
|
|
|||
|
|
@ -174,11 +174,7 @@ extern CV_PossibleValue_t CV_Unsigned[];
|
|||
extern CV_PossibleValue_t CV_Natural[];
|
||||
|
||||
// SRB2kart
|
||||
#define KARTSPEED_AUTO -1
|
||||
#define KARTSPEED_EASY 0
|
||||
#define KARTSPEED_NORMAL 1
|
||||
#define KARTSPEED_HARD 2
|
||||
#define KARTGP_MASTER 3 // Not a speed setting, gives the hardest speed with maxed out bots
|
||||
// the KARTSPEED and KARTGP were previously defined here, but moved to doomstat to avoid circular dependencies
|
||||
extern CV_PossibleValue_t kartspeed_cons_t[], dummykartspeed_cons_t[], gpdifficulty_cons_t[];
|
||||
|
||||
extern consvar_t cv_execversion;
|
||||
|
|
|
|||
|
|
@ -824,7 +824,7 @@ static boolean CL_SendJoin(void)
|
|||
for (; i < MAXSPLITSCREENPLAYERS; i++)
|
||||
strncpy(netbuffer->u.clientcfg.names[i], va("Player %c", 'A' + i), MAXPLAYERNAME);
|
||||
|
||||
memcpy(&netbuffer->u.clientcfg.availabilities, R_GetSkinAvailabilities(false), MAXAVAILABILITY*sizeof(UINT8));
|
||||
memcpy(&netbuffer->u.clientcfg.availabilities, R_GetSkinAvailabilities(false, false), MAXAVAILABILITY*sizeof(UINT8));
|
||||
|
||||
return HSendPacket(servernode, false, 0, sizeof (clientconfig_pak));
|
||||
}
|
||||
|
|
@ -3723,7 +3723,7 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum)
|
|||
static void Got_AddBot(UINT8 **p, INT32 playernum)
|
||||
{
|
||||
INT16 newplayernum;
|
||||
UINT8 i, skinnum = 0;
|
||||
UINT8 skinnum = 0;
|
||||
UINT8 difficulty = DIFFICULTBOT;
|
||||
|
||||
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
|
||||
|
|
@ -3753,14 +3753,8 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
|||
|
||||
playernode[newplayernum] = servernode;
|
||||
|
||||
// todo find a way to have all auto unlocked for dedicated
|
||||
if (playeringame[0])
|
||||
{
|
||||
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||
{
|
||||
players[newplayernum].availabilities[i] = players[0].availabilities[i];
|
||||
}
|
||||
}
|
||||
// this will permit unlocks
|
||||
memcpy(&players[newplayernum].availabilities, R_GetSkinAvailabilities(false, true), MAXAVAILABILITY*sizeof(UINT8));
|
||||
|
||||
players[newplayernum].splitscreenindex = 0;
|
||||
players[newplayernum].bot = true;
|
||||
|
|
@ -3933,7 +3927,7 @@ boolean SV_SpawnServer(void)
|
|||
// strictly speaking, i'm not convinced the following is necessary
|
||||
// but I'm not confident enough to remove it entirely in case it breaks something
|
||||
{
|
||||
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(false);
|
||||
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(false, false);
|
||||
SINT8 node = 0;
|
||||
for (; node < MAXNETNODES; node++)
|
||||
result |= SV_AddWaitingPlayers(node, availabilitiesbuffer, cv_playername[0].zstring, cv_playername[1].zstring, cv_playername[2].zstring, cv_playername[3].zstring);
|
||||
|
|
|
|||
38
src/d_main.c
38
src/d_main.c
|
|
@ -1045,6 +1045,9 @@ void D_ClearState(void)
|
|||
cursongcredit.def = NULL;
|
||||
S_StopSounds();
|
||||
|
||||
if (gamedata && gamedata->deferredsave)
|
||||
G_SaveGameData();
|
||||
|
||||
G_SetGamestate(GS_NULL);
|
||||
wipegamestate = GS_NULL;
|
||||
}
|
||||
|
|
@ -1920,23 +1923,32 @@ void D_SRB2Main(void)
|
|||
newskill = (INT16)j;
|
||||
}
|
||||
|
||||
if (grandprixinfo.gp == true)
|
||||
// Invalidate if locked.
|
||||
if ((newskill >= KARTSPEED_HARD && !M_SecretUnlocked(SECRET_HARDSPEED, true))
|
||||
|| (newskill >= KARTGP_MASTER && !M_SecretUnlocked(SECRET_MASTERMODE, true)))
|
||||
{
|
||||
if (newskill == KARTGP_MASTER)
|
||||
{
|
||||
grandprixinfo.masterbots = true;
|
||||
newskill = KARTSPEED_HARD;
|
||||
}
|
||||
|
||||
grandprixinfo.gamespeed = newskill;
|
||||
}
|
||||
else if (newskill == KARTGP_MASTER)
|
||||
{
|
||||
newskill = KARTSPEED_HARD;
|
||||
newskill = -1;
|
||||
}
|
||||
|
||||
if (newskill != -1)
|
||||
{
|
||||
if (grandprixinfo.gp == true)
|
||||
{
|
||||
if (newskill == KARTGP_MASTER)
|
||||
{
|
||||
grandprixinfo.masterbots = true;
|
||||
newskill = KARTSPEED_HARD;
|
||||
}
|
||||
|
||||
grandprixinfo.gamespeed = newskill;
|
||||
}
|
||||
else if (newskill == KARTGP_MASTER)
|
||||
{
|
||||
newskill = KARTSPEED_HARD;
|
||||
}
|
||||
|
||||
CV_SetValue(&cv_kartspeed, newskill);
|
||||
}
|
||||
}
|
||||
|
||||
if (server && (dedicated || !M_CheckParm("+map")))
|
||||
|
|
@ -1946,7 +1958,7 @@ void D_SRB2Main(void)
|
|||
I_Error("Can't get first map of gametype\n");
|
||||
}
|
||||
|
||||
if (M_MapLocked(pstartmap))
|
||||
if (pstartmap != 1 && M_MapLocked(pstartmap))
|
||||
{
|
||||
G_SetUsedCheats();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2811,7 +2811,7 @@ static void Command_Map_f(void)
|
|||
return;
|
||||
}
|
||||
|
||||
if (M_MapLocked(newmapnum))
|
||||
if (/*newmapnum != 1 &&*/ M_MapLocked(newmapnum))
|
||||
{
|
||||
ischeating = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -333,6 +333,29 @@ struct botvars_t
|
|||
tic_t spindashconfirm; // When high enough, they will try spindashing
|
||||
};
|
||||
|
||||
// player_t struct for round-specific condition tracking
|
||||
|
||||
struct roundconditions_t
|
||||
{
|
||||
// Reduce the number of checks by only updating when this is true
|
||||
boolean checkthisframe;
|
||||
|
||||
// Trivial Yes/no events across multiple UCRP's
|
||||
boolean fell_off;
|
||||
boolean touched_offroad;
|
||||
boolean touched_sneakerpanel;
|
||||
boolean debt_rings;
|
||||
boolean tripwire_hyuu;
|
||||
boolean spb_neuter;
|
||||
boolean landmine_dunk;
|
||||
boolean hit_midair;
|
||||
|
||||
mobjeflag_t wet_player;
|
||||
|
||||
// 32 triggers, one bit each, for map execution
|
||||
UINT32 unlocktriggers;
|
||||
};
|
||||
|
||||
// player_t struct for all skybox variables
|
||||
struct skybox_t {
|
||||
mobj_t * viewpoint;
|
||||
|
|
@ -695,6 +718,7 @@ struct player_t
|
|||
#endif
|
||||
|
||||
sonicloopvars_t loop;
|
||||
roundconditions_t roundconditions;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
382
src/deh_soc.c
382
src/deh_soc.c
|
|
@ -2280,16 +2280,24 @@ void readunlockable(MYFILE *f, INT32 num)
|
|||
unlockables[num].type = SECRET_FOLLOWER;
|
||||
else if (fastcmp(word2, "HARDSPEED"))
|
||||
unlockables[num].type = SECRET_HARDSPEED;
|
||||
else if (fastcmp(word2, "MASTERMODE"))
|
||||
unlockables[num].type = SECRET_MASTERMODE;
|
||||
else if (fastcmp(word2, "ENCORE"))
|
||||
unlockables[num].type = SECRET_ENCORE;
|
||||
else if (fastcmp(word2, "LEGACYBOXRUMMAGE"))
|
||||
unlockables[num].type = SECRET_LEGACYBOXRUMMAGE;
|
||||
else if (fastcmp(word2, "TIMEATTACK"))
|
||||
unlockables[num].type = SECRET_TIMEATTACK;
|
||||
else if (fastcmp(word2, "BREAKTHECAPSULES"))
|
||||
unlockables[num].type = SECRET_BREAKTHECAPSULES;
|
||||
else if (fastcmp(word2, "PRISONBREAK"))
|
||||
unlockables[num].type = SECRET_PRISONBREAK;
|
||||
else if (fastcmp(word2, "SPECIALATTACK"))
|
||||
unlockables[num].type = SECRET_SPECIALATTACK;
|
||||
else if (fastcmp(word2, "SPBATTACK"))
|
||||
unlockables[num].type = SECRET_SPBATTACK;
|
||||
else if (fastcmp(word2, "ONLINE"))
|
||||
unlockables[num].type = SECRET_ONLINE;
|
||||
else if (fastcmp(word2, "ADDONS"))
|
||||
unlockables[num].type = SECRET_ADDONS;
|
||||
else if (fastcmp(word2, "EGGTV"))
|
||||
unlockables[num].type = SECRET_EGGTV;
|
||||
else if (fastcmp(word2, "SOUNDTEST"))
|
||||
unlockables[num].type = SECRET_SOUNDTEST;
|
||||
else if (fastcmp(word2, "ALTTITLE"))
|
||||
|
|
@ -2328,18 +2336,23 @@ void readunlockable(MYFILE *f, INT32 num)
|
|||
static void readcondition(UINT8 set, UINT32 id, char *word2)
|
||||
{
|
||||
INT32 i;
|
||||
char *params[4]; // condition, requirement, extra info, extra info
|
||||
char *params[5]; // condition, requirement, extra info, extra info, stringvar
|
||||
char *spos;
|
||||
char *stringvar = NULL;
|
||||
|
||||
conditiontype_t ty;
|
||||
INT32 re;
|
||||
INT32 re = 0;
|
||||
INT16 x1 = 0, x2 = 0;
|
||||
|
||||
INT32 offset = 0;
|
||||
|
||||
#if 0
|
||||
char *endpos = word2 + strlen(word2);
|
||||
#endif
|
||||
|
||||
spos = strtok(word2, " ");
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
for (i = 0; i < 5; ++i)
|
||||
{
|
||||
if (spos != NULL)
|
||||
{
|
||||
|
|
@ -2356,13 +2369,56 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
|
|||
return;
|
||||
}
|
||||
|
||||
if (fastcmp(params[0], "PLAYTIME")
|
||||
|| (++offset && fastcmp(params[0], "MATCHESPLAYED")))
|
||||
if (fastcmp(params[0], "PLAYTIME"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_PLAYTIME + offset;
|
||||
re = atoi(params[1]);
|
||||
}
|
||||
else if (fastcmp(params[0], "ROUNDSPLAYED"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_ROUNDSPLAYED;
|
||||
re = atoi(params[1]);
|
||||
x1 = GDGT_MAX;
|
||||
|
||||
if (re == 0)
|
||||
{
|
||||
deh_warning("Rounds played requirement is %d for condition ID %d", re, id+1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (params[2])
|
||||
{
|
||||
if (fastcmp(params[2], "RACE"))
|
||||
x1 = GDGT_RACE;
|
||||
else if (fastcmp(params[2], "BATTLE"))
|
||||
x1 = GDGT_BATTLE;
|
||||
else if (fastcmp(params[2], "PRISONS"))
|
||||
x1 = GDGT_PRISONS;
|
||||
else if (fastcmp(params[2], "SPECIAL"))
|
||||
x1 = GDGT_SPECIAL;
|
||||
else if (fastcmp(params[2], "CUSTOM"))
|
||||
x1 = GDGT_CUSTOM;
|
||||
else
|
||||
{
|
||||
deh_warning("gametype requirement \"%s\" invalid for condition ID %d", params[2], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "TOTALRINGS"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_TOTALRINGS;
|
||||
re = atoi(params[1]);
|
||||
|
||||
if (re < 2 || re > GDMAX_RINGS)
|
||||
{
|
||||
deh_warning("Total Rings requirement %d out of range (%d - %d) for condition ID %d", re, 2, GDMAX_RINGS, id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "POWERLEVEL"))
|
||||
{
|
||||
PARAMCHECK(2);
|
||||
|
|
@ -2394,7 +2450,9 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
|
|||
re = atoi(params[1]);
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "MAPVISITED")
|
||||
|| (++offset && fastcmp(params[0], "MAPBEATEN")))
|
||||
|| (++offset && fastcmp(params[0], "MAPBEATEN"))
|
||||
|| (++offset && fastcmp(params[0], "MAPENCORE"))
|
||||
|| (++offset && fastcmp(params[0], "MAPSPBATTACK")))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_MAPVISITED + offset;
|
||||
|
|
@ -2419,17 +2477,26 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "TRIGGER"))
|
||||
else if ((offset=0) || fastcmp(params[0], "ALLCHAOS")
|
||||
|| (++offset && fastcmp(params[0], "ALLSUPER"))
|
||||
|| (++offset && fastcmp(params[0], "ALLEMERALDS")))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_TRIGGER;
|
||||
re = atoi(params[1]);
|
||||
|
||||
// constrained by 32 bits
|
||||
if (re < 0 || re > 31)
|
||||
//PARAMCHECK(1);
|
||||
ty = UC_ALLCHAOS + offset;
|
||||
re = KARTSPEED_NORMAL;
|
||||
if (params[1])
|
||||
{
|
||||
deh_warning("Trigger ID %d out of range (0 - 31) for condition ID %d", re, id+1);
|
||||
return;
|
||||
if (fastcmp(params[1], "NORMAL"))
|
||||
;
|
||||
else if (fastcmp(params[1], "HARD"))
|
||||
x1 = KARTSPEED_HARD;
|
||||
else if (fastcmp(params[1], "MASTER"))
|
||||
x1 = KARTGP_MASTER;
|
||||
else
|
||||
{
|
||||
deh_warning("gamespeed requirement \"%s\" invalid for condition ID %d", params[1], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "TOTALMEDALS"))
|
||||
|
|
@ -2474,13 +2541,273 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "ADDON")
|
||||
|| (++offset && fastcmp(params[0], "REPLAY"))
|
||||
|| (++offset && fastcmp(params[0], "CRASH")))
|
||||
{
|
||||
//PARAMCHECK(1);
|
||||
ty = UC_ADDON + offset;
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "AND")
|
||||
|| (++offset && fastcmp(params[0], "COMMA")))
|
||||
{
|
||||
//PARAMCHECK(1);
|
||||
ty = UC_AND + offset;
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "PREFIX_GRANDPRIX")
|
||||
|| (++offset && fastcmp(params[0], "PREFIX_BONUSROUND"))
|
||||
|| (++offset && fastcmp(params[0], "PREFIX_TIMEATTACK"))
|
||||
|| (++offset && fastcmp(params[0], "PREFIX_PRISONBREAK"))
|
||||
|| (++offset && fastcmp(params[0], "PREFIX_SEALEDSTAR")))
|
||||
{
|
||||
//PARAMCHECK(1);
|
||||
ty = UCRP_PREFIX_GRANDPRIX + offset;
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "PREFIX_ISMAP")
|
||||
|| (++offset && fastcmp(params[0], "ISMAP")))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_PREFIX_ISMAP + offset;
|
||||
re = G_MapNumber(params[1]);
|
||||
|
||||
if (re >= nummapheaders)
|
||||
{
|
||||
deh_warning("Invalid level %s for condition ID %d", params[1], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "ISCHARACTER"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_ISCHARACTER;
|
||||
#if 0
|
||||
{
|
||||
re = R_SkinAvailable(params[1]);
|
||||
|
||||
if (re < 0)
|
||||
{
|
||||
deh_warning("Invalid character %s for condition ID %d", params[1], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
stringvar = Z_StrDup(params[1]);
|
||||
re = -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (fastcmp(params[0], "ISENGINECLASS"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_ISENGINECLASS;
|
||||
if (!params[1][1]
|
||||
&& params[1][0] >= 'A' && params[1][0] <= 'J')
|
||||
{
|
||||
re = params[1][0] - 'A';
|
||||
}
|
||||
else
|
||||
{
|
||||
deh_warning("engine class requirement \"%s\" invalid for condition ID %d", params[1], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "ISDIFFICULTY"))
|
||||
{
|
||||
//PARAMCHECK(1);
|
||||
ty = UCRP_ISDIFFICULTY;
|
||||
re = KARTSPEED_NORMAL;
|
||||
if (params[1])
|
||||
{
|
||||
if (fastcmp(params[1], "NORMAL"))
|
||||
;
|
||||
else if (fastcmp(params[1], "HARD"))
|
||||
x1 = KARTSPEED_HARD;
|
||||
else if (fastcmp(params[1], "MASTER"))
|
||||
x1 = KARTGP_MASTER;
|
||||
else
|
||||
{
|
||||
deh_warning("gamespeed requirement \"%s\" invalid for condition ID %d", params[1], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "PODIUMCUP"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_PODIUMCUP;
|
||||
{
|
||||
cupheader_t *cup = kartcupheaders;
|
||||
while (cup)
|
||||
{
|
||||
if (!strcmp(cup->name, params[1]))
|
||||
break;
|
||||
cup = cup->next;
|
||||
}
|
||||
|
||||
if (!cup)
|
||||
{
|
||||
deh_warning("Invalid cup %s for condition ID %d", params[1], id+1);
|
||||
return;
|
||||
}
|
||||
|
||||
re = cup->id;
|
||||
}
|
||||
|
||||
if (params[2])
|
||||
{
|
||||
if (params[2][0] && !params[2][1])
|
||||
{
|
||||
x2 = 1;
|
||||
|
||||
switch (params[2][0])
|
||||
{
|
||||
case 'E': { x1 = GRADE_E; break; }
|
||||
case 'D': { x1 = GRADE_D; break; }
|
||||
case 'C': { x1 = GRADE_C; break; }
|
||||
case 'B': { x1 = GRADE_B; break; }
|
||||
case 'A': { x1 = GRADE_A; break; }
|
||||
case 'S': { x1 = GRADE_S; break; }
|
||||
default:
|
||||
deh_warning("Invalid grade %s for condition ID %d", params[2], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[2], "GOLD")
|
||||
|| (++offset && fastcmp(params[2], "SILVER"))
|
||||
|| (++offset && fastcmp(params[2], "BRONZE")))
|
||||
{
|
||||
x1 = offset + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
deh_warning("Invalid cup result %s for condition ID %d", params[2], id+1);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "PODIUMEMERALD")
|
||||
|| (++offset && fastcmp(params[0], "PODIUMPRIZE")))
|
||||
{
|
||||
//PARAMCHECK(1);
|
||||
ty = UCRP_PODIUMEMERALD + offset;
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "FINISHCOOL")
|
||||
|| (++offset && fastcmp(params[0], "FINISHALLPRISONS"))
|
||||
|| (++offset && fastcmp(params[0], "NOCONTEST")))
|
||||
{
|
||||
//PARAMCHECK(1);
|
||||
ty = UCRP_FINISHCOOL + offset;
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "FINISHPLACE")
|
||||
|| (++offset && fastcmp(params[0], "FINISHPLACEEXACT")))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_FINISHPLACE + offset;
|
||||
re = atoi(params[1]);
|
||||
|
||||
if (re < 1 || re > MAXPLAYERS)
|
||||
{
|
||||
deh_warning("Invalid place %s for condition ID %d", params[1], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "FINISHTIME")
|
||||
|| (++offset && fastcmp(params[0], "FINISHTIMEEXACT"))
|
||||
|| (++offset && fastcmp(params[0], "FINISHTIMELEFT")))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_FINISHTIME + offset;
|
||||
re = get_number(params[1]);
|
||||
|
||||
if (re < 0)
|
||||
{
|
||||
deh_warning("Invalid time %s for condition ID %d", params[1], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "TRIGGER"))
|
||||
{
|
||||
PARAMCHECK(2); // strictly speaking at LEAST two
|
||||
ty = UCRP_TRIGGER;
|
||||
re = atoi(params[1]);
|
||||
|
||||
// constrained by 32 bits
|
||||
if (re < 0 || re > 31)
|
||||
{
|
||||
deh_warning("Trigger ID %d out of range (0 - 31) for condition ID %d", re, id+1);
|
||||
return;
|
||||
}
|
||||
|
||||
// The following undid the effects of strtok.
|
||||
// Unfortunately, there is no way it can reasonably undo the effects of strupr.
|
||||
// If we want custom descriptions for map execution triggers, we're gonna need a different method.
|
||||
#if 0
|
||||
// undo affect of strtok
|
||||
i = 5;
|
||||
// so spos will still be the strtok from earlier
|
||||
while (i >= 2)
|
||||
{
|
||||
if (!spos)
|
||||
continue;
|
||||
while (*spos != '\0')
|
||||
spos++;
|
||||
if (spos < endpos)
|
||||
*spos = ' ';
|
||||
spos = params[--i];
|
||||
}
|
||||
#endif
|
||||
|
||||
stringvar = Z_StrDup(params[2]);
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "FALLOFF")
|
||||
|| (++offset && fastcmp(params[0], "TOUCHOFFROAD"))
|
||||
|| (++offset && fastcmp(params[0], "TOUCHSNEAKERPANEL"))
|
||||
|| (++offset && fastcmp(params[0], "RINGDEBT")))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_FALLOFF + offset;
|
||||
re = 1;
|
||||
|
||||
if (params[1][0] == 'F' || params[1][0] == 'N' || params[1][0] == '0')
|
||||
re = 0;
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "TRIPWIREHYUU")
|
||||
|| (++offset && fastcmp(params[0], "SPBNEUTER"))
|
||||
|| (++offset && fastcmp(params[0], "LANDMINEDUNK"))
|
||||
|| (++offset && fastcmp(params[0], "HITMIDAIR")))
|
||||
{
|
||||
//PARAMCHECK(1);
|
||||
ty = UCRP_TRIPWIREHYUU + offset;
|
||||
}
|
||||
else if (fastcmp(params[0], "WETPLAYER"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_WETPLAYER;
|
||||
re = MFE_UNDERWATER;
|
||||
x1 = 1;
|
||||
|
||||
if (params[2])
|
||||
{
|
||||
if (fastcmp(params[2], "STRICT"))
|
||||
re |= MFE_TOUCHWATER;
|
||||
else
|
||||
{
|
||||
deh_warning("liquid strictness requirement \"%s\" invalid for condition ID %d", params[2], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
stringvar = Z_StrDup(params[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
deh_warning("Invalid condition name %s for condition ID %d", params[0], id+1);
|
||||
return;
|
||||
}
|
||||
|
||||
M_AddRawCondition(set, (UINT8)id, ty, re, x1, x2);
|
||||
M_AddRawCondition(set, (UINT8)id, ty, re, x1, x2, stringvar);
|
||||
}
|
||||
|
||||
void readconditionset(MYFILE *f, UINT8 setnum)
|
||||
|
|
@ -2633,6 +2960,10 @@ void readmaincfg(MYFILE *f, boolean mainfile)
|
|||
clear_emblems();
|
||||
//clear_levels();
|
||||
doClearLevels = true;
|
||||
|
||||
G_ClearRecords();
|
||||
M_ClearStats();
|
||||
M_ClearSecrets();
|
||||
}
|
||||
#ifndef DEVELOP
|
||||
else if (!mainfile && !gamedataadded)
|
||||
|
|
@ -3101,7 +3432,16 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
|
|||
i = atoi(word2); // used for numerical settings
|
||||
strupr(word2);
|
||||
|
||||
if (fastcmp(word, "ICON"))
|
||||
if (fastcmp(word, "MONITOR"))
|
||||
{
|
||||
if (i > 0 && i < 10)
|
||||
cup->monitor = i;
|
||||
else if (!word2[0] || word2[1] != '\0' || word2[0] == '0')
|
||||
deh_warning("%s Cup: Invalid monitor type \"%s\" (should be 1-9 or A-Z)\n", cup->name, word2);
|
||||
else
|
||||
cup->monitor = (word2[0] - 'A') + 10;
|
||||
}
|
||||
else if (fastcmp(word, "ICON"))
|
||||
{
|
||||
deh_strlcpy(cup->icon, word2,
|
||||
sizeof(cup->icon), va("%s Cup: icon", cup->name));
|
||||
|
|
|
|||
|
|
@ -5837,7 +5837,7 @@ const char *const GAMETYPERULE_LIST[] = {
|
|||
"KARMA",
|
||||
"ITEMARROWS",
|
||||
|
||||
"CAPSULES",
|
||||
"PRISONS",
|
||||
"CATCHER",
|
||||
"ROLLINGSTART",
|
||||
"SPECIALSTART",
|
||||
|
|
|
|||
|
|
@ -486,6 +486,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
|
|||
{
|
||||
cup = Z_Calloc(sizeof (cupheader_t), PU_STATIC, NULL);
|
||||
cup->id = numkartcupheaders;
|
||||
cup->monitor = 1;
|
||||
deh_strlcpy(cup->name, word2,
|
||||
sizeof(cup->name), va("Cup header %s: name", word2));
|
||||
if (prev != NULL)
|
||||
|
|
|
|||
|
|
@ -120,6 +120,30 @@ struct recorddata_t
|
|||
//UINT16 rings; ///< Rings when the level was finished.
|
||||
};
|
||||
|
||||
#define KARTSPEED_AUTO -1
|
||||
#define KARTSPEED_EASY 0
|
||||
#define KARTSPEED_NORMAL 1
|
||||
#define KARTSPEED_HARD 2
|
||||
#define KARTGP_MASTER 3 // Not a speed setting, gives the hardest speed with maxed out bots
|
||||
#define KARTGP_MAX 4
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GRADE_E,
|
||||
GRADE_D,
|
||||
GRADE_C,
|
||||
GRADE_B,
|
||||
GRADE_A,
|
||||
GRADE_S
|
||||
} gp_rank_e;
|
||||
|
||||
struct cupwindata_t
|
||||
{
|
||||
UINT8 best_placement;
|
||||
gp_rank_e best_grade;
|
||||
boolean got_emerald;
|
||||
};
|
||||
|
||||
// mapvisited is now a set of flags that says what we've done in the map.
|
||||
#define MV_VISITED (1)
|
||||
#define MV_BEATEN (1<<1)
|
||||
|
|
@ -352,6 +376,7 @@ struct customoption_t
|
|||
struct cupheader_t
|
||||
{
|
||||
UINT16 id; ///< Cup ID
|
||||
UINT8 monitor; ///< Monitor graphic 1-9 or A-Z
|
||||
char name[15]; ///< Cup title (14 chars)
|
||||
char icon[9]; ///< Name of the icon patch
|
||||
char *levellist[CUPCACHE_MAX]; ///< List of levels that belong to this cup
|
||||
|
|
@ -359,6 +384,7 @@ struct cupheader_t
|
|||
UINT8 numlevels; ///< Number of levels defined in levellist
|
||||
UINT8 numbonus; ///< Number of bonus stages defined
|
||||
UINT8 emeraldnum; ///< ID of Emerald to use for special stage (1-7 for Chaos Emeralds, 8-14 for Super Emeralds, 0 for no emerald)
|
||||
cupwindata_t windata[4]; ///< Data for cup visitation
|
||||
cupheader_t *next; ///< Next cup in linked list
|
||||
};
|
||||
|
||||
|
|
@ -528,7 +554,7 @@ enum GameTypeRules
|
|||
GTR_ITEMARROWS = 1<<9, // Show item box arrows above players
|
||||
|
||||
// Bonus gametype rules
|
||||
GTR_CAPSULES = 1<<10, // Can enter Break The Capsules mode
|
||||
GTR_PRISONS = 1<<10, // Can enter Prison Break mode
|
||||
GTR_CATCHER = 1<<11, // UFO Catcher (only works with GTR_CIRCUIT)
|
||||
GTR_ROLLINGSTART = 1<<12, // Rolling start (only works with GTR_CIRCUIT)
|
||||
GTR_SPECIALSTART = 1<<13, // White fade instant start
|
||||
|
|
|
|||
|
|
@ -310,6 +310,8 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset
|
|||
|
||||
void F_StartIntro(void)
|
||||
{
|
||||
cursongcredit.def = NULL;
|
||||
|
||||
if (gamestate)
|
||||
{
|
||||
F_WipeStartScreen();
|
||||
|
|
@ -1090,7 +1092,7 @@ void F_GameEvaluationTicker(void)
|
|||
{
|
||||
++gamedata->timesBeaten;
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
else
|
||||
|
|
|
|||
11
src/g_demo.c
11
src/g_demo.c
|
|
@ -26,6 +26,7 @@
|
|||
#include "g_game.h"
|
||||
#include "g_demo.h"
|
||||
#include "m_misc.h"
|
||||
#include "m_cond.h"
|
||||
#include "k_menu.h"
|
||||
#include "m_argv.h"
|
||||
#include "hu_stuff.h"
|
||||
|
|
@ -2240,7 +2241,7 @@ static void G_SaveDemoSkins(UINT8 **pp)
|
|||
{
|
||||
char skin[16];
|
||||
UINT8 i;
|
||||
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(true);
|
||||
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(true, false);
|
||||
|
||||
WRITEUINT8((*pp), numskins);
|
||||
for (i = 0; i < numskins; i++)
|
||||
|
|
@ -4189,7 +4190,15 @@ void G_SaveDemo(void)
|
|||
if (!modeattacking)
|
||||
{
|
||||
if (demo.savemode == DSM_SAVED)
|
||||
{
|
||||
CONS_Printf(M_GetText("Demo %s recorded\n"), demoname);
|
||||
if (gamedata->eversavedreplay == false)
|
||||
{
|
||||
gamedata->eversavedreplay = true;
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
}
|
||||
else
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Demo %s not saved\n"), demoname);
|
||||
}
|
||||
|
|
|
|||
289
src/g_game.c
289
src/g_game.c
|
|
@ -465,6 +465,8 @@ void G_AllocMainRecordData(INT16 i)
|
|||
void G_ClearRecords(void)
|
||||
{
|
||||
INT16 i;
|
||||
cupheader_t *cup;
|
||||
|
||||
for (i = 0; i < nummapheaders; ++i)
|
||||
{
|
||||
if (mapheaderinfo[i]->mainrecord)
|
||||
|
|
@ -473,6 +475,11 @@ void G_ClearRecords(void)
|
|||
mapheaderinfo[i]->mainrecord = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (cup = kartcupheaders; cup; cup = cup->next)
|
||||
{
|
||||
memset(&cup->windata, 0, sizeof(cup->windata));
|
||||
}
|
||||
}
|
||||
|
||||
// For easy retrieval of records
|
||||
|
|
@ -662,8 +669,8 @@ void G_UpdateRecords(void)
|
|||
S_StartSound(NULL, sfx_ncitem);
|
||||
}
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
G_SaveGameData();
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
gamedata->deferredsave = true;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -2367,19 +2374,6 @@ static inline void G_PlayerFinishLevel(INT32 player)
|
|||
|
||||
p->starpostnum = 0;
|
||||
memset(&p->respawn, 0, sizeof (p->respawn));
|
||||
|
||||
// SRB2kart: Increment the "matches played" counter.
|
||||
if (player == consoleplayer)
|
||||
{
|
||||
if (legitimateexit && !demo.playback && !mapreset) // (yes you're allowed to unlock stuff this way when the game is modified)
|
||||
{
|
||||
gamedata->matchesplayed++;
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
|
||||
legitimateexit = false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -2454,6 +2448,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
INT32 kickstartaccel;
|
||||
boolean enteredGame;
|
||||
|
||||
roundconditions_t roundconditions;
|
||||
boolean saveroundconditions;
|
||||
|
||||
score = players[player].score;
|
||||
lives = players[player].lives;
|
||||
ctfteam = players[player].ctfteam;
|
||||
|
|
@ -2544,6 +2541,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
khudfinish = 0;
|
||||
khudcardanimation = 0;
|
||||
starpostnum = 0;
|
||||
|
||||
saveroundconditions = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -2592,6 +2591,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
starpostnum = players[player].starpostnum;
|
||||
|
||||
pflags |= (players[player].pflags & (PF_STASIS|PF_ELIMINATED|PF_NOCONTEST|PF_FAULT|PF_LOSTLIFE));
|
||||
|
||||
memcpy(&roundconditions, &players[player].roundconditions, sizeof (roundconditions));
|
||||
saveroundconditions = true;
|
||||
}
|
||||
|
||||
if (!betweenmaps)
|
||||
|
|
@ -2676,6 +2678,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
memcpy(&p->itemRoulette, &itemRoulette, sizeof (p->itemRoulette));
|
||||
memcpy(&p->respawn, &respawn, sizeof (p->respawn));
|
||||
|
||||
if (saveroundconditions)
|
||||
memcpy(&p->roundconditions, &roundconditions, sizeof (p->roundconditions));
|
||||
|
||||
if (follower)
|
||||
P_RemoveMobj(follower);
|
||||
|
||||
|
|
@ -3302,7 +3307,7 @@ static gametype_t defaultgametypes[] =
|
|||
{
|
||||
"Battle",
|
||||
"GT_BATTLE",
|
||||
GTR_SPHERES|GTR_BUMPERS|GTR_PAPERITEMS|GTR_POWERSTONES|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_CLOSERPLAYERS,
|
||||
GTR_SPHERES|GTR_BUMPERS|GTR_PAPERITEMS|GTR_POWERSTONES|GTR_KARMA|GTR_ITEMARROWS|GTR_PRISONS|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_CLOSERPLAYERS,
|
||||
TOL_BATTLE,
|
||||
int_scoreortimeattack,
|
||||
0,
|
||||
|
|
@ -3866,7 +3871,7 @@ static void G_UpdateVisited(void)
|
|||
if ((earnedEmblems = M_CompletionEmblems()))
|
||||
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
|
||||
|
|
@ -3946,7 +3951,7 @@ static void G_GetNextMap(void)
|
|||
{
|
||||
gp_rank_e grade = K_CalculateGPGrade(&grandprixinfo.rank);
|
||||
|
||||
if (grade >= GRADE_A) // On A rank pace? Then you get a chance for S rank!
|
||||
if (grade >= GRADE_A && grandprixinfo.gamespeed >= KARTSPEED_NORMAL) // On A rank pace? Then you get a chance for S rank!
|
||||
{
|
||||
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_SPECIAL];
|
||||
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
|
||||
|
|
@ -3954,6 +3959,13 @@ static void G_GetNextMap(void)
|
|||
grandprixinfo.eventmode = GPEVENT_SPECIAL;
|
||||
nextmap = cupLevelNum;
|
||||
newgametype = G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel);
|
||||
|
||||
if (gamedata->everseenspecial == false)
|
||||
{
|
||||
gamedata->everseenspecial = true;
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4001,7 +4013,7 @@ static void G_GetNextMap(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
nextmap = prevmap; // Prevent uninitialised use
|
||||
nextmap = 0; // Prevent uninitialised use -- go to TEST RUN, it's very obvious
|
||||
}
|
||||
|
||||
grandprixinfo.roundnum++;
|
||||
|
|
@ -4164,6 +4176,33 @@ static void G_DoCompleted(void)
|
|||
if (modeattacking && pausedelay)
|
||||
pausedelay = 0;
|
||||
|
||||
// We do this here so Challenges-related sounds aren't insta-killed
|
||||
S_StopSounds();
|
||||
|
||||
if (legitimateexit && !demo.playback && !mapreset) // (yes you're allowed to unlock stuff this way when the game is modified)
|
||||
{
|
||||
UINT8 roundtype = GDGT_CUSTOM;
|
||||
|
||||
if (gametype == GT_RACE)
|
||||
roundtype = GDGT_RACE;
|
||||
else if (gametype == GT_BATTLE)
|
||||
roundtype = (battleprisons ? GDGT_PRISONS : GDGT_BATTLE);
|
||||
else if (gametype == GT_SPECIAL || gametype == GT_VERSUS)
|
||||
roundtype = GDGT_SPECIAL;
|
||||
|
||||
gamedata->roundsplayed[roundtype]++;
|
||||
gamedata->pendingkeyrounds++;
|
||||
|
||||
// Done before forced addition of PF_NOCONTEST to make UCRP_NOCONTEST harder to achieve
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
gamedata->deferredsave = true;
|
||||
}
|
||||
|
||||
if (gamedata->deferredsave)
|
||||
G_SaveGameData();
|
||||
|
||||
legitimateexit = false;
|
||||
|
||||
gameaction = ga_nothing;
|
||||
|
||||
if (metalplayback)
|
||||
|
|
@ -4174,7 +4213,7 @@ static void G_DoCompleted(void)
|
|||
G_SetGamestate(GS_NULL);
|
||||
wipegamestate = GS_NULL;
|
||||
|
||||
grandprixinfo.rank.capsules += numtargets;
|
||||
grandprixinfo.rank.prisons += numtargets;
|
||||
grandprixinfo.rank.position = MAXPLAYERS;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
|
|
@ -4218,8 +4257,6 @@ static void G_DoCompleted(void)
|
|||
if (automapactive)
|
||||
AM_Stop();
|
||||
|
||||
S_StopSounds();
|
||||
|
||||
prevmap = (INT16)(gamemap-1);
|
||||
|
||||
if (!demo.playback)
|
||||
|
|
@ -4486,7 +4523,7 @@ void G_LoadGameSettings(void)
|
|||
}
|
||||
|
||||
#define GD_VERSIONCHECK 0xBA5ED123 // Change every major version, as usual
|
||||
#define GD_VERSIONMINOR 1 // Change every format update
|
||||
#define GD_VERSIONMINOR 2 // Change every format update
|
||||
|
||||
static const char *G_GameDataFolder(void)
|
||||
{
|
||||
|
|
@ -4509,36 +4546,36 @@ void G_LoadGameData(void)
|
|||
|
||||
//For records
|
||||
UINT32 numgamedatamapheaders;
|
||||
UINT32 numgamedatacups;
|
||||
|
||||
// Stop saving, until we successfully load it again.
|
||||
gamedata->loaded = false;
|
||||
|
||||
// Clear things so previously read gamedata doesn't transfer
|
||||
// to new gamedata
|
||||
// see also M_EraseDataResponse
|
||||
G_ClearRecords(); // records
|
||||
M_ClearStats(); // statistics
|
||||
M_ClearSecrets(); // emblems, unlocks, maps visited, etc
|
||||
|
||||
gamedata->totalplaytime = 0; // total play time (separate from all)
|
||||
gamedata->matchesplayed = 0; // SRB2Kart: matches played & finished
|
||||
|
||||
if (M_CheckParm("-nodata"))
|
||||
{
|
||||
// Don't load at all.
|
||||
// The following used to be in M_ClearSecrets, but that was silly.
|
||||
M_UpdateUnlockablesAndExtraEmblems(false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (M_CheckParm("-resetdata"))
|
||||
{
|
||||
// Don't load, but do save. (essentially, reset)
|
||||
gamedata->loaded = true;
|
||||
return;
|
||||
goto finalisegamedata;
|
||||
}
|
||||
|
||||
if (P_SaveBufferFromFile(&save, va(pandf, srb2home, gamedatafilename)) == false)
|
||||
{
|
||||
// No gamedata. We can save a new one.
|
||||
gamedata->loaded = true;
|
||||
return;
|
||||
goto finalisegamedata;
|
||||
}
|
||||
|
||||
// Version check
|
||||
|
|
@ -4559,13 +4596,44 @@ void G_LoadGameData(void)
|
|||
P_SaveBufferFree(&save);
|
||||
I_Error("Game data is from the future! (expected %d, got %d)\nRename or delete %s (maybe in %s) and try again.", GD_VERSIONMINOR, versionMinor, gamedatafilename, gdfolder);
|
||||
}
|
||||
if (versionMinor == 0)
|
||||
if ((versionMinor == 0 || versionMinor == 1)
|
||||
#ifdef DEVELOP
|
||||
|| M_CheckParm("-resetchallengegrid")
|
||||
#endif
|
||||
)
|
||||
{
|
||||
gridunusable = true;
|
||||
}
|
||||
|
||||
if (versionMinor > 1)
|
||||
{
|
||||
gamedata->evercrashed = (boolean)READUINT8(save.p);
|
||||
}
|
||||
|
||||
gamedata->totalplaytime = READUINT32(save.p);
|
||||
gamedata->matchesplayed = READUINT32(save.p);
|
||||
|
||||
if (versionMinor > 1)
|
||||
{
|
||||
gamedata->totalrings = READUINT32(save.p);
|
||||
|
||||
for (i = 0; i < GDGT_MAX; i++)
|
||||
{
|
||||
gamedata->roundsplayed[i] = READUINT32(save.p);
|
||||
}
|
||||
|
||||
gamedata->pendingkeyrounds = READUINT32(save.p);
|
||||
gamedata->pendingkeyroundoffset = READUINT8(save.p);
|
||||
gamedata->keyspending = READUINT8(save.p);
|
||||
gamedata->chaokeys = READUINT16(save.p);
|
||||
|
||||
gamedata->everloadedaddon = (boolean)READUINT8(save.p);
|
||||
gamedata->eversavedreplay = (boolean)READUINT8(save.p);
|
||||
gamedata->everseenspecial = (boolean)READUINT8(save.p);
|
||||
}
|
||||
else
|
||||
{
|
||||
save.p += 4; // no direct equivalent to matchesplayed
|
||||
}
|
||||
|
||||
{
|
||||
// Quick & dirty hash for what mod this save file is for.
|
||||
|
|
@ -4672,7 +4740,7 @@ void G_LoadGameData(void)
|
|||
G_AllocMainRecordData((INT16)i);
|
||||
mapheaderinfo[i]->mainrecord->time = rectime;
|
||||
mapheaderinfo[i]->mainrecord->lap = reclap;
|
||||
CONS_Printf("ID %d, Time = %d, Lap = %d\n", i, rectime/35, reclap/35);
|
||||
//CONS_Printf("ID %d, Time = %d, Lap = %d\n", i, rectime/35, reclap/35);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -4683,19 +4751,62 @@ void G_LoadGameData(void)
|
|||
}
|
||||
}
|
||||
|
||||
if (versionMinor > 1)
|
||||
{
|
||||
numgamedatacups = READUINT32(save.p);
|
||||
|
||||
for (i = 0; i < numgamedatacups; i++)
|
||||
{
|
||||
char cupname[16];
|
||||
cupheader_t *cup;
|
||||
|
||||
// Find the relevant cup.
|
||||
READSTRINGN(save.p, cupname, sizeof(cupname));
|
||||
for (cup = kartcupheaders; cup; cup = cup->next)
|
||||
{
|
||||
if (strcmp(cup->name, cupname))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
// Digest its data...
|
||||
for (j = 0; j < KARTGP_MAX; j++)
|
||||
{
|
||||
rtemp = READUINT8(save.p);
|
||||
|
||||
// ...but only record it if we actually found the associated cup.
|
||||
if (cup)
|
||||
{
|
||||
cup->windata[j].best_placement = (rtemp & 0x0F);
|
||||
cup->windata[j].best_grade = (rtemp & 0x70)>>4;
|
||||
if (rtemp & 0x80)
|
||||
{
|
||||
if (j == 0)
|
||||
goto datacorrupt;
|
||||
|
||||
cup->windata[j].got_emerald = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
P_SaveBufferFree(&save);
|
||||
|
||||
// Don't consider loaded until it's a success!
|
||||
// It used to do this much earlier, but this would cause the gamedata to
|
||||
// save over itself when it I_Errors from the corruption landing point below,
|
||||
// which can accidentally delete players' legitimate data if the code ever has any tiny mistakes!
|
||||
gamedata->loaded = true;
|
||||
finalisegamedata:
|
||||
{
|
||||
// Don't consider loaded until it's a success!
|
||||
// It used to do this much earlier, but this would cause the gamedata to
|
||||
// save over itself when it I_Errors from the corruption landing point below,
|
||||
// which can accidentally delete players' legitimate data if the code ever has any tiny mistakes!
|
||||
gamedata->loaded = true;
|
||||
|
||||
// Silent update unlockables in case they're out of sync with conditions
|
||||
M_UpdateUnlockablesAndExtraEmblems(false);
|
||||
// Silent update unlockables in case they're out of sync with conditions
|
||||
M_UpdateUnlockablesAndExtraEmblems(false, true);
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
// Landing point for corrupt gamedata
|
||||
datacorrupt:
|
||||
|
|
@ -4710,18 +4821,46 @@ void G_LoadGameData(void)
|
|||
}
|
||||
}
|
||||
|
||||
// G_DirtyGameData
|
||||
// Modifies the gamedata as little as possible to maintain safety in a crash event, while still recording it.
|
||||
void G_DirtyGameData(void)
|
||||
{
|
||||
FILE *handle = NULL;
|
||||
const UINT8 writebytesource = true;
|
||||
|
||||
if (gamedata)
|
||||
gamedata->evercrashed = true;
|
||||
|
||||
//if (FIL_WriteFileOK(name))
|
||||
handle = fopen(va(pandf, srb2home, gamedatafilename), "r+");
|
||||
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
// Write a dirty byte immediately after the gamedata check + minor version.
|
||||
if (fseek(handle, 5, SEEK_SET) != -1)
|
||||
fwrite(&writebytesource, 1, 1, handle);
|
||||
|
||||
fclose(handle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// G_SaveGameData
|
||||
// Saves the main data file, which stores information such as emblems found, etc.
|
||||
void G_SaveGameData(void)
|
||||
{
|
||||
size_t length;
|
||||
INT32 i, j;
|
||||
INT32 i, j, numcups;
|
||||
cupheader_t *cup;
|
||||
UINT8 btemp;
|
||||
savebuffer_t save = {0};
|
||||
|
||||
if (gamedata == NULL || !gamedata->loaded)
|
||||
return; // If never loaded (-nodata), don't save
|
||||
|
||||
gamedata->deferredsave = false;
|
||||
|
||||
if (usedCheats)
|
||||
{
|
||||
#ifdef DEVELOP
|
||||
|
|
@ -4730,12 +4869,27 @@ void G_SaveGameData(void)
|
|||
return;
|
||||
}
|
||||
|
||||
length = (4+1+4+4+1+(MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+4+4+2);
|
||||
length = (4+1+1+
|
||||
4+4+
|
||||
(4*GDGT_MAX)+
|
||||
4+1+1+2+
|
||||
1+1+1+
|
||||
4+
|
||||
(MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+
|
||||
4+2);
|
||||
|
||||
if (gamedata->challengegrid)
|
||||
{
|
||||
length += gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT;
|
||||
}
|
||||
length += nummapheaders * (MAXMAPLUMPNAME+1+4+4);
|
||||
length += 4 + (nummapheaders * (MAXMAPLUMPNAME+1+4+4));
|
||||
|
||||
numcups = 0;
|
||||
for (cup = kartcupheaders; cup; cup = cup->next)
|
||||
{
|
||||
numcups++;
|
||||
}
|
||||
length += 4 + (numcups * (4+16));
|
||||
|
||||
if (P_SaveBufferAlloc(&save, length) == false)
|
||||
{
|
||||
|
|
@ -4747,8 +4901,30 @@ void G_SaveGameData(void)
|
|||
|
||||
WRITEUINT32(save.p, GD_VERSIONCHECK); // 4
|
||||
WRITEUINT8(save.p, GD_VERSIONMINOR); // 1
|
||||
|
||||
// Crash dirtiness
|
||||
// cannot move, see G_DirtyGameData
|
||||
WRITEUINT8(save.p, gamedata->evercrashed); // 1
|
||||
|
||||
// Statistics
|
||||
|
||||
WRITEUINT32(save.p, gamedata->totalplaytime); // 4
|
||||
WRITEUINT32(save.p, gamedata->matchesplayed); // 4
|
||||
WRITEUINT32(save.p, gamedata->totalrings); // 4
|
||||
|
||||
for (i = 0; i < GDGT_MAX; i++) // 4 * GDGT_MAX
|
||||
{
|
||||
WRITEUINT32(save.p, gamedata->roundsplayed[i]);
|
||||
}
|
||||
|
||||
WRITEUINT32(save.p, gamedata->pendingkeyrounds); // 4
|
||||
WRITEUINT8(save.p, gamedata->pendingkeyroundoffset); // 1
|
||||
WRITEUINT8(save.p, gamedata->keyspending); // 1
|
||||
WRITEUINT16(save.p, gamedata->chaokeys); // 2
|
||||
|
||||
WRITEUINT8(save.p, gamedata->everloadedaddon); // 1
|
||||
WRITEUINT8(save.p, gamedata->eversavedreplay); // 1
|
||||
WRITEUINT8(save.p, gamedata->everseenspecial); // 1
|
||||
|
||||
WRITEUINT32(save.p, quickncasehash(timeattackfolder, 64));
|
||||
|
||||
// To save space, use one bit per collected/achieved/unlocked flag
|
||||
|
|
@ -4808,7 +4984,7 @@ void G_SaveGameData(void)
|
|||
|
||||
for (i = 0; i < nummapheaders; i++) // nummapheaders * (255+1+4+4)
|
||||
{
|
||||
// For figuring out which header to assing it to on load
|
||||
// For figuring out which header to assign it to on load
|
||||
WRITESTRINGN(save.p, mapheaderinfo[i]->lumpname, MAXMAPLUMPNAME);
|
||||
|
||||
WRITEUINT8(save.p, (mapheaderinfo[i]->mapvisited & MV_MAX));
|
||||
|
|
@ -4825,6 +5001,24 @@ void G_SaveGameData(void)
|
|||
}
|
||||
}
|
||||
|
||||
WRITEUINT32(save.p, numcups); // 4
|
||||
|
||||
for (cup = kartcupheaders; cup; cup = cup->next)
|
||||
{
|
||||
// For figuring out which header to assign it to on load
|
||||
WRITESTRINGN(save.p, cup->name, 16);
|
||||
|
||||
for (i = 0; i < KARTGP_MAX; i++)
|
||||
{
|
||||
btemp = min(cup->windata[i].best_placement, 0x0F);
|
||||
btemp |= (cup->windata[i].best_grade<<4);
|
||||
if (i != 0 && cup->windata[i].got_emerald == true)
|
||||
btemp |= 0x80;
|
||||
|
||||
WRITEUINT8(save.p, btemp); // 4 * numcups
|
||||
}
|
||||
}
|
||||
|
||||
length = save.p - save.buffer;
|
||||
|
||||
FIL_WriteFile(va(pandf, srb2home, gamedatafilename), save.buffer, length);
|
||||
|
|
@ -5175,9 +5369,6 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr
|
|||
}
|
||||
}
|
||||
|
||||
// Reset unlockable triggers
|
||||
unlocktriggers = 0;
|
||||
|
||||
// clear itemfinder, just in case
|
||||
if (!dedicated) // except in dedicated servers, where it is not registered and can actually I_Error debug builds
|
||||
CV_StealthSetValue(&cv_itemfinder, 0);
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ boolean G_IsTitleCardAvailable(void);
|
|||
void G_LoadGame(UINT32 slot, INT16 mapoverride);
|
||||
|
||||
void G_SaveGameData(void);
|
||||
void G_DirtyGameData(void);
|
||||
|
||||
void G_SaveGame(UINT32 slot, INT16 mapnum);
|
||||
|
||||
|
|
|
|||
|
|
@ -2474,8 +2474,8 @@ static void HU_DrawRankings(void)
|
|||
// draw the current gametype in the lower right
|
||||
if (grandprixinfo.gp == true)
|
||||
V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, "Grand Prix");
|
||||
else if (battlecapsules)
|
||||
V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, "Capsules");
|
||||
else if (battleprisons)
|
||||
V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, "Prisons");
|
||||
else if (gametype >= 0 && gametype < numgametypes)
|
||||
V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, gametypes[gametype]->name);
|
||||
|
||||
|
|
@ -2533,11 +2533,11 @@ static void HU_DrawRankings(void)
|
|||
}
|
||||
|
||||
// Right hand side
|
||||
if (battlecapsules == true)
|
||||
if (battleprisons == true)
|
||||
{
|
||||
if (numtargets < maptargets)
|
||||
{
|
||||
V_DrawCenteredString(256, 8, 0, "CAPSULES");
|
||||
V_DrawCenteredString(256, 8, 0, "PRISONS");
|
||||
V_DrawCenteredString(256, 16, hilicol, va("%d", maptargets - numtargets));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
struct battleovertime battleovertime;
|
||||
|
||||
// Capsules mode enabled for this map?
|
||||
boolean battlecapsules = false;
|
||||
boolean battleprisons = false;
|
||||
|
||||
// box respawning in battle mode
|
||||
INT32 nummapboxes = 0;
|
||||
|
|
@ -38,8 +38,8 @@ UINT8 numtargets = 0; // Capsules busted
|
|||
|
||||
INT32 K_StartingBumperCount(void)
|
||||
{
|
||||
if (battlecapsules)
|
||||
return 0; // always 1 hit in Break the Capsules
|
||||
if (battleprisons)
|
||||
return 0; // always 1 hit in Prison Break
|
||||
|
||||
return cv_kartbumpers.value;
|
||||
}
|
||||
|
|
@ -143,7 +143,7 @@ void K_CheckBumpers(void)
|
|||
|
||||
if (numingame <= 1)
|
||||
{
|
||||
if ((gametyperules & GTR_CAPSULES) && (K_CanChangeRules(true) == true))
|
||||
if ((gametyperules & GTR_PRISONS) && (K_CanChangeRules(true) == true))
|
||||
{
|
||||
// Reset map to turn on battle capsules
|
||||
if (server)
|
||||
|
|
@ -343,7 +343,7 @@ void K_RunPaperItemSpawners(void)
|
|||
UINT8 pcount = 0;
|
||||
INT16 i;
|
||||
|
||||
if (battlecapsules)
|
||||
if (battleprisons)
|
||||
{
|
||||
// Gametype uses paper items, but this specific expression doesn't
|
||||
return;
|
||||
|
|
@ -771,7 +771,7 @@ void K_BattleInit(boolean singleplayercontext)
|
|||
{
|
||||
size_t i;
|
||||
|
||||
if ((gametyperules & GTR_CAPSULES) && singleplayercontext && !battlecapsules)
|
||||
if ((gametyperules & GTR_PRISONS) && singleplayercontext && !battleprisons)
|
||||
{
|
||||
mapthing_t *mt = mapthings;
|
||||
for (i = 0; i < nummapthings; i++, mt++)
|
||||
|
|
@ -782,7 +782,7 @@ void K_BattleInit(boolean singleplayercontext)
|
|||
maptargets++;
|
||||
}
|
||||
|
||||
battlecapsules = true;
|
||||
battleprisons = true;
|
||||
}
|
||||
|
||||
if (gametyperules & GTR_BUMPERS)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ extern struct battleovertime
|
|||
fixed_t x, y, z; ///< Position to center on
|
||||
} battleovertime;
|
||||
|
||||
extern boolean battlecapsules;
|
||||
extern boolean battleprisons;
|
||||
extern INT32 nummapboxes, numgotboxes; // keep track of spawned battle mode items
|
||||
extern UINT8 maptargets, numtargets;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SONIC ROBO BLAST 2 KART
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2018-2022 by Viv "toaster" Grannell
|
||||
// Copyright (C) 2018-2022 by Kart Krew
|
||||
// Copyright (C) 2018-2023 by Vivian "toastergrl" Grannell
|
||||
// Copyright (C) 2018-2023 by Kart Krew
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SONIC ROBO BLAST 2 KART
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2018-2022 by Viv "toaster" Grannell
|
||||
// Copyright (C) 2018-2022 by Kart Krew
|
||||
// Copyright (C) 2018-2023 by Vivian "toastergrl" Grannell
|
||||
// Copyright (C) 2018-2023 by Kart Krew
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
|
|
|
|||
64
src/k_bot.c
64
src/k_bot.c
|
|
@ -108,13 +108,15 @@ boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p)
|
|||
--------------------------------------------------*/
|
||||
void K_UpdateMatchRaceBots(void)
|
||||
{
|
||||
const UINT8 defaultbotskin = R_BotDefaultSkin();
|
||||
const UINT8 difficulty = cv_kartbot.value;
|
||||
UINT8 pmax = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value);
|
||||
UINT8 numplayers = 0;
|
||||
UINT8 numbots = 0;
|
||||
UINT8 numwaiting = 0;
|
||||
SINT8 wantedbots = 0;
|
||||
boolean skinusable[MAXSKINS];
|
||||
UINT8 usableskins = 0;
|
||||
UINT8 grabskins[MAXSKINS+1];
|
||||
UINT8 i;
|
||||
|
||||
if (!server)
|
||||
|
|
@ -122,18 +124,12 @@ void K_UpdateMatchRaceBots(void)
|
|||
return;
|
||||
}
|
||||
|
||||
// init usable bot skins list
|
||||
for (i = 0; i < MAXSKINS; i++)
|
||||
// Init usable bot skins list
|
||||
for (i = 0; i < numskins; i++)
|
||||
{
|
||||
if (i < numskins)
|
||||
{
|
||||
skinusable[i] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
skinusable[i] = false;
|
||||
}
|
||||
grabskins[usableskins++] = i;
|
||||
}
|
||||
grabskins[usableskins] = MAXSKINS;
|
||||
|
||||
if (cv_maxplayers.value > 0)
|
||||
{
|
||||
|
|
@ -146,7 +142,7 @@ void K_UpdateMatchRaceBots(void)
|
|||
{
|
||||
if (!players[i].spectator)
|
||||
{
|
||||
skinusable[players[i].skin] = false;
|
||||
grabskins[players[i].skin] = MAXSKINS;
|
||||
|
||||
if (players[i].bot)
|
||||
{
|
||||
|
|
@ -185,48 +181,42 @@ void K_UpdateMatchRaceBots(void)
|
|||
{
|
||||
// We require MORE bots!
|
||||
UINT8 newplayernum = 0;
|
||||
boolean usedallskins = false;
|
||||
|
||||
if (dedicated)
|
||||
{
|
||||
newplayernum = 1;
|
||||
}
|
||||
|
||||
// Rearrange usable bot skins list to prevent gaps for randomised selection
|
||||
for (i = 0; i < usableskins; i++)
|
||||
{
|
||||
if (!(grabskins[i] == MAXSKINS || !R_SkinUsable(-1, grabskins[i], true)))
|
||||
continue;
|
||||
while (usableskins > i && (grabskins[usableskins] == MAXSKINS || !R_SkinUsable(-1, grabskins[usableskins], true)))
|
||||
{
|
||||
usableskins--;
|
||||
}
|
||||
grabskins[i] = grabskins[usableskins];
|
||||
grabskins[usableskins] = MAXSKINS;
|
||||
}
|
||||
|
||||
while (numbots < wantedbots)
|
||||
{
|
||||
UINT8 skin = M_RandomKey(numskins);
|
||||
UINT8 skinnum = defaultbotskin;
|
||||
|
||||
if (usedallskins == false)
|
||||
if (usableskins > 0)
|
||||
{
|
||||
UINT8 loops = 0;
|
||||
|
||||
while (!skinusable[skin])
|
||||
{
|
||||
if (loops >= numskins)
|
||||
{
|
||||
// no more skins, stick to our first choice
|
||||
usedallskins = true;
|
||||
break;
|
||||
}
|
||||
|
||||
skin++;
|
||||
|
||||
if (skin >= numskins)
|
||||
{
|
||||
skin = 0;
|
||||
}
|
||||
|
||||
loops++;
|
||||
}
|
||||
UINT8 index = M_RandomKey(usableskins);
|
||||
skinnum = grabskins[index];
|
||||
grabskins[index] = grabskins[--usableskins];
|
||||
}
|
||||
|
||||
if (!K_AddBot(skin, difficulty, &newplayernum))
|
||||
if (!K_AddBot(skinnum, difficulty, &newplayernum))
|
||||
{
|
||||
// Not enough player slots to add the bot, break the loop.
|
||||
break;
|
||||
}
|
||||
|
||||
skinusable[skin] = false;
|
||||
numbots++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -406,7 +406,15 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2)
|
|||
|
||||
// Banana snipe!
|
||||
if (t1->health > 1)
|
||||
{
|
||||
if (t1->target && t1->target->player)
|
||||
{
|
||||
t1->target->player->roundconditions.landmine_dunk = true;
|
||||
t1->target->player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
|
||||
S_StartSound(t2, sfx_bsnipe);
|
||||
}
|
||||
|
||||
if (t2->player->flamedash && t2->player->itemtype == KITEM_FLAMESHIELD)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -95,25 +95,6 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers)
|
|||
return points;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
UINT8 K_BotDefaultSkin(void)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
UINT8 K_BotDefaultSkin(void)
|
||||
{
|
||||
const char *defaultbotskinname = "eggrobo";
|
||||
INT32 defaultbotskin = R_SkinAvailable(defaultbotskinname);
|
||||
|
||||
if (defaultbotskin == -1)
|
||||
{
|
||||
// This shouldn't happen, but just in case
|
||||
defaultbotskin = 0;
|
||||
}
|
||||
|
||||
return (UINT8)defaultbotskin;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
UINT8 K_GetGPPlayerCount(UINT8 humans)
|
||||
|
||||
|
|
@ -139,7 +120,7 @@ UINT8 K_GetGPPlayerCount(UINT8 humans)
|
|||
--------------------------------------------------*/
|
||||
void K_InitGrandPrixBots(void)
|
||||
{
|
||||
const UINT8 defaultbotskin = K_BotDefaultSkin();
|
||||
const UINT8 defaultbotskin = R_BotDefaultSkin();
|
||||
|
||||
const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed);
|
||||
UINT8 difficultylevels[MAXPLAYERS];
|
||||
|
|
@ -532,7 +513,7 @@ void K_IncreaseBotDifficulty(player_t *bot)
|
|||
--------------------------------------------------*/
|
||||
void K_RetireBots(void)
|
||||
{
|
||||
const UINT8 defaultbotskin = K_BotDefaultSkin();
|
||||
const UINT8 defaultbotskin = R_BotDefaultSkin();
|
||||
SINT8 newDifficulty;
|
||||
|
||||
UINT8 usableskins;
|
||||
|
|
|
|||
|
|
@ -75,16 +75,6 @@ UINT8 K_BotStartingDifficulty(SINT8 value);
|
|||
INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
UINT8 K_BotDefaultSkin(void);
|
||||
|
||||
Returns the skin number of the skin the game
|
||||
uses as a fallback option.
|
||||
--------------------------------------------------*/
|
||||
|
||||
UINT8 K_BotDefaultSkin(void);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
UINT8 K_GetGPPlayerCount(UINT8 humans)
|
||||
|
||||
|
|
|
|||
12
src/k_hud.c
12
src/k_hud.c
|
|
@ -2855,7 +2855,7 @@ static void K_drawKartBumpersOrKarma(void)
|
|||
V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|V_SLIDEIN|splitflags|flipflag, kp_ringstickersplit[0]);
|
||||
V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|V_SLIDEIN|splitflags, frameslash);
|
||||
|
||||
if (battlecapsules)
|
||||
if (battleprisons)
|
||||
{
|
||||
V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|V_SLIDEIN|splitflags, kp_rankcapsule, NULL);
|
||||
|
||||
|
|
@ -2911,7 +2911,7 @@ static void K_drawKartBumpersOrKarma(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (battlecapsules)
|
||||
if (battleprisons)
|
||||
{
|
||||
if (numtargets > 9 && maptargets > 9)
|
||||
V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_capsulestickerwide, NULL);
|
||||
|
|
@ -3801,7 +3801,7 @@ static void K_drawKartMinimap(void)
|
|||
workingPic = kp_capsuleminimap[(mobj->extravalue1 != 0 ? 1 : 0)];
|
||||
break;
|
||||
case MT_CDUFO:
|
||||
if (battlecapsules) //!battleprisons
|
||||
if (battleprisons)
|
||||
workingPic = kp_capsuleminimap[2];
|
||||
break;
|
||||
default:
|
||||
|
|
@ -4244,7 +4244,7 @@ static void K_drawBattleFullscreen(void)
|
|||
|
||||
if (K_IsPlayerLosing(stplyr))
|
||||
p = kp_battlelose;
|
||||
else if (stplyr->position == 1 && (!battlecapsules || numtargets >= maptargets))
|
||||
else if (stplyr->position == 1 && (!battleprisons || numtargets >= maptargets))
|
||||
p = kp_battlewin;
|
||||
|
||||
V_DrawFixedPatch(x<<FRACBITS, y<<FRACBITS, scale, splitflags, p, NULL);
|
||||
|
|
@ -4940,7 +4940,7 @@ static void K_DrawGPRankDebugger(void)
|
|||
V_DrawThinString(0, 30, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
|
||||
va("CONTINUES: %d", grandprixinfo.rank.continuesUsed));
|
||||
V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
|
||||
va("CAPSULES: %d / %d", grandprixinfo.rank.capsules, grandprixinfo.rank.totalCapsules));
|
||||
va("PRISONS: %d / %d", grandprixinfo.rank.prisons, grandprixinfo.rank.totalPrisons));
|
||||
V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
|
||||
va("RINGS: %d / %d", grandprixinfo.rank.rings, grandprixinfo.rank.totalRings));
|
||||
V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
|
||||
|
|
@ -5090,7 +5090,7 @@ void K_drawKartHUD(void)
|
|||
;
|
||||
else if ((gametyperules & GTR_POWERSTONES))
|
||||
{
|
||||
if (!battlecapsules)
|
||||
if (!battleprisons)
|
||||
K_drawKartEmeralds();
|
||||
}
|
||||
else if (!islonesome)
|
||||
|
|
|
|||
|
|
@ -365,7 +365,7 @@ bool is_object_tracking_target(const mobj_t* mobj)
|
|||
{
|
||||
case MT_BATTLECAPSULE:
|
||||
case MT_CDUFO:
|
||||
return battlecapsules; // battleprisons
|
||||
return battleprisons;
|
||||
|
||||
case MT_SPECIAL_UFO:
|
||||
return true;
|
||||
|
|
|
|||
46
src/k_kart.c
46
src/k_kart.c
|
|
@ -371,7 +371,7 @@ boolean K_IsPlayerLosing(player_t *player)
|
|||
if (player->pflags & PF_NOCONTEST)
|
||||
return true;
|
||||
|
||||
if (battlecapsules && numtargets == 0)
|
||||
if (battleprisons && numtargets == 0)
|
||||
return true; // Didn't even TRY?
|
||||
|
||||
if (player->position == 1)
|
||||
|
|
@ -558,7 +558,7 @@ boolean K_TimeAttackRules(void)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (battlecapsules == true)
|
||||
if (battleprisons == true)
|
||||
{
|
||||
// Break the Capsules always uses Time Attack
|
||||
// rules, since you can bring 2-4 players in
|
||||
|
|
@ -1129,6 +1129,13 @@ static void K_UpdateOffroad(player_t *player)
|
|||
|
||||
if (player->offroad > offroadstrength)
|
||||
player->offroad = offroadstrength;
|
||||
|
||||
if (player->roundconditions.touched_offroad == false
|
||||
&& player->offroad > (2*offroadstrength) / TICRATE)
|
||||
{
|
||||
player->roundconditions.touched_offroad = true;
|
||||
player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
player->offroad = 0;
|
||||
|
|
@ -4223,7 +4230,15 @@ void K_ApplyTripWire(player_t *player, tripwirestate_t state)
|
|||
K_TumblePlayer(player, NULL, NULL);
|
||||
|
||||
if (state == TRIPSTATE_PASSED)
|
||||
{
|
||||
S_StartSound(player->mo, sfx_ssa015);
|
||||
if (player->roundconditions.tripwire_hyuu == false
|
||||
&& player->hyudorotimer > 0)
|
||||
{
|
||||
player->roundconditions.tripwire_hyuu = true;
|
||||
player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
}
|
||||
else if (state == TRIPSTATE_BLOCKED)
|
||||
{
|
||||
S_StartSound(player->mo, sfx_kc40);
|
||||
|
|
@ -5823,6 +5838,13 @@ void K_DoSneaker(player_t *player, INT32 type)
|
|||
{
|
||||
const fixed_t intendedboost = FRACUNIT/2;
|
||||
|
||||
if (player->roundconditions.touched_sneakerpanel == false
|
||||
&& player->floorboost != 0)
|
||||
{
|
||||
player->roundconditions.touched_sneakerpanel = true;
|
||||
player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
|
||||
if (player->floorboost == 0 || player->floorboost == 3)
|
||||
{
|
||||
const sfxenum_t normalsfx = sfx_cdfm01;
|
||||
|
|
@ -7067,7 +7089,7 @@ static void K_UpdateEngineSounds(player_t *player)
|
|||
|
||||
const UINT16 buttons = K_GetKartButtons(player);
|
||||
|
||||
INT32 class, s, w; // engine class number
|
||||
INT32 class; // engine class number
|
||||
|
||||
UINT8 volume = 255;
|
||||
fixed_t volumedampen = FRACUNIT;
|
||||
|
|
@ -7082,17 +7104,7 @@ static void K_UpdateEngineSounds(player_t *player)
|
|||
return;
|
||||
}
|
||||
|
||||
s = (player->kartspeed - 1) / 3;
|
||||
w = (player->kartweight - 1) / 3;
|
||||
|
||||
#define LOCKSTAT(stat) \
|
||||
if (stat < 0) { stat = 0; } \
|
||||
if (stat > 2) { stat = 2; }
|
||||
LOCKSTAT(s);
|
||||
LOCKSTAT(w);
|
||||
#undef LOCKSTAT
|
||||
|
||||
class = s + (3*w);
|
||||
class = R_GetEngineClass(player->kartspeed, player->kartweight, 0); // there are no unique sounds for ENGINECLASS_J
|
||||
|
||||
#if 0
|
||||
if ((leveltime % 8) != ((player-players) % 8)) // Per-player offset, to make engines sound distinct!
|
||||
|
|
@ -11603,7 +11615,7 @@ tic_t K_TimeLimitForGametype(void)
|
|||
// Grand Prix
|
||||
if (!K_CanChangeRules(true))
|
||||
{
|
||||
if (battlecapsules)
|
||||
if (battleprisons)
|
||||
{
|
||||
return 20*TICRATE;
|
||||
}
|
||||
|
|
@ -11617,7 +11629,7 @@ tic_t K_TimeLimitForGametype(void)
|
|||
}
|
||||
|
||||
// No time limit for Break the Capsules FREE PLAY
|
||||
if (battlecapsules)
|
||||
if (battleprisons)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -11668,7 +11680,7 @@ UINT32 K_PointLimitForGametype(void)
|
|||
|
||||
boolean K_Cooperative(void)
|
||||
{
|
||||
if (battlecapsules)
|
||||
if (battleprisons)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
14
src/k_menu.h
14
src/k_menu.h
|
|
@ -698,6 +698,7 @@ boolean M_CharacterSelectHandler(INT32 choice);
|
|||
void M_CharacterSelectTick(void);
|
||||
boolean M_CharacterSelectQuit(void);
|
||||
|
||||
void M_SetupPlayMenu(INT32 choice);
|
||||
void M_SetupGametypeMenu(INT32 choice);
|
||||
void M_SetupRaceMenu(INT32 choice);
|
||||
|
||||
|
|
@ -727,13 +728,16 @@ extern struct levellist_s {
|
|||
SINT8 cursor;
|
||||
UINT16 y;
|
||||
UINT16 dest;
|
||||
INT16 choosemap;
|
||||
UINT16 choosemap;
|
||||
UINT16 mapcount;
|
||||
UINT8 newgametype;
|
||||
UINT8 guessgt;
|
||||
levelsearch_t levelsearch;
|
||||
boolean netgame; // Start the game in an actual server
|
||||
} levellist;
|
||||
|
||||
extern cupheader_t dummy_lostandfound;
|
||||
|
||||
boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch);
|
||||
UINT16 M_CountLevelsToShowInList(levelsearch_t *levelsearch);
|
||||
UINT16 M_GetFirstLevelInList(UINT8 *i, levelsearch_t *levelsearch);
|
||||
|
|
@ -776,6 +780,7 @@ extern struct mpmenu_s {
|
|||
// See M_OptSelectTick, it'll make more sense there. Sorry if this is a bit of a mess!
|
||||
|
||||
UINT8 room;
|
||||
boolean roomforced;
|
||||
tic_t ticker;
|
||||
|
||||
UINT8 servernum;
|
||||
|
|
@ -1133,7 +1138,9 @@ void M_DrawAddons(void);
|
|||
#define CC_UNLOCKED 1
|
||||
#define CC_TALLY 2
|
||||
#define CC_ANIM 3
|
||||
#define CC_MAX 4
|
||||
#define CC_CHAOANIM 4
|
||||
#define CC_CHAONOPE 5
|
||||
#define CC_MAX 6
|
||||
|
||||
#define TILEFLIP_MAX 16
|
||||
|
||||
|
|
@ -1144,7 +1151,6 @@ extern struct timeattackmenu_s {
|
|||
|
||||
} timeattackmenu;
|
||||
|
||||
|
||||
// Keep track of some pause menu data for visual goodness.
|
||||
extern struct challengesmenu_s {
|
||||
|
||||
|
|
@ -1163,6 +1169,7 @@ extern struct challengesmenu_s {
|
|||
|
||||
boolean pending;
|
||||
boolean requestnew;
|
||||
boolean chaokeyadd;
|
||||
|
||||
boolean requestflip;
|
||||
|
||||
|
|
@ -1180,6 +1187,7 @@ boolean M_ChallengesInputs(INT32 ch);
|
|||
extern struct statisticsmenu_s {
|
||||
INT32 location;
|
||||
INT32 nummaps;
|
||||
INT32 numextramedals;
|
||||
INT32 maxscroll;
|
||||
UINT16 *maplist;
|
||||
} statisticsmenu;
|
||||
|
|
|
|||
547
src/k_menudraw.c
547
src/k_menudraw.c
|
|
@ -791,6 +791,8 @@ void M_DrawKartGamemodeMenu(void)
|
|||
|
||||
for (i = 0; i < currentMenu->numitems; i++)
|
||||
{
|
||||
INT32 type;
|
||||
|
||||
if (currentMenu->menuitems[i].status == IT_DISABLED)
|
||||
{
|
||||
continue;
|
||||
|
|
@ -807,9 +809,12 @@ void M_DrawKartGamemodeMenu(void)
|
|||
}
|
||||
}
|
||||
|
||||
switch (currentMenu->menuitems[i].status & IT_DISPLAY)
|
||||
type = (currentMenu->menuitems[i].status & IT_DISPLAY);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case IT_STRING:
|
||||
case IT_TRANSTEXT2:
|
||||
{
|
||||
UINT8 *colormap = NULL;
|
||||
|
||||
|
|
@ -823,7 +828,13 @@ void M_DrawKartGamemodeMenu(void)
|
|||
}
|
||||
|
||||
V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, W_CachePatchName("MENUPLTR", PU_CACHE), colormap);
|
||||
V_DrawGamemodeString(x + 16, y - 3, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text);
|
||||
V_DrawGamemodeString(x + 16, y - 3,
|
||||
(type == IT_TRANSTEXT2
|
||||
? V_TRANSLUCENT
|
||||
: 0
|
||||
)|V_ALLOWLOWERCASE,
|
||||
colormap,
|
||||
currentMenu->menuitems[i].text);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -1470,23 +1481,31 @@ static void M_DrawCharSelectPreview(UINT8 num)
|
|||
|
||||
if (p->showextra == true)
|
||||
{
|
||||
INT32 randomskin = 0;
|
||||
switch (p->mdepth)
|
||||
{
|
||||
case CSSTEP_CHARS: // Character Select grid
|
||||
V_DrawThinString(x-3, y+2, V_6WIDTHSPACE, va("Speed %u - Weight %u", p->gridx+1, p->gridy+1));
|
||||
break;
|
||||
case CSSTEP_ALTS: // Select clone
|
||||
case CSSTEP_READY:
|
||||
if (p->clonenum < setup_chargrid[p->gridx][p->gridy].numskins
|
||||
&& setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum] < numskins)
|
||||
{
|
||||
V_DrawThinString(x-3, y+2, V_6WIDTHSPACE,
|
||||
V_DrawThinString(x-3, y+12, V_6WIDTHSPACE,
|
||||
skins[setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]].name);
|
||||
randomskin = (skins[setup_chargrid[p->gridx][p->gridy].skinlist[p->clonenum]].flags & SF_IRONMAN);
|
||||
}
|
||||
else
|
||||
{
|
||||
V_DrawThinString(x-3, y+2, V_6WIDTHSPACE, va("BAD CLONENUM %u", p->clonenum));
|
||||
V_DrawThinString(x-3, y+12, V_6WIDTHSPACE, va("BAD CLONENUM %u", p->clonenum));
|
||||
}
|
||||
/* FALLTHRU */
|
||||
case CSSTEP_CHARS: // Character Select grid
|
||||
V_DrawThinString(x-3, y+2, V_6WIDTHSPACE, va("Class %c (s %c - w %c)",
|
||||
('A' + R_GetEngineClass(p->gridx+1, p->gridy+1, randomskin)),
|
||||
(randomskin
|
||||
? '?' : ('1'+p->gridx)),
|
||||
(randomskin
|
||||
? '?' : ('1'+p->gridy))
|
||||
));
|
||||
break;
|
||||
case CSSTEP_COLORS: // Select color
|
||||
if (p->color < numskincolors)
|
||||
|
|
@ -2066,7 +2085,11 @@ static void M_DrawCupTitle(INT16 y, levelsearch_t *levelsearch)
|
|||
|
||||
V_DrawScaledPatch(0, y, 0, W_CachePatchName("MENUHINT", PU_CACHE));
|
||||
|
||||
if (levelsearch->cup)
|
||||
if (levelsearch->cup == &dummy_lostandfound)
|
||||
{
|
||||
V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, y+6, 0, "Lost and Found");
|
||||
}
|
||||
else if (levelsearch->cup)
|
||||
{
|
||||
boolean unlocked = (M_GetFirstLevelInList(&temp, levelsearch) != NEXTMAP_INVALID);
|
||||
UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE);
|
||||
|
|
@ -2095,6 +2118,8 @@ static void M_DrawCupTitle(INT16 y, levelsearch_t *levelsearch)
|
|||
void M_DrawCupSelect(void)
|
||||
{
|
||||
UINT8 i, j, temp = 0;
|
||||
UINT8 *colormap = NULL;
|
||||
cupwindata_t *windata = NULL;
|
||||
levelsearch_t templevelsearch = levellist.levelsearch; // full copy
|
||||
|
||||
for (i = 0; i < CUPMENU_COLUMNS; i++)
|
||||
|
|
@ -2105,28 +2130,82 @@ void M_DrawCupSelect(void)
|
|||
patch_t *patch = NULL;
|
||||
INT16 x, y;
|
||||
INT16 icony = 7;
|
||||
char status = 'A';
|
||||
char monitor;
|
||||
INT32 rankx = 0;
|
||||
|
||||
if (!cupgrid.builtgrid[id])
|
||||
break;
|
||||
|
||||
templevelsearch.cup = cupgrid.builtgrid[id];
|
||||
|
||||
/*if (templevelsearch.cup->emeraldnum == 0)
|
||||
patch = W_CachePatchName("CUPMON3A", PU_CACHE);
|
||||
else*/ if (templevelsearch.cup->emeraldnum > 7)
|
||||
if (cupgrid.grandprix
|
||||
&& (cv_dummygpdifficulty.value >= 0 && cv_dummygpdifficulty.value < KARTGP_MAX))
|
||||
{
|
||||
patch = W_CachePatchName("CUPMON2A", PU_CACHE);
|
||||
icony = 5;
|
||||
UINT16 col = SKINCOLOR_NONE;
|
||||
|
||||
windata = &templevelsearch.cup->windata[cv_dummygpdifficulty.value];
|
||||
|
||||
switch (windata->best_placement)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
col = SKINCOLOR_GOLD;
|
||||
status = 'B';
|
||||
break;
|
||||
case 2:
|
||||
col = SKINCOLOR_SILVER;
|
||||
status = 'B';
|
||||
break;
|
||||
case 3:
|
||||
col = SKINCOLOR_BRONZE;
|
||||
status = 'B';
|
||||
break;
|
||||
default:
|
||||
col = SKINCOLOR_BEIGE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (col != SKINCOLOR_NONE)
|
||||
colormap = R_GetTranslationColormap(TC_RAINBOW, col, GTC_MENUCACHE);
|
||||
else
|
||||
colormap = NULL;
|
||||
}
|
||||
|
||||
if (templevelsearch.cup == &dummy_lostandfound)
|
||||
{
|
||||
// No cup? Lost and found!
|
||||
monitor = '0';
|
||||
}
|
||||
else
|
||||
patch = W_CachePatchName("CUPMON1A", PU_CACHE);
|
||||
{
|
||||
if (templevelsearch.cup->monitor < 10)
|
||||
{
|
||||
monitor = '0' + templevelsearch.cup->monitor;
|
||||
|
||||
if (monitor == '2')
|
||||
{
|
||||
icony = 5;
|
||||
rankx = 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
monitor = 'A' + (templevelsearch.cup->monitor - 10);
|
||||
}
|
||||
}
|
||||
|
||||
patch = W_CachePatchName(va("CUPMON%c%c", monitor, status), PU_CACHE);
|
||||
|
||||
x = 14 + (i*42);
|
||||
y = 20 + (j*44) - (30*menutransition.tics);
|
||||
|
||||
V_DrawScaledPatch(x, y, 0, patch);
|
||||
V_DrawFixedPatch((x)*FRACUNIT, (y)<<FRACBITS, FRACUNIT, 0, patch, colormap);
|
||||
|
||||
if (M_GetFirstLevelInList(&temp, &templevelsearch) == NEXTMAP_INVALID)
|
||||
if (templevelsearch.cup == &dummy_lostandfound)
|
||||
; // Only ever placed on the list if valid
|
||||
else if (M_GetFirstLevelInList(&temp, &templevelsearch) == NEXTMAP_INVALID)
|
||||
{
|
||||
patch_t *st = W_CachePatchName(va("ICONST0%d", (cupgrid.previewanim % 4) + 1), PU_CACHE);
|
||||
V_DrawScaledPatch(x + 8, y + icony, 0, st);
|
||||
|
|
@ -2135,6 +2214,39 @@ void M_DrawCupSelect(void)
|
|||
{
|
||||
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName(templevelsearch.cup->icon, PU_CACHE));
|
||||
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName("CUPBOX", PU_CACHE));
|
||||
|
||||
if (!windata)
|
||||
;
|
||||
else if (windata->best_placement != 0)
|
||||
{
|
||||
char gradeChar = '?';
|
||||
|
||||
switch (windata->best_grade)
|
||||
{
|
||||
case GRADE_E: { gradeChar = 'E'; break; }
|
||||
case GRADE_D: { gradeChar = 'D'; break; }
|
||||
case GRADE_C: { gradeChar = 'C'; break; }
|
||||
case GRADE_B: { gradeChar = 'B'; break; }
|
||||
case GRADE_A: { gradeChar = 'A'; break; }
|
||||
case GRADE_S: { gradeChar = 'S'; break; }
|
||||
default: { break; }
|
||||
}
|
||||
|
||||
V_DrawCharacter(x + 5 + rankx, y + icony + 14, gradeChar, false); // rank
|
||||
|
||||
if (windata->got_emerald == true)
|
||||
{
|
||||
if (templevelsearch.cup->emeraldnum == 0)
|
||||
V_DrawCharacter(x + 26 - rankx, y + icony + 14, '*', false); // rank
|
||||
else
|
||||
{
|
||||
UINT16 col = SKINCOLOR_CHAOSEMERALD1 + (templevelsearch.cup->emeraldnum-1) % 7;
|
||||
colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);
|
||||
|
||||
V_DrawFixedPatch((x + 26 - rankx)*FRACUNIT, (y + icony + 13)*FRACUNIT, FRACUNIT, 0, W_CachePatchName("K_EMERC", PU_CACHE), colormap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2320,7 +2432,7 @@ void M_DrawLevelSelect(void)
|
|||
|
||||
void M_DrawTimeAttack(void)
|
||||
{
|
||||
INT16 map = levellist.choosemap;
|
||||
UINT16 map = levellist.choosemap;
|
||||
INT16 t = (48*menutransition.tics);
|
||||
INT16 leftedge = 149+t+16;
|
||||
INT16 rightedge = 149+t+155;
|
||||
|
|
@ -2366,7 +2478,7 @@ void M_DrawTimeAttack(void)
|
|||
K_drawKartTimestamp(timerec, 162+t, timeheight+6, 0, 1);
|
||||
|
||||
// SPB Attack control hint + menu overlay
|
||||
if (levellist.newgametype == GT_RACE && levellist.levelsearch.timeattack == true)
|
||||
if (levellist.newgametype == GT_RACE && levellist.levelsearch.timeattack == true && M_SecretUnlocked(SECRET_SPBATTACK, true))
|
||||
{
|
||||
const UINT8 anim_duration = 16;
|
||||
const UINT8 anim = (timeattackmenu.ticker % (anim_duration * 2)) < anim_duration;
|
||||
|
|
@ -2379,10 +2491,9 @@ void M_DrawTimeAttack(void)
|
|||
else
|
||||
V_DrawScaledPatch(buttonx + 35, buttony - 3, V_SNAPTOLEFT, W_CachePatchName("TLB_IB", PU_CACHE));
|
||||
|
||||
if (timeattackmenu.ticker > (timeattackmenu.spbflicker + TICRATE/6) || timeattackmenu.ticker % 2)
|
||||
if ((timeattackmenu.spbflicker == 0 || timeattackmenu.ticker % 2) == (cv_dummyspbattack.value == 1))
|
||||
{
|
||||
if (cv_dummyspbattack.value)
|
||||
V_DrawMappedPatch(buttonx + 7, buttony - 1, 0, W_CachePatchName("K_SPBATK", PU_CACHE), R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_RED, GTC_MENUCACHE));
|
||||
V_DrawMappedPatch(buttonx + 7, buttony - 1, 0, W_CachePatchName("K_SPBATK", PU_CACHE), R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_RED, GTC_MENUCACHE));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2776,8 +2887,12 @@ void M_DrawMPRoomSelect(void)
|
|||
|
||||
|
||||
// Draw buttons:
|
||||
V_DrawFixedPatch(160<<FRACBITS, 100<<FRACBITS, FRACUNIT, mpmenu.room ? (5<<V_ALPHASHIFT) : 0, butt1[(mpmenu.room) ? 1 : 0], NULL);
|
||||
V_DrawFixedPatch(160<<FRACBITS, 100<<FRACBITS, FRACUNIT, (!mpmenu.room) ? (5<<V_ALPHASHIFT) : 0, butt2[(!mpmenu.room) ? 1 : 0], NULL);
|
||||
|
||||
if (!mpmenu.roomforced || mpmenu.room == 0)
|
||||
V_DrawFixedPatch(160<<FRACBITS, 100<<FRACBITS, FRACUNIT, mpmenu.room ? (5<<V_ALPHASHIFT) : 0, butt1[(mpmenu.room) ? 1 : 0], NULL);
|
||||
|
||||
if (!mpmenu.roomforced || mpmenu.room == 1)
|
||||
V_DrawFixedPatch(160<<FRACBITS, 100<<FRACBITS, FRACUNIT, (!mpmenu.room) ? (5<<V_ALPHASHIFT) : 0, butt2[(!mpmenu.room) ? 1 : 0], NULL);
|
||||
}
|
||||
|
||||
// SERVER BROWSER
|
||||
|
|
@ -4731,18 +4846,22 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, boolean hili
|
|||
case SECRET_CUP:
|
||||
categoryid = '4';
|
||||
break;
|
||||
//case SECRET_MASTERBOTS:
|
||||
case SECRET_HARDSPEED:
|
||||
case SECRET_MASTERMODE:
|
||||
case SECRET_ENCORE:
|
||||
categoryid = '5';
|
||||
break;
|
||||
case SECRET_ONLINE:
|
||||
case SECRET_ADDONS:
|
||||
case SECRET_EGGTV:
|
||||
case SECRET_ALTTITLE:
|
||||
case SECRET_SOUNDTEST:
|
||||
categoryid = '6';
|
||||
break;
|
||||
case SECRET_TIMEATTACK:
|
||||
case SECRET_BREAKTHECAPSULES:
|
||||
case SECRET_PRISONBREAK:
|
||||
case SECRET_SPECIALATTACK:
|
||||
case SECRET_SPBATTACK:
|
||||
categoryid = '7';
|
||||
break;
|
||||
}
|
||||
|
|
@ -4793,16 +4912,25 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, boolean hili
|
|||
break;
|
||||
}
|
||||
|
||||
/*case SECRET_MASTERBOTS:
|
||||
iconid = 4;
|
||||
break;*/
|
||||
case SECRET_HARDSPEED:
|
||||
iconid = 3;
|
||||
break;
|
||||
case SECRET_MASTERMODE:
|
||||
iconid = 4;
|
||||
break;
|
||||
case SECRET_ENCORE:
|
||||
iconid = 5;
|
||||
break;
|
||||
|
||||
case SECRET_ONLINE:
|
||||
iconid = 10;
|
||||
break;
|
||||
case SECRET_ADDONS:
|
||||
iconid = 12;
|
||||
break;
|
||||
case SECRET_EGGTV:
|
||||
iconid = 11;
|
||||
break;
|
||||
case SECRET_ALTTITLE:
|
||||
iconid = 6;
|
||||
break;
|
||||
|
|
@ -4813,12 +4941,15 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, boolean hili
|
|||
case SECRET_TIMEATTACK:
|
||||
iconid = 7;
|
||||
break;
|
||||
case SECRET_BREAKTHECAPSULES:
|
||||
case SECRET_PRISONBREAK:
|
||||
iconid = 8;
|
||||
break;
|
||||
case SECRET_SPECIALATTACK:
|
||||
iconid = 9;
|
||||
break;
|
||||
case SECRET_SPBATTACK:
|
||||
iconid = 0; // TEMPORARY
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
|
|
@ -4896,6 +5027,8 @@ drawborder:
|
|||
}
|
||||
}
|
||||
|
||||
#define challengetransparentstrength 8
|
||||
|
||||
static void M_DrawChallengePreview(INT32 x, INT32 y)
|
||||
{
|
||||
unlockable_t *ref = NULL;
|
||||
|
|
@ -4942,12 +5075,57 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
|
|||
{
|
||||
case SECRET_SKIN:
|
||||
{
|
||||
INT32 skin = M_UnlockableSkinNum(ref);
|
||||
INT32 skin = M_UnlockableSkinNum(ref), i;
|
||||
// Draw our character!
|
||||
if (skin != -1)
|
||||
{
|
||||
colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE);
|
||||
M_DrawCharacterSprite(x, y, skin, false, false, 0, colormap);
|
||||
|
||||
for (i = 0; i < skin; i++)
|
||||
{
|
||||
if (!R_SkinUsable(-1, i, false))
|
||||
continue;
|
||||
if (skins[i].kartspeed != skins[skin].kartspeed)
|
||||
continue;
|
||||
if (skins[i].kartweight != skins[skin].kartweight)
|
||||
continue;
|
||||
|
||||
colormap = R_GetTranslationColormap(i, skins[i].prefcolor, GTC_MENUCACHE);
|
||||
break;
|
||||
}
|
||||
|
||||
V_DrawFixedPatch(4*FRACUNIT, (BASEVIDHEIGHT-(4+16))*FRACUNIT,
|
||||
FRACUNIT,
|
||||
0, faceprefix[i][FACE_RANK],
|
||||
colormap);
|
||||
|
||||
if (i != skin)
|
||||
{
|
||||
V_DrawScaledPatch(4, (11 + BASEVIDHEIGHT-(4+16)), 0, W_CachePatchName("ALTSDOT", PU_CACHE));
|
||||
}
|
||||
|
||||
V_DrawFadeFill(4+16, (BASEVIDHEIGHT-(4+16)), 16, 16, 0, 31, challengetransparentstrength);
|
||||
|
||||
V_DrawFill(4+16+5, (BASEVIDHEIGHT-(4+16))+1, 1, 14, 0);
|
||||
V_DrawFill(4+16+5+5, (BASEVIDHEIGHT-(4+16))+1, 1, 14, 0);
|
||||
V_DrawFill(4+16+1, (BASEVIDHEIGHT-(4+16))+5, 14, 1, 0);
|
||||
V_DrawFill(4+16+1, (BASEVIDHEIGHT-(4+16))+5+5, 14, 1, 0);
|
||||
|
||||
// The following is a partial duplication of R_GetEngineClass
|
||||
{
|
||||
INT32 s = (skins[skin].kartspeed - 1)/3;
|
||||
INT32 w = (skins[skin].kartweight - 1)/3;
|
||||
|
||||
#define LOCKSTAT(stat) \
|
||||
if (stat < 0) { stat = 0; } \
|
||||
if (stat > 2) { stat = 2; }
|
||||
LOCKSTAT(s);
|
||||
LOCKSTAT(w);
|
||||
#undef LOCKSTAT
|
||||
|
||||
V_DrawFill(4+16 + (s*5), (BASEVIDHEIGHT-(4+16)) + (w*5), 6, 6, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -4968,14 +5146,27 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
|
|||
UINT16 col = K_GetEffectiveFollowerColor(followers[fskin].defaultcolor, cv_playercolor[0].value);
|
||||
colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);
|
||||
M_DrawFollowerSprite(x - 16, y, fskin, false, 0, colormap, NULL);
|
||||
|
||||
if (followers[fskin].category < numfollowercategories)
|
||||
{
|
||||
V_DrawFixedPatch(4*FRACUNIT, (BASEVIDHEIGHT-(4+16))*FRACUNIT,
|
||||
FRACUNIT,
|
||||
0, W_CachePatchName(followercategories[followers[fskin].category].icon, PU_CACHE),
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SECRET_CUP:
|
||||
{
|
||||
levelsearch_t templevelsearch;
|
||||
UINT32 i, id, maxid, offset;
|
||||
cupheader_t *temp = M_UnlockableCup(ref);
|
||||
|
||||
templevelsearch.cup = M_UnlockableCup(ref);
|
||||
if (!temp)
|
||||
break;
|
||||
|
||||
templevelsearch.cup = temp;
|
||||
templevelsearch.typeoflevel = G_TOLFlag(GT_RACE)|G_TOLFlag(GT_BATTLE);
|
||||
templevelsearch.cupmode = true;
|
||||
templevelsearch.timeattack = false;
|
||||
|
|
@ -4983,17 +5174,87 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
|
|||
|
||||
M_DrawCupPreview(146, &templevelsearch);
|
||||
|
||||
maxid = id = (temp->id % 14);
|
||||
offset = (temp->id - id) * 2;
|
||||
while (temp && maxid < 14)
|
||||
{
|
||||
maxid++;
|
||||
temp = temp->next;
|
||||
}
|
||||
|
||||
V_DrawFadeFill(4, (BASEVIDHEIGHT-(4+16)), 28 + offset, 16, 0, 31, challengetransparentstrength);
|
||||
|
||||
for (i = 0; i < offset; i += 4)
|
||||
{
|
||||
V_DrawFill(4+1 + i, (BASEVIDHEIGHT-(4+16))+3, 2, 2, 15);
|
||||
V_DrawFill(4+1 + i, (BASEVIDHEIGHT-(4+16))+8+3, 2, 2, 15);
|
||||
}
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
if (templevelsearch.cup && id == i)
|
||||
{
|
||||
V_DrawFill(offset + 4 + (i*4), (BASEVIDHEIGHT-(4+16)), 4, 8, 0);
|
||||
}
|
||||
else if (i < maxid)
|
||||
{
|
||||
V_DrawFill(offset + 4+1 + (i*4), (BASEVIDHEIGHT-(4+16))+3, 2, 2, 0);
|
||||
}
|
||||
|
||||
if (templevelsearch.cup && (templevelsearch.cup->id % 14) == i+7)
|
||||
{
|
||||
V_DrawFill(offset + 4 + (i*4), (BASEVIDHEIGHT-(4+16))+8, 4, 8, 0);
|
||||
}
|
||||
else if (i+7 < maxid)
|
||||
{
|
||||
V_DrawFill(offset + 4+1 + (i*4), (BASEVIDHEIGHT-(4+16))+8+3, 2, 2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SECRET_MAP:
|
||||
{
|
||||
const char *gtname = "INVALID HEADER";
|
||||
UINT16 mapnum = M_UnlockableMapNum(ref);
|
||||
|
||||
K_DrawMapThumbnail(
|
||||
(x-30)<<FRACBITS, (146+2)<<FRACBITS,
|
||||
60<<FRACBITS,
|
||||
(x-50)<<FRACBITS, (146+2)<<FRACBITS,
|
||||
80<<FRACBITS,
|
||||
0,
|
||||
mapnum,
|
||||
NULL);
|
||||
|
||||
if (mapnum < nummapheaders && mapheaderinfo[mapnum] != NULL)
|
||||
{
|
||||
INT32 guessgt = G_GuessGametypeByTOL(mapheaderinfo[mapnum]->typeoflevel);
|
||||
|
||||
if (guessgt == -1)
|
||||
{
|
||||
// No Time Attack support, so specify...
|
||||
gtname = "Match Race/Online";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (guessgt == GT_VERSUS)
|
||||
{
|
||||
// Fudge since there's no Versus-specific menu right now...
|
||||
guessgt = GT_SPECIAL;
|
||||
}
|
||||
|
||||
if (guessgt == GT_SPECIAL && !M_SecretUnlocked(SECRET_SPECIALATTACK, true))
|
||||
{
|
||||
gtname = "???";
|
||||
}
|
||||
else
|
||||
{
|
||||
gtname = gametypes[guessgt]->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
V_DrawThinString(1, BASEVIDHEIGHT-(9+3), V_ALLOWLOWERCASE|V_6WIDTHSPACE, gtname);
|
||||
|
||||
break;
|
||||
}
|
||||
case SECRET_ENCORE:
|
||||
|
|
@ -5016,7 +5277,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
|
|||
specialmap = tamapcache;
|
||||
break;
|
||||
}
|
||||
case SECRET_BREAKTHECAPSULES:
|
||||
case SECRET_PRISONBREAK:
|
||||
{
|
||||
static UINT16 btcmapcache = NEXTMAP_INVALID;
|
||||
if (btcmapcache > nummapheaders)
|
||||
|
|
@ -5036,6 +5297,16 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
|
|||
specialmap = sscmapcache;
|
||||
break;
|
||||
}
|
||||
case SECRET_SPBATTACK:
|
||||
{
|
||||
static UINT16 spbmapcache = NEXTMAP_INVALID;
|
||||
if (spbmapcache > nummapheaders)
|
||||
{
|
||||
spbmapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, 2, 0, false, NULL);
|
||||
}
|
||||
specialmap = spbmapcache;
|
||||
break;
|
||||
}
|
||||
case SECRET_HARDSPEED:
|
||||
{
|
||||
static UINT16 hardmapcache = NEXTMAP_INVALID;
|
||||
|
|
@ -5046,12 +5317,31 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
|
|||
specialmap = hardmapcache;
|
||||
break;
|
||||
}
|
||||
case SECRET_MASTERMODE:
|
||||
{
|
||||
static UINT16 mastermapcache = NEXTMAP_INVALID;
|
||||
if (mastermapcache > nummapheaders)
|
||||
{
|
||||
mastermapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, 2, 0, false, NULL);
|
||||
}
|
||||
specialmap = mastermapcache;
|
||||
break;
|
||||
}
|
||||
case SECRET_ONLINE:
|
||||
{
|
||||
V_DrawFixedPatch(-3*FRACUNIT, (y-40)*FRACUNIT,
|
||||
FRACUNIT,
|
||||
0, W_CachePatchName("EGGASTLA", PU_CACHE),
|
||||
NULL);
|
||||
break;
|
||||
}
|
||||
case SECRET_ALTTITLE:
|
||||
{
|
||||
x = 8;
|
||||
y = BASEVIDHEIGHT-16;
|
||||
V_DrawGamemodeString(x, y - 32, V_ALLOWLOWERCASE, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_MENUCACHE), cv_alttitle.string);
|
||||
V_DrawThinString(x, y, V_6WIDTHSPACE|V_ALLOWLOWERCASE|highlightflags, "Press (A)");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
|
@ -5079,6 +5369,13 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
|
|||
V_DrawFixedPatch((x+40)<<FRACBITS, ((y+25)<<FRACBITS) - (rubyheight<<1), FRACUNIT, 0, W_CachePatchName("RUBYICON", PU_CACHE), NULL);
|
||||
rubyfloattime += FixedMul(ANGLE_MAX/NEWTICRATE, renderdeltatics);
|
||||
}
|
||||
else if (ref->type == SECRET_SPBATTACK)
|
||||
{
|
||||
V_DrawFixedPatch((x+40-25)<<FRACBITS, ((y+25-25)<<FRACBITS),
|
||||
FRACUNIT, 0,
|
||||
W_CachePatchName(K_GetItemPatch(KITEM_SPB, false), PU_CACHE),
|
||||
NULL);
|
||||
}
|
||||
else if (ref->type == SECRET_HARDSPEED)
|
||||
{
|
||||
V_DrawFixedPatch((x+40-25)<<FRACBITS, ((y+25-25)<<FRACBITS),
|
||||
|
|
@ -5086,6 +5383,13 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
|
|||
W_CachePatchName(K_GetItemPatch(KITEM_ROCKETSNEAKER, false), PU_CACHE),
|
||||
NULL);
|
||||
}
|
||||
else if (ref->type == SECRET_MASTERMODE)
|
||||
{
|
||||
V_DrawFixedPatch((x+40-25)<<FRACBITS, ((y+25-25)<<FRACBITS),
|
||||
FRACUNIT, 0,
|
||||
W_CachePatchName(K_GetItemPatch(KITEM_JAWZ, false), PU_CACHE),
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
colormap = R_GetTranslationColormap(TC_DEFAULT, cv_playercolor[0].value, GTC_MENUCACHE);
|
||||
|
|
@ -5096,8 +5400,8 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
|
|||
}
|
||||
}
|
||||
|
||||
#define challengetransparentstrength 8
|
||||
#define challengesgridstep 22
|
||||
#define challengekeybarwidth 50
|
||||
|
||||
void M_DrawChallenges(void)
|
||||
{
|
||||
|
|
@ -5221,6 +5525,34 @@ void M_DrawChallenges(void)
|
|||
|
||||
challengedesc:
|
||||
|
||||
// Chao Keys
|
||||
{
|
||||
patch_t *key = W_CachePatchName("UN_CHA00", PU_CACHE);
|
||||
INT32 offs = challengesmenu.unlockcount[CC_CHAONOPE];
|
||||
if (offs & 1)
|
||||
offs = -offs;
|
||||
offs /= 2;
|
||||
|
||||
if (gamedata->chaokeys > 9)
|
||||
{
|
||||
offs -= 6;
|
||||
if (gamedata->chaokeys > 99)
|
||||
offs -= 2; // as far as we can go
|
||||
}
|
||||
|
||||
V_DrawFixedPatch((8+offs)*FRACUNIT, 5*FRACUNIT, FRACUNIT, 0, key, NULL);
|
||||
V_DrawKartString((27+offs), 9-challengesmenu.unlockcount[CC_CHAOANIM], 0, va("%u", gamedata->chaokeys));
|
||||
|
||||
offs = challengekeybarwidth;
|
||||
if (gamedata->chaokeys < GDMAX_CHAOKEYS)
|
||||
offs = ((gamedata->pendingkeyroundoffset * challengekeybarwidth)/GDCONVERT_ROUNDSTOKEY);
|
||||
|
||||
if (offs > 0)
|
||||
V_DrawFill(1, 25, offs, 2, 0);
|
||||
if (offs < challengekeybarwidth)
|
||||
V_DrawFadeFill(1+offs, 25, challengekeybarwidth-offs, 2, 0, 31, challengetransparentstrength);
|
||||
}
|
||||
|
||||
// Tally
|
||||
{
|
||||
str = va("%d/%d",
|
||||
|
|
@ -5275,6 +5607,7 @@ challengedesc:
|
|||
|
||||
#undef challengetransparentstrength
|
||||
#undef challengesgridstep
|
||||
#undef challengekeybarwidth
|
||||
|
||||
// Statistics menu
|
||||
|
||||
|
|
@ -5302,6 +5635,17 @@ static void M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y)
|
|||
curtype = 2;
|
||||
break;
|
||||
}
|
||||
case ET_MAP:
|
||||
{
|
||||
if (((emblem->flags & ME_ENCORE) && !M_SecretUnlocked(SECRET_ENCORE, true))
|
||||
|| ((emblem->flags & ME_SPBATTACK) && !M_SecretUnlocked(SECRET_SPBATTACK, true)))
|
||||
{
|
||||
emblem = M_GetLevelEmblems(-1);
|
||||
continue;
|
||||
}
|
||||
curtype = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
curtype = 0;
|
||||
break;
|
||||
|
|
@ -5334,27 +5678,53 @@ static void M_DrawStatsMaps(void)
|
|||
V_DrawCharacter(10, y-(skullAnimCounter/5),
|
||||
'\x1A' | highlightflags, false); // up arrow
|
||||
|
||||
while (statisticsmenu.maplist[++i] != NEXTMAP_INVALID)
|
||||
while ((mnum = statisticsmenu.maplist[++i]) != NEXTMAP_INVALID)
|
||||
{
|
||||
if (location)
|
||||
{
|
||||
--location;
|
||||
continue;
|
||||
}
|
||||
else if (dotopname)
|
||||
|
||||
if (dotopname || mnum >= nummapheaders)
|
||||
{
|
||||
V_DrawThinString(20, y, V_6WIDTHSPACE|highlightflags, "LEVEL NAME");
|
||||
V_DrawRightAlignedThinString(BASEVIDWIDTH-20, y, V_6WIDTHSPACE|highlightflags, "MEDALS");
|
||||
if (mnum >= nummapheaders)
|
||||
{
|
||||
mnum = statisticsmenu.maplist[1+i];
|
||||
if (mnum >= nummapheaders)
|
||||
mnum = statisticsmenu.maplist[i-1];
|
||||
}
|
||||
|
||||
if (mnum < nummapheaders)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
if (mapheaderinfo[mnum]->cup)
|
||||
str = va("%s CUP", mapheaderinfo[mnum]->cup->name);
|
||||
else
|
||||
str = "LOST AND FOUND";
|
||||
|
||||
V_DrawThinString(20, y, V_6WIDTHSPACE|highlightflags, str);
|
||||
}
|
||||
|
||||
if (dotopname)
|
||||
{
|
||||
V_DrawRightAlignedThinString(BASEVIDWIDTH-20, y, V_6WIDTHSPACE|highlightflags, "MEDALS");
|
||||
dotopname = false;
|
||||
}
|
||||
|
||||
y += STATSSTEP;
|
||||
dotopname = false;
|
||||
if (y >= BASEVIDHEIGHT-STATSSTEP)
|
||||
goto bottomarrow;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
mnum = statisticsmenu.maplist[i]+1;
|
||||
M_DrawMapMedals(mnum, 291, y);
|
||||
M_DrawMapMedals(mnum+1, 291, y);
|
||||
|
||||
{
|
||||
char *title = G_BuildMapTitle(mnum);
|
||||
V_DrawThinString(20, y, V_6WIDTHSPACE, title);
|
||||
char *title = G_BuildMapTitle(mnum+1);
|
||||
V_DrawThinString(24, y, V_6WIDTHSPACE, title);
|
||||
Z_Free(title);
|
||||
}
|
||||
|
||||
|
|
@ -5363,15 +5733,12 @@ static void M_DrawStatsMaps(void)
|
|||
if (y >= BASEVIDHEIGHT-STATSSTEP)
|
||||
goto bottomarrow;
|
||||
}
|
||||
if (dotopname && !location)
|
||||
{
|
||||
V_DrawString(20, y, V_6WIDTHSPACE|highlightflags, "LEVEL NAME");
|
||||
V_DrawString(256, y, V_6WIDTHSPACE|highlightflags, "MEDALS");
|
||||
y += STATSSTEP;
|
||||
}
|
||||
else if (location)
|
||||
if (location)
|
||||
--location;
|
||||
|
||||
if (statisticsmenu.numextramedals == 0)
|
||||
goto bottomarrow;
|
||||
|
||||
// Extra Emblem headers
|
||||
for (i = 0; i < 2; ++i)
|
||||
{
|
||||
|
|
@ -5410,7 +5777,6 @@ static void M_DrawStatsMaps(void)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (i >= 0)
|
||||
{
|
||||
if (gamedata->unlocked[i])
|
||||
{
|
||||
|
|
@ -5425,7 +5791,7 @@ static void M_DrawStatsMaps(void)
|
|||
V_DrawSmallScaledPatch(291, y+1, V_6WIDTHSPACE, W_CachePatchName("NEEDIT", PU_CACHE));
|
||||
}
|
||||
|
||||
V_DrawThinString(20, y, V_6WIDTHSPACE, va("%s", unlockables[i].name));
|
||||
V_DrawThinString(24, y, V_6WIDTHSPACE, va("%s", unlockables[i].name));
|
||||
}
|
||||
|
||||
y += STATSSTEP;
|
||||
|
|
@ -5441,7 +5807,7 @@ bottomarrow:
|
|||
|
||||
void M_DrawStatistics(void)
|
||||
{
|
||||
char beststr[40];
|
||||
char beststr[256];
|
||||
|
||||
tic_t besttime = 0;
|
||||
|
||||
|
|
@ -5453,14 +5819,71 @@ void M_DrawStatistics(void)
|
|||
V_DrawFixedPatch(0, 0, FRACUNIT, 0, bg, NULL);
|
||||
}
|
||||
|
||||
beststr[0] = 0;
|
||||
V_DrawThinString(20, 22, V_6WIDTHSPACE|V_ALLOWLOWERCASE|highlightflags, "Total Play Time:");
|
||||
V_DrawCenteredThinString(BASEVIDWIDTH/2, 32, V_6WIDTHSPACE,
|
||||
va("%i hours, %i minutes, %i seconds",
|
||||
G_TicsToHours(gamedata->totalplaytime),
|
||||
G_TicsToMinutes(gamedata->totalplaytime, false),
|
||||
G_TicsToSeconds(gamedata->totalplaytime)));
|
||||
V_DrawThinString(20, 42, V_6WIDTHSPACE|V_ALLOWLOWERCASE|highlightflags, "Total Matches:");
|
||||
V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 42, V_6WIDTHSPACE, va("%i played", gamedata->matchesplayed));
|
||||
besttime = G_TicsToHours(gamedata->totalplaytime);
|
||||
if (besttime)
|
||||
{
|
||||
if (besttime >= 24)
|
||||
{
|
||||
strcat(beststr, va("%u day%s, ", besttime/24, (besttime < 48 ? "" : "s")));
|
||||
besttime %= 24;
|
||||
}
|
||||
|
||||
strcat(beststr, va("%u hour%s, ", besttime, (besttime == 1 ? "" : "s")));
|
||||
}
|
||||
besttime = G_TicsToMinutes(gamedata->totalplaytime, false);
|
||||
if (besttime)
|
||||
{
|
||||
strcat(beststr, va("%u minute%s, ", besttime, (besttime == 1 ? "" : "s")));
|
||||
}
|
||||
besttime = G_TicsToSeconds(gamedata->totalplaytime);
|
||||
strcat(beststr, va("%i second%s", besttime, (besttime == 1 ? "" : "s")));
|
||||
V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 22, V_6WIDTHSPACE, beststr);
|
||||
beststr[0] = 0;
|
||||
|
||||
V_DrawThinString(20, 32, V_6WIDTHSPACE|V_ALLOWLOWERCASE|highlightflags, "Total Rings:");
|
||||
if (gamedata->totalrings > GDMAX_RINGS)
|
||||
{
|
||||
sprintf(beststr, "%c999,999,999+", '\x82');
|
||||
}
|
||||
else if (gamedata->totalrings >= 1000000)
|
||||
{
|
||||
sprintf(beststr, "%u,%03u,%03u", (gamedata->totalrings/1000000), (gamedata->totalrings/1000)%1000, (gamedata->totalrings%1000));
|
||||
}
|
||||
else if (gamedata->totalrings >= 1000)
|
||||
{
|
||||
sprintf(beststr, "%u,%03u", (gamedata->totalrings/1000), (gamedata->totalrings%1000));
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(beststr, "%u", gamedata->totalrings);
|
||||
}
|
||||
V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 32, V_6WIDTHSPACE, va("%s collected", beststr));
|
||||
|
||||
beststr[0] = 0;
|
||||
V_DrawThinString(20, 42, V_6WIDTHSPACE|V_ALLOWLOWERCASE|highlightflags, "Total Rounds:");
|
||||
|
||||
strcat(beststr, va("%u Race", gamedata->roundsplayed[GDGT_RACE]));
|
||||
|
||||
if (gamedata->roundsplayed[GDGT_PRISONS] > 0)
|
||||
{
|
||||
strcat(beststr, va(", %u Prisons", gamedata->roundsplayed[GDGT_PRISONS]));
|
||||
}
|
||||
|
||||
strcat(beststr, va(", %u Battle", gamedata->roundsplayed[GDGT_BATTLE]));
|
||||
|
||||
if (gamedata->roundsplayed[GDGT_SPECIAL] > 0)
|
||||
{
|
||||
strcat(beststr, va(", %u Special", gamedata->roundsplayed[GDGT_SPECIAL]));
|
||||
}
|
||||
|
||||
if (gamedata->roundsplayed[GDGT_CUSTOM] > 0)
|
||||
{
|
||||
strcat(beststr, va(", %u Custom", gamedata->roundsplayed[GDGT_CUSTOM]));
|
||||
}
|
||||
|
||||
V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 42, V_6WIDTHSPACE, beststr);
|
||||
|
||||
if (!statisticsmenu.maplist)
|
||||
{
|
||||
|
|
@ -5468,6 +5891,8 @@ void M_DrawStatistics(void)
|
|||
return;
|
||||
}
|
||||
|
||||
besttime = 0;
|
||||
|
||||
for (i = 0; i < nummapheaders; i++)
|
||||
{
|
||||
if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & (LF2_NOTIMEATTACK|LF2_HIDEINSTATS|LF2_HIDEINMENU)))
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include "v_video.h"
|
||||
#include "f_finale.h"
|
||||
#include "m_misc.h"
|
||||
#include "m_cond.h"
|
||||
|
||||
#ifdef PC_DOS
|
||||
#include <stdio.h> // for snprintf
|
||||
|
|
@ -369,9 +370,17 @@ boolean M_Responder(event_t *ev)
|
|||
void M_PlayMenuJam(void)
|
||||
{
|
||||
menu_t *refMenu = (menuactive ? currentMenu : restoreMenu);
|
||||
static boolean loserclubpermitted = false;
|
||||
boolean loserclub = (loserclubpermitted && (gamedata->musicflags & GDMUSIC_LOSERCLUB));
|
||||
|
||||
if (challengesmenu.pending)
|
||||
{
|
||||
S_StopMusic();
|
||||
cursongcredit.def = NULL;
|
||||
|
||||
loserclubpermitted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Playing())
|
||||
return;
|
||||
|
|
@ -382,15 +391,27 @@ void M_PlayMenuJam(void)
|
|||
{
|
||||
S_StopMusic();
|
||||
cursongcredit.def = NULL;
|
||||
return;
|
||||
}
|
||||
else
|
||||
else if (!loserclub)
|
||||
{
|
||||
if (NotCurrentlyPlaying(refMenu->music))
|
||||
{
|
||||
S_ChangeMusicInternal(refMenu->music, true);
|
||||
S_ShowMusicCredit();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (loserclub)
|
||||
{
|
||||
if (refMenu != NULL && NotCurrentlyPlaying("LOSERC"))
|
||||
{
|
||||
S_ChangeMusicInternal("LOSERC", true);
|
||||
S_ShowMusicCredit();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -478,6 +499,7 @@ menu_t *M_SpecificMenuRestore(menu_t *torestore)
|
|||
}
|
||||
|
||||
// One last catch.
|
||||
M_SetupPlayMenu(-1);
|
||||
PLAY_CharSelectDef.prevMenu = &MainDef;
|
||||
|
||||
return torestore;
|
||||
|
|
@ -945,7 +967,7 @@ static void M_HandleMenuInput(void)
|
|||
{
|
||||
if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && majormods)
|
||||
{
|
||||
M_StartMessage(M_GetText("This cannot be done with complex addons\nor in a cheated game.\n\nPress (B)\n"), NULL, MM_NOTHING);
|
||||
M_StartMessage(M_GetText("This cannot be done with complex addons\nor in a cheated game.\n\nPress (B)"), NULL, MM_NOTHING);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
#include "k_podium.h"
|
||||
|
||||
#include "doomdef.h"
|
||||
#include "doomstat.h"
|
||||
#include "d_main.h"
|
||||
#include "d_netcmd.h"
|
||||
#include "f_finale.h"
|
||||
|
|
@ -69,6 +68,31 @@ boolean K_PodiumSequence(void)
|
|||
return (gamestate == GS_CEREMONY);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_PodiumRanking(void)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
boolean K_PodiumRanking(void)
|
||||
{
|
||||
return (gamestate == GS_CEREMONY && podiumData.ranking == true);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_PodiumGrade(void)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
gp_rank_e K_PodiumGrade(void)
|
||||
{
|
||||
if (K_PodiumSequence() == false)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return podiumData.grade;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
UINT8 K_GetPodiumPosition(player_t *player)
|
||||
|
||||
|
|
@ -264,6 +288,10 @@ void K_FinishCeremony(void)
|
|||
}
|
||||
|
||||
podiumData.ranking = true;
|
||||
|
||||
// Play the noise now
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
@ -273,6 +301,8 @@ void K_FinishCeremony(void)
|
|||
--------------------------------------------------*/
|
||||
void K_ResetCeremony(void)
|
||||
{
|
||||
UINT8 i;
|
||||
|
||||
memset(&podiumData, 0, sizeof(struct podiumData_s));
|
||||
|
||||
if (K_PodiumSequence() == false)
|
||||
|
|
@ -280,8 +310,36 @@ void K_ResetCeremony(void)
|
|||
return;
|
||||
}
|
||||
|
||||
// Establish rank and grade for this play session.
|
||||
podiumData.rank = grandprixinfo.rank;
|
||||
podiumData.grade = K_CalculateGPGrade(&podiumData.rank);
|
||||
|
||||
if (!grandprixinfo.cup)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Write grade, position, and emerald-having-ness for later sessions!
|
||||
i = (grandprixinfo.masterbots) ? KARTGP_MASTER : grandprixinfo.gamespeed;
|
||||
|
||||
if ((grandprixinfo.cup->windata[i].best_placement == 0) // First run
|
||||
|| (podiumData.rank.position < grandprixinfo.cup->windata[i].best_placement)) // Later, better run
|
||||
{
|
||||
grandprixinfo.cup->windata[i].best_placement = podiumData.rank.position;
|
||||
|
||||
// The following will not occour in unmodified builds, but pre-emptively sanitise gamedata if someone just changes MAXPLAYERS and calls it a day
|
||||
if (grandprixinfo.cup->windata[i].best_placement > 0x0F)
|
||||
grandprixinfo.cup->windata[i].best_placement = 0x0F;
|
||||
}
|
||||
|
||||
if (podiumData.grade > grandprixinfo.cup->windata[i].best_grade)
|
||||
grandprixinfo.cup->windata[i].best_grade = podiumData.grade;
|
||||
|
||||
if (i != KARTSPEED_EASY && podiumData.rank.specialWon == true)
|
||||
grandprixinfo.cup->windata[i].got_emerald = true;
|
||||
|
||||
// Save before playing the noise
|
||||
G_SaveGameData();
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
@ -458,7 +516,7 @@ void K_CeremonyDrawer(void)
|
|||
case 5:
|
||||
{
|
||||
V_DrawString(x, y, V_ALLOWLOWERCASE,
|
||||
va("CAPSULES: %d / %d", podiumData.rank.capsules, podiumData.rank.totalCapsules)
|
||||
va("PRISONS: %d / %d", podiumData.rank.prisons, podiumData.rank.totalPrisons)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#define __K_PODIUM__
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "doomstat.h" // gp_rank_e
|
||||
#include "d_event.h"
|
||||
#include "p_mobj.h"
|
||||
|
||||
|
|
@ -37,6 +38,37 @@ extern "C" {
|
|||
boolean K_PodiumSequence(void);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_PodiumRanking(void);
|
||||
|
||||
Returns whenver or not we are in the podium
|
||||
final state.
|
||||
|
||||
Input Arguments:-
|
||||
N/A
|
||||
|
||||
Return:-
|
||||
true if we're in GS_CEREMONY, otherwise false.
|
||||
--------------------------------------------------*/
|
||||
|
||||
boolean K_PodiumRanking(void);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_PodiumGrade(void)
|
||||
|
||||
Returns the podium grade.
|
||||
|
||||
Input Arguments:-
|
||||
N/A
|
||||
|
||||
Return:-
|
||||
gp_rank_e constant if we're in GS_CEREMONY, otherwise 0.
|
||||
--------------------------------------------------*/
|
||||
|
||||
gp_rank_e K_PodiumGrade(void);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
UINT8 K_GetPodiumPosition(player_t *player);
|
||||
|
||||
|
|
|
|||
|
|
@ -397,6 +397,7 @@ void K_CashInPowerLevels(void)
|
|||
{
|
||||
SINT8 powerType = K_UsingPowerLevels();
|
||||
UINT8 i;
|
||||
boolean gamedataupdate;
|
||||
|
||||
//CONS_Printf("\n========\n");
|
||||
//CONS_Printf("Cashing in power level changes...\n");
|
||||
|
|
@ -417,14 +418,19 @@ void K_CashInPowerLevels(void)
|
|||
{
|
||||
pr->powerlevels[powerType] = clientpowerlevels[i][powerType];
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
G_SaveGameData();
|
||||
gamedataupdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
clientPowerAdd[i] = 0;
|
||||
}
|
||||
|
||||
if (gamedataupdate)
|
||||
{
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
|
||||
//CONS_Printf("========\n");
|
||||
}
|
||||
|
||||
|
|
@ -637,7 +643,7 @@ void K_PlayerForfeit(UINT8 playerNum, boolean pointLoss)
|
|||
{
|
||||
pr->powerlevels[powerType] = yourPower + inc;
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
17
src/k_rank.c
17
src/k_rank.c
|
|
@ -304,7 +304,12 @@ void K_InitGrandPrixRank(gpRank_t *rankData)
|
|||
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[i];
|
||||
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum] != NULL)
|
||||
{
|
||||
laps += mapheaderinfo[cupLevelNum]->numlaps;
|
||||
if (!cv_gptest.value)
|
||||
{
|
||||
laps += mapheaderinfo[cupLevelNum]->numlaps;
|
||||
continue;
|
||||
}
|
||||
laps++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -335,7 +340,7 @@ void K_InitGrandPrixRank(gpRank_t *rankData)
|
|||
continue;
|
||||
}
|
||||
|
||||
rankData->totalCapsules += RankCapsules_CountFromMap(virt);
|
||||
rankData->totalPrisons += RankCapsules_CountFromMap(virt);
|
||||
vres_Free(virt);
|
||||
}
|
||||
}
|
||||
|
|
@ -360,9 +365,9 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData)
|
|||
const INT32 positionWeight = 150;
|
||||
const INT32 pointsWeight = 100;
|
||||
const INT32 lapsWeight = 100;
|
||||
const INT32 capsulesWeight = 100;
|
||||
const INT32 prisonsWeight = 100;
|
||||
const INT32 ringsWeight = 50;
|
||||
const INT32 total = positionWeight + pointsWeight + lapsWeight + capsulesWeight + ringsWeight;
|
||||
const INT32 total = positionWeight + pointsWeight + lapsWeight + prisonsWeight + ringsWeight;
|
||||
const INT32 continuesPenalty = 20;
|
||||
|
||||
INT32 ours = 0;
|
||||
|
|
@ -385,9 +390,9 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData)
|
|||
ours += (rankData->laps * lapsWeight) / rankData->totalLaps;
|
||||
}
|
||||
|
||||
if (rankData->totalCapsules > 0)
|
||||
if (rankData->totalPrisons > 0)
|
||||
{
|
||||
ours += (rankData->capsules * capsulesWeight) / rankData->totalCapsules;
|
||||
ours += (rankData->prisons * prisonsWeight) / rankData->totalPrisons;
|
||||
}
|
||||
|
||||
if (rankData->totalRings > 0)
|
||||
|
|
|
|||
14
src/k_rank.h
14
src/k_rank.h
|
|
@ -35,8 +35,8 @@ struct gpRank_t
|
|||
|
||||
UINT32 continuesUsed;
|
||||
|
||||
UINT32 capsules;
|
||||
UINT32 totalCapsules;
|
||||
UINT32 prisons;
|
||||
UINT32 totalPrisons;
|
||||
|
||||
UINT32 rings;
|
||||
UINT32 totalRings;
|
||||
|
|
@ -44,15 +44,7 @@ struct gpRank_t
|
|||
boolean specialWon;
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GRADE_E,
|
||||
GRADE_D,
|
||||
GRADE_C,
|
||||
GRADE_B,
|
||||
GRADE_A,
|
||||
GRADE_S
|
||||
} gp_rank_e;
|
||||
// gp_rank_e was once defined here, but moved to doomstat.h to prevent circular dependency
|
||||
|
||||
// 3rd place is neutral, anything below is a penalty
|
||||
#define RANK_NEUTRAL_POSITION (3)
|
||||
|
|
|
|||
|
|
@ -1230,7 +1230,7 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
|
|||
kartitems_t *presetlist = K_KartItemReelTimeAttack;
|
||||
|
||||
// If the objective is not to go fast, it's to cause serious damage.
|
||||
if (gametyperules & GTR_CAPSULES)
|
||||
if (gametyperules & GTR_PRISONS)
|
||||
{
|
||||
presetlist = K_KartItemReelBreakTheCapsules;
|
||||
}
|
||||
|
|
|
|||
908
src/m_cond.c
908
src/m_cond.c
File diff suppressed because it is too large
Load diff
143
src/m_cond.h
143
src/m_cond.h
|
|
@ -1,6 +1,6 @@
|
|||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2022-2023 by Vivian "toaster" Grannell.
|
||||
// Copyright (C) 2022-2023 by Vivian "toastergrl" Grannell.
|
||||
// Copyright (C) 2012-2016 by Matthew "Kaito Sinclaire" Walsh.
|
||||
// Copyright (C) 2012-2020 by Sonic Team Junior.
|
||||
//
|
||||
|
|
@ -29,19 +29,81 @@ extern "C" {
|
|||
typedef enum
|
||||
{
|
||||
UC_PLAYTIME, // PLAYTIME [tics]
|
||||
UC_MATCHESPLAYED, // SRB2Kart: MATCHESPLAYED [x played]
|
||||
UC_ROUNDSPLAYED, // ROUNDSPLAYED [x played]
|
||||
UC_TOTALRINGS, // TOTALRINGS [x collected]
|
||||
|
||||
UC_POWERLEVEL, // SRB2Kart: POWERLEVEL [power level to reach] [gametype, "0" for race, "1" for battle]
|
||||
|
||||
UC_GAMECLEAR, // GAMECLEAR <x times>
|
||||
UC_OVERALLTIME, // OVERALLTIME [time to beat, tics]
|
||||
UC_MAPVISITED, // MAPVISITED [map number]
|
||||
UC_MAPBEATEN, // MAPBEATEN [map number]
|
||||
UC_MAPENCORE, // MAPENCORE [map number]
|
||||
UC_MAPTIME, // MAPTIME [map number] [time to beat, tics]
|
||||
UC_TRIGGER, // TRIGGER [trigger number]
|
||||
|
||||
UC_MAPVISITED, // MAPVISITED [map]
|
||||
UC_MAPBEATEN, // MAPBEATEN [map]
|
||||
UC_MAPENCORE, // MAPENCORE [map]
|
||||
UC_MAPSPBATTACK, // MAPSPBATTACK [map]
|
||||
UC_MAPTIME, // MAPTIME [map] [time to beat, tics]
|
||||
|
||||
UC_ALLCHAOS, // ALLCHAOS [minimum difficulty]
|
||||
UC_ALLSUPER, // ALLSUPER [minimum difficulty]
|
||||
UC_ALLEMERALDS, // ALLEMERALDS [minimum difficulty]
|
||||
|
||||
UC_TOTALMEDALS, // TOTALMEDALS [number of emblems]
|
||||
UC_EMBLEM, // EMBLEM [emblem number]
|
||||
|
||||
UC_UNLOCKABLE, // UNLOCKABLE [unlockable number]
|
||||
UC_CONDITIONSET, // CONDITIONSET [condition set number]
|
||||
|
||||
UC_ADDON, // Ever loaded a custom file?
|
||||
UC_REPLAY, // Save a replay
|
||||
UC_CRASH, // Hee ho !
|
||||
|
||||
// Just for string building
|
||||
UC_AND,
|
||||
UC_COMMA,
|
||||
|
||||
UCRP_REQUIRESPLAYING, // All conditions below this can only be checked if (Playing() && gamestate == GS_LEVEL).
|
||||
|
||||
UCRP_PREFIX_GRANDPRIX = UCRP_REQUIRESPLAYING, // GRAND PRIX:
|
||||
UCRP_PREFIX_BONUSROUND, // BONUS ROUND:
|
||||
UCRP_PREFIX_TIMEATTACK, // TIME ATTACK:
|
||||
UCRP_PREFIX_PRISONBREAK, // PRISON BREAK:
|
||||
UCRP_PREFIX_SEALEDSTAR, // SEALED STAR:
|
||||
|
||||
UCRP_PREFIX_ISMAP, // name of [map]:
|
||||
UCRP_ISMAP, // gamemap == [map]
|
||||
|
||||
UCRP_ISCHARACTER, // character == [skin]
|
||||
UCRP_ISENGINECLASS, // engine class [class]
|
||||
UCRP_ISDIFFICULTY, // difficulty >= [difficulty]
|
||||
|
||||
UCRP_PODIUMCUP, // cup == [cup] [optional: >= grade OR place]
|
||||
UCRP_PODIUMEMERALD, // Get to podium sequence with that cup's emerald
|
||||
UCRP_PODIUMPRIZE, // Get to podium sequence with that cup's bonus (alternate string version of UCRP_PODIUMEMERALD
|
||||
|
||||
UCRP_FINISHCOOL, // Finish in good standing
|
||||
UCRP_FINISHALLPRISONS, // Break all prisons
|
||||
UCRP_NOCONTEST, // No Contest
|
||||
|
||||
UCRP_FINISHPLACE, // Finish at least [place]
|
||||
UCRP_FINISHPLACEEXACT, // Finish at [place] exactly
|
||||
|
||||
UCRP_FINISHTIME, // Finish <= [time, tics]
|
||||
UCRP_FINISHTIMEEXACT, // Finish == [time, tics]
|
||||
UCRP_FINISHTIMELEFT, // Finish with at least [time, tics] to spare
|
||||
|
||||
UCRP_TRIGGER, // Map execution trigger [id]
|
||||
|
||||
UCRP_FALLOFF, // Fall off (or don't)
|
||||
UCRP_TOUCHOFFROAD, // Touch offroad (or don't)
|
||||
UCRP_TOUCHSNEAKERPANEL, // Either touch sneaker panel (or don't)
|
||||
UCRP_RINGDEBT, // Go into debt (or don't)
|
||||
|
||||
UCRP_TRIPWIREHYUU, // Go through tripwire with Hyudoro
|
||||
UCRP_SPBNEUTER, // Kill an SPB with Lightning
|
||||
UCRP_LANDMINEDUNK, // huh? you died? that's weird. all i did was try to hug you...
|
||||
UCRP_HITMIDAIR, // Hit another player mid-air with a kartfielditem
|
||||
|
||||
UCRP_WETPLAYER, // Don't touch [fluid]
|
||||
} conditiontype_t;
|
||||
|
||||
// Condition Set information
|
||||
|
|
@ -55,6 +117,7 @@ struct condition_t
|
|||
INT32 requirement; /// <- The requirement for this variable.
|
||||
INT16 extrainfo1; /// <- Extra information for the condition when needed.
|
||||
INT16 extrainfo2; /// <- Extra information for the condition when needed.
|
||||
char *stringvar; /// <- Extra z-allocated string for the condition when needed
|
||||
};
|
||||
struct conditionset_t
|
||||
{
|
||||
|
|
@ -120,13 +183,19 @@ typedef enum
|
|||
|
||||
// Difficulty restrictions
|
||||
SECRET_HARDSPEED, // Permit Hard gamespeed
|
||||
SECRET_MASTERMODE, // Permit Master Mode bots in GP
|
||||
SECRET_ENCORE, // Permit Encore option
|
||||
SECRET_LEGACYBOXRUMMAGE, // Permit the Legacy Box for record attack, etc
|
||||
|
||||
// Menu restrictions
|
||||
SECRET_TIMEATTACK, // Permit Time attack
|
||||
SECRET_BREAKTHECAPSULES, // Permit SP Capsule attack
|
||||
SECRET_PRISONBREAK, // Permit SP Prison attack
|
||||
SECRET_SPECIALATTACK, // Permit Special attack (You're blue now!)
|
||||
SECRET_SPBATTACK, // Permit SPB mode of Time attack
|
||||
|
||||
// Option restrictions
|
||||
SECRET_ONLINE, // Permit netplay (ankle-high barrier to jumping in the deep end)
|
||||
SECRET_ADDONS, // Permit menu addfile
|
||||
SECRET_EGGTV, // Permit replay playback menu
|
||||
SECRET_SOUNDTEST, // Permit Sound Test
|
||||
SECRET_ALTTITLE, // Permit alternate titlescreen
|
||||
|
||||
|
|
@ -148,12 +217,35 @@ typedef enum
|
|||
#endif
|
||||
#define challengegridloops (gamedata->challengegridwidth >= CHALLENGEGRIDLOOPWIDTH)
|
||||
|
||||
#define GDMUSIC_LOSERCLUB 0x01
|
||||
|
||||
// This is the largest number of 9s that will fit in UINT32 and UINT16 respectively.
|
||||
#define GDMAX_RINGS 999999999
|
||||
#define GDMAX_CHAOKEYS 9999
|
||||
|
||||
#ifdef DEVELOP
|
||||
#define GDCONVERT_ROUNDSTOKEY 20
|
||||
#else
|
||||
#define GDCONVERT_ROUNDSTOKEY 50
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
GDGT_RACE,
|
||||
GDGT_BATTLE,
|
||||
GDGT_PRISONS,
|
||||
GDGT_SPECIAL,
|
||||
GDGT_CUSTOM,
|
||||
GDGT_MAX
|
||||
} roundsplayed_t;
|
||||
|
||||
// GAMEDATA STRUCTURE
|
||||
// Everything that would get saved in gamedata.dat
|
||||
struct gamedata_t
|
||||
{
|
||||
// WHENEVER OR NOT WE'RE READY TO SAVE
|
||||
boolean loaded;
|
||||
boolean deferredsave;
|
||||
boolean deferredconditioncheck;
|
||||
|
||||
// CONDITION SETS ACHIEVED
|
||||
boolean achieved[MAXCONDITIONSETS];
|
||||
|
|
@ -174,7 +266,21 @@ struct gamedata_t
|
|||
|
||||
// PLAY TIME
|
||||
UINT32 totalplaytime;
|
||||
UINT32 matchesplayed;
|
||||
UINT32 roundsplayed[GDGT_MAX];
|
||||
UINT32 totalrings;
|
||||
|
||||
// Chao Key condition bypass
|
||||
UINT32 pendingkeyrounds;
|
||||
UINT8 pendingkeyroundoffset;
|
||||
UINT8 keyspending;
|
||||
UINT16 chaokeys;
|
||||
|
||||
// SPECIFIC SPECIAL EVENTS
|
||||
boolean everloadedaddon;
|
||||
boolean eversavedreplay;
|
||||
boolean everseenspecial;
|
||||
boolean evercrashed;
|
||||
UINT8 musicflags;
|
||||
};
|
||||
|
||||
extern gamedata_t *gamedata;
|
||||
|
|
@ -188,8 +294,6 @@ extern unlockable_t unlockables[MAXUNLOCKABLES];
|
|||
|
||||
extern INT32 numemblems;
|
||||
|
||||
extern UINT32 unlocktriggers;
|
||||
|
||||
void M_NewGameDataStruct(void);
|
||||
|
||||
// Challenges menu stuff
|
||||
|
|
@ -213,16 +317,21 @@ char *M_BuildConditionSetString(UINT8 unlockid);
|
|||
#define DESCRIPTIONWIDTH 170
|
||||
|
||||
// Condition set setup
|
||||
void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2);
|
||||
void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2, char *stringvar);
|
||||
void M_UpdateConditionSetsPending(void);
|
||||
|
||||
// Clearing secrets
|
||||
void M_ClearConditionSet(UINT8 set);
|
||||
void M_ClearSecrets(void);
|
||||
void M_ClearStats(void);
|
||||
|
||||
// Updating conditions and unlockables
|
||||
UINT8 M_CheckCondition(condition_t *cn);
|
||||
boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud);
|
||||
UINT8 M_GetNextAchievedUnlock(void);
|
||||
boolean M_CheckCondition(condition_t *cn, player_t *player);
|
||||
boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud, boolean doall);
|
||||
|
||||
#define PENDING_CHAOKEYS (UINT16_MAX-1)
|
||||
UINT16 M_GetNextAchievedUnlock(void);
|
||||
|
||||
UINT8 M_CheckLevelEmblems(void);
|
||||
UINT8 M_CompletionEmblems(void);
|
||||
|
||||
|
|
@ -230,7 +339,7 @@ UINT8 M_CompletionEmblems(void);
|
|||
boolean M_CheckNetUnlockByID(UINT8 unlockid);
|
||||
boolean M_SecretUnlocked(INT32 type, boolean local);
|
||||
boolean M_CupLocked(cupheader_t *cup);
|
||||
boolean M_MapLocked(INT32 mapnum);
|
||||
boolean M_MapLocked(UINT16 mapnum);
|
||||
INT32 M_CountMedals(boolean all, boolean extraonly);
|
||||
|
||||
// Emblem shit
|
||||
|
|
|
|||
|
|
@ -2,22 +2,26 @@
|
|||
/// \brief Extras Menu
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../m_cond.h"
|
||||
#include "../s_sound.h"
|
||||
|
||||
menuitem_t EXTRAS_Main[] =
|
||||
{
|
||||
// The following has NULL strings for text and tooltip.
|
||||
// These are populated in M_InitExtras depending on unlock state.
|
||||
// (This is legal - they're (const char)*'s, not const (char*)'s.
|
||||
|
||||
{IT_STRING | IT_CALL, "Addons", "Add files to customize your experience.",
|
||||
{IT_STRING | IT_CALL, NULL, NULL,
|
||||
NULL, {.routine = M_Addons}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Challenges", "View the requirements for some of the secret content you can unlock!",
|
||||
NULL, {.routine = M_Challenges}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Replay Hut", "Play the replays you've saved throughout your many races & battles!",
|
||||
NULL, {.routine = M_ReplayHut}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Statistics", "Look back on some of your greatest achievements such as your playtime and wins!",
|
||||
NULL, {.routine = M_Statistics}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, NULL, NULL,
|
||||
NULL, {.routine = M_ReplayHut}, 0, 0},
|
||||
};
|
||||
|
||||
// the extras menu essentially reuses the options menu stuff
|
||||
|
|
@ -54,6 +58,40 @@ void M_InitExtras(INT32 choice)
|
|||
extrasmenu.textx = 0;
|
||||
extrasmenu.texty = 0;
|
||||
|
||||
// Addons
|
||||
if (M_SecretUnlocked(SECRET_ADDONS, true))
|
||||
{
|
||||
EXTRAS_Main[0].status = IT_STRING | IT_CALL;
|
||||
EXTRAS_Main[0].text = "Addons";
|
||||
EXTRAS_Main[0].tooltip = "Add files to customize your experience.";
|
||||
}
|
||||
else
|
||||
{
|
||||
EXTRAS_Main[0].status = IT_STRING | IT_TRANSTEXT;
|
||||
EXTRAS_Main[0].text = EXTRAS_Main[0].tooltip = "???";
|
||||
if (EXTRAS_MainDef.lastOn == 0)
|
||||
{
|
||||
EXTRAS_MainDef.lastOn = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Egg TV
|
||||
if (M_SecretUnlocked(SECRET_EGGTV, true))
|
||||
{
|
||||
EXTRAS_Main[3].status = IT_STRING | IT_CALL;
|
||||
EXTRAS_Main[3].text = "Egg TV";
|
||||
EXTRAS_Main[3].tooltip = "Watch the replays you've saved throughout your many races & battles!";
|
||||
}
|
||||
else
|
||||
{
|
||||
EXTRAS_Main[3].status = IT_STRING | IT_TRANSTEXT;
|
||||
EXTRAS_Main[3].text = EXTRAS_Main[3].tooltip = "???";
|
||||
if (EXTRAS_MainDef.lastOn == 3)
|
||||
{
|
||||
EXTRAS_MainDef.lastOn = 2;
|
||||
}
|
||||
}
|
||||
|
||||
M_SetupNextMenu(&EXTRAS_MainDef, false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@ void M_HandleAddons(INT32 choice)
|
|||
//MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
|
||||
|
||||
if (currentMenu->prevMenu)
|
||||
M_SetupNextMenu(currentMenu->prevMenu, false);
|
||||
M_SetupNextMenu(M_InterruptMenuWithChallenges(currentMenu->prevMenu), false);
|
||||
else
|
||||
M_ClearMenus(true);
|
||||
|
||||
|
|
|
|||
|
|
@ -49,17 +49,59 @@ menu_t MISC_StatisticsDef = {
|
|||
|
||||
struct challengesmenu_s challengesmenu;
|
||||
|
||||
static void M_ChallengesAutoFocus(UINT8 unlockid, boolean fresh)
|
||||
static void M_ChallengesAutoFocus(UINT16 unlockid, boolean fresh)
|
||||
{
|
||||
UINT8 i;
|
||||
SINT8 work;
|
||||
|
||||
if (unlockid >= MAXUNLOCKABLES && gamedata->pendingkeyrounds > 0
|
||||
&& (gamedata->chaokeys < GDMAX_CHAOKEYS))
|
||||
challengesmenu.chaokeyadd = true;
|
||||
|
||||
if (fresh && unlockid >= MAXUNLOCKABLES)
|
||||
{
|
||||
UINT8 selection[MAXUNLOCKABLES];
|
||||
UINT8 numunlocks = 0;
|
||||
|
||||
// Get a random available unlockable.
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
{
|
||||
if (!unlockables[i].conditionset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!gamedata->unlocked[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
selection[numunlocks++] = i;
|
||||
}
|
||||
|
||||
if (!numunlocks)
|
||||
{
|
||||
// ...OK, get a random unlockable.
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
{
|
||||
if (!unlockables[i].conditionset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
selection[numunlocks++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
unlockid = selection[M_RandomKey(numunlocks)];
|
||||
}
|
||||
|
||||
if (unlockid >= MAXUNLOCKABLES)
|
||||
return;
|
||||
|
||||
challengesmenu.currentunlock = unlockid;
|
||||
challengesmenu.unlockcondition = M_BuildConditionSetString(challengesmenu.currentunlock);
|
||||
challengesmenu.unlockanim = 0;
|
||||
challengesmenu.unlockanim = (challengesmenu.pending && !challengesmenu.chaokeyadd ? 0 : MAXUNLOCKTIME);
|
||||
|
||||
if (gamedata->challengegrid == NULL || challengesmenu.extradata == NULL)
|
||||
return;
|
||||
|
|
@ -161,12 +203,16 @@ static void M_ChallengesAutoFocus(UINT8 unlockid, boolean fresh)
|
|||
|
||||
menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu)
|
||||
{
|
||||
UINT8 i;
|
||||
UINT16 newunlock = M_GetNextAchievedUnlock();
|
||||
UINT16 i, newunlock;
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(false);
|
||||
if (Playing())
|
||||
return desiredmenu;
|
||||
|
||||
if ((challengesmenu.pending = (newunlock < MAXUNLOCKABLES)))
|
||||
M_UpdateUnlockablesAndExtraEmblems(false, true);
|
||||
|
||||
newunlock = M_GetNextAchievedUnlock();
|
||||
|
||||
if ((challengesmenu.pending = (newunlock != MAXUNLOCKABLES)))
|
||||
{
|
||||
S_StopMusic();
|
||||
MISC_ChallengesDef.prevMenu = desiredmenu;
|
||||
|
|
@ -177,6 +223,7 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu)
|
|||
challengesmenu.ticker = 0;
|
||||
challengesmenu.requestflip = false;
|
||||
challengesmenu.requestnew = false;
|
||||
challengesmenu.chaokeyadd = false;
|
||||
challengesmenu.currentunlock = MAXUNLOCKABLES;
|
||||
challengesmenu.unlockcondition = NULL;
|
||||
|
||||
|
|
@ -210,6 +257,9 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu)
|
|||
|
||||
if (challengesmenu.pending)
|
||||
M_ChallengesAutoFocus(newunlock, true);
|
||||
else if (newunlock >= MAXUNLOCKABLES && gamedata->pendingkeyrounds > 0
|
||||
&& (gamedata->chaokeys < GDMAX_CHAOKEYS))
|
||||
challengesmenu.chaokeyadd = true;
|
||||
|
||||
return &MISC_ChallengesDef;
|
||||
}
|
||||
|
|
@ -219,7 +269,6 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu)
|
|||
|
||||
void M_Challenges(INT32 choice)
|
||||
{
|
||||
UINT8 i;
|
||||
(void)choice;
|
||||
|
||||
M_InterruptMenuWithChallenges(NULL);
|
||||
|
|
@ -227,40 +276,7 @@ void M_Challenges(INT32 choice)
|
|||
|
||||
if (gamedata->challengegrid != NULL && !challengesmenu.pending)
|
||||
{
|
||||
UINT8 selection[MAXUNLOCKABLES];
|
||||
UINT8 numunlocks = 0;
|
||||
|
||||
// Get a random available unlockable.
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
{
|
||||
if (!unlockables[i].conditionset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!gamedata->unlocked[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
selection[numunlocks++] = i;
|
||||
}
|
||||
|
||||
if (!numunlocks)
|
||||
{
|
||||
// ...OK, get a random unlockable.
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
{
|
||||
if (!unlockables[i].conditionset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
selection[numunlocks++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
M_ChallengesAutoFocus(selection[M_RandomKey(numunlocks)], true);
|
||||
M_ChallengesAutoFocus(UINT16_MAX, true);
|
||||
}
|
||||
|
||||
M_SetupNextMenu(&MISC_ChallengesDef, false);
|
||||
|
|
@ -270,7 +286,7 @@ void M_ChallengesTick(void)
|
|||
{
|
||||
const UINT8 pid = 0;
|
||||
UINT16 i;
|
||||
UINT8 newunlock = MAXUNLOCKABLES;
|
||||
UINT16 newunlock = MAXUNLOCKABLES;
|
||||
|
||||
// Ticking
|
||||
challengesmenu.ticker++;
|
||||
|
|
@ -280,8 +296,11 @@ void M_ChallengesTick(void)
|
|||
if (setup_explosions[i].tics > 0)
|
||||
setup_explosions[i].tics--;
|
||||
}
|
||||
if (challengesmenu.unlockcount[CC_ANIM] > 0)
|
||||
challengesmenu.unlockcount[CC_ANIM]--;
|
||||
for (i = CC_ANIM; i < CC_MAX; i++)
|
||||
{
|
||||
if (challengesmenu.unlockcount[i] > 0)
|
||||
challengesmenu.unlockcount[i]--;
|
||||
}
|
||||
M_CupSelectTick();
|
||||
|
||||
// Update tile flip state.
|
||||
|
|
@ -307,109 +326,158 @@ void M_ChallengesTick(void)
|
|||
}
|
||||
}
|
||||
|
||||
if (challengesmenu.pending)
|
||||
if (challengesmenu.pending && challengesmenu.fade < 5)
|
||||
{
|
||||
// Pending mode.
|
||||
// Fade increase.
|
||||
challengesmenu.fade++;
|
||||
}
|
||||
else if (challengesmenu.chaokeyadd == true)
|
||||
{
|
||||
if (challengesmenu.ticker <= 5)
|
||||
; // recreate the slight delay the unlock fades provide
|
||||
else if (gamedata->pendingkeyrounds == 0)
|
||||
{
|
||||
gamedata->keyspending = 0;
|
||||
gamedata->pendingkeyroundoffset %= GDCONVERT_ROUNDSTOKEY;
|
||||
|
||||
if (challengesmenu.requestnew)
|
||||
{
|
||||
// The menu apparatus is requesting a new unlock.
|
||||
challengesmenu.requestnew = false;
|
||||
if ((newunlock = M_GetNextAchievedUnlock()) < MAXUNLOCKABLES)
|
||||
{
|
||||
// We got one!
|
||||
M_ChallengesAutoFocus(newunlock, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// All done! Let's save the unlocks we've busted open.
|
||||
challengesmenu.pending = false;
|
||||
G_SaveGameData();
|
||||
}
|
||||
challengesmenu.chaokeyadd = false;
|
||||
challengesmenu.requestnew = true;
|
||||
}
|
||||
else if (challengesmenu.fade < 5)
|
||||
else if (gamedata->chaokeys >= GDMAX_CHAOKEYS)
|
||||
{
|
||||
// Fade increase.
|
||||
challengesmenu.fade++;
|
||||
// The above condition will run on the next tic because of this set
|
||||
gamedata->pendingkeyrounds = 0;
|
||||
gamedata->pendingkeyroundoffset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unlock sequence.
|
||||
tic_t nexttime = M_MenuExtraHeld(pid) ? (UNLOCKTIME*2) : MAXUNLOCKTIME;
|
||||
UINT32 keyexchange = gamedata->keyspending;
|
||||
|
||||
if (++challengesmenu.unlockanim >= nexttime)
|
||||
if (keyexchange > gamedata->pendingkeyrounds)
|
||||
{
|
||||
challengesmenu.requestnew = true;
|
||||
keyexchange = 1;
|
||||
}
|
||||
else if (keyexchange >= GDCONVERT_ROUNDSTOKEY/2)
|
||||
{
|
||||
keyexchange = GDCONVERT_ROUNDSTOKEY/2;
|
||||
}
|
||||
|
||||
if (challengesmenu.currentunlock < MAXUNLOCKABLES
|
||||
&& challengesmenu.unlockanim == UNLOCKTIME)
|
||||
keyexchange |= 1; // guarantee an odd delta for the sake of the sound
|
||||
|
||||
gamedata->pendingkeyrounds -= keyexchange;
|
||||
gamedata->pendingkeyroundoffset += keyexchange;
|
||||
|
||||
if (!(gamedata->pendingkeyrounds & 1))
|
||||
{
|
||||
// Unlock animation... also tied directly to the actual unlock!
|
||||
gamedata->unlocked[challengesmenu.currentunlock] = true;
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
S_StartSound(NULL, sfx_ptally);
|
||||
}
|
||||
|
||||
// Update shown description just in case..?
|
||||
challengesmenu.unlockcondition = M_BuildConditionSetString(challengesmenu.currentunlock);
|
||||
if (gamedata->pendingkeyroundoffset >= GDCONVERT_ROUNDSTOKEY)
|
||||
{
|
||||
gamedata->pendingkeyroundoffset %= GDCONVERT_ROUNDSTOKEY;
|
||||
|
||||
challengesmenu.unlockcount[CC_TALLY]++;
|
||||
challengesmenu.unlockcount[CC_ANIM]++;
|
||||
|
||||
if (challengesmenu.extradata)
|
||||
if (gamedata->keyspending > 0)
|
||||
{
|
||||
unlockable_t *ref;
|
||||
UINT16 bombcolor;
|
||||
|
||||
M_UpdateChallengeGridExtraData(challengesmenu.extradata);
|
||||
|
||||
ref = &unlockables[challengesmenu.currentunlock];
|
||||
bombcolor = SKINCOLOR_NONE;
|
||||
|
||||
if (ref->color != SKINCOLOR_NONE && ref->color < numskincolors)
|
||||
{
|
||||
bombcolor = ref->color;
|
||||
}
|
||||
else switch (ref->type)
|
||||
{
|
||||
case SECRET_SKIN:
|
||||
{
|
||||
INT32 skin = M_UnlockableSkinNum(ref);
|
||||
if (skin != -1)
|
||||
{
|
||||
bombcolor = skins[skin].prefcolor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SECRET_FOLLOWER:
|
||||
{
|
||||
INT32 skin = M_UnlockableFollowerNum(ref);
|
||||
if (skin != -1)
|
||||
{
|
||||
bombcolor = K_GetEffectiveFollowerColor(followers[skin].defaultcolor, cv_playercolor[0].value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (bombcolor == SKINCOLOR_NONE)
|
||||
{
|
||||
bombcolor = cv_playercolor[0].value;
|
||||
}
|
||||
|
||||
i = (ref->majorunlock && M_RandomChance(FRACUNIT/2)) ? 1 : 0;
|
||||
M_SetupReadyExplosions(false, challengesmenu.hilix, challengesmenu.hiliy+i, bombcolor);
|
||||
if (ref->majorunlock)
|
||||
{
|
||||
M_SetupReadyExplosions(false, challengesmenu.hilix+1, challengesmenu.hiliy+(1-i), bombcolor);
|
||||
}
|
||||
|
||||
S_StartSound(NULL, sfx_s3k4e);
|
||||
S_StartSound(NULL, sfx_achiev);
|
||||
gamedata->keyspending--;
|
||||
gamedata->chaokeys++;
|
||||
challengesmenu.unlockcount[CC_CHAOANIM]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (challengesmenu.requestnew)
|
||||
{
|
||||
// The menu apparatus is requesting a new unlock.
|
||||
challengesmenu.requestnew = false;
|
||||
if ((newunlock = M_GetNextAchievedUnlock()) != MAXUNLOCKABLES)
|
||||
{
|
||||
// We got one!
|
||||
M_ChallengesAutoFocus(newunlock, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// All done! Let's save the unlocks we've busted open.
|
||||
challengesmenu.pending = challengesmenu.chaokeyadd = false;
|
||||
G_SaveGameData();
|
||||
}
|
||||
}
|
||||
else if (challengesmenu.pending)
|
||||
{
|
||||
tic_t nexttime = M_MenuExtraHeld(pid) ? (UNLOCKTIME*2) : MAXUNLOCKTIME;
|
||||
|
||||
if (++challengesmenu.unlockanim >= nexttime)
|
||||
{
|
||||
challengesmenu.requestnew = true;
|
||||
}
|
||||
|
||||
if (challengesmenu.currentunlock < MAXUNLOCKABLES
|
||||
&& challengesmenu.unlockanim == UNLOCKTIME)
|
||||
{
|
||||
// Unlock animation... also tied directly to the actual unlock!
|
||||
gamedata->unlocked[challengesmenu.currentunlock] = true;
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
|
||||
// Update shown description just in case..?
|
||||
challengesmenu.unlockcondition = M_BuildConditionSetString(challengesmenu.currentunlock);
|
||||
|
||||
challengesmenu.unlockcount[CC_TALLY]++;
|
||||
challengesmenu.unlockcount[CC_ANIM]++;
|
||||
|
||||
if (challengesmenu.extradata)
|
||||
{
|
||||
unlockable_t *ref;
|
||||
UINT16 bombcolor;
|
||||
|
||||
M_UpdateChallengeGridExtraData(challengesmenu.extradata);
|
||||
|
||||
ref = &unlockables[challengesmenu.currentunlock];
|
||||
bombcolor = SKINCOLOR_NONE;
|
||||
|
||||
if (ref->color != SKINCOLOR_NONE && ref->color < numskincolors)
|
||||
{
|
||||
bombcolor = ref->color;
|
||||
}
|
||||
else switch (ref->type)
|
||||
{
|
||||
case SECRET_SKIN:
|
||||
{
|
||||
INT32 skin = M_UnlockableSkinNum(ref);
|
||||
if (skin != -1)
|
||||
{
|
||||
bombcolor = skins[skin].prefcolor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SECRET_FOLLOWER:
|
||||
{
|
||||
INT32 skin = M_UnlockableFollowerNum(ref);
|
||||
if (skin != -1)
|
||||
{
|
||||
bombcolor = K_GetEffectiveFollowerColor(followers[skin].defaultcolor, cv_playercolor[0].value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (bombcolor == SKINCOLOR_NONE)
|
||||
{
|
||||
bombcolor = cv_playercolor[0].value;
|
||||
}
|
||||
|
||||
i = (ref->majorunlock && M_RandomChance(FRACUNIT/2)) ? 1 : 0;
|
||||
M_SetupReadyExplosions(false, challengesmenu.hilix, challengesmenu.hiliy+i, bombcolor);
|
||||
if (ref->majorunlock)
|
||||
{
|
||||
M_SetupReadyExplosions(false, challengesmenu.hilix+1, challengesmenu.hiliy+(1-i), bombcolor);
|
||||
}
|
||||
|
||||
S_StartSound(NULL, sfx_s3k4e);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
|
@ -441,28 +509,57 @@ boolean M_ChallengesInputs(INT32 ch)
|
|||
const boolean move = (menucmd[pid].dpad_ud != 0 || menucmd[pid].dpad_lr != 0);
|
||||
(void) ch;
|
||||
|
||||
if (challengesmenu.fade)
|
||||
if (challengesmenu.fade || challengesmenu.chaokeyadd)
|
||||
{
|
||||
;
|
||||
}
|
||||
#ifdef DEVELOP
|
||||
else if (M_MenuExtraPressed(pid) && challengesmenu.extradata) // debugging
|
||||
else if (M_MenuExtraPressed(pid)
|
||||
&& challengesmenu.extradata)
|
||||
{
|
||||
if (challengesmenu.currentunlock < MAXUNLOCKABLES)
|
||||
{
|
||||
Z_Free(gamedata->challengegrid);
|
||||
gamedata->challengegrid = NULL;
|
||||
gamedata->challengegridwidth = 0;
|
||||
M_PopulateChallengeGrid();
|
||||
M_UpdateChallengeGridExtraData(challengesmenu.extradata);
|
||||
i = (challengesmenu.hilix * CHALLENGEGRIDHEIGHT) + challengesmenu.hiliy;
|
||||
|
||||
M_ChallengesAutoFocus(challengesmenu.currentunlock, true);
|
||||
if (challengesmenu.currentunlock < MAXUNLOCKABLES
|
||||
&& !gamedata->unlocked[challengesmenu.currentunlock]
|
||||
&& !unlockables[challengesmenu.currentunlock].majorunlock
|
||||
&& ((challengesmenu.extradata[i].flags & CHE_HINT)
|
||||
|| (challengesmenu.unlockcount[CC_UNLOCKED] + challengesmenu.unlockcount[CC_TALLY] == 0))
|
||||
&& gamedata->chaokeys > 0)
|
||||
{
|
||||
gamedata->chaokeys--;
|
||||
challengesmenu.unlockcount[CC_CHAOANIM]++;
|
||||
|
||||
S_StartSound(NULL, sfx_chchng);
|
||||
|
||||
challengesmenu.pending = true;
|
||||
M_ChallengesAutoFocus(challengesmenu.currentunlock, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
challengesmenu.unlockcount[CC_CHAONOPE] = 6;
|
||||
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kb2
|
||||
#if 0 // debugging
|
||||
if (challengesmenu.currentunlock < MAXUNLOCKABLES)
|
||||
{
|
||||
if (gamedata->unlocked[challengesmenu.currentunlock] && challengesmenu.unlockanim >= UNLOCKTIME)
|
||||
{
|
||||
if (challengesmenu.unlockcount[CC_TALLY] > 0)
|
||||
challengesmenu.unlockcount[CC_TALLY]--;
|
||||
else
|
||||
challengesmenu.unlockcount[CC_UNLOCKED]--;
|
||||
}
|
||||
|
||||
Z_Free(gamedata->challengegrid);
|
||||
gamedata->challengegrid = NULL;
|
||||
gamedata->challengegridwidth = 0;
|
||||
M_PopulateChallengeGrid();
|
||||
M_UpdateChallengeGridExtraData(challengesmenu.extradata);
|
||||
challengesmenu.pending = true;
|
||||
M_ChallengesAutoFocus(challengesmenu.currentunlock, true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
if (M_MenuBackPressed(pid) || start)
|
||||
|
|
|
|||
|
|
@ -8,37 +8,80 @@
|
|||
|
||||
struct statisticsmenu_s statisticsmenu;
|
||||
|
||||
static boolean M_StatisticsAddMap(UINT16 map, cupheader_t *cup, boolean *headerexists)
|
||||
{
|
||||
if (!mapheaderinfo[map])
|
||||
return false;
|
||||
|
||||
if (mapheaderinfo[map]->cup != cup)
|
||||
return false;
|
||||
|
||||
// Check for no visibility
|
||||
if (mapheaderinfo[map]->menuflags & (LF2_NOTIMEATTACK|LF2_HIDEINSTATS|LF2_HIDEINMENU))
|
||||
return false;
|
||||
|
||||
// No TEST RUN, as that's another exception to Time Attack too
|
||||
if (!mapheaderinfo[map]->typeoflevel)
|
||||
return false;
|
||||
|
||||
// Check for completion
|
||||
if ((mapheaderinfo[map]->menuflags & LF2_FINISHNEEDED)
|
||||
&& !(mapheaderinfo[map]->mapvisited & MV_BEATEN))
|
||||
return false;
|
||||
|
||||
// Check for unlock
|
||||
if (M_MapLocked(map+1))
|
||||
return false;
|
||||
|
||||
if (*headerexists == false)
|
||||
{
|
||||
statisticsmenu.maplist[statisticsmenu.nummaps++] = NEXTMAP_TITLE; // cheeky hack
|
||||
*headerexists = true;
|
||||
}
|
||||
|
||||
statisticsmenu.maplist[statisticsmenu.nummaps++] = map;
|
||||
return true;
|
||||
}
|
||||
|
||||
void M_Statistics(INT32 choice)
|
||||
{
|
||||
UINT16 i = 0;
|
||||
cupheader_t *cup;
|
||||
UINT16 i;
|
||||
boolean headerexists;
|
||||
|
||||
(void)choice;
|
||||
|
||||
statisticsmenu.maplist = Z_Malloc(sizeof(UINT16) * nummapheaders, PU_STATIC, NULL);
|
||||
statisticsmenu.maplist = Z_Malloc(sizeof(UINT16) * (nummapheaders+1 + numkartcupheaders), PU_STATIC, NULL);
|
||||
statisticsmenu.nummaps = 0;
|
||||
|
||||
for (cup = kartcupheaders; cup; cup = cup->next)
|
||||
{
|
||||
headerexists = false;
|
||||
|
||||
if (M_CupLocked(cup))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < CUPCACHE_MAX; i++)
|
||||
{
|
||||
if (cup->cachedlevels[i] >= nummapheaders)
|
||||
continue;
|
||||
|
||||
M_StatisticsAddMap(cup->cachedlevels[i], cup, &headerexists);
|
||||
}
|
||||
}
|
||||
|
||||
headerexists = false;
|
||||
|
||||
for (i = 0; i < nummapheaders; i++)
|
||||
{
|
||||
if (!mapheaderinfo[i])
|
||||
continue;
|
||||
|
||||
// Check for no visibility + legacy box
|
||||
if (mapheaderinfo[i]->menuflags & (LF2_NOTIMEATTACK|LF2_HIDEINSTATS|LF2_HIDEINMENU))
|
||||
continue;
|
||||
|
||||
// Check for completion
|
||||
if ((mapheaderinfo[i]->menuflags & LF2_FINISHNEEDED)
|
||||
&& !(mapheaderinfo[i]->mapvisited & MV_BEATEN))
|
||||
continue;
|
||||
|
||||
// Check for unlock
|
||||
if (M_MapLocked(i+1))
|
||||
continue;
|
||||
|
||||
statisticsmenu.maplist[statisticsmenu.nummaps++] = i;
|
||||
M_StatisticsAddMap(i, NULL, &headerexists);
|
||||
}
|
||||
|
||||
if ((i = statisticsmenu.numextramedals = M_CountMedals(true, true)) != 0)
|
||||
i += 2;
|
||||
|
||||
statisticsmenu.maplist[statisticsmenu.nummaps] = NEXTMAP_INVALID;
|
||||
statisticsmenu.maxscroll = (statisticsmenu.nummaps + M_CountMedals(true, true) + 2) - 10;
|
||||
statisticsmenu.maxscroll = (statisticsmenu.nummaps + i) - 11;
|
||||
statisticsmenu.location = 0;
|
||||
|
||||
if (statisticsmenu.maxscroll < 0)
|
||||
|
|
|
|||
|
|
@ -91,6 +91,9 @@ void M_InitOptions(INT32 choice)
|
|||
(M_SecretUnlocked(SECRET_ENCORE, false) ? (IT_STRING | IT_CVAR) : IT_DISABLED);
|
||||
}
|
||||
|
||||
OPTIONS_DataDef.menuitems[dopt_addon].status = (M_SecretUnlocked(SECRET_ADDONS, true)
|
||||
? (IT_STRING | IT_SUBMENU)
|
||||
: (IT_TRANSTEXT2 | IT_SPACE));
|
||||
OPTIONS_DataDef.menuitems[dopt_erase].status = (gamestate == GS_MENU
|
||||
? (IT_STRING | IT_SUBMENU)
|
||||
: (IT_TRANSTEXT2 | IT_SPACE));
|
||||
|
|
|
|||
|
|
@ -6,27 +6,31 @@
|
|||
#include "../m_cond.h" // Condition Sets
|
||||
#include "../f_finale.h"
|
||||
|
||||
#define EC_CHALLENGES 0x01
|
||||
#define EC_STATISTICS 0x02
|
||||
#define EC_TIMEATTACK 0x04
|
||||
#define EC_ALLGAME (EC_CHALLENGES|EC_STATISTICS|EC_TIMEATTACK)
|
||||
|
||||
menuitem_t OPTIONS_DataErase[] =
|
||||
{
|
||||
{IT_STRING | IT_CALL, "Erase Challenges Data", "Be careful! What's deleted is gone forever!",
|
||||
NULL, {.routine = M_EraseData}, EC_CHALLENGES, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Erase Time Attack Data", "Be careful! What's deleted is gone forever!",
|
||||
NULL, {.routine = M_EraseData}, 0, 0},
|
||||
{IT_STRING | IT_CALL, "Erase Statistics Data", "Be careful! What's deleted is gone forever!",
|
||||
NULL, {.routine = M_EraseData}, EC_STATISTICS, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Erase Unlockable Data", "Be careful! What's deleted is gone forever!",
|
||||
NULL, {.routine = M_EraseData}, 0, 0},
|
||||
{IT_STRING | IT_CALL, "Erase GP & Time Attack Data", "Be careful! What's deleted is gone forever!",
|
||||
NULL, {.routine = M_EraseData}, EC_TIMEATTACK, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "\x85\x45rase all Game Data", "Be careful! What's deleted is gone forever!",
|
||||
NULL, {.routine = M_EraseData}, EC_ALLGAME, 0},
|
||||
|
||||
{IT_SPACE | IT_NOTHING, NULL, NULL,
|
||||
NULL, {NULL}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Erase Profile Data...", "Select a Profile to erase.",
|
||||
{IT_STRING | IT_CALL, "Erase a Profile...", "Select a Profile to erase.",
|
||||
NULL, {.routine = M_CheckProfileData}, 0, 0},
|
||||
|
||||
{IT_SPACE | IT_NOTHING, NULL, NULL,
|
||||
NULL, {NULL}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "\x85\x45rase all Data", "Be careful! What's deleted is gone forever!",
|
||||
NULL, {.routine = M_EraseData}, 0, 0},
|
||||
|
||||
};
|
||||
|
||||
menu_t OPTIONS_DataEraseDef = {
|
||||
|
|
@ -53,17 +57,17 @@ static void M_EraseDataResponse(INT32 ch)
|
|||
S_StartSound(NULL, sfx_itrole); // bweh heh heh
|
||||
|
||||
// Delete the data
|
||||
if (optionsmenu.erasecontext == 2)
|
||||
{
|
||||
// SRB2Kart: This actually needs to be done FIRST, so that you don't immediately regain playtime/matches secrets
|
||||
gamedata->totalplaytime = 0;
|
||||
gamedata->matchesplayed = 0;
|
||||
}
|
||||
if (optionsmenu.erasecontext != 1)
|
||||
// see also G_LoadGameData
|
||||
// We do these in backwards order to prevent things from being immediately re-unlocked.
|
||||
if (optionsmenu.erasecontext & EC_TIMEATTACK)
|
||||
G_ClearRecords();
|
||||
if (optionsmenu.erasecontext != 0)
|
||||
if (optionsmenu.erasecontext & EC_STATISTICS)
|
||||
M_ClearStats();
|
||||
if (optionsmenu.erasecontext & EC_CHALLENGES)
|
||||
M_ClearSecrets();
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(false, true);
|
||||
|
||||
F_StartIntro();
|
||||
M_ClearMenus(true);
|
||||
}
|
||||
|
|
@ -71,15 +75,21 @@ static void M_EraseDataResponse(INT32 ch)
|
|||
void M_EraseData(INT32 choice)
|
||||
{
|
||||
const char *eschoice, *esstr = M_GetText("Are you sure you want to erase\n%s?\n\nPress (A) to confirm or (B) to cancel\n");
|
||||
(void)choice;
|
||||
|
||||
optionsmenu.erasecontext = (UINT8)choice;
|
||||
optionsmenu.erasecontext = (UINT8)currentMenu->menuitems[itemOn].mvar1;
|
||||
|
||||
if (choice == 0)
|
||||
eschoice = M_GetText("Time Attack data");
|
||||
else if (choice == 1)
|
||||
eschoice = M_GetText("Secrets data");
|
||||
else
|
||||
if (optionsmenu.erasecontext == EC_CHALLENGES)
|
||||
eschoice = M_GetText("Challenges data");
|
||||
else if (optionsmenu.erasecontext == EC_STATISTICS)
|
||||
eschoice = M_GetText("Statistics data");
|
||||
else if (optionsmenu.erasecontext == EC_TIMEATTACK)
|
||||
eschoice = M_GetText("GP & Time Attack data");
|
||||
else if (optionsmenu.erasecontext == EC_ALLGAME)
|
||||
eschoice = M_GetText("ALL game data");
|
||||
else
|
||||
eschoice = "[misconfigured erasecontext]";
|
||||
|
||||
|
||||
M_StartMessage(va(esstr, eschoice), FUNCPTRCAST(M_EraseDataResponse), MM_YESNO);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,42 @@
|
|||
/// \brief Play Menu
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../m_cond.h"
|
||||
|
||||
menuitem_t PLAY_MainMenu[] =
|
||||
{
|
||||
{IT_STRING | IT_CALL, "Local Play", "Play only on this computer.",
|
||||
NULL, {.routine = M_SetupGametypeMenu}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Online", "Connect to other computers.",
|
||||
{IT_STRING | IT_CALL, "Online", NULL,
|
||||
NULL, {.routine = M_MPOptSelectInit}, /*M_MPRoomSelectInit,*/ 0, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Back", NULL, NULL, {.routine = M_GoBack}, 0, 0},
|
||||
};
|
||||
|
||||
menu_t PLAY_MainDef = KARTGAMEMODEMENU(PLAY_MainMenu, &PLAY_CharSelectDef);
|
||||
|
||||
void M_SetupPlayMenu(INT32 choice)
|
||||
{
|
||||
#ifdef TESTERS
|
||||
(void)choice;
|
||||
#else
|
||||
if (choice != -1)
|
||||
PLAY_MainDef.prevMenu = currentMenu;
|
||||
|
||||
// Enable/disable online play.
|
||||
if (!M_SecretUnlocked(SECRET_ONLINE, true))
|
||||
{
|
||||
PLAY_MainMenu[1].status = IT_TRANSTEXT2 | IT_CALL;
|
||||
PLAY_MainMenu[1].tooltip = "You'll need experience to play over the internet!";
|
||||
}
|
||||
else
|
||||
{
|
||||
PLAY_MainMenu[1].status = IT_STRING | IT_CALL;
|
||||
PLAY_MainMenu[1].tooltip = "Connect to other computers over the internet.";
|
||||
}
|
||||
|
||||
if (choice != -1)
|
||||
M_SetupNextMenu(&PLAY_MainDef, false);
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "../r_skins.h"
|
||||
#include "../s_sound.h"
|
||||
#include "../k_grandprix.h" // K_CanChangeRules
|
||||
#include "../m_cond.h" // Condition Sets
|
||||
|
||||
menuitem_t PLAY_CharSelect[] =
|
||||
{
|
||||
|
|
@ -1503,7 +1504,7 @@ void M_CharacterSelectTick(void)
|
|||
#if defined (TESTERS)
|
||||
M_MPOptSelectInit(0);
|
||||
#else
|
||||
M_SetupNextMenu(&PLAY_MainDef, false);
|
||||
M_SetupPlayMenu(0);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ menuitem_t PLAY_GamemodesMenu[] =
|
|||
{IT_STRING | IT_CALL, "Battle", "It's last kart standing in this free-for-all!",
|
||||
"MENIMG00", {.routine = M_LevelSelectInit}, 0, GT_BATTLE},
|
||||
|
||||
{IT_STRING | IT_CALL, "Capsules", "Bust up all of the capsules in record time!",
|
||||
{IT_STRING | IT_CALL, "Prisons", "Bust up all of the Prison Eggs in record time!",
|
||||
NULL, {.routine = M_LevelSelectInit}, 1, GT_BATTLE},
|
||||
|
||||
{IT_STRING | IT_CALL, "Special", "Strike your target and secure the prize!",
|
||||
|
|
@ -42,7 +42,7 @@ void M_SetupGametypeMenu(INT32 choice)
|
|||
{
|
||||
boolean anyunlocked = false;
|
||||
|
||||
if (M_SecretUnlocked(SECRET_BREAKTHECAPSULES, true))
|
||||
if (M_SecretUnlocked(SECRET_PRISONBREAK, true))
|
||||
{
|
||||
// Re-add Capsules
|
||||
PLAY_GamemodesMenu[2].status = IT_STRING | IT_CALL;
|
||||
|
|
@ -59,7 +59,7 @@ void M_SetupGametypeMenu(INT32 choice)
|
|||
if (!anyunlocked)
|
||||
{
|
||||
// Only one non-Back entry, let's skip straight to Race.
|
||||
M_SetupRaceMenu(0);
|
||||
M_SetupRaceMenu(choice);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ void M_SetupDifficultyOptions(INT32 choice)
|
|||
PLAY_RaceDifficulty[drace_mapselect].status = IT_STRING|IT_CALL; // Level Select (Match Race)
|
||||
PLAY_RaceDifficultyDef.lastOn = drace_mapselect; // Select map select by default.
|
||||
|
||||
if (M_SecretUnlocked(SECRET_ENCORE, false))
|
||||
if (M_SecretUnlocked(SECRET_ENCORE, true))
|
||||
{
|
||||
PLAY_RaceDifficulty[drace_encore].status = IT_STRING2|IT_CVAR; // Encore on/off
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "../d_main.h" // srb2home
|
||||
#include "../m_misc.h" // M_MkdirEach
|
||||
#include "../z_zone.h" // Z_StrDup/Z_Free
|
||||
#include "../m_cond.h"
|
||||
|
||||
static void CV_SPBAttackChanged(void)
|
||||
{
|
||||
|
|
@ -21,7 +22,11 @@ struct timeattackmenu_s timeattackmenu;
|
|||
|
||||
void M_TimeAttackTick(void)
|
||||
{
|
||||
timeattackmenu.ticker++;
|
||||
timeattackmenu.ticker++;
|
||||
if (timeattackmenu.spbflicker > 0)
|
||||
{
|
||||
timeattackmenu.spbflicker--;
|
||||
}
|
||||
}
|
||||
|
||||
boolean M_TimeAttackInputs(INT32 ch)
|
||||
|
|
@ -30,10 +35,10 @@ boolean M_TimeAttackInputs(INT32 ch)
|
|||
const boolean buttonR = M_MenuButtonPressed(pid, MBT_R);
|
||||
(void) ch;
|
||||
|
||||
if (buttonR && levellist.newgametype == GT_RACE)
|
||||
if (buttonR && levellist.newgametype == GT_RACE && M_SecretUnlocked(SECRET_SPBATTACK, true))
|
||||
{
|
||||
CV_AddValue(&cv_dummyspbattack, 1);
|
||||
timeattackmenu.spbflicker = timeattackmenu.ticker;
|
||||
timeattackmenu.spbflicker = TICRATE/6;
|
||||
if (cv_dummyspbattack.value)
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k9f);
|
||||
|
|
@ -231,6 +236,9 @@ void M_PrepareTimeAttack(INT32 choice)
|
|||
}
|
||||
}
|
||||
|
||||
if (levellist.levelsearch.timeattack == false || levellist.newgametype != GT_RACE || !M_SecretUnlocked(SECRET_SPBATTACK, true))
|
||||
CV_SetValue(&cv_dummyspbattack, 0);
|
||||
|
||||
// Time-sticker Medals
|
||||
G_UpdateTimeStickerMedals(levellist.choosemap, false);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
/// \brief MULTIPLAYER OPTION SELECT
|
||||
|
||||
#include "../k_menu.h"
|
||||
#include "../m_cond.h"
|
||||
#include "../s_sound.h"
|
||||
|
||||
#if defined (TESTERS)
|
||||
#define IT_STRING_CALL_NOTESTERS IT_DISABLED
|
||||
|
|
@ -62,6 +64,15 @@ void M_MPOptSelectInit(INT32 choice)
|
|||
INT16 arrcpy[3][3] = {{0,68,0}, {0,12,0}, {0,74,0}};
|
||||
const UINT32 forbidden = GTR_FORBIDMP;
|
||||
|
||||
#ifndef TESTERS
|
||||
if (choice != -1 && !M_SecretUnlocked(SECRET_ONLINE, true))
|
||||
{
|
||||
M_StartMessage("Online play is ""\x8B""not yet unlocked""\x80"".\n\nYou'll want experience in ""\x8B""Grand Prix""\x80""\nbefore even thinking about facing\nopponents from across the world.\n\nPress (B)", NULL, MM_NOTHING);
|
||||
S_StartSound(NULL, sfx_s3k36);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
mpmenu.modechoice = 0;
|
||||
mpmenu.ticker = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "../k_menu.h"
|
||||
#include "../s_sound.h"
|
||||
#include "../m_cond.h"
|
||||
|
||||
menuitem_t PLAY_MP_RoomSelect[] =
|
||||
{
|
||||
|
|
@ -30,23 +31,23 @@ void M_MPRoomSelect(INT32 choice)
|
|||
const UINT8 pid = 0;
|
||||
(void) choice;
|
||||
|
||||
if (menucmd[pid].dpad_lr)
|
||||
{
|
||||
mpmenu.room = (!mpmenu.room) ? 1 : 0;
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
else if (M_MenuBackPressed(pid))
|
||||
if (M_MenuBackPressed(pid))
|
||||
{
|
||||
M_GoBack(0);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
else if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
M_ServersMenu(0);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
else if (mpmenu.roomforced == false
|
||||
&& menucmd[pid].dpad_lr != 0)
|
||||
{
|
||||
mpmenu.room ^= 1;
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
}
|
||||
|
||||
void M_MPRoomSelectTick(void)
|
||||
|
|
@ -57,7 +58,8 @@ void M_MPRoomSelectTick(void)
|
|||
void M_MPRoomSelectInit(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
mpmenu.room = 0;
|
||||
mpmenu.room = (modifiedgame == true) ? 1 : 0;
|
||||
mpmenu.roomforced = ((modifiedgame == true) || (!M_SecretUnlocked(SECRET_ADDONS, true)));
|
||||
mpmenu.ticker = 0;
|
||||
mpmenu.servernum = 0;
|
||||
mpmenu.scrolln = 0;
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ void M_CupSelectHandler(INT32 choice)
|
|||
|
||||
if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/)
|
||||
{
|
||||
INT16 count;
|
||||
UINT16 count;
|
||||
cupheader_t *newcup = cupgrid.builtgrid[CUPMENU_CURSORID];
|
||||
cupheader_t *oldcup = levellist.levelsearch.cup;
|
||||
|
||||
|
|
@ -94,7 +94,7 @@ void M_CupSelectHandler(INT32 choice)
|
|||
count = M_CountLevelsToShowInList(&levellist.levelsearch);
|
||||
|
||||
if ((!newcup)
|
||||
|| (count <= 0)
|
||||
|| (count == 0)
|
||||
|| (cupgrid.grandprix == true && newcup->cachedlevels[0] == NEXTMAP_INVALID))
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3kb2);
|
||||
|
|
@ -161,7 +161,7 @@ void M_CupSelectHandler(INT32 choice)
|
|||
|
||||
restoreMenu = &PLAY_CupSelectDef;
|
||||
}
|
||||
else if (count == 1)
|
||||
else if (count == 1 && levellist.levelsearch.timeattack == true)
|
||||
{
|
||||
PLAY_TimeAttackDef.transitionID = currentMenu->transitionID+1;
|
||||
M_LevelSelected(0);
|
||||
|
|
@ -174,6 +174,7 @@ void M_CupSelectHandler(INT32 choice)
|
|||
levellist.cursor = 0;
|
||||
}
|
||||
|
||||
levellist.mapcount = count;
|
||||
M_LevelSelectScrollDest();
|
||||
levellist.y = levellist.dest;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
#include "../../f_finale.h" // F_WipeStartScreen
|
||||
#include "../../v_video.h"
|
||||
|
||||
cupheader_t dummy_lostandfound;
|
||||
|
||||
menuitem_t PLAY_LevelSelect[] =
|
||||
{
|
||||
{IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, {.routine = M_LevelSelectHandler}, 0, 0},
|
||||
|
|
@ -57,8 +59,9 @@ boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch)
|
|||
if (mapheaderinfo[mapnum]->lumpnum == LUMPERROR)
|
||||
return false;
|
||||
|
||||
// Check for TOL
|
||||
if (!(mapheaderinfo[mapnum]->typeoflevel & levelsearch->typeoflevel))
|
||||
// Check for TOL (permits TEST RUN outside of time attack)
|
||||
if ((levelsearch->timeattack || mapheaderinfo[mapnum]->typeoflevel)
|
||||
&& !(mapheaderinfo[mapnum]->typeoflevel & levelsearch->typeoflevel))
|
||||
return false;
|
||||
|
||||
// Should the map be hidden?
|
||||
|
|
@ -70,10 +73,14 @@ boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch)
|
|||
return false;
|
||||
|
||||
// Don't permit cup when no cup requested (also no dupes in time attack)
|
||||
if (levelsearch->cupmode
|
||||
&& (levelsearch->timeattack || !levelsearch->cup)
|
||||
&& mapheaderinfo[mapnum]->cup != levelsearch->cup)
|
||||
return false;
|
||||
if (levelsearch->cupmode)
|
||||
{
|
||||
cupheader_t *cup = (levelsearch->cup == &dummy_lostandfound) ? NULL : levelsearch->cup;
|
||||
|
||||
if ((!cup || levelsearch->timeattack)
|
||||
&& mapheaderinfo[mapnum]->cup != cup)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finally, the most complex check: does the map have lock conditions?
|
||||
if (levelsearch->checklocked)
|
||||
|
|
@ -97,9 +104,9 @@ UINT16 M_CountLevelsToShowInList(levelsearch_t *levelsearch)
|
|||
INT16 i, count = 0;
|
||||
|
||||
if (!levelsearch)
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
if (levelsearch->cup)
|
||||
if (levelsearch->cup && levelsearch->cup != &dummy_lostandfound)
|
||||
{
|
||||
if (levelsearch->checklocked && M_CupLocked(levelsearch->cup))
|
||||
return 0;
|
||||
|
|
@ -126,9 +133,9 @@ UINT16 M_GetFirstLevelInList(UINT8 *i, levelsearch_t *levelsearch)
|
|||
INT16 mapnum = NEXTMAP_INVALID;
|
||||
|
||||
if (!levelsearch)
|
||||
return false;
|
||||
return NEXTMAP_INVALID;
|
||||
|
||||
if (levelsearch->cup)
|
||||
if (levelsearch->cup && levelsearch->cup != &dummy_lostandfound)
|
||||
{
|
||||
if (levelsearch->checklocked && M_CupLocked(levelsearch->cup))
|
||||
{
|
||||
|
|
@ -151,6 +158,9 @@ UINT16 M_GetFirstLevelInList(UINT8 *i, levelsearch_t *levelsearch)
|
|||
for (mapnum = 0; mapnum < nummapheaders; mapnum++)
|
||||
if (M_CanShowLevelInList(mapnum, levelsearch))
|
||||
break;
|
||||
|
||||
if (mapnum >= nummapheaders)
|
||||
mapnum = NEXTMAP_INVALID;
|
||||
}
|
||||
|
||||
return mapnum;
|
||||
|
|
@ -159,9 +169,9 @@ UINT16 M_GetFirstLevelInList(UINT8 *i, levelsearch_t *levelsearch)
|
|||
UINT16 M_GetNextLevelInList(UINT16 mapnum, UINT8 *i, levelsearch_t *levelsearch)
|
||||
{
|
||||
if (!levelsearch)
|
||||
return false;
|
||||
return NEXTMAP_INVALID;
|
||||
|
||||
if (levelsearch->cup)
|
||||
if (levelsearch->cup && levelsearch->cup != &dummy_lostandfound)
|
||||
{
|
||||
mapnum = NEXTMAP_INVALID;
|
||||
(*i)++;
|
||||
|
|
@ -185,14 +195,14 @@ UINT16 M_GetNextLevelInList(UINT16 mapnum, UINT8 *i, levelsearch_t *levelsearch)
|
|||
|
||||
void M_LevelSelectScrollDest(void)
|
||||
{
|
||||
UINT16 m = M_CountLevelsToShowInList(&levellist.levelsearch)-1;
|
||||
UINT16 m = levellist.mapcount-1;
|
||||
|
||||
levellist.dest = (6*levellist.cursor);
|
||||
|
||||
if (levellist.dest < 3)
|
||||
levellist.dest = 3;
|
||||
|
||||
if (levellist.dest > (6*m)-3)
|
||||
if (m && levellist.dest > (6*m)-3)
|
||||
levellist.dest = (6*m)-3;
|
||||
}
|
||||
|
||||
|
|
@ -209,6 +219,9 @@ boolean M_LevelListFromGametype(INT16 gt)
|
|||
{
|
||||
cupgrid.cappages = 0;
|
||||
cupgrid.builtgrid = NULL;
|
||||
dummy_lostandfound.cachedlevels[0] = NEXTMAP_INVALID;
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
levellist.newgametype = gt;
|
||||
|
|
@ -227,11 +240,8 @@ boolean M_LevelListFromGametype(INT16 gt)
|
|||
|
||||
levellist.levelsearch.cupmode = (!(gametypes[gt]->rules & GTR_NOCUPSELECT));
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (levellist.levelsearch.timeattack == false || levellist.newgametype != GT_RACE)
|
||||
CV_SetValue(&cv_dummyspbattack, 0);
|
||||
}
|
||||
|
||||
// Obviously go to Cup Select in gametypes that have cups.
|
||||
// Use a really long level select in gametypes that don't use cups.
|
||||
|
|
@ -266,6 +276,31 @@ boolean M_LevelListFromGametype(INT16 gt)
|
|||
}
|
||||
memset(cupgrid.builtgrid, 0, cupgrid.cappages * pagelen);
|
||||
|
||||
// The following doubles the size of the buffer if necessary.
|
||||
#define GRID_INSERTCUP \
|
||||
if ((currentid * sizeof(cupheader_t*)) >= cupgrid.cappages * pagelen) \
|
||||
{ \
|
||||
const size_t firstlen = cupgrid.cappages * pagelen; \
|
||||
cupgrid.builtgrid = Z_Realloc(cupgrid.builtgrid, \
|
||||
firstlen * 2, \
|
||||
PU_STATIC, NULL); \
|
||||
\
|
||||
if (!cupgrid.builtgrid) \
|
||||
{ \
|
||||
I_Error("M_LevelListFromGametype: Not enough memory to reallocate builtgrid"); \
|
||||
} \
|
||||
\
|
||||
cupgrid.cappages *= 2; \
|
||||
} \
|
||||
\
|
||||
cupgrid.builtgrid[currentid] = templevelsearch.cup;
|
||||
|
||||
#define GRID_FOCUSCUP \
|
||||
cupgrid.x = currentid % CUPMENU_COLUMNS; \
|
||||
cupgrid.y = (currentid / CUPMENU_COLUMNS) % CUPMENU_ROWS; \
|
||||
cupgrid.pageno = currentid / (CUPMENU_COLUMNS * CUPMENU_ROWS); \
|
||||
currentvalid = true;
|
||||
|
||||
while (templevelsearch.cup)
|
||||
{
|
||||
templevelsearch.checklocked = false;
|
||||
|
|
@ -278,23 +313,7 @@ boolean M_LevelListFromGametype(INT16 gt)
|
|||
|
||||
foundany = true;
|
||||
|
||||
if ((currentid * sizeof(cupheader_t*)) >= cupgrid.cappages * pagelen)
|
||||
{
|
||||
// Double the size of the buffer, and clear the other stuff.
|
||||
const size_t firstlen = cupgrid.cappages * pagelen;
|
||||
cupgrid.builtgrid = Z_Realloc(cupgrid.builtgrid,
|
||||
firstlen * 2,
|
||||
PU_STATIC, NULL);
|
||||
|
||||
if (!cupgrid.builtgrid)
|
||||
{
|
||||
I_Error("M_LevelListFromGametype: Not enough memory to reallocate builtgrid");
|
||||
}
|
||||
|
||||
cupgrid.cappages *= 2;
|
||||
}
|
||||
|
||||
cupgrid.builtgrid[currentid] = templevelsearch.cup;
|
||||
GRID_INSERTCUP;
|
||||
|
||||
templevelsearch.checklocked = true;
|
||||
if (M_GetFirstLevelInList(&temp, &templevelsearch) != NEXTMAP_INVALID)
|
||||
|
|
@ -305,10 +324,7 @@ boolean M_LevelListFromGametype(INT16 gt)
|
|||
? (mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->cup == templevelsearch.cup)
|
||||
: (gt == -1 && levellist.levelsearch.cup == templevelsearch.cup))
|
||||
{
|
||||
cupgrid.x = currentid % CUPMENU_COLUMNS;
|
||||
cupgrid.y = (currentid / CUPMENU_COLUMNS) % CUPMENU_ROWS;
|
||||
cupgrid.pageno = currentid / (CUPMENU_COLUMNS * CUPMENU_ROWS);
|
||||
currentvalid = true;
|
||||
GRID_FOCUSCUP;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -316,6 +332,34 @@ boolean M_LevelListFromGametype(INT16 gt)
|
|||
templevelsearch.cup = templevelsearch.cup->next;
|
||||
}
|
||||
|
||||
// Lost and found, a simplified version of the above loop.
|
||||
if (cupgrid.grandprix == false)
|
||||
{
|
||||
templevelsearch.cup = &dummy_lostandfound;
|
||||
templevelsearch.checklocked = true;
|
||||
|
||||
if (M_GetFirstLevelInList(&temp, &levellist.levelsearch) != NEXTMAP_INVALID)
|
||||
{
|
||||
foundany = true;
|
||||
GRID_INSERTCUP;
|
||||
highestunlockedid = currentid;
|
||||
|
||||
if (Playing()
|
||||
? (mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->cup == NULL)
|
||||
: (gt == -1 && levellist.levelsearch.cup == templevelsearch.cup))
|
||||
{
|
||||
GRID_FOCUSCUP;
|
||||
}
|
||||
|
||||
currentid++;
|
||||
}
|
||||
|
||||
templevelsearch.cup = NULL;
|
||||
}
|
||||
|
||||
#undef GRID_INSERTCUP
|
||||
#undef GRID_FOCUSCUP
|
||||
|
||||
if (foundany == false)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -356,6 +400,7 @@ boolean M_LevelListFromGametype(INT16 gt)
|
|||
levellist.levelsearch.cup = NULL;
|
||||
}
|
||||
|
||||
levellist.mapcount = M_CountLevelsToShowInList(&levellist.levelsearch);
|
||||
M_LevelSelectScrollDest();
|
||||
levellist.y = levellist.dest;
|
||||
|
||||
|
|
@ -518,7 +563,6 @@ void M_LevelSelected(INT16 add)
|
|||
|
||||
void M_LevelSelectHandler(INT32 choice)
|
||||
{
|
||||
INT16 maxlevels = M_CountLevelsToShowInList(&levellist.levelsearch);
|
||||
const UINT8 pid = 0;
|
||||
|
||||
(void)choice;
|
||||
|
|
@ -531,7 +575,7 @@ void M_LevelSelectHandler(INT32 choice)
|
|||
if (menucmd[pid].dpad_ud > 0)
|
||||
{
|
||||
levellist.cursor++;
|
||||
if (levellist.cursor >= maxlevels)
|
||||
if (levellist.cursor >= levellist.mapcount)
|
||||
levellist.cursor = 0;
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
|
|
@ -540,7 +584,7 @@ void M_LevelSelectHandler(INT32 choice)
|
|||
{
|
||||
levellist.cursor--;
|
||||
if (levellist.cursor < 0)
|
||||
levellist.cursor = maxlevels-1;
|
||||
levellist.cursor = levellist.mapcount-1;
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ static inline size_t M_StringHeight(const char *string)
|
|||
void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
size_t max = 0, start = 0, strlines = 0, i;
|
||||
size_t max = 0, maxatstart = 0, start = 0, strlines, i;
|
||||
static char *message = NULL;
|
||||
Z_Free(message);
|
||||
message = Z_StrDup(string);
|
||||
|
|
@ -41,12 +41,13 @@ void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtyp
|
|||
{
|
||||
start = i;
|
||||
max += 4;
|
||||
maxatstart = max;
|
||||
}
|
||||
else if (message[i] == '\n')
|
||||
{
|
||||
strlines = i;
|
||||
start = 0;
|
||||
max = 0;
|
||||
maxatstart = 0;
|
||||
continue;
|
||||
}
|
||||
else if (message[i] & 0x80)
|
||||
|
|
@ -58,8 +59,7 @@ void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtyp
|
|||
if (max >= BASEVIDWIDTH && start > 0)
|
||||
{
|
||||
message[start] = '\n';
|
||||
max -= (start-strlines)*8;
|
||||
strlines = start;
|
||||
max -= maxatstart;
|
||||
start = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "../../k_menu.h"
|
||||
#include "../../k_grandprix.h" // K_CanChangeRules
|
||||
#include "../../m_cond.h"
|
||||
#include "../../s_sound.h"
|
||||
|
||||
// ESC pause menu
|
||||
|
|
@ -136,7 +137,11 @@ void M_OpenPauseMenu(void)
|
|||
|
||||
PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_CALL;
|
||||
PAUSE_Main[mpause_restartmap].status = IT_STRING | IT_CALL;
|
||||
PAUSE_Main[mpause_addons].status = IT_STRING | IT_CALL;
|
||||
|
||||
if (M_SecretUnlocked(SECRET_ADDONS, true))
|
||||
{
|
||||
PAUSE_Main[mpause_addons].status = IT_STRING | IT_CALL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!netgame && !demo.playback)
|
||||
|
|
|
|||
|
|
@ -534,9 +534,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
if (P_IsLocalPlayer(player) && !gamedata->collected[special->health-1])
|
||||
{
|
||||
gamedata->collected[special->health-1] = gotcollected = true;
|
||||
if (!M_UpdateUnlockablesAndExtraEmblems(true))
|
||||
if (!M_UpdateUnlockablesAndExtraEmblems(true, true))
|
||||
S_StartSound(NULL, sfx_ncitem);
|
||||
G_SaveGameData();
|
||||
gamedata->deferredsave = true;
|
||||
}
|
||||
|
||||
if (netgame)
|
||||
|
|
@ -636,7 +636,7 @@ static void P_AddBrokenPrison(mobj_t *target, mobj_t *source)
|
|||
{
|
||||
(void)target;
|
||||
|
||||
if (!battlecapsules) // !battleprisons
|
||||
if (!battleprisons)
|
||||
return;
|
||||
|
||||
if ((gametyperules & GTR_POINTLIMIT) && (source && source->player))
|
||||
|
|
@ -845,7 +845,7 @@ void P_CheckPointLimit(void)
|
|||
if (!(gametyperules & GTR_POINTLIMIT))
|
||||
return;
|
||||
|
||||
if (battlecapsules)
|
||||
if (battleprisons)
|
||||
return;
|
||||
|
||||
// pointlimit is nonzero, check if it's been reached by this player
|
||||
|
|
@ -1953,6 +1953,13 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source,
|
|||
switch (type)
|
||||
{
|
||||
case DMG_DEATHPIT:
|
||||
// Fell off the stage
|
||||
if (player->roundconditions.fell_off == false)
|
||||
{
|
||||
player->roundconditions.fell_off = true;
|
||||
player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
|
||||
if (gametyperules & GTR_BUMPERS)
|
||||
{
|
||||
player->mo->health--;
|
||||
|
|
@ -2087,6 +2094,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
player_t *player;
|
||||
player_t *playerInflictor;
|
||||
boolean force = false;
|
||||
boolean spbpop = false;
|
||||
|
||||
INT32 laglength = 6;
|
||||
|
||||
|
|
@ -2125,6 +2133,16 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
return false;
|
||||
break;
|
||||
|
||||
case MT_SPB:
|
||||
spbpop = (damagetype & DMG_TYPEMASK) == DMG_VOLTAGE;
|
||||
if (spbpop && source && source->player
|
||||
&& source->player->roundconditions.spb_neuter == false)
|
||||
{
|
||||
source->player->roundconditions.spb_neuter = true;
|
||||
source->player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -2143,7 +2161,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
|
||||
if (!force)
|
||||
{
|
||||
if (!(target->type == MT_SPB && (damagetype & DMG_TYPEMASK) == DMG_VOLTAGE))
|
||||
if (!spbpop)
|
||||
{
|
||||
if (!(target->flags & MF_SHOOTABLE))
|
||||
return false; // shouldn't happen...
|
||||
|
|
@ -2190,6 +2208,18 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
}
|
||||
}
|
||||
|
||||
if (inflictor && source && source->player)
|
||||
{
|
||||
if (source->player->roundconditions.hit_midair == false
|
||||
&& K_IsMissileOrKartItem(source)
|
||||
&& target->player->airtime > TICRATE/2
|
||||
&& source->player->airtime > TICRATE/2)
|
||||
{
|
||||
source->player->roundconditions.hit_midair = true;
|
||||
source->player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Instant-Death
|
||||
if ((damagetype & DMG_DEATHMASK))
|
||||
{
|
||||
|
|
|
|||
16
src/p_mobj.c
16
src/p_mobj.c
|
|
@ -3427,6 +3427,20 @@ void P_MobjCheckWater(mobj_t *mobj)
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(p->roundconditions.wet_player & MFE_TOUCHWATER)
|
||||
&& (mobj->eflags & MFE_TOUCHWATER))
|
||||
{
|
||||
p->roundconditions.wet_player |= MFE_TOUCHWATER;
|
||||
p->roundconditions.checkthisframe = true;
|
||||
}
|
||||
|
||||
if (!(p->roundconditions.wet_player & MFE_UNDERWATER)
|
||||
&& (mobj->eflags & MFE_UNDERWATER))
|
||||
{
|
||||
p->roundconditions.wet_player |= MFE_UNDERWATER;
|
||||
p->roundconditions.checkthisframe = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mobj->flags & MF_APPLYTERRAIN)
|
||||
|
|
@ -9381,7 +9395,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
{
|
||||
if (gametyperules & GTR_PAPERITEMS)
|
||||
{
|
||||
if (battlecapsules == true)
|
||||
if (battleprisons == true)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -488,6 +488,10 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEFIXED(save->p, players[i].loop.shift.x);
|
||||
WRITEFIXED(save->p, players[i].loop.shift.y);
|
||||
WRITEUINT8(save->p, players[i].loop.flip);
|
||||
|
||||
// ACS has read access to this, so it has to be net-communicated.
|
||||
// It is the ONLY roundcondition that is sent over the wire and I'd like it to stay that way.
|
||||
WRITEUINT32(save->p, players[i].roundconditions.unlocktriggers);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -876,6 +880,10 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].loop.shift.y = READFIXED(save->p);
|
||||
players[i].loop.flip = READUINT8(save->p);
|
||||
|
||||
// ACS has read access to this, so it has to be net-communicated.
|
||||
// It is the ONLY roundcondition that is sent over the wire and I'd like it to stay that way.
|
||||
players[i].roundconditions.unlocktriggers = READUINT32(save->p);
|
||||
|
||||
//players[i].viewheight = P_GetPlayerViewHeight(players[i]); // scale cannot be factored in at this point
|
||||
}
|
||||
}
|
||||
|
|
@ -4997,7 +5005,7 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
|
|||
// SRB2kart
|
||||
WRITEINT32(save->p, numgotboxes);
|
||||
WRITEUINT8(save->p, numtargets);
|
||||
WRITEUINT8(save->p, battlecapsules);
|
||||
WRITEUINT8(save->p, battleprisons);
|
||||
|
||||
WRITEUINT8(save->p, gamespeed);
|
||||
WRITEUINT8(save->p, numlaps);
|
||||
|
|
@ -5166,7 +5174,7 @@ static inline boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
|
|||
// SRB2kart
|
||||
numgotboxes = READINT32(save->p);
|
||||
numtargets = READUINT8(save->p);
|
||||
battlecapsules = (boolean)READUINT8(save->p);
|
||||
battleprisons = (boolean)READUINT8(save->p);
|
||||
|
||||
gamespeed = READUINT8(save->p);
|
||||
numlaps = READUINT8(save->p);
|
||||
|
|
|
|||
|
|
@ -7071,7 +7071,7 @@ static void P_InitLevelSettings(void)
|
|||
nummaprings = 0;
|
||||
nummapboxes = numgotboxes = 0;
|
||||
maptargets = numtargets = 0;
|
||||
battlecapsules = false;
|
||||
battleprisons = false;
|
||||
|
||||
// emerald hunt
|
||||
hunt1 = hunt2 = hunt3 = NULL;
|
||||
|
|
@ -7501,6 +7501,7 @@ static void P_InitGametype(void)
|
|||
|
||||
// Started a game? Move on to the next jam when you go back to the title screen
|
||||
CV_SetValue(&cv_menujam_update, 1);
|
||||
gamedata->musicflags = 0;
|
||||
}
|
||||
|
||||
struct minimapinfo minimapinfo;
|
||||
|
|
@ -7993,7 +7994,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
{
|
||||
mapheaderinfo[gamemap-1]->mapvisited |= MV_VISITED;
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
|
||||
|
|
|
|||
29
src/p_spec.c
29
src/p_spec.c
|
|
@ -1541,17 +1541,18 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
|
|||
return false;
|
||||
break;
|
||||
case 317:
|
||||
if (actor && actor->player)
|
||||
{ // Unlockable triggers required
|
||||
INT32 trigid = triggerline->args[1];
|
||||
|
||||
if ((modifiedgame && !savemoddata) || (netgame || multiplayer))
|
||||
return false;
|
||||
else if (trigid < 0 || trigid > 31) // limited by 32 bit variable
|
||||
if (trigid < 0 || trigid > 31) // limited by 32 bit variable
|
||||
{
|
||||
CONS_Debug(DBG_GAMELOGIC, "Unlockable trigger (sidedef %hu): bad trigger ID %d\n", triggerline->sidenum[0], trigid);
|
||||
return false;
|
||||
}
|
||||
else if (!(unlocktriggers & (1 << trigid)))
|
||||
else if (!(actor && actor->player))
|
||||
return false;
|
||||
else if (!(actor->player->roundconditions.unlocktriggers & (1 << trigid)))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
|
@ -2111,6 +2112,12 @@ static void K_HandleLapIncrement(player_t *player)
|
|||
}
|
||||
|
||||
lastLowestLap = lowestLap;
|
||||
|
||||
if (P_IsLocalPlayer(player))
|
||||
{
|
||||
player->roundconditions.checkthisframe = true;
|
||||
gamedata->deferredconditioncheck = true;
|
||||
}
|
||||
}
|
||||
else if (player->starpostnum)
|
||||
{
|
||||
|
|
@ -3420,29 +3427,23 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
|
|||
break;
|
||||
|
||||
case 441: // Trigger unlockable
|
||||
if (!(demo.playback || netgame || multiplayer))
|
||||
{
|
||||
INT32 trigid = args[0];
|
||||
|
||||
if (trigid < 0 || trigid > 31) // limited by 32 bit variable
|
||||
CONS_Debug(DBG_GAMELOGIC, "Unlockable trigger: bad trigger ID %d\n", trigid);
|
||||
else
|
||||
else if (mo && mo->player)
|
||||
{
|
||||
UINT32 flag = 1 << trigid;
|
||||
|
||||
if (unlocktriggers & flag)
|
||||
if (mo->player->roundconditions.unlocktriggers & flag)
|
||||
{
|
||||
// Execute one time only
|
||||
break;
|
||||
}
|
||||
|
||||
unlocktriggers |= flag;
|
||||
|
||||
// Unlocked something?
|
||||
if (M_UpdateUnlockablesAndExtraEmblems(true))
|
||||
{
|
||||
G_SaveGameData(); // only save if unlocked something
|
||||
}
|
||||
mo->player->roundconditions.unlocktriggers |= flag;
|
||||
mo->player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -708,6 +708,11 @@ void P_Ticker(boolean run)
|
|||
}
|
||||
|
||||
ps_playerthink_time = I_GetPreciseTime() - ps_playerthink_time;
|
||||
|
||||
// TODO would this be laggy with more conditions in play...
|
||||
if (((!demo.playback && leveltime > introtime && M_UpdateUnlockablesAndExtraEmblems(true, false))
|
||||
|| (gamedata && gamedata->deferredsave)))
|
||||
G_SaveGameData();
|
||||
}
|
||||
|
||||
// Keep track of how long they've been playing!
|
||||
|
|
|
|||
17
src/p_user.c
17
src/p_user.c
|
|
@ -509,6 +509,11 @@ INT32 P_GivePlayerRings(player_t *player, INT32 num_rings)
|
|||
if ((gametyperules & GTR_SPHERES)) // No rings in Battle Mode
|
||||
return 0;
|
||||
|
||||
if (gamedata && num_rings > 0 && P_IsLocalPlayer(player) && gamedata->totalrings <= GDMAX_RINGS)
|
||||
{
|
||||
gamedata->totalrings += num_rings;
|
||||
}
|
||||
|
||||
test = player->rings + num_rings;
|
||||
if (test > 20) // Caps at 20 rings, sorry!
|
||||
num_rings -= (test-20);
|
||||
|
|
@ -517,6 +522,12 @@ INT32 P_GivePlayerRings(player_t *player, INT32 num_rings)
|
|||
|
||||
player->rings += num_rings;
|
||||
|
||||
if (player->roundconditions.debt_rings == false && player->rings < 0)
|
||||
{
|
||||
player->roundconditions.debt_rings = true;
|
||||
player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
|
||||
return num_rings;
|
||||
}
|
||||
|
||||
|
|
@ -1268,7 +1279,11 @@ void P_DoPlayerExit(player_t *player)
|
|||
return;
|
||||
|
||||
if (P_IsLocalPlayer(player) && (!player->spectator && !demo.playback))
|
||||
{
|
||||
legitimateexit = true;
|
||||
player->roundconditions.checkthisframe = true;
|
||||
gamedata->deferredconditioncheck = true;
|
||||
}
|
||||
|
||||
if (G_GametypeUsesLives() && losing)
|
||||
{
|
||||
|
|
@ -3807,6 +3822,8 @@ void P_DoTimeOver(player_t *player)
|
|||
if (P_IsLocalPlayer(player) && !demo.playback)
|
||||
{
|
||||
legitimateexit = true; // SRB2kart: losing a race is still seeing it through to the end :p
|
||||
player->roundconditions.checkthisframe = true;
|
||||
gamedata->deferredconditioncheck = true;
|
||||
}
|
||||
|
||||
if (netgame && !player->bot && !(gametyperules & GTR_BOSS))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2016-2023 by Vivian "toastergrl" Grannell
|
||||
// Copyright (C) 1993-1996 by id Software, Inc.
|
||||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||||
// Copyright (C) 1999-2020 by Sonic Team Junior.
|
||||
|
|
@ -129,6 +130,27 @@ static void Sk_SetDefaultValue(skin_t *skin)
|
|||
skin->soundsid[S_sfx[i].skinsound] = i;
|
||||
}
|
||||
|
||||
// Grab the default skin
|
||||
UINT8 R_BotDefaultSkin(void)
|
||||
{
|
||||
static INT32 defaultbotskin = -1;
|
||||
|
||||
if (defaultbotskin == -1)
|
||||
{
|
||||
const char *defaultbotskinname = "eggrobo";
|
||||
|
||||
defaultbotskin = R_SkinAvailable(defaultbotskinname);
|
||||
|
||||
if (defaultbotskin == -1)
|
||||
{
|
||||
// This shouldn't happen, but just in case
|
||||
defaultbotskin = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return (UINT8)defaultbotskin;
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize the basic skins
|
||||
//
|
||||
|
|
@ -156,13 +178,15 @@ void R_InitSkins(void)
|
|||
R_LoadSpriteInfoLumps(i, wadfiles[i]->numlumps);
|
||||
}
|
||||
ST_ReloadSkinFaceGraphics();
|
||||
M_UpdateConditionSetsPending();
|
||||
}
|
||||
|
||||
UINT8 *R_GetSkinAvailabilities(boolean demolock)
|
||||
UINT8 *R_GetSkinAvailabilities(boolean demolock, boolean forbots)
|
||||
{
|
||||
UINT8 i, shif, byte;
|
||||
INT32 skinid;
|
||||
static UINT8 responsebuffer[MAXAVAILABILITY];
|
||||
UINT8 defaultbotskin = R_BotDefaultSkin();
|
||||
|
||||
memset(&responsebuffer, 0, sizeof(responsebuffer));
|
||||
|
||||
|
|
@ -171,15 +195,17 @@ UINT8 *R_GetSkinAvailabilities(boolean demolock)
|
|||
if (unlockables[i].type != SECRET_SKIN)
|
||||
continue;
|
||||
|
||||
// NEVER EVER EVER M_CheckNetUnlockByID
|
||||
if (gamedata->unlocked[i] != true && !demolock)
|
||||
continue;
|
||||
|
||||
skinid = M_UnlockableSkinNum(&unlockables[i]);
|
||||
|
||||
if (skinid < 0 || skinid >= MAXSKINS)
|
||||
continue;
|
||||
|
||||
if ((forbots
|
||||
? (M_CheckNetUnlockByID(i) || skinid == defaultbotskin) // Assert the host's lock.
|
||||
: gamedata->unlocked[i]) // Assert the local lock.
|
||||
!= true && !demolock)
|
||||
continue;
|
||||
|
||||
shif = (skinid % 8);
|
||||
byte = (skinid / 8);
|
||||
|
||||
|
|
@ -250,8 +276,11 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins)
|
|||
return !!(players[playernum].availabilities[byte] & (1 << shif));
|
||||
}
|
||||
|
||||
// Use the host's if it's checking general state
|
||||
if (playernum == -1)
|
||||
return M_CheckNetUnlockByID(i);
|
||||
|
||||
// Use the unlockables table directly
|
||||
// NOTE: M_CheckNetUnlockByID would be correct in many circumstances... but not all. TODO figure out how to discern.
|
||||
return (boolean)(gamedata->unlocked[i]);
|
||||
}
|
||||
|
||||
|
|
@ -270,6 +299,25 @@ INT32 R_SkinAvailable(const char *name)
|
|||
return -1;
|
||||
}
|
||||
|
||||
// Returns engine class dependent on skin properties
|
||||
engineclass_t R_GetEngineClass(SINT8 speed, SINT8 weight, skinflags_t flags)
|
||||
{
|
||||
if (flags & SF_IRONMAN)
|
||||
return ENGINECLASS_J;
|
||||
|
||||
speed = (speed - 1) / 3;
|
||||
weight = (weight - 1) / 3;
|
||||
|
||||
#define LOCKSTAT(stat) \
|
||||
if (stat < 0) { stat = 0; } \
|
||||
if (stat > 2) { stat = 2; }
|
||||
LOCKSTAT(speed);
|
||||
LOCKSTAT(weight);
|
||||
#undef LOCKSTAT
|
||||
|
||||
return (speed + (3*weight));
|
||||
}
|
||||
|
||||
// Auxillary function that actually sets the skin
|
||||
static void SetSkin(player_t *player, INT32 skinnum)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2016-2023 by Vivian "toastergrl" Grannell
|
||||
// Copyright (C) 1993-1996 by id Software, Inc.
|
||||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||||
// Copyright (C) 1999-2020 by Sonic Team Junior.
|
||||
|
|
@ -74,6 +75,25 @@ enum facepatches {
|
|||
NUMFACES
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ENGINECLASS_A,
|
||||
ENGINECLASS_B,
|
||||
ENGINECLASS_C,
|
||||
|
||||
ENGINECLASS_D,
|
||||
ENGINECLASS_E,
|
||||
ENGINECLASS_F,
|
||||
|
||||
ENGINECLASS_G,
|
||||
ENGINECLASS_H,
|
||||
ENGINECLASS_I,
|
||||
|
||||
ENGINECLASS_J
|
||||
|
||||
} engineclass_t;
|
||||
|
||||
engineclass_t R_GetEngineClass(SINT8 speed, SINT8 weight, skinflags_t flags);
|
||||
|
||||
/// Externs
|
||||
extern INT32 numskins;
|
||||
extern skin_t skins[MAXSKINS];
|
||||
|
|
@ -91,8 +111,9 @@ void ClearFakePlayerSkin(player_t* player);
|
|||
boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins);
|
||||
INT32 GetSkinNumClosestToStats(UINT8 kartspeed, UINT8 kartweight, UINT32 flags, boolean unlock);
|
||||
|
||||
UINT8 *R_GetSkinAvailabilities(boolean demolock);
|
||||
UINT8 *R_GetSkinAvailabilities(boolean demolock, boolean forbots);
|
||||
INT32 R_SkinAvailable(const char *name);
|
||||
UINT8 R_BotDefaultSkin(void);
|
||||
|
||||
void R_PatchSkins(UINT16 wadnum, boolean mainfile);
|
||||
void R_AddSkins(UINT16 wadnum, boolean mainfile);
|
||||
|
|
|
|||
|
|
@ -328,6 +328,7 @@ FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num)
|
|||
{
|
||||
D_QuitNetGame(); // Fix server freezes
|
||||
CL_AbortDownloadResume();
|
||||
G_DirtyGameData();
|
||||
#ifdef UNIXBACKTRACE
|
||||
write_backtrace(num);
|
||||
#endif
|
||||
|
|
@ -724,6 +725,8 @@ static void I_RegisterSignals (void)
|
|||
#ifdef NEWSIGNALHANDLER
|
||||
static void signal_handler_child(INT32 num)
|
||||
{
|
||||
G_DirtyGameData();
|
||||
|
||||
#ifdef UNIXBACKTRACE
|
||||
write_backtrace(num);
|
||||
#endif
|
||||
|
|
@ -1542,6 +1545,7 @@ void I_Error(const char *error, ...)
|
|||
if (errorcount == 8)
|
||||
{
|
||||
M_SaveConfig(NULL);
|
||||
G_DirtyGameData(); // done first in case an error is in G_SaveGameData
|
||||
G_SaveGameData();
|
||||
}
|
||||
if (errorcount > 20)
|
||||
|
|
@ -1573,6 +1577,7 @@ void I_Error(const char *error, ...)
|
|||
|
||||
M_SaveConfig(NULL); // save game config, cvars..
|
||||
D_SaveBan(); // save the ban list
|
||||
G_DirtyGameData(); // done first in case an error is in G_SaveGameData
|
||||
G_SaveGameData(); // Tails 12-08-2002
|
||||
|
||||
// Shutdown. Here might be other errors.
|
||||
|
|
|
|||
|
|
@ -357,6 +357,7 @@ static void signal_handler(INT32 num)
|
|||
sigmsg = sigdef;
|
||||
}
|
||||
|
||||
G_DirtyGameData();
|
||||
I_OutputMsg("signal_handler() error: %s\n", sigmsg);
|
||||
signal(num, SIG_DFL); //default signal action
|
||||
raise(num);
|
||||
|
|
@ -3083,6 +3084,7 @@ void I_Error(const char *error, ...)
|
|||
if (errorcount == 9)
|
||||
{
|
||||
M_SaveConfig(NULL);
|
||||
G_DirtyGameData(); // done first in case an error is in G_SaveGameData
|
||||
G_SaveGameData();
|
||||
}
|
||||
if (errorcount > 20)
|
||||
|
|
@ -3147,6 +3149,7 @@ void I_Error(const char *error, ...)
|
|||
#ifndef NONET
|
||||
D_SaveBan(); // save the ban list
|
||||
#endif
|
||||
G_DirtyGameData(); // done first in case an error is in G_SaveGameData
|
||||
G_SaveGameData(); // Tails 12-08-2002
|
||||
|
||||
// Shutdown. Here might be other errors.
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ TYPEDEF (discordRequest_t);
|
|||
// d_player.h
|
||||
TYPEDEF (respawnvars_t);
|
||||
TYPEDEF (botvars_t);
|
||||
TYPEDEF (roundconditions_t);
|
||||
TYPEDEF (skybox_t);
|
||||
TYPEDEF (itemroulette_t);
|
||||
TYPEDEF (altview_t);
|
||||
|
|
@ -107,6 +108,7 @@ TYPEDEF (skincolor_t);
|
|||
// doomstat.h
|
||||
TYPEDEF (precipprops_t);
|
||||
TYPEDEF (recorddata_t);
|
||||
TYPEDEF (cupwindata_t);
|
||||
TYPEDEF (scene_t);
|
||||
TYPEDEF (cutscene_t);
|
||||
TYPEDEF (textpage_t);
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
#include "fastcmp.h"
|
||||
|
||||
#include "g_game.h" // G_LoadGameData
|
||||
#include "m_cond.h" // gamedata itself
|
||||
#include "filesrch.h"
|
||||
|
||||
#include "i_video.h" // rendermode
|
||||
|
|
@ -813,6 +814,14 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup)
|
|||
}
|
||||
#endif
|
||||
|
||||
// Do this immediately before anything of consequence that invalidates gamedata can happen.
|
||||
if ((mainfile == false) && (gamedata != NULL) && (gamedata->everloadedaddon == false))
|
||||
{
|
||||
gamedata->everloadedaddon = true;
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
|
||||
switch(type = ResourceFileDetect(filename))
|
||||
{
|
||||
case RET_SOC:
|
||||
|
|
|
|||
|
|
@ -469,6 +469,7 @@ static void signal_handler(int num)
|
|||
char sigdef[64];
|
||||
|
||||
D_QuitNetGame(); // Fix server freezes
|
||||
G_DirtyGameData();
|
||||
I_ShutdownSystem();
|
||||
|
||||
switch (num)
|
||||
|
|
@ -607,6 +608,7 @@ void I_Error(const char *error, ...)
|
|||
if (errorcount == 7)
|
||||
{
|
||||
M_SaveConfig(NULL);
|
||||
G_DirtyGameData(); // done first in case an error is in G_SaveGameData
|
||||
G_SaveGameData();
|
||||
}
|
||||
if (errorcount > 20)
|
||||
|
|
@ -636,6 +638,7 @@ void I_Error(const char *error, ...)
|
|||
if (!errorcount)
|
||||
{
|
||||
M_SaveConfig(NULL); // save game config, cvars..
|
||||
G_DirtyGameData(); // done first in case an error is in G_SaveGameData
|
||||
G_SaveGameData();
|
||||
}
|
||||
|
||||
|
|
@ -726,7 +729,7 @@ void I_Quit(void)
|
|||
G_CheckDemoStatus();
|
||||
|
||||
M_SaveConfig(NULL); // save game config, cvars..
|
||||
G_SaveGameData();
|
||||
G_SaveGameData(); // undirty your save
|
||||
|
||||
// maybe it needs that the ticcount continues,
|
||||
// or something else that will be finished by I_ShutdownSystem(),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue