Merge branch 'snapshot-late' into 'master'

Take `snapshotmap` on leveltime 5

Closes #1619

See merge request kart-krew-dev/ring-racers-internal!2730
This commit is contained in:
Oni VelocitOni 2025-08-19 19:39:10 +00:00
commit 46094fa813
7 changed files with 169 additions and 97 deletions

View file

@ -6743,6 +6743,36 @@ boolean TryRunTics(tic_t realtics)
P_PostLoadLevel();
}
// Complete dipshit location for this code, but we do sort of need to
// delay snapshotmaps to cover for elements that are set up on tic 0
if (roundqueue.snapshotmaps == true && leveltime == 5)
{
if (roundqueue.size > 0)
{
D_TakeMapSnapshots();
G_GetNextMap();
// roundqueue is wiped after the last round, but
// preserve this to track state into the Podium!
roundqueue.snapshotmaps = true;
G_NextLevel();
}
else
{
// Podium: snapshotmaps is finished. Yay!
HU_DoTitlecardCEcho(NULL, va("Congratulations,\\%s!\\Check the console!", cv_playername[0].string), true);
livestudioaudience_timer = 0;
LiveStudioAudience();
CONS_Printf("\n\n\x83""snapshotmaps: Find your images in %s\n", srb2home);
roundqueue.snapshotmaps = false;
}
}
boolean run = (gametic % NEWTICRATERATIO) == 0;
if (run && tickInterp)

View file

