Merge pull request 'Map Patches' (#52) from mappatch into blankart-dev

Reviewed-on: https://codeberg.org/NepDisk/blankart/pulls/52
This commit is contained in:
NepDisk 2025-05-08 21:51:03 +00:00
commit 0699647d60
16 changed files with 331 additions and 85 deletions

View file

@ -43,7 +43,7 @@ engine = "zdoom";
linetagindicatesectors = true;
// The format interface handles the map data format - DoomMapSetIO for SRB2DB2, SRB2MapSetIO for Zone Builder
formatinterface = "SRB2MapSetIO";
formatinterface = "DoomMapSetIO";
//Sky textures for vanilla maps
defaultskytextures

View file

@ -82,8 +82,14 @@ udmf
prefix = "(2001)";
arg0
{
title = "Flip?";
type = 3;
title = "Flags";
type = 12;
enum
{
1 = "Flip";
2 = "Single-use";
4 = "Require sector special";
}
}
}

View file

@ -5388,6 +5388,17 @@ udmf
5 = "End drift";
}
}
arg4
{
title = "Patch finish flags";
type = 12;
enum
{
1 = "Flip";
2 = "Single-use";
4 = "Require sector special";
}
}
}
2004

View file

@ -441,7 +441,7 @@ UINT32 quickncasehash (const char *p, size_t n)
#include "xxhash.h"
#define HASH64(p, n) ((UINT64)XXH3_64bits(p, n))
#define HASH32(p, n) ((UINT32)(XXH3_64bits(p, n) & UINT32_MAX))
#define HASHFMT "%jx"
#define HASHFMT "%016jx"
#ifndef __cplusplus
#ifndef min // Double-Check with WATTCP-32's cdefs.h

View file

@ -4790,21 +4790,15 @@ static void K_drawCheckpointDebugger(void)
if (stplyr != &players[displayplayers[0]]) // only for p1
return;
if (K_UsingLegacyCheckpoints())
{
if (stplyr->starpostnum >= (numstarposts - (numstarposts/2)))
V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Can finish)", stplyr->starpostnum, numstarposts));
else
V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Skip: %d)", stplyr->starpostnum, numstarposts, ((numstarposts/2) + stplyr->starpostnum)));
V_DrawString(8, 192, 0, va("Waypoint dist: Prev %d, Next %d", stplyr->prevcheck, stplyr->nextcheck));
}
if (stplyr->starpostnum >= K_CheckpointThreshold(true))
V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Can finish)", stplyr->starpostnum, numstarposts));
else if (K_CheckpointThreshold(false) != numstarposts)
V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Skip: %d)", stplyr->starpostnum, numstarposts, K_CheckpointThreshold(false) + stplyr->starpostnum));
else
{
if (stplyr->starpostnum == numstarposts)
V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Can finish)", stplyr->starpostnum, numstarposts));
else
V_DrawString(8, 184, 0, va("Checkpoint: %d / %d", stplyr->starpostnum, numstarposts));
}
V_DrawString(8, 184, 0, va("Checkpoint: %d / %d", stplyr->starpostnum, numstarposts));
if (K_UsingLegacyCheckpoints())
V_DrawString(8, 192, 0, va("Waypoint dist: Prev %d, Next %d", stplyr->prevcheck, stplyr->nextcheck));
}
static void K_DrawWaypointDebugger(void)

View file

