From b55c67c29dddb69c36d67dfe43153cf3b9754db2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 25 Nov 2022 20:53:02 -0500 Subject: [PATCH 1/7] Add currentwaypoint --- src/d_player.h | 1 + src/k_hud.c | 3 ++- src/k_kart.c | 3 ++- src/p_saveg.c | 9 +++++++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index d3b6975ed..193a9d887 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -413,6 +413,7 @@ typedef struct player_s UINT8 oldposition; // Used for taunting when you pass someone UINT8 positiondelay; // Used for position number, so it can grow when passing UINT32 distancetofinish; + waypoint_t *currentwaypoint; waypoint_t *nextwaypoint; respawnvars_t respawn; // Respawn info tic_t airtime; // Keep track of how long you've been in the air diff --git a/src/k_hud.c b/src/k_hud.c index 4a5644442..37d9a2074 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -4634,7 +4634,8 @@ static void K_DrawWaypointDebugger(void) if (stplyr != &players[displayplayers[0]]) // only for p1 return; - V_DrawString(8, 166, 0, va("'Best' Waypoint ID: %d", K_GetWaypointID(stplyr->nextwaypoint))); + V_DrawString(8, 156, 0, va("Current Waypoint ID: %d", K_GetWaypointID(stplyr->currentwaypoint))); + V_DrawString(8, 166, 0, va("Next Waypoint ID: %d", K_GetWaypointID(stplyr->nextwaypoint))); V_DrawString(8, 176, 0, va("Finishline Distance: %d", stplyr->distancetofinish)); if (numstarposts > 0) diff --git a/src/k_kart.c b/src/k_kart.c index 6bb2be28e..dcd1fa60b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8855,7 +8855,8 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) waypoint_t *waypoint = K_GetBestWaypointForMobj(player->mo); boolean updaterespawn = false; - bestwaypoint = waypoint; + // Our current waypoint. + player->currentwaypoint = bestwaypoint = waypoint; // check the waypoint's location in relation to the player // If it's generally in front, it's fine, otherwise, use the best next/previous waypoint. diff --git a/src/p_saveg.c b/src/p_saveg.c index c9ede8b78..65b2d81a3 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4422,6 +4422,15 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&mobj->player->follower, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "follower not found on %d\n", mobj->type); } + if (mobj->player->currentwaypoint) + { + temp = (UINT32)(size_t)mobj->player->currentwaypoint; + mobj->player->currentwaypoint = K_GetWaypointFromIndex(temp); + if (mobj->player->currentwaypoint == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "currentwaypoint not found on %d\n", mobj->type); + } + } if (mobj->player->nextwaypoint) { temp = (UINT32)(size_t)mobj->player->nextwaypoint; From d5b62886d2e22cc5d4b777ecb6cdd9b8f8ebe987 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 26 Nov 2022 00:04:09 -0500 Subject: [PATCH 2/7] Change distance to finish calculation It now calculates it from a position clamped inbetween lines formed by the waypoints. --- src/k_kart.c | 153 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 135 insertions(+), 18 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index dcd1fa60b..8ca23b483 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8900,20 +8900,17 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) // We're using a lot of angle calculations here, because only using facing angle or only using momentum angle both have downsides. // nextwaypoints will be picked if you're facing OR moving forward. // prevwaypoints will be picked if you're facing AND moving backward. - if ((angledelta > ANGLE_45 || momdelta > ANGLE_45) - && (finishlinehack == false)) + if ( +#if 0 + (angledelta > ANGLE_45 || momdelta > ANGLE_45) && +#endif + (finishlinehack == false) + ) { - angle_t nextbestdelta = angledelta; - angle_t nextbestmomdelta = momdelta; + angle_t nextbestdelta = ANGLE_MAX; + angle_t nextbestmomdelta = ANGLE_MAX; size_t i = 0U; - if (K_PlayerUsesBotMovement(player)) - { - // Try to force bots to use a next waypoint - nextbestdelta = ANGLE_MAX; - nextbestmomdelta = ANGLE_MAX; - } - if ((waypoint->nextwaypoints != NULL) && (waypoint->numnextwaypoints > 0U)) { for (i = 0U; i < waypoint->numnextwaypoints; i++) @@ -9151,7 +9148,7 @@ void K_UpdateDistanceFromFinishLine(player_t *const player) // Player has finished, we don't need to calculate this player->distancetofinish = 0U; } - else if ((player->nextwaypoint != NULL) && (finishline != NULL)) + else if ((player->currentwaypoint != NULL) && (player->nextwaypoint != NULL) && (finishline != NULL)) { const boolean useshortcuts = false; const boolean huntbackwards = false; @@ -9165,17 +9162,137 @@ void K_UpdateDistanceFromFinishLine(player_t *const player) // Using shortcuts won't find a path, so distance won't be updated until the player gets back on track if (pathfindsuccess == true) { - // Add euclidean distance to the next waypoint to the distancetofinish - UINT32 adddist; - fixed_t disttowaypoint = + const boolean pathBackwardsReverse = ((player->pflags & PF_WRONGWAY) == 0); + boolean pathBackwardsSuccess = false; + path_t pathBackwards = {0}; + + fixed_t disttonext = 0; + UINT32 traveldist = 0; + UINT32 adddist = 0; + + disttonext = P_AproxDistance( (player->mo->x >> FRACBITS) - (player->nextwaypoint->mobj->x >> FRACBITS), (player->mo->y >> FRACBITS) - (player->nextwaypoint->mobj->y >> FRACBITS)); - disttowaypoint = P_AproxDistance(disttowaypoint, (player->mo->z >> FRACBITS) - (player->nextwaypoint->mobj->z >> FRACBITS)); + disttonext = P_AproxDistance(disttonext, (player->mo->z >> FRACBITS) - (player->nextwaypoint->mobj->z >> FRACBITS)); - adddist = (UINT32)disttowaypoint; + traveldist = ((UINT32)disttonext) * 2; + pathBackwardsSuccess = + K_PathfindThruCircuit(player->nextwaypoint, traveldist, &pathBackwards, false, pathBackwardsReverse); - player->distancetofinish = pathtofinish.totaldist + adddist; + if (pathBackwardsSuccess == true) + { + if (pathBackwards.numnodes > 1) + { + // Find the closest segment, and add the distance to reach it. + vector3_t point; + size_t i; + + vector3_t best; + fixed_t bestPoint = INT32_MAX; + fixed_t bestDist = INT32_MAX; + UINT32 bestGScore = UINT32_MAX; + + point.x = player->mo->x; + point.y = player->mo->y; + point.z = player->mo->z; + + best.x = point.x; + best.y = point.y; + best.z = point.z; + + for (i = 1; i < pathBackwards.numnodes; i++) + { + vector3_t line[2]; + vector3_t result; + + waypoint_t *pwp = (waypoint_t *)pathBackwards.array[i - 1].nodedata; + waypoint_t *wp = (waypoint_t *)pathBackwards.array[i].nodedata; + + fixed_t pDist = 0; + UINT32 g = pathBackwards.array[i - 1].gscore; + + line[0].x = pwp->mobj->x; + line[0].y = pwp->mobj->y; + line[0].z = pwp->mobj->z; + + line[1].x = wp->mobj->x; + line[1].y = wp->mobj->y; + line[1].z = wp->mobj->z; + + P_ClosestPointOnLine3D(&point, line, &result); + + pDist = P_AproxDistance(point.x - result.x, point.y - result.y); + pDist = P_AproxDistance(pDist, point.z - result.z); + + if (pDist < bestPoint) + { + FV3_Copy(&best, &result); + + bestPoint = pDist; + + bestDist = + P_AproxDistance( + (result.x >> FRACBITS) - (line[0].x >> FRACBITS), + (result.y >> FRACBITS) - (line[0].y >> FRACBITS)); + bestDist = P_AproxDistance(bestDist, (result.z >> FRACBITS) - (line[0].z >> FRACBITS)); + + bestGScore = g + ((UINT32)bestDist); + } + } + + if (cv_kartdebugwaypoints.value) + { + mobj_t *debugmobj = P_SpawnMobj(best.x, best.y, best.z, MT_SPARK); + P_SetMobjState(debugmobj, S_WAYPOINTORB); + + debugmobj->frame &= ~FF_TRANSMASK; + debugmobj->frame |= FF_FULLBRIGHT; //FF_TRANS20 + + debugmobj->tics = 2; + debugmobj->color = SKINCOLOR_BANANA; + } + + adddist = bestGScore; + } + /* + else + { + // Only one point to work with, so just add your euclidean distance to that. + waypoint_t *wp = (waypoint_t *)pathBackwards.array[0].nodedata; + fixed_t disttowaypoint = + P_AproxDistance( + (player->mo->x >> FRACBITS) - (wp->mobj->x >> FRACBITS), + (player->mo->y >> FRACBITS) - (wp->mobj->y >> FRACBITS)); + disttowaypoint = P_AproxDistance(disttowaypoint, (player->mo->z >> FRACBITS) - (wp->mobj->z >> FRACBITS)); + + adddist = (UINT32)disttowaypoint; + } + */ + } + /* + else + { + // Fallback to adding euclidean distance to the next waypoint to the distancetofinish + adddist = (UINT32)disttonext; + } + */ + + if (pathBackwardsReverse == false) + { + if (pathtofinish.totaldist > adddist) + { + player->distancetofinish = pathtofinish.totaldist - adddist; + } + else + { + player->distancetofinish = 0; + } + } + else + { + player->distancetofinish = pathtofinish.totaldist + adddist; + } Z_Free(pathtofinish.array); // distancetofinish is currently a flat distance to the finish line, but in order to be fully From cb580031caf26e851d1b9a7bc155c348aa3b7092 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 26 Nov 2022 01:26:46 -0500 Subject: [PATCH 3/7] Condense all of the P_CheckSight copy-paste For something for my next commit --- src/p_sight.c | 700 ++++++++++++++++++++++---------------------------- 1 file changed, 306 insertions(+), 394 deletions(-) diff --git a/src/p_sight.c b/src/p_sight.c index 64bba3969..50b22243d 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -27,13 +27,26 @@ // killough 4/19/98: // Convert LOS info to struct for reentrancy and efficiency of data locality -typedef struct { - fixed_t sightzstart, t2x, t2y; // eye z of looker - divline_t strace; // from t1 to t2 - fixed_t topslope, bottomslope; // slopes to top and bottom of target +typedef struct +{ + fixed_t sightzstart, t2x, t2y; // eye z of looker + divline_t strace; // from t1 to t2 + fixed_t topslope, bottomslope; // slopes to top and bottom of target fixed_t bbox[4]; + + mobj_t *compareThing; // Original thing + boolean alreadyHates; // For bot traversal, for if the bot is already in a sector it doesn't want to be } los_t; +typedef boolean (*los_valid_t)(seg_t *, divline_t *, register los_t *); +typedef boolean (*los_valid_poly_t)(polyobj_t *, divline_t *, register los_t *); + +typedef struct +{ + los_valid_t validate; // Validation function. If true, continue iterating for possible success. If false, end early with failure. + los_valid_poly_t validatePolyobj; // If not NULL, then we will also check polyobject lines using this func. +} los_funcs_t; + static INT32 sightcounts[2]; // @@ -103,23 +116,58 @@ static fixed_t P_InterceptVector2(divline_t *v2, divline_t *v1) return frac; } -static boolean P_CrossSubsecPolyObj(polyobj_t *po, register los_t *los) +static boolean P_IsVisiblePolyObj(polyobj_t *po, divline_t *divl, register los_t *los) { - size_t i; - sector_t *polysec; + sector_t *polysec = po->lines[0]->backsector; + fixed_t frac; + fixed_t topslope, bottomslope; if (!(po->flags & POF_RENDERALL)) + { return true; // the polyobject isn't visible, so we can ignore it + } - polysec = po->lines[0]->backsector; + // stop because it is not two sided + /* + if (!(po->flags & POF_TESTHEIGHT)) + { + return false; + } + */ + + frac = P_InterceptVector2(&los->strace, divl); + + // get slopes of top and bottom of this polyobject line + topslope = FixedDiv(polysec->ceilingheight - los->sightzstart , frac); + bottomslope = FixedDiv(polysec->floorheight - los->sightzstart , frac); + + if (topslope >= los->topslope && bottomslope <= los->bottomslope) + { + // view completely blocked + return false; + } + + // TODO: figure out if it's worth considering partially blocked cases or not? + // maybe to adjust los's top/bottom slopes if needed + /* + if (los->topslope <= los->bottomslope) + { + return false; + } + */ + + return true; +} + +static boolean P_CrossSubsecPolyObj(polyobj_t *po, register los_t *los, register los_funcs_t *funcs) +{ + size_t i; for (i = 0; i < po->numLines; ++i) { line_t *line = po->lines[i]; divline_t divl; const vertex_t *v1,*v2; - fixed_t frac; - fixed_t topslope, bottomslope; // already checked other side? if (line->validcount == validcount) @@ -150,23 +198,195 @@ static boolean P_CrossSubsecPolyObj(polyobj_t *po, register los_t *los) P_DivlineSide(los->t2x, los->t2y, &divl)) continue; - // stop because it is not two sided - //if (!(po->flags & POF_TESTHEIGHT)) - //return false; + if (funcs->validatePolyobj(po, &divl, los) == false) + { + return false; + } + } - frac = P_InterceptVector2(&los->strace, &divl); + return true; +} - // get slopes of top and bottom of this polyobject line - topslope = FixedDiv(polysec->ceilingheight - los->sightzstart , frac); - bottomslope = FixedDiv(polysec->floorheight - los->sightzstart , frac); +static boolean P_IsVisible(seg_t *seg, divline_t *divl, register los_t *los) +{ + line_t *line = seg->linedef; + fixed_t popentop, popenbottom; + const sector_t *front, *back; + fixed_t frac; + fixed_t fracx, fracy; + fixed_t frontf, backf, frontc, backc; - if (topslope >= los->topslope && bottomslope <= los->bottomslope) - return false; // view completely blocked + // stop because it is not two sided anyway + if (!(line->flags & ML_TWOSIDED)) + { + return false; + } + // calculate fractional intercept (how far along we are divided by how far we are from t2) + frac = P_InterceptVector2(&los->strace, divl); + + front = seg->frontsector; + back = seg->backsector; + // calculate position at intercept + fracx = los->strace.x + FixedMul(los->strace.dx, frac); + fracy = los->strace.y + FixedMul(los->strace.dy, frac); + // calculate sector heights + frontf = P_GetSectorFloorZAt (front, fracx, fracy); + frontc = P_GetSectorCeilingZAt(front, fracx, fracy); + backf = P_GetSectorFloorZAt (back , fracx, fracy); + backc = P_GetSectorCeilingZAt(back , fracx, fracy); + // crosses a two sided line + // no wall to block sight with? + if (frontf == backf && frontc == backc + && !front->ffloors & !back->ffloors) // (and no FOFs) + { + return true; + } + + // possible occluder + // because of ceiling height differences + popentop = min(frontc, backc); + + // because of floor height differences + popenbottom = max(frontf, backf); + + // quick test for totally closed doors + if (popenbottom >= popentop) + { + return false; + } + + if (frontf != backf) + { + fixed_t slope = FixedDiv(popenbottom - los->sightzstart , frac); + if (slope > los->bottomslope) + los->bottomslope = slope; + } + + if (frontc != backc) + { + fixed_t slope = FixedDiv(popentop - los->sightzstart , frac); + if (slope < los->topslope) + los->topslope = slope; + } + + if (los->topslope <= los->bottomslope) + { + return false; + } + + // Monster Iestyn: check FOFs! + if (front->ffloors || back->ffloors) + { + ffloor_t *rover; + fixed_t topslope, bottomslope; + fixed_t topz, bottomz; + // check front sector's FOFs first + for (rover = front->ffloors; rover; rover = rover->next) + { + if (!(rover->fofflags & FOF_EXISTS) + || !(rover->fofflags & FOF_RENDERSIDES) || (rover->fofflags & (FOF_TRANSLUCENT|FOF_FOG))) + { + continue; + } + + topz = P_GetFFloorTopZAt (rover, fracx, fracy); + bottomz = P_GetFFloorBottomZAt(rover, fracx, fracy); + topslope = FixedDiv( topz - los->sightzstart, frac); + bottomslope = FixedDiv(bottomz - los->sightzstart, frac); + + if (topslope >= los->topslope && bottomslope <= los->bottomslope) + { + return false; // view completely blocked + } + } + // check back sector's FOFs as well + for (rover = back->ffloors; rover; rover = rover->next) + { + if (!(rover->fofflags & FOF_EXISTS) + || !(rover->fofflags & FOF_RENDERSIDES) || (rover->fofflags & (FOF_TRANSLUCENT|FOF_FOG))) + { + continue; + } + + topz = P_GetFFloorTopZAt (rover, fracx, fracy); + bottomz = P_GetFFloorBottomZAt(rover, fracx, fracy); + topslope = FixedDiv( topz - los->sightzstart, frac); + bottomslope = FixedDiv(bottomz - los->sightzstart, frac); + + if (topslope >= los->topslope && bottomslope <= los->bottomslope) + { + return false; // view completely blocked + } + } // TODO: figure out if it's worth considering partially blocked cases or not? // maybe to adjust los's top/bottom slopes if needed - //if (los->topslope <= los->bottomslope) - //return false; + } + + return true; +} + +static boolean P_CanTraceBlockingLine(seg_t *seg, divline_t *divl, register los_t *los) +{ + line_t *line = seg->linedef; + + (void)divl; + + if (P_IsLineBlocking(line, los->compareThing) == true) + { + // This line will always block us + return false; + } + + if (los->compareThing->player != NULL) + { + if (P_IsLineTripWire(line) == true && K_TripwirePass(los->compareThing->player) == false) + { + // Can't go through trip wire. + return false; + } + } + + return true; +} + +static boolean P_CanBotTraverse(seg_t *seg, divline_t *divl, register los_t *los) +{ + line_t *line = seg->linedef; + fixed_t maxstep = 0; + + if (P_CanTraceBlockingLine(seg, divl, los) == false) + { + // Blocked, so obviously can't traverse either. + return false; + } + + // set openrange, opentop, openbottom + tm.x = los->compareThing->x; + tm.y = los->compareThing->y; + P_LineOpening(line, los->compareThing); + maxstep = P_GetThingStepUp(los->compareThing, tm.x, tm.y); + + if ((openrange < los->compareThing->height) // doesn't fit + || (opentop - los->compareThing->z < los->compareThing->height) // mobj is too high + || (openbottom - los->compareThing->z > maxstep)) // too big a step up + { + // This line situationally blocks us + return false; + } + + if (los->compareThing->player != NULL && los->alreadyHates == false) + { + // Treat damage sectors like walls, if you're not already in a bad sector. + vertex_t pos; + P_ClosestPointOnLine(los->compareThing->x, los->compareThing->y, line, &pos); + + if (K_BotHatesThisSector(los->compareThing->player, line->frontsector, pos.x, pos.y) + || K_BotHatesThisSector(los->compareThing->player, line->backsector, pos.x, pos.y)) + { + // This line does not block us, but we don't want to be in it. + return false; + } } return true; @@ -177,11 +397,10 @@ static boolean P_CrossSubsecPolyObj(polyobj_t *po, register los_t *los) // // Returns true if strace crosses the given subsector successfully. // -static boolean P_CrossSubsector(size_t num, register los_t *los) +static boolean P_CrossSubsector(size_t num, register los_t *los, register los_funcs_t *funcs) { seg_t *seg; INT32 count; - polyobj_t *po; // haleyjd 02/23/06 #ifdef RANGECHECK if (num >= numsubsectors) @@ -192,30 +411,30 @@ static boolean P_CrossSubsector(size_t num, register los_t *los) seg = segs + subsectors[num].firstline; // haleyjd 02/23/06: check polyobject lines - if ((po = subsectors[num].polyList)) + if (funcs->validatePolyobj != NULL) { - while (po) + polyobj_t *po; + + if ((po = subsectors[num].polyList)) { - if (po->validcount != validcount) + while (po) { - po->validcount = validcount; - if (!P_CrossSubsecPolyObj(po, los)) - return false; + if (po->validcount != validcount) + { + po->validcount = validcount; + if (!P_CrossSubsecPolyObj(po, los, funcs)) + return false; + } + po = (polyobj_t *)(po->link.next); } - po = (polyobj_t *)(po->link.next); } } for (count = subsectors[num].numlines; --count >= 0; seg++) // check lines { line_t *line = seg->linedef; + const vertex_t *v1, *v2; divline_t divl; - fixed_t popentop, popenbottom; - const sector_t *front, *back; - const vertex_t *v1,*v2; - fixed_t frac; - fixed_t frontf, backf, frontc, backc; - fixed_t fracx, fracy; if (seg->glseg) continue; @@ -249,99 +468,9 @@ static boolean P_CrossSubsector(size_t num, register los_t *los) P_DivlineSide(los->t2x, los->t2y, &divl)) continue; - // stop because it is not two sided anyway - if (!(line->flags & ML_TWOSIDED)) - return false; - - // calculate fractional intercept (how far along we are divided by how far we are from t2) - frac = P_InterceptVector2(&los->strace, &divl); - - front = seg->frontsector; - back = seg->backsector; - // calculate position at intercept - fracx = los->strace.x + FixedMul(los->strace.dx, frac); - fracy = los->strace.y + FixedMul(los->strace.dy, frac); - // calculate sector heights - frontf = P_GetSectorFloorZAt (front, fracx, fracy); - frontc = P_GetSectorCeilingZAt(front, fracx, fracy); - backf = P_GetSectorFloorZAt (back , fracx, fracy); - backc = P_GetSectorCeilingZAt(back , fracx, fracy); - // crosses a two sided line - // no wall to block sight with? - if (frontf == backf && frontc == backc - && !front->ffloors & !back->ffloors) // (and no FOFs) - continue; - - // possible occluder - // because of ceiling height differences - popentop = min(frontc, backc); - - // because of floor height differences - popenbottom = max(frontf, backf); - - // quick test for totally closed doors - if (popenbottom >= popentop) - return false; - - if (frontf != backf) + if (funcs->validate(seg, &divl, los) == false) { - fixed_t slope = FixedDiv(popenbottom - los->sightzstart , frac); - if (slope > los->bottomslope) - los->bottomslope = slope; - } - - if (frontc != backc) - { - fixed_t slope = FixedDiv(popentop - los->sightzstart , frac); - if (slope < los->topslope) - los->topslope = slope; - } - - if (los->topslope <= los->bottomslope) return false; - - // Monster Iestyn: check FOFs! - if (front->ffloors || back->ffloors) - { - ffloor_t *rover; - fixed_t topslope, bottomslope; - fixed_t topz, bottomz; - // check front sector's FOFs first - for (rover = front->ffloors; rover; rover = rover->next) - { - if (!(rover->fofflags & FOF_EXISTS) - || !(rover->fofflags & FOF_RENDERSIDES) || (rover->fofflags & (FOF_TRANSLUCENT|FOF_FOG))) - { - continue; - } - - topz = P_GetFFloorTopZAt (rover, fracx, fracy); - bottomz = P_GetFFloorBottomZAt(rover, fracx, fracy); - topslope = FixedDiv( topz - los->sightzstart, frac); - bottomslope = FixedDiv(bottomz - los->sightzstart, frac); - - if (topslope >= los->topslope && bottomslope <= los->bottomslope) - return false; // view completely blocked - } - // check back sector's FOFs as well - for (rover = back->ffloors; rover; rover = rover->next) - { - if (!(rover->fofflags & FOF_EXISTS) - || !(rover->fofflags & FOF_RENDERSIDES) || (rover->fofflags & (FOF_TRANSLUCENT|FOF_FOG))) - { - continue; - } - - topz = P_GetFFloorTopZAt (rover, fracx, fracy); - bottomz = P_GetFFloorBottomZAt(rover, fracx, fracy); - topslope = FixedDiv( topz - los->sightzstart, frac); - bottomslope = FixedDiv(bottomz - los->sightzstart, frac); - - if (topslope >= los->topslope && bottomslope <= los->bottomslope) - return false; // view completely blocked - } - // TODO: figure out if it's worth considering partially blocked cases or not? - // maybe to adjust los's top/bottom slopes if needed } } @@ -356,24 +485,32 @@ static boolean P_CrossSubsector(size_t num, register los_t *los) // // killough 4/20/98: rewritten to remove tail recursion, clean up, and optimize -static boolean P_CrossBSPNode(INT32 bspnum, register los_t *los) +static boolean P_CrossBSPNode(INT32 bspnum, register los_t *los, register los_funcs_t *funcs) { + if (funcs->validate == NULL) + { + return false; + } + while (!(bspnum & NF_SUBSECTOR)) { register node_t *bsp = nodes + bspnum; - INT32 side = P_DivlineSide(los->strace.x,los->strace.y,(divline_t *)bsp)&1; + INT32 side = P_DivlineSide(los->strace.x, los->strace.y, (divline_t *)bsp) & 1; + if (side == P_DivlineSide(los->t2x, los->t2y, (divline_t *) bsp)) + { bspnum = bsp->children[side]; // doesn't touch the other side + } else // the partition plane is crossed here { - if (!P_CrossBSPNode(bsp->children[side], los)) - return 0; // cross the starting side + if (!P_CrossBSPNode(bsp->children[side], los, funcs)) + return false; // cross the starting side else - bspnum = bsp->children[side^1]; // cross the ending side + bspnum = bsp->children[side ^ 1]; // cross the ending side } } - return - P_CrossSubsector((bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR), los); + + return P_CrossSubsector((bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR), los, funcs); } // @@ -387,6 +524,7 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) const sector_t *s1, *s2; size_t pnum; los_t los; + los_funcs_t funcs; // First check for trivial rejection. if (!t1 || !t2) @@ -423,6 +561,9 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) validcount++; + los.compareThing = t1; + los.alreadyHates = false; + los.topslope = (los.bottomslope = t2->z - (los.sightzstart = t1->z + t1->height - @@ -498,127 +639,19 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) } } + funcs.validate = &P_IsVisible; + funcs.validatePolyobj = &P_IsVisiblePolyObj; + // the head node is the last node output - return P_CrossBSPNode((INT32)numnodes - 1, &los); -} - -// -// P_TraceBlockingLines -// -// Returns true if a straight line between t1 and t2 is unobstructed. -// Unlike P_CheckSight, simplifed down to only check for explicit blocking lines on the 2D plane. -// Intended for Kart waypoints. -// Might be better in it's own file? -// - -typedef struct { - fixed_t t2x, t2y; - divline_t strace; // from t1 to t2 - fixed_t bbox[4]; - mobj_t *compareThing; - boolean alreadyHates; -} traceblocking_t; - -static boolean P_CrossBlockingSubsector(size_t num, register traceblocking_t *tb) -{ - seg_t *seg; - INT32 count; - -#ifdef RANGECHECK - if (num >= numsubsectors) - I_Error("P_CrossBlockingSubsector: ss %s with numss = %s\n", sizeu1(num), sizeu2(numsubsectors)); -#endif - - // haleyjd 02/23/06: this assignment should be after the above check - seg = segs + subsectors[num].firstline; - - for (count = subsectors[num].numlines; --count >= 0; seg++) // check lines - { - line_t *line = seg->linedef; - divline_t divl; - const vertex_t *v1,*v2; - - if (seg->glseg) - continue; - - // already checked other side? - if (line->validcount == validcount) - continue; - - line->validcount = validcount; - - // OPTIMIZE: killough 4/20/98: Added quick bounding-box rejection test - if (line->bbox[BOXLEFT ] > tb->bbox[BOXRIGHT ] || - line->bbox[BOXRIGHT ] < tb->bbox[BOXLEFT ] || - line->bbox[BOXBOTTOM] > tb->bbox[BOXTOP ] || - line->bbox[BOXTOP] < tb->bbox[BOXBOTTOM]) - continue; - - v1 = line->v1; - v2 = line->v2; - - // line isn't crossed? - if (P_DivlineSide(v1->x, v1->y, &tb->strace) == - P_DivlineSide(v2->x, v2->y, &tb->strace)) - continue; - - // stop because it is not two sided anyway - if (!(line->flags & ML_TWOSIDED)) - return false; - - divl.dx = v2->x - (divl.x = v1->x); - divl.dy = v2->y - (divl.y = v1->y); - - // line isn't crossed? - if (P_DivlineSide(tb->strace.x, tb->strace.y, &divl) == - P_DivlineSide(tb->t2x, tb->t2y, &divl)) - continue; - - if (P_IsLineBlocking(line, tb->compareThing) == true) - { - // This line will always block us - return false; - } - - if (tb->compareThing->player != NULL) - { - if (P_IsLineTripWire(line) == true && K_TripwirePass(tb->compareThing->player) == false) - { - // Can't go through trip wire. - return false; - } - } - } - - // passed the subsector ok - return true; -} - -static boolean P_CrossBSPNodeBlocking(INT32 bspnum, register traceblocking_t *tb) -{ - while (!(bspnum & NF_SUBSECTOR)) - { - register node_t *bsp = nodes + bspnum; - INT32 side = P_DivlineSide(tb->strace.x,tb->strace.y,(divline_t *)bsp)&1; - if (side == P_DivlineSide(tb->t2x, tb->t2y, (divline_t *) bsp)) - bspnum = bsp->children[side]; // doesn't touch the other side - else // the partition plane is crossed here - { - if (!P_CrossBSPNodeBlocking(bsp->children[side], tb)) - return false; // cross the starting side - else - bspnum = bsp->children[side^1]; // cross the ending side - } - } - - return P_CrossBlockingSubsector((bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR), tb); + return P_CrossBSPNode((INT32)numnodes - 1, &los, &funcs); } boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2) { const sector_t *s1, *s2; size_t pnum; - traceblocking_t tb; + los_t los; + los_funcs_t funcs; // First check for trivial rejection. if (!t1 || !t2) @@ -651,161 +684,38 @@ boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2) validcount++; - tb.strace.dx = (tb.t2x = t2->x) - (tb.strace.x = t1->x); - tb.strace.dy = (tb.t2y = t2->y) - (tb.strace.y = t1->y); + los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x); + los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y); if (t1->x > t2->x) - tb.bbox[BOXRIGHT] = t1->x, tb.bbox[BOXLEFT] = t2->x; + los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x; else - tb.bbox[BOXRIGHT] = t2->x, tb.bbox[BOXLEFT] = t1->x; + los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x; if (t1->y > t2->y) - tb.bbox[BOXTOP] = t1->y, tb.bbox[BOXBOTTOM] = t2->y; + los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y; else - tb.bbox[BOXTOP] = t2->y, tb.bbox[BOXBOTTOM] = t1->y; + los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y; - tb.compareThing = t1; - tb.alreadyHates = false; + los.compareThing = t1; + los.alreadyHates = false; + + funcs.validate = &P_CanTraceBlockingLine; // the head node is the last node output - return P_CrossBSPNodeBlocking((INT32)numnodes - 1, &tb); + return P_CrossBSPNode((INT32)numnodes - 1, &los, &funcs); } // // ANOTHER version, this time for bot traversal. -// (TODO: since we have so many versions of this function, the differences -// should maybe just be a function var that gets called?) // -static boolean P_CrossBotTraversalSubsector(size_t num, register traceblocking_t *tb) -{ - seg_t *seg; - INT32 count; - -#ifdef RANGECHECK - if (num >= numsubsectors) - I_Error("P_CrossBotTraversalSubsector: ss %s with numss = %s\n", sizeu1(num), sizeu2(numsubsectors)); -#endif - - // haleyjd 02/23/06: this assignment should be after the above check - seg = segs + subsectors[num].firstline; - - for (count = subsectors[num].numlines; --count >= 0; seg++) // check lines - { - line_t *line = seg->linedef; - divline_t divl; - const vertex_t *v1,*v2; - fixed_t maxstep = INT32_MAX; - - if (seg->glseg) - continue; - - // already checked other side? - if (line->validcount == validcount) - continue; - - line->validcount = validcount; - - // OPTIMIZE: killough 4/20/98: Added quick bounding-box rejection test - if (line->bbox[BOXLEFT ] > tb->bbox[BOXRIGHT ] || - line->bbox[BOXRIGHT ] < tb->bbox[BOXLEFT ] || - line->bbox[BOXBOTTOM] > tb->bbox[BOXTOP ] || - line->bbox[BOXTOP] < tb->bbox[BOXBOTTOM]) - continue; - - v1 = line->v1; - v2 = line->v2; - - // line isn't crossed? - if (P_DivlineSide(v1->x, v1->y, &tb->strace) == - P_DivlineSide(v2->x, v2->y, &tb->strace)) - continue; - - // stop because it is not two sided anyway - if (!(line->flags & ML_TWOSIDED)) - return false; - - divl.dx = v2->x - (divl.x = v1->x); - divl.dy = v2->y - (divl.y = v1->y); - - // line isn't crossed? - if (P_DivlineSide(tb->strace.x, tb->strace.y, &divl) == - P_DivlineSide(tb->t2x, tb->t2y, &divl)) - continue; - - if (P_IsLineBlocking(line, tb->compareThing) == true) - { - // This line will always block us - return false; - } - - if (tb->compareThing->player != NULL) - { - if (P_IsLineTripWire(line) == true && K_TripwirePass(tb->compareThing->player) == false) - { - // Can't go through trip wire. - return false; - } - } - - // set openrange, opentop, openbottom - tm.x = tb->compareThing->x; - tm.y = tb->compareThing->y; - P_LineOpening(line, tb->compareThing); - maxstep = P_GetThingStepUp(tb->compareThing, tm.x, tm.y); - - if ((openrange < tb->compareThing->height) // doesn't fit - || (opentop - tb->compareThing->z < tb->compareThing->height) // mobj is too high - || (openbottom - tb->compareThing->z > maxstep)) // too big a step up - { - // This line situationally blocks us - return false; - } - - if (tb->compareThing->player != NULL && tb->alreadyHates == false) - { - // Treat damage sectors like walls, if you're not already in a bad sector. - vertex_t pos; - P_ClosestPointOnLine(tb->compareThing->x, tb->compareThing->y, line, &pos); - - if (K_BotHatesThisSector(tb->compareThing->player, line->frontsector, pos.x, pos.y) - || K_BotHatesThisSector(tb->compareThing->player, line->backsector, pos.x, pos.y)) - { - // This line does not block us, but we don't want to be in it. - return false; - } - } - } - - // passed the subsector ok - return true; -} - -static boolean P_CrossBSPNodeBotTraversal(INT32 bspnum, register traceblocking_t *tb) -{ - while (!(bspnum & NF_SUBSECTOR)) - { - register node_t *bsp = nodes + bspnum; - INT32 side = P_DivlineSide(tb->strace.x,tb->strace.y,(divline_t *)bsp)&1; - if (side == P_DivlineSide(tb->t2x, tb->t2y, (divline_t *) bsp)) - bspnum = bsp->children[side]; // doesn't touch the other side - else // the partition plane is crossed here - { - if (!P_CrossBSPNodeBotTraversal(bsp->children[side], tb)) - return false; // cross the starting side - else - bspnum = bsp->children[side^1]; // cross the ending side - } - } - - return P_CrossBotTraversalSubsector((bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR), tb); -} - boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2) { const sector_t *s1, *s2; size_t pnum; - traceblocking_t tb; + los_t los; + los_funcs_t funcs; // First check for trivial rejection. if (!t1 || !t2) @@ -838,33 +748,35 @@ boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2) validcount++; - tb.strace.dx = (tb.t2x = t2->x) - (tb.strace.x = t1->x); - tb.strace.dy = (tb.t2y = t2->y) - (tb.strace.y = t1->y); + los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x); + los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y); if (t1->x > t2->x) - tb.bbox[BOXRIGHT] = t1->x, tb.bbox[BOXLEFT] = t2->x; + los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x; else - tb.bbox[BOXRIGHT] = t2->x, tb.bbox[BOXLEFT] = t1->x; + los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x; if (t1->y > t2->y) - tb.bbox[BOXTOP] = t1->y, tb.bbox[BOXBOTTOM] = t2->y; + los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y; else - tb.bbox[BOXTOP] = t2->y, tb.bbox[BOXBOTTOM] = t1->y; + los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y; - tb.compareThing = t1; + los.compareThing = t1; if (t1->player != NULL) { - tb.alreadyHates = K_BotHatesThisSector( + los.alreadyHates = K_BotHatesThisSector( t1->player, t1->subsector->sector, t1->x, t1->y ); } else { - tb.alreadyHates = false; + los.alreadyHates = false; } + funcs.validate = &P_CanBotTraverse; + // the head node is the last node output - return P_CrossBSPNodeBotTraversal((INT32)numnodes - 1, &tb); + return P_CrossBSPNode((INT32)numnodes - 1, &los, &funcs); } From 0053cda35c9f1b79d3352e5d2e4b02603a9e677c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 26 Nov 2022 02:31:11 -0500 Subject: [PATCH 4/7] Don't allow nextwaypoint to be past finish linedef --- src/k_kart.c | 121 +++++++---------------------------------------- src/k_waypoint.c | 4 +- src/p_local.h | 1 + src/p_sight.c | 79 +++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 107 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 8ca23b483..3fd42d6bf 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8848,6 +8848,7 @@ void K_KartPlayerAfterThink(player_t *player) --------------------------------------------------*/ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) { + waypoint_t *finishline = K_GetFinishLineWaypoint(); waypoint_t *bestwaypoint = NULL; if ((player != NULL) && (player->mo != NULL) && (P_MobjWasRemoved(player->mo) == false)) @@ -8864,13 +8865,15 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) // Otherwise it breaks the distance calculations. if (waypoint != NULL) { - boolean finishlinehack = false; angle_t playerangle = player->mo->angle; angle_t momangle = K_MomentumAngle(player->mo); angle_t angletowaypoint = R_PointToAngle2(player->mo->x, player->mo->y, waypoint->mobj->x, waypoint->mobj->y); - angle_t angledelta = ANGLE_MAX; - angle_t momdelta = ANGLE_MAX; + angle_t angledelta = ANGLE_180; + angle_t momdelta = ANGLE_180; + + // Remove WRONG WAY flag. + player->pflags &= ~PF_WRONGWAY; angledelta = playerangle - angletowaypoint; if (angledelta > ANGLE_180) @@ -8884,31 +8887,15 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) momdelta = InvAngle(momdelta); } - if (bestwaypoint == K_GetFinishLineWaypoint()) - { - waypoint_t *nextwaypoint = waypoint->nextwaypoints[0]; - angle_t angletonextwaypoint = - R_PointToAngle2(waypoint->mobj->x, waypoint->mobj->y, nextwaypoint->mobj->x, nextwaypoint->mobj->y); - - // facing towards the finishline - if (AngleDelta(angletonextwaypoint, angletowaypoint) <= ANGLE_90) - { - finishlinehack = true; - } - } - // We're using a lot of angle calculations here, because only using facing angle or only using momentum angle both have downsides. // nextwaypoints will be picked if you're facing OR moving forward. // prevwaypoints will be picked if you're facing AND moving backward. - if ( #if 0 - (angledelta > ANGLE_45 || momdelta > ANGLE_45) && + if (angledelta > ANGLE_45 || momdelta > ANGLE_45) #endif - (finishlinehack == false) - ) { - angle_t nextbestdelta = ANGLE_MAX; - angle_t nextbestmomdelta = ANGLE_MAX; + angle_t nextbestdelta = ANGLE_90; + angle_t nextbestmomdelta = ANGLE_90; size_t i = 0U; if ((waypoint->nextwaypoints != NULL) && (waypoint->numnextwaypoints > 0U)) @@ -8952,10 +8939,13 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) if (angledelta < nextbestdelta || momdelta < nextbestmomdelta) { - if (P_TraceBlockingLines(player->mo, waypoint->nextwaypoints[i]->mobj) == false) + if (waypoint->nextwaypoints[i] != finishline) // Allow finish line. { - // Save sight checks when all of the other checks pass, so we only do it if we have to - continue; + if (P_TraceWaypointTraversal(player->mo, waypoint->nextwaypoints[i]->mobj) == false) + { + // Save sight checks when all of the other checks pass, so we only do it if we have to + continue; + } } bestwaypoint = waypoint->nextwaypoints[i]; @@ -8969,8 +8959,6 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) nextbestmomdelta = momdelta; } - // Remove wrong way flag if we're using nextwaypoints - player->pflags &= ~PF_WRONGWAY; updaterespawn = true; } } @@ -9004,7 +8992,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) if (angledelta < nextbestdelta && momdelta < nextbestmomdelta) { - if (P_TraceBlockingLines(player->mo, waypoint->prevwaypoints[i]->mobj) == false) + if (P_TraceWaypointTraversal(player->mo, waypoint->prevwaypoints[i]->mobj) == false) { // Save sight checks when all of the other checks pass, so we only do it if we have to continue; @@ -9044,69 +9032,6 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) return bestwaypoint; } -#if 0 -static boolean K_PlayerCloserToNextWaypoints(waypoint_t *const waypoint, player_t *const player) -{ - boolean nextiscloser = true; - - if ((waypoint != NULL) && (player != NULL) && (player->mo != NULL)) - { - size_t i = 0U; - waypoint_t *currentwpcheck = NULL; - angle_t angletoplayer = ANGLE_MAX; - angle_t currentanglecheck = ANGLE_MAX; - angle_t bestangle = ANGLE_MAX; - - angletoplayer = R_PointToAngle2(waypoint->mobj->x, waypoint->mobj->y, - player->mo->x, player->mo->y); - - for (i = 0U; i < waypoint->numnextwaypoints; i++) - { - currentwpcheck = waypoint->nextwaypoints[i]; - currentanglecheck = R_PointToAngle2( - waypoint->mobj->x, waypoint->mobj->y, currentwpcheck->mobj->x, currentwpcheck->mobj->y); - - // Get delta angle - currentanglecheck = currentanglecheck - angletoplayer; - - if (currentanglecheck > ANGLE_180) - { - currentanglecheck = InvAngle(currentanglecheck); - } - - if (currentanglecheck < bestangle) - { - bestangle = currentanglecheck; - } - } - - for (i = 0U; i < waypoint->numprevwaypoints; i++) - { - currentwpcheck = waypoint->prevwaypoints[i]; - currentanglecheck = R_PointToAngle2( - waypoint->mobj->x, waypoint->mobj->y, currentwpcheck->mobj->x, currentwpcheck->mobj->y); - - // Get delta angle - currentanglecheck = currentanglecheck - angletoplayer; - - if (currentanglecheck > ANGLE_180) - { - currentanglecheck = InvAngle(currentanglecheck); - } - - if (currentanglecheck < bestangle) - { - bestangle = currentanglecheck; - nextiscloser = false; - break; - } - } - } - - return nextiscloser; -} -#endif - /*-------------------------------------------------- void K_UpdateDistanceFromFinishLine(player_t *const player) @@ -9302,21 +9227,7 @@ void K_UpdateDistanceFromFinishLine(player_t *const player) if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == 0U) { const UINT8 numfulllapsleft = ((UINT8)numlaps - player->laps); - player->distancetofinish += numfulllapsleft * K_GetCircuitLength(); - -#if 0 - // An additional HACK, to fix looking backwards towards the finish line - // If the player's next waypoint is the finishline and the angle distance from player to - // connectin waypoints implies they're closer to a next waypoint, add a full track distance - if (player->nextwaypoint == finishline) - { - if (K_PlayerCloserToNextWaypoints(player->nextwaypoint, player) == true) - { - player->distancetofinish += K_GetCircuitLength(); - } - } -#endif } } } diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 28ff00d04..837f77102 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -361,7 +361,7 @@ waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj) // remember: huge radius if (closestdist <= rad && checkdist <= rad && finishline != NULL) { - if (!P_TraceBlockingLines(mobj, checkwaypoint->mobj)) + if (!P_TraceBlockingLines(mobj, checkwaypoint->mobj)) // Intentionally not P_TraceWaypointTraversal { // Save sight checks when all of the other checks pass, so we only do it if we have to continue; @@ -379,7 +379,7 @@ waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj) } else if (checkdist < closestdist && bestfindist == INT32_MAX) { - if (!P_TraceBlockingLines(mobj, checkwaypoint->mobj)) + if (!P_TraceBlockingLines(mobj, checkwaypoint->mobj)) // Intentionally not P_TraceWaypointTraversal { // Save sight checks when all of the other checks pass, so we only do it if we have to continue; diff --git a/src/p_local.h b/src/p_local.h index 1042f1193..9bc92cb64 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -456,6 +456,7 @@ void P_BounceMove(mobj_t *mo, TryMoveResult_t *result); boolean P_CheckSight(mobj_t *t1, mobj_t *t2); boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2); boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2); +boolean P_TraceWaypointTraversal(mobj_t *t1, mobj_t *t2); void P_CheckHoopPosition(mobj_t *hoopthing, fixed_t x, fixed_t y, fixed_t z, fixed_t radius); boolean P_CheckSector(sector_t *sector, boolean crunch); diff --git a/src/p_sight.c b/src/p_sight.c index 50b22243d..b582931f6 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -392,6 +392,26 @@ static boolean P_CanBotTraverse(seg_t *seg, divline_t *divl, register los_t *los return true; } +static boolean P_CanWaypointTraverse(seg_t *seg, divline_t *divl, register los_t *los) +{ + line_t *line = seg->linedef; + + if (P_CanTraceBlockingLine(seg, divl, los) == false) + { + // Blocked, so obviously can't traverse either. + return false; + } + + if (line->special == 2001) + { + // Don't allow through the finish linedef. + // Causes some janky behavior. + return false; + } + + return true; +} + // // P_CrossSubsector // @@ -780,3 +800,62 @@ boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2) return P_CrossBSPNode((INT32)numnodes - 1, &los, &funcs); } +boolean P_TraceWaypointTraversal(mobj_t *t1, mobj_t *t2) +{ + const sector_t *s1, *s2; + size_t pnum; + los_t los; + los_funcs_t funcs; + + // First check for trivial rejection. + if (!t1 || !t2) + return false; + + I_Assert(!P_MobjWasRemoved(t1)); + I_Assert(!P_MobjWasRemoved(t2)); + + if (!t1->subsector || !t2->subsector + || !t1->subsector->sector || !t2->subsector->sector) + return false; + + s1 = t1->subsector->sector; + s2 = t2->subsector->sector; + pnum = (s1-sectors)*numsectors + (s2-sectors); + + if (rejectmatrix != NULL) + { + // Check in REJECT table. + if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected + return false; + } + + // killough 11/98: shortcut for melee situations + // same subsector? obviously visible + // haleyjd 02/23/06: can't do this if there are polyobjects in the subsec + if (!t1->subsector->polyList && + t1->subsector == t2->subsector) + return true; + + validcount++; + + los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x); + los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y); + + if (t1->x > t2->x) + los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x; + else + los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x; + + if (t1->y > t2->y) + los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y; + else + los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y; + + los.compareThing = t1; + los.alreadyHates = false; + + funcs.validate = &P_CanWaypointTraverse; + + // the head node is the last node output + return P_CrossBSPNode((INT32)numnodes - 1, &los, &funcs); +} From fdd016eafc21c8701cd2e8ba781f4289d1888cd1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 26 Nov 2022 02:39:49 -0500 Subject: [PATCH 5/7] Smarter WRONG WAY again --- src/k_kart.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 3fd42d6bf..deff7b3ee 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8872,9 +8872,6 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) angle_t angledelta = ANGLE_180; angle_t momdelta = ANGLE_180; - // Remove WRONG WAY flag. - player->pflags &= ~PF_WRONGWAY; - angledelta = playerangle - angletowaypoint; if (angledelta > ANGLE_180) { @@ -8939,6 +8936,12 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) if (angledelta < nextbestdelta || momdelta < nextbestmomdelta) { + // Wanted to use a next waypoint, so remove WRONG WAY flag. + // Done here instead of when set, because of finish line + // hacks meaning we might not actually use this one, but + // we still want to acknowledge we're facing the right way. + player->pflags &= ~PF_WRONGWAY; + if (waypoint->nextwaypoints[i] != finishline) // Allow finish line. { if (P_TraceWaypointTraversal(player->mo, waypoint->nextwaypoints[i]->mobj) == false) @@ -9166,6 +9169,7 @@ void K_UpdateDistanceFromFinishLine(player_t *const player) } } +#if 0 if (cv_kartdebugwaypoints.value) { mobj_t *debugmobj = P_SpawnMobj(best.x, best.y, best.z, MT_SPARK); @@ -9174,9 +9178,10 @@ void K_UpdateDistanceFromFinishLine(player_t *const player) debugmobj->frame &= ~FF_TRANSMASK; debugmobj->frame |= FF_FULLBRIGHT; //FF_TRANS20 - debugmobj->tics = 2; + debugmobj->tics = 1; debugmobj->color = SKINCOLOR_BANANA; } +#endif adddist = bestGScore; } From eef87c4676bb16f1c109069b7b5a41f34388a262 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 26 Nov 2022 03:15:03 -0500 Subject: [PATCH 6/7] Remove even more code dupe from p_sight.c --- src/p_sight.c | 294 ++++++++++++++++---------------------------------- 1 file changed, 90 insertions(+), 204 deletions(-) diff --git a/src/p_sight.c b/src/p_sight.c index b582931f6..e96bf9db8 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -38,11 +38,13 @@ typedef struct boolean alreadyHates; // For bot traversal, for if the bot is already in a sector it doesn't want to be } los_t; +typedef boolean (*los_init_t)(mobj_t *, mobj_t *, register los_t *); typedef boolean (*los_valid_t)(seg_t *, divline_t *, register los_t *); typedef boolean (*los_valid_poly_t)(polyobj_t *, divline_t *, register los_t *); typedef struct { + los_init_t init; // Initialization function. If true, we'll continue with checking across linedefs. If false, end early with failure. los_valid_t validate; // Validation function. If true, continue iterating for possible success. If false, end early with failure. los_valid_poly_t validatePolyobj; // If not NULL, then we will also check polyobject lines using this func. } los_funcs_t; @@ -507,11 +509,6 @@ static boolean P_CrossSubsector(size_t num, register los_t *los, register los_fu static boolean P_CrossBSPNode(INT32 bspnum, register los_t *los, register los_funcs_t *funcs) { - if (funcs->validate == NULL) - { - return false; - } - while (!(bspnum & NF_SUBSECTOR)) { register node_t *bsp = nodes + bspnum; @@ -533,79 +530,22 @@ static boolean P_CrossBSPNode(INT32 bspnum, register los_t *los, register los_fu return P_CrossSubsector((bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR), los, funcs); } -// -// P_CheckSight -// -// Returns true if a straight line between t1 and t2 is unobstructed. -// Uses REJECT. -// -boolean P_CheckSight(mobj_t *t1, mobj_t *t2) +static boolean P_InitCheckSight(mobj_t *t1, mobj_t *t2, register los_t *los) { const sector_t *s1, *s2; - size_t pnum; - los_t los; - los_funcs_t funcs; - - // First check for trivial rejection. - if (!t1 || !t2) - return false; - - I_Assert(!P_MobjWasRemoved(t1)); - I_Assert(!P_MobjWasRemoved(t2)); - - if (!t1->subsector || !t2->subsector - || !t1->subsector->sector || !t2->subsector->sector) - return false; - - s1 = t1->subsector->sector; - s2 = t2->subsector->sector; - pnum = (s1-sectors)*numsectors + (s2-sectors); - - if (rejectmatrix != NULL) - { - // Check in REJECT table. - if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected - return false; - } - - // killough 11/98: shortcut for melee situations - // same subsector? obviously visible - // haleyjd 02/23/06: can't do this if there are polyobjects in the subsec - if (!t1->subsector->polyList && - t1->subsector == t2->subsector) - return true; // An unobstructed LOS is possible. // Now look from eyes of t1 to any part of t2. sightcounts[1]++; - validcount++; - - los.compareThing = t1; - los.alreadyHates = false; - - los.topslope = - (los.bottomslope = t2->z - (los.sightzstart = - t1->z + t1->height - - (t1->height>>2))) + t2->height; - los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x); - los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y); - - if (t1->x > t2->x) - los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x; - else - los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x; - - if (t1->y > t2->y) - los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y; - else - los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y; - // Prevent SOME cases of looking through 3dfloors // // This WILL NOT work for things like 3d stairs with monsters behind // them - they will still see you! TODO: Fix. // + s1 = t1->subsector->sector; + s2 = t2->subsector->sector; + if (s1 == s2) // Both sectors are the same. { ffloor_t *rover; @@ -629,8 +569,8 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) bottomz2 = P_GetFFloorBottomZAt(rover, t2->x, t2->y); // Check for blocking floors here. - if ((los.sightzstart < bottomz1 && t2->z >= topz2) - || (los.sightzstart >= topz1 && t2->z + t2->height < bottomz2)) + if ((los->sightzstart < bottomz1 && t2->z >= topz2) + || (los->sightzstart >= topz1 && t2->z + t2->height < bottomz2)) { // no way to see through that return false; @@ -641,182 +581,66 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) if (rover->fofflags & FOF_BOTHPLANES || !(rover->fofflags & FOF_INVERTPLANES)) { - if (los.sightzstart >= topz1 && t2->z + t2->height < topz2) + if (los->sightzstart >= topz1 && t2->z + t2->height < topz2) return false; // blocked by upper outside plane - if (los.sightzstart < bottomz1 && t2->z >= bottomz2) + if (los->sightzstart < bottomz1 && t2->z >= bottomz2) return false; // blocked by lower outside plane } if (rover->fofflags & FOF_BOTHPLANES || rover->fofflags & FOF_INVERTPLANES) { - if (los.sightzstart < topz1 && t2->z >= topz2) + if (los->sightzstart < topz1 && t2->z >= topz2) return false; // blocked by upper inside plane - if (los.sightzstart >= bottomz1 && t2->z + t2->height < bottomz2) + if (los->sightzstart >= bottomz1 && t2->z + t2->height < bottomz2) return false; // blocked by lower inside plane } } } - funcs.validate = &P_IsVisible; - funcs.validatePolyobj = &P_IsVisiblePolyObj; - - // the head node is the last node output - return P_CrossBSPNode((INT32)numnodes - 1, &los, &funcs); + return true; } -boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2) +static boolean P_InitTraceBotTraversal(mobj_t *t1, mobj_t *t2, register los_t *los) { - const sector_t *s1, *s2; - size_t pnum; - los_t los; - los_funcs_t funcs; + (void)t2; - // First check for trivial rejection. - if (!t1 || !t2) - return false; - - I_Assert(!P_MobjWasRemoved(t1)); - I_Assert(!P_MobjWasRemoved(t2)); - - if (!t1->subsector || !t2->subsector - || !t1->subsector->sector || !t2->subsector->sector) - return false; - - s1 = t1->subsector->sector; - s2 = t2->subsector->sector; - pnum = (s1-sectors)*numsectors + (s2-sectors); - - if (rejectmatrix != NULL) - { - // Check in REJECT table. - if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected - return false; - } - - // killough 11/98: shortcut for melee situations - // same subsector? obviously visible - // haleyjd 02/23/06: can't do this if there are polyobjects in the subsec - if (!t1->subsector->polyList && - t1->subsector == t2->subsector) - return true; - - validcount++; - - los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x); - los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y); - - if (t1->x > t2->x) - los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x; - else - los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x; - - if (t1->y > t2->y) - los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y; - else - los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y; - - los.compareThing = t1; - los.alreadyHates = false; - - funcs.validate = &P_CanTraceBlockingLine; - - // the head node is the last node output - return P_CrossBSPNode((INT32)numnodes - 1, &los, &funcs); -} - -// -// ANOTHER version, this time for bot traversal. -// - -boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2) -{ - const sector_t *s1, *s2; - size_t pnum; - los_t los; - los_funcs_t funcs; - - // First check for trivial rejection. - if (!t1 || !t2) - return false; - - I_Assert(!P_MobjWasRemoved(t1)); - I_Assert(!P_MobjWasRemoved(t2)); - - if (!t1->subsector || !t2->subsector - || !t1->subsector->sector || !t2->subsector->sector) - return false; - - s1 = t1->subsector->sector; - s2 = t2->subsector->sector; - pnum = (s1-sectors)*numsectors + (s2-sectors); - - if (rejectmatrix != NULL) - { - // Check in REJECT table. - if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected - return false; - } - - // killough 11/98: shortcut for melee situations - // same subsector? obviously visible - // haleyjd 02/23/06: can't do this if there are polyobjects in the subsec - if (!t1->subsector->polyList && - t1->subsector == t2->subsector) - return true; - - validcount++; - - los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x); - los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y); - - if (t1->x > t2->x) - los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x; - else - los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x; - - if (t1->y > t2->y) - los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y; - else - los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y; - - los.compareThing = t1; if (t1->player != NULL) { - los.alreadyHates = K_BotHatesThisSector( + los->alreadyHates = K_BotHatesThisSector( t1->player, t1->subsector->sector, t1->x, t1->y ); } else { - los.alreadyHates = false; + los->alreadyHates = false; } - funcs.validate = &P_CanBotTraverse; - - // the head node is the last node output - return P_CrossBSPNode((INT32)numnodes - 1, &los, &funcs); + return true; } -boolean P_TraceWaypointTraversal(mobj_t *t1, mobj_t *t2) +static boolean P_CompareMobjsAcrossLines(mobj_t *t1, mobj_t *t2, register los_funcs_t *funcs) { + los_t los; const sector_t *s1, *s2; size_t pnum; - los_t los; - los_funcs_t funcs; // First check for trivial rejection. if (!t1 || !t2) + { return false; + } I_Assert(!P_MobjWasRemoved(t1)); I_Assert(!P_MobjWasRemoved(t2)); if (!t1->subsector || !t2->subsector || !t1->subsector->sector || !t2->subsector->sector) + { return false; + } s1 = t1->subsector->sector; s2 = t2->subsector->sector; @@ -826,7 +650,9 @@ boolean P_TraceWaypointTraversal(mobj_t *t1, mobj_t *t2) { // Check in REJECT table. if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected + { return false; + } } // killough 11/98: shortcut for melee situations @@ -834,10 +660,19 @@ boolean P_TraceWaypointTraversal(mobj_t *t1, mobj_t *t2) // haleyjd 02/23/06: can't do this if there are polyobjects in the subsec if (!t1->subsector->polyList && t1->subsector == t2->subsector) + { return true; + } validcount++; + los.compareThing = t1; + los.alreadyHates = false; + + los.topslope = + (los.bottomslope = t2->z - (los.sightzstart = + t1->z + t1->height - + (t1->height>>2))) + t2->height; los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x); los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y); @@ -851,11 +686,62 @@ boolean P_TraceWaypointTraversal(mobj_t *t1, mobj_t *t2) else los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y; - los.compareThing = t1; - los.alreadyHates = false; + if (funcs->init != NULL) + { + if (funcs->init(t1, t2, &los) == false) + { + return false; + } + } + + // The only required function. + I_Assert(funcs->validate != NULL); + + // the head node is the last node output + return P_CrossBSPNode((INT32)numnodes - 1, &los, funcs); +} + +// +// P_CheckSight +// +// Returns true if a straight line between t1 and t2 is unobstructed. +// Uses REJECT. +// +boolean P_CheckSight(mobj_t *t1, mobj_t *t2) +{ + los_funcs_t funcs = {0}; + + funcs.init = &P_InitCheckSight; + funcs.validate = &P_IsVisible; + funcs.validatePolyobj = &P_IsVisiblePolyObj; + + return P_CompareMobjsAcrossLines(t1, t2, &funcs); +} + +boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2) +{ + los_funcs_t funcs = {0}; + + funcs.validate = &P_CanTraceBlockingLine; + + return P_CompareMobjsAcrossLines(t1, t2, &funcs); +} + +boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2) +{ + los_funcs_t funcs = {0}; + + funcs.init = &P_InitTraceBotTraversal; + funcs.validate = &P_CanBotTraverse; + + return P_CompareMobjsAcrossLines(t1, t2, &funcs); +} + +boolean P_TraceWaypointTraversal(mobj_t *t1, mobj_t *t2) +{ + los_funcs_t funcs = {0}; funcs.validate = &P_CanWaypointTraverse; - // the head node is the last node output - return P_CrossBSPNode((INT32)numnodes - 1, &los, &funcs); + return P_CompareMobjsAcrossLines(t1, t2, &funcs); } From 0e654aa2f9d2fb476715e6b53160220efc67fb96 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 26 Nov 2022 15:27:51 -0500 Subject: [PATCH 7/7] Fix missing currentwaypoint in saveg --- src/p_saveg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_saveg.c b/src/p_saveg.c index 65b2d81a3..3d379879e 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -249,6 +249,7 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].oldposition); WRITEUINT8(save_p, players[i].positiondelay); WRITEUINT32(save_p, players[i].distancetofinish); + WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].currentwaypoint)); WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].nextwaypoint)); WRITEUINT32(save_p, players[i].airtime); WRITEUINT8(save_p, players[i].startboost); @@ -548,6 +549,7 @@ static void P_NetUnArchivePlayers(void) players[i].oldposition = READUINT8(save_p); players[i].positiondelay = READUINT8(save_p); players[i].distancetofinish = READUINT32(save_p); + players[i].currentwaypoint = (waypoint_t *)(size_t)READUINT32(save_p); players[i].nextwaypoint = (waypoint_t *)(size_t)READUINT32(save_p); players[i].airtime = READUINT32(save_p); players[i].startboost = READUINT8(save_p);