@ -448,7 +448,9 @@ void D_RegisterServerCommands(void)
COM_AddDebugCommand("eval", Command_Eval);
COM_AddCommand("writetextmap", Command_WriteTextmap);
COM_AddCommand("snapshotmaps", Command_SnapshotMaps);
COM_AddCommand("snapshotmap", Command_SnapshotMaps);
COM_AddCommand("snapshotmaps", Command_SnapshotMaps); // alias
// for master server connection
AddMServCommands();
@ -6694,8 +6696,8 @@ static void Command_SnapshotMaps(void)
if (COM_Argc() < 2)
{
CONS_Printf(
"snapshotmaps <map> [map2...]: Create a thumbnail screenshot for the specified levels.\n"
"- Use the full map name, e.g. RR_TestRun.\n"
"snapshotmap <map> [map2...]: Create a thumbnail screenshot for the specified levels.\n"
"- You can do partial names, but no spaces (without \"quotes around them\").\n"
"- You can give this command UP TO %d map names and it will create images for all of them.\n"
"- This command generates two images -- one 320x200 for the PICTURE lump, another 1024x1024 for rich presence.\n"
"- The map requires a \"snapshot camera\" object to have been placed.\n"
@ -6735,23 +6737,31 @@ static void Command_SnapshotMaps(void)
roundqueue.snapshotmaps = true;
size_t i;
INT32 map;
const char *mapname;
char *realmapname;
for (i = 1; i < COM_Argc(); ++i)
{
INT32 map = G_MapNumber(COM_Argv(i));
mapname = COM_Argv(i);
map = G_FindMapByNameOrCode(COM_Argv(i), &realmapname); // G_MapNumber(COM_Argv(i));
if (map < 0 || map >= nummapheaders)
if (map == 0)
{
CONS_Alert(CONS_WARNING, "%s: Map doesn't exist. Not doing anything.\n", COM_Argv(i));
CONS_Alert(CONS_ERROR, M_GetText("Could not find any map described as '%s'.\nNot snapshotting any maps."), mapname);
// clear round queue (to be safe)
memset(&roundqueue, 0, sizeof(struct roundqueue));
return;
}
map--;
INT32 gt = G_GuessGametypeByTOL(mapheaderinfo[map]->typeoflevel);
G_MapIntoRoundQueue(map, gt != -1 ? gt : GT_RACE, false, false);
Z_Free(realmapname);
}
D_MapChange(1 + roundqueue.entries[0].mapnum, roundqueue.entries[0].gametype, false, true, 1, false, false);

View file

@ -53,6 +53,73 @@ typedef enum
/// - MAIN BODY - ///
static void VS_BlendEye_Eye_Parts(mobj_t *mobj, INT32 angledelta)
{
fixed_t x, y;
// Before angle update, move the shield.
if (mobj->tracer && mobj->tracer->hnext && mobj->movedir < BLENDEYE_EXPLODING)
{
mobj_t *ref = mobj->tracer->hnext;
while (ref)
{
x = mobj->x + P_ReturnThrustX(mobj, mobj->tracer->movedir + ref->movedir, 40*mobj->scale);
y = mobj->y + P_ReturnThrustY(mobj, mobj->tracer->movedir + ref->movedir, 40*mobj->scale);
P_MoveOrigin(ref, x, y, mobj->z);
ref->angle = mobj->tracer->movedir + ref->movedir - ANGLE_90;
ref = ref->hnext;
}
mobj->tracer->movedir = mobj->angle;
}
if (mobj->movedir != BLENDEYE_PINCH_DRILLING || mobj->movecount > TICRATE/2)
mobj->angle += angledelta;
// And after, move the eye.
if (mobj->tracer)
{
x = mobj->x + P_ReturnThrustX(mobj, mobj->angle, 38*mobj->scale);
y = mobj->y + P_ReturnThrustY(mobj, mobj->angle, 38*mobj->scale);
P_MoveOrigin(mobj->tracer, x, y, mobj->z + (mobj->height - mobj->tracer->height)/2);
mobj->tracer->angle = mobj->angle - ANGLE_90;
if (mobj->tracer->hprev)
{
P_MoveOrigin(mobj->tracer->hprev, mobj->x, mobj->y, max(mobj->floorz, mobj->z - (mobj->tracer->hprev->height)));
}
}
}
static mobj_t *VS_BlendEye_LoadAmmo(mobj_t *mobj, INT32 id)
{
angle_t ang = mobj->tracer->cusval + FixedAngle(id*360*FRACUNIT/3);
fixed_t h = mobj->z + mobj->height - 4*mapobjectscale;
fixed_t dist = mobj->cvmem - 2*FRACUNIT;
if (id <= 3)
ang += ANGLE_180;
mobj_t *ammo = P_SpawnMobjFromMobj(mobj,
P_ReturnThrustX(mobj, ang, dist),
P_ReturnThrustY(mobj, ang, dist),
256*FRACUNIT,
MT_BLENDEYE_PUYO);
P_SetScale(ammo, (ammo->destscale *= 2));
if (id <= 3)
h += ammo->height;
else
ammo->cusval = (id-3)*TICRATE;
P_SetObjectMomZ(ammo, -10*FRACUNIT, false);
ammo->angle = ang;
ammo->cvmem = dist;
ammo->movefactor = (TICRATE/2) + P_RandomKey(PR_MOVINGTARGET, TICRATE/8);
P_SetTarget(&ammo->tracer, mobj);
ammo->extravalue1 = h;
ammo->flags2 |= MF2_STRONGBOX;
return ammo;
}
void VS_BlendEye_Init(mobj_t *mobj)
{
UINT8 i;
@ -137,6 +204,10 @@ void VS_BlendEye_Init(mobj_t *mobj)
mobj->reactiontime = mobj->z + (3*mobj->height)/2;
mobj->cvmem = 24*FRACUNIT;
VS_BlendEye_Eye_Parts(mobj, 0);
{
const char *enemyname, *subtitle;
if (!encoremode)
@ -153,6 +224,23 @@ void VS_BlendEye_Init(mobj_t *mobj)
K_InitBossHealthBar(enemyname, subtitle, 0, mobj->tracer->health*(FRACUNIT/mobj->health), mobj->tracer->cvmem);
K_DeclareWeakspot(mobj, SPOT_NONE, EYECOLOR, true);
}
if (roundqueue.snapshotmaps)
{
mobj->tracer->cusval = ANGLE_45/2; // chosen by dice roll guaranteed to be random
// Test load
for (i = 6; i > 0; i--)
{
mobj_t *ammo = VS_BlendEye_LoadAmmo(mobj, i);
ammo->momz = 0;
ammo->z = ammo->extravalue1;
ammo->flags |= MF_NOGRAVITY;
P_SetMobjState(ammo, S_BLENDEYE_PUYO);
if (i != 6) // one random
ammo->sprite = mobj->movedir = SPR_PUYA + i - 1;
}
}
}
static mobj_t *sourceofmurder;
@ -240,32 +328,7 @@ void VS_BlendEye_Thinker(mobj_t *mobj)
// SPAWN YOUR AMMO
if ((mobj->movecount % 5) == 0)
{
i = (mobj->movecount/5);
angle_t ang = mobj->tracer->cusval + FixedAngle(i*360*FRACUNIT/3);
fixed_t h = mobj->z + mobj->height - 4*mapobjectscale;
fixed_t dist = mobj->cvmem - 2*FRACUNIT;
if (i <= 3)
ang += ANGLE_180;
mobj_t *ammo = P_SpawnMobjFromMobj(mobj,
P_ReturnThrustX(mobj, ang, dist),
P_ReturnThrustY(mobj, ang, dist),
256*FRACUNIT,
MT_BLENDEYE_PUYO);
P_SetScale(ammo, (ammo->destscale *= 2));
if (i <= 3)
h += ammo->height;
else
ammo->cusval = (i-3)*TICRATE;
P_SetObjectMomZ(ammo, -10*FRACUNIT, false);
ammo->angle = ang;
ammo->cvmem = dist;
ammo->movefactor = (TICRATE/2) + P_RandomKey(PR_MOVINGTARGET, TICRATE/8);
P_SetTarget(&ammo->tracer, mobj);
ammo->extravalue1 = h;
ammo->flags2 |= MF2_STRONGBOX;
VS_BlendEye_LoadAmmo(mobj, (mobj->movecount/5));
}
if ((--mobj->movecount) == 0)
@ -327,6 +390,7 @@ void VS_BlendEye_Thinker(mobj_t *mobj)
{
// PINCH TRANSITION BEGIN !
mobj->rollangle = 0;
mobj->shadowscale = FRACUNIT;
mobj->movedir = BLENDEYE_PINCH_THROWN;
mobj_t *ref = mobj->hnext;
mobj_t *refnext = NULL;
@ -880,7 +944,6 @@ void VS_BlendEye_Thinker(mobj_t *mobj)
mobj->movedir = BLENDEYE_LOADAMMO;
mobj->movecount = 6*5;
mobj->tracer->cusval = FixedAngle(P_RandomKey(PR_MOVINGTARGET, 360)*FRACUNIT);
mobj->cvmem = 24*FRACUNIT;
}
else if (mobj->movecount == TICRATE)
{
@ -900,7 +963,6 @@ void VS_BlendEye_Thinker(mobj_t *mobj)
// Look around.
if (mobj->target) // !deathwatch
{
fixed_t x, y;
INT32 angledelta = AngleDeltaSigned(R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y), mobj->angle)/4;
const INT32 maxdelta = (ANGLE_45/2);
@ -909,36 +971,7 @@ void VS_BlendEye_Thinker(mobj_t *mobj)
else if (angledelta < -maxdelta)
angledelta = -maxdelta;
// Before angle update, move the shield.
if (mobj->tracer && mobj->tracer->hnext && mobj->movedir < BLENDEYE_EXPLODING)
{
mobj_t *ref = mobj->tracer->hnext;
while (ref)
{
x = mobj->x + P_ReturnThrustX(mobj, mobj->tracer->movedir + ref->movedir, 40*mobj->scale);
y = mobj->y + P_ReturnThrustY(mobj, mobj->tracer->movedir + ref->movedir, 40*mobj->scale);
P_MoveOrigin(ref, x, y, mobj->z);
ref->angle = mobj->tracer->movedir + ref->movedir - ANGLE_90;
ref = ref->hnext;
}
mobj->tracer->movedir = mobj->angle;
}
if (mobj->movedir != BLENDEYE_PINCH_DRILLING || mobj->movecount > TICRATE/2)
mobj->angle += angledelta;
// And after, move the eye.
if (mobj->tracer)
{
x = mobj->x + P_ReturnThrustX(mobj, mobj->angle, 38*mobj->scale);
y = mobj->y + P_ReturnThrustY(mobj, mobj->angle, 38*mobj->scale);
P_MoveOrigin(mobj->tracer, x, y, mobj->z + (mobj->height - mobj->tracer->height)/2);
mobj->tracer->angle = mobj->angle - ANGLE_90;
if (mobj->tracer->hprev)
{
P_MoveOrigin(mobj->tracer->hprev, mobj->x, mobj->y, max(mobj->floorz, mobj->z - (mobj->tracer->hprev->height)));
}
}
VS_BlendEye_Eye_Parts(mobj, angledelta);
}
}

View file

@ -11264,6 +11264,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing)
thing->shadowscale = FRACUNIT/3;
break;
case MT_SNEAKERPANEL:
case MT_BLENDEYE_MAIN:
thing->shadowscale = 0;
break;
case MT_KURAGEN:

