Merge branch 'will-it-blend' into 'master'

Will It Blend

See merge request KartKrew/Kart!1589
This commit is contained in:
Oni 2023-11-05 18:45:51 +00:00
commit 3b04bde5b9
43 changed files with 2978 additions and 217 deletions

View file

@ -5592,7 +5592,7 @@ static INT16 Consistancy(void)
// Coop desynching enemies is painful
if (gamestate == GS_LEVEL)
{
for (i = 0; i < PRNUMCLASS; i++)
for (i = 0; i < PRNUMSYNCED; i++)
{
if (i & 1)
{

View file

@ -853,8 +853,6 @@ void D_SRB2Loop(void)
realtics = entertic - oldentertics;
oldentertics = entertic;
refreshdirmenu = 0; // not sure where to put this, here as good as any?
if (demo.playback && gamestate == GS_LEVEL)
{
// Nicer place to put this.
@ -870,8 +868,16 @@ void D_SRB2Loop(void)
interp = R_UsingFrameInterpolation() && !dedicated;
doDisplay = false;
if (realtics > 0 || singletics)
renderisnewtic = (realtics > 0 || singletics);
bool timeisprogressing = (!(paused || P_AutoPause()) && !hu_stopped);
if (renderisnewtic)
{
refreshdirmenu = 0;
P_ResetInterpHudRandSeed(timeisprogressing);
// don't skip more than 10 frames at a time
// (fadein / fadeout cause massive frame skip!)
if (realtics > 8)
@ -906,19 +912,13 @@ void D_SRB2Loop(void)
doDisplay = true;
}
renderisnewtic = true;
}
else
{
renderisnewtic = false;
}
if (interp)
{
renderdeltatics = FLOAT_TO_FIXED(deltatics);
if (!(paused || P_AutoPause()) && !hu_stopped)
if (timeisprogressing)
{
rendertimefrac = g_time.timefrac;
}
@ -938,6 +938,9 @@ void D_SRB2Loop(void)
if ((interp || doDisplay) && !frameskip)
{
if (!renderisnewtic)
P_ResetInterpHudRandSeed(false);
ranwipe = D_Display();
}

View file

@ -785,9 +785,11 @@ void readgametype(MYFILE *f, char *gtname)
INT32 newgttimelimit = 0;
UINT8 newgtinttype = 0;
char gtconst[MAXLINELEN];
char gppic[9];
char gppicmini[9];
// Empty strings.
gtconst[0] = '\0';
gtconst[0] = gppic[0] = gppicmini[0] = '\0';
do
{
@ -827,7 +829,15 @@ void readgametype(MYFILE *f, char *gtname)
else if (fastcmp(word, "IDENTIFIER"))
{
// GT_
strncpy(gtconst, word2, MAXLINELEN);
strlcpy(gtconst, word2, MAXLINELEN);
}
else if (fastcmp(word, "GPPIC"))
{
strlcpy(gppic, word2, 9);
}
else if (fastcmp(word, "GPPICMINI"))
{
strlcpy(gppicmini, word2, 9);
}
// Point and time limits
else if (fastcmp(word, "DEFAULTPOINTLIMIT"))
@ -926,6 +936,8 @@ void readgametype(MYFILE *f, char *gtname)
newgametype->name = Z_StrDup((const char *)gtname);
newgametype->rules = newgtrules;
newgametype->constant = G_PrepareGametypeConstant((const char *)gtconst);
strlcpy(newgametype->gppic, gppic, 9);
strlcpy(newgametype->gppicmini, gppicmini, 9);
newgametype->tol = newgttol;
newgametype->intermission = newgtinttype;
newgametype->pointlimit = newgtpointlimit;

View file

@ -4730,6 +4730,46 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_BALLSWITCH_BALL_ACTIVE",
"S_BALLSWITCH_PAD",
"S_BALLSWITCH_PAD_ACTIVE",
"S_SPIKEDTARGET",
"S_SPIKEDLENS",
"S_BLENDEYE_MAIN",
"S_BLENDEYE_MAIN_LAUNCHED",
"S_BLENDEYE_EYE",
"S_BLENDEYE_EYE_FLASH",
"S_BLENDEYE_GLASS",
"S_BLENDEYE_GLASS_STRESS",
"S_BLENDEYE_SHIELD",
"S_BLENDEYE_SHIELD_L",
"S_BLENDEYE_SHIELD_R",
"S_BLENDEYE_SHIELD_BUSTED",
"S_BLENDEYE_SHIELD_BUSTED_L",
"S_BLENDEYE_SHIELD_BUSTED_R",
"S_BLENDEYE_EGGBEATER_EXTEND_1",
"S_BLENDEYE_EGGBEATER_EXTEND_2",
"S_BLENDEYE_EGGBEATER",
"S_BLENDEYE_EGGBEATER_SPIN",
"S_BLENDEYE_FLAME",
"S_BLENDEYE_GENERATOR",
"S_BLENDEYE_GENERATOR_BUSTED_L",
"S_BLENDEYE_GENERATOR_BUSTED_R",
"S_BLENDEYE_PUYO_SPAWN_1",
"S_BLENDEYE_PUYO_SPAWN_2",
"S_BLENDEYE_PUYO_SPAWN_3",
"S_BLENDEYE_PUYO",
"S_BLENDEYE_PUYO_LAND_1",
"S_BLENDEYE_PUYO_LAND_2",
"S_BLENDEYE_PUYO_LAND_3",
"S_BLENDEYE_PUYO_LAND_4",
"S_BLENDEYE_PUYO_SHOCK",
"S_BLENDEYE_PUYO_DIE",
"S_BLENDEYE_PUYO_DUST",
};
// RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1",
@ -5914,6 +5954,19 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_BALLSWITCH_BALL",
"MT_BALLSWITCH_PAD",
"MT_BOSSARENACENTER",
"MT_SPIKEDTARGET",
"MT_BLENDEYE_MAIN",
"MT_BLENDEYE_EYE",
"MT_BLENDEYE_GLASS",
"MT_BLENDEYE_SHIELD",
"MT_BLENDEYE_EGGBEATER",
"MT_BLENDEYE_GENERATOR",
"MT_BLENDEYE_PUYO",
"MT_BLENDEYE_PUYO_DUST",
"MT_BLENDEYE_PUYO_DUST_COFFEE",
};
const char *const MOBJFLAG_LIST[] = {

View file

@ -607,6 +607,8 @@ struct gametype_t
UINT8 intermission;
INT32 pointlimit;
INT32 timelimit;
char gppic[9];
char gppicmini[9];
};
extern gametype_t *gametypes[MAXGAMETYPES+1];

View file

@ -1475,6 +1475,8 @@ void F_StartTitleScreen(void)
INT32 titleMapNum;
setup_numplayers = 0;
encoremode = false;
if (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS)
{
ttuser_count = 0;
@ -1495,9 +1497,6 @@ void F_StartTitleScreen(void)
titlemapinaction = true;
gamemap = titleMapNum+1;
maptol = mapheaderinfo[titleMapNum]->typeoflevel;
globalweather = mapheaderinfo[titleMapNum]->weather;
G_DoLoadLevelEx(true, GS_TITLESCREEN);
if (!titlemap)
return;

View file

@ -364,7 +364,7 @@ void G_ReadDemoExtraData(void)
switch (p)
{
case DW_RNG:
for (i = 0; i < PRNUMCLASS; i++)
for (i = 0; i < PRNUMSYNCED; i++)
{
rng = READUINT32(demobuf.p);
@ -507,7 +507,7 @@ void G_WriteDemoExtraData(void)
timeout = 16;
WRITEUINT8(demobuf.p, DW_RNG);
for (i = 0; i < PRNUMCLASS; i++)
for (i = 0; i < PRNUMSYNCED; i++)
{
WRITEUINT32(demobuf.p, P_GetRandSeed(i));
}
@ -1213,7 +1213,7 @@ readghosttic:
else if (ziptic == DW_RNG)
{
INT32 i;
for (i = 0; i < PRNUMCLASS; i++)
for (i = 0; i < PRNUMSYNCED; i++)
{
g->p += 4; // RNG seed
}
@ -2449,7 +2449,7 @@ void G_BeginRecording(void)
demotime_p = NULL;
}
for (i = 0; i < PRNUMCLASS; i++)
for (i = 0; i < PRNUMSYNCED; i++)
{
WRITEUINT32(demobuf.p, P_GetInitSeed(i));
}
@ -2913,7 +2913,7 @@ void G_LoadDemoInfo(menudemo_t *pdemo)
goto badreplay;
}
for (i = 0; i < PRNUMCLASS; i++)
for (i = 0; i < PRNUMSYNCED; i++)
{
info.p += 4; // RNG seed
}
@ -3042,7 +3042,7 @@ void G_DoPlayDemo(const char *defdemoname)
char *pdemoname;
UINT8 availabilities[MAXPLAYERS][MAXAVAILABILITY];
UINT8 version,subversion;
UINT32 randseed[PRNUMCLASS];
UINT32 randseed[PRNUMSYNCED];
char msg[1024];
boolean spectator, bot;
@ -3322,7 +3322,7 @@ void G_DoPlayDemo(const char *defdemoname)
hu_demolap = READUINT32(demobuf.p);
// Random seed
for (i = 0; i < PRNUMCLASS; i++)
for (i = 0; i < PRNUMSYNCED; i++)
{
randseed[i] = READUINT32(demobuf.p);
}
@ -3544,7 +3544,7 @@ void G_DoPlayDemo(const char *defdemoname)
R_ExecuteSetViewSize();
for (i = 0; i < PRNUMCLASS; i++)
for (i = 0; i < PRNUMSYNCED; i++)
{
P_SetRandSeed(i, randseed[i]);
}
@ -3681,7 +3681,7 @@ void G_AddGhost(savebuffer_t *buffer, char *defdemoname)
if (flags & ATTACKING_LAP)
p += 4;
for (i = 0; i < PRNUMCLASS; i++)
for (i = 0; i < PRNUMSYNCED; i++)
{
p += 4; // random seed
}
@ -3890,7 +3890,7 @@ staffbrief_t *G_GetStaffGhostBrief(UINT8 *buffer)
if (flags & ATTACKING_LAP)
temp.lap = READUINT32(p);
for (i = 0; i < PRNUMCLASS; i++)
for (i = 0; i < PRNUMSYNCED; i++)
{
p += 4; // random seed
}

View file

@ -1761,7 +1761,7 @@ void G_Ticker(boolean run)
}
}
D_MapChange(gamemap, gametype, (cv_kartencore.value == 1), false, 1, false, false);
D_MapChange(gamemap, gametype, encoremode, false, 1, false, false);
}
}
@ -3036,6 +3036,8 @@ static gametype_t defaultgametypes[] =
int_time,
0,
0,
"",
"",
},
// GT_BATTLE
@ -3047,6 +3049,8 @@ static gametype_t defaultgametypes[] =
int_scoreortimeattack,
0,
3,
"TT_RNDB",
"TT_RNSB",
},
// GT_SPECIAL
@ -3058,6 +3062,8 @@ static gametype_t defaultgametypes[] =
int_time,
0,
0,
"TT_RNDSS",
"TT_RNSSS",
},
// GT_VERSUS
@ -3069,6 +3075,8 @@ static gametype_t defaultgametypes[] =
int_scoreortimeattack,
0,
0,
"",
"",
},
// GT_TUTORIAL
@ -3080,6 +3088,8 @@ static gametype_t defaultgametypes[] =
int_none,
0,
0,
"",
"",
},
};
@ -5820,9 +5830,6 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr
gamemap = map;
maptol = mapheaderinfo[gamemap-1]->typeoflevel;
globalweather = mapheaderinfo[gamemap-1]->weather;
// Don't carry over custom music change to another map.
mapmusflags |= MUSIC_RELOADRESET;

