// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id:$ // // Copyright (C) 1993-1996 by id Software, Inc. // // This source is available for distribution and/or modification // only under the terms of the DOOM Source Code License as // published by id Software. All rights reserved. // // The source is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License // for more details. // // $Log:$ // // DESCRIPTION: // Movement/collision utility functions, // as used by function in p_map.c. // BLOCKMAP Iterator functions, // and some PIT_* functions to use for iteration. // //----------------------------------------------------------------------------- #include #include "m_bbox.h" #include "doomdef.h" #include "doomstat.h" #include "p_local.h" // State. #include "r_state.h" #include "templates.h" static AActor *RoughBlockCheck (AActor *mo, int index); //========================================================================== // // P_AproxDistance // // Gives an estimation of distance (not exact) // //========================================================================== fixed_t P_AproxDistance (fixed_t dx, fixed_t dy) { dx = abs(dx); dy = abs(dy); return (dx < dy) ? dx+dy-(dx>>1) : dx+dy-(dy>>1); } //========================================================================== // // P_BoxOnLineSide // // Considers the line to be infinite // Returns side 0 or 1, -1 if box crosses the line. // //========================================================================== int P_BoxOnLineSide (const fixed_t *tmbox, const line_t *ld) { int p1; int p2; switch (ld->slopetype) { case ST_HORIZONTAL: p1 = tmbox[BOXTOP] > ld->v1->y; p2 = tmbox[BOXBOTTOM] > ld->v1->y; if (ld->dx < 0) { p1 ^= 1; p2 ^= 1; } break; case ST_VERTICAL: p1 = tmbox[BOXRIGHT] < ld->v1->x; p2 = tmbox[BOXLEFT] < ld->v1->x; if (ld->dy < 0) { p1 ^= 1; p2 ^= 1; } break; case ST_POSITIVE: p1 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXTOP], ld); p2 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld); break; case ST_NEGATIVE: default: // Just to assure GCC that p1 and p2 really do get initialized p1 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXTOP], ld); p2 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld); break; } return (p1 == p2) ? p1 : -1; } //========================================================================== // // P_InterceptVector // // Returns the fractional intercept point along the first divline. // This is only called by the addthings and addlines traversers. // //========================================================================== fixed_t P_InterceptVector (const divline_t *v2, const divline_t *v1) { #if 1 // [RH] Use 64 bit ints, so long divlines don't overflow SQWORD den = ((SQWORD)v1->dy*v2->dx - (SQWORD)v1->dx*v2->dy) >> FRACBITS; if (den == 0) return 0; // parallel SQWORD num = ((SQWORD)(v1->x - v2->x)*v1->dy + (SQWORD)(v2->y - v1->y)*v1->dx); return (fixed_t)(num / den); #elif 1 // This is the original Doom version fixed_t frac; fixed_t num; fixed_t den; den = FixedMul (v1->dy>>8,v2->dx) - FixedMul(v1->dx>>8,v2->dy); if (den == 0) return 0; // I_Error ("P_InterceptVector: parallel"); num = FixedMul ( (v1->x - v2->x)>>8 ,v1->dy ) +FixedMul ( (v2->y - v1->y)>>8, v1->dx ); frac = FixedDiv (num , den); return frac; #else // UNUSED, float debug. float frac; float num; float den; float v1x = (float)v1->x/FRACUNIT; float v1y = (float)v1->y/FRACUNIT; float v1dx = (float)v1->dx/FRACUNIT; float v1dy = (float)v1->dy/FRACUNIT; float v2x = (float)v2->x/FRACUNIT; float v2y = (float)v2->y/FRACUNIT; float v2dx = (float)v2->dx/FRACUNIT; float v2dy = (float)v2->dy/FRACUNIT; den = v1dy*v2dx - v1dx*v2dy; if (den == 0) return 0; // parallel num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; frac = num / den; return frac*FRACUNIT; #endif } //========================================================================== // // P_LineOpening // // Sets opentop and openbottom to the window // through a two sided line. // OPTIMIZE: keep this precalculated // //========================================================================== fixed_t opentop; fixed_t openbottom; fixed_t openrange; fixed_t lowfloor; extern int tmfloorpic; sector_t *openbottomsec; sector_t *opentopsec; void P_LineOpening (const line_t *linedef, fixed_t x, fixed_t y, fixed_t refx, fixed_t refy) { sector_t *front, *back; fixed_t fc, ff, bc, bf; if (linedef->sidenum[1] == NO_SIDE) { // single sided line openrange = 0; return; } front = linedef->frontsector; back = linedef->backsector; fc = front->ceilingplane.ZatPoint (x, y); ff = front->floorplane.ZatPoint (x, y); bc = back->ceilingplane.ZatPoint (x, y); bf = back->floorplane.ZatPoint (x, y); /*Printf ("]]]]]] %d %d\n", ff, bf);*/ opentopsec = fc < bc? front : back; opentop = fc < bc ? fc : bc; bool usefront; // [RH] fudge a bit for actors that are moving across lines // bordering a slope/non-slope that meet on the floor. Note // that imprecisions in the plane equation mean there is a // good chance that even if a slope and non-slope look like // they line up, they won't be perfectly aligned. if (refx == FIXED_MIN || abs (ff-bf) > 256) { usefront = (ff > bf); } else { if ((front->floorplane.a | front->floorplane.b) == 0) usefront = true; else if ((back->floorplane.a | front->floorplane.b) == 0) usefront = false; else usefront = !P_PointOnLineSide (refx, refy, linedef); } if (usefront) { openbottom = ff; openbottomsec = front; lowfloor = bf; //tmfloorpic = front->floorpic; } else { openbottom = bf; openbottomsec = back; lowfloor = ff; //tmfloorpic = back->floorpic; } openrange = opentop - openbottom; } // // THING POSITION SETTING // // // P_UnsetThingPosition // Unlinks a thing from block map and sectors. // On each position change, BLOCKMAP and other // lookups maintaining lists of things inside // these structures need to be updated. // void AActor::UnlinkFromWorld () { sector_list = NULL; if (!(flags & MF_NOSECTOR)) { // invisible things don't need to be in sector list // unlink from subsector // killough 8/11/98: simpler scheme using pointers-to-pointers for prev // pointers, allows head node pointers to be treated like everything else AActor **prev = sprev; AActor *next = snext; if ((*prev = next)) // unlink from sector list next->sprev = prev; snext = NULL; sprev = (AActor **)(size_t)0xBeefCafe; // Woo! Bug-catching value! // phares 3/14/98 // // Save the sector list pointed to by touching_sectorlist. // In P_SetThingPosition, we'll keep any nodes that represent // sectors the Thing still touches. We'll add new ones then, and // delete any nodes for sectors the Thing has vacated. Then we'll // put it back into touching_sectorlist. It's done this way to // avoid a lot of deleting/creating for nodes, when most of the // time you just get back what you deleted anyway. // // If this Thing is being removed entirely, then the calling // routine will clear out the nodes in sector_list. sector_list = touching_sectorlist; touching_sectorlist = NULL; //to be restored by P_SetThingPosition } if (!(flags & MF_NOBLOCKMAP)) { // [RH] Unlink from all blocks this actor uses FBlockNode *block = this->BlockNode; while (block != NULL) { if (block->NextActor != NULL) { block->NextActor->PrevActor = block->PrevActor; } *(block->PrevActor) = block->NextActor; FBlockNode *next = block->NextBlock; block->Release (); block = next; } BlockNode = NULL; } } // // P_SetThingPosition // Links a thing into both a block and a subsector based on it's x y. // Sets thing->sector properly // void AActor::LinkToWorld (bool buggy) { // link into subsector sector_t *sec; if (!buggy || numnodes == 0) { sec = R_PointInSubsector (x, y)->sector; } else { sec = LinkToWorldForMapThing (); } LinkToWorld (sec); } void AActor::LinkToWorld (sector_t *sec) { if (sec == NULL) { LinkToWorld (); return; } Sector = sec; if ( !(flags & MF_NOSECTOR) ) { // invisible things don't go into the sector links // killough 8/11/98: simpler scheme using pointer-to-pointer prev // pointers, allows head nodes to be treated like everything else AActor **link = &sec->thinglist; AActor *next = *link; if ((snext = next)) next->sprev = &snext; sprev = link; *link = this; // phares 3/16/98 // // If sector_list isn't NULL, it has a collection of sector // nodes that were just removed from this Thing. // Collect the sectors the object will live in by looking at // the existing sector_list and adding new nodes and deleting // obsolete ones. // When a node is deleted, its sector links (the links starting // at sector_t->touching_thinglist) are broken. When a node is // added, new sector links are created. P_CreateSecNodeList (this, x, y); touching_sectorlist = sector_list; // Attach to thing sector_list = NULL; // clear for next time } // link into blockmap (inert things don't need to be in the blockmap) if ( !(flags & MF_NOBLOCKMAP) ) { int x1 = (x - radius - bmaporgx)>>MAPBLOCKSHIFT; int x2 = (x + radius - bmaporgx)>>MAPBLOCKSHIFT; int y1 = (y - radius - bmaporgy)>>MAPBLOCKSHIFT; int y2 = (y + radius - bmaporgy)>>MAPBLOCKSHIFT; if (x1 >= bmapwidth || x2 < 0 || y1 >= bmapheight || y2 < 0) { // thing is off the map BlockNode = NULL; } else { // [RH] Link into every block this actor touches, not just the center one FBlockNode **alink = &this->BlockNode; x1 = MAX (0, x1); y1 = MAX (0, y1); x2 = MIN (bmapwidth - 1, x2); y2 = MIN (bmapheight - 1, y2); for (int y = y1; y <= y2; ++y) { for (int x = x1; x <= x2; ++x) { FBlockNode **link = &blocklinks[y*bmapwidth + x]; FBlockNode *node = FBlockNode::Create (this, x, y); // Link in to block if ((node->NextActor = *link) != NULL) { (*link)->PrevActor = &node->NextActor; } node->PrevActor = link; *link = node; // Link in to actor node->PrevBlock = alink; node->NextBlock = NULL; (*alink) = node; alink = &node->NextBlock; } } } } } // // [RH] LinkToWorldForMapThing // // Emulate buggy PointOnLineSide and fix actors that lie on // lines to compensate for some IWAD maps. // static int R_PointOnSideSlow (fixed_t x, fixed_t y, node_t *node) { // [RH] This might have been faster than two multiplies and an // add on a 386/486, but it certainly isn't on anything newer than that. fixed_t dx; fixed_t dy; fixed_t left; fixed_t right; if (!node->dx) { if (x <= node->x) return node->dy > 0; return node->dy < 0; } if (!node->dy) { if (y <= node->y) return node->dx < 0; return node->dx > 0; } dx = (x - node->x); dy = (y - node->y); // Try to quickly decide by looking at sign bits. if ( (node->dy ^ node->dx ^ dx ^ dy)&0x80000000 ) { if ( (node->dy ^ dx) & 0x80000000 ) { // (left is negative) return 1; } return 0; } left = FixedMul ( node->dy>>FRACBITS , dx ); right = FixedMul ( dy , node->dx>>FRACBITS ); if (right < left) { // front side return 0; } // back side return 1; } sector_t *AActor::LinkToWorldForMapThing () { node_t *node = nodes + numnodes - 1; do { // Use original buggy point-on-side test when spawning // things at level load so that the map spots in the // emerald key room of Hexen MAP01 are spawned on the // window ledge instead of the blocking floor in front // of it. Why do I consider it buggy? Because a point // that lies directly on a line should always be // considered as "in front" of the line. The orientation // of the line should be irrelevant. node = (node_t *)node->children[R_PointOnSideSlow (x, y, node)]; } while (!((size_t)node & 1)); subsector_t *ssec = (subsector_t *)((BYTE *)node - 1); if (flags4 & MF4_FIXMAPTHINGPOS) { // If the thing is exactly on a line, move it into the subsector // slightly in order to resolve clipping issues in the renderer. // This check needs to use the blockmap, because an actor on a // one-sided line might go into a subsector behind the line, so // the line would not be included as one of its subsector's segs. int blockx = (x - bmaporgx) >> MAPBLOCKSHIFT; int blocky = (y - bmaporgy) >> MAPBLOCKSHIFT; if ((unsigned int)blockx < (unsigned int)bmapwidth && (unsigned int)blocky < (unsigned int)bmapheight) { int *list; for (list = blockmaplump + blockmap[blocky*bmapwidth + blockx] + 1; *list != -1; ++list) { line_t *ldef = &lines[*list]; if (ldef->frontsector == ldef->backsector) { // Skip two-sided lines inside a single sector continue; } if (ldef->backsector != NULL) { if (ldef->frontsector->floorplane == ldef->backsector->floorplane && ldef->frontsector->ceilingplane == ldef->backsector->ceilingplane) { // Skip two-sided lines without any height difference on either side continue; } } if (DMulScale32 (y - ldef->v1->y, ldef->dx, ldef->v1->x - x, ldef->dy) == 0) { // It touches the infinite line; now make sure it touches the linedef SQWORD num, den; den = (SQWORD)ldef->dx*ldef->dx + (SQWORD)ldef->dy*ldef->dy; if (den != 0) { num = (SQWORD)(x-ldef->v1->x)*ldef->dx+(SQWORD)(y-ldef->v1->y)*ldef->dy; if (num >= 0 && num <= den) { DPrintf ("%s at (%d,%d) lies directly on line %d\n", this->GetClass()->TypeName.GetChars(), x>>FRACBITS, y>>FRACBITS, ldef-lines); angle_t finean = R_PointToAngle2 (0, 0, ldef->dx, ldef->dy); if (ldef->backsector != NULL && ldef->backsector == ssec->sector) { finean += ANGLE_90; } else { finean -= ANGLE_90; } finean >>= ANGLETOFINESHIFT; x += finecosine[finean] >> 2; y += finesine[finean] >> 2; break; } } } } } } return ssec->sector; } void AActor::SetOrigin (fixed_t ix, fixed_t iy, fixed_t iz) { UnlinkFromWorld (); x = ix; y = iy; z = iz; LinkToWorld (); floorz = Sector->floorplane.ZatPoint (ix, iy); ceilingz = Sector->ceilingplane.ZatPoint (ix, iy); } FBlockNode *FBlockNode::FreeBlocks = NULL; FBlockNode *FBlockNode::Create (AActor *who, int x, int y) { FBlockNode *block; if (FreeBlocks != NULL) { block = FreeBlocks; FreeBlocks = block->NextBlock; } else { block = new FBlockNode; } block->BlockIndex = x + y*bmapwidth; block->Me = who; block->NextActor = NULL; block->PrevActor = NULL; block->PrevBlock = NULL; block->NextBlock = NULL; return block; } void FBlockNode::Release () { NextBlock = FreeBlocks; FreeBlocks = this; } // // BLOCK MAP ITERATORS // For each line/thing in the given mapblock, // call the passed PIT_* function. // If the function returns false, // exit with false without checking anything else. // // // P_BlockLinesIterator // The validcount flags are used to avoid checking lines // that are marked in multiple mapblocks, // so increment validcount before the first call // to P_BlockLinesIterator, then make one or more calls // to it. // extern polyblock_t **PolyBlockMap; bool P_BlockLinesIterator (int x, int y, bool(*func)(line_t*)) { if (x<0 || y<0 || x>=bmapwidth || y>=bmapheight) { return true; } else { int offset; int *list; /* [RH] Polyobj stuff from Hexen --> */ polyblock_t *polyLink; offset = y*bmapwidth + x; if (PolyBlockMap) { polyLink = PolyBlockMap[offset]; while (polyLink) { if (polyLink->polyobj && polyLink->polyobj->validcount != validcount) { int i; seg_t **tempSeg = polyLink->polyobj->segs; polyLink->polyobj->validcount = validcount; for (i = polyLink->polyobj->numsegs; i; i--, tempSeg++) { if ((*tempSeg)->linedef->validcount != validcount) { (*tempSeg)->linedef->validcount = validcount; if (!func ((*tempSeg)->linedef)) return false; } } } polyLink = polyLink->next; } } /* <-- Polyobj stuff from Hexen */ offset = *(blockmap + offset); // There is an extra entry at the beginning of every block. // Apparently, id had originally intended for it to be used // to keep track of things, but the final code does not do that. for (list = blockmaplump + offset + 1; *list != -1; list++) { line_t *ld = &lines[*list]; if (ld->validcount != validcount) { ld->validcount = validcount; if ( !func(ld) ) return false; } } } return true; // everything was checked } // // P_BlockThingsIterator // bool P_BlockThingsIterator (int x, int y, bool(*func)(AActor*), TArray &checkarray, AActor *actor) { if ((unsigned int)x >= (unsigned int)bmapwidth || (unsigned int)y >= (unsigned int)bmapheight) { return true; } else { FBlockNode *block; int index = y*bmapwidth + x; if (actor == NULL) { block = blocklinks[index]; } else { block = actor->BlockNode; while (block != NULL && block->BlockIndex != index) { block = block->NextBlock; } if (block != NULL) { block = block->NextActor; } } while (block != NULL) { FBlockNode *next = block->NextActor; int i; // Don't recheck things that were already checked for (i = (int)checkarray.Size() - 1; i >= 0; --i) { if (checkarray[i] == block->Me) { break; } } if (i < 0) { checkarray.Push (block->Me); if (!func (block->Me)) { return false; } } block = next; } } return true; } // // INTERCEPT ROUTINES // TArray intercepts (128); divline_t trace; INTBOOL earlyout; int ptflags; // // PIT_AddLineIntercepts. // Looks for lines in the given block // that intercept the given trace // to add to the intercepts list. // // A line is crossed if its endpoints // are on opposite sides of the trace. // Returns true if earlyout and a solid line hit. // bool PIT_AddLineIntercepts (line_t *ld) { int s1; int s2; fixed_t frac; divline_t dl; // avoid precision problems with two routines if ( trace.dx > FRACUNIT*16 || trace.dy > FRACUNIT*16 || trace.dx < -FRACUNIT*16 || trace.dy < -FRACUNIT*16) { s1 = P_PointOnDivlineSide (ld->v1->x, ld->v1->y, &trace); s2 = P_PointOnDivlineSide (ld->v2->x, ld->v2->y, &trace); } else { s1 = P_PointOnLineSide (trace.x, trace.y, ld); s2 = P_PointOnLineSide (trace.x+trace.dx, trace.y+trace.dy, ld); } if (s1 == s2) return true; // line isn't crossed // hit the line P_MakeDivline (ld, &dl); frac = P_InterceptVector (&trace, &dl); if (frac < 0) return true; // behind source // try to early out the check if (earlyout && frac < FRACUNIT && !ld->backsector) { return false; // stop checking } intercept_t newintercept; newintercept.frac = frac; newintercept.isaline = true; newintercept.d.line = ld; intercepts.Push (newintercept); return true; // continue } // // PIT_AddThingIntercepts // bool PIT_AddThingIntercepts (AActor* thing) { int numfronts = 0; divline_t line; int i; // [RH] Don't check a corner to corner crossection for hit. // Instead, check against the actual bounding box. // There's probably a smarter way to determine which two sides // of the thing face the trace than by trying all four sides... for (i = 0; i < 4; ++i) { switch (i) { case 0: // Top edge line.x = thing->x + thing->radius; line.y = thing->y + thing->radius; line.dx = -thing->radius * 2; line.dy = 0; break; case 1: // Right edge line.x = thing->x + thing->radius; line.y = thing->y - thing->radius; line.dx = 0; line.dy = thing->radius * 2; break; case 2: // Bottom edge line.x = thing->x - thing->radius; line.y = thing->y - thing->radius; line.dx = thing->radius * 2; line.dy = 0; break; case 3: // Left edge line.x = thing->x - thing->radius; line.y = thing->y + thing->radius; line.dx = 0; line.dy = thing->radius * -2; break; } // Check if this side is facing the trace origin if (P_PointOnDivlineSide (trace.x, trace.y, &line) == 0) { numfronts++; // If it is, see if the trace crosses it if (P_PointOnDivlineSide (line.x, line.y, &trace) != P_PointOnDivlineSide (line.x + line.dx, line.y + line.dy, &trace)) { // It's a hit fixed_t frac = P_InterceptVector (&trace, &line); if (frac < 0) { // behind source return true; } intercept_t newintercept; newintercept.frac = frac; newintercept.isaline = false; newintercept.d.thing = thing; intercepts.Push (newintercept); return true; // keep going } } } // If none of the sides was facing the trace, then the trace // must have started inside the box, so add it as an intercept. if (numfronts == 0) { intercept_t newintercept; newintercept.frac = 0; newintercept.isaline = false; newintercept.d.thing = thing; intercepts.Push (newintercept); return true; // keep going } // Didn't hit it return true; } // // P_TraverseIntercepts // Returns true if the traverser function returns true // for all lines. // bool P_TraverseIntercepts (traverser_t func, fixed_t maxfrac) { unsigned int count; fixed_t dist; unsigned int scanpos; intercept_t *scan; intercept_t *in = NULL; count = intercepts.Size (); while (count--) { dist = FIXED_MAX; for (scanpos = 0; scanpos < intercepts.Size (); scanpos++) { scan = &intercepts[scanpos]; if (scan->frac < dist) { dist = scan->frac; in = scan; } } if (dist > maxfrac || in == NULL) return true; // checked everything in range if (!func (in)) return false; // don't bother going farther in->frac = FIXED_MAX; } return true; // everything was traversed } // // P_PathTraverse // Traces a line from x1,y1 to x2,y2, // calling the traverser function for each. // Returns true if the traverser function returns true // for all lines. // bool P_PathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, bool (*trav) (intercept_t *)) { static TArray pathbt; fixed_t xt1; fixed_t yt1; fixed_t xt2; fixed_t yt2; fixed_t xstep; fixed_t ystep; fixed_t partialx, partialy; fixed_t xintercept; fixed_t yintercept; int mapx; int mapy; int mapxstep; int mapystep; int count; earlyout = flags & PT_EARLYOUT; validcount++; intercepts.Clear (); pathbt.Clear (); if ( ((x1-bmaporgx)&(MAPBLOCKSIZE-1)) == 0) x1 += FRACUNIT; // don't side exactly on a line if ( ((y1-bmaporgy)&(MAPBLOCKSIZE-1)) == 0) y1 += FRACUNIT; // don't side exactly on a line trace.x = x1; trace.y = y1; trace.dx = x2 - x1; trace.dy = y2 - y1; x1 -= bmaporgx; y1 -= bmaporgy; xt1 = x1>>MAPBLOCKSHIFT; yt1 = y1>>MAPBLOCKSHIFT; x2 -= bmaporgx; y2 -= bmaporgy; xt2 = x2>>MAPBLOCKSHIFT; yt2 = y2>>MAPBLOCKSHIFT; if (xt2 > xt1) { mapxstep = 1; partialx = FRACUNIT - ((x1>>MAPBTOFRAC)&(FRACUNIT-1)); ystep = FixedDiv (y2-y1,abs(x2-x1)); } else if (xt2 < xt1) { mapxstep = -1; partialx = (x1>>MAPBTOFRAC)&(FRACUNIT-1); ystep = FixedDiv (y2-y1,abs(x2-x1)); } else { mapxstep = 0; partialx = FRACUNIT; ystep = 256*FRACUNIT; } yintercept = (y1>>MAPBTOFRAC) + FixedMul (partialx, ystep); if (yt2 > yt1) { mapystep = 1; partialy = FRACUNIT - ((y1>>MAPBTOFRAC)&(FRACUNIT-1)); xstep = FixedDiv (x2-x1,abs(y2-y1)); } else if (yt2 < yt1) { mapystep = -1; partialy = (y1>>MAPBTOFRAC)&(FRACUNIT-1); xstep = FixedDiv (x2-x1,abs(y2-y1)); } else { mapystep = 0; partialy = FRACUNIT; xstep = 256*FRACUNIT; } xintercept = (x1>>MAPBTOFRAC) + FixedMul (partialy, xstep); // [RH] Fix for traces that pass only through blockmap corners. In that case, // xintercept and yintercept can both be set ahead of mapx and mapy, so the // for loop would never advance anywhere. if (abs(xstep) == FRACUNIT && abs(ystep) == FRACUNIT) { if (ystep < 0) { partialx = FRACUNIT - partialx; } if (xstep < 0) { partialy = FRACUNIT - partialy; } if (partialx == partialy) { xintercept = xt1 << FRACBITS; yintercept = yt1 << FRACBITS; } } // Step through map blocks. // Count is present to prevent a round off error // from skipping the break statement. mapx = xt1; mapy = yt1; for (count = 0 ; count < 100 ; count++) { if (flags & PT_ADDLINES) { if (!P_BlockLinesIterator (mapx, mapy, PIT_AddLineIntercepts)) return false; // early out } if (flags & PT_ADDTHINGS) { if (!P_BlockThingsIterator (mapx, mapy, PIT_AddThingIntercepts, pathbt)) return false; // early out } if (mapx == xt2 && mapy == yt2) { break; } // [RH] Handle corner cases properly instead of pretending they don't exist. switch ((((yintercept >> FRACBITS) == mapy) << 1) | ((xintercept >> FRACBITS) == mapx)) { case 0: // neither xintercept nor yintercept match! count = 100; // Stop traversing, because somebody screwed up. break; case 1: // xintercept matches xintercept += xstep; mapy += mapystep; break; case 2: // yintercept matches yintercept += ystep; mapx += mapxstep; break; case 3: // xintercept and yintercept both match // The trace is exiting a block through its corner. Not only does the block // being entered need to be checked (which will happen when this loop // continues), but the other two blocks adjacent to the corner also need to // be checked. if (flags & PT_ADDLINES) { if (!P_BlockLinesIterator (mapx + mapxstep, mapy, PIT_AddLineIntercepts) || !P_BlockLinesIterator (mapx, mapy + mapystep, PIT_AddLineIntercepts)) return false; // early out } if (flags & PT_ADDTHINGS) { if (!P_BlockThingsIterator (mapx + mapxstep, mapy, PIT_AddThingIntercepts, pathbt) || !P_BlockThingsIterator (mapx, mapy + mapystep, PIT_AddThingIntercepts, pathbt)) return false; // early out } xintercept += xstep; yintercept += ystep; mapx += mapxstep; mapy += mapystep; break; } } // go through the sorted list return P_TraverseIntercepts ( trav, FRACUNIT ); } //=========================================================================== // // P_RoughMonsterSearch // // Searches though the surrounding mapblocks for monsters/players // distance is in MAPBLOCKUNITS //=========================================================================== AActor *P_RoughMonsterSearch (AActor *mo, int distance) { return P_BlockmapSearch (mo, distance, RoughBlockCheck); } AActor *P_BlockmapSearch (AActor *mo, int distance, AActor *(*check)(AActor*, int)) { int blockX; int blockY; int startX, startY; int blockIndex; int firstStop; int secondStop; int thirdStop; int finalStop; int count; AActor *target; startX = (mo->x-bmaporgx)>>MAPBLOCKSHIFT; startY = (mo->y-bmaporgy)>>MAPBLOCKSHIFT; validcount++; if (startX >= 0 && startX < bmapwidth && startY >= 0 && startY < bmapheight) { if ( (target = check (mo, startY*bmapwidth+startX)) ) { // found a target right away return target; } } for (count = 1; count <= distance; count++) { blockX = clamp (startX-count, 0, bmapwidth-1); blockY = clamp (startY-count, 0, bmapheight-1); blockIndex = blockY*bmapwidth+blockX; firstStop = startX+count; if (firstStop < 0) { continue; } if (firstStop >= bmapwidth) { firstStop = bmapwidth-1; } secondStop = startY+count; if (secondStop < 0) { continue; } if (secondStop >= bmapheight) { secondStop = bmapheight-1; } thirdStop = secondStop*bmapwidth+blockX; secondStop = secondStop*bmapwidth+firstStop; firstStop += blockY*bmapwidth; finalStop = blockIndex; // Trace the first block section (along the top) for (; blockIndex <= firstStop; blockIndex++) { if ( (target = check (mo, blockIndex)) ) { return target; } } // Trace the second block section (right edge) for (blockIndex--; blockIndex <= secondStop; blockIndex += bmapwidth) { if ( (target = check (mo, blockIndex)) ) { return target; } } // Trace the third block section (bottom edge) for (blockIndex -= bmapwidth; blockIndex >= thirdStop; blockIndex--) { if ( (target = check (mo, blockIndex)) ) { return target; } } // Trace the final block section (left edge) for (blockIndex++; blockIndex > finalStop; blockIndex -= bmapwidth) { if ( (target = check (mo, blockIndex)) ) { return target; } } } return NULL; } //=========================================================================== // // RoughBlockCheck // //=========================================================================== static AActor *RoughBlockCheck (AActor *mo, int index) { FBlockNode *link; for (link = blocklinks[index]; link != NULL; link = link->NextActor) { if (link->Me != mo) { if (mo->IsOkayToAttack (link->Me)) { return link->Me; } } } return NULL; }