@ -8358,6 +8358,11 @@ static boolean K_SetPlayerNextWaypoint(player_t *player)
if (bestwaypoint != NULL)
{
player->currentwaypoint = bestwaypoint;
// if currentwaypoint is the finish line, don't set nextwaypoint to it!
// fixes bigwaypointgap triggering if we're facing the back of the finish line
if (bestwaypoint == finishline)
bestwaypoint = NULL;
}
// check the waypoint's location in relation to the player
@ -8475,7 +8480,8 @@ static boolean K_SetPlayerNextWaypoint(player_t *player)
}
if ((waypoint->prevwaypoints != NULL) && (waypoint->numprevwaypoints > 0U)
&& !(K_PlayerUsesBotMovement(player))) // Bots do not need prev waypoints
&& !(K_PlayerUsesBotMovement(player)) // Bots do not need prev waypoints
&& waypoint != finishline) // do not check finish line's prevwaypoints
{
for (i = 0U; i < waypoint->numprevwaypoints; i++)
{
@ -10978,6 +10984,11 @@ boolean K_SlipdashActive(void)
boolean K_UsingLegacyCheckpoints(void)
{
if (K_UsingPatchedMap() && waypointcap)
{
// we're presumably adding waypoints to an existing map
return false;
}
if (numbosswaypoints > 0)
{
// We are using Kart V1 waypoints!
@ -10987,6 +10998,25 @@ boolean K_UsingLegacyCheckpoints(void)
return false;
}
boolean K_UsingPatchedMap(void)
{
if (patch_version)
{
// This map has been patched!
return true;
}
return false;
}
INT32 K_CheckpointThreshold(boolean roundup)
{
if (K_UsingLegacyCheckpoints() || K_UsingPatchedMap())
return roundup ? numstarposts - numstarposts/2 : numstarposts/2;
else
return numstarposts;
}
void K_UpdateMobjItemOverlay(mobj_t *part, SINT8 itemType, UINT8 itemCount)
{
switch (itemType)

View file

@ -255,6 +255,8 @@ boolean K_SlipdashActive(void);
boolean K_BoostChain(player_t *player, INT32 timer, boolean chainsound);
INT32 K_ChainOrDeincrementTime(player_t *player, INT32 timer, INT32 deincrement, boolean chainsound);
boolean K_UsingLegacyCheckpoints(void);
boolean K_UsingPatchedMap(void);
INT32 K_CheckpointThreshold(boolean roundup);
void K_UpdateMobjItemOverlay(mobj_t *part, SINT8 itemType, UINT8 itemCount);

View file

@ -2179,14 +2179,14 @@ static waypoint_t *K_SetupWaypoint(mobj_t *const mobj)
firstwaypoint = thiswaypoint;
}
if (K_GetWaypointIsFinishline(thiswaypoint))
if (K_GetWaypointIsFinishline(thiswaypoint) && K_GetWaypointIsEnabled(thiswaypoint))
{
if (finishline != NULL)
{
const INT32 oldfinishlineid = K_GetWaypointID(finishline);
const INT32 thiswaypointid = K_GetWaypointID(thiswaypoint);
CONS_Alert(
CONS_WARNING, "Multiple finish line waypoints with IDs %d and %d! Using %d.",
CONS_WARNING, "Multiple finish line waypoints with IDs %d and %d! Using %d.\n",
oldfinishlineid, thiswaypointid, thiswaypointid);
}
finishline = thiswaypoint;

View file

@ -941,8 +941,10 @@ void P_TouchStarPost(mobj_t *post, player_t *player, boolean snaptopost)
(void)snaptopost;
// Player must have touched all previous starposts
if ((post->health - player->starpostnum > 1) && (!K_UsingLegacyCheckpoints()))
// Going backwards triggers sound
if (K_CheckpointThreshold(false) == numstarposts
? post->health - player->starpostnum > 1
: post->health >= player->starpostnum + K_CheckpointThreshold(false))
{
if (!player->checkskip)
{
@ -952,17 +954,7 @@ void P_TouchStarPost(mobj_t *post, player_t *player, boolean snaptopost)
player->grieftime += TICRATE;
}
}
player->checkskip = 3;
return;
}
// Going backwards triggers sound
if ((post->health >= ((numstarposts/2) + player->starpostnum)) && (K_UsingLegacyCheckpoints()))
{
if (!player->checkskip)
S_StartSound(toucher, sfx_s26d);
player->checkskip = 3;
return;

View file

@ -100,6 +100,9 @@ camera_t *mapcampointer;
//
static boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z)
{
boolean startingonground = P_IsObjectOnGround(thing);
sector_t *oldsector = thing->subsector->sector;
numspechit = 0U;
// the move is ok,
@ -129,6 +132,8 @@ static boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z)
thing->floorrover = g_tm.floorrover;
thing->ceilingrover = g_tm.ceilingrover;
P_CheckSectorTransitionalEffects(thing, oldsector, startingonground);
return true;
}
@ -2745,6 +2750,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff, Try
fixed_t oldx = thing->x;
fixed_t oldy = thing->y;
fixed_t startingonground = P_IsObjectOnGround(thing);
sector_t *oldsector = thing->subsector->sector;
// Is the move OK?
if (increment_move(thing, x, y, allowdropoff, result) == false)
@ -2847,6 +2853,8 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff, Try
P_SetThingPosition(thing);
P_CheckSectorTransitionalEffects(thing, oldsector, startingonground);
// remove any duplicates that may be in spechitint
spechitint_removedups();

View file

@ -1236,7 +1236,6 @@ static void P_LinkToBlockMap(mobj_t *thing, mobj_t **bmap)
void P_SetThingPosition(mobj_t *thing)
{ // link into subsector
subsector_t *ss;
sector_t *prevsec = NULL;
sector_t *oldsec = NULL;
fixed_t tfloorz, tceilz;
@ -1249,11 +1248,6 @@ void P_SetThingPosition(mobj_t *thing)
oldsec = thing->subsector->sector;
}
if (thing->subsector)
{
prevsec = thing->subsector->sector;
}
ss = thing->subsector = R_PointInSubsector(thing->x, thing->y);
if (!(thing->flags & MF_NOSECTOR))
@ -1310,12 +1304,6 @@ void P_SetThingPosition(mobj_t *thing)
else if (thing->z <= tfloorz)
thing->eflags |= MFE_JUSTSTEPPEDDOWN;
}
if (udmf && prevsec != thing->subsector->sector)
{
// Check for each time / once sector special actions
P_CheckMobjTouchingSectorActions(thing, false);
}
}
//