View file

@ -933,6 +933,23 @@ char sprnames[NUMSPRITES + 1][5] =
"SA2S", // SA2-style Ball Switch
"STRG", // Spiked Target
"BLEA", // m'A'in unit
"BLEB", // o'B'server
"BLEC", // 'C'lear glass
"BLED", // shiel'D'
"BLEE", // 'E'ggbeater
"BLEF", // 'F'lamejet
"BLEG", // 'G'enerator
// Puyo hazards
"PUYA",
"PUYB",
"PUYC",
"PUYD",
"PUYE",
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
"VIEW",
};
@ -5521,6 +5538,48 @@ state_t states[NUMSTATES] =
{SPR_SA2S, FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM|4, -1, {NULL}, 1, 1, S_NULL}, // S_BALLSWITCH_BALL_ACTIVE
{SPR_SA2S, FF_FLOORSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BALLSWITCH_PAD
{SPR_SA2S, FF_FLOORSPRITE|FF_ANIMATE|FF_GLOBALANIM|1, -1, {NULL}, 1, 1, S_NULL}, // S_BALLSWITCH_PAD_ACTIVE
{SPR_STRG, FF_ADD|FF_FLOORSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SPIKEDTARGET,
{SPR_STRG, FF_ADD, -1, {NULL}, 0, 0, S_NULL}, // S_SPIKEDLENS,
{SPR_BLEA, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BLENDEYE_MAIN,
{SPR_BLEA, 1, -1, {NULL}, 0, 0, S_NULL}, // S_BLENDEYE_MAIN_LAUNCHED,
{SPR_BLEB, FF_PAPERSPRITE|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BLENDEYE_EYE,
{SPR_BLEB, FF_PAPERSPRITE|FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 1, 1, S_BLENDEYE_EYE}, // S_BLENDEYE_EYE_FLASH,
{SPR_BLEC, FF_PAPERSPRITE|FF_ADD, -1, {NULL}, 0, 0, S_NULL}, // S_BLENDEYE_GLASS,
{SPR_BLEC, 1|FF_PAPERSPRITE|FF_ADD|FF_ANIMATE, 2, {NULL}, 1, 1, S_BLENDEYE_GLASS}, // S_BLENDEYE_GLASS_STRESS,
{SPR_BLED, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BLENDEYE_SHIELD,
{SPR_BLED, 1|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BLENDEYE_SHIELD_L,
{SPR_BLED, 2|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BLENDEYE_SHIELD_R,
{SPR_BLED, 3|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BLENDEYE_SHIELD_BUSTED,
{SPR_BLED, 5|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BLENDEYE_SHIELD_BUSTED_L,
{SPR_BLED, 6|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BLENDEYE_SHIELD_BUSTED_R,
{SPR_BLEE, 0, 4, {NULL}, 0, 0, S_BLENDEYE_EGGBEATER_EXTEND_2}, // S_BLENDEYE_EGGBEATER_EXTEND_1,
{SPR_BLEE, 1|FF_ANIMATE, 8, {NULL}, 1, 2, S_BLENDEYE_EGGBEATER}, // S_BLENDEYE_EGGBEATER_EXTEND_2,
{SPR_BLEE, 2, -1, {NULL}, 0, 0, S_NULL}, // S_BLENDEYE_EGGBEATER,
{SPR_BLEE, 2|FF_ANIMATE, -1, {NULL}, 1, 2, S_NULL}, // S_BLENDEYE_EGGBEATER_SPIN,
{SPR_BLEF, FF_ADD|FF_ANIMATE, -1, {NULL}, 1, 2, S_NULL}, // S_BLENDEYE_FLAME,
{SPR_BLEG, FF_PAPERSPRITE|FF_ANIMATE, -1, {NULL}, 1, 1, S_NULL}, // S_BLENDEYE_GENERATOR,
{SPR_BLEG, 2|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BLENDEYE_GENERATOR_BUSTED_L,
{SPR_BLEG, 3|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BLENDEYE_GENERATOR_BUSTED_R,
{SPR_PUYA, 6, 2, {A_BlendEyePuyoHack}, 0, 0, S_BLENDEYE_PUYO_SPAWN_2}, // S_BLENDEYE_PUYO_SPAWN_1,
{SPR_PUYA, 5, 2, {A_BlendEyePuyoHack}, 0, 0, S_BLENDEYE_PUYO_SPAWN_3}, // S_BLENDEYE_PUYO_SPAWN_2,
{SPR_PUYA, 4, 2, {A_BlendEyePuyoHack}, 0, 0, S_BLENDEYE_PUYO}, // S_BLENDEYE_PUYO_SPAWN_3,
{SPR_PUYA, 0, -1, {A_BlendEyePuyoHack}, 0, 0, S_NULL}, // S_BLENDEYE_PUYO,
{SPR_PUYA, 2, 2, {A_BlendEyePuyoHack}, 0, 0, S_BLENDEYE_PUYO_LAND_2}, // S_BLENDEYE_PUYO_LAND_1,
{SPR_PUYA, 0, 2, {A_BlendEyePuyoHack}, 0, 0, S_BLENDEYE_PUYO_LAND_3}, // S_BLENDEYE_PUYO_LAND_2,
{SPR_PUYA, 1, 2, {A_BlendEyePuyoHack}, 0, 0, S_BLENDEYE_PUYO_LAND_4}, // S_BLENDEYE_PUYO_LAND_3,
{SPR_PUYA, 0, 2, {A_BlendEyePuyoHack}, 1, S_BLENDEYE_PUYO_LAND_1, S_BLENDEYE_PUYO}, // S_BLENDEYE_PUYO_LAND_4,
{SPR_PUYA, 3, -1, {A_BlendEyePuyoHack}, 0, 0, S_NULL}, // S_BLENDEYE_PUYO_SHOCK,
{SPR_PUYA, 4|FF_ANIMATE, 5, {A_BlendEyePuyoHack}, 2, 2, S_NULL}, // S_BLENDEYE_PUYO_DIE,
{SPR_PUYA, 5, 2, {A_BlendEyePuyoHack}, 0, 0, S_BLENDEYE_PUYO_DIE}, // S_BLENDEYE_PUYO_DUST,
};
mobjinfo_t mobjinfo[NUMMOBJTYPES] =
@ -31106,6 +31165,303 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY, // flags
S_NULL // raisestate
},
{ // MT_BOSSARENACENTER
2130, // doomednum
S_INVISIBLE, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
FRACUNIT, // radius
FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SCENERY, // flags
S_NULL // raisestate
},
{ // MT_SPIKEDTARGET
-1, // doomednum
S_SPIKEDTARGET, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
24*FRACUNIT, // radius
48*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_SCENERY|MF_NOCLIPTHING|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
{ // MT_BLENDEYE_MAIN
2131, // doomednum
S_BLENDEYE_MAIN, // spawnstate
3, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_BLENDEYE_MAIN, // painstate
0, // painchance
sfx_mbs60, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_BLENDEYE_MAIN, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
42*FRACUNIT, // radius
56*FRACUNIT, // height
0, // display offset
DMG_NORMAL, // mass
0, // damage
sfx_None, // activesound
MF_BOSS|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPTHING|MF_SOLID, // flags
S_BLENDEYE_MAIN_LAUNCHED // raisestate
},
{ // MT_BLENDEYE_EYE
-1, // doomednum
S_BLENDEYE_EYE, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
14*FRACUNIT, // radius
28*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOCLIPTHING|MF_NOGRAVITY, // flags
S_BLENDEYE_EYE_FLASH // raisestate
},
{ // MT_BLENDEYE_GLASS
-1, // doomednum
S_BLENDEYE_GLASS, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_BLENDEYE_GLASS_STRESS, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
16*FRACUNIT, // radius
96*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOGRAVITY|MF_PAPERCOLLISION|MF_PAIN, // flags
S_NULL // raisestate
},
{ // MT_BLENDEYE_SHIELD
-1, // doomednum
S_BLENDEYE_SHIELD, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
17*FRACUNIT, // radius
42*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOGRAVITY|MF_PAPERCOLLISION|MF_PAIN, // flags
S_NULL // raisestate
},
{ // MT_BLENDEYE_EGGBEATER
-1, // doomednum
S_BLENDEYE_EGGBEATER_EXTEND_1, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
26*FRACUNIT, // radius
56*FRACUNIT, // height
-1, // display offset
DMG_TUMBLE, // mass
0, // damage
sfx_None, // activesound
MF_NOGRAVITY|MF_PAIN, // flags
S_BLENDEYE_EGGBEATER_SPIN // raisestate
},
{ // MT_BLENDEYE_GENERATOR
-1, // doomednum
S_BLENDEYE_GENERATOR, // spawnstate
1, // 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_BLENDEYE_GENERATOR_BUSTED_L, // deathstate
S_BLENDEYE_GENERATOR_BUSTED_R, // xdeathstate
sfx_None, // deathsound
0, // speed
26*FRACUNIT, // radius
42*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_SHOOTABLE|MF_PAPERCOLLISION|MF_SOLID, // flags
S_NULL // raisestate
},
{ // MT_BLENDEYE_PUYO
-1, // doomednum
S_BLENDEYE_PUYO_SPAWN_1, // spawnstate
1, // 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_BLENDEYE_PUYO_DIE, // deathstate
S_NULL, // xdeathstate
sfx_mbs4c, // deathsound
0, // speed
8*FRACUNIT, // radius
12*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOHITLAGFORME, // flags
S_NULL // raisestate
},
{ // MT_BLENDEYE_PUYO_DUST
-1, // doomednum
S_BLENDEYE_PUYO_DUST, // spawnstate
1, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
4*FRACUNIT, // radius
4*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_SCENERY|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
{ // MT_BLENDEYE_PUYO_DUST_COFFEE
-1, // doomednum
S_BLENDEYE_PUYO_DUST, // spawnstate
1, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
4*FRACUNIT, // radius
4*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_SCENERY|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
};

View file

@ -296,6 +296,7 @@ enum actionnum
A_SPAWNITEMDEBRISCLOUD,
A_RINGSHOOTERFACE,
A_SPAWNSNEAKERPANEL,
A_BLENDEYEPUYOHACK,
NUMACTIONS
};
@ -572,6 +573,7 @@ void A_InvincSparkleRotate();
void A_SpawnItemDebrisCloud();
void A_RingShooterFace();
void A_SpawnSneakerPanel();
void A_BlendEyePuyoHack();
extern boolean actionsoverridden[NUMACTIONS];
@ -1485,6 +1487,23 @@ typedef enum sprite
SPR_SA2S, // SA2-style Ball Switch
SPR_STRG, // Spiked Target
SPR_BLEA, // m'A'in unit
SPR_BLEB, // o'B'server
SPR_BLEC, // 'C'lear glass
SPR_BLED, // shiel'D'
SPR_BLEE, // 'E'ggbeater
SPR_BLEF, // 'F'lamejet
SPR_BLEG, // 'G'enerator
// Puyo hazards
SPR_PUYA,
SPR_PUYB,
SPR_PUYC,
SPR_PUYD,
SPR_PUYE,
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
SPR_VIEW,
@ -5946,6 +5965,46 @@ typedef enum state
S_BALLSWITCH_PAD,
S_BALLSWITCH_PAD_ACTIVE,
S_SPIKEDTARGET,
S_SPIKEDLENS,
S_BLENDEYE_MAIN,
S_BLENDEYE_MAIN_LAUNCHED,
S_BLENDEYE_EYE,
S_BLENDEYE_EYE_FLASH,
S_BLENDEYE_GLASS,
S_BLENDEYE_GLASS_STRESS,
S_BLENDEYE_SHIELD,
S_BLENDEYE_SHIELD_L,
S_BLENDEYE_SHIELD_R,
S_BLENDEYE_SHIELD_BUSTED,
S_BLENDEYE_SHIELD_BUSTED_L,
S_BLENDEYE_SHIELD_BUSTED_R,
S_BLENDEYE_EGGBEATER_EXTEND_1,
S_BLENDEYE_EGGBEATER_EXTEND_2,
S_BLENDEYE_EGGBEATER,
S_BLENDEYE_EGGBEATER_SPIN,
S_BLENDEYE_FLAME,
S_BLENDEYE_GENERATOR,
S_BLENDEYE_GENERATOR_BUSTED_L,
S_BLENDEYE_GENERATOR_BUSTED_R,
S_BLENDEYE_PUYO_SPAWN_1,
S_BLENDEYE_PUYO_SPAWN_2,
S_BLENDEYE_PUYO_SPAWN_3,
S_BLENDEYE_PUYO,
S_BLENDEYE_PUYO_LAND_1,
S_BLENDEYE_PUYO_LAND_2,
S_BLENDEYE_PUYO_LAND_3,
S_BLENDEYE_PUYO_LAND_4,
S_BLENDEYE_PUYO_SHOCK,
S_BLENDEYE_PUYO_DIE,
S_BLENDEYE_PUYO_DUST,
S_FIRSTFREESLOT,
S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1,
NUMSTATES
@ -7149,6 +7208,19 @@ typedef enum mobj_type
MT_BALLSWITCH_BALL,
MT_BALLSWITCH_PAD,
MT_BOSSARENACENTER,
MT_SPIKEDTARGET,
MT_BLENDEYE_MAIN,
MT_BLENDEYE_EYE,
MT_BLENDEYE_GLASS,
MT_BLENDEYE_SHIELD,
MT_BLENDEYE_EGGBEATER,
MT_BLENDEYE_GENERATOR,
MT_BLENDEYE_PUYO,
MT_BLENDEYE_PUYO_DUST,
MT_BLENDEYE_PUYO_DUST_COFFEE,
MT_FIRSTFREESLOT,
MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
NUMMOBJTYPES

View file

@ -39,7 +39,7 @@ UINT8 numtargets = 0; // Capsules busted
INT32 K_StartingBumperCount(void)
{
if (battleprisons)
if (battleprisons || K_CheckBossIntro())
{
if (grandprixinfo.gp)
{

View file

@ -1,4 +1,4 @@
// SONIC ROBO BLAST 2 KART
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2018-2023 by Vivian "toastergrl" Grannell
// Copyright (C) 2018-2023 by Kart Krew

View file

@ -1,4 +1,4 @@
// SONIC ROBO BLAST 2 KART
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2018-2023 by Vivian "toastergrl" Grannell
// Copyright (C) 2018-2023 by Kart Krew
@ -129,6 +129,30 @@ void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean
boolean K_CheckBossIntro(void);
// Arena objects
boolean VS_ArenaCenterInit(mobj_t *mobj, mapthing_t *mthing);
mobj_t *VS_GetArena(INT32 bossindex);
fixed_t *VS_PredictAroundArena(mobj_t *arena, mobj_t *movingobject, fixed_t magnitude, angle_t mompoint, fixed_t radiussubtract, boolean forcegoaround, fixed_t radiusdeltafactor);
fixed_t *VS_RandomPointOnArena(mobj_t *arena, fixed_t radiussubtract);
// Blend Eye
void VS_BlendEye_Init(mobj_t *mobj);
void VS_BlendEye_Thinker(mobj_t *mobj);
boolean VS_BlendEye_Touched(mobj_t *special, mobj_t *toucher);
void VS_BlendEye_Damage(mobj_t *mobj, mobj_t *inflictor, mobj_t *source, INT32 damage);
void VS_BlendEye_Death(mobj_t *mobj);
boolean VS_BlendEye_Eye_Thinker(mobj_t *mobj);
void VS_BlendEye_Glass_Death(mobj_t *mobj);
void VS_BlendEye_Eggbeater_Touched(mobj_t *t1, mobj_t *t2);
void VS_BlendEye_Generator_DeadThinker(mobj_t *mobj);
boolean VS_PuyoTouched(mobj_t *special, mobj_t *toucher);
void VS_PuyoThinker(mobj_t *mobj);
void VS_PuyoDeath(mobj_t *mobj);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -2432,7 +2432,7 @@ static void K_drawBossHealthBar(void)
{
rolrand = 10;
}
V_DrawRightAlignedThinString(startx, starty-rolrand, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, bossinfo.enemyname);
V_DrawRightAlignedThinString(startx, starty-rolrand, V_FORCEUPPERCASE|V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, bossinfo.enemyname);
rolrand = 0;
}
@ -2448,8 +2448,8 @@ static void K_drawBossHealthBar(void)
randtemp = bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT));
if (randtemp > 0)
randlen = M_RandomKey(randtemp)+1;
randsign = M_RandomChance(FRACUNIT/2);
randlen = P_RandomKey(PR_INTERPHUDRANDOM, randtemp)+1;
randsign = P_RandomChance(PR_INTERPHUDRANDOM, FRACUNIT/2);
// Right wing.
V_DrawScaledPatch(startx-1, starty, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_FLIP, kp_bossbar[0]);
@ -2465,10 +2465,10 @@ static void K_drawBossHealthBar(void)
{
randtemp = bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT));
if (randtemp > 0)
randlen = M_RandomKey(randtemp)+1;
randlen = P_RandomKey(PR_INTERPHUDRANDOM, randtemp)+1;
if (barstatus > 1)
{
rolrand = M_RandomKey(barstatus)+1;
rolrand = P_RandomKey(PR_INTERPHUDRANDOM, barstatus)+1;
}
else
{

View file

@ -12544,16 +12544,16 @@ UINT32 K_PointLimitForGametype(void)
return 0;
}
if (cv_pointlimit.value != -1)
{
return cv_pointlimit.value;
}
if (K_Cooperative())
{
return 0;
}
if (K_CanChangeRules(true) == true && cv_pointlimit.value != -1)
{
return cv_pointlimit.value;
}
if ((gametyperules & battleRules) == battleRules) // why isn't this just another GTR_??
{
INT32 i;

View file

@ -4767,43 +4767,22 @@ void M_DrawPause(void)
if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)
{
const char *append = NULL;
switch (grandprixinfo.eventmode)
{
case GPEVENT_SPECIAL:
{
append = "SS";
break;
}
case GPEVENT_BONUS:
{
append = "B";
break;
}
default:
break;
}
if (append)
if (gametypes[gametype]->gppicmini[0])
smallroundpatch = W_CachePatchName(gametypes[gametype]->gppicmini, PU_PATCH);
else
smallroundpatch = W_CachePatchName("TT_RNSX", PU_PATCH);
}
else if (roundqueue.size > 0)
{
if (roundqueue.roundnum > 0 && roundqueue.roundnum <= 10)
{
smallroundpatch =
W_CachePatchName(
va("TT_RNS%s", append),
va("TT_RNS%d", roundqueue.roundnum),
PU_PATCH
);
}
}
else if (roundqueue.roundnum > 0 && roundqueue.roundnum <= 10)
{
smallroundpatch =
W_CachePatchName(
va("TT_RNS%d", roundqueue.roundnum),
PU_PATCH
);
}
if (smallroundpatch != NULL)
{

View file

@ -932,8 +932,7 @@ boolean K_StartCeremony(void)
{
gamemap = podiumMapNum+1;
maptol = mapheaderinfo[gamemap-1]->typeoflevel;
globalweather = static_cast<preciptype_t>(mapheaderinfo[gamemap-1]->weather);
encoremode = grandprixinfo.encore;
if (savedata.lives > 0)
{

View file

@ -295,6 +295,7 @@ void level_tally_t::Init(player_t *player)
time = std::min(static_cast<INT32>(player->realtime), (100 * 60 * TICRATE) - 1);
ringPool = player->totalring;
livesAdded = 0;
position = numPlayers = 0;
rings = 0;
@ -410,10 +411,16 @@ void level_tally_t::Init(player_t *player)
);
}
if (roundqueue.size > 0 && roundqueue.roundnum > 0
&& (grandprixinfo.gp == false || grandprixinfo.eventmode == GPEVENT_NONE))
if (roundqueue.size > 0 && roundqueue.roundnum > 0)
{
roundNum = roundqueue.roundnum;
if ((grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE))
{
roundNum = INTERMISSIONROUND_BONUS;
}
else
{
roundNum = roundqueue.roundnum;
}
}
}
else
@ -436,7 +443,7 @@ void level_tally_t::Init(player_t *player)
roundNum = roundqueue.roundnum;
}
else if (bossinfo.valid == true && bossinfo.enemyname)
else if (K_CheckBossIntro() == true && bossinfo.enemyname)
{
snprintf(
header, sizeof header,
@ -567,7 +574,10 @@ boolean level_tally_t::IncrementLine(void)
}
value = &displayStat[i];
lives_check = (stats[i] == TALLY_STAT_TOTALRINGS);
lives_check = (
stats[i] == TALLY_STAT_TOTALRINGS // Rings also shows the Lives.
&& livesAdded < owner->xtralife // Don't check if we've maxxed out!
);
switch (stats[i])
{
@ -649,6 +659,8 @@ boolean level_tally_t::IncrementLine(void)
return true;
}
const boolean playSounds = P_IsDisplayPlayer(owner);
if (*value == dest)
{
// We've reached our destination
@ -656,7 +668,6 @@ boolean level_tally_t::IncrementLine(void)
}
const INT32 prevVal = *value;
const boolean playSounds = P_IsDisplayPlayer(owner);
if (playSounds == true && tickSound == 0)
{
@ -693,6 +704,7 @@ boolean level_tally_t::IncrementLine(void)
// Handle extra life sound & blinking
if (extra > oldExtra)
{
livesAdded++;
xtraBlink = TICRATE;
if (playSounds == true)
@ -775,7 +787,24 @@ void level_tally_t::Tick(void)
{
if (IncrementLine() == true)
{
if (playSounds)
if (grandprixinfo.gp == true // In GP
&& lines >= lineCount // Finished the bonuses
&& livesAdded < owner->xtralife // Didn't max out by other causes
)
{
// This is only true if Rings alone aren't responsible for our added lives.
// Generally for Prison Break, but could be earned in custom contexts too.
livesAdded = owner->xtralife;
xtraBlink = TICRATE;
if (playSounds == true)
{
S_StopSoundByNum(sfx_cdfm73);
S_StartSound(NULL, sfx_cdfm73);
}
}
if (playSounds == true)
{
S_StopSoundByNum(sfx_mbs5b);
S_StartSound(NULL, (lines >= lineCount) ? sfx_mbs70 : sfx_mbs5b);
@ -1190,13 +1219,8 @@ void level_tally_t::Draw(void)
.colormap(owner->skin, color)
.patch(faceprefix[owner->skin][r_splitscreen ? FACE_MINIMAP : FACE_RANK]);
const UINT8 lifethreshold = 20;
const UINT8 oldExtra = ringPool / lifethreshold;
const UINT8 extra = displayStat[i] / lifethreshold;
const INT32 increase = (extra - oldExtra);
UINT8 lives_num = owner->lives + increase;
if (xtraBlink > 0 && (xtraBlink & 1) == 0 && increase > 0)
UINT8 lives_num = std::min(owner->lives + livesAdded, 10);
if (xtraBlink > 0 && (xtraBlink & 1) == 0 && livesAdded > 0)
{
lives_num = 0;
}

View file

@ -72,6 +72,7 @@ struct level_tally_t
// Stats
INT32 time;
UINT16 ringPool;
UINT8 livesAdded;
tally_stat_e stats[TALLY_WINDOW_SIZE];
// Possible grade metrics

View file

@ -864,7 +864,7 @@ void Y_VoteDrawer(void)
static void Y_FinalizeVote(const SINT8 level)
{
nextmap = g_voteLevels[level][0];
deferencoremode = (g_voteLevels[level][1] & VOTE_MOD_ENCORE);
deferencoremode = ((g_voteLevels[level][1] & VOTE_MOD_ENCORE) == VOTE_MOD_ENCORE);
}
static void Y_VoteStops(SINT8 pick, SINT8 level)

View file

@ -2559,6 +2559,8 @@ static int lib_gAddGametype(lua_State *L)
const char *gtname = NULL;
const char *gtconst = NULL;
const char *gppic = NULL;
const char *gppicmini = NULL;
UINT32 newgtrules = 0;
UINT32 newgttol = 0;
INT32 newgtpointlimit = 0;
@ -2621,6 +2623,14 @@ static int lib_gAddGametype(lua_State *L)
if (!lua_isnumber(L, 3))
TYPEERROR("defaulttimelimit", LUA_TNUMBER)
newgttimelimit = (INT32)lua_tointeger(L, 3);
} else if (i == 8 || (k && fasticmp(k, "gppic"))) {
if (!lua_isstring(L, 3))
TYPEERROR("gppic", LUA_TSTRING)
gppic = lua_tostring(L, 3);
} else if (i == 9 || (k && fasticmp(k, "gppicmini"))) {
if (!lua_isstring(L, 3))
TYPEERROR("gppicmini", LUA_TSTRING)
gppicmini = lua_tostring(L, 3);
}
lua_pop(L, 1);
}
@ -2662,6 +2672,18 @@ static int lib_gAddGametype(lua_State *L)
newgametype->pointlimit = newgtpointlimit;
newgametype->timelimit = newgttimelimit;
if (gppic != NULL)
{
// Calloc means only set if valid
strlcpy(newgametype->gppic, gppic, 9);
}
if (gppicmini != NULL)
{
// Calloc means only set if valid
strlcpy(newgametype->gppicmini, gppicmini, 9);
}
gametypes[numgametypes++] = newgametype;
// done
@ -3507,6 +3529,49 @@ static int lib_kDeclareWeakspot(lua_State *L)
return 0;
}
static int lib_vsGetArena(lua_State *L)
{
INT32 bossindex = luaL_checkinteger(L, 1);
//HUDSAFE
LUA_PushUserdata(L, VS_GetArena(bossindex), META_MOBJ);
return 1;
}
static int lib_vsPredictAroundArena(lua_State *L)
{
mobj_t *arena = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
mobj_t *movingobject = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
fixed_t magnitude = luaL_checkfixed(L, 3);
fixed_t mompoint = luaL_checkangle(L, 4);
fixed_t radiussubtract = luaL_checkfixed(L, 5);
boolean forcegoaround = lua_optboolean(L, 6);
fixed_t radiusdeltafactor = luaL_optinteger(L, 7, FRACUNIT); //optfixed?
NOHUD
if (!arena || !movingobject)
return LUA_ErrInvalid(L, "mobj_t");
fixed_t *result = VS_PredictAroundArena(arena, movingobject, magnitude, mompoint, radiussubtract, forcegoaround, radiusdeltafactor);
lua_pushfixed(L, result[0]);
lua_pushfixed(L, result[1]);
return 2;
}
static int lib_vsRandomPointOnArena(lua_State *L)
{
mobj_t *arena = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
fixed_t radiussubtract = luaL_checkfixed(L, 2);
NOHUD
if (!arena)
return LUA_ErrInvalid(L, "mobj_t");
fixed_t *result = VS_RandomPointOnArena(arena, radiussubtract);
lua_pushfixed(L, result[0]);
lua_pushfixed(L, result[1]);
return 2;
}
static int lib_getTimeMicros(lua_State *L)
{
lua_pushinteger(L, I_GetPreciseTime() / (I_GetPrecisePrecision() / 1000000));
@ -3782,6 +3847,9 @@ static luaL_Reg lib[] = {
{"K_InitBossHealthBar", lib_kInitBossHealthBar},
{"K_UpdateBossHealthBar", lib_kUpdateBossHealthBar},
{"K_DeclareWeakspot", lib_kDeclareWeakspot},
{"VS_GetArena", lib_vsGetArena},
{"VS_PredictAroundArena", lib_vsPredictAroundArena},
{"VS_RandomPointOnArena", lib_vsRandomPointOnArena},
// hu_stuff technically?
{"HU_DoTitlecardCEcho", lib_startTitlecardCecho},

View file

@ -342,6 +342,37 @@ void P_SetRandSeedNetD(const char *rfile, INT32 rline, pr_class_t pr_class, UINT
rng.seed[pr_class] = seed;
}
/** Change PR_INTERPHUDRANDOM state.
* Used for interp-safe HUD randomisation.
*
* \sa P_SetRandSeed
*/
#ifndef DEBUGRANDOM
void P_ResetInterpHudRandSeed(boolean newframe)
{
#else
void P_ResetInterpHudRandSeedD(const char *rfile, INT32 rline, boolean newframe)
{
CONS_Printf("P_ResetInterpHudRandSeed(%c) at: %sp %d\n", (newframe ? 'T' : 'F'), rfile, rline);
#endif
if (newframe == true)
{
// Advance the initialisation to the current seed.
rng.init[PR_INTERPHUDRANDOM] = rng.seed[PR_INTERPHUDRANDOM];
}
else
{
// Rewind the seed to the last initialisation.
rng.seed[PR_INTERPHUDRANDOM] = rng.init[PR_INTERPHUDRANDOM];
}
// xorshift requires a nonzero seed
// this should never happen, but just in case it DOES, we check
if (!rng.seed[PR_INTERPHUDRANDOM])
rng.seed[PR_INTERPHUDRANDOM] = rng.init[PR_INTERPHUDRANDOM] = DEFAULT_SEED;
}
/** Initializes random seeds for all classes.
* Used at the beginning of a game.
*
@ -354,7 +385,7 @@ void P_ClearRandom(UINT32 seed)
if (!seed) seed = DEFAULT_SEED;
for (i = 0; i < PRNUMCLASS; i++)
for (i = 0; i < PRNUMSYNCED; i++)
{
P_SetRandSeed(i, seed);

View file

@ -83,6 +83,10 @@ typedef enum
PR_FUZZ, // Stability testing
PRNUMSYNCED,
PR_INTERPHUDRANDOM = PRNUMSYNCED, // Interpolation-accomodating HUD randomisation
PRNUMCLASS
} pr_class_t;
@ -132,15 +136,18 @@ UINT32 P_RandomPeek(pr_class_t pr_class);
#define P_GetInitSeed(pr) P_GetInitSeedD(__FILE__, __LINE__, pr)
#define P_SetRandSeed(pr, s) P_SetRandSeedD(__FILE__, __LINE__, pr, s)
#define P_SetRandSeedNet(pr, i, s) P_SetRandSeedD(__FILE__, __LINE__, pr, i, s)
#define P_ResetInterpHudRandSeed(newframe) P_ResetInterpHudRandSeedD(__FILE__, __LINE__, newframe)
UINT32 P_GetRandSeedD(const char *rfile, INT32 rline, pr_class_t pr_class);
UINT32 P_GetInitSeedD(const char *rfile, INT32 rline, pr_class_t pr_class);
void P_SetRandSeedD(const char *rfile, INT32 rline, pr_class_t pr_class, UINT32 seed);
void P_SetRandSeedNetD(const char *rfile, INT32 rline, pr_class_t pr_class, UINT32 init, UINT32 seed);
void P_ResetInterpHudRandSeedD(const char *rfile, INT32 rline, boolean newframe);
#else
UINT32 P_GetRandSeed(pr_class_t pr_class);
UINT32 P_GetInitSeed(pr_class_t pr_class);
void P_SetRandSeed(pr_class_t pr_class, UINT32 seed);
void P_SetRandSeedNet(pr_class_t pr_class, UINT32 init, UINT32 seed);
void P_ResetInterpHudRandSeed(boolean newframe);
#endif
void P_ClearRandom(UINT32 seed);

View file

@ -31,6 +31,8 @@ void Music_Init(void)
tune.resume_fade_in = 750;
tune.sync = true;
tune.credit = true;
tune.vapes = true;
tune.nightcoreable = true;
}
{
@ -75,6 +77,7 @@ void Music_Init(void)
tune.song = "racent";
tune.priority = 40;
tune.vapes = true;
}
{

View file

@ -16,6 +16,7 @@
#include "doomdef.h"
#include "doomtype.h"
#include "k_boss.h"
#include "music_detail.hpp"
namespace srb2::music
@ -61,6 +62,10 @@ public:
// resumed).
bool credit = false;
// This tune will vape in encoremode contexts.
bool vapes = false;
bool nightcoreable = false;
// Start playing this number of tics into the tune.
tic_t seek = 0;
@ -78,12 +83,21 @@ public:
bool paused() const { return pause_.has_value(); }
bool can_end() const { return end_ != INFTICS; }
// Slow level music down a bit in Encore. (Values are vibe-based. WE GET IT YOU VAPE)
const float encoremul = 0.86471f;
float speed() const
{
// Slow level music down a bit in Encore. (Values are vibe-based. WE GET IT YOU VAPE)
if (encoremode && gamestate == GS_LEVEL)
// Apply to all tunes that support vape mode.
if (encoremode && vapes)
{
return 0.86471f;
if (nightcoreable && K_CheckBossIntro())
{
// In Versus, the vape makes you think you can start a nightcore YT channel
return (1.f/encoremul);
}
return encoremul;
}
return 1.f;

View file

@ -40,3 +40,5 @@ target_sources(SRB2SDL2 PRIVATE
shadow.cpp
ball-switch.cpp
)
add_subdirectory(versus)

View file

@ -255,6 +255,12 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL);
damageitem = true;
}
else if (t2->flags & MF_PAIN)
{
// Hazard blocks
P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH);
damageitem = true;
}
if (t1->type == MT_GARDENTOP)
{

View file

@ -127,7 +127,7 @@ void Obj_RandomItemVisuals(mobj_t *mobj)
// Don't transform stuff that isn't a Ring Box, idiot
statenum_t boxstate = mobj->state - states;
if (boxstate >= S_RANDOMITEM1 && boxstate <= S_RANDOMITEM12)
if (boxstate < S_RINGBOX1 || boxstate > S_RINGBOX12)
return;
if (mobj->extravalue1 == RINGBOX_TIME || specialstageinfo.valid)
@ -141,7 +141,7 @@ void Obj_RandomItemVisuals(mobj_t *mobj)
boolean Obj_RandomItemSpawnIn(mobj_t *mobj)
{
if ((leveltime == starttime) && !(gametyperules & GTR_CIRCUIT) && (mobj->flags2 & MF2_BOSSNOTRAP)) // here on map start?
if ((leveltime == starttime) && !(gametyperules & GTR_CIRCUIT) && (mobj->flags2 & MF2_BOSSFLEE)) // here on map start?
{
if (gametyperules & GTR_PAPERITEMS)
{
@ -192,6 +192,18 @@ void Obj_RandomItemSpawn(mobj_t *mobj)
P_SetMobjState(mobj, S_RANDOMITEM1);
}
if (leveltime == 0)
{
mobj->flags2 |= MF2_BOSSFLEE; // mark as here on map start
if ((gametyperules & GTR_CIRCUIT) != GTR_CIRCUIT) // delayed
{
P_UnsetThingPosition(mobj);
mobj->flags |= (MF_NOCLIPTHING|MF_NOBLOCKMAP);
mobj->renderflags |= RF_DONTDRAW;
P_SetThingPosition(mobj);
}
}
mobj->destscale = Obj_RandomItemScale(mobj->destscale);
P_SetScale(mobj, mobj->destscale);
}

View file

@ -0,0 +1,4 @@
target_sources(SRB2SDL2 PRIVATE
arena.c
blendeye.c
)

150
src/objects/versus/arena.c Normal file
View file

@ -0,0 +1,150 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2022-2023 by Vivian "toastergrl" Grannell
// Copyright (C) 2022-2023 by Kart Krew
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file arena.c
/// \brief Boss battle arena logic
#include "../../p_local.h"
#include "../../p_setup.h"
#include "../../m_random.h"
#include "../../k_boss.h"
#include "../../r_main.h" // R_PointToAngle2, R_PointToDist2
boolean VS_ArenaCenterInit(mobj_t *mobj, mapthing_t *mthing)
{
INT32 dist1 = mthing->thing_args[1]*FRACUNIT;
INT32 dist2 = mthing->thing_args[2]*FRACUNIT;
if (dist1 || dist2)
{
if (dist1 > dist2)
{
INT32 swap = dist1;
dist1 = dist2;
dist2 = swap;
}
mobj->extravalue1 = dist1;
mobj->extravalue2 = dist2;
return true;
}
CONS_Alert(CONS_ERROR, "Versus arena of index %d has no max radius.", mthing->thing_args[0]);
return false;
}
mobj_t *VS_GetArena(INT32 bossindex)
{
size_t i;
for (i = 0; i < nummapthings; ++i)
{
if (mapthings[i].type != mobjinfo[MT_BOSSARENACENTER].doomednum)
continue;
if (mapthings[i].thing_args[0] != bossindex)
continue;
if (mapthings[i].mobj == NULL)
continue;
return mapthings[i].mobj;
}
CONS_Alert(CONS_ERROR, "No Versus arena of index %d found.", bossindex);
return NULL;
}
fixed_t *VS_PredictAroundArena(mobj_t *arena, mobj_t *movingobject, fixed_t magnitude, angle_t mompoint, fixed_t radiussubtract, boolean forcegoaround, fixed_t radiusdeltafactor)
{
static fixed_t dest[2] = {0, 0};
if (P_MobjWasRemoved(arena))
{
CONS_Alert(CONS_ERROR, "VS_PredictAroundArena: No Versus arena provided.");
return dest;
}
if (P_MobjWasRemoved(movingobject))
{
CONS_Alert(CONS_ERROR, "VS_PredictAroundArena: No moving object provided.");
return dest;
}
fixed_t radius = FixedHypot(movingobject->x - arena->x, movingobject->y - arena->y);
angle_t basedir = R_PointToAngle2(arena->x, arena->y, movingobject->x, movingobject->y);
fixed_t radiusdelta = 0;
const fixed_t minarena = arena->extravalue1 + radiussubtract;
const fixed_t maxarena = arena->extravalue2 - radiussubtract;
mompoint -= basedir;
if (radiusdeltafactor > 0) // for kneecapping the prediction
{
boolean clipped = false;
// Add radius so we can compare against the arena size.
radiusdelta = radius + P_ReturnThrustX(arena, mompoint, magnitude);
if (radiusdelta > maxarena)
{
radiusdelta = maxarena;
clipped = true;
}
else if (radiusdelta < minarena)
{
radiusdelta = minarena;
clipped = true;
}
if (clipped == true && forcegoaround == true)
{
mompoint = (mompoint < ANGLE_180) ? ANGLE_90 : ANGLE_270;
}
// Subtract the radius so it's usable as a delta!
radiusdelta -= radius;
if (radiusdelta && radiusdeltafactor != FRACUNIT)
radiusdelta = FixedDiv(radiusdelta, radiusdeltafactor);
}
radius += (radiusdelta/2); // average...
if (radius > 0)
{
fixed_t sidecomponent = P_ReturnThrustY(arena, mompoint, magnitude);
// percent*(TAU/100)% of circumference = radians*r, so divide by r to reverse that
fixed_t radians = FixedDiv(sidecomponent, radius);
basedir += FixedAngle(FixedDiv(360*radians, M_TAU_FIXED)); // converting to degrees along the way.
}
radius += (radiusdelta/2); // ...and then finalise!
dest[0] = arena->x + P_ReturnThrustX(arena, basedir, radius);
dest[1] = arena->y + P_ReturnThrustY(arena, basedir, radius);
return dest;
}
fixed_t *VS_RandomPointOnArena(mobj_t *arena, fixed_t radiussubtract)
{
static fixed_t dest[2] = {0, 0};
if (P_MobjWasRemoved(arena))
return dest;
const fixed_t minarena = arena->extravalue1 + radiussubtract;
const fixed_t maxarena = arena->extravalue2 - radiussubtract;
angle_t rand1 = FixedAngle(P_RandomKey(PR_MOVINGTARGET, 360)*FRACUNIT);
fixed_t rand2 = P_RandomRange(PR_MOVINGTARGET, minarena/FRACUNIT, maxarena/FRACUNIT)*FRACUNIT;
dest[0] = arena->x + P_ReturnThrustX(arena, rand1, rand2);
dest[1] = arena->y + P_ReturnThrustY(arena, rand1, rand2);
return dest;
}

File diff suppressed because it is too large Load diff

View file

@ -328,6 +328,7 @@ void A_InvincSparkleRotate(mobj_t *actor);
void A_SpawnItemDebrisCloud(mobj_t *actor);
void A_RingShooterFace(mobj_t *actor);
void A_SpawnSneakerPanel(mobj_t *actor);
void A_BlendEyePuyoHack(mobj_t *actor);
//for p_enemy.c
@ -9232,27 +9233,29 @@ void A_ForceStop(mobj_t *actor)
//
// Description: Makes all players win the level.
//
// var1 = unused
// var1:
// if var1 == 1, give a life in GP contexts
// var2 = unused
//
void A_ForceWin(mobj_t *actor)
{
INT32 i;
INT32 locvar1 = var1;
if (LUA_CallAction(A_FORCEWIN, actor))
return;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && ((players[i].mo && players[i].mo->health)
&& !(players[i].pflags & PF_NOCONTEST)))
if (playeringame[i]
&& !(players[i].pflags & PF_NOCONTEST))
break;
}
if (i == MAXPLAYERS)
return;
P_DoAllPlayersExit(0, false);
P_DoAllPlayersExit(0, (locvar1 == 1));
}
// Function: A_SpikeRetract
@ -13754,3 +13757,42 @@ void A_SpawnSneakerPanel(mobj_t *actor)
mo->angle = actor->angle;
Obj_SneakerPanelSpriteScale(mo);
}
// Function: A_BlendEyePuyoHack
//
// Description: Blend Eye Puyo hazards visual/repeat behaviour
//
// var1:
// var2:
// See A_Repeat
//
void A_BlendEyePuyoHack(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
if (LUA_CallAction(A_BLENDEYEPUYOHACK, actor))
{
return;
}
actor->sprite = actor->movedir;
if (locvar1 != 0 && !(actor->state->frame & FF_ANIMATE))
{
// See A_Repeat
if ((!actor->extravalue2 || actor->extravalue2 > locvar1))
actor->extravalue2 = locvar1;
if (--actor->extravalue2 > 0)
P_SetMobjState(actor, locvar2);
}
if (actor->movedir == SPR_PUYC
&& (actor->state-states) == S_BLENDEYE_PUYO_SHOCK
&& P_RandomChance(PR_DECORATION, FRACUNIT/50))
{
// Funny
actor->frame = 7;
}
}

View file

@ -253,8 +253,13 @@ static void P_ItemPop(mobj_t *actor)
Obj_SpawnItemDebrisEffects(actor, actor->target);
if (!specialstageinfo.valid) // In Special, you'll respawn as a Ring Box (random-item.c), don't confuse the player.
if (!specialstageinfo.valid
&& (gametyperules & GTR_SPHERES) != GTR_SPHERES)
{
// Doesn't apply to Special
P_SetMobjState(actor, S_RINGBOX1);
}
actor->extravalue1 = 0;
// de-solidify
@ -281,7 +286,7 @@ static void P_ItemPop(mobj_t *actor)
*/
// Here at mapload in battle?
if (!(gametyperules & GTR_CIRCUIT) && (actor->flags2 & MF2_BOSSNOTRAP))
if (!(gametyperules & GTR_CIRCUIT) && (actor->flags2 & MF2_BOSSFLEE))
{
numgotboxes++;
@ -362,6 +367,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
/////ENEMIES & BOSSES!!/////////////////////////////////
////////////////////////////////////////////////////////
if (special->type == MT_BLENDEYE_MAIN)
{
if (!VS_BlendEye_Touched(special, toucher))
return;
}
P_DamageMobj(toucher, special, special, 1, DMG_NORMAL);
return;
}
@ -381,6 +392,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
P_InstaThrust(player->mo, player->mo->angle, 20<<FRACBITS);
return;
case MT_FLOATINGITEM: // SRB2Kart
// Avoid being picked up immediately
if (special->scale < special->destscale/2)
return;
if (special->threshold >= FIRSTPOWERUP)
{
if (P_PlayerInPain(player))
@ -946,6 +960,13 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return;
}
case MT_BLENDEYE_PUYO:
{
if (!VS_PuyoTouched(special, toucher))
return;
break;
}
default: // SOC or script pickup
P_SetTarget(&special->target, toucher);
break;
@ -1075,7 +1096,7 @@ static void P_AddBrokenPrison(mobj_t *target, mobj_t *inflictor, mobj_t *source)
if (++numtargets >= maptargets)
{
P_DoAllPlayersExit(0, (grandprixinfo.gp == true));
P_DoAllPlayersExit(0, true);
}
else
{
@ -2163,6 +2184,15 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
case MT_BATTLEUFO:
Obj_BattleUFODeath(target);
break;
case MT_BLENDEYE_MAIN:
VS_BlendEye_Death(target);
break;
case MT_BLENDEYE_GLASS:
VS_BlendEye_Glass_Death(target);
break;
case MT_BLENDEYE_PUYO:
VS_PuyoDeath(target);
break;
default:
break;
}
@ -2206,7 +2236,6 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
P_InstaThrust(target, R_PointToAngle2(inflictor->x, inflictor->y, target->x, target->y)+ANGLE_90, 16<<FRACBITS);
}
// Final state setting - do something instead of P_SetMobjState;
// Final state setting - do something instead of P_SetMobjState;
if (target->type == MT_SPIKE && target->info->deathstate != S_NULL)
{
@ -2349,6 +2378,27 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
if (!sprflip)
target->frame |= FF_VERTICALFLIP;
}
else if (target->type == MT_BLENDEYE_GENERATOR && !P_MobjWasRemoved(inflictor))
{
mobj_t *refobj = (inflictor->type == MT_INSTAWHIP) ? source : inflictor;
angle_t impactangle = R_PointToAngle2(target->x, target->y, refobj->x - refobj->momx, refobj->y - refobj->momy) - (target->angle + ANGLE_90);
if (P_MobjWasRemoved(target->tracer) == false)
{
target->tracer->flags2 &= ~MF2_FRET;
target->tracer->flags |= MF_SHOOTABLE;
P_DamageMobj(target->tracer, inflictor, source, 1, DMG_NORMAL);
target->tracer->flags &= ~MF_SHOOTABLE;
}
P_SetMobjState(
target,
((impactangle < ANGLE_180)
? target->info->deathstate
: target->info->xdeathstate
)
);
}
else if (target->player)
{
P_SetPlayerMobjState(target, target->info->deathstate);
@ -3089,12 +3139,54 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
{
return Obj_SpecialUFODamage(target, inflictor, source, damagetype);
}
else if (target->type == MT_BLENDEYE_MAIN)
{
VS_BlendEye_Damage(target, inflictor, source, damage);
}
if (damagetype & DMG_STEAL)
{
// Not a player, steal damage is intended to not do anything
return false;
}
if ((target->flags & MF_BOSS) == MF_BOSS)
{
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:
inflictor->extravalue2 = 1; // Disable whip collision
targetdamaging = UFOD_WHIP;
break;
case MT_PLAYER:
targetdamaging = UFOD_BOOST;
break;
case MT_JAWZ:
case MT_JAWZ_SHIELD:
targetdamaging = UFOD_JAWZ;
break;
case MT_SPB:
targetdamaging = UFOD_SPB;
break;
default:
break;
}
P_TrackRoundConditionTargetDamage(targetdamaging);
}
}
// do the damage

View file

@ -33,6 +33,7 @@
#include "i_system.h" // SRB2kart
#include "k_terrain.h"
#include "k_objects.h"
#include "k_boss.h"
#include "r_splats.h"
@ -658,6 +659,11 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
return BMIT_CONTINUE; // force no collide
}
// Blend-Eye internal noclip
if ((thing->type == MT_BLENDEYE_GLASS || thing->type == MT_BLENDEYE_SHIELD || thing->type == MT_BLENDEYE_EGGBEATER)
&& (tm.thing->type == MT_BLENDEYE_MAIN || tm.thing->type == MT_BLENDEYE_EYE || tm.thing->type == MT_BLENDEYE_PUYO))
return BMIT_CONTINUE;
// When solid spikes move, assume they just popped up and teleport things on top of them to hurt.
if (tm.thing->type == MT_SPIKE && tm.thing->flags & MF_SOLID)
{
@ -689,7 +695,9 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
if (P_DamageMobj(tm.thing, thing, thing, 1, damagetype) && (damagetype = (thing->info->mass>>8)))
S_StartSound(thing, damagetype);
}
return BMIT_CONTINUE;
if (P_MobjWasRemoved(tm.thing) || P_MobjWasRemoved(thing))
return BMIT_CONTINUE;
}
else if (tm.thing->flags & MF_PAIN && thing->player)
{ // Painful thing splats player in the face
@ -705,7 +713,9 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
if (P_DamageMobj(thing, tm.thing, tm.thing, 1, damagetype) && (damagetype = (tm.thing->info->mass>>8)))
S_StartSound(tm.thing, damagetype);
}
return BMIT_CONTINUE;
if (P_MobjWasRemoved(tm.thing) || P_MobjWasRemoved(thing))
return BMIT_CONTINUE;
}
// check for skulls slamming into things
@ -779,6 +789,22 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
return BMIT_CONTINUE;
}
if (thing->type == MT_BLENDEYE_EGGBEATER
&& tm.thing->type == MT_PLAYER && tm.thing->player)
{
if (tm.thing->z > thing->z + thing->height)
{
return BMIT_CONTINUE; // overhead
}
if (tm.thing->z + tm.thing->height < thing->z)
{
return BMIT_CONTINUE; // underneath
}
VS_BlendEye_Eggbeater_Touched(thing, tm.thing);
}
if (thing->type == MT_SPB)
{
if (tm.thing->type != MT_PLAYER

View file

@ -38,6 +38,7 @@
// SRB2kart
#include "k_kart.h"
#include "k_boss.h"
#include "k_battle.h"
#include "k_color.h"
#include "k_follower.h"
@ -2388,16 +2389,14 @@ boolean P_ZMovement(mobj_t *mo)
break;
}
if (!mo->player && P_CheckDeathPitCollide(mo))
if (!mo->player && P_CheckDeathPitCollide(mo) && mo->health)
{
if (mo->flags & MF_ENEMY || mo->flags & MF_BOSS || mo->type == MT_MINECART)
if ((mo->flags & (MF_ENEMY|MF_BOSS)) == MF_ENEMY)
{
// Kill enemies, bosses and minecarts that fall into death pits.
if (mo->health)
{
P_KillMobj(mo, NULL, NULL, DMG_NORMAL);
}
return false;
// Kill enemies that fall into death pits.
P_KillMobj(mo, NULL, NULL, DMG_NORMAL);
if (P_MobjWasRemoved(mo))
return false;
}
}
@ -4662,28 +4661,42 @@ boolean P_BossTargetPlayer(mobj_t *actor, boolean closest)
// Finds the player no matter what they're hiding behind (even lead!)
boolean P_SupermanLook4Players(mobj_t *actor)
{
INT32 c, stop = 0;
player_t *playersinthegame[MAXPLAYERS];
UINT8 c, stop = 0;
UINT8 playersinthegame[MAXPLAYERS];
for (c = 0; c < MAXPLAYERS; c++)
{
if (playeringame[c] && !players[c].spectator)
{
if (!players[c].mo)
continue;
// Playing status
if (!playeringame[c])
continue;
if (players[c].spectator)
continue;
if (players[c].mo->health <= 0)
continue; // dead
// Mobj status
if (!players[c].mo)
continue;
if (players[c].mo->health <= 0)
continue; // dead
playersinthegame[stop] = &players[c];
stop++;
}
// Pain status
if (players[c].respawn.state != RESPAWNST_NONE)
continue; // don't wail on the respawning
playersinthegame[stop] = c;
stop++;
}
if (!stop)
return false;
P_SetTarget(&actor->target, playersinthegame[P_RandomKey(PR_UNDEFINED, stop)]->mo);
P_SetTarget(
&actor->target,
players[
playersinthegame[
P_RandomKey(PR_MOVINGTARGET, stop)
]
].mo
);
return true;
}
@ -5872,8 +5885,21 @@ static void P_MobjSceneryThink(mobj_t *mobj)
P_RemoveMobj(mobj);
return;
}
else
P_AddOverlay(mobj);
if (mobj->fuse)
{
mobj->fuse--;
if (!mobj->fuse)
{
if (!LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse)))
{
P_RemoveMobj(mobj);
return;
}
}
}
P_AddOverlay(mobj);
if (mobj->target->hitlag) // move to the correct position, update to the correct properties, but DON'T STATE-ANIMATE
return;
switch (mobj->target->type)
@ -6783,6 +6809,17 @@ static void P_MobjSceneryThink(mobj_t *mobj)
break;
}
case MT_SPIKEDTARGET:
{
if (P_MobjWasRemoved(mobj->target) || (mobj->target->health <= 0) || (mobj->target->z == mobj->target->floorz))
{
P_RemoveMobj(mobj);
return;
}
mobj->angle += ANG2;
break;
}
case MT_VWREF:
case MT_VWREB:
{
@ -6827,16 +6864,19 @@ static boolean P_MobjBossThink(mobj_t *mobj)
}
else if (P_MobjWasRemoved(mobj))
return false;
else
switch (mobj->type)
{
// No SRB2Kart bosses... yet :)
else switch (mobj->type)
{
case MT_BLENDEYE_MAIN:
VS_BlendEye_Thinker(mobj);
break;
default: // Generic SOC-made boss
if (mobj->flags2 & MF2_SKULLFLY)
P_SpawnGhostMobj(mobj);
P_GenericBossThinker(mobj);
break;
}
}
if (P_MobjWasRemoved(mobj))
return false;
if (mobj->flags2 & MF2_BOSSFLEE)
{
if (mobj->extravalue1)
@ -7031,6 +7071,11 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
}
break;
}
case MT_BLENDEYE_GENERATOR:
{
VS_BlendEye_Generator_DeadThinker(mobj);
break;
}
default:
break;
}
@ -9952,6 +9997,24 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
break;
}
case MT_BLENDEYE_EYE:
{
if (!VS_BlendEye_Eye_Thinker(mobj))
{
return false;
}
break;
}
case MT_BLENDEYE_PUYO:
{
VS_PuyoThinker(mobj);
if (P_MobjWasRemoved(mobj))
{
return false;
}
break;
}
default:
// check mobj against possible water content, before movement code
P_MobjCheckWater(mobj);
@ -10069,6 +10132,7 @@ static boolean P_CanFlickerFuse(mobj_t *mobj)
case MT_POGOSPRING:
case MT_KART_LEFTOVER:
case MT_EMERALD:
case MT_BLENDEYE_PUYO:
if (mobj->fuse <= TICRATE)
{
return true;
@ -10538,13 +10602,6 @@ void P_MobjThinker(mobj_t *mobj)
P_SquishThink(mobj);
K_UpdateTerrainOverlay(mobj);
if (mobj->flags & (MF_ENEMY|MF_BOSS) && mobj->health
&& P_CheckDeathPitCollide(mobj)) // extra pit check in case these didn't have momz
{
P_KillMobj(mobj, NULL, NULL, DMG_DEATHPIT);
return;
}
// Crush enemies!
if (mobj->ceilingz - mobj->floorz < mobj->height)
{
@ -11441,6 +11498,24 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
case MT_BALLSWITCH_BALL:
Obj_BallSwitchInit(mobj);
break;
case MT_BLENDEYE_MAIN:
VS_BlendEye_Init(mobj);
break;
case MT_BLENDEYE_PUYO:
mobj->sprite = mobj->movedir = P_RandomRange(PR_DECORATION, SPR_PUYA, SPR_PUYE);
if (encoremode == false)
{
mobj->color = SKINCOLOR_LEATHER;
mobj->colorized = true;
}
break;
case MT_BLENDEYE_PUYO_DUST_COFFEE:
mobj->color = SKINCOLOR_LEATHER;
mobj->colorized = true;
// FALLTHRU
case MT_BLENDEYE_PUYO_DUST:
mobj->sprite = mobj->movedir = P_RandomRange(PR_DECORATION, SPR_PUYA, SPR_PUYE);
break;
default:
break;
}
@ -12153,6 +12228,8 @@ void P_RespawnBattleBoxes(void)
if (gametyperules & GTR_CIRCUIT)
return;
tic_t setduration = (nummapboxes > 1) ? TICRATE : (2*TICRATE);
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
mobj_t *box;
@ -12163,13 +12240,20 @@ void P_RespawnBattleBoxes(void)
box = (mobj_t *)th;
if (box->type != MT_RANDOMITEM
|| (box->flags2 & MF2_DONTRESPAWN)
|| ((box->flags2 & (MF2_DONTRESPAWN|MF2_BOSSFLEE)) != MF2_BOSSFLEE)
|| !(box->flags & MF_NOCLIPTHING)
|| box->fuse)
continue; // only popped items
box->fuse = TICRATE; // flicker back in
P_SetMobjState(box, box->info->raisestate);
box->fuse = setduration; // flicker back in
P_SetMobjState(
box,
(((gametyperules & GTR_SPHERES) == GTR_SPHERES)
? box->info->raisestate
: box->info->spawnstate
)
);
box->renderflags |= RF_DONTDRAW; // guarantee start invisible
if (numgotboxes > 0)
numgotboxes--; // you've restored a box, remove it from the count
@ -13410,6 +13494,15 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj)
P_InitSkyboxPoint(mobj, mthing);
break;
}
case MT_BOSSARENACENTER:
{
if (!VS_ArenaCenterInit(mobj, mthing))
{
P_RemoveMobj(mobj);
return false;
}
break;
}
case MT_EGGSTATUE:
if (mthing->thing_args[1])
{
@ -13772,18 +13865,6 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj)
}
case MT_RANDOMITEM:
{
const boolean delayed = !(gametyperules & GTR_CIRCUIT);
if (leveltime == 0)
{
mobj->flags2 |= MF2_BOSSNOTRAP; // mark as here on map start
if (delayed)
{
P_UnsetThingPosition(mobj);
mobj->flags |= (MF_NOCLIPTHING|MF_NOBLOCKMAP);
mobj->renderflags |= RF_DONTDRAW;
P_SetThingPosition(mobj);
}
}
if (mthing->thing_args[0] == 1)
mobj->flags2 |= MF2_BOSSDEAD;
break;

View file

@ -6367,7 +6367,7 @@ static void P_NetArchiveRNG(savebuffer_t *save)
WRITEUINT32(save->p, ARCHIVEBLOCK_RNG);
for (i = 0; i < PRNUMCLASS; i++)
for (i = 0; i < PRNUMSYNCED; i++)
{
WRITEUINT32(save->p, P_GetInitSeed(i));
WRITEUINT32(save->p, P_GetRandSeed(i));
@ -6381,7 +6381,7 @@ static inline void P_NetUnArchiveRNG(savebuffer_t *save)
if (READUINT32(save->p) != ARCHIVEBLOCK_RNG)
I_Error("Bad $$$.sav at archive block RNG");
for (i = 0; i < PRNUMCLASS; i++)
for (i = 0; i < PRNUMSYNCED; i++)
{
UINT32 init = READUINT32(save->p);
UINT32 seed = READUINT32(save->p);

View file

@ -7953,9 +7953,14 @@ static void P_InitGametype(void)
G_RecordDemo(buf);
}
// Started a game? Move on to the next jam when you go back to the title screen
CV_SetValue(&cv_menujam_update, 1);
gamedata->musicstate = GDMUSIC_NONE;
if (gamestate != GS_TITLESCREEN)
{
// Started a game? Move on to the next jam when you go back to the title screen
// this permits all but titlescreen, instead of only GS_LEVEL
// because that one's way too easy to activate again and again
CV_SetValue(&cv_menujam_update, 1);
gamedata->musicstate = GDMUSIC_NONE;
}
}
struct minimapinfo minimapinfo;

View file

@ -1296,7 +1296,7 @@ void P_DoPlayerExit(player_t *player, pflags_t flags)
player->hudrings = 20;
if (grandprixinfo.gp == true
&& grandprixinfo.eventmode == GPEVENT_NONE
&& grandprixinfo.eventmode != GPEVENT_SPECIAL
&& player->bot == false && losing == false)
{
const UINT8 lifethreshold = 20;
@ -1364,6 +1364,13 @@ void P_DoAllPlayersExit(pflags_t flags, boolean trygivelife)
UINT8 i;
const boolean dofinishsound = (musiccountdown == 0);
if (grandprixinfo.gp == false
|| grandprixinfo.eventmode == GPEVENT_SPECIAL
|| (flags & PF_NOCONTEST))
{
trygivelife = false;
}
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
@ -1382,7 +1389,7 @@ void P_DoAllPlayersExit(pflags_t flags, boolean trygivelife)
continue;
}
P_GivePlayerLives(&players[i], 1);
players[i].xtralife++;
}
if (!dofinishsound)

View file

@ -839,7 +839,7 @@ sfxinfo_t S_sfx[NUMSFX] =
// Mean Bean Machine sounds
{"mbs41", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbs42", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbs42", true, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbs43", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbs44", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbs45", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
@ -849,7 +849,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"mbs49", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbs4a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbs4b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbs4c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbs4c", true, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbs4d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbs4e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbs4f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
@ -1240,6 +1240,9 @@ sfxinfo_t S_sfx[NUMSFX] =
{"ridr3", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Rideroid Loop
{"ridr4", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Leaving Rideroid
{"befan1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Whisking"}, // Blend Eye whisk startup
{"befan2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Whisking"}, // Blend Eye whisk
// Damage sounds
{"dmga1", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Damaged"},
{"dmga2", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Damaged"},

View file

@ -1302,13 +1302,17 @@ typedef enum
sfx_rainbr,
sfx_rank,
// rideroid
sfx_ridr1,
sfx_ridr2,
sfx_ridr3,
sfx_ridr4,
// Blend Eye
sfx_befan1,
sfx_befan2,
// Damage sounds
sfx_dmga1,
sfx_dmga2,

View file

@ -567,8 +567,8 @@ static patch_t *tcroundbonus;
static patch_t *tcactnum[10];
static patch_t *tcact;
static patch_t *twarn;
static patch_t *twarn2;
static patch_t *twarn[2];
static patch_t *ttext[2];
// some coordinates define to make my life easier....
#define FINAL_ROUNDX (24)
@ -625,8 +625,11 @@ static void ST_cacheLevelTitle(void)
tcact = (patch_t *)W_CachePatchName("TT_ACT", PU_HUDGFX);
twarn = (patch_t *)W_CachePatchName("K_BOSW01", PU_HUDGFX);
twarn2 = (patch_t *)W_CachePatchName("K_BOSW02", PU_HUDGFX);
twarn[0] = (patch_t *)W_CachePatchName("K_BOSW01", PU_HUDGFX);
twarn[1] = (patch_t *)W_CachePatchName("K_BOSW02", PU_HUDGFX);
ttext[0] = (patch_t *)W_CachePatchName("K_BOST01", PU_HUDGFX);
ttext[1] = (patch_t *)W_CachePatchName("K_BOST02", PU_HUDGFX);
// Cache round #
for (i=1; i < 11; i++)
@ -634,7 +637,7 @@ static void ST_cacheLevelTitle(void)
sprintf(buf, "TT_RND%d", i);
tcroundnum[i-1] = (patch_t *)W_CachePatchName(buf, PU_HUDGFX);
}
tcroundbonus = (patch_t *)W_CachePatchName("TT_RNDB", PU_HUDGFX);
tcroundbonus = (patch_t *)W_CachePatchName("TT_RNDX", PU_HUDGFX);
// Cache act #
for (i=0; i < 10; i++)
@ -747,7 +750,20 @@ void ST_runTitleCard(void)
}
}
// No matter the circumstances, scroll the WARN...
bannerx = -((lt_ticker*2)%((encoremode ? twarn2 : twarn)->width));
{
patch_t *localwarn = twarn[encoremode ? 1 : 0];
patch_t *localtext = ttext[encoremode ? 1 : 0];
if (localwarn->width)
{
bannerx = ((lt_ticker*2)%(localwarn->width));
}
if (localtext->width)
{
bannery = -((lt_ticker*4)%(localtext->width));
}
}
if (run && lt_ticker < PRELEVELTIME)
{
@ -935,27 +951,40 @@ void ST_drawTitleCard(void)
{
#define LOTIME 5
#define HITIME 15
patch_t *localwarn = (encoremode ? twarn2 : twarn);
INT32 transp = (lt_ticker+HITIME) % (LOTIME+HITIME);
patch_t *localwarn;
INT32 transp;
boolean encorehack = ((levelfadecol == 0) && lt_ticker <= PRELEVELTIME+4);
if ((localwarn->width > 0) && (lt_ticker + (HITIME-transp) <= lt_endtime))
{
if (transp > HITIME-1)
{
transp = HITIME-1;
}
transp = (((10*transp)/HITIME)<<V_ALPHASHIFT) | (encorehack ? V_SUBTRACT : V_ADD);
while (bx > -pad)
bx -= localwarn->width;
while (bx < BASEVIDWIDTH+pad)
{
V_DrawFixedPatch(bx*FRACUNIT, 55*FRACUNIT, FRACUNIT, V_SNAPTOLEFT|transp, localwarn, NULL);
bx += localwarn->width;
}
#define DRAWBOSSWARN(pat) \
localwarn = pat[encoremode ? 1 : 0];\
\
if ((localwarn->width > 0) && (lt_ticker + (HITIME-transp) <= lt_endtime)) \
{ \
if (transp > HITIME-1)\
{ \
transp = HITIME-1; \
} \
\
transp = (((10*transp)/HITIME)<<V_ALPHASHIFT) | (encorehack ? V_SUBTRACT : V_ADD); \
\
while (bx > -pad) \
bx -= localwarn->width; \
while (bx < BASEVIDWIDTH+pad) \
{ \
V_DrawFixedPatch(bx*FRACUNIT, 60*FRACUNIT, FRACUNIT, V_SNAPTOLEFT|transp, localwarn, NULL); \
bx += localwarn->width; \
} \
}
transp = (lt_ticker+HITIME) % (LOTIME+HITIME);
DRAWBOSSWARN(twarn);
transp = (lt_ticker+HITIME+3) % (LOTIME+HITIME);
bx = bannery;
DRAWBOSSWARN(ttext);
#undef DRAWBOSSWARN
#undef LOTIME
#undef HITIME
}
@ -966,7 +995,7 @@ void ST_drawTitleCard(void)
bx = V_TitleCardStringWidth(bossinfo.enemyname, false);
// Name.
V_DrawTitleCardString((BASEVIDWIDTH - bx)/2, 75, bossinfo.enemyname, 0, true, bossinfo.titleshow, lt_exitticker, false);
V_DrawTitleCardString((BASEVIDWIDTH - bx)/2, 80, bossinfo.enemyname, 0, true, bossinfo.titleshow, lt_exitticker, false);
// Under-bar.
{
@ -994,7 +1023,7 @@ void ST_drawTitleCard(void)
// Handle subtitle.
else if (bossinfo.subtitle && lt_ticker >= TICRATE/2)
{
INT32 by = 75+32;
INT32 by = 80+32;
if (lt_ticker == TICRATE/2 || lt_exitticker == 1)
{
;
@ -1008,15 +1037,15 @@ void ST_drawTitleCard(void)
by += 5;
}
V_DrawRightAlignedThinString((BASEVIDWIDTH+bx)/2, by, 0, bossinfo.subtitle);
V_DrawRightAlignedThinString((BASEVIDWIDTH+bx)/2, by, V_FORCEUPPERCASE, bossinfo.subtitle);
}
// Now draw the under-bar itself.
if (scalex > 0)
{
bx = FixedMul(bx, scalex);
V_DrawFill((BASEVIDWIDTH-(bx+2))/2, 75+32, bx+2, 3, 31);
V_DrawFill((BASEVIDWIDTH-(bx))/2, 75+32+1, bx, 1, 0);
V_DrawFill((BASEVIDWIDTH-(bx+2))/2, 80+32, bx+2, 3, 31);
V_DrawFill((BASEVIDWIDTH-(bx))/2, 80+32+1, bx, 1, 0);
}
}
}
@ -1060,17 +1089,10 @@ void ST_drawTitleCard(void)
; // TODO: Ruby
else if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)
{
switch (grandprixinfo.eventmode)
{
case GPEVENT_BONUS:
roundico = tcroundbonus; // TODO don't show capsule if we have other bonus types
break;
/*case GPEVENT_SPECIAL:
; // TODO: Emerald/mount
break;*/
default:
break;
}
if (gametypes[gametype]->gppic[0])
roundico = W_CachePatchName(gametypes[gametype]->gppic, PU_PATCH);
else
roundico = tcroundbonus; // Generic star
}
else if (roundqueue.size > 0)
{

View file

@ -381,13 +381,16 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
skins[players[i].skin].realname);
}
if (roundqueue.size > 0
&& roundqueue.roundnum > 0
&& (grandprixinfo.gp == false
|| grandprixinfo.eventmode == GPEVENT_NONE)
)
if (roundqueue.size > 0 && roundqueue.roundnum > 0)
{
data.roundnum = roundqueue.roundnum;
if ((grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE))
{
data.roundnum = INTERMISSIONROUND_BONUS;
}
else
{
data.roundnum = roundqueue.roundnum;
}
}
}
else
@ -411,7 +414,7 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
data.roundnum = roundqueue.roundnum;
}
else if (bossinfo.valid == true && bossinfo.enemyname)
else if (K_CheckBossIntro() == true && bossinfo.enemyname)
{
snprintf(data.headerstring,
sizeof data.headerstring,
@ -833,12 +836,16 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
// Progress markers
patch_t *level_dot[BPP_MAIN];
patch_t *bonus_dot[BPP_MAIN];
patch_t *capsu_dot[BPP_MAIN];
patch_t *prize_dot[BPP_MAIN];
level_dot[BPP_AHEAD] = W_CachePatchName("R_RRMRK2", PU_PATCH);
level_dot[BPP_DONE] = W_CachePatchName("R_RRMRK1", PU_PATCH);
bonus_dot[BPP_AHEAD] = W_CachePatchName("R_RRMRK7", PU_PATCH);
bonus_dot[BPP_DONE] = W_CachePatchName("R_RRMRK8", PU_PATCH);
capsu_dot[BPP_AHEAD] = W_CachePatchName("R_RRMRK3", PU_PATCH);
capsu_dot[BPP_DONE] = W_CachePatchName("R_RRMRK5", PU_PATCH);
@ -1296,7 +1303,14 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
&& roundqueue.entries[i].gametype != roundqueue.entries[0].gametype
)
{
chose_dot = capsu_dot;
if ((gametypes[roundqueue.entries[i].gametype]->rules & GTR_PRISONS) == GTR_PRISONS)
{
chose_dot = capsu_dot;
}
else
{
chose_dot = bonus_dot;
}
}
else
{
@ -1459,14 +1473,29 @@ void Y_DrawIntermissionHeader(fixed_t x, fixed_t y, boolean gotthrough, const ch
}
// Draw round numbers
if (roundnum > 0 && roundnum <= 10)
patch_t *roundpatch = NULL;
if (roundnum == INTERMISSIONROUND_BONUS)
{
patch_t *roundpatch =
W_CachePatchName(
va("TT_RN%s%d", (small ? "S" : "D"), roundnum),
const char *gppic = (small ? gametypes[gametype]->gppicmini : gametypes[gametype]->gppic);
if (gppic[0])
roundpatch = W_CachePatchName(gppic, PU_PATCH);
else
roundpatch = W_CachePatchName(
va("TT_RN%cX", (small ? 'S' : 'D')),
PU_PATCH
);
}
else if (roundnum > 0 && roundnum <= 10)
{
roundpatch = W_CachePatchName(
va("TT_RN%c%d", (small ? 'S' : 'D'), roundnum),
PU_PATCH
);
}
if (roundpatch)
{
fixed_t roundx = (v_width * 3 * FRACUNIT) / 4;
if (headerwidth != 0)

View file

@ -16,6 +16,8 @@
extern "C" {
#endif
#define INTERMISSIONROUND_BONUS UINT8_MAX
typedef struct
{
boolean rankingsmode; // rankings mode