diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index 6ba60be0eb..59359a981b 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -35,6 +35,8 @@ #include "i_system.h" #include "doomstat.h" +#define FUDGEFACTOR 10 + static FRandom pr_teleport ("Teleport"); extern void P_CalcHeight (player_t *player); @@ -476,16 +478,52 @@ bool EV_SilentLineTeleport (line_t *line, int side, AActor *thing, int id, INTBO player_t *player = thing->player && thing->player->mo == thing ? thing->player : NULL; - // Height of thing above ground - fixed_t z; - - z = thing->z - line->backsector->floorplane.ZatPoint (thing->x, thing->y) - + l->frontsector->floorplane.ZatPoint (x, y); + // Whether walking towards first side of exit linedef steps down + bool stepdown = l->frontsector->floorplane.ZatPoint(x, y) < l->backsector->floorplane.ZatPoint(x, y); + + // Height of thing above ground + fixed_t z = thing->z - thing->floorz; + + // Side to exit the linedef on positionally. + // + // Notes: + // + // This flag concerns exit position, not momentum. Due to + // roundoff error, the thing can land on either the left or + // the right side of the exit linedef, and steps must be + // taken to make sure it does not end up on the wrong side. + // + // Exit momentum is always towards side 1 in a reversed + // teleporter, and always towards side 0 otherwise. + // + // Exiting positionally on side 1 is always safe, as far + // as avoiding oscillations and stuck-in-wall problems, + // but may not be optimum for non-reversed teleporters. + // + // Exiting on side 0 can cause oscillations if momentum + // is towards side 1, as it is with reversed teleporters. + // + // Exiting on side 1 slightly improves player viewing + // when going down a step on a non-reversed teleporter. + + int side = reverse || (player && stepdown); + int fudge = FUDGEFACTOR; + + // Make sure we are on correct side of exit linedef. + while (P_PointOnLineSide(x, y, l) != side && --fudge >= 0) + { + if (abs(l->dx) > abs(l->dy)) + y -= l->dx < 0 != side ? -1 : 1; + else + x += l->dy < 0 != side ? -1 : 1; + } - // Attempt to teleport, aborting if blocked // Adjust z position to be same height above ground as before. // Ground level at the exit is measured as the higher of the // two floor heights at the exit linedef. + z = z + l->sidedef[stepdown]->sector->floorplane.ZatPoint(x, y); + + // Attempt to teleport, aborting if blocked if (!P_TeleportMove (thing, x, y, z, false)) { return false;