UC_PRISONEGGCD

Gamedata minor version was updated again.
(God this was a weirdly big amount of work and it's not even polished.)
- Condition1 = PrisonEggCD [Level that has to be unlocked]
- Approximately every 30 Prison Eggs destroyed, you get a shot at a Prison Egg Drop.
    - The only Prison Egg Drop implemented right now is an Alt Music CD.
    - Your [Wild Prize] is guaranteed to be selected only from conditions associated with levels that are unlocked!
- Only spawns in Grand Prix Bonus Rounds, for netsync and game design.
- The number is fuzzed. If you start the level with 0 Prison Eggs to destroy, it selects a random number of Prisons in the level to bust.
- If you miss the pickup (such as into a deathpit), you'll get another shot in the immediate next Bonus Round you play.

Also:
- The number of Chao Keys you start your gamedata with is now part of the header file, not buried in the wiping function.
- Removed the ACTUAL last object definition vestiges of the Emerald Hunt gamemode.
This commit is contained in:
toaster 2023-10-12 21:12:08 +01:00
parent 71a95b3096
commit f8de2cfc83
14 changed files with 427 additions and 56 deletions

View file

@ -2643,6 +2643,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")))
{

View file

@ -1218,10 +1218,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",
@ -4841,8 +4839,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_EMERALD",
"MT_EMERALDSPARK",
"MT_EMERALDFLARE",
"MT_EMERHUNT", // Emerald Hunt
"MT_EMERALDSPAWN", // Emerald spawner w/ delay
"MT_PRISONEGGDROP",
// Springs and others
"MT_FAN",

View file

@ -4425,7 +4425,7 @@ void G_LoadGameSettings(void)
}
#define GD_VERSIONCHECK 0xBA5ED123 // Change every major version, as usual
#define GD_VERSIONMINOR 7 // Change every format update
#define GD_VERSIONMINOR 8 // Change every format update
// You can't rearrange these without a special format update
typedef enum
@ -4583,6 +4583,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);
@ -5090,6 +5097,7 @@ void G_SaveGameData(void)
(4*GDGT_MAX)+
4+1+2+2+
4+
2+2+
4+
(MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+
4+2);
@ -5256,6 +5264,10 @@ void G_SaveGameData(void)
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

View file

@ -148,7 +148,9 @@ char sprnames[NUMSPRITES + 1][5] =
"EMRC", // Chaos Emeralds
"SEMR", // Super Emeralds
"ESPK",
"SHRD", // Emerald Hunt
// Prison Egg Drops
"ALTM",
// Interactive Objects
"BBLS", // water bubble source
@ -1920,10 +1922,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, -1, {NULL}, 0, 0, S_NULL}, // S_PRISONEGGDROP_CD
// Bubble Source
{SPR_BBLS, 0, 8, {A_BubbleSpawn}, 2048, 0, S_BUBBLES2}, // S_BUBBLES1
@ -8482,9 +8482,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
@ -8495,44 +8495,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
},

View file

@ -703,7 +703,9 @@ typedef enum sprite
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
@ -2400,10 +2402,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,
@ -6062,8 +6062,7 @@ typedef enum mobj_type
MT_EMERALD,
MT_EMERALDSPARK,
MT_EMERALDFLARE,
MT_EMERHUNT, // Emerald Hunt
MT_EMERALDSPAWN, // Emerald spawner w/ delay
MT_PRISONEGGDROP,
// Springs and others
MT_FAN,

View file

@ -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)

View file