View file

@ -2175,10 +2175,13 @@ boolean P_ZMovement(mobj_t *mo)
{
fixed_t dist, delta;
boolean onground;
boolean wasonground;
I_Assert(mo != NULL);
I_Assert(!P_MobjWasRemoved(mo));
wasonground = P_IsObjectOnGround(mo);
// Intercept the stupid 'fall through 3dfloors' bug
if (mo->subsector->sector->ffloors)
P_AdjustMobjFloorZ_FFloors(mo, mo->subsector->sector, 0);
@ -2604,6 +2607,8 @@ boolean P_ZMovement(mobj_t *mo)
}
}
P_CheckSectorTransitionalEffects(mo, mo->subsector->sector, wasonground);
return true;
}
@ -2697,6 +2702,7 @@ static boolean P_PlayerPolyObjectZMovement(mobj_t *mo)
void P_PlayerZMovement(mobj_t *mo)
{
boolean onground;
boolean wasonground;
I_Assert(mo != NULL);
I_Assert(!P_MobjWasRemoved(mo));
@ -2704,6 +2710,8 @@ void P_PlayerZMovement(mobj_t *mo)
if (!mo->player)
return;
wasonground = P_IsObjectOnGround(mo);
// Intercept the stupid 'fall through 3dfloors' bug
if (mo->subsector->sector->ffloors)
P_AdjustMobjFloorZ_FFloors(mo, mo->subsector->sector, 0);
@ -2834,6 +2842,8 @@ void P_PlayerZMovement(mobj_t *mo)
P_CheckGravity(mo, true);
}
}
P_CheckSectorTransitionalEffects(mo, mo->subsector->sector, wasonground);
}
boolean P_SceneryZMovement(mobj_t *mo)
@ -10212,7 +10222,7 @@ void P_MobjThinker(mobj_t *mobj)
if (udmf)
{
// Check for continuous sector special actions
P_CheckMobjTouchingSectorActions(mobj, true);
P_CheckMobjTouchingSectorActions(mobj, true, true);
}
else
{
@ -12856,15 +12866,21 @@ static boolean P_SetupParticleGen(mapthing_t *mthing, mobj_t *mobj)
return true;
}
static void P_SnapToFinishLine(mobj_t *mobj)
static void SpreadFinishLine(line_t *line, line_t *prev, INT32 args)
{
line_t *finishline = P_FindNearestLine(mobj->x, mobj->y,
mobj->subsector->sector, 2001); // case 2001: Finish Line
if (finishline != NULL)
size_t i;
for (i = 0; i < numlines; i++)
{
P_UnsetThingPosition(mobj);
P_ClosestPointOnLine(mobj->x, mobj->y, finishline, (vertex_t *)&mobj->x);
P_SetThingPosition(mobj);
line_t *line2 = &lines[i];
if (line2 != line && line2 != prev && (line2->slopetype == ST_HORIZONTAL || line2->slopetype == ST_VERTICAL) && line2->slopetype == line->slopetype &&
(line->v1 == line2->v1 || line->v2 == line2->v2 || line->v1 == line2->v2 || line->v2 == line2->v1))
{
boolean flipped = line->v1 == line2->v1 || line->v2 == line2->v2 ? TMCFF_FLIP : 0;
line2->special = 2001; // Finish Line
line2->activation = SPAC_CROSS|SPAC_REPEATSPECIAL;
line2->args[0] = args ^ flipped;
SpreadFinishLine(line2, line, args ^ flipped);
}
}
}
@ -13128,6 +13144,7 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
mobj->threshold = mthing->args[0];
mobj->movecount = tag;
if (mthing->args[2] & TMWPF_DISABLED)
{
mobj->extravalue1 = 0; // The waypoint is disabled if extra is on
@ -13152,12 +13169,28 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
{
mobj->reactiontime = 1;
}
if (mthing->args[2] & TMWPF_FINISHLINE)
{
mobj->extravalue2 = 1; // args[2] of 1 means the waypoint is at the finish line
mobj->reactiontime = 0; // Also don't respawn at finish lines
P_SnapToFinishLine(mobj);
line_t *finishline = P_FindNearestLine(mobj->x, mobj->y,
mobj->subsector->sector, K_UsingPatchedMap() ? -1 : 2001); // case 2001: Finish Line
if (finishline != NULL)
{
P_UnsetThingPosition(mobj);
P_ClosestPointOnLine(mobj->x, mobj->y, finishline, (vertex_t *)&mobj->x);
P_SetThingPosition(mobj);
if (K_UsingPatchedMap())
{
finishline->special = 2001; // Finish Line
finishline->activation = SPAC_CROSS|SPAC_REPEATSPECIAL;
finishline->args[0] = mthing->args[4];
SpreadFinishLine(finishline, NULL, mthing->args[4]);
}
}
}
else
{

View file

@ -125,7 +125,7 @@ UINT64 maphash;
//
boolean udmf;
INT32 udmf_version;
INT32 udmf_version, patch_version;
mapnamespace_t mapnamespace = MNS_UNKNOWN;
size_t numvertexes, numsegs, numsectors, numsubsectors, numnodes, numlines, numsides, nummapthings;
size_t num_orig_vertexes;
@ -1461,30 +1461,95 @@ static void TextmapStorePos(textmap_block_t *blocks, size_t *count)
}
// Determine total amount of map data in TEXTMAP.
static boolean TextmapCount(size_t size)
static boolean TextmapCount(size_t size, boolean patch)
{
TracyCZone(__zone, true);
const char *tkn = M_TokenizerRead(0);
UINT8 brackets = 0;
nummapthings = 0;
numlines = 0;
numsides = 0;
numvertexes = 0;
numsectors = 0;
// check namespace for patches
if (patch)
{
// Look for namespace at the beginning.
if (!fastcmp(tkn, "namespace"))
{
CONS_Alert(CONS_ERROR, "No namespace at beginning of lump!\n");
return false;
}
// skip namespace, it's checked by P_SetMapNamespace
M_TokenizerRead(0);
// Check if namespace is valid.
tkn = M_TokenizerRead(0);
if (!fastcmp(tkn, "blankart-patch"))
{
CONS_Alert(CONS_ERROR, "Invalid namespace '%s'. Only 'blankart-patch' is supported.\n", tkn);
return false;
}
}
else
M_TokenizerRead(0);
// Check for version
tkn = M_TokenizerRead(0);
if (fastcmp(tkn, "version"))
{
tkn = M_TokenizerRead(0);
udmf_version = atoi(tkn);
if (udmf_version > UDMF_CURRENT_VERSION)
CONS_Alert(CONS_WARNING, "Map is intended for future UDMF version '%d', current supported version is '%d'. This map may have issues loading.\n", udmf_version, UDMF_CURRENT_VERSION);
if (patch)
{
patch_version = atoi(tkn);
if (patch_version > PATCH_CURRENT_VERSION)
CONS_Alert(CONS_WARNING, "Patch is intended for future patch version '%d', current supported version is '%d'. This patch may have issues loading.\n", patch_version, PATCH_CURRENT_VERSION);
}
else
{
udmf_version = atoi(tkn);
if (udmf_version > UDMF_CURRENT_VERSION)
CONS_Alert(CONS_WARNING, "Map is intended for future UDMF version '%d', current supported version is '%d'. This map may have issues loading.\n", udmf_version, UDMF_CURRENT_VERSION);
}
}
// check hash for patches
if (patch)
{
tkn = M_TokenizerRead(0);
if (!fastcmp(tkn, "checksum"))
{
CONS_Alert(CONS_ERROR, "No checksum in lump!\n");
return false;
}
tkn = M_TokenizerRead(0);
boolean foundhash = false;
const char *hashstr = tkn;
char *end;
// check for hashes separated by pipes
// e.g. "abc123|def456|ghi789"
do
{
UINT64 hash = strtoull(hashstr, &end, 16);
if (hash == maphash)
foundhash = true;
if (end == hashstr || (*end && *end != '|'))
{
CONS_Alert(CONS_ERROR, "Invalid checksum string '%s'\n", tkn);
return false;
}
hashstr = end + 1;
}
while (*end);
if (!foundhash)
CONS_Alert(CONS_WARNING, "This map patch is not compatible with the current map (checksum: " HASHFMT "). The patch may not work.\n", maphash);
}
nummapthings = 0;
if (!patch)
{
numlines = 0;
numsides = 0;
numvertexes = 0;
numsectors = 0;
}
while ((tkn = M_TokenizerRead(0)) && M_TokenizerGetEndPos() < size)
@ -1500,6 +1565,8 @@ static boolean TextmapCount(size_t size)
// Check for valid fields.
else if (fastcmp(tkn, "thing"))
TextmapStorePos(&mapthingBlocks, &nummapthings);
else if (patch)
continue;
else if (fastcmp(tkn, "linedef"))
TextmapStorePos(&linedefBlocks, &numlines);
else if (fastcmp(tkn, "sidedef"))
@ -3213,7 +3280,7 @@ static void P_WriteTextmapWaypoints(void)
/** Loads the textmap data, after obtaining the elements count and allocating their respective space.
*/
static void P_LoadTextmap(void)
static void P_LoadTextmap(boolean patch)
{
TracyCZone(__zone, true);
@ -3232,6 +3299,7 @@ static void P_LoadTextmap(void)
/// from the textmap, and therefore we have to account for it by
/// preemptively setting that value beforehand.
if (!patch)
for (i = 0, vt = vertexes; i < numvertexes; i++, vt++)
{
// Defaults.
@ -3247,6 +3315,7 @@ static void P_LoadTextmap(void)
I_Error("P_LoadTextmap: vertex %s has no y value set!\n", sizeu1(i));
}
if (!patch)
for (i = 0, sc = sectors; i < numsectors; i++, sc++)
{
// Defaults.
@ -3332,6 +3401,7 @@ static void P_LoadTextmap(void)
TextmapFixFlatOffsets(sc);
}
if (!patch)
for (i = 0, ld = lines; i < numlines; i++, ld++)
{
// Defaults.
@ -3362,6 +3432,7 @@ static void P_LoadTextmap(void)
P_InitializeLinedef(ld);
}
if (!patch)
for (i = 0, sd = sides; i < numsides; i++, sd++)
{
// Defaults.
@ -3576,7 +3647,7 @@ static boolean P_LoadMapData(const virtres_t *virt)
{
virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
M_TokenizerOpen((char *)textmap->data, textmap->size);
if (!TextmapCount(textmap->size))
if (!TextmapCount(textmap->size, false))
{
M_TokenizerClose();
TracyCZoneEnd(__zone);
@ -3640,7 +3711,7 @@ static boolean P_LoadMapData(const virtres_t *virt)
// Load map data.
if (udmf)
{
P_LoadTextmap();
P_LoadTextmap(false);
M_TokenizerClose();
}
else
@ -3670,6 +3741,44 @@ static boolean P_LoadMapData(const virtres_t *virt)
return true;
}
static boolean P_LoadMapPatchData(const lumpnum_t lumpnum)
{
TracyCZone(__zone, true);
size_t realnummapthings = nummapthings;
// Count map data.
size_t patchlen = W_LumpLength(lumpnum);
char *patchdata = malloc(patchlen);
W_ReadLump(lumpnum, patchdata);
M_TokenizerOpen(patchdata, patchlen);
if (!TextmapCount(patchlen, true))
{
M_TokenizerClose();
free(patchdata);
TracyCZoneEnd(__zone);
return false;
}
mapthing_t *realmapthings = mapthings;
mapthings = Z_Calloc(nummapthings * sizeof (*mapthings), PU_LEVEL, NULL);
// Load map data.
P_LoadTextmap(true);
M_TokenizerClose();
free(patchdata);
// concatenate the two mapthing arrays
realmapthings = Z_Realloc(realmapthings, (nummapthings + realnummapthings) * sizeof (*mapthings), PU_LEVEL, NULL);
memcpy(realmapthings + realnummapthings, mapthings, nummapthings * sizeof (*mapthings));
Z_Free(mapthings);
mapthings = realmapthings;
nummapthings += realnummapthings;
TracyCZoneEnd(__zone);
return true;
}
static void P_InitializeSubsector(subsector_t *ss)
{
ss->sector = NULL;
@ -7728,7 +7837,7 @@ static boolean P_LoadMapFromFile(void)
virtlump_t *textmap = vres_Find(curmapvirt, "TEXTMAP");
size_t i;
udmf = textmap != NULL;
udmf_version = 0;
udmf_version = patch_version = 0;
if (!P_LoadMapData(curmapvirt))
{
@ -7748,6 +7857,14 @@ static boolean P_LoadMapFromFile(void)
if (!udmf)
P_ConvertBinaryMap();
P_MakeMapHash(curmapvirt, &maphash);
// load map patch?
lumpnum_t patchlump = W_CheckNumForNameInFolder(G_BuildMapName(gamemap), "MapPatch/");
if (patchlump != LUMPERROR)
if (!P_LoadMapPatchData(patchlump))
CONS_Alert(CONS_ERROR, "Failed to load map patch for %s\n", G_BuildMapName(gamemap));
// Copy relevant map data for NetArchive purposes.
spawnsectors = Z_Calloc(numsectors * sizeof(*sectors), PU_LEVEL, NULL);
spawnlines = Z_Calloc(numlines * sizeof(*lines), PU_LEVEL, NULL);
@ -7761,7 +7878,6 @@ static boolean P_LoadMapFromFile(void)
if (sectors[i].tags.count)
spawnsectors[i].tags.tags = memcpy(Z_Malloc(sectors[i].tags.count*sizeof(mtag_t), PU_LEVEL, NULL), sectors[i].tags.tags, sectors[i].tags.count*sizeof(mtag_t));
P_MakeMapHash(curmapvirt, &maphash);
TracyCZoneEnd(__zone);
return true;
}

View file

@ -1988,7 +1988,7 @@ static void K_HandleLapIncrement(player_t *player)
{
if (K_IgnoreFinishLine(player))
return;
if (((K_UsingLegacyCheckpoints()) ? (player->starpostnum >= (numstarposts - (numstarposts/2))) : (player->starpostnum == numstarposts)) || (player->laps == 0))
if ((player->starpostnum >= K_CheckpointThreshold(true)) || (player->laps == 0))
{
size_t i = 0;
UINT8 nump = 0;
@ -2051,7 +2051,7 @@ static void K_HandleLapIncrement(player_t *player)
player->karthud[khud_lapanimation] = 80;
}
if (netgame && player->laps >= numlaps)
if (netgame && player->laps > numlaps)
CON_LogMessage(va(M_GetText("%s has finished the race.\n"), player_names[player-players]));
if (P_IsDisplayPlayer(player))
@ -2135,7 +2135,9 @@ static void K_HandleLapDecrement(player_t *player)
{
if (player)
{
if ((player->starpostnum == 0) && (player->laps > 0))
if (K_IgnoreFinishLine(player))
return;
if (player->laps > 0)
{
player->starpostnum = numstarposts;
player->laps--;
@ -4449,6 +4451,36 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
if ((gametyperules & GTR_CIRCUIT) && (mo->player->exiting == 0) && !(mo->player->pflags & PF_HITFINISHLINE))
{
// megablock cope 1: single-use finish lines
if (args[0] & TMCFF_SINGLEUSE && mo->player->laps > 0)
return false;
// megablock cope 2: require a circuit finish line special to be hit at the same time
if (args[0] & TMCFF_NEEDSECTOR)
{
boolean touching = false;
if (P_MobjTouchingSectorSpecialFlag(mo, SSF_FINISHLINE))
touching = true;
// we have to check if we WERE touching a finish line sector last tic too!
// otherwise lap decrementing wouldn't work!
// YAAAAAAAAAAAAAAY
fixed_t realx = mo->x, realy = mo->y;
P_UnsetThingPosition(mo);
mo->x = mo->old_x, mo->y = mo->old_y;
P_SetThingPosition(mo);
if (P_MobjTouchingSectorSpecialFlag(mo, SSF_FINISHLINE))
touching = true;
P_UnsetThingPosition(mo);
mo->x = realx, mo->y = realy;
P_SetThingPosition(mo);
if (!touching)
return false;
}
if (((args[0] & TMCFF_FLIP) && (side == 0))
|| (!(args[0] & TMCFF_FLIP) && (side == 1))) // crossed from behind to infront
{
@ -5356,7 +5388,7 @@ static void P_EvaluateSpecialFlags(player_t *player, sector_t *sector, sector_t
P_ProcessZoomTube(player, sectag, true);
if (sector->specialflags & SSF_FINISHLINE)
{
if ((gametyperules & GTR_CIRCUIT) && (player->exiting == 0) && !(player->pflags & PF_HITFINISHLINE))
if (K_UsingLegacyCheckpoints() && (gametyperules & GTR_CIRCUIT) && (player->exiting == 0) && !(player->pflags & PF_HITFINISHLINE))
{
K_HandleLapIncrement(player);
player->pflags |= PF_HITFINISHLINE;
@ -5761,7 +5793,7 @@ static boolean P_AllowSpecialCeiling(sector_t *sec, mobj_t *thing)
return false;
}
static void P_CheckMobj3DFloorAction(mobj_t *mo, sector_t *sec, boolean continuous)
static void P_CheckMobj3DFloorAction(mobj_t *mo, sector_t *sec, boolean continuous, boolean sectorchanged)
{
sector_t *originalsector = mo->subsector->sector;
ffloor_t *rover;
@ -5820,6 +5852,10 @@ static void P_CheckMobj3DFloorAction(mobj_t *mo, sector_t *sec, boolean continuo
continue;
}
}
else if (sectorchanged == false)
{
continue;
}
activator = Z_Calloc(sizeof(activator_t), PU_LEVEL, NULL);
I_Assert(activator != NULL);
@ -5839,7 +5875,7 @@ static void P_CheckMobj3DFloorAction(mobj_t *mo, sector_t *sec, boolean continuo
}
}
static void P_CheckMobjPolyobjAction(mobj_t *mo, boolean continuous)
static void P_CheckMobjPolyobjAction(mobj_t *mo, boolean continuous, boolean sectorchanged)
{
sector_t *originalsector = mo->subsector->sector;
polyobj_t *po;
@ -5894,6 +5930,10 @@ static void P_CheckMobjPolyobjAction(mobj_t *mo, boolean continuous)
continue;
}
}
else if (sectorchanged == false)
{
continue;
}
activator = Z_Calloc(sizeof(activator_t), PU_LEVEL, NULL);
I_Assert(activator != NULL);
@ -5913,7 +5953,7 @@ static void P_CheckMobjPolyobjAction(mobj_t *mo, boolean continuous)
}
}
static void P_CheckMobjSectorAction(mobj_t *mo, sector_t *sec, boolean continuous)
static void P_CheckMobjSectorAction(mobj_t *mo, sector_t *sec, boolean continuous, boolean sectorchanged)
{
activator_t *activator = NULL;
boolean result = false;
@ -5950,6 +5990,10 @@ static void P_CheckMobjSectorAction(mobj_t *mo, sector_t *sec, boolean continuou
return;
}
}
else if (sectorchanged == false)
{
return;
}
activator = Z_Calloc(sizeof(activator_t), PU_LEVEL, NULL);
I_Assert(activator != NULL);
@ -5966,7 +6010,7 @@ static void P_CheckMobjSectorAction(mobj_t *mo, sector_t *sec, boolean continuou
}
}
void P_CheckMobjTouchingSectorActions(mobj_t *mobj, boolean continuous)
void P_CheckMobjTouchingSectorActions(mobj_t *mobj, boolean continuous, boolean sectorchanged)
{
sector_t *originalsector;
@ -5992,13 +6036,13 @@ void P_CheckMobjTouchingSectorActions(mobj_t *mobj, boolean continuous)
}
}
P_CheckMobj3DFloorAction(mobj, originalsector, continuous);
P_CheckMobj3DFloorAction(mobj, originalsector, continuous, sectorchanged);
if TELEPORTED(mobj) return;
P_CheckMobjPolyobjAction(mobj, continuous);
P_CheckMobjPolyobjAction(mobj, continuous, sectorchanged);
if TELEPORTED(mobj) return;
P_CheckMobjSectorAction(mobj, originalsector, continuous);
P_CheckMobjSectorAction(mobj, originalsector, continuous, sectorchanged);
}
#undef TELEPORTED
@ -9491,3 +9535,21 @@ void P_StartQuake(fixed_t intensity, tic_t time)
quake.intensity = FixedMul(intensity, mapobjectscale);
quake.time = time;
}
void P_CheckSectorTransitionalEffects(mobj_t *thing, sector_t *prevsec, boolean wasgrounded)
{
if (!udmf)
{
return;
}
boolean sectorchanged = (prevsec != thing->subsector->sector);
if (!sectorchanged && wasgrounded == P_IsObjectOnGround(thing))
{
return;
}
// Check for each time / once sector special actions
P_CheckMobjTouchingSectorActions(thing, false, sectorchanged);
}

View file

@ -487,7 +487,9 @@ typedef enum
typedef enum
{
TMCFF_FLIP = 1,
TMCFF_FLIP = 1,
TMCFF_SINGLEUSE = 1<<1,
TMCFF_NEEDSECTOR = 1<<2,
} textmapcrossfinishflags_t;
typedef enum
@ -542,12 +544,13 @@ sector_t *P_PlayerTouchingSectorSpecial(player_t *player, INT32 section, INT32 n
sector_t *P_PlayerTouchingSectorSpecialFlag(player_t *player, sectorspecialflags_t flag);
void P_PlayerInSpecialSector(player_t *player);
void P_CheckMobjTrigger(mobj_t *mobj, boolean pushable);
void P_CheckMobjTouchingSectorActions(mobj_t *mobj, boolean continuous);
void P_CheckMobjTouchingSectorActions(mobj_t *mobj, boolean continuous, boolean sectorchanged);
sector_t *P_FindPlayerTrigger(player_t *player, line_t *sourceline);
boolean P_IsPlayerValid(size_t playernum);
boolean P_CanPlayerTrigger(size_t playernum);
void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *roversector);
sector_t *P_ThingOnSpecial3DFloor(mobj_t *mo);
void P_CheckSectorTransitionalEffects(mobj_t *thing, sector_t *prevsec, boolean wasgrounded);
fixed_t P_FindLowestFloorSurrounding(sector_t *sec);
fixed_t P_FindHighestFloorSurrounding(sector_t *sec);

View file

@ -64,8 +64,9 @@ extern size_t numspritelumps, max_spritelumps;
// Lookup tables for map data.
//
#define UDMF_CURRENT_VERSION (3)
#define PATCH_CURRENT_VERSION (1)
extern boolean udmf;
extern INT32 udmf_version;
extern INT32 udmf_version, patch_version;
extern size_t numsprites;
extern spritedef_t *sprites;