diff --git a/src/k_waypoint.c b/src/k_waypoint.c index b9581e871..db8289aaa 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -314,7 +314,7 @@ waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj) } else if (checkdist < closestdist && bestfindist == INT32_MAX) { - if (!P_CheckSight(mobj, checkwaypoint->mobj)) + if (!P_TraceBlockingLines(mobj, checkwaypoint->mobj)) { // Save sight checks for the end, so we only do it if we have to continue; diff --git a/src/lua_baselib.c b/src/lua_baselib.c index d689dc848..da2c21bbb 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -1410,6 +1410,18 @@ static int lib_pCheckSight(lua_State *L) return 1; } +static int lib_pTraceBlockingLines(lua_State *L) +{ + mobj_t *t1 = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + mobj_t *t2 = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); + //HUDSAFE? + INLEVEL + if (!t1 || !t2) + return LUA_ErrInvalid(L, "mobj_t"); + lua_pushboolean(L, P_TraceBlockingLines(t1, t2)); + return 1; +} + static int lib_pCheckHoopPosition(lua_State *L) { mobj_t *hoopthing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); @@ -3783,6 +3795,7 @@ static luaL_Reg lib[] = { {"P_SlideMove",lib_pSlideMove}, {"P_BounceMove",lib_pBounceMove}, {"P_CheckSight", lib_pCheckSight}, + {"P_TraceBlockingLines", lib_pTraceBlockingLines}, {"P_CheckHoopPosition",lib_pCheckHoopPosition}, {"P_RadiusAttack",lib_pRadiusAttack}, {"P_FloorzAtPos",lib_pFloorzAtPos}, diff --git a/src/p_local.h b/src/p_local.h index f4029aedc..47a65563c 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -400,6 +400,7 @@ void P_UnsetThingPosition(mobj_t *thing); void P_SetThingPosition(mobj_t *thing); void P_SetUnderlayPosition(mobj_t *thing); +boolean P_IsLineBlocking(const line_t *ld, const mobj_t *thing); boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y); boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam); boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff); @@ -409,6 +410,7 @@ void P_SlideMove(mobj_t *mo); void P_BouncePlayerMove(mobj_t *mo); void P_BounceMove(mobj_t *mo); boolean P_CheckSight(mobj_t *t1, mobj_t *t2); +boolean P_TraceBlockingLines(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_map.c b/src/p_map.c index 898ee0a49..98966c14d 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1624,7 +1624,7 @@ static boolean PIT_CheckCameraLine(line_t *ld) return true; } -static boolean P_IsLineBlocking(const line_t *ld, const mobj_t *thing) +boolean P_IsLineBlocking(const line_t *ld, const mobj_t *thing) { // missiles can cross uncrossable lines if ((thing->flags & MF_MISSILE)) diff --git a/src/p_sight.c b/src/p_sight.c index a18f22d40..38a50df36 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -498,3 +498,161 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) // 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; +} 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 block us + 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); +} + +boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2) +{ + const sector_t *s1, *s2; + size_t pnum; + traceblocking_t tb; + + // 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++; + + tb.strace.dx = (tb.t2x = t2->x) - (tb.strace.x = t1->x); + tb.strace.dy = (tb.t2y = t2->y) - (tb.strace.y = t1->y); + + if (t1->x > t2->x) + tb.bbox[BOXRIGHT] = t1->x, tb.bbox[BOXLEFT] = t2->x; + else + tb.bbox[BOXRIGHT] = t2->x, tb.bbox[BOXLEFT] = t1->x; + + if (t1->y > t2->y) + tb.bbox[BOXTOP] = t1->y, tb.bbox[BOXBOTTOM] = t2->y; + else + tb.bbox[BOXTOP] = t2->y, tb.bbox[BOXBOTTOM] = t1->y; + + tb.compareThing = t1; + + // the head node is the last node output + return P_CrossBSPNodeBlocking((INT32)numnodes - 1, &tb); +}