View file

@ -9093,35 +9093,6 @@ void P_PostLoadLevel(void)
// We're now done loading the level.
levelloading = false;
if (roundqueue.snapshotmaps == true)
{
if (roundqueue.size > 0)
{
D_TakeMapSnapshots();
G_GetNextMap();
// roundqueue is wiped after the last round, but
// preserve this to track state into the Podium!
roundqueue.snapshotmaps = true;
G_NextLevel();
return;
}
else
{
// Podium: snapshotmaps is finished. Yay!
HU_DoTitlecardCEcho(NULL, va("Congratulations,\\%s!\\Check the console!", cv_playername[0].string), true);
livestudioaudience_timer = 0;
LiveStudioAudience();
CONS_Printf("\n\n\x83""snapshotmaps: Find your images in %s\n", srb2home);
roundqueue.snapshotmaps = false;
}
}
TracyCZoneEnd(__zone);
}

View file

@ -255,7 +255,13 @@ void Command_CountMobjs_f(void)
count++;
}
CONS_Printf(M_GetText("There are %d objects of type %d currently in the level.\n"), count, i);
const char *name;
if (i >= MT_FIRSTFREESLOT)
name = FREE_MOBJS[i-MT_FIRSTFREESLOT];
else
name = MOBJTYPE_LIST[i];
CONS_Printf(M_GetText("There are %d objects of type %d (%s) currently in the level.\n"), count, i, name);
}
return;
}
@ -276,7 +282,14 @@ void Command_CountMobjs_f(void)
}
if (count > 0) // Don't bother displaying if there are none of this type!
CONS_Printf(" * %d: %d\n", i, count);
{
const char *name;
if (i >= MT_FIRSTFREESLOT)
name = FREE_MOBJS[i-MT_FIRSTFREESLOT];
else
name = MOBJTYPE_LIST[i];
CONS_Printf(" * %d (%s): %d\n", i, name, count);
}
}
}

