// // P_BoxOnLineSide // Considers the line to be infinite // Returns side 0 or 1, -1 if box crosses the line. // // killough 5/3/98: reformatted, cleaned up // int P_BoxOnLineSide(const fixed_t *tmbox, const line_t *ld) { int p; switch (ld->slopetype) { default: // shut up compiler warnings -- killough case ST_HORIZONTAL: return (tmbox[BOXBOTTOM] > ld->v1->y) == (p = tmbox[BOXTOP] > ld->v1->y) ? p ^ (ld->dx < 0) : -1; case ST_VERTICAL: return (tmbox[BOXLEFT] < ld->v1->x) == (p = tmbox[BOXRIGHT] < ld->v1->x) ? p ^ (ld->dy < 0) : -1; case ST_POSITIVE: return P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld) == (p = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld)) ? p : -1; case ST_NEGATIVE: return (P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld)) == (p = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld)) ? p : -1; } }
// // Utility function which returns true if the divline dl crosses line // Returns -1 if there's no crossing, or the starting point's 0 or 1 side. // int P_LineIsCrossed(const line_t &line, const divline_t &dl) { int a; return (a = P_PointOnLineSide(dl.x, dl.y, &line)) != P_PointOnLineSide(dl.x + dl.dx, dl.y + dl.dy, &line) && P_PointOnDivlineSide(line.v1->x, line.v1->y, &dl) != P_PointOnDivlineSide(line.v1->x + line.dx, line.v1->y + line.dy, &dl) ? a : -1; }
int GLMirrorPortal::ClipSeg(seg_t *seg) { // this seg is completely behind the mirror! if (P_PointOnLineSide(seg->v1->x, seg->v1->y, linedef) && P_PointOnLineSide(seg->v2->x, seg->v2->y, linedef)) { return PClip_InFront; } return PClip_Inside; }
int GLMirrorPortal::ClipPoint(fixed_t x, fixed_t y) { if (P_PointOnLineSide(x, y, linedef)) { return PClip_InFront; } return PClip_Inside; }
// // Checks if a point is behind a subsector's 1-sided seg // bool P_IsInVoid(fixed_t x, fixed_t y, const subsector_t &ss) { for(int i = 0; i < ss.numlines; ++i) { const seg_t &seg = segs[ss.firstline + i]; if(seg.backsector) continue; if(P_PointOnLineSide(x, y, seg.linedef)) return true; } return false; }
int FBoundingBox::BoxOnLineSide (const line_t *ld) const { int p1; int p2; if (ld->dx == 0) { // ST_VERTICAL p1 = m_Box[BOXRIGHT] < ld->v1->x; p2 = m_Box[BOXLEFT] < ld->v1->x; if (ld->dy < 0) { p1 ^= 1; p2 ^= 1; } } else if (ld->dy == 0) { // ST_HORIZONTAL: p1 = m_Box[BOXTOP] > ld->v1->y; p2 = m_Box[BOXBOTTOM] > ld->v1->y; if (ld->dx < 0) { p1 ^= 1; p2 ^= 1; } } else if ((ld->dy ^ ld->dx) >= 0) { // ST_POSITIVE: p1 = P_PointOnLineSide (m_Box[BOXLEFT], m_Box[BOXTOP], ld); p2 = P_PointOnLineSide (m_Box[BOXRIGHT], m_Box[BOXBOTTOM], ld); } else { // ST_NEGATIVE: p1 = P_PointOnLineSide (m_Box[BOXRIGHT], m_Box[BOXTOP], ld); p2 = P_PointOnLineSide (m_Box[BOXLEFT], m_Box[BOXBOTTOM], ld); } return (p1 == p2) ? p1 : -1; }
// // P_BoxOnLineSide // Considers the line to be infinite // Returns side 0 or 1, -1 if box crosses the line. // killough 5/3/98: reformatted, cleaned up // int P_BoxOnLineSide(fixed_t *tmbox, line_t *ld) { switch (ld->slopetype) { int p; default: case ST_HORIZONTAL: return ((tmbox[BOXBOTTOM] > ld->v1->y) == (p = (tmbox[BOXTOP] > ld->v1->y)) ? p ^ (ld->dx < 0) : -1); case ST_VERTICAL: return ((tmbox[BOXLEFT] < ld->v1->x) == (p = (tmbox[BOXRIGHT] < ld->v1->x)) ? p ^ (ld->dy < 0) : -1); case ST_POSITIVE: return (P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld) == (p = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld)) ? p : -1); case ST_NEGATIVE: return ((P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld)) == (p = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld)) ? p : -1); } }
// // PTR_SlideTraverse // static boolean PTR_SlideTraverse(intercept_t * in) { line_t *li; if(!in->isaline) Con_Error("PTR_SlideTraverse: not a line?"); li = in->d.line; if(!(li->flags & ML_TWOSIDED)) { if(P_PointOnLineSide(slidemo->x, slidemo->y, li)) { // don't hit the back side return true; } goto isblocking; } // set openrange, opentop, openbottom P_LineOpening(li); if(openrange < slidemo->height) goto isblocking; // doesn't fit if(opentop - slidemo->z < slidemo->height) goto isblocking; // mobj is too high if(openbottom - slidemo->z > 24 * FRACUNIT) goto isblocking; // too big a step up // this line doesn't block movement return true; // the line does block movement, // see if it is closer than best so far isblocking: if(in->frac < bestslidefrac) { secondslidefrac = bestslidefrac; secondslideline = bestslideline; bestslidefrac = in->frac; bestslideline = li; } return false; // stop }
// // P_WallMomSlide // Adjusts the xmove / ymove // so that the next move will slide along the wall. // static void P_WallMomSlide(line_t *ld) { int side; angle_t lineangle; angle_t moveangle; angle_t deltaangle; fixed_t movelen; fixed_t newlen; // First check the simple cases. if(ld->slopetype == ST_HORIZONTAL) { tmymove = 0; return; } if(ld->slopetype == ST_VERTICAL) { tmxmove = 0; return; } side = P_PointOnLineSide(slidemo->x, slidemo->y, ld); lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy); if(side == 1) lineangle += ANG180; moveangle = R_PointToAngle2(0, 0, tmxmove, tmymove); deltaangle = moveangle - lineangle; if(deltaangle > ANG180) deltaangle += ANG180; lineangle >>= ANGLETOFINESHIFT; deltaangle >>= ANGLETOFINESHIFT; movelen = P_ApproxDistance(tmxmove, tmymove); newlen = FixedMul(movelen, finecosine[deltaangle]); tmxmove = FixedMul(newlen, finecosine[lineangle]); tmymove = FixedMul(newlen, finesine[lineangle]); }
// // R_AddLine // Clips the given segment and adds any visible pieces to the line list. // static void R_AddLine(seg_t *line) { INT32 x1, x2; angle_t angle1, angle2, span, tspan; static sector_t tempsec; // ceiling/water hack if (line->polyseg && !(line->polyseg->flags & POF_RENDERSIDES)) return; curline = line; // OPTIMIZE: quickly reject orthogonal back sides. angle1 = R_PointToAngle(line->v1->x, line->v1->y); angle2 = R_PointToAngle(line->v2->x, line->v2->y); // Clip to view edges. span = angle1 - angle2; // Back side? i.e. backface culling? if (span >= ANGLE_180) return; // Global angle needed by segcalc. rw_angle1 = angle1; angle1 -= viewangle; angle2 -= viewangle; tspan = angle1 + clipangle; if (tspan > doubleclipangle) { tspan -= doubleclipangle; // Totally off the left edge? if (tspan >= span) return; angle1 = clipangle; } tspan = clipangle - angle2; if (tspan > doubleclipangle) { tspan -= doubleclipangle; // Totally off the left edge? if (tspan >= span) return; angle2 = -(signed)clipangle; } // The seg is in the view range, but not necessarily visible. angle1 = (angle1+ANGLE_90)>>ANGLETOFINESHIFT; angle2 = (angle2+ANGLE_90)>>ANGLETOFINESHIFT; x1 = viewangletox[angle1]; x2 = viewangletox[angle2]; // Does not cross a pixel? if (x1 >= x2) // killough 1/31/98 -- change == to >= for robustness return; backsector = line->backsector; // Portal line if (line->linedef->special == 40 && P_PointOnLineSide(viewx, viewy, line->linedef) == 0) { if (portalrender < cv_maxportals.value) { // Find the other side! INT32 line2 = P_FindSpecialLineFromTag(40, line->linedef->tag, -1); if (line->linedef == &lines[line2]) line2 = P_FindSpecialLineFromTag(40, line->linedef->tag, line2); if (line2 >= 0) // found it! { R_AddPortal(line->linedef-lines, line2, x1, x2); // Remember the lines for later rendering //return; // Don't fill in that space now! goto clipsolid; } } // Recursed TOO FAR (viewing a portal within a portal) // So uhhh, render it as a normal wall instead or something ??? } // Single sided line? if (!backsector) goto clipsolid; backsector = R_FakeFlat(backsector, &tempsec, NULL, NULL, true); doorclosed = 0; // Closed door. #ifdef ESLOPE // Just don't bother checking this if one side is sloped. This is probably inefficient, but it's better than // random renderer stopping around slopes... if (!(frontsector->f_slope || frontsector->c_slope || backsector->f_slope || backsector->c_slope)) #endif if (backsector->ceilingheight <= frontsector->floorheight || backsector->floorheight >= frontsector->ceilingheight) { goto clipsolid; } // Check for automap fix. Store in doorclosed for r_segs.c doorclosed = R_DoorClosed(); if (doorclosed) goto clipsolid; // Window. if (backsector->ceilingheight != frontsector->ceilingheight || backsector->floorheight != frontsector->floorheight) { goto clippass; } // Reject empty lines used for triggers and special events. // Identical floor and ceiling on both sides, identical light levels on both sides, // and no middle texture. if ( #ifdef POLYOBJECTS !line->polyseg && #endif backsector->ceilingpic == frontsector->ceilingpic && backsector->floorpic == frontsector->floorpic #ifdef ESLOPE && backsector->f_slope == frontsector->f_slope && backsector->c_slope == frontsector->c_slope #endif && backsector->lightlevel == frontsector->lightlevel && !curline->sidedef->midtexture // Check offsets too! && backsector->floor_xoffs == frontsector->floor_xoffs && backsector->floor_yoffs == frontsector->floor_yoffs && backsector->floorpic_angle == frontsector->floorpic_angle && backsector->ceiling_xoffs == frontsector->ceiling_xoffs && backsector->ceiling_yoffs == frontsector->ceiling_yoffs && backsector->ceilingpic_angle == frontsector->ceilingpic_angle // Consider altered lighting. && backsector->floorlightsec == frontsector->floorlightsec && backsector->ceilinglightsec == frontsector->ceilinglightsec // Consider colormaps && backsector->extra_colormap == frontsector->extra_colormap && ((!frontsector->ffloors && !backsector->ffloors) || frontsector->tag == backsector->tag)) { return; } clippass: R_ClipPassWallSegment(x1, x2 - 1); return; clipsolid: R_ClipSolidWallSegment(x1, x2 - 1); }
int EV_Teleport( line_t *line,mobj_t *thing ) { int i; int tag; boolean flag; mobj_t *m,*fog; unsigned int an; sector_t *sector; fixed_t oldx, oldy, oldz; int side; side = !P_PointOnLineSide (thing->x, thing->y, line); if(thing->flags & MF_MISSILE) return 0; /* don't teleport missiles */ if(side == 1) /* don't teleport if hit back of line, */ return 0; /* so you can get out of teleporter */ tag = line->tag; for(i = 0; i < numsectors; i++) { if (sectors[ i ].tag == tag ) { for(m = mobjhead.next; m != &mobjhead; m = m->next) { // CALICO: skip removed mobjs if(m->latecall == P_RemoveMobjDeferred) continue; if(m->type != MT_TELEPORTMAN) continue; // not a teleportman sector = m->subsector->sector; if(sector-sectors != i ) continue; // wrong sector oldx = thing->x; oldy = thing->y; oldz = thing->z; thing->flags |= MF_TELEPORT; P_Telefrag(thing, m->x, m->y); flag = P_TryMove (thing, m->x, m->y); thing->flags &= ~MF_TELEPORT; if(!flag) return 0; /* move is blocked */ thing->z = thing->floorz; /* spawn teleport fog at source and destination */ fog = P_SpawnMobj (oldx, oldy, oldz, MT_TFOG); S_StartSound(fog, sfx_telept); an = m->angle >> ANGLETOFINESHIFT; fog = P_SpawnMobj (m->x+20*finecosine[an], m->y+20*finesine[an], thing->z, MT_TFOG); S_StartSound(fog, sfx_telept); if(thing->player) thing->reactiontime = 18; /* don't move for a bit */ thing->angle = m->angle; thing->momx = thing->momy = thing->momz = 0; return 1; } } } return 0; }
static void AddLine (seg_t *seg,sector_t * sector,subsector_t * polysub) { angle_t startAngle, endAngle; sector_t * backsector = NULL; sector_t bs; ClipWall.Clock(); if (GLPortal::mirrorline) { // this seg is completely behind the mirror! if (P_PointOnLineSide(seg->v1->x, seg->v1->y, GLPortal::mirrorline) && P_PointOnLineSide(seg->v2->x, seg->v2->y, GLPortal::mirrorline)) { ClipWall.Unclock(); return; } } startAngle = seg->v2->GetViewAngle(); endAngle = seg->v1->GetViewAngle(); // Back side, i.e. backface culling - read: endAngle >= startAngle! if (startAngle-endAngle<ANGLE_180 || !seg->linedef) { ClipWall.Unclock(); return; } if (!clipper.SafeCheckRange(startAngle, endAngle)) { ClipWall.Unclock(); return; } if (!seg->backsector) { clipper.SafeAddClipRange(startAngle, endAngle); } else if (!polysub) // Two-sided polyobjects never obstruct the view { if (sector->sectornum == seg->backsector->sectornum) { FTexture * tex = TexMan(seg->sidedef->GetTexture(side_t::mid)); if (!tex || tex->UseType==FTexture::TEX_Null) { // nothing to do here! ClipWall.Unclock(); seg->linedef->validcount=validcount; return; } backsector=sector; } else { // clipping checks are only needed when the backsector is not the same as the front sector gl_CheckViewArea(seg->v1, seg->v2, seg->frontsector, seg->backsector); backsector = gl_FakeFlat(seg->backsector, &bs, true); if (gl_CheckClip(seg->sidedef, sector, backsector)) { clipper.SafeAddClipRange(startAngle, endAngle); } } } else { // Backsector for polyobj segs is always the containing sector itself backsector = sector; } seg->linedef->flags |= ML_MAPPED; ClipWall.Unclock(); if (!gl_render_segs) { // rendering per linedef as opposed per seg is significantly more efficient // so mark the linedef as rendered here and render it completely. if (seg->linedef->validcount!=validcount) seg->linedef->validcount=validcount; else return; } GLWall wall; SetupWall.Clock(); wall.Process(seg, sector, backsector, polysub, gl_render_segs); rendered_lines++; SetupWall.Unclock(); }
bool gl_GetSpriteLight(AActor *self, fixed_t x, fixed_t y, fixed_t z, subsector_t * subsec, int desaturation, float * out, line_t *line, int side) { ADynamicLight *light; float frac, lr, lg, lb; float radius; bool changed = false; out[0]=out[1]=out[2]=0; for(int j=0;j<2;j++) { // Go through both light lists FLightNode * node = subsec->lighthead[j]; while (node) { light=node->lightsource; //if (!light->owned || light->target == NULL || light->target->IsVisibleToPlayer()) { if (!(light->flags2&MF2_DORMANT) && (!(light->flags4&MF4_DONTLIGHTSELF) || light->target != self)) { float dist = FVector3(FIXED2FLOAT(x - light->x), FIXED2FLOAT(y - light->y), FIXED2FLOAT(z - light->z)).Length(); radius = light->GetRadius() * gl_lights_size; if (dist < radius) { frac = 1.0f - (dist / radius); if (frac > 0) { if (line != NULL) { if (P_PointOnLineSide(light->x, light->y, line) != side) { node = node->nextLight; continue; } } lr = light->GetRed() / 255.0f * gl_lights_intensity; lg = light->GetGreen() / 255.0f * gl_lights_intensity; lb = light->GetBlue() / 255.0f * gl_lights_intensity; if (light->IsSubtractive()) { float bright = FVector3(lr, lg, lb).Length(); FVector3 lightColor(lr, lg, lb); lr = (bright - lr) * -1; lg = (bright - lg) * -1; lb = (bright - lb) * -1; } out[0] += lr * frac; out[1] += lg * frac; out[2] += lb * frac; changed = true; } } } } node = node->nextLight; } } // Desaturate dynamic lighting if applicable if (desaturation>0 && desaturation<=CM_DESAT31) { float gray=(out[0]*77 + out[1]*143 + out[2]*37)/257; out[0]= (out[0]*(31-desaturation)+ gray*desaturation)/31; out[1]= (out[1]*(31-desaturation)+ gray*desaturation)/31; out[2]= (out[2]*(31-desaturation)+ gray*desaturation)/31; } return changed; }
// // A_PainShootSkull // Spawn a lost soul and launch it at the target // void A_PainShootSkull (AActor *self, angle_t angle, const PClass *spawntype, int flags = 0, int limit = -1) { fixed_t x, y, z; AActor *other; angle_t an; int prestep; if (spawntype == NULL) return; if (self->DamageType==NAME_Massacre) return; // [RH] check to make sure it's not too close to the ceiling if (self->z + self->height + 8*FRACUNIT > self->ceilingz) { if (self->flags & MF_FLOAT) { self->velz -= 2*FRACUNIT; self->flags |= MF_INFLOAT; self->flags4 |= MF4_VFRICTION; } return; } // [RH] make this optional if (limit == -1 && (i_compatflags & COMPATF_LIMITPAIN)) limit = 21; if (limit) { // count total number of skulls currently on the level // if there are already 21 skulls on the level, don't spit another one int count = limit; FThinkerIterator iterator (spawntype); DThinker *othink; while ( (othink = iterator.Next ()) ) { if (--count == 0) return; } } // okay, there's room for another one an = angle >> ANGLETOFINESHIFT; prestep = 4*FRACUNIT + 3*(self->radius + GetDefaultByType(spawntype)->radius)/2; x = self->x + FixedMul (prestep, finecosine[an]); y = self->y + FixedMul (prestep, finesine[an]); z = self->z + 8*FRACUNIT; // Check whether the Lost Soul is being fired through a 1-sided // phares // wall or an impassible line, or a "monsters can't cross" line.// | // If it is, then we don't allow the spawn. // V FBoundingBox box(MIN(self->x, x), MIN(self->y, y), MAX(self->x, x), MAX(self->y, y)); FBlockLinesIterator it(box); line_t *ld; while ((ld = it.Next())) { if (!(ld->flags & ML_TWOSIDED) || (ld->flags & (ML_BLOCKING|ML_BLOCKMONSTERS|ML_BLOCKEVERYTHING))) { if (!(box.Left() > ld->bbox[BOXRIGHT] || box.Right() < ld->bbox[BOXLEFT] || box.Top() < ld->bbox[BOXBOTTOM] || box.Bottom() > ld->bbox[BOXTOP])) { if (P_PointOnLineSide(self->x,self->y,ld) != P_PointOnLineSide(x,y,ld)) return; // line blocks trajectory // ^ } } } other = Spawn (spawntype, x, y, z, ALLOW_REPLACE); // Check to see if the new Lost Soul's z value is above the // ceiling of its new sector, or below the floor. If so, kill it. if ((other->z > (other->Sector->ceilingplane.ZatPoint (other->x, other->y) - other->height)) || (other->z < other->Sector->floorplane.ZatPoint (other->x, other->y))) { // kill it immediately P_DamageMobj (other, self, self, TELEFRAG_DAMAGE, NAME_None);// ^ return; // | } // phares // Check for movements. if (!P_CheckPosition (other, other->x, other->y)) { // kill it immediately P_DamageMobj (other, self, self, TELEFRAG_DAMAGE, NAME_None); return; } // [RH] Lost souls hate the same things as their pain elementals other->CopyFriendliness (self, !(flags & PAF_NOTARGET)); if (!(flags & PAF_NOSKULLATTACK)) A_SkullAttack(other, SKULLSPEED); }
static void RB_CarveDecal(rbDecal_t *decal) { line_t *line; sector_t *sector; int side; int leftcount; int rightcount; byte pointsides[NUM_DECAL_POINTS]; int i, j; sector = decal->ssect->sector; for(i = 0; i < sector->linecount; ++i) { boolean ok = false; line = sector->lines[i]; if(line->backsector) { if(line->frontsector == line->backsector) { // ignore 2-sided lines continue; } if(decal->type == DCT_FLOOR) { if(line->backsector->floorheight == line->frontsector->floorheight) { // ignore flat floor heights continue; } } else if(decal->type == DCT_CEILING) { if(line->backsector->ceilingheight == line->frontsector->ceilingheight) { // ignore flat ceiling heights continue; } } } leftcount = 0; rightcount = 0; side = P_PointOnLineSide(decal->x, decal->y, line); memset(pointsides, 0, sizeof(NUM_DECAL_POINTS)); for(j = 0; j < decal->numpoints; ++j) { pointsides[j] = P_PointOnLineSide(FLOAT2FIXED(decal->points[j].x), FLOAT2FIXED(decal->points[j].y), line); leftcount += pointsides[j]; rightcount += pointsides[j] ^ 1; } if(leftcount == decal->numpoints || rightcount == decal->numpoints) { // all points are on one side continue; } // // some points are on the other side. begin cutting // for(j = 0; j < decal->numpoints; ++j) { int idx1 = j; int idx2 = j + 1; if(idx2 == decal->numpoints) { idx2 -= decal->numpoints; } // check if segments crosses sides if(pointsides[idx1] != pointsides[idx2]) { float x, y; float mx, my; if(!RB_IntersectDecalSegment(decal->points[idx1].x, decal->points[idx1].y, decal->points[idx2].x, decal->points[idx2].y, line, &x, &y)) { // didn't actually intersect continue; } ok = true; if(decal->numpoints+1 >= NUM_DECAL_POINTS) { fprintf(stderr, "RB_CarveDecal: Vertex overflow\n"); return; } decal->numpoints++; mx = decal->points[idx2].x - decal->points[idx1].x; my = decal->points[idx2].y - decal->points[idx1].y; // make room for new vertex memmove(&decal->points[idx2 + 1], &decal->points[idx2], (decal->numpoints - idx2 - 1) * sizeof(rbDecalVertex_t)); decal->points[idx2].x = x; decal->points[idx2].y = y; decal->points[idx2].z = decal->points[0].z; if(mx == 0) { // segment is perfectly straight, vertically. // tu should be already known decal->points[idx2].tu = decal->points[idx2+1].tu; } else { float mu = (x - decal->points[idx1].x) / mx; float tu2 = decal->points[idx2+1].tu; float tu1 = decal->points[idx1].tu; decal->points[idx2].tu = (tu2 - tu1) * mu + tu1; // goddamnit this is not going to work... if(decal->points[idx2].tu > 1) decal->points[idx2].tu = 1; if(decal->points[idx2].tu < -1) decal->points[idx2].tu = -1; } if(my == 0) { // segment is perfectly straight, horizontally. // tv should be already known decal->points[idx2].tv = decal->points[idx2+1].tv; } else { float mu = (y - decal->points[idx1].y) / my; float tv2 = decal->points[idx2+1].tv; float tv1 = decal->points[idx1].tv; decal->points[idx2].tv = (tv2 - tv1) * mu + tv1; // goddamnit this is not going to work... if(decal->points[idx2].tv > 1) decal->points[idx2].tv = 1; if(decal->points[idx2].tv < -1) decal->points[idx2].tv = -1; } memmove(&pointsides[idx2 + 1], &pointsides[idx2], decal->numpoints - idx2 - 1); pointsides[idx2] = side; // skip new vertex j++; } } if(ok) { // discard verts that's on the other side for(j = 0; j < decal->numpoints; ++j) { if(pointsides[j] != side) { // shift next item in array down memmove(&decal->points[j], &decal->points[j + 1], (decal->numpoints - j - 1) * sizeof(rbDecalVertex_t)); memmove(&pointsides[j], &pointsides[j + 1], decal->numpoints - j - 1); decal->numpoints--; j--; } } } } }
bool FTraceInfo::LineCheck(intercept_t *in, double dist, DVector3 hit) { int lineside; sector_t *entersector; double ff, fc, bf = 0, bc = 0; if (in->d.line->frontsector->sectornum == CurSector->sectornum) { lineside = 0; } else if (in->d.line->backsector && in->d.line->backsector->sectornum == CurSector->sectornum) { lineside = 1; } else { // Dammit. Why does Doom have to allow non-closed sectors? if (in->d.line->backsector == NULL) { lineside = 0; CurSector = in->d.line->frontsector; } else { lineside = P_PointOnLineSide(Start, in->d.line); CurSector = lineside ? in->d.line->backsector : in->d.line->frontsector; } } if (!(in->d.line->flags & ML_TWOSIDED)) { entersector = NULL; } else { entersector = (lineside == 0) ? in->d.line->backsector : in->d.line->frontsector; // For backwards compatibility: Ignore lines with the same sector on both sides. // This is the way Doom.exe did it and some WADs (e.g. Alien Vendetta MAP15) need it. if (i_compatflags & COMPATF_TRACE && in->d.line->backsector == in->d.line->frontsector) { // We must check special activation here because the code below is never reached. if (TraceFlags & TRACE_PCross) { P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_PCross); } if (TraceFlags & TRACE_Impact) { P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); } return true; } } ff = CurSector->floorplane.ZatPoint(hit); fc = CurSector->ceilingplane.ZatPoint(hit); if (entersector != NULL) { bf = entersector->floorplane.ZatPoint(hit); bc = entersector->ceilingplane.ZatPoint(hit); } sector_t *hsec = CurSector->GetHeightSec(); if (Results->CrossedWater == NULL && hsec != NULL && Start.Z > hsec->floorplane.ZatPoint(Start) && hit.Z <= hsec->floorplane.ZatPoint(hit)) { // hit crossed a water plane if (CheckSectorPlane(hsec, true)) { Results->CrossedWater = &level.sectors[CurSector->sectornum]; Results->CrossedWaterPos = Results->HitPos; Results->Distance = 0; } } if (hit.Z <= ff) { // hit floor in front of wall Results->HitType = TRACE_HitFloor; Results->HitTexture = CurSector->GetTexture(sector_t::floor); } else if (hit.Z >= fc) { // hit ceiling in front of wall Results->HitType = TRACE_HitCeiling; Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); } else if (entersector == NULL || hit.Z < bf || hit.Z > bc || in->d.line->flags & WallMask) { // hit the wall Results->HitType = TRACE_HitWall; Results->Tier = entersector == NULL ? TIER_Middle : hit.Z <= bf ? TIER_Lower : hit.Z >= bc ? TIER_Upper : TIER_Middle; if (TraceFlags & TRACE_Impact) { P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); } } else { // made it past the wall // check for 3D floors first if (entersector->e->XFloor.ffloors.Size()) { memcpy(&DummySector[sectorsel], entersector, sizeof(sector_t)); entersector = &DummySector[sectorsel]; sectorsel ^= 1; for (auto rover : entersector->e->XFloor.ffloors) { int entershootthrough = !!(rover->flags&FF_SHOOTTHROUGH); if (entershootthrough != inshootthrough && rover->flags&FF_EXISTS) { double ff_bottom = rover->bottom.plane->ZatPoint(hit); double ff_top = rover->top.plane->ZatPoint(hit); // clip to the part of the sector we are in if (hit.Z > ff_top) { // 3D floor height is the same as the floor height. We need to test a second spot to see if it is above or below if (fabs(bf - ff_top) < EQUAL_EPSILON) { double cf = entersector->floorplane.ZatPoint(entersector->centerspot); double ffc = rover->top.plane->ZatPoint(entersector->centerspot); if (ffc > cf) { bf = ff_top - EQUAL_EPSILON; } } // above if (bf < ff_top) { entersector->floorplane = *rover->top.plane; entersector->SetTexture(sector_t::floor, *rover->top.texture, false); entersector->ClearPortal(sector_t::floor); bf = ff_top; } } else if (hit.Z < ff_bottom) { // 3D floor height is the same as the ceiling height. We need to test a second spot to see if it is above or below if (fabs(bc - ff_bottom) < EQUAL_EPSILON) { double cc = entersector->ceilingplane.ZatPoint(entersector->centerspot); double fcc = rover->bottom.plane->ZatPoint(entersector->centerspot); if (fcc < cc) { bc = ff_bottom + EQUAL_EPSILON; } } //below if (bc > ff_bottom) { entersector->ceilingplane = *rover->bottom.plane; entersector->SetTexture(sector_t::ceiling, *rover->bottom.texture, false); entersector->ClearPortal(sector_t::ceiling); bc = ff_bottom; } } else { //hit the edge - equivalent to hitting the wall Results->HitType = TRACE_HitWall; Results->Tier = TIER_FFloor; Results->ffloor = rover; if ((TraceFlags & TRACE_Impact) && in->d.line->special) { P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); } goto cont; } } } } Results->HitType = TRACE_HitNone; if (TraceFlags & TRACE_PCross) { P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_PCross); } if (TraceFlags & TRACE_Impact) { // This is incorrect for "impact", but Hexen did this, so // we need to as well, for compatibility P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); } } cont: if (Results->HitType != TRACE_HitNone) { // We hit something, so figure out where exactly Results->Sector = &level.sectors[CurSector->sectornum]; if (Results->HitType != TRACE_HitWall && !CheckSectorPlane(CurSector, Results->HitType == TRACE_HitFloor)) { // trace is parallel to the plane (or right on it) if (entersector == NULL) { Results->HitType = TRACE_HitWall; Results->Tier = TIER_Middle; } else { if (hit.Z <= bf || hit.Z >= bc) { Results->HitType = TRACE_HitWall; Results->Tier = hit.Z <= bf ? TIER_Lower : hit.Z >= bc ? TIER_Upper : TIER_Middle; } else { Results->HitType = TRACE_HitNone; } } if (Results->HitType == TRACE_HitWall && TraceFlags & TRACE_Impact) { P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); } } if (Results->HitType == TRACE_HitWall) { Results->HitPos = hit; SetSourcePosition(); Results->Distance = dist; Results->Fraction = in->frac; Results->Line = in->d.line; Results->Side = lineside; } } if (TraceCallback != NULL && Results->HitType != TRACE_HitNone) { switch (TraceCallback(*Results, TraceCallbackData)) { case TRACE_Stop: return false; case TRACE_Abort: Results->HitType = TRACE_HitNone; return false; case TRACE_Skip: Results->HitType = TRACE_HitNone; break; default: break; } } if (Results->HitType == TRACE_HitNone) { CurSector = entersector; EnterDist = dist; return true; } else { return false; } }