diff --git a/extras/blanbinary/BlanKartBinary-Config.cfg b/extras/blanbinary/BlanKartBinary-Config.cfg index e23530ffd..4f02d8429 100644 --- a/extras/blanbinary/BlanKartBinary-Config.cfg +++ b/extras/blanbinary/BlanKartBinary-Config.cfg @@ -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 diff --git a/extras/blanudmf/Includes/BlanKart_linedefs.cfg b/extras/blanudmf/Includes/BlanKart_linedefs.cfg index c32780194..a9734db93 100644 --- a/extras/blanudmf/Includes/BlanKart_linedefs.cfg +++ b/extras/blanudmf/Includes/BlanKart_linedefs.cfg @@ -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"; + } } } diff --git a/extras/blanudmf/Includes/BlanKart_things.cfg b/extras/blanudmf/Includes/BlanKart_things.cfg index 2b0662aa8..876cf637e 100644 --- a/extras/blanudmf/Includes/BlanKart_things.cfg +++ b/extras/blanudmf/Includes/BlanKart_things.cfg @@ -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 diff --git a/src/doomdef.h b/src/doomdef.h index 0e596946b..4a5f83f0b 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -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 diff --git a/src/k_hud.c b/src/k_hud.c index 3f179e861..c34bd2a67 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -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) diff --git a/src/k_kart.c b/src/k_kart.c index 1a94b2a1f..54d0fb492 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -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) diff --git a/src/k_kart.h b/src/k_kart.h index 5ae2dbf45..13f50aaca 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -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); diff --git a/src/k_waypoint.cpp b/src/k_waypoint.cpp index ee24e321d..d8d605e5c 100644 --- a/src/k_waypoint.cpp +++ b/src/k_waypoint.cpp @@ -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; diff --git a/src/p_inter.c b/src/p_inter.c index 7e435af2a..ad37fa90a 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -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; diff --git a/src/p_map.c b/src/p_map.c index e629db522..b5de9cb23 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -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(); diff --git a/src/p_maputl.c b/src/p_maputl.c index 9069026dd..95fc912f8 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -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); - } } // diff --git a/src/p_mobj.c b/src/p_mobj.c index cb0996401..84a5fadfd 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -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 { diff --git a/src/p_setup.c b/src/p_setup.c index 6ed8862d6..7c9532122 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -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; } diff --git a/src/p_spec.c b/src/p_spec.c index 322fba5d9..5cc241847 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -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); +} diff --git a/src/p_spec.h b/src/p_spec.h index 7bfab7f13..9b247ab7f 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -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); diff --git a/src/r_state.h b/src/r_state.h index bb5aea6b3..2bbcab956 100644 --- a/src/r_state.h +++ b/src/r_state.h @@ -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;