View file

@ -3785,21 +3785,35 @@ boolean R_ThingVisible (mobj_t *thing)
{
switch (thing->type)
{
// Players
case MT_PLAYER:
case MT_FOLLOWER:
// Individual pickups
case MT_RING:
case MT_FLINGRING:
case MT_BLUESPHERE:
case MT_SPRAYCAN:
// Item Boxes and Capsules
case MT_EXPLODE:
case MT_RANDOMITEM:
case MT_SPHEREBOX:
case MT_ITEMCAPSULE:
case MT_ITEMCAPSULE_PART:
case MT_OVERLAY: // mostly capsule numbers :)))
// Prison Eggs
case MT_BATTLECAPSULE:
case MT_BATTLECAPSULE_PIECE:
case MT_SPRAYCAN:
case MT_PLAYER:
// Duel hazards
case MT_DUELBOMB:
case MT_LANDMINE:
case MT_SSMINE:
case MT_SSMINE_SHIELD:
case MT_MINERADIUS:
case MT_POGOSPRING:
case MT_DROPTARGET:
case MT_HYUDORO:
case MT_SHADOW: // hyuu fake shadow
// Checkpoints
case MT_CHECKPOINT_END:
case MT_SIGNSPARKLE:
case MT_THOK: // checkpoint parts