@ -679,6 +679,14 @@ void M_ClearSecrets(void)
gamedata->numspraycans = 0;
gamedata->gotspraycans = 0;
Z_Free(gamedata->prisoneggpickups);
gamedata->prisoneggpickups = NULL;
gamedata->numprisoneggpickups = 0;
gamedata->gettableprisoneggpickups = 0;
gamedata->thisprisoneggpickup = MAXCONDITIONSETS;
gamedata->thisprisoneggpickup_cached = NULL;
gamedata->thisprisoneggpickupgrabbed = false;
UINT16 i, j;
for (i = 0; i < nummapheaders; i++)
{
@ -713,7 +721,9 @@ void M_ClearSecrets(void)
gamedata->pendingkeyrounds = 0;
gamedata->pendingkeyroundoffset = 0;
gamedata->keyspending = 0;
gamedata->chaokeys = 3; // Start with 3 !!
gamedata->chaokeys = GDINIT_CHAOKEYS;
gamedata->prisoneggstothispickup = GDINIT_PRISONSTOPRIZE;
}
// For lack of a better idea on where to put this
@ -835,6 +845,171 @@ static void M_AssignSpraycans(void)
}
}
static void M_InitPrisonEggPickups(void)
{
// Init ordered list of skincolors
UINT16 temppickups[MAXCONDITIONSETS];
UINT16 listlen = 0;
UINT32 i, j;
conditionset_t *c;
condition_t *cn;
for (i = 0; i < MAXCONDITIONSETS; ++i)
{
// Optimisation - unlike Spray Cans, these are rebuilt every game launch/savedata wipe.
// Therefore, we don't need to re-store the ones that have been achieved.
if (gamedata->achieved[i])
continue;
c = &conditionSets[i];
if (!c->numconditions)
continue;
for (j = 0; j < c->numconditions; ++j)
{
cn = &c->condition[j];
if (cn->type != UC_PRISONEGGCD)
continue;
temppickups[listlen] = i;
listlen++;
break;
}
}
if (!listlen)
{
return;
}
// This list doesn't need to be shuffled because it's always being randomly grabbed.
// (Unlike Spray Cans, you don't know which CD you miss out on.)
gamedata->prisoneggpickups = Z_Realloc(
gamedata->prisoneggpickups,
sizeof(UINT16) * listlen,
PU_STATIC,
NULL);
while (gamedata->numprisoneggpickups < listlen)
{
gamedata->prisoneggpickups[gamedata->numprisoneggpickups]
= temppickups[gamedata->numprisoneggpickups];
gamedata->numprisoneggpickups++;
}
M_UpdateNextPrisonEggPickup();
}
void M_UpdateNextPrisonEggPickup(void)
{
UINT16 i = gamedata->gettableprisoneggpickups, j, swap;
conditionset_t *c;
condition_t *cn;
boolean firstrun = true;
cacheprisoneggpickup:
// Check if the current roll is fine
gamedata->thisprisoneggpickup_cached = NULL;
if (gamedata->thisprisoneggpickup < MAXCONDITIONSETS)
{
//CONS_Printf("CACHE TEST: thisprisoneggpickup is set to %u\n", gamedata->thisprisoneggpickup);
if (gamedata->achieved[gamedata->thisprisoneggpickup] == false)
{
c = &conditionSets[gamedata->thisprisoneggpickup];
if (c->numconditions)
{
for (j = 0; j < c->numconditions; ++j)
{
cn = &c->condition[j];
if (cn->type != UC_PRISONEGGCD)
continue;
if (cn->requirement < nummapheaders && M_MapLocked(cn->requirement+1))
continue;
// Good! Attach the cache.
gamedata->thisprisoneggpickup_cached = cn;
//CONS_Printf(" successfully set to cn!\n");
break;
}
}
}
if (gamedata->thisprisoneggpickup_cached == NULL)
{
gamedata->thisprisoneggpickup = MAXCONDITIONSETS;
gamedata->thisprisoneggpickupgrabbed = false;
}
}
if (firstrun && gamedata->numprisoneggpickups && gamedata->thisprisoneggpickup == MAXCONDITIONSETS)
{
for (; i < gamedata->numprisoneggpickups; i++)
{
if (gamedata->achieved[gamedata->prisoneggpickups[i]] == false)
{
c = &conditionSets[gamedata->prisoneggpickups[i]];
if (c->numconditions)
{
for (j = 0; j < c->numconditions; ++j)
{
cn = &c->condition[j];
if (cn->type != UC_PRISONEGGCD)
continue;
// Locked associated map? Keep in the rear end dimension!
if (cn->requirement < nummapheaders && M_MapLocked(cn->requirement+1))
break; // not continue intentionally
// Okay, this should be available.
// Bring to the front!
swap = gamedata->prisoneggpickups[gamedata->gettableprisoneggpickups];
gamedata->prisoneggpickups[gamedata->gettableprisoneggpickups] =
gamedata->prisoneggpickups[i];
gamedata->prisoneggpickups[i] = swap;
gamedata->gettableprisoneggpickups++;
break;
}
if (j < c->numconditions)
continue;
}
}
// Fell all the way through?
// Push this all the way to the back, and lop it off!
swap = gamedata->prisoneggpickups[gamedata->numprisoneggpickups];
gamedata->prisoneggpickups[gamedata->numprisoneggpickups] =
gamedata->prisoneggpickups[i];
gamedata->prisoneggpickups[i] = swap;
gamedata->numprisoneggpickups--;
i--; // We run the loop again for this entry
}
if (gamedata->gettableprisoneggpickups)
{
gamedata->thisprisoneggpickup =
gamedata->prisoneggpickups[
M_RandomKey(gamedata->gettableprisoneggpickups)
];
firstrun = false;
goto cacheprisoneggpickup;
}
}
//CONS_Printf("thisprisoneggpickup = %u (MAXCONDITIONSETS is %u)\n", gamedata->thisprisoneggpickup, MAXCONDITIONSETS);
}
static void M_PrecacheLevelLocks(void)
{
UINT16 i, j;
@ -908,6 +1083,9 @@ void M_FinaliseGameData(void)
// Place the spraycans, which CAN'T be done lazily.
M_AssignSpraycans();
// You could probably do the Prison Egg Pickups lazily, but it'd be a lagspike mid-combat.
M_InitPrisonEggPickups();
// Don't consider loaded until it's a success!
// It used to do this much earlier, but this would cause the gamedata
// to save over itself when it I_Errors from corruption, which can
@ -1160,6 +1338,7 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
return false;
case UC_PASSWORD:
return (cn->stringvar == NULL);
case UC_SPRAYCAN:
{
if (cn->requirement <= 0
@ -1174,6 +1353,9 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
return (gamedata->spraycans[can_id].map < nummapheaders);
}
case UC_PRISONEGGCD:
return ((gamedata->thisprisoneggpickupgrabbed == true) && (cn == gamedata->thisprisoneggpickup_cached));
// Just for string building
case UC_AND:
case UC_COMMA:
@ -1748,6 +1930,7 @@ static const char *M_GetConditionString(condition_t *cn)
return NULL;
case UC_PASSWORD:
return "enter a secret password";
case UC_SPRAYCAN:
{
if (cn->requirement <= 0
@ -1771,6 +1954,10 @@ static const char *M_GetConditionString(condition_t *cn)
return va("grab %d Spray Cans", can_id + 1);
}
case UC_PRISONEGGCD:
// :butterfly: "alternatively you could say 'grab a hot toooon' or 'smooth beeat'"
return "BONUS ROUND: grab a prize from a Prison Egg";
case UC_AND:
return "&";
case UC_COMMA:
@ -2161,6 +2348,8 @@ boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud, boolean doall)
{
response = M_CheckUnlockConditions(NULL);
M_UpdateNextPrisonEggPickup();
if (gamedata->pendingkeyrounds == 0
|| (gamedata->chaokeys >= GDMAX_CHAOKEYS))
{

View file

@ -63,6 +63,8 @@ typedef enum
UC_SPRAYCAN, // Grab a spraycan
UC_PRISONEGGCD, // Grab a CD from a Prison Egg
// Just for string building
UC_AND,
UC_COMMA,
@ -246,6 +248,9 @@ typedef enum {
#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,
GDGT_BATTLE,
@ -287,6 +292,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;
@ -375,6 +389,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);

View file

@ -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:

View file

@ -800,6 +800,49 @@ 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 (
grandprixinfo.gp == true // Bonus Round
&& netgame == false // game design + makes it easier to implement
&& gamedata->thisprisoneggpickup_cached != NULL
)
{
gamedata->thisprisoneggpickupgrabbed = true;
gamedata->prisoneggstothispickup = GDINIT_PRISONSTOPRIZE;
if (!M_UpdateUnlockablesAndExtraEmblems(true, true))
S_StartSound(NULL, sfx_ncitem);
gamedata->deferredsave = true;
}
break;
}
case MT_LSZ_BUNGEE:
Obj_BungeeSpecial(special, player);
return;
@ -936,6 +979,11 @@ static void P_AddBrokenPrison(mobj_t *target, mobj_t *source)
K_SpawnBattlePoints(source->player, NULL, 1);
}
if (gamedata->prisoneggstothispickup)
{
gamedata->prisoneggstothispickup--;
}
if (++numtargets >= maptargets)
{
P_DoAllPlayersExit(0, (grandprixinfo.gp == true));
@ -948,6 +996,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
- FixedMul(mobjinfo[MT_PRISONEGGDROP].height, mapobjectscale)
),
MT_PRISONEGGDROP
);
if (secretpickup)
{
secretpickup->hitlag = target->hitlag;
P_SetScale(secretpickup, mapobjectscale/TICRATE);
// secretpickup->destscale = mapobjectscale; -- safe assumption it's already set?
secretpickup->scalespeed = (2*mapobjectscale)/(3*TICRATE);
// NOT from the target - just in case it's just been placed on the ceiling as a gimmick
K_FlipFromObject(secretpickup, source);
// 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)
);
}
}
}
}

View file

@ -7638,6 +7638,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
break;
}
case MT_EMERALD:
{
Obj_EmeraldThink(mobj);
if (P_MobjWasRemoved(mobj))
@ -7645,6 +7646,56 @@ 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 (P_IsObjectOnGround(mobj))
{
if (P_CheckDeathPitCollide(mobj))
{
P_RemoveMobj(mobj);
return false;
}
mobj->momx = mobj->momy = 0;
}
teststate = (mobj->state-states);
}
else if (!netgame)
{
if (gamedata->thisprisoneggpickup_cached->type == UC_PRISONEGGCD)
{
teststate = S_PRISONEGGDROP_CD;
}
P_SetMobjState(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)
{
mobj->angle += ANGLE_MAX/TICRATE;
}
break;
}
case MT_EMERALDFLARE:
Obj_EmeraldFlareThink(mobj);

View file

@ -2708,6 +2708,10 @@ 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;
}

View file

@ -1193,6 +1193,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"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"},

View file

@ -1263,6 +1263,7 @@ typedef enum
sfx_horn00,
sfx_melody,
sfx_cdsprk,
sfx_monch,
sfx_etexpl,