mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-04-13 05:36:22 +00:00
Merge branch 'break-through-them-all' into 'master'
Break Through Them All Closes #704 See merge request KartKrew/Kart!1570
This commit is contained in:
commit
a5bd49b882
47 changed files with 3163 additions and 655 deletions
|
|
@ -918,7 +918,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, false), MAXAVAILABILITY*sizeof(UINT8));
|
||||
memcpy(&netbuffer->u.clientcfg.availabilities, R_GetSkinAvailabilities(false, -1), MAXAVAILABILITY*sizeof(UINT8));
|
||||
|
||||
// Don't leak old signatures from prior sessions.
|
||||
memset(&netbuffer->u.clientcfg.challengeResponse, 0, sizeof(((clientconfig_pak *)0)->challengeResponse));
|
||||
|
|
@ -3968,7 +3968,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, false);
|
||||
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(false, -1);
|
||||
SINT8 node = 0;
|
||||
for (; node < MAXNETNODES; node++)
|
||||
result |= SV_AddWaitingPlayers(node, availabilitiesbuffer,
|
||||
|
|
@ -5607,6 +5607,9 @@ static INT16 Consistancy(void)
|
|||
|
||||
mo = (mobj_t *)th;
|
||||
|
||||
if (TypeIsNetSynced(mo->type) == false)
|
||||
continue;
|
||||
|
||||
if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY))
|
||||
{
|
||||
ret -= mo->type;
|
||||
|
|
@ -5620,7 +5623,7 @@ static INT16 Consistancy(void)
|
|||
ret -= mo->flags;
|
||||
ret += mo->flags2;
|
||||
ret -= mo->eflags;
|
||||
if (mo->target)
|
||||
if (mo->target && TypeIsNetSynced(mo->target->type))
|
||||
{
|
||||
ret += mo->target->type;
|
||||
ret -= mo->target->x;
|
||||
|
|
@ -5636,11 +5639,11 @@ static INT16 Consistancy(void)
|
|||
ret -= mo->target->state - states;
|
||||
ret += mo->target->tics;
|
||||
ret -= mo->target->sprite;
|
||||
ret += mo->target->frame;
|
||||
//ret += mo->target->frame;
|
||||
}
|
||||
else
|
||||
ret ^= 0x3333;
|
||||
if (mo->tracer && mo->tracer->type != MT_OVERLAY)
|
||||
if (mo->tracer && TypeIsNetSynced(mo->tracer->type))
|
||||
{
|
||||
ret += mo->tracer->type;
|
||||
ret -= mo->tracer->x;
|
||||
|
|
@ -5656,12 +5659,12 @@ static INT16 Consistancy(void)
|
|||
ret -= mo->tracer->state - states;
|
||||
ret += mo->tracer->tics;
|
||||
ret -= mo->tracer->sprite;
|
||||
ret += mo->tracer->frame;
|
||||
//ret += mo->tracer->frame;
|
||||
}
|
||||
else
|
||||
ret ^= 0xAAAA;
|
||||
// SRB2Kart: We use hnext & hprev very extensively
|
||||
if (mo->hnext && mo->hnext->type != MT_OVERLAY)
|
||||
if (mo->hnext && TypeIsNetSynced(mo->hnext->type))
|
||||
{
|
||||
ret += mo->hnext->type;
|
||||
ret -= mo->hnext->x;
|
||||
|
|
@ -5677,11 +5680,11 @@ static INT16 Consistancy(void)
|
|||
ret -= mo->hnext->state - states;
|
||||
ret += mo->hnext->tics;
|
||||
ret -= mo->hnext->sprite;
|
||||
ret += mo->hnext->frame;
|
||||
//ret += mo->hnext->frame;
|
||||
}
|
||||
else
|
||||
ret ^= 0x5555;
|
||||
if (mo->hprev && mo->hprev->type != MT_OVERLAY)
|
||||
if (mo->hprev && TypeIsNetSynced(mo->hprev->type))
|
||||
{
|
||||
ret += mo->hprev->type;
|
||||
ret -= mo->hprev->x;
|
||||
|
|
@ -5697,14 +5700,14 @@ static INT16 Consistancy(void)
|
|||
ret -= mo->hprev->state - states;
|
||||
ret += mo->hprev->tics;
|
||||
ret -= mo->hprev->sprite;
|
||||
ret += mo->hprev->frame;
|
||||
//ret += mo->hprev->frame;
|
||||
}
|
||||
else
|
||||
ret ^= 0xCCCC;
|
||||
ret -= mo->state - states;
|
||||
ret += mo->tics;
|
||||
ret -= mo->sprite;
|
||||
ret += mo->frame;
|
||||
//ret += mo->frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Maximum laps per map.
|
||||
// (done here as p_local.h, the previous host, has this as a dependency - but we must use it here)
|
||||
#define MAX_LAPS 99
|
||||
|
||||
// Extra abilities/settings for skins (combinable stuff)
|
||||
typedef enum
|
||||
{
|
||||
|
|
@ -383,6 +387,19 @@ struct botvars_t
|
|||
|
||||
// player_t struct for round-specific condition tracking
|
||||
|
||||
typedef enum
|
||||
{
|
||||
UFOD_GENERIC = 1,
|
||||
UFOD_BOOST = 1<<1,
|
||||
UFOD_WHIP = 1<<2,
|
||||
UFOD_BANANA = 1<<3,
|
||||
UFOD_ORBINAUT = 1<<4,
|
||||
UFOD_JAWZ = 1<<5,
|
||||
UFOD_SPB = 1<<6,
|
||||
UFOD_GACHABOM = 1<<7,
|
||||
// free up to and including 1<<31
|
||||
} targetdamaging_t;
|
||||
|
||||
struct roundconditions_t
|
||||
{
|
||||
// Reduce the number of checks by only updating when this is true
|
||||
|
|
@ -393,10 +410,31 @@ struct roundconditions_t
|
|||
boolean touched_offroad;
|
||||
boolean touched_sneakerpanel;
|
||||
boolean debt_rings;
|
||||
boolean faulted;
|
||||
|
||||
// Basically the same, but it's a specific event where no is an easy default
|
||||
boolean tripwire_hyuu;
|
||||
boolean whip_hyuu;
|
||||
boolean spb_neuter;
|
||||
boolean landmine_dunk;
|
||||
boolean hit_midair;
|
||||
boolean hit_drafter_lookback;
|
||||
boolean giant_foe_shrunken_orbi;
|
||||
boolean returntosender_mark;
|
||||
|
||||
UINT8 hittrackhazard[((MAX_LAPS+1)/8) + 1];
|
||||
|
||||
// Attack-based conditions
|
||||
targetdamaging_t targetdamaging;
|
||||
UINT8 gachabom_miser;
|
||||
|
||||
fixed_t maxspeed;
|
||||
|
||||
tic_t continuousdraft;
|
||||
tic_t continuousdraft_best;
|
||||
|
||||
UINT8 consecutive_grow_lasers;
|
||||
UINT8 best_consecutive_grow_lasers;
|
||||
|
||||
mobjeflag_t wet_player;
|
||||
|
||||
|
|
|
|||
512
src/deh_soc.c
512
src/deh_soc.c
|
|
@ -73,6 +73,8 @@ fixed_t get_number(const char *word)
|
|||
|
||||
#define PARAMCHECK(n) do { if (!params[n]) { deh_warning("Too few parameters, need %d", n); return; }} while (0)
|
||||
|
||||
#define EXTENDEDPARAMCHECK(spos, n) do { if (!spos || !(*spos)) { deh_warning("Missing extended parameter, need at least %d", n); return; }} while (0)
|
||||
|
||||
/* ======================================================================== */
|
||||
// Load a dehacked file format
|
||||
/* ======================================================================== */
|
||||
|
|
@ -1220,6 +1222,8 @@ void readlevelheader(MYFILE *f, char * name)
|
|||
break;
|
||||
deh_strlcpy(mapheaderinfo[num]->musname[j], tmp,
|
||||
sizeof(mapheaderinfo[num]->musname[j]), va("Level header %d: music", num));
|
||||
if (j)
|
||||
mapheaderinfo[num]->cache_muslock[j - 1] = MAXUNLOCKABLES;
|
||||
j++;
|
||||
} while ((tmp = strtok(NULL,",")) != NULL);
|
||||
|
||||
|
|
@ -1379,6 +1383,28 @@ void readlevelheader(MYFILE *f, char * name)
|
|||
}
|
||||
else if (fastcmp(word, "GRAVITY"))
|
||||
mapheaderinfo[num]->gravity = FLOAT_TO_FIXED(atof(word2));
|
||||
else if (fastcmp(word, "DESTROYOBJECTSFORCHALLENGES"))
|
||||
{
|
||||
if (fastcmp(word2, "NONE"))
|
||||
{
|
||||
mapheaderinfo[num]->destroyforchallenge_size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
UINT8 j = 0; // i was declared elsewhere
|
||||
tmp = strtok(word2, ",");
|
||||
do {
|
||||
if (j >= MAXDESTRUCTIBLES)
|
||||
break;
|
||||
mapheaderinfo[num]->destroyforchallenge[j] = get_mobjtype(word2);
|
||||
j++;
|
||||
} while ((tmp = strtok(NULL,",")) != NULL);
|
||||
|
||||
if (tmp != NULL)
|
||||
deh_warning("Level header %d: additional destructibles past %d discarded", num, MAXDESTRUCTIBLES);
|
||||
mapheaderinfo[num]->destroyforchallenge_size = j;
|
||||
}
|
||||
}
|
||||
else
|
||||
deh_warning("Level header %d: unknown word '%s'", num, word);
|
||||
}
|
||||
|
|
@ -2295,6 +2321,55 @@ void reademblemdata(MYFILE *f, INT32 num)
|
|||
Z_Free(s);
|
||||
}
|
||||
|
||||
static INT16 parseunlockabletype(char *type)
|
||||
{
|
||||
if (fastcmp(type, "EXTRAMEDAL"))
|
||||
return SECRET_EXTRAMEDAL;
|
||||
else if (fastcmp(type, "CUP"))
|
||||
return SECRET_CUP;
|
||||
else if (fastcmp(type, "MAP"))
|
||||
return SECRET_MAP;
|
||||
else if (fastcmp(type, "ALTMUSIC"))
|
||||
return SECRET_ALTMUSIC;
|
||||
else if (fastcmp(type, "SKIN"))
|
||||
return SECRET_SKIN;
|
||||
else if (fastcmp(type, "FOLLOWER"))
|
||||
return SECRET_FOLLOWER;
|
||||
else if (fastcmp(type, "COLOR"))
|
||||
return SECRET_COLOR;
|
||||
|
||||
else if (fastcmp(type, "HARDSPEED"))
|
||||
return SECRET_HARDSPEED;
|
||||
else if (fastcmp(type, "MASTERMODE"))
|
||||
return SECRET_MASTERMODE;
|
||||
else if (fastcmp(type, "ENCORE"))
|
||||
return SECRET_ENCORE;
|
||||
else if (fastcmp(type, "TIMEATTACK"))
|
||||
return SECRET_TIMEATTACK;
|
||||
else if (fastcmp(type, "PRISONBREAK"))
|
||||
return SECRET_PRISONBREAK;
|
||||
else if (fastcmp(type, "SPECIALATTACK"))
|
||||
return SECRET_SPECIALATTACK;
|
||||
else if (fastcmp(type, "SPBATTACK"))
|
||||
return SECRET_SPBATTACK;
|
||||
else if (fastcmp(type, "ONLINE"))
|
||||
return SECRET_ONLINE;
|
||||
else if (fastcmp(type, "ADDONS"))
|
||||
return SECRET_ADDONS;
|
||||
else if (fastcmp(type, "EGGTV"))
|
||||
return SECRET_EGGTV;
|
||||
else if (fastcmp(type, "SOUNDTEST"))
|
||||
return SECRET_SOUNDTEST;
|
||||
else if (fastcmp(type, "ALTTITLE"))
|
||||
return SECRET_ALTTITLE;
|
||||
else if (fastcmp(type, "MEMETAUNTS"))
|
||||
return SECRET_MEMETAUNTS;
|
||||
else if (fastcmp(type, "ITEMFINDER"))
|
||||
return SECRET_ITEMFINDER;
|
||||
|
||||
return SECRET_NONE;
|
||||
}
|
||||
|
||||
void readunlockable(MYFILE *f, INT32 num)
|
||||
{
|
||||
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
||||
|
|
@ -2349,50 +2424,7 @@ void readunlockable(MYFILE *f, INT32 num)
|
|||
unlockables[num].majorunlock = (UINT8)(i != 0 || word2[0] == 'T' || word2[0] == 'Y');
|
||||
else if (fastcmp(word, "TYPE"))
|
||||
{
|
||||
if (fastcmp(word2, "NONE"))
|
||||
unlockables[num].type = SECRET_NONE;
|
||||
else if (fastcmp(word2, "EXTRAMEDAL"))
|
||||
unlockables[num].type = SECRET_EXTRAMEDAL;
|
||||
else if (fastcmp(word2, "CUP"))
|
||||
unlockables[num].type = SECRET_CUP;
|
||||
else if (fastcmp(word2, "MAP"))
|
||||
unlockables[num].type = SECRET_MAP;
|
||||
else if (fastcmp(word2, "SKIN"))
|
||||
unlockables[num].type = SECRET_SKIN;
|
||||
else if (fastcmp(word2, "FOLLOWER"))
|
||||
unlockables[num].type = SECRET_FOLLOWER;
|
||||
else if (fastcmp(word2, "COLOR"))
|
||||
unlockables[num].type = SECRET_COLOR;
|
||||
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, "TIMEATTACK"))
|
||||
unlockables[num].type = SECRET_TIMEATTACK;
|
||||
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"))
|
||||
unlockables[num].type = SECRET_ALTTITLE;
|
||||
else if (fastcmp(word2, "MEMETAUNTS"))
|
||||
unlockables[num].type = SECRET_MEMETAUNTS;
|
||||
else if (fastcmp(word2, "ITEMFINDER"))
|
||||
unlockables[num].type = SECRET_ITEMFINDER;
|
||||
else
|
||||
unlockables[num].type = (INT16)i;
|
||||
unlockables[num].type = parseunlockabletype(word2);
|
||||
unlockables[num].stringVarCache = -1;
|
||||
}
|
||||
else if (fastcmp(word, "VAR"))
|
||||
|
|
@ -2420,47 +2452,133 @@ void readunlockable(MYFILE *f, INT32 num)
|
|||
Z_Free(s);
|
||||
}
|
||||
|
||||
// This is a home-grown strtok(" ") equivalent so we can isolate the first chunk without destroying the rest of the line.
|
||||
static void conditiongetparam(char **params, UINT8 paramid, char **spos)
|
||||
{
|
||||
if (*spos == NULL || *(*spos) == '\0')
|
||||
{
|
||||
params[paramid] = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
params[paramid] = *spos;
|
||||
while (*(*spos) != '\0' && *(*spos) != ' ')
|
||||
{
|
||||
*(*spos) = toupper(*(*spos));
|
||||
(*spos)++;
|
||||
}
|
||||
if (*(*spos) == ' ')
|
||||
{
|
||||
*(*spos) = '\0';
|
||||
(*spos)++;
|
||||
|
||||
while (*(*spos) == ' ')
|
||||
(*spos)++;
|
||||
}
|
||||
}
|
||||
|
||||
static void readcondition(UINT16 set, UINT32 id, char *word2)
|
||||
{
|
||||
INT32 i;
|
||||
char *params[5]; // condition, requirement, extra info, extra info, stringvar
|
||||
char *spos;
|
||||
const UINT8 MAXCONDITIONPARAMS = 5;
|
||||
char *params[MAXCONDITIONPARAMS]; // condition, requirement, extra info, extra info, stringvar
|
||||
char *spos = NULL;
|
||||
char *stringvar = NULL;
|
||||
|
||||
conditiontype_t ty;
|
||||
conditiontype_t ty = UC_NONE;
|
||||
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 < 5; ++i)
|
||||
// Lop the leading spaces off
|
||||
if (word2 && *word2)
|
||||
{
|
||||
if (spos != NULL)
|
||||
{
|
||||
params[i] = spos;
|
||||
spos = strtok(NULL, " ");
|
||||
}
|
||||
else
|
||||
params[i] = NULL;
|
||||
spos = word2;
|
||||
while (*spos == ' ')
|
||||
spos++;
|
||||
}
|
||||
|
||||
conditiongetparam(params, 0, &spos);
|
||||
|
||||
if (!params[0])
|
||||
{
|
||||
deh_warning("condition line is empty for condition ID %d", id+1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fastcmp(params[0], "PLAYTIME"))
|
||||
// We do free descriptions first.
|
||||
|
||||
if (fastcmp(params[0], "DESCRIPTIONOVERRIDE"))
|
||||
{
|
||||
EXTENDEDPARAMCHECK(spos, 1);
|
||||
ty = UC_DESCRIPTIONOVERRIDE;
|
||||
|
||||
stringvar = Z_StrDup(spos);
|
||||
}
|
||||
else if (fastcmp(params[0], "PASSWORD"))
|
||||
{
|
||||
EXTENDEDPARAMCHECK(spos, 1);
|
||||
ty = UC_PASSWORD;
|
||||
|
||||
stringvar = Z_StrDup(spos);
|
||||
}
|
||||
|
||||
if (ty != UC_NONE)
|
||||
goto setcondition;
|
||||
|
||||
// Now conditions that take one standard param and one free description.
|
||||
|
||||
conditiongetparam(params, 1, &spos);
|
||||
|
||||
if (fastcmp(params[0], "WETPLAYER"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_PLAYTIME + offset;
|
||||
re = atoi(params[1]);
|
||||
//EXTENDEDPARAMCHECK(spos, 2);
|
||||
ty = UCRP_WETPLAYER;
|
||||
re = MFE_UNDERWATER;
|
||||
x1 = 1;
|
||||
|
||||
if (fastcmp(params[1], "STRICT"))
|
||||
re |= MFE_TOUCHWATER;
|
||||
else if (fastcmp(params[1], "STANDARD"))
|
||||
;
|
||||
else
|
||||
{
|
||||
deh_warning("liquid strictness requirement \"%s\" invalid for condition ID %d", params[1], id+1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (spos && *spos)
|
||||
stringvar = Z_StrDup(spos);
|
||||
}
|
||||
else if (fastcmp(params[0], "MAPDESTROYOBJECTS"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
EXTENDEDPARAMCHECK(spos, 2);
|
||||
ty = UCRP_MAPDESTROYOBJECTS;
|
||||
re = G_MapNumber(params[1]);
|
||||
|
||||
stringvar = Z_StrDup(spos);
|
||||
}
|
||||
|
||||
if (ty != UC_NONE)
|
||||
goto setcondition;
|
||||
|
||||
// Now for all other conditions.
|
||||
|
||||
for (i = 2; i < MAXCONDITIONPARAMS; i++)
|
||||
{
|
||||
conditiongetparam(params, i, &spos);
|
||||
}
|
||||
|
||||
if (ty != UC_NONE)
|
||||
;
|
||||
else if (fastcmp(params[0], "PLAYTIME"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_PLAYTIME;
|
||||
re = get_number(params[1]);
|
||||
}
|
||||
else if (fastcmp(params[0], "ROUNDSPLAYED"))
|
||||
{
|
||||
|
|
@ -2506,6 +2624,12 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "TOTALTUMBLETIME"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_TOTALTUMBLETIME;
|
||||
re = get_number(params[1]);
|
||||
}
|
||||
else if (fastcmp(params[0], "GAMECLEAR"))
|
||||
{
|
||||
ty = UC_GAMECLEAR;
|
||||
|
|
@ -2515,12 +2639,13 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_OVERALLTIME;
|
||||
re = atoi(params[1]);
|
||||
re = get_number(params[1]);
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "MAPVISITED")
|
||||
|| (++offset && fastcmp(params[0], "MAPBEATEN"))
|
||||
|| (++offset && fastcmp(params[0], "MAPENCORE"))
|
||||
|| (++offset && fastcmp(params[0], "MAPSPBATTACK")))
|
||||
|| (++offset && fastcmp(params[0], "MAPSPBATTACK"))
|
||||
|| (++offset && fastcmp(params[0], "MAPMYSTICMELODY")))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_MAPVISITED + offset;
|
||||
|
|
@ -2536,7 +2661,7 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
{
|
||||
PARAMCHECK(2);
|
||||
ty = UC_MAPTIME;
|
||||
re = atoi(params[2]);
|
||||
re = get_number(params[2]);
|
||||
x1 = G_MapNumber(params[1]);
|
||||
|
||||
if (x1 >= nummapheaders)
|
||||
|
|
@ -2617,6 +2742,40 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "UNLOCKPERCENT"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_UNLOCKPERCENT;
|
||||
re = atoi(params[1]);
|
||||
x1 = SECRET_NONE;
|
||||
|
||||
// Valid percentages only!
|
||||
if (re <= 0 || re > 100)
|
||||
{
|
||||
deh_warning("Condition percent %d out of range (1 - 100) for condition ID %d", re, id+1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (params[2] && params[2][0])
|
||||
{
|
||||
x1 = parseunlockabletype(params[2]);
|
||||
|
||||
if (x1 <= SECRET_NONE || x1 >= SECRET_ONEPERBOARD)
|
||||
{
|
||||
deh_warning("Condition challenge type \"%s\" invalid for condition ID %d", params[2], id+1);
|
||||
return;
|
||||
}
|
||||
|
||||
x2 = 0;
|
||||
if (params[3])
|
||||
{
|
||||
// fudge value
|
||||
x2 = atoi(params[3]);
|
||||
}
|
||||
}
|
||||
else
|
||||
x2 = 1; // guaranteed fudge for raw Unlockables count
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "ADDON")
|
||||
|| (++offset && fastcmp(params[0], "CREDITS"))
|
||||
|| (++offset && fastcmp(params[0], "REPLAY"))
|
||||
|
|
@ -2625,13 +2784,6 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
//PARAMCHECK(1);
|
||||
ty = UC_ADDON + offset;
|
||||
}
|
||||
else if (fastcmp(params[0], "PASSWORD"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_PASSWORD;
|
||||
stringvar = Z_StrDup(params[1]);
|
||||
re = -1;
|
||||
}
|
||||
else if (fastcmp(params[0], "SPRAYCAN"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
|
|
@ -2641,6 +2793,22 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
// Force at head of the list?
|
||||
x1 = (params[2] && (params[2][0] == 'Y' || params[2][0] == 'T')) ? 1 : 0;
|
||||
}
|
||||
else if (fastcmp(params[0], "PRISONEGGCD"))
|
||||
{
|
||||
ty = UC_PRISONEGGCD;
|
||||
re = NEXTMAP_INVALID;
|
||||
|
||||
if (params[1])
|
||||
{
|
||||
re = G_MapNumber(params[1]);
|
||||
|
||||
if (re >= nummapheaders)
|
||||
{
|
||||
deh_warning("Invalid level %s for condition ID %d", params[1], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "AND")
|
||||
|| (++offset && fastcmp(params[0], "COMMA")))
|
||||
{
|
||||
|
|
@ -2691,6 +2859,13 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "HASFOLLOWER"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_HASFOLLOWER;
|
||||
stringvar = Z_StrDup(params[1]);
|
||||
re = -1;
|
||||
}
|
||||
else if (fastcmp(params[0], "ISDIFFICULTY"))
|
||||
{
|
||||
//PARAMCHECK(1);
|
||||
|
|
@ -2701,9 +2876,9 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
if (fastcmp(params[1], "NORMAL"))
|
||||
;
|
||||
else if (fastcmp(params[1], "HARD"))
|
||||
x1 = KARTSPEED_HARD;
|
||||
re = KARTSPEED_HARD;
|
||||
else if (fastcmp(params[1], "MASTER"))
|
||||
x1 = KARTGP_MASTER;
|
||||
re = KARTGP_MASTER;
|
||||
else
|
||||
{
|
||||
deh_warning("gamespeed requirement \"%s\" invalid for condition ID %d", params[1], id+1);
|
||||
|
|
@ -2715,6 +2890,9 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_PODIUMCUP;
|
||||
|
||||
re = -1;
|
||||
if (!fastcmp(params[1], "ANY"))
|
||||
{
|
||||
cupheader_t *cup = kartcupheaders;
|
||||
UINT32 hash = quickncasehash(params[1], MAXCUPNAME);
|
||||
|
|
@ -2768,18 +2946,29 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
}
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "PODIUMEMERALD")
|
||||
|| (++offset && fastcmp(params[0], "PODIUMPRIZE")))
|
||||
|| (++offset && fastcmp(params[0], "PODIUMPRIZE"))
|
||||
|| (++offset && fastcmp(params[0], "PODIUMNOCONTINUES")))
|
||||
{
|
||||
//PARAMCHECK(1);
|
||||
ty = UCRP_PODIUMEMERALD + offset;
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "FINISHCOOL")
|
||||
|| (++offset && fastcmp(params[0], "FINISHPERFECT"))
|
||||
|| (++offset && fastcmp(params[0], "FINISHALLPRISONS"))
|
||||
|| (++offset && fastcmp(params[0], "NOCONTEST")))
|
||||
|| (++offset && fastcmp(params[0], "NOCONTEST"))
|
||||
|| (++offset && fastcmp(params[0], "SMASHUFO"))
|
||||
|| (++offset && fastcmp(params[0], "CHASEDBYSPB")))
|
||||
{
|
||||
//PARAMCHECK(1);
|
||||
ty = UCRP_FINISHCOOL + offset;
|
||||
}
|
||||
else if (fastcmp(params[0], "MAKERETIRE"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_MAKERETIRE;
|
||||
stringvar = Z_StrDup(params[1]);
|
||||
re = -1;
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "FINISHPLACE")
|
||||
|| (++offset && fastcmp(params[0], "FINISHPLACEEXACT")))
|
||||
{
|
||||
|
|
@ -2793,6 +2982,31 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "FINISHGRADE"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_FINISHGRADE;
|
||||
|
||||
re = -1;
|
||||
if (!params[1][1])
|
||||
{
|
||||
switch (params[1][0])
|
||||
{
|
||||
case 'E': { re = GRADE_E; break; }
|
||||
case 'D': { re = GRADE_D; break; }
|
||||
case 'C': { re = GRADE_C; break; }
|
||||
case 'B': { re = GRADE_B; break; }
|
||||
case 'A': { re = GRADE_A; break; }
|
||||
default: { break; }
|
||||
}
|
||||
}
|
||||
|
||||
if (re == -1)
|
||||
{
|
||||
deh_warning("Invalid grade %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")))
|
||||
|
|
@ -2807,6 +3021,55 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "RINGS")
|
||||
|| (++offset && fastcmp(params[0], "RINGSEXACT")))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_RINGS + offset;
|
||||
re = get_number(params[1]);
|
||||
|
||||
if (re < -20 || re > 20)
|
||||
{
|
||||
deh_warning("Invalid ring count %d for condition ID %d", re, id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "SPEEDOMETER"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_SPEEDOMETER;
|
||||
re = get_number(params[1]);
|
||||
|
||||
if (re < 100 || re > 999)
|
||||
{
|
||||
deh_warning("Speed percent %d out of range (100 - 999) for condition ID %d", re, id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "DRAFTDURATION"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_DRAFTDURATION;
|
||||
re = get_number(params[1]);
|
||||
|
||||
if (re < 5)
|
||||
{
|
||||
deh_warning("Duration %d seconds too low for condition ID %d", re, id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "GROWCONSECUTIVEBEAMS"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_GROWCONSECUTIVEBEAMS;
|
||||
re = get_number(params[1]);
|
||||
|
||||
if (re < 2 || re > UINT8_MAX)
|
||||
{
|
||||
deh_warning("Touch count %d out of range (2 - %u) for condition ID %d", re, UINT8_MAX, id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "TRIGGER"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
|
|
@ -2819,32 +3082,12 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
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];
|
||||
}
|
||||
|
||||
stringvar = Z_StrDup(params[2]);
|
||||
#endif
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "FALLOFF")
|
||||
|| (++offset && fastcmp(params[0], "TOUCHOFFROAD"))
|
||||
|| (++offset && fastcmp(params[0], "TOUCHSNEAKERPANEL"))
|
||||
|| (++offset && fastcmp(params[0], "RINGDEBT")))
|
||||
|| (++offset && fastcmp(params[0], "RINGDEBT"))
|
||||
|| (++offset && fastcmp(params[0], "FAULTED")))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_FALLOFF + offset;
|
||||
|
|
@ -2854,32 +3097,73 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
re = 0;
|
||||
}
|
||||
else if ((offset=0) || fastcmp(params[0], "TRIPWIREHYUU")
|
||||
|| (++offset && fastcmp(params[0], "WHIPHYUU"))
|
||||
|| (++offset && fastcmp(params[0], "SPBNEUTER"))
|
||||
|| (++offset && fastcmp(params[0], "LANDMINEDUNK"))
|
||||
|| (++offset && fastcmp(params[0], "HITMIDAIR")))
|
||||
|| (++offset && fastcmp(params[0], "HITMIDAIR"))
|
||||
|| (++offset && fastcmp(params[0], "HITDRAFTERLOOKBACK"))
|
||||
|| (++offset && fastcmp(params[0], "GIANTRACERSHRUNKENORBI"))
|
||||
|| (++offset && fastcmp(params[0], "RETURNMARKTOSENDER")))
|
||||
{
|
||||
//PARAMCHECK(1);
|
||||
ty = UCRP_TRIPWIREHYUU + offset;
|
||||
}
|
||||
else if (fastcmp(params[0], "WETPLAYER"))
|
||||
else if (fastcmp(params[0], "TRACKHAZARD"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_WETPLAYER;
|
||||
re = MFE_UNDERWATER;
|
||||
x1 = 1;
|
||||
ty = UCRP_TRACKHAZARD;
|
||||
re = 1;
|
||||
x1 = -1;
|
||||
|
||||
if (params[1][0] == 'F' || params[1][0] == 'N' || params[1][0] == '0')
|
||||
re = 0;
|
||||
|
||||
if (params[2])
|
||||
{
|
||||
if (fastcmp(params[2], "STRICT"))
|
||||
re |= MFE_TOUCHWATER;
|
||||
if (fastcmp(params[2], "FINAL"))
|
||||
x1 = -2;
|
||||
else
|
||||
{
|
||||
deh_warning("liquid strictness requirement \"%s\" invalid for condition ID %d", params[2], id+1);
|
||||
return;
|
||||
x1 = atoi(params[2]);
|
||||
|
||||
if (re < 0 || re > MAX_LAPS)
|
||||
{
|
||||
deh_warning("Lap number %d out of range (0 - %u) for condition ID %d", x1, MAX_LAPS, id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "TARGETATTACKMETHOD"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UCRP_TARGETATTACKMETHOD;
|
||||
|
||||
stringvar = Z_StrDup(params[1]);
|
||||
// See targetdamaging_t
|
||||
if (fastcmp(params[1], "BOOST"))
|
||||
re = UFOD_BOOST;
|
||||
else if (fastcmp(params[1], "WHIP"))
|
||||
re = UFOD_WHIP;
|
||||
else if (fastcmp(params[1], "BANANA"))
|
||||
re = UFOD_BANANA;
|
||||
else if (fastcmp(params[1], "ORBINAUT"))
|
||||
re = UFOD_ORBINAUT;
|
||||
else if (fastcmp(params[1], "JAWZ"))
|
||||
re = UFOD_JAWZ;
|
||||
else if (fastcmp(params[1], "SPB"))
|
||||
re = UFOD_SPB;
|
||||
else if (fastcmp(params[1], "GACHABOM"))
|
||||
re = UFOD_GACHABOM;
|
||||
else
|
||||
{
|
||||
deh_warning("Unknown attack method %s for condition ID %d", params[1], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "GACHABOMMISER"))
|
||||
{
|
||||
//PARAMCHECK(1);
|
||||
ty = UCRP_GACHABOMMISER;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -2887,6 +3171,7 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
return;
|
||||
}
|
||||
|
||||
setcondition:
|
||||
M_AddRawCondition(set, (UINT8)id, ty, re, x1, x2, stringvar);
|
||||
}
|
||||
|
||||
|
|
@ -2929,7 +3214,7 @@ void readconditionset(MYFILE *f, UINT16 setnum)
|
|||
|
||||
// Now get the part after
|
||||
word2 = tmp += 2;
|
||||
strupr(word2);
|
||||
//strupr(word2);
|
||||
|
||||
if (fastncmp(word, "CONDITION", 9))
|
||||
{
|
||||
|
|
@ -3913,6 +4198,11 @@ if (!followers[numfollowers].field) \
|
|||
NOSTATE(hitconfirmstate, "HITCONFIRMSTATE");
|
||||
#undef NOSTATE
|
||||
|
||||
if (!followers[numfollowers].hornsound)
|
||||
{
|
||||
followers[numfollowers].hornsound = sfx_horn00;
|
||||
}
|
||||
|
||||
CONS_Printf("Added follower '%s'\n", testname);
|
||||
if (followers[numfollowers].category < numfollowercategories)
|
||||
followercategories[followers[numfollowers].category].numincategory++;
|
||||
|
|
|
|||
|
|
@ -1195,6 +1195,25 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
|
|||
// Spray Can
|
||||
"S_SPRAYCAN",
|
||||
|
||||
// Ancient Shrine
|
||||
"S_ANCIENTSHRINE",
|
||||
|
||||
"S_MORB1",
|
||||
"S_MORB2",
|
||||
"S_MORB3",
|
||||
"S_MORB4",
|
||||
"S_MORB5",
|
||||
"S_MORB6",
|
||||
"S_MORB7",
|
||||
"S_MORB8",
|
||||
"S_MORB9",
|
||||
"S_MORB10",
|
||||
"S_MORB11",
|
||||
"S_MORB12",
|
||||
"S_MORB13",
|
||||
"S_MORB14",
|
||||
"S_MORB15",
|
||||
|
||||
// Chaos Emeralds
|
||||
"S_CHAOSEMERALD1",
|
||||
"S_CHAOSEMERALD2",
|
||||
|
|
@ -1215,10 +1234,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
|
|||
|
||||
"S_EMERALDFLARE1",
|
||||
|
||||
// Emerald hunt shards
|
||||
"S_SHRD1",
|
||||
"S_SHRD2",
|
||||
"S_SHRD3",
|
||||
// Prison Egg Drops
|
||||
"S_PRISONEGGDROP_CD",
|
||||
|
||||
// Bubble Source
|
||||
"S_BUBBLES1",
|
||||
|
|
@ -4845,11 +4862,11 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
|
|||
"MT_BLUEFLAG", // Blue CTF Flag
|
||||
"MT_EMBLEM",
|
||||
"MT_SPRAYCAN",
|
||||
"MT_ANCIENTSHRINE",
|
||||
"MT_EMERALD",
|
||||
"MT_EMERALDSPARK",
|
||||
"MT_EMERALDFLARE",
|
||||
"MT_EMERHUNT", // Emerald Hunt
|
||||
"MT_EMERALDSPAWN", // Emerald spawner w/ delay
|
||||
"MT_PRISONEGGDROP",
|
||||
|
||||
// Springs and others
|
||||
"MT_FAN",
|
||||
|
|
|
|||
|
|
@ -491,6 +491,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
|
|||
cup = Z_Calloc(sizeof (cupheader_t), PU_STATIC, NULL);
|
||||
cup->id = numkartcupheaders;
|
||||
cup->monitor = 1;
|
||||
cup->cache_cuplock = MAXUNLOCKABLES;
|
||||
deh_strlcpy(cup->name, word2,
|
||||
sizeof(cup->name), va("Cup header %s: name", word2));
|
||||
cup->namehash = hash;
|
||||
|
|
|
|||
|
|
@ -142,7 +142,8 @@ struct skinreference_t
|
|||
#define MV_BEATEN (1<<1)
|
||||
#define MV_ENCORE (1<<2)
|
||||
#define MV_SPBATTACK (1<<3)
|
||||
#define MV_MAX (MV_VISITED|MV_BEATEN|MV_ENCORE|MV_SPBATTACK)
|
||||
#define MV_MYSTICMELODY (1<<4)
|
||||
#define MV_MAX (MV_VISITED|MV_BEATEN|MV_ENCORE|MV_SPBATTACK|MV_MYSTICMELODY)
|
||||
#define MV_FINISHNEEDED (1<<7)
|
||||
#define MV_PERSISTUNLOADED (MV_SPBATTACK|MV_FINISHNEEDED)
|
||||
|
||||
|
|
@ -407,6 +408,8 @@ struct cupheader_t
|
|||
|
||||
boolean playcredits; ///< Play the credits?
|
||||
|
||||
UINT16 cache_cuplock; ///< Cached Unlockable ID
|
||||
|
||||
cupwindata_t windata[4]; ///< Data for cup visitation
|
||||
cupheader_t *next; ///< Next cup in linked list
|
||||
};
|
||||
|
|
@ -437,6 +440,7 @@ struct staffbrief_t
|
|||
};
|
||||
|
||||
#define MAXMUSNAMES 3 // maximum definable music tracks per level
|
||||
#define MAXDESTRUCTIBLES 3
|
||||
#define MAXHEADERFOLLOWERS 32
|
||||
|
||||
struct mapheader_lighting_t
|
||||
|
|
@ -492,13 +496,14 @@ struct mapheader_t
|
|||
char relevantskin[SKINNAMESIZE+1]; ///< Skin to use for tutorial (if not provided, uses Eggman.)
|
||||
|
||||
// Music information
|
||||
char musname[MAXMUSNAMES][7]; ///< Music tracks to play. First dimension is the track number, second is the music string. "" for no music.
|
||||
char associatedmus[MAXMUSNAMES][7]; ///< Associated music tracks for sound test unlock.
|
||||
char positionmus[7]; ///< Custom Position track. Doesn't play in Encore or other fun game-controlled contexts
|
||||
UINT8 musname_size; ///< Number of music tracks defined
|
||||
UINT8 associatedmus_size; ///< Number of associated music tracks defined
|
||||
UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore.
|
||||
UINT32 muspos; ///< Music position to jump to.
|
||||
char musname[MAXMUSNAMES][7]; ///< Music tracks to play. First dimension is the track number, second is the music string. "" for no music.
|
||||
UINT16 cache_muslock[MAXMUSNAMES-1]; ///< Cached Alt Music IDs
|
||||
char associatedmus[MAXMUSNAMES][7]; ///< Associated music tracks for sound test unlock.
|
||||
char positionmus[7]; ///< Custom Position track. Doesn't play in Encore or other fun game-controlled contexts
|
||||
UINT8 musname_size; ///< Number of music tracks defined
|
||||
UINT8 associatedmus_size; ///< Number of associated music tracks defined
|
||||
UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore.
|
||||
UINT32 muspos; ///< Music position to jump to.
|
||||
|
||||
// Sky information
|
||||
UINT8 weather; ///< See preciptype_t
|
||||
|
|
@ -533,8 +538,12 @@ struct mapheader_t
|
|||
UINT8 precutscenenum; ///< Cutscene number to play BEFORE a level starts.
|
||||
UINT8 cutscenenum; ///< Cutscene number to use, 0 for none.
|
||||
|
||||
mobjtype_t destroyforchallenge[MAXDESTRUCTIBLES]; ///< Assistive for UCRP_MAPDESTROYOBJECTS
|
||||
UINT8 destroyforchallenge_size; ///< Number for above
|
||||
|
||||
UINT32 _saveid; ///< Purely assistive in gamedata save processes
|
||||
UINT16 cache_spraycan; ///< Cached Spraycan ID
|
||||
UINT16 cache_maplock; ///< Cached Unlockable ID
|
||||
|
||||
// Lua information
|
||||
UINT8 numCustomOptions; ///< Internal. For Lua custom value support.
|
||||
|
|
@ -717,6 +726,7 @@ extern INT32 luabanks[NUM_LUABANKS];
|
|||
extern INT32 nummaprings; //keep track of spawned rings/coins
|
||||
|
||||
extern UINT8 nummapspraycans;
|
||||
extern UINT16 numchallengedestructibles;
|
||||
|
||||
extern UINT32 bluescore; ///< Blue Team Scores
|
||||
extern UINT32 redscore; ///< Red Team Scores
|
||||
|
|
@ -765,8 +775,6 @@ extern UINT8 useSeal;
|
|||
extern UINT8 use1upSound;
|
||||
extern UINT8 maxXtraLife; // Max extra lives from rings
|
||||
|
||||
extern mobj_t *hunt1, *hunt2, *hunt3; // Emerald hunt locations
|
||||
|
||||
struct exitcondition_t
|
||||
{
|
||||
boolean losing;
|
||||
|
|
|
|||
|
|
@ -2233,7 +2233,7 @@ static void G_SaveDemoSkins(UINT8 **pp)
|
|||
{
|
||||
char skin[16];
|
||||
UINT8 i;
|
||||
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(true, false);
|
||||
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(true, -1);
|
||||
|
||||
WRITEUINT8((*pp), numskins);
|
||||
for (i = 0; i < numskins; i++)
|
||||
|
|
|
|||
181
src/g_game.c
181
src/g_game.c
|
|
@ -222,6 +222,7 @@ UINT32 bluescore, redscore; // CTF and Team Match team scores
|
|||
INT32 nummaprings = 0;
|
||||
|
||||
UINT8 nummapspraycans = 0;
|
||||
UINT16 numchallengedestructibles = 0;
|
||||
|
||||
// Elminates unnecessary searching.
|
||||
boolean CheckForBustableBlocks;
|
||||
|
|
@ -270,11 +271,6 @@ UINT8 introtoplay;
|
|||
UINT8 creditscutscene;
|
||||
UINT8 useSeal = 1;
|
||||
|
||||
// Emerald locations
|
||||
mobj_t *hunt1;
|
||||
mobj_t *hunt2;
|
||||
mobj_t *hunt3;
|
||||
|
||||
tic_t racecountdown, exitcountdown, musiccountdown; // for racing
|
||||
exitcondition_t g_exit;
|
||||
|
||||
|
|
@ -534,20 +530,12 @@ void G_UpdateRecords(void)
|
|||
&& (time < UINT32_MAX)) // DNF
|
||||
mapheaderinfo[gamemap-1]->records.time = time;
|
||||
}
|
||||
else
|
||||
{
|
||||
mapheaderinfo[gamemap-1]->records.time = 0;
|
||||
}
|
||||
|
||||
if (modeattacking & ATTACKING_LAP)
|
||||
{
|
||||
if ((mapheaderinfo[gamemap-1]->records.lap == 0) || (bestlap < mapheaderinfo[gamemap-1]->records.lap))
|
||||
mapheaderinfo[gamemap-1]->records.lap = bestlap;
|
||||
}
|
||||
else
|
||||
{
|
||||
mapheaderinfo[gamemap-1]->records.lap = 0;
|
||||
}
|
||||
|
||||
// Check emblems when level data is updated
|
||||
if ((earnedEmblems = M_CheckLevelEmblems()))
|
||||
|
|
@ -4038,48 +4026,21 @@ static void G_DoCompleted(void)
|
|||
{
|
||||
INT32 i;
|
||||
|
||||
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)
|
||||
{
|
||||
if (gametype != GT_TUTORIAL)
|
||||
{
|
||||
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)
|
||||
G_StopMetalDemo();
|
||||
if (metalrecording)
|
||||
G_StopMetalRecording(false);
|
||||
|
||||
G_SetGamestate(GS_NULL);
|
||||
wipegamestate = GS_NULL;
|
||||
// First, loop over all players to:
|
||||
// - fake bot results
|
||||
// - set client power add
|
||||
// - grand prix updates (for those who have finished)
|
||||
// - for bots
|
||||
// - set up difficulty increase (if applicable)
|
||||
// - for humans
|
||||
// - update Rings
|
||||
// - award Lives
|
||||
// - update over-all GP rank
|
||||
// - wipe some level-only player struct data
|
||||
// (The common thread is it needs to be done before Challenges updates.)
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
|
|
@ -4090,8 +4051,7 @@ static void G_DoCompleted(void)
|
|||
|
||||
player_t *const player = &players[i];
|
||||
|
||||
// Exitlevel shouldn't get you the points
|
||||
if (player->exiting == false && (player->pflags & PF_NOCONTEST) == 0)
|
||||
if ((player->exiting == 0) && (player->pflags & PF_NOCONTEST) == 0)
|
||||
{
|
||||
clientPowerAdd[i] = 0;
|
||||
|
||||
|
|
@ -4099,13 +4059,9 @@ static void G_DoCompleted(void)
|
|||
{
|
||||
K_FakeBotResults(player);
|
||||
}
|
||||
else
|
||||
{
|
||||
player->pflags |= PF_NOCONTEST;
|
||||
}
|
||||
}
|
||||
|
||||
if (grandprixinfo.gp == true && grandprixinfo.wonround == true && player->exiting == true)
|
||||
if (grandprixinfo.gp == true && grandprixinfo.wonround == true && player->exiting)
|
||||
{
|
||||
if (player->bot == true)
|
||||
{
|
||||
|
|
@ -4141,11 +4097,77 @@ static void G_DoCompleted(void)
|
|||
G_PlayerFinishLevel(i); // take away cards and stuff
|
||||
}
|
||||
|
||||
if (automapactive)
|
||||
AM_Stop();
|
||||
// Then, do gamedata-relevant material.
|
||||
// This has to be done second because some Challenges
|
||||
// are dependent on round standings.
|
||||
if (legitimateexit && !demo.playback && !mapreset)
|
||||
{
|
||||
if (gametype != GT_TUTORIAL)
|
||||
{
|
||||
UINT8 roundtype = GDGT_CUSTOM;
|
||||
|
||||
prevmap = (INT16)(gamemap-1);
|
||||
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++;
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
gamedata->deferredsave = true;
|
||||
}
|
||||
|
||||
// This isn't in the above block because other
|
||||
// mechanisms can queue up a gamedata save.
|
||||
if (gamedata->deferredsave)
|
||||
G_SaveGameData();
|
||||
|
||||
// Then, update some important game state.
|
||||
{
|
||||
legitimateexit = false;
|
||||
|
||||
if (modeattacking && pausedelay)
|
||||
pausedelay = 0;
|
||||
|
||||
gameaction = ga_nothing;
|
||||
|
||||
if (metalplayback)
|
||||
G_StopMetalDemo();
|
||||
if (metalrecording)
|
||||
G_StopMetalRecording(false);
|
||||
|
||||
if (automapactive)
|
||||
AM_Stop();
|
||||
|
||||
G_SetGamestate(GS_NULL);
|
||||
wipegamestate = GS_NULL;
|
||||
|
||||
prevmap = (INT16)(gamemap-1);
|
||||
}
|
||||
|
||||
// Finally, if you're not exiting, guarantee NO CONTEST.
|
||||
// We do this seperately from the loop above Challenges,
|
||||
// so NOCONTEST-related Challenges don't fire on exitlevel.
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (players[i].exiting || (players[i].pflags & PF_NOCONTEST))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
players[i].pflags |= PF_NOCONTEST;
|
||||
}
|
||||
|
||||
// And lastly, everything in anticipation for Intermission/level change.
|
||||
if (!demo.playback)
|
||||
{
|
||||
// Set up power level gametype scrambles
|
||||
|
|
@ -4404,14 +4426,17 @@ void G_LoadGameSettings(void)
|
|||
}
|
||||
|
||||
#define GD_VERSIONCHECK 0xBA5ED123 // Change every major version, as usual
|
||||
#define GD_VERSIONMINOR 6 // Change every format update
|
||||
#define GD_VERSIONMINOR 9 // Change every format update
|
||||
|
||||
// You can't rearrange these without a special format update
|
||||
typedef enum
|
||||
{
|
||||
GDEVER_ADDON = 1,
|
||||
GDEVER_CREDITS = 1<<1,
|
||||
GDEVER_REPLAY = 1<<2,
|
||||
GDEVER_SPECIAL = 1<<3,
|
||||
GDEVER_KEYTUTORIAL = 1<<4,
|
||||
GDEVER_KEYMAJORSKIP = 1<<5,
|
||||
} gdeverdone_t;
|
||||
|
||||
static const char *G_GameDataFolder(void)
|
||||
|
|
@ -4497,7 +4522,7 @@ void G_LoadGameData(void)
|
|||
FIL_WriteFile(va("%s" PATHSEP "%s.bak", srb2home, gamedatafilename), save.buffer, save.size);
|
||||
}
|
||||
|
||||
if ((versionMinor == 0 || versionMinor == 1)
|
||||
if ((versionMinor <= 6)
|
||||
#ifdef DEVELOP
|
||||
|| M_CheckParm("-resetchallengegrid")
|
||||
#endif
|
||||
|
|
@ -4517,6 +4542,11 @@ void G_LoadGameData(void)
|
|||
{
|
||||
gamedata->totalrings = READUINT32(save.p);
|
||||
|
||||
if (versionMinor >= 9)
|
||||
{
|
||||
gamedata->totaltumbletime = READUINT32(save.p);
|
||||
}
|
||||
|
||||
for (i = 0; i < GDGT_MAX; i++)
|
||||
{
|
||||
gamedata->roundsplayed[i] = READUINT32(save.p);
|
||||
|
|
@ -4544,6 +4574,8 @@ void G_LoadGameData(void)
|
|||
gamedata->everfinishedcredits = !!(everflags & GDEVER_CREDITS);
|
||||
gamedata->eversavedreplay = !!(everflags & GDEVER_REPLAY);
|
||||
gamedata->everseenspecial = !!(everflags & GDEVER_SPECIAL);
|
||||
gamedata->chaokeytutorial = !!(everflags & GDEVER_KEYTUTORIAL);
|
||||
gamedata->majorkeyskipattempted = !!(everflags & GDEVER_KEYMAJORSKIP);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -4557,6 +4589,13 @@ void G_LoadGameData(void)
|
|||
save.p += 4; // no direct equivalent to matchesplayed
|
||||
}
|
||||
|
||||
// Prison Egg Pickups
|
||||
if (versionMinor >= 8)
|
||||
{
|
||||
gamedata->thisprisoneggpickup = READUINT16(save.p);
|
||||
gamedata->prisoneggstothispickup = READUINT16(save.p);
|
||||
}
|
||||
|
||||
{
|
||||
// Quick & dirty hash for what mod this save file is for.
|
||||
UINT32 modID = READUINT32(save.p);
|
||||
|
|
@ -4609,7 +4648,7 @@ void G_LoadGameData(void)
|
|||
if (gridunusable)
|
||||
{
|
||||
UINT16 burn = READUINT16(save.p); // Previous challengegridwidth
|
||||
UINT8 height = (versionMinor > 0) ? CHALLENGEGRIDHEIGHT : 5;
|
||||
UINT8 height = (versionMinor && versionMinor <= 6) ? 4 : CHALLENGEGRIDHEIGHT;
|
||||
save.p += (burn * height * unlockreadsize); // Step over previous grid data
|
||||
|
||||
gamedata->challengegridwidth = 0;
|
||||
|
|
@ -5060,10 +5099,11 @@ void G_SaveGameData(void)
|
|||
}
|
||||
|
||||
length = (4+1+1+
|
||||
4+4+
|
||||
4+4+4+
|
||||
(4*GDGT_MAX)+
|
||||
4+1+2+2+
|
||||
4+
|
||||
2+2+
|
||||
4+
|
||||
(MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+
|
||||
4+2);
|
||||
|
|
@ -5200,6 +5240,7 @@ void G_SaveGameData(void)
|
|||
|
||||
WRITEUINT32(save.p, gamedata->totalplaytime); // 4
|
||||
WRITEUINT32(save.p, gamedata->totalrings); // 4
|
||||
WRITEUINT32(save.p, gamedata->totaltumbletime); // 4
|
||||
|
||||
for (i = 0; i < GDGT_MAX; i++) // 4 * GDGT_MAX
|
||||
{
|
||||
|
|
@ -5222,10 +5263,18 @@ void G_SaveGameData(void)
|
|||
everflags |= GDEVER_REPLAY;
|
||||
if (gamedata->everseenspecial)
|
||||
everflags |= GDEVER_SPECIAL;
|
||||
if (gamedata->chaokeytutorial)
|
||||
everflags |= GDEVER_KEYTUTORIAL;
|
||||
if (gamedata->majorkeyskipattempted)
|
||||
everflags |= GDEVER_KEYMAJORSKIP;
|
||||
|
||||
WRITEUINT32(save.p, everflags); // 4
|
||||
}
|
||||
|
||||
// Prison Egg Pickups
|
||||
WRITEUINT16(save.p, gamedata->thisprisoneggpickup); // 2
|
||||
WRITEUINT16(save.p, gamedata->prisoneggstothispickup); // 2
|
||||
|
||||
WRITEUINT32(save.p, quickncasehash(timeattackfolder, 64)); // 4
|
||||
|
||||
// To save space, use one bit per collected/achieved/unlocked flag
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "i_joy.h" // JOYAXISRANGE
|
||||
#include "r_draw.h" // GTC_ macros for assigning gamepad indicator colors
|
||||
#include "v_video.h" // V_GetColor for assigning gamepad indictaor colors
|
||||
#include "r_skins.h" // skins[].prefcolor for assigning gamepad indicator colors
|
||||
#include "z_zone.h"
|
||||
|
||||
// current state of the keys
|
||||
|
|
@ -206,9 +207,7 @@ void G_SetDeviceForPlayer(INT32 player, INT32 device)
|
|||
void G_SetPlayerGamepadIndicatorToPlayerColor(INT32 player)
|
||||
{
|
||||
INT32 device;
|
||||
INT32 skin;
|
||||
UINT16 skincolor;
|
||||
UINT8 *colormap;
|
||||
byteColor_t byte_color;
|
||||
|
||||
I_Assert(player >= 0 && player < MAXSPLITSCREENPLAYERS);
|
||||
|
|
@ -220,16 +219,16 @@ void G_SetPlayerGamepadIndicatorToPlayerColor(INT32 player)
|
|||
return;
|
||||
}
|
||||
|
||||
skin = cv_skin[player].value;
|
||||
skincolor = cv_playercolor[player].value;
|
||||
colormap = R_GetTranslationColormap(skin, skincolor, GTC_MENUCACHE);
|
||||
|
||||
if (colormap == NULL)
|
||||
if (skincolor == SKINCOLOR_NONE)
|
||||
{
|
||||
return;
|
||||
INT32 skin = cv_skin[player].value;
|
||||
if (skin == -1)
|
||||
skin = 0;
|
||||
skincolor = skins[skin].prefcolor;
|
||||
}
|
||||
|
||||
byte_color = V_GetColor(colormap[104]).s;
|
||||
byte_color = V_GetColor(skincolors[skincolor].ramp[8]).s;
|
||||
|
||||
I_SetGamepadIndicatorColor(device, byte_color.red, byte_color.green, byte_color.blue);
|
||||
}
|
||||
|
|
|
|||
103
src/info.c
103
src/info.c
|
|
@ -143,10 +143,15 @@ char sprnames[NUMSPRITES + 1][5] =
|
|||
"NCHP", // NiGHTS chip
|
||||
"NSTR", // NiGHTS star
|
||||
"EMBM", // Emblem
|
||||
"SPCN", // Spray Can
|
||||
"MMSH", // Ancient Shrine
|
||||
"MORB", // One Morbillion
|
||||
"EMRC", // Chaos Emeralds
|
||||
"SEMR", // Super Emeralds
|
||||
"ESPK",
|
||||
"SHRD", // Emerald Hunt
|
||||
|
||||
// Prison Egg Drops
|
||||
"ALTM",
|
||||
|
||||
// Interactive Objects
|
||||
"BBLS", // water bubble source
|
||||
|
|
@ -635,7 +640,6 @@ char sprnames[NUMSPRITES + 1][5] =
|
|||
"POKE", // Pokey
|
||||
"AUDI", // Audience members
|
||||
"DECO", // Old 1.0 Kart Decoratives + New misc ones
|
||||
"SPCN", // Spray Can replaces all the old D00Dkart objects
|
||||
"SNES", // Sprites for SNES remake maps
|
||||
"GBAS", // Sprites for GBA remake maps
|
||||
"SPRS", // Sapphire Coast Spring Shell
|
||||
|
|
@ -1907,6 +1911,25 @@ state_t states[NUMSTATES] =
|
|||
// Spray Can
|
||||
{SPR_SPCN, FF_ANIMATE|FF_SEMIBRIGHT, -1, {NULL}, 15, 2, S_NULL}, // S_SPRAYCAN
|
||||
|
||||
// Ancient Shrine
|
||||
{SPR_MMSH, 0, -1, {NULL}, 0, 0, S_NULL}, // S_ANCIENTSHRINE
|
||||
|
||||
{SPR_MORB, 0|FF_ADD, 1, {A_FireShrink}, 2*FRACUNIT/3, 12, S_MORB2}, // S_MORB1
|
||||
{SPR_MORB, 1|FF_ADD, 1, {NULL}, 0, 0, S_MORB3}, // S_MORB2
|
||||
{SPR_MORB, 2|FF_ADD, 1, {NULL}, 0, 0, S_MORB4}, // S_MORB3
|
||||
{SPR_MORB, 3|FF_ADD, 1, {NULL}, 0, 0, S_MORB5}, // S_MORB4
|
||||
{SPR_MORB, 4|FF_ADD, 1, {NULL}, 0, 0, S_MORB6}, // S_MORB5
|
||||
{SPR_MORB, 5|FF_ADD, 1, {NULL}, 0, 0, S_MORB7}, // S_MORB6
|
||||
{SPR_MORB, 6|FF_ADD, 1, {NULL}, 0, 0, S_MORB8}, // S_MORB7
|
||||
{SPR_MORB, 7|FF_ADD, 4, {NULL}, 0, 0, S_MORB9}, // S_MORB8
|
||||
{SPR_MORB, 6|FF_ADD, 1, {A_FireShrink}, 1, 12, S_MORB10}, // S_MORB9
|
||||
{SPR_MORB, 5|FF_ADD, 1, {NULL}, 0, 0, S_MORB11}, // S_MORB10
|
||||
{SPR_MORB, 4|FF_ADD, 1, {NULL}, 0, 0, S_MORB12}, // S_MORB11
|
||||
{SPR_MORB, 3|FF_ADD, 1, {NULL}, 0, 0, S_MORB13}, // S_MORB12
|
||||
{SPR_MORB, 2|FF_ADD, 1, {NULL}, 0, 0, S_MORB14}, // S_MORB13
|
||||
{SPR_MORB, 1|FF_ADD, 1, {NULL}, 0, 0, S_MORB15}, // S_MORB14
|
||||
{SPR_MORB, 0|FF_ADD, 1, {NULL}, 0, 0, S_NULL}, // S_MORB15
|
||||
|
||||
// Chaos Emeralds
|
||||
{SPR_EMRC, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_CHAOSEMERALD2}, // S_CHAOSEMERALD1
|
||||
{SPR_EMRC, FF_FULLBRIGHT|FF_ADD, 1, {NULL}, 0, 0, S_CHAOSEMERALD1}, // S_CHAOSEMERALD2
|
||||
|
|
@ -1927,10 +1950,8 @@ state_t states[NUMSTATES] =
|
|||
|
||||
{SPR_LENS, FF_FULLBRIGHT|FF_ADD|FF_TRANS10|FF_ANIMATE|11, 8, {NULL}, 7, 1, S_GAINAX_MID2}, // S_EMERALDFLARE1
|
||||
|
||||
// Emerald hunt shards
|
||||
{SPR_SHRD, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHRD1
|
||||
{SPR_SHRD, 1, -1, {NULL}, 0, 0, S_NULL}, // S_SHRD2
|
||||
{SPR_SHRD, 2, -1, {NULL}, 0, 0, S_NULL}, // S_SHRD3
|
||||
// Prison Egg Drops
|
||||
{SPR_ALTM, 0|FF_PAPERSPRITE|FF_SEMIBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_PRISONEGGDROP_CD
|
||||
|
||||
// Bubble Source
|
||||
{SPR_BBLS, 0, 8, {A_BubbleSpawn}, 2048, 0, S_BUBBLES2}, // S_BUBBLES1
|
||||
|
|
@ -8392,6 +8413,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_ANCIENTSHRINE
|
||||
2256, // doomednum
|
||||
S_ANCIENTSHRINE,// spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
8, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
48*FRACUNIT, // radius
|
||||
80*FRACUNIT, // height
|
||||
0, // display offset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOGRAVITY|MF_SOLID|MF_DONTENCOREMAP, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_EMERALD
|
||||
-1, // doomednum
|
||||
S_CHAOSEMERALD1, // spawnstate
|
||||
|
|
@ -8473,9 +8521,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_EMERHUNT
|
||||
320, // doomednum
|
||||
S_SHRD1, // spawnstate
|
||||
{ // MT_PRISONEGGDROP
|
||||
-1, // doomednum
|
||||
S_INVISIBLE, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
|
|
@ -8486,44 +8534,17 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_SPRK1, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_cgot, // deathsound
|
||||
8, // speed
|
||||
12*FRACUNIT, // radius
|
||||
42*FRACUNIT, // height
|
||||
0, // display offset
|
||||
4, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_SPECIAL|MF_NOGRAVITY, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_EMERALDSPAWN
|
||||
321, // doomednum
|
||||
S_INVISIBLE, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
0, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
sfx_s3k9c, // deathsound
|
||||
0, // speed
|
||||
8, // radius
|
||||
8, // height
|
||||
65*FRACUNIT, // radius
|
||||
130*FRACUNIT, // height
|
||||
0, // display offset
|
||||
10, // mass
|
||||
16, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOSECTOR, // flags
|
||||
MF_SPECIAL|MF_PICKUPFROMBELOW|MF_DONTENCOREMAP, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
|
|
|
|||
37
src/info.h
37
src/info.h
|
|
@ -698,10 +698,15 @@ typedef enum sprite
|
|||
SPR_NCHP, // NiGHTS chip
|
||||
SPR_NSTR, // NiGHTS star
|
||||
SPR_EMBM, // Emblem
|
||||
SPR_SPCN, // Spray Can
|
||||
SPR_MMSH, // Ancient Shrine
|
||||
SPR_MORB, // One Morbillion
|
||||
SPR_EMRC, // Chaos Emeralds
|
||||
SPR_SEMR, // Super Emeralds
|
||||
SPR_ESPK,
|
||||
SPR_SHRD, // Emerald Hunt
|
||||
|
||||
// Prison Egg Drops
|
||||
SPR_ALTM,
|
||||
|
||||
// Interactive Objects
|
||||
SPR_BBLS, // water bubble source
|
||||
|
|
@ -1190,7 +1195,6 @@ typedef enum sprite
|
|||
SPR_POKE, // Pokey
|
||||
SPR_AUDI, // Audience members
|
||||
SPR_DECO, // Old 1.0 Kart Decoratives + New misc ones
|
||||
SPR_SPCN, // Spray Can replaces all the old D00Dkart objects
|
||||
SPR_SNES, // Sprites for SNES remake maps
|
||||
SPR_GBAS, // Sprites for GBA remake maps
|
||||
SPR_SPRS, // Sapphire Coast Spring Shell
|
||||
|
|
@ -2387,6 +2391,25 @@ typedef enum state
|
|||
// Spray Can
|
||||
S_SPRAYCAN,
|
||||
|
||||
// Ancient Shrine
|
||||
S_ANCIENTSHRINE,
|
||||
|
||||
S_MORB1,
|
||||
S_MORB2,
|
||||
S_MORB3,
|
||||
S_MORB4,
|
||||
S_MORB5,
|
||||
S_MORB6,
|
||||
S_MORB7,
|
||||
S_MORB8,
|
||||
S_MORB9,
|
||||
S_MORB10,
|
||||
S_MORB11,
|
||||
S_MORB12,
|
||||
S_MORB13,
|
||||
S_MORB14,
|
||||
S_MORB15,
|
||||
|
||||
// Chaos Emeralds
|
||||
S_CHAOSEMERALD1,
|
||||
S_CHAOSEMERALD2,
|
||||
|
|
@ -2407,10 +2430,8 @@ typedef enum state
|
|||
|
||||
S_EMERALDFLARE1,
|
||||
|
||||
// Emerald hunt shards
|
||||
S_SHRD1,
|
||||
S_SHRD2,
|
||||
S_SHRD3,
|
||||
// Prison Egg Drops
|
||||
S_PRISONEGGDROP_CD,
|
||||
|
||||
// Bubble Source
|
||||
S_BUBBLES1,
|
||||
|
|
@ -6076,11 +6097,11 @@ typedef enum mobj_type
|
|||
MT_BLUEFLAG, // Blue CTF Flag
|
||||
MT_EMBLEM,
|
||||
MT_SPRAYCAN,
|
||||
MT_ANCIENTSHRINE,
|
||||
MT_EMERALD,
|
||||
MT_EMERALDSPARK,
|
||||
MT_EMERALDFLARE,
|
||||
MT_EMERHUNT, // Emerald Hunt
|
||||
MT_EMERALDSPAWN, // Emerald spawner w/ delay
|
||||
MT_PRISONEGGDROP,
|
||||
|
||||
// Springs and others
|
||||
MT_FAN,
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ void K_SetBot(UINT8 newplayernum, UINT8 skinnum, UINT8 difficulty, botStyle_e st
|
|||
playernode[newplayernum] = servernode;
|
||||
|
||||
// this will permit unlocks
|
||||
memcpy(&players[newplayernum].availabilities, R_GetSkinAvailabilities(false, true), MAXAVAILABILITY*sizeof(UINT8));
|
||||
memcpy(&players[newplayernum].availabilities, R_GetSkinAvailabilities(false, skinnum), MAXAVAILABILITY*sizeof(UINT8));
|
||||
|
||||
players[newplayernum].splitscreenindex = 0;
|
||||
players[newplayernum].bot = true;
|
||||
|
|
|
|||
|
|
@ -910,6 +910,14 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim)
|
|||
K_AddHitLag(victim, victimHitlag, true);
|
||||
K_AddHitLag(attacker, attackerHitlag, false);
|
||||
shield->hitlag = attacker->hitlag;
|
||||
|
||||
if (attackerPlayer->roundconditions.whip_hyuu == false
|
||||
&& attackerPlayer->hyudorotimer > 0)
|
||||
{
|
||||
attackerPlayer->roundconditions.whip_hyuu = true;
|
||||
attackerPlayer->roundconditions.checkthisframe = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -710,25 +710,47 @@ void K_HandleFollower(player_t *player)
|
|||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_FollowerHornTaunt(player_t *taunter, player_t *victim)
|
||||
void K_FollowerHornTaunt(player_t *taunter, player_t *victim, boolean mysticmelodyspecial)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
void K_FollowerHornTaunt(player_t *taunter, player_t *victim)
|
||||
void K_FollowerHornTaunt(player_t *taunter, player_t *victim, boolean mysticmelodyspecial)
|
||||
{
|
||||
// Basic checks
|
||||
if (
|
||||
(cv_karthorns.value == 0)
|
||||
|| taunter == NULL
|
||||
taunter == NULL
|
||||
|| victim == NULL
|
||||
|| taunter->followerskin < 0
|
||||
|| taunter->followerskin >= numfollowers
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const follower_t *fl = &followers[taunter->followerskin];
|
||||
|
||||
// Restrict mystic melody special status
|
||||
if (mysticmelodyspecial == true)
|
||||
{
|
||||
mysticmelodyspecial = (
|
||||
(demo.playback == false) // No downloading somebody else's replay
|
||||
&& (fl->hornsound == sfx_melody) // Must be the Mystic Melody
|
||||
&& (taunter->bot == false) // No getting your puppies to do it for you
|
||||
&& P_IsLocalPlayer(taunter) // Must be in your party
|
||||
&& !(mapheaderinfo[gamemap-1]->records.mapvisited & MV_MYSTICMELODY) // Not already done
|
||||
);
|
||||
}
|
||||
|
||||
// More expensive checks
|
||||
if (
|
||||
(cv_karthorns.value == 0 && mysticmelodyspecial == false)
|
||||
|| (P_IsDisplayPlayer(victim) == false && cv_karthorns.value != 2)
|
||||
|| P_MobjWasRemoved(taunter->mo) == true
|
||||
|| P_MobjWasRemoved(taunter->follower) == true
|
||||
)
|
||||
{
|
||||
return;
|
||||
|
||||
const follower_t *fl = &followers[taunter->followerskin];
|
||||
}
|
||||
|
||||
const boolean tasteful = (taunter->karthud[khud_taunthorns] == 0);
|
||||
|
||||
|
|
@ -737,6 +759,40 @@ void K_FollowerHornTaunt(player_t *taunter, player_t *victim)
|
|||
mobj_t *honk = taunter->follower->hprev;
|
||||
const fixed_t desiredscale = (2*taunter->mo->scale)/3;
|
||||
|
||||
if (mysticmelodyspecial == true)
|
||||
{
|
||||
mobj_t *mobj = NULL, *next = NULL;
|
||||
|
||||
for (mobj = trackercap; mobj; mobj = next)
|
||||
{
|
||||
next = mobj->itnext;
|
||||
if (mobj->type != MT_ANCIENTSHRINE)
|
||||
{
|
||||
// Not relevant
|
||||
continue;
|
||||
}
|
||||
|
||||
if (P_MobjWasRemoved(mobj->tracer) == false)
|
||||
{
|
||||
// Already initiated
|
||||
continue;
|
||||
}
|
||||
|
||||
// Cleverly a mobj type where TypeIsNetSynced is false
|
||||
P_SetTarget(&mobj->tracer, P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_HORNCODE));
|
||||
|
||||
if (P_MobjWasRemoved(mobj->tracer) == true)
|
||||
{
|
||||
// Unrecoverable?!
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is a helper non-netsynced countdown
|
||||
mobj->tracer->renderflags |= RF_DONTDRAW;
|
||||
mobj->tracer->fuse = 2*TICRATE;
|
||||
}
|
||||
}
|
||||
|
||||
if (P_MobjWasRemoved(honk) == true)
|
||||
{
|
||||
honk = P_SpawnMobj(
|
||||
|
|
@ -773,12 +829,21 @@ void K_FollowerHornTaunt(player_t *taunter, player_t *victim)
|
|||
honk->fuse = TICRATE/2;
|
||||
honk->renderflags |= RF_DONTDRAW;
|
||||
|
||||
if (P_IsDisplayPlayer(victim) || P_IsDisplayPlayer(taunter))
|
||||
S_StartSound(NULL, fl->hornsound);
|
||||
|
||||
honk->flags2 |= MF2_AMBUSH;
|
||||
}
|
||||
|
||||
honk->renderflags &= ~K_GetPlayerDontDrawFlag(victim);
|
||||
UINT32 dontdrawflag = K_GetPlayerDontDrawFlag(victim);
|
||||
|
||||
// A display player is affected!
|
||||
if (dontdrawflag != 0)
|
||||
{
|
||||
// Only play the sound for the first seen display player
|
||||
if ((honk->renderflags & RF_DONTDRAW) == RF_DONTDRAW)
|
||||
{
|
||||
S_StartSound(NULL, fl->hornsound);
|
||||
}
|
||||
|
||||
honk->renderflags &= ~dontdrawflag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -227,19 +227,20 @@ void K_HandleFollower(player_t *player);
|
|||
void K_RemoveFollower(player_t *player);
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_FollowerHornTaunt(player_t *taunter, player_t *victim)
|
||||
void K_FollowerHornTaunt(player_t *taunter, player_t *victim, boolean mysticmelodyspecial)
|
||||
|
||||
Plays horn and spawns object (MOSTLY non-netsynced)
|
||||
|
||||
Input Arguments:-
|
||||
taunter - Source player with a follower
|
||||
victim - Player that hears and sees the honk
|
||||
mysticmelodyspecial - Special Mystic Melody behaviour
|
||||
|
||||
Return:-
|
||||
None
|
||||
--------------------------------------------------*/
|
||||
|
||||
void K_FollowerHornTaunt(player_t *taunter, player_t *victim);
|
||||
void K_FollowerHornTaunt(player_t *taunter, player_t *victim, boolean mysticmelodyspecial);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ void K_InitGrandPrixBots(void)
|
|||
for (j = 0; j < numplayers; j++)
|
||||
{
|
||||
player_t *p = &players[competitors[j]];
|
||||
char *rivalname = skins[p->skin].rivals[i];
|
||||
const char *rivalname = skins[p->skin].rivals[i];
|
||||
INT32 rivalnum = R_SkinAvailable(rivalname);
|
||||
|
||||
// Intentionally referenced before (currently dummied out) unlock check. Such a tease!
|
||||
|
|
@ -714,11 +714,13 @@ void K_RetireBots(void)
|
|||
|
||||
if (usableskins > 0)
|
||||
{
|
||||
UINT8 index = P_RandomKey(PR_RULESCRAMBLE, usableskins);
|
||||
UINT8 index = P_RandomKey(PR_BOTS, usableskins);
|
||||
skinnum = grabskins[index];
|
||||
grabskins[index] = grabskins[--usableskins];
|
||||
}
|
||||
|
||||
memcpy(&bot->availabilities, R_GetSkinAvailabilities(false, skinnum), MAXAVAILABILITY*sizeof(UINT8));
|
||||
|
||||
bot->botvars.difficulty = newDifficulty;
|
||||
bot->botvars.diffincrease = 0;
|
||||
|
||||
|
|
@ -763,9 +765,15 @@ void K_FakeBotResults(player_t *bot)
|
|||
}
|
||||
}
|
||||
|
||||
if (besttime == UINT32_MAX // No one finished, so you don't finish either.
|
||||
|| bot->distancetofinish >= worstdist) // Last place, you aren't going to finish.
|
||||
if (besttime == UINT32_MAX) // No one finished, so you don't finish either.
|
||||
{
|
||||
// We don't apply PF_NOCONTEST in the exitlevel case - that's done for all players in G_DoCompleted.
|
||||
return;
|
||||
}
|
||||
|
||||
if (bot->distancetofinish >= worstdist) // Last place, you aren't going to finish.
|
||||
{
|
||||
// This was a successful murder!
|
||||
bot->pflags |= PF_NOCONTEST;
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,8 +75,8 @@ static patch_t *kp_racefinish[6];
|
|||
|
||||
static patch_t *kp_positionnum[10][2][2]; // number, overlay or underlay, splitscreen
|
||||
|
||||
static patch_t *kp_facenum[MAXPLAYERS+1];
|
||||
patch_t *kp_facehighlight[8];
|
||||
patch_t *kp_facenum[MAXPLAYERS+1];
|
||||
static patch_t *kp_facehighlight[8];
|
||||
|
||||
static patch_t *kp_nocontestminimap;
|
||||
static patch_t *kp_spbminimap;
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ extern patch_t *kp_button_right[2];
|
|||
extern patch_t *kp_button_left[2];
|
||||
|
||||
extern patch_t *kp_eggnum[6];
|
||||
extern patch_t *kp_facenum[MAXPLAYERS+1];
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
|
|
|||
122
src/k_kart.c
122
src/k_kart.c
|
|
@ -267,6 +267,22 @@ void K_TimerInit(void)
|
|||
{
|
||||
K_SpawnDuelOnlyItems();
|
||||
}
|
||||
|
||||
if (
|
||||
battleprisons == true
|
||||
&& grandprixinfo.gp == true
|
||||
&& netgame == false
|
||||
&& gamedata->thisprisoneggpickup_cached != NULL
|
||||
&& gamedata->prisoneggstothispickup == 0
|
||||
&& maptargets > 1
|
||||
)
|
||||
{
|
||||
// This calculation is like this so...
|
||||
// - You can't get a Prison Egg Drop on the last broken target
|
||||
// - If it were 0 at minimum there'd be a slight bias towards the start of the round
|
||||
// - This is bad because it benefits CD farming like in Brawl :D
|
||||
gamedata->prisoneggstothispickup = 1 + M_RandomKey(maptargets - 1);
|
||||
}
|
||||
}
|
||||
|
||||
UINT32 K_GetPlayerDontDrawFlag(player_t *player)
|
||||
|
|
@ -1133,6 +1149,7 @@ static void K_UpdateOffroad(player_t *player)
|
|||
player->offroad = offroadstrength;
|
||||
|
||||
if (player->roundconditions.touched_offroad == false
|
||||
&& !(player->exiting || (player->pflags & PF_NOCONTEST))
|
||||
&& player->offroad > (2*offroadstrength) / TICRATE)
|
||||
{
|
||||
player->roundconditions.touched_offroad = true;
|
||||
|
|
@ -1449,7 +1466,8 @@ static void K_UpdateDraft(player_t *player)
|
|||
|
||||
if (K_TryDraft(player, otherPlayer->mo, minDist, draftdistance, leniency) == true)
|
||||
{
|
||||
return; // Finished doing our draft.
|
||||
//return;
|
||||
goto draftdurationhandling; // Finished doing our draft.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1484,7 +1502,14 @@ static void K_UpdateDraft(player_t *player)
|
|||
{
|
||||
player->draftpower = 0;
|
||||
player->lastdraft = -1;
|
||||
player->roundconditions.continuousdraft = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
draftdurationhandling:
|
||||
player->roundconditions.continuousdraft++;
|
||||
if (player->roundconditions.continuousdraft > player->roundconditions.continuousdraft_best)
|
||||
player->roundconditions.continuousdraft_best = player->roundconditions.continuousdraft;
|
||||
}
|
||||
|
||||
void K_KartPainEnergyFling(player_t *player)
|
||||
|
|
@ -2035,10 +2060,10 @@ static SINT8 K_GlanceAtPlayers(player_t *glancePlayer, boolean horn)
|
|||
{
|
||||
const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
||||
const angle_t blindSpotSize = ANG10; // ANG5
|
||||
UINT8 i;
|
||||
SINT8 glanceDir = 0;
|
||||
SINT8 lastValidGlance = 0;
|
||||
boolean podiumspecial = (K_PodiumSequence() == true && glancePlayer->nextwaypoint == NULL && glancePlayer->speed == 0);
|
||||
const boolean podiumspecial = (K_PodiumSequence() == true && glancePlayer->nextwaypoint == NULL && glancePlayer->speed == 0);
|
||||
boolean mysticmelodyspecial = false;
|
||||
|
||||
if (podiumspecial)
|
||||
{
|
||||
|
|
@ -2057,43 +2082,47 @@ static SINT8 K_GlanceAtPlayers(player_t *glancePlayer, boolean horn)
|
|||
|
||||
// See if there's any players coming up behind us.
|
||||
// If so, your character will glance at 'em.
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
mobj_t *victim = NULL, *victimnext = NULL;
|
||||
|
||||
for (victim = trackercap; victim; victim = victimnext)
|
||||
{
|
||||
player_t *p;
|
||||
player_t *p = victim->player;
|
||||
angle_t back;
|
||||
angle_t diff;
|
||||
fixed_t distance;
|
||||
SINT8 dir = -1;
|
||||
|
||||
if (!playeringame[i])
|
||||
victimnext = victim->itnext;
|
||||
|
||||
if (p != NULL)
|
||||
{
|
||||
// Invalid player
|
||||
continue;
|
||||
if (p == glancePlayer)
|
||||
{
|
||||
// FOOL! Don't glance at yerself!
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p->spectator || p->hyudorotimer > 0)
|
||||
{
|
||||
// Not playing / invisible
|
||||
continue;
|
||||
}
|
||||
|
||||
if (podiumspecial && p->position >= glancePlayer->position)
|
||||
{
|
||||
// On the podium, only look with envy, not condesencion
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
p = &players[i];
|
||||
|
||||
if (p == glancePlayer)
|
||||
else if (victim->type != MT_ANCIENTSHRINE)
|
||||
{
|
||||
// FOOL! Don't glance at yerself!
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!p->mo || P_MobjWasRemoved(p->mo))
|
||||
{
|
||||
// Invalid mobj
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p->spectator || p->hyudorotimer > 0)
|
||||
{
|
||||
// Not playing / invisible
|
||||
// Ancient Shrines are a special exception to glance logic.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!podiumspecial)
|
||||
{
|
||||
distance = R_PointToDist2(glancePlayer->mo->x, glancePlayer->mo->y, p->mo->x, p->mo->y);
|
||||
distance = R_PointToDist2(glancePlayer->mo->x, glancePlayer->mo->y, victim->x, victim->y);
|
||||
|
||||
if (distance > maxdistance)
|
||||
{
|
||||
|
|
@ -2101,13 +2130,9 @@ static SINT8 K_GlanceAtPlayers(player_t *glancePlayer, boolean horn)
|
|||
continue;
|
||||
}
|
||||
}
|
||||
else if (p->position >= glancePlayer->position)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
back = glancePlayer->mo->angle + ANGLE_180;
|
||||
diff = R_PointToAngle2(glancePlayer->mo->x, glancePlayer->mo->y, p->mo->x, p->mo->y) - back;
|
||||
diff = R_PointToAngle2(glancePlayer->mo->x, glancePlayer->mo->y, victim->x, victim->y) - back;
|
||||
|
||||
if (diff > ANGLE_180)
|
||||
{
|
||||
|
|
@ -2127,7 +2152,7 @@ static SINT8 K_GlanceAtPlayers(player_t *glancePlayer, boolean horn)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!podiumspecial && P_CheckSight(glancePlayer->mo, p->mo) == false)
|
||||
if (!podiumspecial && P_CheckSight(glancePlayer->mo, victim) == false)
|
||||
{
|
||||
// Blocked by a wall, we can't glance at 'em!
|
||||
continue;
|
||||
|
|
@ -2142,7 +2167,14 @@ static SINT8 K_GlanceAtPlayers(player_t *glancePlayer, boolean horn)
|
|||
|
||||
if (horn == true)
|
||||
{
|
||||
K_FollowerHornTaunt(glancePlayer, p);
|
||||
if (p != NULL)
|
||||
{
|
||||
K_FollowerHornTaunt(glancePlayer, p, false);
|
||||
}
|
||||
else if (victim->type == MT_ANCIENTSHRINE)
|
||||
{
|
||||
mysticmelodyspecial = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2150,7 +2182,7 @@ static SINT8 K_GlanceAtPlayers(player_t *glancePlayer, boolean horn)
|
|||
{
|
||||
const boolean tasteful = (glancePlayer->karthud[khud_taunthorns] == 0);
|
||||
|
||||
K_FollowerHornTaunt(glancePlayer, glancePlayer);
|
||||
K_FollowerHornTaunt(glancePlayer, glancePlayer, mysticmelodyspecial);
|
||||
|
||||
if (tasteful && glancePlayer->karthud[khud_taunthorns] < 2*TICRATE)
|
||||
glancePlayer->karthud[khud_taunthorns] = 2*TICRATE;
|
||||
|
|
@ -3810,6 +3842,7 @@ void K_RemoveGrowShrink(player_t *player)
|
|||
}
|
||||
|
||||
player->growshrinktimer = 0;
|
||||
player->roundconditions.consecutive_grow_lasers = 0;
|
||||
}
|
||||
|
||||
boolean K_IsBigger(mobj_t *compare, mobj_t *other)
|
||||
|
|
@ -6066,6 +6099,7 @@ void K_DoSneaker(player_t *player, INT32 type)
|
|||
const fixed_t intendedboost = FRACUNIT/2;
|
||||
|
||||
if (player->roundconditions.touched_sneakerpanel == false
|
||||
&& !(player->exiting || (player->pflags & PF_NOCONTEST))
|
||||
&& player->floorboost != 0)
|
||||
{
|
||||
player->roundconditions.touched_sneakerpanel = true;
|
||||
|
|
@ -7624,6 +7658,14 @@ void K_KartPlayerHUDUpdate(player_t *player)
|
|||
}
|
||||
else
|
||||
player->karthud[khud_finish] = 0;
|
||||
|
||||
if (demo.playback == false && P_IsLocalPlayer(player) == true)
|
||||
{
|
||||
if (player->tumbleBounces != 0 && gamedata->totaltumbletime != UINT32_MAX)
|
||||
{
|
||||
gamedata->totaltumbletime++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef RINGANIM_DELAYMAX
|
||||
|
|
@ -8407,7 +8449,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
//player->flashing = 0;
|
||||
eggsexplode = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SPBEXPLOSION);
|
||||
eggsexplode->height = 2 * player->mo->height;
|
||||
eggsexplode->color = player->mo->color;
|
||||
K_FlipFromObject(eggsexplode, player->mo);
|
||||
|
||||
eggsexplode->threshold = KITEM_EGGMAN;
|
||||
|
||||
P_SetTarget(&eggsexplode->tracer, player->mo);
|
||||
|
||||
if (player->eggmanblame >= 0
|
||||
&& player->eggmanblame < MAXPLAYERS
|
||||
|
|
@ -11783,6 +11829,10 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
K_ThrowKartItem(player, true, MT_GACHABOM, 0, 0, 0);
|
||||
K_PlayAttackTaunt(player->mo);
|
||||
player->itemamount--;
|
||||
player->roundconditions.gachabom_miser = (
|
||||
(player->roundconditions.gachabom_miser == 0)
|
||||
? 1 : 0xFF
|
||||
);
|
||||
K_UpdateHnextList(player, false);
|
||||
}
|
||||
break;
|
||||
|
|
@ -12361,6 +12411,7 @@ void K_EggmanTransfer(player_t *source, player_t *victim)
|
|||
K_AddHitLag(victim->mo, 2, true);
|
||||
K_DropItems(victim);
|
||||
victim->eggmanexplode = 6*TICRATE;
|
||||
victim->eggmanblame = (source - players);
|
||||
K_StopRoulette(&victim->itemRoulette);
|
||||
|
||||
if (P_IsDisplayPlayer(victim))
|
||||
|
|
@ -12368,6 +12419,7 @@ void K_EggmanTransfer(player_t *source, player_t *victim)
|
|||
|
||||
K_AddHitLag(source->mo, 2, true);
|
||||
source->eggmanexplode = 0;
|
||||
source->eggmanblame = -1;
|
||||
K_StopRoulette(&source->itemRoulette);
|
||||
source->eggmanTransferDelay = 10;
|
||||
|
||||
|
|
|
|||
43
src/k_menu.h
43
src/k_menu.h
|
|
@ -1142,6 +1142,9 @@ void M_HandleImageDef(INT32 choice);
|
|||
#define recommendedflags V_GREENMAP
|
||||
#define warningflags V_GRAYMAP
|
||||
|
||||
// For some menu highlights
|
||||
UINT16 M_GetCvPlayerColor(UINT8 pnum);
|
||||
|
||||
void M_UpdateMenuBGImage(boolean forceReset);
|
||||
void M_DrawMenuBackground(void);
|
||||
void M_DrawMenuForeground(void);
|
||||
|
|
@ -1201,19 +1204,34 @@ void M_DrawAddons(void);
|
|||
#define RIGHTUNLOCKSCROLL 3
|
||||
#define LEFTUNLOCKSCROLL (RIGHTUNLOCKSCROLL-1)
|
||||
|
||||
#define CC_TOTAL 0
|
||||
#define CC_UNLOCKED 1
|
||||
#define CC_TALLY 2
|
||||
#define CC_ANIM 3
|
||||
#define CC_CHAOANIM 4
|
||||
#define CC_CHAONOPE 5
|
||||
#define CC_MAX 6
|
||||
typedef enum
|
||||
{
|
||||
CMC_TOTAL = 0,
|
||||
CMC_UNLOCKED,
|
||||
|
||||
CMC_KEYED,
|
||||
CMC_MAJORSKIPPED,
|
||||
|
||||
CMC_PERCENT,
|
||||
|
||||
CMC_MEDALID,
|
||||
CMC_MEDALBLANK,
|
||||
CMC_MEDALFILLED,
|
||||
|
||||
CMC_ANIM,
|
||||
CMC_CHAOANIM,
|
||||
CMC_CHAONOPE,
|
||||
|
||||
CMC_MAX,
|
||||
} challengesmenucount_e;
|
||||
|
||||
#define TILEFLIP_MAX 16
|
||||
|
||||
#define CHAOHOLD_MAX (3*TICRATE/2)
|
||||
#define CHAOHOLD_BEGIN 7
|
||||
#define CHAOHOLD_END 3
|
||||
#define CHAOHOLD_STANDARD (40) // (Close to 3*TICRATE/2 after padding, but adjusted to evenly divide by 10)
|
||||
#define CHAOHOLD_MAJOR (60) //(3*CHAOHOLD_STANDARD/2)
|
||||
#define CHAOHOLD_BEGIN (7)
|
||||
#define CHAOHOLD_END (3)
|
||||
#define CHAOHOLD_PADDING (CHAOHOLD_BEGIN + CHAOHOLD_END)
|
||||
|
||||
extern struct timeattackmenu_s {
|
||||
|
||||
|
|
@ -1241,12 +1259,12 @@ extern struct challengesmenu_s {
|
|||
boolean pending;
|
||||
boolean requestnew;
|
||||
|
||||
boolean chaokeyadd;
|
||||
boolean chaokeyadd, keywasadded;
|
||||
UINT8 chaokeyhold;
|
||||
|
||||
boolean requestflip;
|
||||
|
||||
UINT16 unlockcount[CC_MAX];
|
||||
UINT16 unlockcount[CMC_MAX];
|
||||
|
||||
UINT8 fade;
|
||||
} challengesmenu;
|
||||
|
|
@ -1256,6 +1274,7 @@ void M_Challenges(INT32 choice);
|
|||
void M_DrawChallenges(void);
|
||||
void M_ChallengesTick(void);
|
||||
boolean M_ChallengesInputs(INT32 ch);
|
||||
boolean M_CanKeyHiliTile(void);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
|
|
|||
417
src/k_menudraw.c
417
src/k_menudraw.c
|
|
@ -231,6 +231,22 @@ void M_DrawMenuBackground(void)
|
|||
}
|
||||
}
|
||||
|
||||
UINT16 M_GetCvPlayerColor(UINT8 pnum)
|
||||
{
|
||||
if (pnum >= MAXSPLITSCREENPLAYERS)
|
||||
return SKINCOLOR_NONE;
|
||||
|
||||
UINT16 color = cv_playercolor[pnum].value;
|
||||
if (color != SKINCOLOR_NONE)
|
||||
return color;
|
||||
|
||||
INT32 skin = R_SkinAvailable(cv_skin[pnum].string);
|
||||
if (skin == -1)
|
||||
return SKINCOLOR_NONE;
|
||||
|
||||
return skins[skin].prefcolor;
|
||||
}
|
||||
|
||||
static void M_DrawMenuParty(void)
|
||||
{
|
||||
const INT32 PLATTER_WIDTH = 19;
|
||||
|
|
@ -253,6 +269,18 @@ static void M_DrawMenuParty(void)
|
|||
x = 2;
|
||||
y = BASEVIDHEIGHT - small->height - 2;
|
||||
|
||||
// Despite the work put into it, can't use M_GetCvPlayerColor directly - we need to reference skin always.
|
||||
#define grab_skin_and_colormap(pnum) \
|
||||
{ \
|
||||
skin = R_SkinAvailable(cv_skin[pnum].string); \
|
||||
color = cv_playercolor[pnum].value; \
|
||||
if (skin == -1) \
|
||||
skin = 0; \
|
||||
if (color == SKINCOLOR_NONE) \
|
||||
color = skins[skin].prefcolor; \
|
||||
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE); \
|
||||
}
|
||||
|
||||
switch (setup_numplayers)
|
||||
{
|
||||
case 1:
|
||||
|
|
@ -260,9 +288,7 @@ static void M_DrawMenuParty(void)
|
|||
x -= 8;
|
||||
V_DrawScaledPatch(x, y, 0, small);
|
||||
|
||||
skin = R_SkinAvailable(cv_skin[0].string);
|
||||
color = cv_playercolor[0].value;
|
||||
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
||||
grab_skin_and_colormap(0);
|
||||
|
||||
V_DrawMappedPatch(x + 22, y + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
||||
break;
|
||||
|
|
@ -273,15 +299,11 @@ static void M_DrawMenuParty(void)
|
|||
V_DrawScaledPatch(x, y, 0, small);
|
||||
V_DrawScaledPatch(x + PLATTER_OFFSET, y - PLATTER_STAGGER, 0, small);
|
||||
|
||||
skin = R_SkinAvailable(cv_skin[1].string);
|
||||
color = cv_playercolor[1].value;
|
||||
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
||||
grab_skin_and_colormap(1);
|
||||
|
||||
V_DrawMappedPatch(x + PLATTER_OFFSET + 22, y - PLATTER_STAGGER + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
||||
|
||||
skin = R_SkinAvailable(cv_skin[0].string);
|
||||
color = cv_playercolor[0].value;
|
||||
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
||||
grab_skin_and_colormap(0);
|
||||
|
||||
V_DrawMappedPatch(x + 22, y + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
||||
break;
|
||||
|
|
@ -291,21 +313,15 @@ static void M_DrawMenuParty(void)
|
|||
V_DrawScaledPatch(x, y, 0, large);
|
||||
V_DrawScaledPatch(x + PLATTER_OFFSET, y - PLATTER_STAGGER, 0, small);
|
||||
|
||||
skin = R_SkinAvailable(cv_skin[1].string);
|
||||
color = cv_playercolor[1].value;
|
||||
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
||||
grab_skin_and_colormap(1);
|
||||
|
||||
V_DrawMappedPatch(x + PLATTER_OFFSET + 22, y - PLATTER_STAGGER + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
||||
|
||||
skin = R_SkinAvailable(cv_skin[0].string);
|
||||
color = cv_playercolor[0].value;
|
||||
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
||||
grab_skin_and_colormap(0);
|
||||
|
||||
V_DrawMappedPatch(x + 12, y - 2, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
||||
|
||||
skin = R_SkinAvailable(cv_skin[2].string);
|
||||
color = cv_playercolor[2].value;
|
||||
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
||||
grab_skin_and_colormap(2);
|
||||
|
||||
V_DrawMappedPatch(x + 22, y + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
||||
break;
|
||||
|
|
@ -315,27 +331,19 @@ static void M_DrawMenuParty(void)
|
|||
V_DrawScaledPatch(x, y, 0, large);
|
||||
V_DrawScaledPatch(x + PLATTER_OFFSET, y - PLATTER_STAGGER, 0, large);
|
||||
|
||||
skin = R_SkinAvailable(cv_skin[1].string);
|
||||
color = cv_playercolor[1].value;
|
||||
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
||||
grab_skin_and_colormap(1);
|
||||
|
||||
V_DrawMappedPatch(x + PLATTER_OFFSET + 12, y - PLATTER_STAGGER - 2, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
||||
|
||||
skin = R_SkinAvailable(cv_skin[0].string);
|
||||
color = cv_playercolor[0].value;
|
||||
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
||||
grab_skin_and_colormap(0);
|
||||
|
||||
V_DrawMappedPatch(x + 12, y - 2, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
||||
|
||||
skin = R_SkinAvailable(cv_skin[3].string);
|
||||
color = cv_playercolor[3].value;
|
||||
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
||||
grab_skin_and_colormap(3);
|
||||
|
||||
V_DrawMappedPatch(x + PLATTER_OFFSET + 22, y - PLATTER_STAGGER + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
||||
|
||||
skin = R_SkinAvailable(cv_skin[2].string);
|
||||
color = cv_playercolor[2].value;
|
||||
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
||||
grab_skin_and_colormap(2);
|
||||
|
||||
V_DrawMappedPatch(x + 22, y + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
||||
break;
|
||||
|
|
@ -346,6 +354,8 @@ static void M_DrawMenuParty(void)
|
|||
}
|
||||
}
|
||||
|
||||
#undef grab_skin_and_color
|
||||
|
||||
x += PLATTER_WIDTH;
|
||||
y += small->height;
|
||||
V_DrawScaledPatch(x + 16, y - 12, 0, W_CachePatchName(va("OPPRNK0%d", setup_numplayers % 10), PU_CACHE));
|
||||
|
|
@ -5261,7 +5271,7 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, boolean hili
|
|||
|
||||
if (categoryside)
|
||||
{
|
||||
char categoryid = '8';
|
||||
char categoryid = '0';
|
||||
colormap = bgmap;
|
||||
switch (ref->type)
|
||||
{
|
||||
|
|
@ -5299,6 +5309,9 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, boolean hili
|
|||
case SECRET_SPBATTACK:
|
||||
categoryid = '7';
|
||||
break;
|
||||
case SECRET_ALTMUSIC:
|
||||
categoryid = '9';
|
||||
break;
|
||||
}
|
||||
pat = W_CachePatchName(va("UN_RR0%c%c",
|
||||
categoryid,
|
||||
|
|
@ -5361,6 +5374,9 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, boolean hili
|
|||
case SECRET_MAP:
|
||||
iconid = 14;
|
||||
break;
|
||||
case SECRET_ALTMUSIC:
|
||||
iconid = 16;
|
||||
break;
|
||||
|
||||
case SECRET_HARDSPEED:
|
||||
iconid = 3;
|
||||
|
|
@ -5469,7 +5485,7 @@ drawborder:
|
|||
buffer[7] = (skullAnimCounter/5) ? '2' : '1';
|
||||
pat = W_CachePatchName(buffer, PU_CACHE);
|
||||
|
||||
colormap = R_GetTranslationColormap(TC_DEFAULT, cv_playercolor[0].value, GTC_MENUCACHE);
|
||||
colormap = R_GetTranslationColormap(TC_DEFAULT, M_GetCvPlayerColor(0), GTC_MENUCACHE);
|
||||
|
||||
V_DrawFixedPatch(
|
||||
x*FRACUNIT, y*FRACUNIT,
|
||||
|
|
@ -5555,7 +5571,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
|
|||
addflags ^= V_FLIP; // This sprite is left/right flipped!
|
||||
}
|
||||
|
||||
V_DrawFixedPatch(x*FRACUNIT, (y+6)*FRACUNIT, FRACUNIT, addflags, patch, NULL);
|
||||
V_DrawFixedPatch(x*FRACUNIT, (y+2)*FRACUNIT, FRACUNIT, addflags, patch, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -5583,7 +5599,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
|
|||
break;
|
||||
}
|
||||
|
||||
M_DrawCharacterIconAndEngine(4, BASEVIDHEIGHT-(4+16), i, colormap, (i == skin));
|
||||
M_DrawCharacterIconAndEngine(4, BASEVIDHEIGHT-(4+16), i, colormap, (i != skin));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -5870,7 +5886,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
|
|||
}
|
||||
else
|
||||
{
|
||||
colormap = R_GetTranslationColormap(TC_DEFAULT, cv_playercolor[0].value, GTC_MENUCACHE);
|
||||
colormap = R_GetTranslationColormap(TC_DEFAULT, M_GetCvPlayerColor(0), GTC_MENUCACHE);
|
||||
V_DrawFixedPatch((x+40)<<FRACBITS, ((y+25)<<FRACBITS),
|
||||
FRACUNIT/2, 0,
|
||||
W_CachePatchName("K_LAPE02", PU_CACHE),
|
||||
|
|
@ -5879,12 +5895,190 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
|
|||
}
|
||||
|
||||
#define challengesgridstep 22
|
||||
#define challengekeybarwidth 50
|
||||
|
||||
void M_DrawChallenges(void)
|
||||
static void M_DrawChallengeKeys(INT32 tilex, INT32 tiley)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
|
||||
patch_t *key = W_CachePatchName("UN_CHA00", PU_CACHE);
|
||||
INT32 offs = challengesmenu.unlockcount[CMC_CHAONOPE];
|
||||
if (offs & 1)
|
||||
offs = -offs;
|
||||
offs /= 2;
|
||||
|
||||
fixed_t keyx = (8+offs)*FRACUNIT, keyy = 0;
|
||||
|
||||
// Button prompt
|
||||
K_drawButton(
|
||||
24 << FRACBITS,
|
||||
16 << FRACBITS,
|
||||
0, kp_button_c[1],
|
||||
menumessage.active == false && M_MenuExtraHeld(pid) == true
|
||||
);
|
||||
|
||||
// Metyr of rounds played that contribute to Chao Key generation
|
||||
{
|
||||
const INT32 keybarlen = 32, keybary = 28;
|
||||
|
||||
offs = keybarlen;
|
||||
if (gamedata->chaokeys < GDMAX_CHAOKEYS)
|
||||
{
|
||||
#if (GDCONVERT_ROUNDSTOKEY != 32)
|
||||
offs = ((gamedata->pendingkeyroundoffset * keybarlen)/GDCONVERT_ROUNDSTOKEY);
|
||||
#else
|
||||
offs = gamedata->pendingkeyroundoffset;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (offs > 0)
|
||||
V_DrawFill(1+2, keybary, offs, 1, 0);
|
||||
if (offs < keybarlen)
|
||||
V_DrawFadeFill(1+2+offs, keybary, keybarlen-offs, 1, 0, 31, challengetransparentstrength);
|
||||
}
|
||||
|
||||
// Counter
|
||||
{
|
||||
INT32 textx = 4, texty = 20-challengesmenu.unlockcount[CMC_CHAOANIM];
|
||||
UINT8 numbers[4];
|
||||
numbers[0] = ((gamedata->chaokeys / 100) % 10);
|
||||
numbers[1] = ((gamedata->chaokeys / 10) % 10);
|
||||
numbers[2] = (gamedata->chaokeys % 10);
|
||||
|
||||
numbers[3] = ((gamedata->chaokeys / 1000) % 10);
|
||||
if (numbers[3] != 0)
|
||||
{
|
||||
V_DrawScaledPatch(textx - 4, texty, 0, kp_facenum[numbers[3]]);
|
||||
textx += 2;
|
||||
}
|
||||
|
||||
UINT8 i = 0;
|
||||
while (i < 3)
|
||||
{
|
||||
V_DrawScaledPatch(textx, texty, 0, kp_facenum[numbers[i]]);
|
||||
textx += 6;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
UINT8 keysbeingused = 0;
|
||||
|
||||
// The Chao Key swooping animation
|
||||
if (challengesmenu.currentunlock < MAXUNLOCKABLES && challengesmenu.chaokeyhold)
|
||||
{
|
||||
fixed_t baseradius = challengesgridstep;
|
||||
|
||||
boolean major = false, ending = false;
|
||||
if (unlockables[challengesmenu.currentunlock].majorunlock == true)
|
||||
{
|
||||
major = true;
|
||||
tilex += challengesgridstep/2;
|
||||
tiley += challengesgridstep/2;
|
||||
baseradius = (7*baseradius)/4;
|
||||
}
|
||||
|
||||
const INT32 chaohold_duration =
|
||||
CHAOHOLD_PADDING
|
||||
+ (major
|
||||
? CHAOHOLD_MAJOR
|
||||
: CHAOHOLD_STANDARD
|
||||
);
|
||||
|
||||
if (challengesmenu.chaokeyhold >= chaohold_duration - CHAOHOLD_END)
|
||||
{
|
||||
ending = true;
|
||||
baseradius = ((chaohold_duration - challengesmenu.chaokeyhold)*baseradius)*(FRACUNIT/CHAOHOLD_END);
|
||||
}
|
||||
|
||||
INT16 specifickeyholdtime = challengesmenu.chaokeyhold;
|
||||
|
||||
for (; keysbeingused < (major ? 10 : 1); keysbeingused++, specifickeyholdtime -= (CHAOHOLD_STANDARD/10))
|
||||
{
|
||||
fixed_t radius = baseradius;
|
||||
fixed_t thiskeyx, thiskeyy;
|
||||
fixed_t keyholdrotation = 0;
|
||||
|
||||
if (specifickeyholdtime < CHAOHOLD_BEGIN)
|
||||
{
|
||||
if (specifickeyholdtime <= 0)
|
||||
{
|
||||
// Nothing following will be relevant
|
||||
break;
|
||||
}
|
||||
|
||||
radius = (specifickeyholdtime*radius)*(FRACUNIT/CHAOHOLD_BEGIN);
|
||||
thiskeyx = keyx + specifickeyholdtime*((tilex*FRACUNIT) - keyx)/CHAOHOLD_BEGIN;
|
||||
thiskeyy = keyy + specifickeyholdtime*((tiley*FRACUNIT) - keyy)/CHAOHOLD_BEGIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyholdrotation = (-36 * keysbeingused) * FRACUNIT; // 360/10
|
||||
|
||||
if (ending == false)
|
||||
{
|
||||
radius <<= FRACBITS;
|
||||
|
||||
keyholdrotation += 360 * ((challengesmenu.chaokeyhold - CHAOHOLD_BEGIN))
|
||||
* (FRACUNIT/(CHAOHOLD_STANDARD)); // intentionally not chaohold_duration
|
||||
|
||||
if (keysbeingused == 0)
|
||||
{
|
||||
INT32 time = (major ? 5 : 3) - (keyholdrotation - 1) / (90 * FRACUNIT);
|
||||
if (time <= 5 && time >= 0)
|
||||
V_DrawScaledPatch(tilex + 2, tiley - 2, 0, kp_eggnum[time]);
|
||||
}
|
||||
}
|
||||
|
||||
thiskeyx = tilex*FRACUNIT;
|
||||
thiskeyy = tiley*FRACUNIT;
|
||||
}
|
||||
|
||||
if (radius != 0)
|
||||
{
|
||||
angle_t ang = (FixedAngle(
|
||||
keyholdrotation
|
||||
) >> ANGLETOFINESHIFT) & FINEMASK;
|
||||
|
||||
thiskeyx += FixedMul(radius, FINESINE(ang));
|
||||
thiskeyy -= FixedMul(radius, FINECOSINE(ang));
|
||||
}
|
||||
|
||||
V_DrawFixedPatch(thiskeyx, thiskeyy, FRACUNIT, 0, key, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// The final Chao Key on the stack
|
||||
{
|
||||
UINT8 *lastkeycolormap = NULL;
|
||||
|
||||
if (gamedata->chaokeys <= keysbeingused)
|
||||
{
|
||||
// Greyed out if there's going to be none left
|
||||
lastkeycolormap = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_BLACK, GTC_MENUCACHE);
|
||||
}
|
||||
|
||||
V_DrawFixedPatch(keyx, keyy, FRACUNIT, 0, key, lastkeycolormap);
|
||||
|
||||
// Extra glowverlay if you can use a Chao Key
|
||||
if (keysbeingused == 0 && M_CanKeyHiliTile())
|
||||
{
|
||||
INT32 trans = (((challengesmenu.ticker/5) % 6) - 3);
|
||||
if (trans)
|
||||
{
|
||||
trans = ((trans < 0)
|
||||
? (10 + trans)
|
||||
: (10 - trans)
|
||||
) << V_ALPHASHIFT;
|
||||
|
||||
V_DrawFixedPatch(keyx, keyy, FRACUNIT, trans, key,
|
||||
R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_MENUCACHE)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void M_DrawChallenges(void)
|
||||
{
|
||||
INT32 x = currentMenu->x, explodex, selectx = 0, selecty = 0;
|
||||
INT32 y;
|
||||
INT16 i, j;
|
||||
|
|
@ -5914,6 +6108,7 @@ void M_DrawChallenges(void)
|
|||
}
|
||||
|
||||
// Do underlay for everything else early so the bottom of the reticule doesn't get shaded over.
|
||||
if (challengesmenu.currentunlock < MAXUNLOCKABLES)
|
||||
{
|
||||
y = 120;
|
||||
|
||||
|
|
@ -5932,7 +6127,7 @@ void M_DrawChallenges(void)
|
|||
|
||||
y = currentMenu->y;
|
||||
|
||||
V_DrawFadeFill(0, y-2, BASEVIDWIDTH, 90, 0, 31, challengetransparentstrength);
|
||||
V_DrawFadeFill(0, y-2, BASEVIDWIDTH, (challengesgridstep * CHALLENGEGRIDHEIGHT) + 2, 0, 31, challengetransparentstrength);
|
||||
|
||||
x -= (challengesgridstep-1);
|
||||
|
||||
|
|
@ -6017,108 +6212,85 @@ challengedesc:
|
|||
{
|
||||
str = "???"; //M_CreateSecretMenuOption(str);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
str = "---";
|
||||
}
|
||||
|
||||
offset = V_LSTitleLowStringWidth(str, 0) / 2;
|
||||
V_DrawLSTitleLowString(BASEVIDWIDTH/2 - offset, y+6, 0, str);
|
||||
offset = V_LSTitleLowStringWidth(str, 0) / 2;
|
||||
V_DrawLSTitleLowString(BASEVIDWIDTH/2 - offset, y+6, 0, str);
|
||||
}
|
||||
}
|
||||
|
||||
// Tally
|
||||
// Wings
|
||||
{
|
||||
str = va("%d/%d",
|
||||
challengesmenu.unlockcount[CC_UNLOCKED] + challengesmenu.unlockcount[CC_TALLY],
|
||||
challengesmenu.unlockcount[CC_TOTAL]
|
||||
);
|
||||
V_DrawRightAlignedTimerString(BASEVIDWIDTH-7, 9-challengesmenu.unlockcount[CC_ANIM], 0, str);
|
||||
const INT32 endy = 18, endlen = 38;
|
||||
patch_t *endwing = W_CachePatchName("K_BOSB01", PU_CACHE);
|
||||
|
||||
V_DrawFill(0, endy, endlen, 11, 24);
|
||||
V_DrawFixedPatch(endlen*FRACUNIT, endy*FRACUNIT, FRACUNIT, V_FLIP, endwing, NULL);
|
||||
|
||||
V_DrawFill(BASEVIDWIDTH - endlen, endy, endlen, 11, 24);
|
||||
V_DrawFixedPatch((BASEVIDWIDTH - endlen)*FRACUNIT, endy*FRACUNIT, FRACUNIT, 0, endwing, NULL);
|
||||
}
|
||||
|
||||
// Chao Keys
|
||||
// Percentage
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
fixed_t keyx = (8+offs)*FRACUNIT, keyy = 5*FRACUNIT;
|
||||
|
||||
const char *timerstr = va("%u", gamedata->chaokeys);
|
||||
|
||||
V_DrawTimerString((27+offs), 9-challengesmenu.unlockcount[CC_CHAOANIM], 0, timerstr);
|
||||
|
||||
K_drawButton(
|
||||
(27 + offs + V_TimerStringWidth(timerstr, 0) + 2) << FRACBITS,
|
||||
11 << FRACBITS,
|
||||
0, kp_button_c[1],
|
||||
M_MenuExtraHeld(pid)
|
||||
patch_t *medal = W_CachePatchName(
|
||||
va("UN_MDL%c", '0' + challengesmenu.unlockcount[CMC_MEDALID]),
|
||||
PU_CACHE
|
||||
);
|
||||
|
||||
offs = challengekeybarwidth;
|
||||
if (gamedata->chaokeys < GDMAX_CHAOKEYS)
|
||||
offs = ((gamedata->pendingkeyroundoffset * challengekeybarwidth)/GDCONVERT_ROUNDSTOKEY);
|
||||
fixed_t medalchopy = 1;
|
||||
|
||||
if (offs > 0)
|
||||
V_DrawFill(1, 25, offs, 2, 0);
|
||||
if (offs < challengekeybarwidth)
|
||||
V_DrawFadeFill(1+offs, 25, challengekeybarwidth-offs, 2, 0, 31, challengetransparentstrength);
|
||||
|
||||
if (challengesmenu.chaokeyhold)
|
||||
for (i = CMC_MEDALBLANK; i <= CMC_MEDALFILLED; i++)
|
||||
{
|
||||
fixed_t keyholdrotation = 0, radius = challengesgridstep;
|
||||
if (challengesmenu.unlockcount[i] == 0)
|
||||
continue;
|
||||
|
||||
if (challengesmenu.chaokeyhold < CHAOHOLD_BEGIN)
|
||||
V_SetClipRect(
|
||||
0,
|
||||
medalchopy << FRACBITS,
|
||||
BASEVIDWIDTH << FRACBITS,
|
||||
(medalchopy + challengesmenu.unlockcount[i]) << FRACBITS,
|
||||
0
|
||||
);
|
||||
|
||||
UINT8 *medalcolormap = NULL;
|
||||
if (i == CMC_MEDALBLANK)
|
||||
{
|
||||
radius = (challengesmenu.chaokeyhold*radius)*(FRACUNIT/CHAOHOLD_BEGIN);
|
||||
keyx += challengesmenu.chaokeyhold*((selectx*FRACUNIT) - keyx)/CHAOHOLD_BEGIN;
|
||||
keyy += challengesmenu.chaokeyhold*((selecty*FRACUNIT) - keyy)/CHAOHOLD_BEGIN;
|
||||
medalcolormap = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_BLACK, GTC_MENUCACHE);
|
||||
}
|
||||
else
|
||||
else if (challengesmenu.unlockcount[CMC_MEDALID] == 0)
|
||||
{
|
||||
if (challengesmenu.chaokeyhold < CHAOHOLD_MAX - CHAOHOLD_END)
|
||||
{
|
||||
radius <<= FRACBITS;
|
||||
|
||||
keyholdrotation = 360 * ((challengesmenu.chaokeyhold - CHAOHOLD_BEGIN))
|
||||
* (FRACUNIT/(CHAOHOLD_MAX - (CHAOHOLD_BEGIN + CHAOHOLD_END)));
|
||||
|
||||
INT32 time = 3 - (keyholdrotation - 1) / (90 * FRACUNIT);
|
||||
if (time <= 5 && time >= 0)
|
||||
V_DrawScaledPatch(selectx + 2, selecty - 2, 0, kp_eggnum[time]);
|
||||
}
|
||||
else
|
||||
{
|
||||
radius = ((CHAOHOLD_MAX - challengesmenu.chaokeyhold)*radius)*(FRACUNIT/CHAOHOLD_END);
|
||||
}
|
||||
|
||||
keyx = selectx*FRACUNIT;
|
||||
keyy = selecty*FRACUNIT;
|
||||
medalcolormap = R_GetTranslationColormap(TC_DEFAULT, M_GetCvPlayerColor(0), GTC_MENUCACHE);
|
||||
}
|
||||
|
||||
if (radius)
|
||||
{
|
||||
angle_t ang = (FixedAngle(
|
||||
keyholdrotation
|
||||
) >> ANGLETOFINESHIFT) & FINEMASK;
|
||||
V_DrawFixedPatch((BASEVIDWIDTH - 31)*FRACUNIT, 1*FRACUNIT, FRACUNIT, 0, medal, medalcolormap);
|
||||
|
||||
keyx += FixedMul(radius, FINESINE(ang));
|
||||
keyy -= FixedMul(radius, FINECOSINE(ang));
|
||||
}
|
||||
V_ClearClipRect();
|
||||
|
||||
medalchopy += challengesmenu.unlockcount[i];
|
||||
}
|
||||
|
||||
V_DrawFixedPatch(keyx, keyy, FRACUNIT, 0, key, NULL);
|
||||
INT32 textx = BASEVIDWIDTH - 21, texty = 20-challengesmenu.unlockcount[CMC_ANIM];
|
||||
UINT8 numbers[3];
|
||||
numbers[0] = ((challengesmenu.unlockcount[CMC_PERCENT] / 100) % 10);
|
||||
numbers[1] = ((challengesmenu.unlockcount[CMC_PERCENT] / 10) % 10);
|
||||
numbers[2] = (challengesmenu.unlockcount[CMC_PERCENT] % 10);
|
||||
|
||||
patch_t *percent = W_CachePatchName("K_SPDML1", PU_CACHE);
|
||||
|
||||
V_DrawScaledPatch(textx + 2, texty, 0, percent);
|
||||
|
||||
i = 3;
|
||||
while (i)
|
||||
{
|
||||
i--;
|
||||
textx -= 6;
|
||||
V_DrawScaledPatch(textx, texty, 0, kp_facenum[numbers[i]]);
|
||||
}
|
||||
}
|
||||
|
||||
// Chao Key information
|
||||
M_DrawChallengeKeys(selectx, selecty);
|
||||
|
||||
// Derived from M_DrawCharSelectPreview
|
||||
x = 40;
|
||||
y = BASEVIDHEIGHT-16;
|
||||
|
|
@ -6143,7 +6315,6 @@ challengedesc:
|
|||
|
||||
#undef challengetransparentstrength
|
||||
#undef challengesgridstep
|
||||
#undef challengekeybarwidth
|
||||
|
||||
// Statistics menu
|
||||
|
||||
|
|
@ -6226,6 +6397,12 @@ static void M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y)
|
|||
}
|
||||
x -= 8;
|
||||
}
|
||||
|
||||
if (mapheaderinfo[mapnum]->records.mapvisited & MV_MYSTICMELODY)
|
||||
{
|
||||
V_DrawScaledPatch(x, y, 0, W_CachePatchName("GOTMEL", PU_CACHE));
|
||||
x -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static void M_DrawStatsMaps(void)
|
||||
|
|
@ -6681,7 +6858,7 @@ void M_DrawStatistics(void)
|
|||
|
||||
case statisticspage_maps:
|
||||
{
|
||||
pagename = "LEVELS & MEDALS";
|
||||
pagename = "COURSES & MEDALS";
|
||||
M_DrawStatsMaps();
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,6 +111,12 @@ void K_DoFault(player_t *player)
|
|||
player->pflags |= PF_FAULT;
|
||||
player->mo->renderflags |= RF_DONTDRAW;
|
||||
player->mo->flags |= MF_NOCLIPTHING;
|
||||
|
||||
if (player->roundconditions.faulted == false)
|
||||
{
|
||||
player->roundconditions.faulted = true;
|
||||
player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -800,6 +800,9 @@ void level_tally_t::Tick(void)
|
|||
transition = 0;
|
||||
transitionTime = TICRATE/7;
|
||||
delay = TICRATE/2;
|
||||
|
||||
// for UCRP_FINISHGRADE
|
||||
owner->roundconditions.checkthisframe = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ static UINT8 cheatf_devmode(void)
|
|||
|
||||
static cheatseq_t cheat_warp = {
|
||||
NULL, cheatf_warp,
|
||||
(UINT8[]){ SCRAMBLE('p'), SCRAMBLE('l'), SCRAMBLE('a'), SCRAMBLE('c'), SCRAMBLE('e'), SCRAMBLE('h'), SCRAMBLE('o'), SCRAMBLE('l'), SCRAMBLE('d'), SCRAMBLE('e'), SCRAMBLE('r'), 0xff }
|
||||
(UINT8[]){ SCRAMBLE('c'), SCRAMBLE('h'), SCRAMBLE('a'), SCRAMBLE('o'), SCRAMBLE('s'), SCRAMBLE(' '), SCRAMBLE('z'), SCRAMBLE('e'), SCRAMBLE('r'), SCRAMBLE('o'), SCRAMBLE(' '), SCRAMBLE('6'), SCRAMBLE('4'), 0xff }
|
||||
};
|
||||
|
||||
static cheatseq_t cheat_wrongwarp = {
|
||||
|
|
@ -263,7 +263,7 @@ boolean cht_Interpret(const char *password)
|
|||
cheatseqid = 0;
|
||||
while (cheatseqlist[cheatseqid])
|
||||
{
|
||||
ret += cht_CheckCheat(cheatseqlist[cheatseqid], *password, (password == endofpassword));
|
||||
ret += cht_CheckCheat(cheatseqlist[cheatseqid], tolower(*password), (password == endofpassword));
|
||||
cheatseqid++;
|
||||
}
|
||||
|
||||
|
|
|
|||
1122
src/m_cond.c
1122
src/m_cond.c
File diff suppressed because it is too large
Load diff
71
src/m_cond.h
71
src/m_cond.h
|
|
@ -28,9 +28,12 @@ extern "C" {
|
|||
// [required] <optional>
|
||||
typedef enum
|
||||
{
|
||||
UC_NONE,
|
||||
|
||||
UC_PLAYTIME, // PLAYTIME [tics]
|
||||
UC_ROUNDSPLAYED, // ROUNDSPLAYED [x played]
|
||||
UC_TOTALRINGS, // TOTALRINGS [x collected]
|
||||
UC_TOTALTUMBLETIME, // TOTALTUMBLETIME [tics]
|
||||
|
||||
UC_GAMECLEAR, // GAMECLEAR <x times>
|
||||
UC_OVERALLTIME, // OVERALLTIME [time to beat, tics]
|
||||
|
|
@ -39,6 +42,7 @@ typedef enum
|
|||
UC_MAPBEATEN, // MAPBEATEN [map]
|
||||
UC_MAPENCORE, // MAPENCORE [map]
|
||||
UC_MAPSPBATTACK, // MAPSPBATTACK [map]
|
||||
UC_MAPMYSTICMELODY, // MAPMYSTICMELODY [map]
|
||||
UC_MAPTIME, // MAPTIME [map] [time to beat, tics]
|
||||
|
||||
UC_CHARACTERWINS, // CHARACTERWINS [character] [x rounds]
|
||||
|
|
@ -53,6 +57,8 @@ typedef enum
|
|||
UC_UNLOCKABLE, // UNLOCKABLE [unlockable number]
|
||||
UC_CONDITIONSET, // CONDITIONSET [condition set number]
|
||||
|
||||
UC_UNLOCKPERCENT, // Unlock <x percent> of [unlockable type]
|
||||
|
||||
UC_ADDON, // Ever loaded a custom file?
|
||||
UC_CREDITS, // Finish watching the credits
|
||||
UC_REPLAY, // Save a replay
|
||||
|
|
@ -62,9 +68,12 @@ typedef enum
|
|||
|
||||
UC_SPRAYCAN, // Grab a spraycan
|
||||
|
||||
UC_PRISONEGGCD, // Grab a CD from a Prison Egg
|
||||
|
||||
// Just for string building
|
||||
UC_AND,
|
||||
UC_COMMA,
|
||||
UC_DESCRIPTIONOVERRIDE,
|
||||
|
||||
UCRP_REQUIRESPLAYING, // All conditions below this can only be checked if (Playing() && gamestate == GS_LEVEL).
|
||||
|
||||
|
|
@ -79,36 +88,64 @@ typedef enum
|
|||
|
||||
UCRP_ISCHARACTER, // character == [skin]
|
||||
UCRP_ISENGINECLASS, // engine class [class]
|
||||
UCRP_HASFOLLOWER, // follower == [followerskin]
|
||||
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_PODIUMNOCONTINUES, // Get to podium sequence without any continues
|
||||
|
||||
UCRP_FINISHCOOL, // Finish in good standing
|
||||
UCRP_FINISHPERFECT, // Finish a perfect race
|
||||
UCRP_FINISHALLPRISONS, // Break all prisons
|
||||
UCRP_NOCONTEST, // No Contest
|
||||
|
||||
UCRP_SMASHUFO, // Smash the UFO Catcher
|
||||
UCRP_CHASEDBYSPB, // Chased by SPB
|
||||
UCRP_MAPDESTROYOBJECTS, // LEVELNAME: Destroy all [object names] -- CAUTION: You have to add to the level's header too to get them successfully tracked!
|
||||
|
||||
UCRP_MAKERETIRE, // Make another player of [skin] No Contest
|
||||
|
||||
UCRP_FINISHPLACE, // Finish at least [place]
|
||||
UCRP_FINISHPLACEEXACT, // Finish at [place] exactly
|
||||
|
||||
UCRP_FINISHGRADE, // Finish with at least grade [grade]
|
||||
|
||||
UCRP_FINISHTIME, // Finish <= [time, tics]
|
||||
UCRP_FINISHTIMEEXACT, // Finish == [time, tics]
|
||||
UCRP_FINISHTIMELEFT, // Finish with at least [time, tics] to spare
|
||||
|
||||
UCRP_RINGS, // >= [rings]
|
||||
UCRP_RINGSEXACT, // == [rings]
|
||||
|
||||
UCRP_SPEEDOMETER, // >= [percentage]
|
||||
UCRP_DRAFTDURATION, // >= [time, seconds]
|
||||
UCRP_GROWCONSECUTIVEBEAMS, // touch more than n times consecutively
|
||||
|
||||
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_FAULTED, // FAULT
|
||||
|
||||
UCRP_TRIPWIREHYUU, // Go through tripwire with Hyudoro
|
||||
UCRP_WHIPHYUU, // Use Insta-Whip 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_HITDRAFTERLOOKBACK, // Hit a player that's behind you, while looking back at them, and they're drafting off you
|
||||
UCRP_GIANTRACERSHRUNKENORBI, // Hit a giant racer with a shrunken Orbinaut
|
||||
UCRP_RETURNMARKTOSENDER, // Hit the player responsible for Eggman Marking you with that explosion
|
||||
|
||||
UCRP_WETPLAYER, // Don't touch [fluid]
|
||||
UCRP_TRACKHAZARD, // (Don't) get hit by a track hazard (maybe specific lap)
|
||||
|
||||
UCRP_TARGETATTACKMETHOD, // Break targets/UFO using only one method
|
||||
UCRP_GACHABOMMISER, // Break targets/UFO using exactly one Gachabom repeatedly
|
||||
|
||||
UCRP_WETPLAYER, // Don't touch [strictness] [fluid]
|
||||
} conditiontype_t;
|
||||
|
||||
// Condition Set information
|
||||
|
|
@ -184,14 +221,18 @@ typedef enum
|
|||
// Level restrictions
|
||||
SECRET_CUP, // Permit access to entire cup (overrides SECRET_MAP)
|
||||
SECRET_MAP, // Permit access to single map
|
||||
SECRET_ALTMUSIC, // Permit access to single map music track
|
||||
|
||||
// Player restrictions
|
||||
SECRET_SKIN, // Permit this character
|
||||
SECRET_FOLLOWER, // Permit this follower
|
||||
SECRET_COLOR, // Permit this color
|
||||
|
||||
// Everything below this line is supposed to be only one per Challenges list
|
||||
SECRET_ONEPERBOARD,
|
||||
|
||||
// Difficulty restrictions
|
||||
SECRET_HARDSPEED, // Permit Hard gamespeed
|
||||
SECRET_HARDSPEED = SECRET_ONEPERBOARD, // Permit Hard gamespeed
|
||||
SECRET_MASTERMODE, // Permit Master Mode bots in GP
|
||||
SECRET_ENCORE, // Permit Encore option
|
||||
|
||||
|
|
@ -219,7 +260,7 @@ typedef enum
|
|||
#define MAXEMBLEMS (MAXCONDITIONSETS*2)
|
||||
#define MAXUNLOCKABLES MAXCONDITIONSETS
|
||||
|
||||
#define CHALLENGEGRIDHEIGHT 4
|
||||
#define CHALLENGEGRIDHEIGHT 5
|
||||
#ifdef DEVELOP
|
||||
#define CHALLENGEGRIDLOOPWIDTH 3
|
||||
#else
|
||||
|
|
@ -240,11 +281,10 @@ typedef enum {
|
|||
#define GDMAX_RINGS 999999999
|
||||
#define GDMAX_CHAOKEYS 9999
|
||||
|
||||
#ifdef DEVELOP
|
||||
#define GDCONVERT_ROUNDSTOKEY 20
|
||||
#else
|
||||
#define GDCONVERT_ROUNDSTOKEY 50
|
||||
#endif
|
||||
#define GDCONVERT_ROUNDSTOKEY 32
|
||||
|
||||
#define GDINIT_CHAOKEYS 3 // Start with 3 Chao Keys !!
|
||||
#define GDINIT_PRISONSTOPRIZE 30 // 30 Prison Eggs to your [Wild Prize] !!
|
||||
|
||||
typedef enum {
|
||||
GDGT_RACE,
|
||||
|
|
@ -287,6 +327,15 @@ struct gamedata_t
|
|||
UINT16 gotspraycans;
|
||||
candata_t* spraycans;
|
||||
|
||||
// PRISON EGG PICKUPS
|
||||
UINT16 numprisoneggpickups;
|
||||
UINT16 gettableprisoneggpickups;
|
||||
UINT16 thisprisoneggpickup;
|
||||
condition_t *thisprisoneggpickup_cached;
|
||||
boolean thisprisoneggpickupgrabbed;
|
||||
UINT16 prisoneggstothispickup;
|
||||
UINT16* prisoneggpickups;
|
||||
|
||||
// CHALLENGE GRID
|
||||
UINT16 challengegridwidth;
|
||||
UINT16 *challengegrid;
|
||||
|
|
@ -298,6 +347,7 @@ struct gamedata_t
|
|||
UINT32 totalplaytime;
|
||||
UINT32 roundsplayed[GDGT_MAX];
|
||||
UINT32 totalrings;
|
||||
UINT32 totaltumbletime;
|
||||
|
||||
// Chao Key condition bypass
|
||||
UINT32 pendingkeyrounds;
|
||||
|
|
@ -311,6 +361,8 @@ struct gamedata_t
|
|||
boolean eversavedreplay;
|
||||
boolean everseenspecial;
|
||||
boolean evercrashed;
|
||||
boolean chaokeytutorial;
|
||||
boolean majorkeyskipattempted;
|
||||
gdmusic_t musicstate;
|
||||
|
||||
// BACKWARDS COMPAT ASSIST
|
||||
|
|
@ -347,6 +399,7 @@ void M_UpdateChallengeGridExtraData(challengegridextradata_t *extradata);
|
|||
#define CHE_CONNECTEDLEFT (1<<1)
|
||||
#define CHE_CONNECTEDUP (1<<2)
|
||||
#define CHE_DONTDRAW (CHE_CONNECTEDLEFT|CHE_CONNECTEDUP)
|
||||
#define CHE_ALLCLEAR (1<<3)
|
||||
|
||||
char *M_BuildConditionSetString(UINT16 unlockid);
|
||||
#define DESCRIPTIONWIDTH 170
|
||||
|
|
@ -372,6 +425,8 @@ boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud, boolean doall);
|
|||
#define PENDING_CHAOKEYS (UINT16_MAX-1)
|
||||
UINT16 M_GetNextAchievedUnlock(boolean canskipchaokeys);
|
||||
|
||||
void M_UpdateNextPrisonEggPickup(void);
|
||||
|
||||
UINT16 M_CheckLevelEmblems(void);
|
||||
UINT16 M_CompletionEmblems(void);
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ menu_t MISC_ChallengesDef = {
|
|||
&MainDef,
|
||||
0,
|
||||
MISC_ChallengesStatsDummyMenu,
|
||||
BASEVIDWIDTH/2, 30,
|
||||
BASEVIDWIDTH/2, 11,
|
||||
0, 0,
|
||||
0,
|
||||
"UNLOCK",
|
||||
|
|
@ -54,6 +54,83 @@ menu_t MISC_StatisticsDef = {
|
|||
|
||||
struct challengesmenu_s challengesmenu;
|
||||
|
||||
static void M_UpdateChallengeGridVisuals(void)
|
||||
{
|
||||
UINT16 i;
|
||||
|
||||
challengesmenu.unlockcount[CMC_UNLOCKED] = 0;
|
||||
challengesmenu.unlockcount[CMC_TOTAL] = 0;
|
||||
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
{
|
||||
if (!unlockables[i].conditionset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
challengesmenu.unlockcount[CMC_TOTAL]++;
|
||||
|
||||
if (!gamedata->unlocked[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
challengesmenu.unlockcount[CMC_UNLOCKED]++;
|
||||
|
||||
if (M_Achieved(unlockables[i].conditionset - 1) == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
challengesmenu.unlockcount[CMC_KEYED]++;
|
||||
|
||||
if (unlockables[i].majorunlock == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
challengesmenu.unlockcount[CMC_MAJORSKIPPED]++;
|
||||
}
|
||||
|
||||
challengesmenu.unlockcount[CMC_PERCENT] =
|
||||
(100 * challengesmenu.unlockcount[CMC_UNLOCKED])
|
||||
/challengesmenu.unlockcount[CMC_TOTAL];
|
||||
|
||||
#define medalheight (19)
|
||||
|
||||
challengesmenu.unlockcount[CMC_MEDALID] = 0;
|
||||
|
||||
challengesmenu.unlockcount[CMC_MEDALFILLED] =
|
||||
(medalheight * (
|
||||
challengesmenu.unlockcount[CMC_UNLOCKED]
|
||||
- challengesmenu.unlockcount[CMC_MAJORSKIPPED]
|
||||
)) / challengesmenu.unlockcount[CMC_TOTAL];
|
||||
|
||||
if (challengesmenu.unlockcount[CMC_PERCENT] == 100)
|
||||
{
|
||||
if (challengesmenu.unlockcount[CMC_KEYED] == 0)
|
||||
{
|
||||
challengesmenu.unlockcount[CMC_MEDALID] = 2;
|
||||
challengesmenu.unlockcount[CMC_PERCENT]++; // 101%
|
||||
}
|
||||
else if (challengesmenu.unlockcount[CMC_MAJORSKIPPED] == 0)
|
||||
{
|
||||
challengesmenu.unlockcount[CMC_MEDALID] = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (challengesmenu.unlockcount[CMC_MEDALFILLED] == 0 && challengesmenu.unlockcount[CMC_UNLOCKED] != 0)
|
||||
{
|
||||
// Cheat to give you a sliver of pixel.
|
||||
challengesmenu.unlockcount[CMC_MEDALFILLED] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
challengesmenu.unlockcount[CMC_MEDALBLANK] =
|
||||
medalheight - challengesmenu.unlockcount[CMC_MEDALFILLED];
|
||||
}
|
||||
|
||||
static void M_ChallengesAutoFocus(UINT16 unlockid, boolean fresh)
|
||||
{
|
||||
UINT16 i;
|
||||
|
|
@ -210,7 +287,7 @@ static void M_ChallengesAutoFocus(UINT16 unlockid, boolean fresh)
|
|||
|
||||
menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu)
|
||||
{
|
||||
UINT16 i, newunlock;
|
||||
UINT16 newunlock;
|
||||
|
||||
if (Playing())
|
||||
return desiredmenu;
|
||||
|
|
@ -231,6 +308,7 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu)
|
|||
challengesmenu.requestflip = false;
|
||||
challengesmenu.requestnew = false;
|
||||
challengesmenu.chaokeyadd = false;
|
||||
challengesmenu.keywasadded = false;
|
||||
challengesmenu.chaokeyhold = 0;
|
||||
challengesmenu.currentunlock = MAXUNLOCKABLES;
|
||||
challengesmenu.unlockcondition = NULL;
|
||||
|
|
@ -246,22 +324,8 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu)
|
|||
|
||||
memset(setup_explosions, 0, sizeof(setup_explosions));
|
||||
memset(&challengesmenu.unlockcount, 0, sizeof(challengesmenu.unlockcount));
|
||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
{
|
||||
if (!unlockables[i].conditionset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
challengesmenu.unlockcount[CC_TOTAL]++;
|
||||
|
||||
if (!gamedata->unlocked[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
challengesmenu.unlockcount[CC_UNLOCKED]++;
|
||||
}
|
||||
M_UpdateChallengeGridVisuals();
|
||||
|
||||
if (challengesmenu.pending)
|
||||
M_ChallengesAutoFocus(newunlock, true);
|
||||
|
|
@ -290,10 +354,10 @@ void M_Challenges(INT32 choice)
|
|||
M_SetupNextMenu(&MISC_ChallengesDef, false);
|
||||
}
|
||||
|
||||
static boolean M_CanKeyHiliTile(boolean devskip)
|
||||
boolean M_CanKeyHiliTile(void)
|
||||
{
|
||||
// No keys to do it with?
|
||||
if (gamedata->chaokeys == 0 && !devskip)
|
||||
if (gamedata->chaokeys == 0)
|
||||
return false;
|
||||
|
||||
// No tile data?
|
||||
|
|
@ -308,22 +372,73 @@ static boolean M_CanKeyHiliTile(boolean devskip)
|
|||
if (gamedata->unlocked[challengesmenu.currentunlock] == true)
|
||||
return false;
|
||||
|
||||
// Marked as unskippable?
|
||||
if (unlockables[challengesmenu.currentunlock].majorunlock == true && !devskip)
|
||||
return false;
|
||||
|
||||
UINT16 i = (challengesmenu.hilix * CHALLENGEGRIDHEIGHT) + challengesmenu.hiliy;
|
||||
|
||||
// Not a hinted tile OR a fresh board.
|
||||
if (!(challengesmenu.extradata[i].flags & CHE_HINT)
|
||||
&& (challengesmenu.unlockcount[CC_UNLOCKED] + challengesmenu.unlockcount[CC_TALLY] > 0)
|
||||
&& !devskip)
|
||||
&& (challengesmenu.unlockcount[CMC_UNLOCKED] > 0))
|
||||
return false;
|
||||
|
||||
// Marked as major?
|
||||
if (unlockables[challengesmenu.currentunlock].majorunlock == true)
|
||||
{
|
||||
if (!(challengesmenu.extradata[i].flags & CHE_ALLCLEAR))
|
||||
return false;
|
||||
|
||||
if (gamedata->chaokeys < 10)
|
||||
return false;
|
||||
}
|
||||
|
||||
// All good!
|
||||
return true;
|
||||
}
|
||||
|
||||
enum {
|
||||
CCTUTORIAL_KEYGEN = 0,
|
||||
CCTUTORIAL_MAJORSKIP,
|
||||
} cctutorial_e;
|
||||
|
||||
static void M_ChallengesTutorial(UINT8 option)
|
||||
{
|
||||
switch (option)
|
||||
{
|
||||
case CCTUTORIAL_KEYGEN:
|
||||
{
|
||||
M_StartMessage("Challenges & Chao Keys",
|
||||
va(M_GetText(
|
||||
"You just generated a Chao Key!\n"
|
||||
"These can clear tough Challenges.\n"
|
||||
"\n"
|
||||
"Use them wisely - it'll take\n"
|
||||
"%u rounds to pick up another!\n"
|
||||
), GDCONVERT_ROUNDSTOKEY
|
||||
), NULL, MM_NOTHING, NULL, NULL);
|
||||
gamedata->chaokeytutorial = true;
|
||||
break;
|
||||
}
|
||||
case CCTUTORIAL_MAJORSKIP:
|
||||
{
|
||||
M_StartMessage("Big Challenges & Chao Keys",
|
||||
M_GetText(
|
||||
"Watch out! You need 10 Chao Keys.\n"
|
||||
"to break open Big Challenge tiles.\n"
|
||||
"\n"
|
||||
"You'll also need to unlock\n"
|
||||
"the surrounding tiles first.\n"
|
||||
), NULL, MM_NOTHING, NULL, NULL);
|
||||
gamedata->majorkeyskipattempted = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
M_StartMessage("M_ChallengesTutorial ERROR",
|
||||
"Invalid argument!?\n",
|
||||
NULL, MM_NOTHING, NULL, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void M_ChallengesTick(void)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
|
|
@ -338,7 +453,7 @@ void M_ChallengesTick(void)
|
|||
if (setup_explosions[i].tics > 0)
|
||||
setup_explosions[i].tics--;
|
||||
}
|
||||
for (i = CC_ANIM; i < CC_MAX; i++)
|
||||
for (i = CMC_ANIM; i < CMC_MAX; i++)
|
||||
{
|
||||
if (challengesmenu.unlockcount[i] > 0)
|
||||
challengesmenu.unlockcount[i]--;
|
||||
|
|
@ -370,25 +485,28 @@ void M_ChallengesTick(void)
|
|||
|
||||
if (challengesmenu.chaokeyhold)
|
||||
{
|
||||
boolean devskip = false;
|
||||
#ifdef DEVELOP
|
||||
devskip = M_MenuButtonHeld(pid, MBT_Z);
|
||||
#endif
|
||||
// A little messy, but don't freak out, this is just so devs don't crash the game on non-tiles
|
||||
if ((devskip || M_MenuExtraHeld(pid)) && M_CanKeyHiliTile(devskip))
|
||||
if (M_MenuExtraHeld(pid) && M_CanKeyHiliTile())
|
||||
{
|
||||
// Not pressed just this frame?
|
||||
if (!M_MenuExtraPressed(pid))
|
||||
{
|
||||
challengesmenu.chaokeyhold++;
|
||||
|
||||
if (challengesmenu.chaokeyhold > CHAOHOLD_MAX)
|
||||
const UINT32 chaohold_duration =
|
||||
CHAOHOLD_PADDING
|
||||
+ ((unlockables[challengesmenu.currentunlock].majorunlock == true)
|
||||
? CHAOHOLD_MAJOR
|
||||
: CHAOHOLD_STANDARD
|
||||
);
|
||||
|
||||
if (challengesmenu.chaokeyhold > chaohold_duration)
|
||||
{
|
||||
#ifndef CHAOKEYDEBUG
|
||||
gamedata->chaokeys--;
|
||||
gamedata->chaokeys -= (unlockables[challengesmenu.currentunlock].majorunlock == true)
|
||||
? 10 : 1;
|
||||
#endif
|
||||
challengesmenu.chaokeyhold = 0;
|
||||
challengesmenu.unlockcount[CC_CHAOANIM]++;
|
||||
challengesmenu.unlockcount[CMC_CHAOANIM]++;
|
||||
|
||||
S_StartSound(NULL, sfx_chchng);
|
||||
|
||||
|
|
@ -401,7 +519,7 @@ void M_ChallengesTick(void)
|
|||
else
|
||||
{
|
||||
challengesmenu.chaokeyhold = 0;
|
||||
challengesmenu.unlockcount[CC_CHAONOPE] = 6;
|
||||
challengesmenu.unlockcount[CMC_CHAONOPE] = 6;
|
||||
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kb2
|
||||
}
|
||||
}
|
||||
|
|
@ -461,10 +579,12 @@ void M_ChallengesTick(void)
|
|||
S_StartSound(NULL, sfx_achiev);
|
||||
gamedata->keyspending--;
|
||||
gamedata->chaokeys++;
|
||||
challengesmenu.unlockcount[CC_CHAOANIM]++;
|
||||
challengesmenu.unlockcount[CMC_CHAOANIM]++;
|
||||
|
||||
if (gamedata->musicstate < GDMUSIC_KEYG)
|
||||
gamedata->musicstate = GDMUSIC_KEYG;
|
||||
|
||||
challengesmenu.keywasadded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -511,9 +631,9 @@ void M_ChallengesTick(void)
|
|||
if (challengesmenu.unlockcondition)
|
||||
Z_Free(challengesmenu.unlockcondition);
|
||||
challengesmenu.unlockcondition = M_BuildConditionSetString(challengesmenu.currentunlock);
|
||||
M_UpdateChallengeGridVisuals();
|
||||
|
||||
challengesmenu.unlockcount[CC_TALLY]++;
|
||||
challengesmenu.unlockcount[CC_ANIM]++;
|
||||
challengesmenu.unlockcount[CMC_ANIM]++;
|
||||
|
||||
if (challengesmenu.extradata)
|
||||
{
|
||||
|
|
@ -558,14 +678,7 @@ void M_ChallengesTick(void)
|
|||
|
||||
if (bombcolor == SKINCOLOR_NONE)
|
||||
{
|
||||
bombcolor = cv_playercolor[0].value;
|
||||
if (bombcolor == SKINCOLOR_NONE)
|
||||
{
|
||||
INT32 psk = R_SkinAvailable(cv_skin[0].string);
|
||||
if (psk == -1)
|
||||
psk = 0;
|
||||
bombcolor = skins[psk].prefcolor;
|
||||
}
|
||||
bombcolor = M_GetCvPlayerColor(0);
|
||||
}
|
||||
|
||||
i = (ref->majorunlock && M_RandomChance(FRACUNIT/2)) ? 1 : 0;
|
||||
|
|
@ -581,15 +694,6 @@ void M_ChallengesTick(void)
|
|||
}
|
||||
else if (!challengesmenu.chaokeyhold)
|
||||
{
|
||||
|
||||
// Tick down the tally. (currently not visible)
|
||||
/*if ((challengesmenu.ticker & 1)
|
||||
&& challengesmenu.unlockcount[CC_TALLY] > 0)
|
||||
{
|
||||
challengesmenu.unlockcount[CC_TALLY]--;
|
||||
challengesmenu.unlockcount[CC_UNLOCKED]++;
|
||||
}*/
|
||||
|
||||
if (challengesmenu.fade > 0)
|
||||
{
|
||||
// Fade decrease.
|
||||
|
|
@ -597,6 +701,12 @@ void M_ChallengesTick(void)
|
|||
{
|
||||
// Play music the moment control returns.
|
||||
M_PlayMenuJam();
|
||||
|
||||
if (gamedata->chaokeytutorial == false
|
||||
&& challengesmenu.keywasadded == true)
|
||||
{
|
||||
M_ChallengesTutorial(CCTUTORIAL_KEYGEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -616,13 +726,21 @@ boolean M_ChallengesInputs(INT32 ch)
|
|||
}
|
||||
else if (M_MenuExtraPressed(pid))
|
||||
{
|
||||
if (M_CanKeyHiliTile(false))
|
||||
if (gamedata->chaokeytutorial == true
|
||||
&& gamedata->majorkeyskipattempted == false
|
||||
&& challengesmenu.currentunlock < MAXUNLOCKABLES
|
||||
&& gamedata->unlocked[challengesmenu.currentunlock] == false
|
||||
&& unlockables[challengesmenu.currentunlock].majorunlock == true)
|
||||
{
|
||||
M_ChallengesTutorial(CCTUTORIAL_MAJORSKIP);
|
||||
}
|
||||
else if (M_CanKeyHiliTile())
|
||||
{
|
||||
challengesmenu.chaokeyhold = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
challengesmenu.unlockcount[CC_CHAONOPE] = 6;
|
||||
challengesmenu.unlockcount[CMC_CHAONOPE] = 6;
|
||||
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kb2
|
||||
|
||||
#ifdef CHAOKEYDEBUG
|
||||
|
|
@ -630,10 +748,7 @@ boolean M_ChallengesInputs(INT32 ch)
|
|||
{
|
||||
gamedata->unlocked[challengesmenu.currentunlock] = gamedata->unlockpending[challengesmenu.currentunlock] = false;
|
||||
|
||||
if (challengesmenu.unlockcount[CC_TALLY] > 0)
|
||||
challengesmenu.unlockcount[CC_TALLY]--;
|
||||
else
|
||||
challengesmenu.unlockcount[CC_UNLOCKED]--;
|
||||
M_UpdateChallengeGridVisuals();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -642,7 +757,15 @@ boolean M_ChallengesInputs(INT32 ch)
|
|||
#ifdef DEVELOP
|
||||
else if (M_MenuButtonPressed(pid, MBT_Z))
|
||||
{
|
||||
challengesmenu.chaokeyhold = 1;
|
||||
gamedata->chaokeys++;
|
||||
challengesmenu.unlockcount[CMC_CHAOANIM]++;
|
||||
|
||||
if (gamedata->chaokeytutorial == false)
|
||||
{
|
||||
M_ChallengesTutorial(CCTUTORIAL_KEYGEN);
|
||||
}
|
||||
|
||||
S_StartSound(NULL, sfx_dbgsal);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -73,6 +73,8 @@ bool award_target(mobj_t* mobj)
|
|||
{
|
||||
player->itemtype = KITEM_GACHABOM;
|
||||
player->itemamount++;
|
||||
if (player->roundconditions.gachabom_miser == 1)
|
||||
player->roundconditions.gachabom_miser = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -547,6 +547,16 @@ boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim)
|
|||
victim->player->growshrinktimer += 6*TICRATE;
|
||||
S_StartSound(victim, sfx_kc5a);
|
||||
|
||||
if (victim->player->roundconditions.consecutive_grow_lasers < UINT8_MAX)
|
||||
{
|
||||
victim->player->roundconditions.consecutive_grow_lasers++;
|
||||
if (victim->player->roundconditions.consecutive_grow_lasers > victim->player->roundconditions.best_consecutive_grow_lasers)
|
||||
{
|
||||
victim->player->roundconditions.best_consecutive_grow_lasers
|
||||
= victim->player->roundconditions.consecutive_grow_lasers;
|
||||
}
|
||||
}
|
||||
|
||||
if (prevTimer <= 0)
|
||||
{
|
||||
victim->scalespeed = mapobjectscale/TICRATE;
|
||||
|
|
|
|||
|
|
@ -1025,6 +1025,8 @@ void Obj_SPBExplode(mobj_t *spb)
|
|||
P_SetTarget(&spbExplode->target, spb_owner(spb));
|
||||
}
|
||||
|
||||
spbExplode->threshold = KITEM_SPB;
|
||||
|
||||
// Tell the explosion to use alternate knockback.
|
||||
spbExplode->movefactor = ((SPB_CHASETIMESCALE - spb_chasetime(spb)) * SPB_CHASETIMEMUL) / SPB_CHASETIMESCALE;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include "../k_objects.h"
|
||||
#include "../m_random.h"
|
||||
#include "../p_local.h"
|
||||
#include "../m_cond.h"
|
||||
#include "../r_main.h"
|
||||
#include "../s_sound.h"
|
||||
#include "../g_game.h"
|
||||
|
|
@ -763,52 +764,86 @@ static void UFOKillPieces(mobj_t *ufo)
|
|||
|
||||
static UINT8 GetUFODamage(mobj_t *inflictor, UINT8 damageType)
|
||||
{
|
||||
UINT8 ret = 0;
|
||||
targetdamaging_t targetdamaging = UFOD_GENERIC;
|
||||
|
||||
if (inflictor != NULL && P_MobjWasRemoved(inflictor) == false)
|
||||
{
|
||||
switch (inflictor->type)
|
||||
{
|
||||
// Shields deal chip damage.
|
||||
case MT_JAWZ_SHIELD:
|
||||
{
|
||||
targetdamaging = UFOD_JAWZ;
|
||||
ret = 10;
|
||||
break;
|
||||
}
|
||||
case MT_ORBINAUT_SHIELD:
|
||||
{
|
||||
targetdamaging = UFOD_ORBINAUT;
|
||||
ret = 10;
|
||||
break;
|
||||
}
|
||||
case MT_INSTAWHIP:
|
||||
{
|
||||
// Shields deal chip damage.
|
||||
return 10;
|
||||
targetdamaging = UFOD_WHIP;
|
||||
ret = 10;
|
||||
break;
|
||||
}
|
||||
case MT_JAWZ:
|
||||
{
|
||||
// Thrown Jawz deal a bit extra.
|
||||
return 15;
|
||||
targetdamaging = UFOD_JAWZ;
|
||||
ret = 15;
|
||||
break;
|
||||
}
|
||||
case MT_ORBINAUT:
|
||||
{
|
||||
// Thrown orbinauts deal double damage.
|
||||
return 20;
|
||||
targetdamaging = UFOD_ORBINAUT;
|
||||
ret = 20;
|
||||
break;
|
||||
}
|
||||
case MT_GACHABOM:
|
||||
{
|
||||
// Thrown gachabom need to be tracked, but have no special damage value as of yet.
|
||||
targetdamaging = UFOD_GACHABOM;
|
||||
break;
|
||||
}
|
||||
case MT_SPB:
|
||||
{
|
||||
// SPB deals triple damage.
|
||||
return 30;
|
||||
targetdamaging |= UFOD_SPB;
|
||||
ret = 30;
|
||||
break;
|
||||
}
|
||||
case MT_BANANA:
|
||||
{
|
||||
targetdamaging = UFOD_BANANA;
|
||||
|
||||
// Banana snipes deal triple damage,
|
||||
// laid down bananas deal regular damage.
|
||||
if (inflictor->health > 1)
|
||||
{
|
||||
return 30;
|
||||
ret = 30;
|
||||
break;
|
||||
}
|
||||
|
||||
return 10;
|
||||
ret = 10;
|
||||
break;
|
||||
}
|
||||
case MT_PLAYER:
|
||||
{
|
||||
// Players deal damage relative to how many sneakers they used.
|
||||
return 15 * max(1, inflictor->player->numsneakers);
|
||||
targetdamaging = UFOD_BOOST;
|
||||
ret = 15 * max(1, inflictor->player->numsneakers);
|
||||
break;
|
||||
}
|
||||
case MT_SPECIAL_UFO:
|
||||
{
|
||||
// UFODebugSetHealth
|
||||
return 1;
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
|
@ -817,6 +852,11 @@ static UINT8 GetUFODamage(mobj_t *inflictor, UINT8 damageType)
|
|||
}
|
||||
}
|
||||
|
||||
P_TrackRoundConditionTargetDamage(targetdamaging);
|
||||
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
// Guess from damage type.
|
||||
switch (damageType & DMG_TYPEMASK)
|
||||
{
|
||||
|
|
@ -884,6 +924,8 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN
|
|||
// Destroy the UFO parts, and make the emerald collectible!
|
||||
UFOKillPieces(ufo);
|
||||
|
||||
gamedata->deferredconditioncheck = true; // Check Challenges!
|
||||
|
||||
ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW);
|
||||
ufo->shadowscale = FRACUNIT/3;
|
||||
|
||||
|
|
|
|||
|
|
@ -12652,11 +12652,11 @@ void A_FireShrink(mobj_t *actor)
|
|||
INT32 locvar1 = var1;
|
||||
INT32 locvar2 = var2;
|
||||
|
||||
if (LUA_CallAction(A_FIRESHRINK, actor))
|
||||
if (LUA_CallAction(A_FIRESHRINK, actor) || locvar2 == 0)
|
||||
return;
|
||||
|
||||
actor->destscale = locvar1;
|
||||
actor->scalespeed = FRACUNIT/locvar2;
|
||||
actor->scalespeed = mapobjectscale/locvar2;
|
||||
}
|
||||
|
||||
// Function: A_SpawnPterabytes
|
||||
|
|
|
|||
|
|
@ -980,7 +980,6 @@ static mobj_t *SearchMarioNode(msecnode_t *node)
|
|||
case MT_THOK:
|
||||
case MT_GHOST:
|
||||
case MT_OVERLAY:
|
||||
case MT_EMERALDSPAWN:
|
||||
case MT_ELEMENTAL_ORB:
|
||||
case MT_ATTRACT_ORB:
|
||||
case MT_FORCE_ORB:
|
||||
|
|
|
|||
257
src/p_inter.c
257
src/p_inter.c
|
|
@ -206,10 +206,19 @@ boolean P_CanPickupEmblem(player_t *player, INT32 emblemID)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (player->bot)
|
||||
if (player != NULL)
|
||||
{
|
||||
// Your nefarious opponent puppy can't grab these for you.
|
||||
return false;
|
||||
if (player->bot)
|
||||
{
|
||||
// Your nefarious opponent puppy can't grab these for you.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player->exiting)
|
||||
{
|
||||
// Yeah but YOU didn't actually do it now did you
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -714,6 +723,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
return;
|
||||
}
|
||||
|
||||
if (player->exiting)
|
||||
{
|
||||
// Yeah but YOU didn't actually do it now did you
|
||||
return;
|
||||
}
|
||||
|
||||
if (!P_IsLocalPlayer(player))
|
||||
{
|
||||
// Must be party.
|
||||
|
|
@ -799,6 +814,72 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
return;
|
||||
}
|
||||
|
||||
case MT_PRISONEGGDROP:
|
||||
{
|
||||
if (demo.playback)
|
||||
{
|
||||
// Never collect emblems in replays.
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->bot)
|
||||
{
|
||||
// Your nefarious opponent puppy can't grab these for you.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!P_IsLocalPlayer(player))
|
||||
{
|
||||
// Must be party.
|
||||
return;
|
||||
}
|
||||
|
||||
if (special->hitlag || special->scale < mapobjectscale/2)
|
||||
{
|
||||
// Don't get during the initial activation
|
||||
return;
|
||||
}
|
||||
|
||||
if (special->extravalue1)
|
||||
{
|
||||
// Don't get during destruction
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
grandprixinfo.gp == true // Bonus Round
|
||||
&& netgame == false // game design + makes it easier to implement
|
||||
&& gamedata->thisprisoneggpickup_cached != NULL
|
||||
)
|
||||
{
|
||||
gamedata->thisprisoneggpickupgrabbed = true;
|
||||
if (gamedata->prisoneggstothispickup < GDINIT_PRISONSTOPRIZE)
|
||||
{
|
||||
// Just in case it's set absurdly low for testing.
|
||||
gamedata->prisoneggstothispickup = GDINIT_PRISONSTOPRIZE;
|
||||
}
|
||||
|
||||
if (!M_UpdateUnlockablesAndExtraEmblems(true, true))
|
||||
S_StartSound(NULL, sfx_ncitem);
|
||||
gamedata->deferredsave = true;
|
||||
}
|
||||
|
||||
statenum_t teststate = (special->state-states);
|
||||
|
||||
if (teststate == S_PRISONEGGDROP_CD)
|
||||
{
|
||||
special->momz = P_MobjFlip(special) * 2 * mapobjectscale;
|
||||
special->flags = (special->flags & ~MF_SPECIAL) | (MF_NOGRAVITY|MF_NOCLIPHEIGHT);
|
||||
special->extravalue1 = 1;
|
||||
|
||||
special->renderflags = (special->renderflags & ~RF_BRIGHTMASK) | (RF_ADD | RF_FULLBRIGHT);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MT_LSZ_BUNGEE:
|
||||
Obj_BungeeSpecial(special, player);
|
||||
return;
|
||||
|
|
@ -910,10 +991,25 @@ void P_TouchCheatcheck(mobj_t *post, player_t *player, boolean snaptopost)
|
|||
player->cheatchecknum = post->health;
|
||||
}
|
||||
|
||||
static void P_AddBrokenPrison(mobj_t *target, mobj_t *source)
|
||||
void P_TrackRoundConditionTargetDamage(targetdamaging_t targetdamaging)
|
||||
{
|
||||
(void)target;
|
||||
UINT8 i;
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
if (!playeringame[g_localplayers[i]])
|
||||
continue;
|
||||
if (players[g_localplayers[i]].spectator)
|
||||
continue;
|
||||
players[g_localplayers[i]].roundconditions.targetdamaging |= targetdamaging;
|
||||
/* -- the following isn't needed because we can just check for targetdamaging == UFOD_GACHABOM
|
||||
if (targetdamaging != UFOD_GACHABOM)
|
||||
players[g_localplayers[i]].roundconditions.gachabom_miser = 0xFF;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
static void P_AddBrokenPrison(mobj_t *target, mobj_t *inflictor, mobj_t *source)
|
||||
{
|
||||
if (!battleprisons)
|
||||
return;
|
||||
|
||||
|
|
@ -935,6 +1031,48 @@ static void P_AddBrokenPrison(mobj_t *target, mobj_t *source)
|
|||
K_SpawnBattlePoints(source->player, NULL, 1);
|
||||
}
|
||||
|
||||
targetdamaging_t targetdamaging = UFOD_GENERIC;
|
||||
if (P_MobjWasRemoved(inflictor) == true)
|
||||
;
|
||||
else switch (inflictor->type)
|
||||
{
|
||||
case MT_GACHABOM:
|
||||
targetdamaging = UFOD_GACHABOM;
|
||||
break;
|
||||
case MT_ORBINAUT:
|
||||
case MT_ORBINAUT_SHIELD:
|
||||
targetdamaging = UFOD_ORBINAUT;
|
||||
break;
|
||||
case MT_BANANA:
|
||||
targetdamaging = UFOD_BANANA;
|
||||
break;
|
||||
case MT_INSTAWHIP:
|
||||
targetdamaging = UFOD_WHIP;
|
||||
break;
|
||||
// This is only accessible for MT_CDUFO's touch!
|
||||
case MT_PLAYER:
|
||||
targetdamaging = UFOD_BOOST;
|
||||
break;
|
||||
// The following can't be accessed in standard play...
|
||||
// but the cost of tracking them here is trivial :D
|
||||
case MT_JAWZ:
|
||||
case MT_JAWZ_SHIELD:
|
||||
targetdamaging = UFOD_JAWZ;
|
||||
break;
|
||||
case MT_SPB:
|
||||
targetdamaging = UFOD_SPB;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
P_TrackRoundConditionTargetDamage(targetdamaging);
|
||||
|
||||
if (gamedata->prisoneggstothispickup)
|
||||
{
|
||||
gamedata->prisoneggstothispickup--;
|
||||
}
|
||||
|
||||
if (++numtargets >= maptargets)
|
||||
{
|
||||
P_DoAllPlayersExit(0, (grandprixinfo.gp == true));
|
||||
|
|
@ -947,6 +1085,55 @@ static void P_AddBrokenPrison(mobj_t *target, mobj_t *source)
|
|||
extratimeintics += 10*TICRATE;
|
||||
secretextratime = TICRATE/2;
|
||||
}
|
||||
|
||||
if (
|
||||
grandprixinfo.gp == true // Bonus Round
|
||||
&& demo.playback == false // Not playback
|
||||
&& netgame == false // game design + makes it easier to implement
|
||||
&& gamedata->thisprisoneggpickup_cached != NULL
|
||||
&& gamedata->prisoneggstothispickup == 0
|
||||
&& gamedata->thisprisoneggpickupgrabbed == false
|
||||
)
|
||||
{
|
||||
// Will be 0 for the next level
|
||||
gamedata->prisoneggstothispickup = (maptargets - numtargets);
|
||||
|
||||
mobj_t *secretpickup = P_SpawnMobj(
|
||||
target->x, target->y,
|
||||
target->z + target->height/2,
|
||||
MT_PRISONEGGDROP
|
||||
);
|
||||
|
||||
if (secretpickup)
|
||||
{
|
||||
secretpickup->hitlag = target->hitlag;
|
||||
|
||||
secretpickup->z -= secretpickup->height/2;
|
||||
|
||||
P_SetScale(secretpickup, mapobjectscale/TICRATE);
|
||||
// secretpickup->destscale = mapobjectscale; -- safe assumption it's already set?
|
||||
secretpickup->scalespeed = (2*mapobjectscale)/(3*TICRATE);
|
||||
|
||||
// flags are NOT from the target - just in case it's just been placed on the ceiling as a gimmick
|
||||
secretpickup->flags2 |= (source->flags2 & MF2_OBJECTFLIP);
|
||||
secretpickup->eflags |= (source->eflags & MFE_VERTICALFLIP);
|
||||
|
||||
// Okay these have to use M_Random because replays...
|
||||
// The spawning of these won't be recorded back!
|
||||
const angle_t launchangle = FixedAngle(M_RandomRange(60, 80) * FRACUNIT);
|
||||
const fixed_t launchmomentum = 20 * mapobjectscale;
|
||||
|
||||
secretpickup->momz = P_MobjFlip(target) // THIS one uses target!
|
||||
* P_ReturnThrustY(secretpickup, launchangle, launchmomentum);
|
||||
|
||||
secretpickup->angle = FixedAngle(M_RandomKey(360) * FRACUNIT);
|
||||
|
||||
P_InstaThrust(
|
||||
secretpickup, secretpickup->angle,
|
||||
P_ReturnThrustX(secretpickup, launchangle, launchmomentum)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1887,7 +2074,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
|
|||
|
||||
S_StartSound(target, sfx_mbs60);
|
||||
|
||||
P_AddBrokenPrison(target, source);
|
||||
P_AddBrokenPrison(target, inflictor, source);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -1897,7 +2084,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
|
|||
target->momz = -(3*mapobjectscale)/2;
|
||||
target->fuse = 2*TICRATE;
|
||||
|
||||
P_AddBrokenPrison(target, source);
|
||||
P_AddBrokenPrison(target, inflictor, source);
|
||||
break;
|
||||
|
||||
case MT_BATTLEBUMPER:
|
||||
|
|
@ -2193,6 +2380,8 @@ static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *sou
|
|||
|
||||
static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 type)
|
||||
{
|
||||
const boolean beforeexit = !(player->exiting || (player->pflags & PF_NOCONTEST));
|
||||
|
||||
if (type == DMG_SPECTATOR && (G_GametypeHasTeams() || G_GametypeHasSpectators()))
|
||||
{
|
||||
P_SetPlayerSpectator(player-players);
|
||||
|
|
@ -2240,7 +2429,8 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source,
|
|||
{
|
||||
case DMG_DEATHPIT:
|
||||
// Fell off the stage
|
||||
if (player->roundconditions.fell_off == false)
|
||||
if (player->roundconditions.fell_off == false
|
||||
&& beforeexit == true)
|
||||
{
|
||||
player->roundconditions.fell_off = true;
|
||||
player->roundconditions.checkthisframe = true;
|
||||
|
|
@ -2500,16 +2690,63 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
}
|
||||
}
|
||||
|
||||
if (inflictor && source && source->player)
|
||||
if (source && source->player)
|
||||
{
|
||||
if (source->player->roundconditions.hit_midair == false
|
||||
&& K_IsMissileOrKartItem(source)
|
||||
&& source != target
|
||||
&& inflictor
|
||||
&& K_IsMissileOrKartItem(inflictor)
|
||||
&& target->player->airtime > TICRATE/2
|
||||
&& source->player->airtime > TICRATE/2)
|
||||
{
|
||||
source->player->roundconditions.hit_midair = true;
|
||||
source->player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
|
||||
if (source->player->roundconditions.hit_drafter_lookback == false
|
||||
&& source != target
|
||||
&& target->player->lastdraft == (source->player - players)
|
||||
&& (K_GetKartButtons(source->player) & BT_LOOKBACK) == BT_LOOKBACK
|
||||
/*&& (AngleDelta(K_MomentumAngle(source), R_PointToAngle2(source->x, source->y, target->x, target->y)) > ANGLE_90)*/)
|
||||
{
|
||||
source->player->roundconditions.hit_drafter_lookback = true;
|
||||
source->player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
|
||||
if (source->player->roundconditions.giant_foe_shrunken_orbi == false
|
||||
&& source != target
|
||||
&& player->growshrinktimer > 0
|
||||
&& !P_MobjWasRemoved(inflictor)
|
||||
&& inflictor->type == MT_ORBINAUT
|
||||
&& inflictor->scale < FixedMul((FRACUNIT + SHRINK_SCALE), mapobjectscale * 2)) // halfway between base scale and shrink scale, a little bit of leeway
|
||||
{
|
||||
source->player->roundconditions.giant_foe_shrunken_orbi = true;
|
||||
source->player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
|
||||
if (source == target
|
||||
&& !P_MobjWasRemoved(inflictor)
|
||||
&& inflictor->type == MT_SPBEXPLOSION
|
||||
&& inflictor->threshold == KITEM_EGGMAN
|
||||
&& !P_MobjWasRemoved(inflictor->tracer)
|
||||
&& inflictor->tracer != source
|
||||
&& inflictor->tracer->player
|
||||
&& inflictor->tracer->player->roundconditions.returntosender_mark == false)
|
||||
{
|
||||
inflictor->tracer->player->roundconditions.returntosender_mark = true;
|
||||
inflictor->tracer->player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
}
|
||||
else if (!(inflictor && inflictor->player)
|
||||
&& player->laps <= numlaps
|
||||
&& damagetype != DMG_DEATHPIT)
|
||||
{
|
||||
const UINT8 requiredbit = 1<<(player->laps & 7);
|
||||
if (!(player->roundconditions.hittrackhazard[player->laps/8] & requiredbit))
|
||||
{
|
||||
player->roundconditions.hittrackhazard[player->laps/8] |= requiredbit;
|
||||
player->roundconditions.checkthisframe = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Instant-Death
|
||||
|
|
|
|||
|
|
@ -32,9 +32,6 @@ extern "C" {
|
|||
|
||||
//#define VIEWHEIGHTS "41"
|
||||
|
||||
// Maximum laps per map.
|
||||
#define MAX_LAPS 99
|
||||
|
||||
// Maximum player score.
|
||||
#define MAXSCORE 99999990 // 999999990
|
||||
|
||||
|
|
@ -260,7 +257,6 @@ void P_RecalcPrecipInSector(sector_t *sector);
|
|||
void P_PrecipitationEffects(void);
|
||||
|
||||
void P_RemoveMobj(mobj_t *th);
|
||||
boolean P_MobjWasRemoved(const mobj_t *th);
|
||||
void P_RemoveSavegameMobj(mobj_t *th);
|
||||
boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state);
|
||||
boolean P_SetMobjState(mobj_t *mobj, statenum_t state);
|
||||
|
|
@ -547,6 +543,8 @@ void P_UpdateLastPickup(player_t *player, UINT8 type);
|
|||
boolean P_CanPickupEmblem(player_t *player, INT32 emblemID);
|
||||
boolean P_EmblemWasCollected(INT32 emblemID);
|
||||
|
||||
void P_TrackRoundConditionTargetDamage(targetdamaging_t targetdamaging);
|
||||
|
||||
//
|
||||
// P_SPEC
|
||||
//
|
||||
|
|
|
|||
231
src/p_mobj.c
231
src/p_mobj.c
|
|
@ -5339,6 +5339,10 @@ static boolean P_IsTrackerType(INT32 type)
|
|||
case MT_JAWZ:
|
||||
return true;
|
||||
|
||||
// Players need to be able to glance at the Ancient Shrines
|
||||
case MT_ANCIENTSHRINE:
|
||||
return true;
|
||||
|
||||
// Primarily for minimap data, handle with care
|
||||
case MT_SPB:
|
||||
case MT_BATTLECAPSULE:
|
||||
|
|
@ -5349,6 +5353,7 @@ static boolean P_IsTrackerType(INT32 type)
|
|||
case MT_PLAYER:
|
||||
return true;
|
||||
|
||||
// HUD tracking
|
||||
case MT_OVERTIME_CENTER:
|
||||
case MT_MONITOR:
|
||||
case MT_EMERALD:
|
||||
|
|
@ -7226,7 +7231,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
mobj->frame &= ~FF_TRANSMASK;
|
||||
mobj->renderflags &= ~RF_TRANSMASK;
|
||||
|
||||
if (P_EmblemWasCollected(mobj->health - 1) || !P_CanPickupEmblem(&players[consoleplayer], mobj->health - 1))
|
||||
if (P_EmblemWasCollected(mobj->health - 1) || !P_CanPickupEmblem(NULL, mobj->health - 1))
|
||||
{
|
||||
trans = tr_trans50;
|
||||
}
|
||||
|
|
@ -7251,6 +7256,64 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
|
||||
break;
|
||||
}
|
||||
case MT_ANCIENTSHRINE:
|
||||
{
|
||||
boolean docolorized = false;
|
||||
|
||||
if (P_MobjWasRemoved(mobj->tracer) == false)
|
||||
{
|
||||
if (mobj->tracer->fuse == 1)
|
||||
{
|
||||
if (!(mapheaderinfo[gamemap-1]->records.mapvisited & MV_MYSTICMELODY))
|
||||
{
|
||||
mapheaderinfo[gamemap-1]->records.mapvisited |= MV_MYSTICMELODY;
|
||||
|
||||
if (!M_UpdateUnlockablesAndExtraEmblems(true, true))
|
||||
S_StartSound(NULL, sfx_ncitem);
|
||||
gamedata->deferredsave = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Non-RNG-advancing equivalent of Obj_SpawnEmeraldSparks
|
||||
if (leveltime % 3 == 0)
|
||||
{
|
||||
mobj_t *sparkle = P_SpawnMobjFromMobj(
|
||||
mobj,
|
||||
M_RandomRange(-48, 48) * FRACUNIT,
|
||||
M_RandomRange(-48, 48) * FRACUNIT,
|
||||
M_RandomRange(0, 64) * FRACUNIT,
|
||||
MT_SPARK
|
||||
);
|
||||
P_SetMobjState(sparkle, S_MORB1);
|
||||
|
||||
sparkle->color = SKINCOLOR_PLAGUE;
|
||||
sparkle->momz += 6 * mobj->scale * P_MobjFlip(mobj);
|
||||
P_SetScale(sparkle, 2);
|
||||
}
|
||||
|
||||
docolorized = !!(leveltime & 1);
|
||||
}
|
||||
|
||||
if (mobj->colorized != docolorized)
|
||||
{
|
||||
if (docolorized)
|
||||
{
|
||||
mobj->colorized = true;
|
||||
mobj->color = SKINCOLOR_PLAGUE;
|
||||
mobj->spriteyoffset = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
mobj->colorized = false;
|
||||
mobj->color = SKINCOLOR_NONE;
|
||||
mobj->spriteyoffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
mobj->frame = (mapheaderinfo[gamemap-1]->records.mapvisited & MV_MYSTICMELODY) ? 1 : 0;
|
||||
|
||||
break;
|
||||
}
|
||||
case MT_FLOATINGITEM:
|
||||
{
|
||||
P_ResetPitchRoll(mobj);
|
||||
|
|
@ -7615,6 +7678,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
break;
|
||||
}
|
||||
case MT_EMERALD:
|
||||
{
|
||||
Obj_EmeraldThink(mobj);
|
||||
|
||||
if (P_MobjWasRemoved(mobj))
|
||||
|
|
@ -7622,6 +7686,94 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MT_PRISONEGGDROP:
|
||||
{
|
||||
// If it gets any more complicated than this I'll make an objects/prisoneggdrop.c file, promise
|
||||
// ~toast 121023
|
||||
|
||||
statenum_t teststate = S_NULL;
|
||||
|
||||
if (mobj->flags2 & MF2_AMBUSH)
|
||||
{
|
||||
if (mobj->extravalue1 == 0 && P_IsObjectOnGround(mobj))
|
||||
{
|
||||
if (P_CheckDeathPitCollide(mobj))
|
||||
{
|
||||
P_RemoveMobj(mobj);
|
||||
return false;
|
||||
}
|
||||
|
||||
mobj->momx = mobj->momy = 0;
|
||||
mobj->flags2 |= MF2_STRONGBOX;
|
||||
}
|
||||
|
||||
teststate = (mobj->state-states);
|
||||
}
|
||||
else if (!netgame)
|
||||
{
|
||||
if (gamedata->thisprisoneggpickup_cached->type == UC_PRISONEGGCD)
|
||||
{
|
||||
teststate = S_PRISONEGGDROP_CD;
|
||||
mobj->renderflags |= RF_SEMIBRIGHT;
|
||||
}
|
||||
|
||||
P_SetMobjStateNF(mobj, teststate);
|
||||
|
||||
if (P_MobjWasRemoved(mobj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
S_StartSound(NULL, sfx_cdsprk);
|
||||
|
||||
mobj->z += P_MobjFlip(mobj);
|
||||
mobj->flags2 |= MF2_AMBUSH;
|
||||
}
|
||||
|
||||
if (teststate == S_PRISONEGGDROP_CD)
|
||||
{
|
||||
if (mobj->extravalue1)
|
||||
{
|
||||
++mobj->extravalue1;
|
||||
|
||||
INT32 trans = (mobj->extravalue1 * NUMTRANSMAPS) / (TICRATE);
|
||||
if (trans >= NUMTRANSMAPS)
|
||||
{
|
||||
P_RemoveMobj(mobj);
|
||||
return false;
|
||||
}
|
||||
|
||||
mobj->angle += ANGLE_MAX/(TICRATE/3);
|
||||
mobj->renderflags = (mobj->renderflags & ~RF_TRANSMASK) | (trans << RF_TRANSSHIFT);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mobj->flags2 & MF2_STRONGBOX)
|
||||
mobj->angle += ANGLE_MAX/TICRATE;
|
||||
else
|
||||
mobj->angle += ANGLE_MAX/(TICRATE/3);
|
||||
|
||||
// Non-RNG-advancing equivalent of Obj_SpawnEmeraldSparks
|
||||
if (leveltime % 3 == 0)
|
||||
{
|
||||
mobj_t *sparkle = P_SpawnMobjFromMobj(
|
||||
mobj,
|
||||
M_RandomRange(-48, 48) * FRACUNIT,
|
||||
M_RandomRange(-48, 48) * FRACUNIT,
|
||||
M_RandomRange(0, 64) * FRACUNIT,
|
||||
MT_SPARK
|
||||
);
|
||||
P_SetMobjStateNF(sparkle, mobjinfo[MT_EMERALDSPARK].spawnstate);
|
||||
|
||||
sparkle->color = M_RandomChance(FRACUNIT/2) ? SKINCOLOR_ULTRAMARINE : SKINCOLOR_MAGENTA;
|
||||
sparkle->momz += 8 * mobj->scale * P_MobjFlip(mobj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case MT_EMERALDFLARE:
|
||||
Obj_EmeraldFlareThink(mobj);
|
||||
|
||||
|
|
@ -11454,18 +11606,40 @@ void P_RemoveMobj(mobj_t *mobj)
|
|||
mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; // needed for P_UnsetThingPosition, etc. to work.
|
||||
|
||||
// Rings only, please!
|
||||
if (mobj->spawnpoint &&
|
||||
(mobj->type == MT_RING
|
||||
|| mobj->type == MT_BLUESPHERE)
|
||||
&& !(mobj->flags2 & MF2_DONTRESPAWN))
|
||||
if (mobj->spawnpoint == NULL)
|
||||
;
|
||||
else
|
||||
{
|
||||
//CONS_Printf("added to queue at tic %d\n", leveltime);
|
||||
itemrespawnque[iquehead] = mobj->spawnpoint;
|
||||
itemrespawntime[iquehead] = leveltime;
|
||||
iquehead = (iquehead+1)&(ITEMQUESIZE-1);
|
||||
// lose one off the end?
|
||||
if (iquehead == iquetail)
|
||||
iquetail = (iquetail+1)&(ITEMQUESIZE-1);
|
||||
if ((mobj->type == MT_RING
|
||||
|| mobj->type == MT_BLUESPHERE)
|
||||
&& !(mobj->flags2 & MF2_DONTRESPAWN))
|
||||
{
|
||||
//CONS_Printf("added to queue at tic %d\n", leveltime);
|
||||
itemrespawnque[iquehead] = mobj->spawnpoint;
|
||||
itemrespawntime[iquehead] = leveltime;
|
||||
iquehead = (iquehead+1)&(ITEMQUESIZE-1);
|
||||
// lose one off the end?
|
||||
if (iquehead == iquetail)
|
||||
iquetail = (iquetail+1)&(ITEMQUESIZE-1);
|
||||
}
|
||||
|
||||
if (numchallengedestructibles && numchallengedestructibles != UINT16_MAX)
|
||||
{
|
||||
UINT8 i;
|
||||
for (i = 0; i < mapheaderinfo[gamemap-1]->destroyforchallenge_size; i++)
|
||||
{
|
||||
if (mobj->type != mapheaderinfo[gamemap-1]->destroyforchallenge[i])
|
||||
continue;
|
||||
|
||||
if ((--numchallengedestructibles) == 0)
|
||||
{
|
||||
numchallengedestructibles = UINT16_MAX;
|
||||
gamedata->deferredconditioncheck = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (P_IsTrackerType(mobj->type))
|
||||
|
|
@ -13209,6 +13383,22 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj)
|
|||
nummapspraycans++;
|
||||
break;
|
||||
}
|
||||
case MT_ANCIENTSHRINE:
|
||||
{
|
||||
angle_t remainderangle = (mobj->angle % ANGLE_90);
|
||||
|
||||
if (remainderangle)
|
||||
{
|
||||
// Always lock to 90 degree grid.
|
||||
if (remainderangle > ANGLE_45)
|
||||
mobj->angle += ANGLE_90;
|
||||
mobj->angle -= remainderangle;
|
||||
}
|
||||
|
||||
P_SetScale(mobj, mobj->destscale = 2*mobj->scale);
|
||||
|
||||
break;
|
||||
}
|
||||
case MT_SKYBOX:
|
||||
{
|
||||
P_InitSkyboxPoint(mobj, mthing);
|
||||
|
|
@ -13988,12 +14178,12 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj)
|
|||
return true;
|
||||
}
|
||||
|
||||
static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y, fixed_t z, mobjtype_t i)
|
||||
static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
|
||||
{
|
||||
mobj_t *mobj = NULL;
|
||||
size_t arg = SIZE_MAX;
|
||||
|
||||
mobj = P_SpawnMobj(x, y, z, i);
|
||||
mobj = P_SpawnMobj(x, y, z, type);
|
||||
mobj->spawnpoint = mthing;
|
||||
|
||||
mobj->angle = FixedAngle(mthing->angle << FRACBITS);
|
||||
|
|
@ -14077,6 +14267,19 @@ static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y,
|
|||
mobj->flags2 |= MF2_OBJECTFLIP;
|
||||
}
|
||||
|
||||
if (mapheaderinfo[gamemap-1]->destroyforchallenge_size && numchallengedestructibles != UINT16_MAX)
|
||||
{
|
||||
UINT8 i;
|
||||
for (i = 0; i < mapheaderinfo[gamemap-1]->destroyforchallenge_size; i++)
|
||||
{
|
||||
if (type != mapheaderinfo[gamemap-1]->destroyforchallenge[i])
|
||||
continue;
|
||||
|
||||
numchallengedestructibles++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return mobj;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -510,6 +510,9 @@ struct precipmobj_t
|
|||
tic_t lastThink;
|
||||
};
|
||||
|
||||
// It's extremely important that all mobj_t*-reading code have access to this.
|
||||
boolean P_MobjWasRemoved(const mobj_t *th);
|
||||
|
||||
struct actioncache_t
|
||||
{
|
||||
actioncache_t *next;
|
||||
|
|
|
|||
|
|
@ -2696,7 +2696,7 @@ static UINT32 SaveSlope(const pslope_t *slope)
|
|||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
static boolean TypeIsNetSynced(mobjtype_t type)
|
||||
boolean TypeIsNetSynced(mobjtype_t type)
|
||||
{
|
||||
// Ignore stationary hoops - these will be respawned from mapthings.
|
||||
if (type == MT_HOOP)
|
||||
|
|
@ -2718,6 +2718,10 @@ static boolean TypeIsNetSynced(mobjtype_t type)
|
|||
if (type == MT_HORNCODE)
|
||||
return false;
|
||||
|
||||
// MT_PRISONEGGDROP: Yeah these are completely local
|
||||
if (type == MT_PRISONEGGDROP)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -5738,7 +5742,7 @@ static inline void P_ArchiveMisc(savebuffer_t *save)
|
|||
WRITEUINT32(save->p, grandprixinfo.rank.laps);
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.totalLaps);
|
||||
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.continuesUsed);
|
||||
WRITEUINT32(save->p, (grandprixinfo.rank.continuesUsed + 1));
|
||||
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.prisons);
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.totalPrisons);
|
||||
|
|
@ -6064,6 +6068,8 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
|
|||
WRITEUINT32(save->p, darktimer);
|
||||
WRITEFIXED(save->p, darkness);
|
||||
|
||||
WRITEUINT16(save->p, numchallengedestructibles);
|
||||
|
||||
// Is it paused?
|
||||
if (paused)
|
||||
WRITEUINT8(save->p, 0x2f);
|
||||
|
|
@ -6246,6 +6252,8 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
|
|||
darktimer = READUINT32(save->p);
|
||||
darkness = READFIXED(save->p);
|
||||
|
||||
numchallengedestructibles = READUINT16(save->p);
|
||||
|
||||
// Is it paused?
|
||||
if (READUINT8(save->p) == 0x2f)
|
||||
paused = true;
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ boolean P_SaveBufferFromFile(savebuffer_t *save, char const *name);
|
|||
void P_SaveBufferFree(savebuffer_t *save);
|
||||
size_t P_SaveBufferRemaining(const savebuffer_t *save);
|
||||
|
||||
boolean TypeIsNetSynced(mobjtype_t type);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -463,8 +463,12 @@ static void P_ClearSingleMapHeaderInfo(INT16 num)
|
|||
mapheaderinfo[num]->justPlayed = 0;
|
||||
mapheaderinfo[num]->anger = 0;
|
||||
|
||||
mapheaderinfo[num]->destroyforchallenge_size = 0;
|
||||
|
||||
mapheaderinfo[num]->cache_spraycan = UINT16_MAX;
|
||||
|
||||
mapheaderinfo[num]->cache_maplock = MAXUNLOCKABLES;
|
||||
|
||||
mapheaderinfo[num]->customopts = NULL;
|
||||
mapheaderinfo[num]->numCustomOptions = 0;
|
||||
}
|
||||
|
|
@ -7551,9 +7555,7 @@ static void P_InitLevelSettings(void)
|
|||
battleprisons = false;
|
||||
|
||||
nummapspraycans = 0;
|
||||
|
||||
// emerald hunt
|
||||
hunt1 = hunt2 = hunt3 = NULL;
|
||||
numchallengedestructibles = 0;
|
||||
|
||||
// circuit, race and competition stuff
|
||||
numcheatchecks = 0;
|
||||
|
|
@ -8057,10 +8059,32 @@ static void P_InitMinimapInfo(void)
|
|||
|
||||
void P_ResetLevelMusic(void)
|
||||
{
|
||||
mapmusrng = 0;
|
||||
|
||||
if (mapheaderinfo[gamemap-1]->musname_size > 1)
|
||||
mapmusrng = P_RandomKey(PR_MUSICSELECT, mapheaderinfo[gamemap-1]->musname_size);
|
||||
else
|
||||
mapmusrng = 0;
|
||||
{
|
||||
UINT8 tempmapmus[MAXMUSNAMES], tempmapmus_size = 1, i;
|
||||
|
||||
tempmapmus[0] = 0;
|
||||
|
||||
for (i = 1; i < mapheaderinfo[gamemap-1]->musname_size; i++)
|
||||
{
|
||||
if (mapheaderinfo[gamemap-1]->cache_muslock[i-1] < MAXUNLOCKABLES
|
||||
&& !M_CheckNetUnlockByID(mapheaderinfo[gamemap-1]->cache_muslock[i-1]))
|
||||
continue;
|
||||
|
||||
//CONS_Printf("TEST - %u\n", i);
|
||||
|
||||
tempmapmus[tempmapmus_size++] = i;
|
||||
}
|
||||
|
||||
if (tempmapmus_size > 1)
|
||||
{
|
||||
mapmusrng = P_RandomKey(PR_MUSICSELECT, tempmapmus_size);
|
||||
//CONS_Printf("Rolled position %u, maps to %u\n", mapmusrng, tempmapmus[mapmusrng]);
|
||||
mapmusrng = tempmapmus[mapmusrng];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void P_LoadLevelMusic(void)
|
||||
|
|
|
|||
17
src/p_user.c
17
src/p_user.c
|
|
@ -519,7 +519,9 @@ INT32 P_GivePlayerRings(player_t *player, INT32 num_rings)
|
|||
|
||||
player->rings += num_rings;
|
||||
|
||||
if (player->roundconditions.debt_rings == false && player->rings < 0)
|
||||
if (player->roundconditions.debt_rings == false
|
||||
&& !(player->exiting || (player->pflags & PF_NOCONTEST))
|
||||
&& player->rings < 0)
|
||||
{
|
||||
player->roundconditions.debt_rings = true;
|
||||
player->roundconditions.checkthisframe = true;
|
||||
|
|
@ -1920,6 +1922,19 @@ static void P_3dMovement(player_t *player)
|
|||
// Calculates player's speed based on distance-of-a-line formula
|
||||
player->speed = R_PointToDist2(0, 0, player->rmomx, player->rmomy);
|
||||
|
||||
const fixed_t topspeedometer = K_GetKartSpeed(player, false, true);
|
||||
|
||||
if (player->speed > topspeedometer)
|
||||
{
|
||||
const fixed_t convSpeed = (player->speed * 100) / topspeedometer;
|
||||
|
||||
if (convSpeed > player->roundconditions.maxspeed)
|
||||
{
|
||||
player->roundconditions.maxspeed = convSpeed;
|
||||
//player->roundconditions.checkthisframe = true; -- no, safe to leave until lapchange at worst
|
||||
}
|
||||
}
|
||||
|
||||
// Monster Iestyn - 04-11-13
|
||||
// Quadrants are stupid, excessive and broken, let's do this a much simpler way!
|
||||
// Get delta angle from rmom angle and player angle first
|
||||
|
|
|
|||
|
|
@ -186,13 +186,13 @@ void R_InitSkins(void)
|
|||
M_UpdateConditionSetsPending();
|
||||
}
|
||||
|
||||
UINT8 *R_GetSkinAvailabilities(boolean demolock, boolean forbots)
|
||||
UINT8 *R_GetSkinAvailabilities(boolean demolock, INT32 botforcecharacter)
|
||||
{
|
||||
UINT16 i;
|
||||
UINT8 shif, byte;
|
||||
INT32 skinid;
|
||||
static UINT8 responsebuffer[MAXAVAILABILITY];
|
||||
UINT8 defaultbotskin = R_BotDefaultSkin();
|
||||
const boolean forbots = (botforcecharacter != -1);
|
||||
|
||||
memset(&responsebuffer, 0, sizeof(responsebuffer));
|
||||
|
||||
|
|
@ -207,7 +207,7 @@ UINT8 *R_GetSkinAvailabilities(boolean demolock, boolean forbots)
|
|||
continue;
|
||||
|
||||
if ((forbots
|
||||
? (M_CheckNetUnlockByID(i) || skinid == defaultbotskin) // Assert the host's lock.
|
||||
? (M_CheckNetUnlockByID(i) || skinid == botforcecharacter) // Assert the host's lock.
|
||||
: gamedata->unlocked[i]) // Assert the local lock.
|
||||
!= true && !demolock)
|
||||
continue;
|
||||
|
|
@ -452,7 +452,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
|
|||
|
||||
if (P_IsLocalPlayer(player))
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Requested skin %d not found\n"), skinnum);
|
||||
else if(server || IsPlayerAdmin(consoleplayer))
|
||||
else if (server || IsPlayerAdmin(consoleplayer))
|
||||
CONS_Alert(CONS_WARNING, "Player %d (%s) skin %d not found\n", playernum, player_names[playernum], skinnum);
|
||||
|
||||
SetSkin(player, GetPlayerDefaultSkin(playernum)); // not found put the eggman skin
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ void R_PatchSkins(UINT16 wadnum, boolean mainfile);
|
|||
// Access
|
||||
INT32 R_SkinAvailable(const char *name);
|
||||
boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins);
|
||||
UINT8 *R_GetSkinAvailabilities(boolean demolock, boolean forbots);
|
||||
UINT8 *R_GetSkinAvailabilities(boolean demolock, INT32 botforcecharacter);
|
||||
|
||||
// Setting
|
||||
void SetPlayerSkin(INT32 playernum,const char *skinname);
|
||||
|
|
|
|||
|
|
@ -1224,7 +1224,7 @@ musicdef_t *musicdefstart = NULL;
|
|||
struct cursongcredit cursongcredit; // Currently displayed song credit info
|
||||
struct soundtest soundtest = {.tune = ""}; // Sound Test (sound test)
|
||||
|
||||
static void S_InsertMusicAtSoundTestSequenceTail(const char *musname, UINT16 map, musicdef_t ***tail)
|
||||
static void S_InsertMusicAtSoundTestSequenceTail(const char *musname, UINT16 map, UINT8 altref, musicdef_t ***tail)
|
||||
{
|
||||
UINT8 i = 0;
|
||||
musicdef_t *def = S_FindMusicDef(musname, &i);
|
||||
|
|
@ -1237,6 +1237,7 @@ static void S_InsertMusicAtSoundTestSequenceTail(const char *musname, UINT16 map
|
|||
|
||||
def->sequence.id = soundtest.sequence.id;
|
||||
def->sequence.map = map;
|
||||
def->sequence.altref = altref;
|
||||
|
||||
// So what we're doing here is to avoid iterating
|
||||
// for every insertion, we dereference the pointer
|
||||
|
|
@ -1255,17 +1256,17 @@ static void S_InsertMapIntoSoundTestSequence(UINT16 map, musicdef_t ***tail)
|
|||
|
||||
if (mapheaderinfo[map]->positionmus[0])
|
||||
{
|
||||
S_InsertMusicAtSoundTestSequenceTail(mapheaderinfo[map]->positionmus, map, tail);
|
||||
S_InsertMusicAtSoundTestSequenceTail(mapheaderinfo[map]->positionmus, map, 0, tail);
|
||||
}
|
||||
|
||||
for (i = 0; i < mapheaderinfo[map]->musname_size; i++)
|
||||
{
|
||||
S_InsertMusicAtSoundTestSequenceTail(mapheaderinfo[map]->musname[i], map, tail);
|
||||
S_InsertMusicAtSoundTestSequenceTail(mapheaderinfo[map]->musname[i], map, i, tail);
|
||||
}
|
||||
|
||||
for (i = 0; i < mapheaderinfo[map]->associatedmus_size; i++)
|
||||
{
|
||||
S_InsertMusicAtSoundTestSequenceTail(mapheaderinfo[map]->associatedmus[i], map, tail);
|
||||
S_InsertMusicAtSoundTestSequenceTail(mapheaderinfo[map]->associatedmus[i], map, ALTREF_REQUIRESBEATEN, tail);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1352,6 +1353,7 @@ void S_PopulateSoundTestSequence(void)
|
|||
|
||||
def->sequence.id = soundtest.sequence.id;
|
||||
def->sequence.map = NEXTMAP_INVALID;
|
||||
def->sequence.altref = 0;
|
||||
|
||||
def->sequence.next = soundtest.sequence.next;
|
||||
soundtest.sequence.next = def;
|
||||
|
|
@ -1369,6 +1371,7 @@ void S_PopulateSoundTestSequence(void)
|
|||
|
||||
def->sequence.id = soundtest.sequence.id;
|
||||
def->sequence.map = NEXTMAP_INVALID;
|
||||
def->sequence.altref = 0;
|
||||
|
||||
def->sequence.next = *tail;
|
||||
*tail = def;
|
||||
|
|
@ -1380,14 +1383,27 @@ static boolean S_SoundTestDefLocked(musicdef_t *def)
|
|||
{
|
||||
// temporary - i'd like to find a way to conditionally hide
|
||||
// specific musicdefs that don't have any map associated.
|
||||
if (def->sequence.map >= nummapheaders)
|
||||
if (def->sequence.map >= nummapheaders || !mapheaderinfo[def->sequence.map])
|
||||
return false;
|
||||
|
||||
mapheader_t *header = mapheaderinfo[def->sequence.map];
|
||||
|
||||
// Is the level tied to SP progression?
|
||||
if ((mapheaderinfo[def->sequence.map]->menuflags & (LF2_FINISHNEEDED|LF2_HIDEINMENU))
|
||||
&& !(mapheaderinfo[def->sequence.map]->records.mapvisited & MV_BEATEN))
|
||||
if ((
|
||||
(header->menuflags & (LF2_FINISHNEEDED|LF2_HIDEINMENU))
|
||||
|| (def->sequence.altref == ALTREF_REQUIRESBEATEN) // Associated music only when completed
|
||||
)
|
||||
&& !(header->records.mapvisited & MV_BEATEN))
|
||||
return true;
|
||||
|
||||
if (def->sequence.altref != 0 && def->sequence.altref < header->musname_size)
|
||||
{
|
||||
// Alt music requires unlocking the alt
|
||||
if ((header->cache_muslock[def->sequence.altref - 1] < MAXUNLOCKABLES)
|
||||
&& gamedata->unlocked[header->cache_muslock[def->sequence.altref - 1]] == false)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Finally, do a full-fat map check.
|
||||
return M_MapLocked(def->sequence.map+1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,11 +144,13 @@ boolean S_MusicNotInFocus(void);
|
|||
|
||||
|
||||
#define MAXDEFTRACKS 3
|
||||
#define ALTREF_REQUIRESBEATEN UINT8_MAX
|
||||
|
||||
struct soundtestsequence_t
|
||||
{
|
||||
UINT8 id;
|
||||
UINT16 map;
|
||||
UINT8 altref;
|
||||
musicdef_t *next;
|
||||
|
||||
size_t shuffleinfo;
|
||||
|
|
|
|||
|
|
@ -1196,6 +1196,8 @@ sfxinfo_t S_sfx[NUMSFX] =
|
|||
{"wchrg2", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SF_X2AWAYSOUND
|
||||
|
||||
{"horn00", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, "/"}, // HORNCODE
|
||||
{"melody", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, "/"}, // Mystic Melody
|
||||
{"cdsprk", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, "/"}, // Prison Egg CD sparkling
|
||||
{"monch", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
|
||||
{"etexpl", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Game crash"},
|
||||
|
||||
|
|
|
|||
|
|
@ -1265,6 +1265,8 @@ typedef enum
|
|||
sfx_wchrg2,
|
||||
|
||||
sfx_horn00,
|
||||
sfx_melody,
|
||||
sfx_cdsprk,
|
||||
sfx_monch,
|
||||
sfx_etexpl,
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue