diff --git a/src/doomstat.h b/src/doomstat.h index c283c5674..57ea3e049 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -631,6 +631,7 @@ mobj_t *P_GetLastWaypoint(UINT8 sequence); mobj_t *P_GetPreviousWaypoint(mobj_t *current, boolean wrap); mobj_t *P_GetNextWaypoint(mobj_t *current, boolean wrap); mobj_t *P_GetClosestWaypoint(UINT8 sequence, mobj_t *mo); +boolean P_IsDegeneratedWaypointSequence(UINT8 sequence); // ===================================== // Internal parameters, used for engine. diff --git a/src/p_polyobj.c b/src/p_polyobj.c index b1da51462..3b6195285 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -1569,15 +1569,39 @@ void T_PolyObjMove(polymove_t *th) } } +static void T_MovePolyObj(polyobj_t *po, fixed_t distx, fixed_t disty, fixed_t distz) +{ + polyobj_t *child; + INT32 start; + + Polyobj_moveXY(po, distx, disty, true); + // TODO: use T_MovePlane + po->lines[0]->backsector->floorheight += distz; + po->lines[0]->backsector->ceilingheight += distz; + // Sal: Remember to check your sectors! + // Monster Iestyn: we only need to bother with the back sector, now that P_CheckSector automatically checks the blockmap + // updating objects in the front one too just added teleporting to ground bugs + P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage)); + // Apply action to mirroring polyobjects as well + start = 0; + while ((child = Polyobj_GetChild(po, &start))) + { + if (child->isBad) + continue; + + Polyobj_moveXY(child, distx, disty, true); + // TODO: use T_MovePlane + child->lines[0]->backsector->floorheight += distz; + child->lines[0]->backsector->ceilingheight += distz; + P_CheckSector(child->lines[0]->backsector, (boolean)(child->damage)); + } +} + void T_PolyObjWaypoint(polywaypoint_t *th) { mobj_t *target = NULL; - mobj_t *waypoint = NULL; - fixed_t adjustx, adjusty, adjustz; - fixed_t momx, momy, momz, dist; - INT32 start; polyobj_t *po = Polyobj_GetForNum(th->polyObjNum); - polyobj_t *oldpo = po; + fixed_t speed = th->speed; if (!po) #ifdef RANGECHECK @@ -1594,7 +1618,7 @@ void T_PolyObjWaypoint(polywaypoint_t *th) if (!po->thinker) po->thinker = &th->thinker; - target = th->target; + target = waypoints[th->sequence][th->pointnum]; if (!target) { @@ -1602,152 +1626,93 @@ void T_PolyObjWaypoint(polywaypoint_t *th) return; } - // Compensate for position offset - adjustx = po->centerPt.x + th->diffx; - adjusty = po->centerPt.y + th->diffy; - adjustz = po->lines[0]->backsector->floorheight + (po->lines[0]->backsector->ceilingheight - po->lines[0]->backsector->floorheight)/2 + th->diffz; - - dist = P_AproxDistance(P_AproxDistance(target->x - adjustx, target->y - adjusty), target->z - adjustz); - - if (dist < 1) - dist = 1; - - momx = FixedMul(FixedDiv(target->x - adjustx, dist), (th->speed)); - momy = FixedMul(FixedDiv(target->y - adjusty, dist), (th->speed)); - momz = FixedMul(FixedDiv(target->z - adjustz, dist), (th->speed)); - - // Calculate the distance between the polyobject and the waypoint - // 'dist' already equals this. - - // Will the polyobject be FURTHER away if the momx/momy/momz is added to - // its current coordinates, or closer? (shift down to fracunits to avoid approximation errors) - if (dist>>FRACBITS <= P_AproxDistance(P_AproxDistance(target->x - adjustx - momx, target->y - adjusty - momy), target->z - adjustz - momz)>>FRACBITS) + // Move along the waypoint sequence until speed for the current tic is exhausted + while (speed > 0) { - // If further away, set XYZ of polyobject to waypoint location - fixed_t amtx, amty, amtz; - fixed_t diffz; - amtx = (target->x - th->diffx) - po->centerPt.x; - amty = (target->y - th->diffy) - po->centerPt.y; - Polyobj_moveXY(po, amtx, amty, true); - // TODO: use T_MovePlane - amtz = (po->lines[0]->backsector->ceilingheight - po->lines[0]->backsector->floorheight)/2; - diffz = po->lines[0]->backsector->floorheight - (target->z - amtz); - po->lines[0]->backsector->floorheight = target->z - amtz; - po->lines[0]->backsector->ceilingheight = target->z + amtz; - // Sal: Remember to check your sectors! - // Monster Iestyn: we only need to bother with the back sector, now that P_CheckSector automatically checks the blockmap - // updating objects in the front one too just added teleporting to ground bugs - P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage)); - // Apply action to mirroring polyobjects as well - start = 0; - while ((po = Polyobj_GetChild(oldpo, &start))) + mobj_t *waypoint = NULL; + fixed_t pox, poy, poz; + fixed_t distx, disty, distz, dist; + + // Current position of polyobject + pox = po->centerPt.x; + poy = po->centerPt.y; + poz = (po->lines[0]->backsector->floorheight + po->lines[0]->backsector->ceilingheight)/2; + + // Calculate the distance between the polyobject and the waypoint + distx = target->x - pox; + disty = target->y - poy; + distz = target->z - poz; + dist = P_AproxDistance(P_AproxDistance(distx, disty), distz); + + if (dist < 1) + dist = 1; + + // Will the polyobject overshoot its target? + if (speed < dist) { - if (po->isBad) - continue; + // No. Move towards waypoint + fixed_t momx, momy, momz; - Polyobj_moveXY(po, amtx, amty, true); - // TODO: use T_MovePlane - po->lines[0]->backsector->floorheight += diffz; // move up/down by same amount as the parent did - po->lines[0]->backsector->ceilingheight += diffz; - // Sal: Remember to check your sectors! - // Monster Iestyn: we only need to bother with the back sector, now that P_CheckSector automatically checks the blockmap - // updating objects in the front one too just added teleporting to ground bugs - P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage)); - } - - po = oldpo; - - if (!th->stophere) - { - CONS_Debug(DBG_POLYOBJ, "Looking for next waypoint...\n"); - waypoint = (th->direction == -1) ? P_GetPreviousWaypoint(target, false) : P_GetNextWaypoint(target, false); - - if (!waypoint && th->wrap) // If specified, wrap waypoints - { - if (!th->continuous) - { - th->wrap = 0; - th->stophere = true; - } - - waypoint = (th->direction == -1) ? P_GetLastWaypoint(th->sequence) : P_GetFirstWaypoint(th->sequence); - } - else if (!waypoint && th->comeback) // Come back to the start - { - th->direction = -th->direction; - - if (!th->continuous) - th->comeback = false; - - waypoint = (th->direction == -1) ? P_GetPreviousWaypoint(target, false) : P_GetNextWaypoint(target, false); - } - } - - if (waypoint) - { - CONS_Debug(DBG_POLYOBJ, "Found waypoint (sequence %d, number %d).\n", waypoint->threshold, waypoint->health); - - target = waypoint; - th->pointnum = target->health; - // Set the mobj as your target! -- Monster Iestyn 27/12/19 - P_SetTarget(&th->target, target); - - // calculate MOMX/MOMY/MOMZ for next waypoint - // change slope - dist = P_AproxDistance(P_AproxDistance(target->x - adjustx, target->y - adjusty), target->z - adjustz); - - if (dist < 1) - dist = 1; - - momx = FixedMul(FixedDiv(target->x - adjustx, dist), (th->speed)); - momy = FixedMul(FixedDiv(target->y - adjusty, dist), (th->speed)); - momz = FixedMul(FixedDiv(target->z - adjustz, dist), (th->speed)); + momx = FixedMul(FixedDiv(target->x - pox, dist), speed); + momy = FixedMul(FixedDiv(target->y - poy, dist), speed); + momz = FixedMul(FixedDiv(target->z - poz, dist), speed); + T_MovePolyObj(po, momx, momy, momz); + return; } else { - momx = momy = momz = 0; + // Yes. Teleport to waypoint and look for the next one + T_MovePolyObj(po, distx, disty, distz); if (!th->stophere) - CONS_Debug(DBG_POLYOBJ, "Next waypoint not found!\n"); + { + CONS_Debug(DBG_POLYOBJ, "Looking for next waypoint...\n"); + waypoint = (th->direction == -1) ? P_GetPreviousWaypoint(target, false) : P_GetNextWaypoint(target, false); - if (po->thinker == &th->thinker) - po->thinker = NULL; + if (!waypoint && th->returnbehavior == PWR_WRAP) // If specified, wrap waypoints + { + if (!th->continuous) + { + th->returnbehavior = PWR_STOP; + th->stophere = true; + } - P_RemoveThinker(&th->thinker); - return; + waypoint = (th->direction == -1) ? P_GetLastWaypoint(th->sequence) : P_GetFirstWaypoint(th->sequence); + } + else if (!waypoint && th->returnbehavior == PWR_COMEBACK) // Come back to the start + { + th->direction = -th->direction; + + if (!th->continuous) + th->returnbehavior = PWR_STOP; + + waypoint = (th->direction == -1) ? P_GetPreviousWaypoint(target, false) : P_GetNextWaypoint(target, false); + } + } + + if (waypoint) + { + CONS_Debug(DBG_POLYOBJ, "Found waypoint (sequence %d, number %d).\n", waypoint->threshold, waypoint->health); + + target = waypoint; + th->pointnum = target->health; + + // Calculate remaining speed + speed -= dist; + } + else + { + if (!th->stophere) + CONS_Debug(DBG_POLYOBJ, "Next waypoint not found!\n"); + + if (po->thinker == &th->thinker) + po->thinker = NULL; + + P_RemoveThinker(&th->thinker); + return; + } } } - else - { - // momx/momy/momz already equals the right speed - } - - // Move the polyobject - Polyobj_moveXY(po, momx, momy, true); - // TODO: use T_MovePlane - po->lines[0]->backsector->floorheight += momz; - po->lines[0]->backsector->ceilingheight += momz; - // Sal: Remember to check your sectors! - // Monster Iestyn: we only need to bother with the back sector, now that P_CheckSector automatically checks the blockmap - // updating objects in the front one too just added teleporting to ground bugs - P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage)); - - // Apply action to mirroring polyobjects as well - start = 0; - while ((po = Polyobj_GetChild(oldpo, &start))) - { - if (po->isBad) - continue; - - Polyobj_moveXY(po, momx, momy, true); - // TODO: use T_MovePlane - po->lines[0]->backsector->floorheight += momz; - po->lines[0]->backsector->ceilingheight += momz; - // Sal: Remember to check your sectors! - // Monster Iestyn: we only need to bother with the back sector, now that P_CheckSector automatically checks the blockmap - // updating objects in the front one too just added teleporting to ground bugs - P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage)); - } } void T_PolyDoorSlide(polyslidedoor_t *th) @@ -2166,8 +2131,6 @@ boolean EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata) polyobj_t *po; polywaypoint_t *th; mobj_t *first = NULL; - mobj_t *last = NULL; - mobj_t *target = NULL; if (!(po = Polyobj_GetForNum(pwdata->polyObjNum))) { @@ -2191,17 +2154,16 @@ boolean EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata) // set fields th->polyObjNum = pwdata->polyObjNum; th->speed = pwdata->speed; - th->sequence = pwdata->sequence; // Used to specify sequence # - th->direction = pwdata->reverse ? -1 : 1; + th->sequence = pwdata->sequence; + th->direction = (pwdata->flags & PWF_REVERSE) ? -1 : 1; - th->comeback = pwdata->comeback; - th->continuous = pwdata->continuous; - th->wrap = pwdata->wrap; + th->returnbehavior = pwdata->returnbehavior; + if (pwdata->flags & PWF_LOOP) + th->continuous = true; th->stophere = false; // Find the first waypoint we need to use first = (th->direction == -1) ? P_GetLastWaypoint(th->sequence) : P_GetFirstWaypoint(th->sequence); - last = (th->direction == -1) ? P_GetFirstWaypoint(th->sequence) : P_GetLastWaypoint(th->sequence); if (!first) { @@ -2211,47 +2173,16 @@ boolean EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata) return false; } - // Hotfix to not crash on single-waypoint sequences -Red - if (!last) - last = first; - - // Set diffx, diffy, diffz - // Put these at 0 for now...might not be needed after all. - th->diffx = 0;//first->x - po->centerPt.x; - th->diffy = 0;//first->y - po->centerPt.y; - th->diffz = 0;//first->z - (po->lines[0]->backsector->floorheight + (po->lines[0]->backsector->ceilingheight - po->lines[0]->backsector->floorheight)/2); - - if (last->x == po->centerPt.x - && last->y == po->centerPt.y - && last->z == (po->lines[0]->backsector->floorheight + (po->lines[0]->backsector->ceilingheight - po->lines[0]->backsector->floorheight)/2)) + // Sanity check: If all waypoints are in the same location, + // don't allow the movement to be continuous so we don't get stuck in an infinite loop. + if (th->continuous && P_IsDegeneratedWaypointSequence(th->sequence)) { - // Already at the destination point... - if (!th->wrap) - { - po->thinker = NULL; - P_RemoveThinker(&th->thinker); - } + CONS_Debug(DBG_POLYOBJ, "EV_DoPolyObjWaypoint: All waypoints are in the same location!\n"); + th->continuous = false; } - // Find the actual target movement waypoint - target = first; + th->pointnum = first->health; - if (!target) - { - CONS_Debug(DBG_POLYOBJ, "EV_DoPolyObjWaypoint: Missing target waypoint!\n"); - po->thinker = NULL; - P_RemoveThinker(&th->thinker); - return false; - } - - // Set pointnum - th->pointnum = target->health; - th->target = NULL; // set to NULL first so the below doesn't go wrong - // Set the mobj as your target! -- Monster Iestyn 27/12/19 - P_SetTarget(&th->target, target); - - // We don't deal with the mirror crap here, we'll - // handle that in the T_Thinker function. return true; } diff --git a/src/p_polyobj.h b/src/p_polyobj.h index 68aff4bf1..8037c545f 100644 --- a/src/p_polyobj.h +++ b/src/p_polyobj.h @@ -140,26 +140,26 @@ typedef struct polymove_s UINT32 angle; // angle along which to move } polymove_t; +// PolyObject waypoint movement return behavior +typedef enum +{ + PWR_STOP, // Stop after reaching last waypoint + PWR_WRAP, // Wrap back to first waypoint + PWR_COMEBACK, // Repeat sequence in reverse +} polywaypointreturn_e; + typedef struct polywaypoint_s { thinker_t thinker; // must be first - INT32 polyObjNum; // numeric id of polyobject - INT32 speed; // resultant velocity - INT32 sequence; // waypoint sequence # - INT32 pointnum; // waypoint # - INT32 direction; // 1 for normal, -1 for backwards - UINT8 comeback; // reverses and comes back when the end is reached - UINT8 wrap; // Wrap around waypoints - UINT8 continuous; // continuously move - used with COMEBACK or WRAP - UINT8 stophere; // Will stop after it reaches the next waypoint - - // Difference between location of PO and location of waypoint (offset) - fixed_t diffx; - fixed_t diffy; - fixed_t diffz; - - mobj_t *target; // next waypoint mobj + INT32 polyObjNum; // numeric id of polyobject + INT32 speed; // resultant velocity + INT32 sequence; // waypoint sequence # + INT32 pointnum; // waypoint # + INT32 direction; // 1 for normal, -1 for backwards + UINT8 returnbehavior; // behavior after reaching the last waypoint + UINT8 continuous; // continuously move - used with PWR_WRAP or PWR_COMEBACK + UINT8 stophere; // Will stop after it reaches the next waypoint } polywaypoint_t; typedef struct polyslidedoor_s @@ -254,15 +254,19 @@ typedef struct polymovedata_s UINT8 overRide; // if true, will override any action on the object } polymovedata_t; +typedef enum +{ + PWF_REVERSE = 1, // Move through waypoints in reverse order + PWF_LOOP = 1<<1, // Loop movement (used with PWR_WRAP or PWR_COMEBACK) +} polywaypointflags_e; + typedef struct polywaypointdata_s { - INT32 polyObjNum; // numeric id of polyobject to affect - INT32 sequence; // waypoint sequence # - fixed_t speed; // linear speed - UINT8 reverse; // if true, will go in reverse waypoint order - UINT8 comeback; // reverses and comes back when the end is reached - UINT8 wrap; // Wrap around waypoints - UINT8 continuous; // continuously move - used with COMEBACK or WRAP + INT32 polyObjNum; // numeric id of polyobject to affect + INT32 sequence; // waypoint sequence # + fixed_t speed; // linear speed + UINT8 returnbehavior; // behavior after reaching the last waypoint + UINT8 flags; // PWF_ flags } polywaypointdata_t; // polyobject door types diff --git a/src/p_saveg.c b/src/p_saveg.c index d00845879..bec64ed35 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2018,14 +2018,9 @@ static void SavePolywaypointThinker(const thinker_t *th, UINT8 type) WRITEINT32(save_p, ht->sequence); WRITEINT32(save_p, ht->pointnum); WRITEINT32(save_p, ht->direction); - WRITEUINT8(save_p, ht->comeback); - WRITEUINT8(save_p, ht->wrap); + WRITEUINT8(save_p, ht->returnbehavior); WRITEUINT8(save_p, ht->continuous); WRITEUINT8(save_p, ht->stophere); - WRITEFIXED(save_p, ht->diffx); - WRITEFIXED(save_p, ht->diffy); - WRITEFIXED(save_p, ht->diffz); - WRITEUINT32(save_p, SaveMobjnum(ht->target)); } static void SavePolyslidedoorThinker(const thinker_t *th, const UINT8 type) @@ -3157,14 +3152,9 @@ static inline thinker_t* LoadPolywaypointThinker(actionf_p1 thinker) ht->sequence = READINT32(save_p); ht->pointnum = READINT32(save_p); ht->direction = READINT32(save_p); - ht->comeback = READUINT8(save_p); - ht->wrap = READUINT8(save_p); + ht->returnbehavior = READUINT8(save_p); ht->continuous = READUINT8(save_p); ht->stophere = READUINT8(save_p); - ht->diffx = READFIXED(save_p); - ht->diffy = READFIXED(save_p); - ht->diffz = READFIXED(save_p); - ht->target = LoadMobj(READUINT32(save_p)); return &ht->thinker; } @@ -3411,7 +3401,6 @@ static void P_NetUnArchiveThinkers(void) case tc_polywaypoint: th = LoadPolywaypointThinker((actionf_p1)T_PolyObjWaypoint); - restoreNum = true; break; case tc_polyslidedoor: @@ -3471,7 +3460,6 @@ static void P_NetUnArchiveThinkers(void) if (restoreNum) { executor_t *delay = NULL; - polywaypoint_t *polywp = NULL; UINT32 mobjnum; for (currentthinker = thlist[THINK_MAIN].next; currentthinker != &thlist[THINK_MAIN]; currentthinker = currentthinker->next) { @@ -3482,15 +3470,6 @@ static void P_NetUnArchiveThinkers(void) continue; delay->caller = P_FindNewPosition(mobjnum); } - for (currentthinker = thlist[THINK_POLYOBJ].next; currentthinker != &thlist[THINK_POLYOBJ]; currentthinker = currentthinker->next) - { - if (currentthinker->function.acp1 != (actionf_p1)T_PolyObjWaypoint) - continue; - polywp = (void *)currentthinker; - if (!(mobjnum = (UINT32)(size_t)polywp->target)) - continue; - polywp->target = P_FindNewPosition(mobjnum); - } } } diff --git a/src/p_setup.c b/src/p_setup.c index f454bc1fd..84e89d746 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -239,6 +239,38 @@ mobj_t *P_GetClosestWaypoint(UINT8 sequence, mobj_t *mo) return result; } +// Return true if all waypoints are in the same location +boolean P_IsDegeneratedWaypointSequence(UINT8 sequence) +{ + mobj_t *first, *waypoint; + UINT8 wp; + + if (numwaypoints[sequence] <= 1) + return true; + + first = waypoints[sequence][0]; + + for (wp = 1; wp < numwaypoints[sequence]; wp++) + { + waypoint = waypoints[sequence][wp]; + + if (!waypoint) + continue; + + if (waypoint->x != first->x) + return false; + + if (waypoint->y != first->y) + return false; + + if (waypoint->z != first->z) + return false; + } + + return true; +} + + /** Logs an error about a map being corrupt, then terminate. * This allows reporting highly technical errors for usefulness, without * confusing a novice map designer who simply needs to run ZenNode. diff --git a/src/p_spec.c b/src/p_spec.c index f34138269..d45a33c47 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1276,10 +1276,21 @@ static boolean PolyWaypoint(line_t *line) pwd.polyObjNum = line->tag; pwd.speed = sides[line->sidenum[0]].textureoffset / 8; pwd.sequence = sides[line->sidenum[0]].rowoffset >> FRACBITS; // Sequence # - pwd.reverse = (line->flags & ML_EFFECT1) == ML_EFFECT1; // Reverse? - pwd.comeback = (line->flags & ML_EFFECT2) == ML_EFFECT2; // Return when reaching end? - pwd.wrap = (line->flags & ML_EFFECT3) == ML_EFFECT3; // Wrap around waypoints - pwd.continuous = (line->flags & ML_EFFECT4) == ML_EFFECT4; // Continuously move - used with COMEBACK or WRAP + + // Behavior after reaching the last waypoint? + if (line->flags & ML_EFFECT3) + pwd.returnbehavior = PWR_WRAP; // Wrap back to first waypoint + else if (line->flags & ML_EFFECT2) + pwd.returnbehavior = PWR_COMEBACK; // Go through sequence in reverse + else + pwd.returnbehavior = PWR_STOP; // Stop + + // Flags + pwd.flags = 0; + if (line->flags & ML_EFFECT1) + pwd.flags |= PWF_REVERSE; + if (line->flags & ML_EFFECT4) + pwd.flags |= PWF_LOOP; return EV_DoPolyObjWaypoint(&pwd); }