bool RenderTranslucentPass::ClipSpriteColumnWithPortals(int x, VisibleSprite *spr) { RenderPortal *renderportal = Thread->Portal.get(); // [ZZ] 10.01.2016: don't clip sprites from the root of a skybox. if (renderportal->CurrentPortalInSkybox) return false; for (DrawSegment *seg : portaldrawsegs) { // ignore segs from other portals if (seg->CurrentPortalUniq != renderportal->CurrentPortalUniq) continue; // (all checks that are already done in R_CollectPortals have been removed for performance reasons.) // don't clip if the sprite is in front of the portal if (!P_PointOnLineSidePrecise(spr->WorldPos().X, spr->WorldPos().Y, seg->curline->linedef)) continue; // now if current column is covered by this drawseg, we clip it away if ((x >= seg->x1) && (x < seg->x2)) return true; } return false; }
int HWLinePortal::ClipPoint(const DVector2 &pos) { if (P_PointOnLineSidePrecise(pos, line())) { return PClip_InFront; } return PClip_Inside; }
int HWLinePortal::ClipSubsector(subsector_t *sub) { // this seg is completely behind the mirror for (unsigned int i = 0; i<sub->numlines; i++) { if (P_PointOnLineSidePrecise(sub->firstline[i].v1->fPos(), line()) == 0) return PClip_Inside; } return PClip_InFront; }
//----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void GLLineToLinePortal::DrawContents() { // TODO: Handle recursion more intelligently if (renderdepth>r_mirror_recursions) { ClearScreen(); return; } GLRenderer->mClipPortal = this; line_t *origin = glport->lines[0]->mOrigin; P_TranslatePortalXY(origin, ViewPos.X, ViewPos.Y); P_TranslatePortalAngle(origin, ViewAngle); P_TranslatePortalZ(origin, ViewPos.Z); P_TranslatePortalXY(origin, ViewPath[0].X, ViewPath[0].Y); P_TranslatePortalXY(origin, ViewPath[1].X, ViewPath[1].Y); if (!r_showviewer && camera != nullptr && P_PointOnLineSidePrecise(ViewPath[0], glport->lines[0]->mDestination) != P_PointOnLineSidePrecise(ViewPath[1], glport->lines[0]->mDestination)) { double distp = (ViewPath[0] - ViewPath[1]).Length(); if (distp > EQUAL_EPSILON) { double dist1 = (ViewPos - ViewPath[0]).Length(); double dist2 = (ViewPos - ViewPath[1]).Length(); if (dist1 + dist2 < distp + 1) { camera->renderflags |= RF_INVISIBLE; } } } SaveMapSection(); for (unsigned i = 0; i < lines.Size(); i++) { line_t *line = lines[i].seg->linedef->getPortalDestination(); subsector_t *sub; if (line->sidedef[0]->Flags & WALLF_POLYOBJ) sub = R_PointInSubsector(line->v1->fixX(), line->v1->fixY()); else sub = line->frontsector->subsectors[0]; int mapsection = sub->mapsection; currentmapsection[mapsection >> 3] |= 1 << (mapsection & 7); } GLRenderer->mViewActor = nullptr; GLRenderer->SetupView(ViewPos.X, ViewPos.Y, ViewPos.Z, ViewAngle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); ClearClipper(); gl_RenderState.SetClipLine(glport->lines[0]->mDestination); gl_RenderState.EnableClipLine(true); GLRenderer->DrawScene(DM_PORTAL); gl_RenderState.EnableClipLine(false); RestoreMapSection(); }
//----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- bool HWLineToLinePortal::Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) { // TODO: Handle recursion more intelligently auto &state = mState; if (state->renderdepth>r_mirror_recursions) { return false; } auto &vp = di->Viewpoint; di->mClipPortal = this; line_t *origin = glport->lines[0]->mOrigin; P_TranslatePortalXY(origin, vp.Pos.X, vp.Pos.Y); P_TranslatePortalXY(origin, vp.ActorPos.X, vp.ActorPos.Y); P_TranslatePortalAngle(origin, vp.Angles.Yaw); P_TranslatePortalZ(origin, vp.Pos.Z); P_TranslatePortalXY(origin, vp.Path[0].X, vp.Path[0].Y); P_TranslatePortalXY(origin, vp.Path[1].X, vp.Path[1].Y); if (!vp.showviewer && vp.camera != nullptr && P_PointOnLineSidePrecise(vp.Path[0], glport->lines[0]->mDestination) != P_PointOnLineSidePrecise(vp.Path[1], glport->lines[0]->mDestination)) { double distp = (vp.Path[0] - vp.Path[1]).Length(); if (distp > EQUAL_EPSILON) { double dist1 = (vp.Pos - vp.Path[0]).Length(); double dist2 = (vp.Pos - vp.Path[1]).Length(); if (dist1 + dist2 < distp + 1) { vp.camera->renderflags |= RF_MAYBEINVISIBLE; } } } for (unsigned i = 0; i < lines.Size(); i++) { line_t *line = lines[i].seg->linedef->getPortalDestination(); subsector_t *sub; if (line->sidedef[0]->Flags & WALLF_POLYOBJ) sub = di->Level->PointInRenderSubsector(line->v1->fixX(), line->v1->fixY()); else sub = line->frontsector->subsectors[0]; di->CurrentMapSections.Set(sub->mapsection); } vp.ViewActor = nullptr; di->SetClipLine(glport->lines[0]->mDestination); di->SetupView(rstate, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); ClearClipper(di, clipper); return true; }
static void P_SlopeLineToPoint (int lineid, const DVector3 &pos, bool slopeCeil) { int linenum; FLineIdIterator itr(lineid); while ((linenum = itr.Next()) >= 0) { const line_t *line = &lines[linenum]; sector_t *sec; secplane_t *plane; if (P_PointOnLineSidePrecise (pos, line) == 0) { sec = line->frontsector; } else { sec = line->backsector; } if (sec == NULL) { continue; } if (slopeCeil) { plane = &sec->ceilingplane; } else { plane = &sec->floorplane; } DVector3 p, v1, v2, cross; p[0] = line->v1->fX(); p[1] = line->v1->fY(); p[2] = plane->ZatPoint (line->v1); v1[0] = line->Delta().X; v1[1] = line->Delta().Y; v1[2] = plane->ZatPoint (line->v2) - p[2]; v2[0] = pos.X - p[0]; v2[1] = pos.Y - p[1]; v2[2] = pos.Z - p[2]; cross = v1 ^ v2; double len = cross.Length(); if (len == 0) { Printf ("Slope thing at (%f,%f) lies directly on its target line.\n", pos.X, pos.Y); return; } cross /= len; // Fix backward normals if ((cross.Z < 0 && !slopeCeil) || (cross.Z > 0 && slopeCeil)) { cross = -cross; } double dist = -cross[0] * pos.X - cross[1] * pos.Y - cross[2] * pos.Z; plane->set(cross[0], cross[1], cross[2], dist); } }
static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable) { TMap<int, double> vt_heights[2]; FMapThing *mt; bool vt_found = false; for (mt = firstmt; mt < lastmt; ++mt) { if (mt->info != NULL && mt->info->Type == NULL) { if (mt->info->Special == SMT_VertexFloorZ || mt->info->Special == SMT_VertexCeilingZ) { for (int i = 0; i < numvertexes; i++) { if (vertexes[i].fX() == mt->pos.X && vertexes[i].fY() == mt->pos.Y) { if (mt->info->Special == SMT_VertexFloorZ) { vt_heights[0][i] = mt->pos.Z; } else { vt_heights[1][i] = mt->pos.Z; } vt_found = true; } } mt->EdNum = 0; } } } for(int i = 0; i < numvertexdatas; i++) { int ii = oldvertextable == NULL ? i : oldvertextable[i]; if (vertexdatas[i].flags & VERTEXFLAG_ZCeilingEnabled) { vt_heights[1][ii] = vertexdatas[i].zCeiling; vt_found = true; } if (vertexdatas[i].flags & VERTEXFLAG_ZFloorEnabled) { vt_heights[0][ii] = vertexdatas[i].zFloor; vt_found = true; } } // If vertexdata_t is ever extended for non-slope usage, this will obviously have to be deferred or removed. delete[] vertexdatas; vertexdatas = NULL; numvertexdatas = 0; if (vt_found) { for (int i = 0; i < numsectors; i++) { sector_t *sec = §ors[i]; if (sec->linecount != 3) continue; // only works with triangular sectors DVector3 vt1, vt2, vt3, cross; DVector3 vec1, vec2; int vi1, vi2, vi3; vi1 = int(sec->lines[0]->v1 - vertexes); vi2 = int(sec->lines[0]->v2 - vertexes); vi3 = (sec->lines[1]->v1 == sec->lines[0]->v1 || sec->lines[1]->v1 == sec->lines[0]->v2)? int(sec->lines[1]->v2 - vertexes) : int(sec->lines[1]->v1 - vertexes); vt1 = DVector3(vertexes[vi1].fPos(), 0); vt2 = DVector3(vertexes[vi2].fPos(), 0); vt3 = DVector3(vertexes[vi3].fPos(), 0); for(int j=0; j<2; j++) { double *h1 = vt_heights[j].CheckKey(vi1); double *h2 = vt_heights[j].CheckKey(vi2); double *h3 = vt_heights[j].CheckKey(vi3); if (h1 == NULL && h2 == NULL && h3 == NULL) continue; vt1.Z = h1? *h1 : j==0? sec->GetPlaneTexZ(sector_t::floor) : sec->GetPlaneTexZ(sector_t::ceiling); vt2.Z = h2? *h2 : j==0? sec->GetPlaneTexZ(sector_t::floor) : sec->GetPlaneTexZ(sector_t::ceiling); vt3.Z = h3? *h3 : j==0? sec->GetPlaneTexZ(sector_t::floor) : sec->GetPlaneTexZ(sector_t::ceiling); if (P_PointOnLineSidePrecise(vertexes[vi3].fX(), vertexes[vi3].fY(), sec->lines[0]) == 0) { vec1 = vt2 - vt3; vec2 = vt1 - vt3; } else { vec1 = vt1 - vt3; vec2 = vt2 - vt3; } DVector3 cross = vec1 ^ vec2; double len = cross.Length(); if (len == 0) { // Only happens when all vertices in this sector are on the same line. // Let's just ignore this case. continue; } cross /= len; // Fix backward normals if ((cross.Z < 0 && j == 0) || (cross.Z > 0 && j == 1)) { cross = -cross; } secplane_t *plane = j==0? &sec->floorplane : &sec->ceilingplane; double dist = -cross[0] * vertexes[vi3].fX() - cross[1] * vertexes[vi3].fY() - cross[2] * vt3.Z; plane->set(cross[0], cross[1], cross[2], dist); } } } }
// [RH] Modified to support different source and destination ids. // [RH] Modified some more to be accurate. bool EV_SilentLineTeleport (line_t *line, int side, AActor *thing, int id, INTBOOL reverse) { int i; line_t *l; if (side || thing->flags2 & MF2_NOTELEPORT || !line || line->sidedef[1] == NULL) return false; FLineIdIterator itr(id); while ((i = itr.Next()) >= 0) { if (line->Index() == i) continue; if ((l=&level.lines[i]) != line && l->backsector) { // Get the thing's position along the source linedef double pos; DVector2 npos; // offsets from line double den; den = line->Delta().LengthSquared(); if (den == 0) { pos = 0; npos.Zero(); } else { double num = (thing->Pos().XY() - line->v1->fPos()) | line->Delta(); if (num <= 0) { pos = 0; } else if (num >= den) { pos = 1; } else { pos = num / den; } npos = thing->Pos().XY() - line->v1->fPos() - line->Delta() * pos; } // Get the angle between the two linedefs, for rotating // orientation and velocity. Rotate 180 degrees, and flip // the position across the exit linedef, if reversed. DAngle angle = l->Delta().Angle() - line->Delta().Angle(); if (!reverse) { angle += 180.; pos = 1 - pos; } // Sine, cosine of angle adjustment double s = angle.Sin(); double c = angle.Cos(); DVector2 p; // Rotate position along normal to match exit linedef p.X = npos.X*c - npos.Y*s; p.Y = npos.Y*c + npos.X*s; // Interpolate position across the exit linedef p += l->v1->fPos() + pos*l->Delta(); // Whether this is a player, and if so, a pointer to its player_t. // Voodoo dolls are excluded by making sure thing->player->mo==thing. player_t *player = thing->player && thing->player->mo == thing ? thing->player : NULL; // Whether walking towards first side of exit linedef steps down bool stepdown = l->frontsector->floorplane.ZatPoint(p) < l->backsector->floorplane.ZatPoint(p); // Height of thing above ground double 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. // Is this really still necessary with real math instead of imprecise trig tables? #if 1 int side = reverse || (player && stepdown); int fudge = FUDGEFACTOR; double dx = line->Delta().X; double dy = line->Delta().Y; // Make sure we are on correct side of exit linedef. while (P_PointOnLineSidePrecise(p, l) != side && --fudge >= 0) { if (fabs(dx) > fabs(dy)) p.Y -= (dx < 0) != side ? -1 : 1; else p.X += (dy < 0) != side ? -1 : 1; } #endif // 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(p); // Attempt to teleport, aborting if blocked if (!P_TeleportMove (thing, DVector3(p, z), false)) { return false; } if (thing == players[consoleplayer].camera) { R_ResetViewInterpolation (); } // Rotate thing's orientation according to difference in linedef angles thing->Angles.Yaw += angle; // Rotate thing's velocity to come out of exit just like it entered p = thing->Vel.XY(); thing->Vel.X = p.X*c - p.Y*s; thing->Vel.Y = p.Y*c + p.X*s; // Adjust a player's view, in case there has been a height change if (player && player->mo == thing) { // Adjust player's local copy of velocity p = player->Vel; player->Vel.X = p.X*c - p.Y*s; player->Vel.Y = p.Y*c + p.X*s; // Save the current deltaviewheight, used in stepping double deltaviewheight = player->deltaviewheight; // Clear deltaviewheight, since we don't want any changes now player->deltaviewheight = 0; // Set player's view according to the newly set parameters P_CalcHeight(player); // Reset the delta to have the same dynamics as before player->deltaviewheight = deltaviewheight; } return true; } } return false; }
void RenderPortal::RenderLinePortal(PortalDrawseg* pds, int depth) { auto viewport = Thread->Viewport.get(); auto &viewpoint = viewport->viewpoint; // [ZZ] check depth. fill portal with black if it's exceeding the visual recursion limit, and continue like nothing happened. if (depth >= r_portal_recursions) { uint8_t color = (uint8_t)BestColor((uint32_t *)GPalette.BaseColors, 0, 0, 0, 0, 255); int spacing = viewport->RenderTarget->GetPitch(); for (int x = pds->x1; x < pds->x2; x++) { if (x < 0 || x >= viewport->RenderTarget->GetWidth()) continue; int Ytop = pds->ceilingclip[x - pds->x1]; int Ybottom = pds->floorclip[x - pds->x1]; if (viewport->RenderTarget->IsBgra()) { uint32_t *dest = (uint32_t*)viewport->RenderTarget->GetBuffer() + x + Ytop * spacing; uint32_t c = GPalette.BaseColors[color].d; for (int y = Ytop; y <= Ybottom; y++) { *dest = c; dest += spacing; } } else { uint8_t *dest = viewport->RenderTarget->GetBuffer() + x + Ytop * spacing; for (int y = Ytop; y <= Ybottom; y++) { *dest = color; dest += spacing; } } } if (r_highlight_portals) RenderLinePortalHighlight(pds); return; } DAngle startang = viewpoint.Angles.Yaw; DVector3 startpos = viewpoint.Pos; DVector3 savedpath[2] = { viewpoint.Path[0], viewpoint.Path[1] }; ActorRenderFlags savedvisibility = viewpoint.camera ? viewpoint.camera->renderflags & RF_INVISIBLE : ActorRenderFlags::FromInt(0); viewpoint.camera->renderflags &= ~RF_INVISIBLE; CurrentPortalUniq++; unsigned int portalsAtStart = WallPortals.Size(); if (pds->mirror) { //vertex_t *v1 = ds->curline->v1; vertex_t *v1 = pds->src->v1; // Reflect the current view behind the mirror. if (pds->src->Delta().X == 0) { // vertical mirror viewpoint.Pos.X = v1->fX() - startpos.X + v1->fX(); } else if (pds->src->Delta().Y == 0) { // horizontal mirror viewpoint.Pos.Y = v1->fY() - startpos.Y + v1->fY(); } else { // any mirror vertex_t *v2 = pds->src->v2; double dx = v2->fX() - v1->fX(); double dy = v2->fY() - v1->fY(); double x1 = v1->fX(); double y1 = v1->fY(); double x = startpos.X; double y = startpos.Y; // the above two cases catch len == 0 double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy); viewpoint.Pos.X = (x1 + r * dx) * 2 - x; viewpoint.Pos.Y = (y1 + r * dy) * 2 - y; } viewpoint.Angles.Yaw = pds->src->Delta().Angle() * 2 - startang; } else { P_TranslatePortalXY(pds->src, viewpoint.Pos.X, viewpoint.Pos.Y); P_TranslatePortalZ(pds->src, viewpoint.Pos.Z); P_TranslatePortalAngle(pds->src, viewpoint.Angles.Yaw); P_TranslatePortalXY(pds->src, viewpoint.Path[0].X, viewpoint.Path[0].Y); P_TranslatePortalXY(pds->src, viewpoint.Path[1].X, viewpoint.Path[1].Y); if (!viewpoint.showviewer && viewpoint.camera && P_PointOnLineSidePrecise(viewpoint.Path[0], pds->dst) != P_PointOnLineSidePrecise(viewpoint.Path[1], pds->dst)) { double distp = (viewpoint.Path[0] - viewpoint.Path[1]).Length(); if (distp > EQUAL_EPSILON) { double dist1 = (viewpoint.Pos - viewpoint.Path[0]).Length(); double dist2 = (viewpoint.Pos - viewpoint.Path[1]).Length(); if (dist1 + dist2 < distp + 1) { viewpoint.camera->renderflags |= RF_INVISIBLE; } } } } viewpoint.Sin = viewpoint.Angles.Yaw.Sin(); viewpoint.Cos = viewpoint.Angles.Yaw.Cos(); viewpoint.TanSin = Thread->Viewport->viewwindow.FocalTangent * viewpoint.Sin; viewpoint.TanCos = Thread->Viewport->viewwindow.FocalTangent * viewpoint.Cos; CopyStackedViewParameters(); validcount++; PortalDrawseg* prevpds = CurrentPortal; CurrentPortal = pds; Thread->PlaneList->ClearKeepFakePlanes(); Thread->ClipSegments->Clear(pds->x1, pds->x2); WindowLeft = pds->x1; WindowRight = pds->x2; // RF_XFLIP should be removed before calling the root function int prevmf = MirrorFlags; if (pds->mirror) { if (MirrorFlags & RF_XFLIP) MirrorFlags &= ~RF_XFLIP; else MirrorFlags |= RF_XFLIP; } // some portals have height differences, account for this here Thread->Clip3D->EnterSkybox(); // push 3D floor height map CurrentPortalInSkybox = false; // first portal in a skybox should set this variable to false for proper clipping in skyboxes. // first pass, set clipping auto ceilingclip = Thread->OpaquePass->ceilingclip; auto floorclip = Thread->OpaquePass->floorclip; memcpy(ceilingclip + pds->x1, &pds->ceilingclip[0], pds->len * sizeof(*ceilingclip)); memcpy(floorclip + pds->x1, &pds->floorclip[0], pds->len * sizeof(*floorclip)); Thread->OpaquePass->RenderScene(); Thread->Clip3D->ResetClip(); // reset clips (floor/ceiling) if (!savedvisibility && viewpoint.camera) viewpoint.camera->renderflags &= ~RF_INVISIBLE; Thread->PlaneList->Render(); RenderPlanePortals(); double vzp = viewpoint.Pos.Z; int prevuniq = CurrentPortalUniq; // depth check is in another place right now unsigned int portalsAtEnd = WallPortals.Size(); for (; portalsAtStart < portalsAtEnd; portalsAtStart++) { RenderLinePortal(WallPortals[portalsAtStart], depth + 1); } int prevuniq2 = CurrentPortalUniq; CurrentPortalUniq = prevuniq; if (Thread->MainThread) NetUpdate(); Thread->TranslucentPass->Render(); // this is required since with portals there often will be cases when more than 80% of the view is inside a portal. if (Thread->MainThread) NetUpdate(); Thread->Clip3D->LeaveSkybox(); // pop 3D floor height map CurrentPortalUniq = prevuniq2; // draw a red line around a portal if it's being highlighted if (r_highlight_portals) RenderLinePortalHighlight(pds); CurrentPortal = prevpds; MirrorFlags = prevmf; viewpoint.Angles.Yaw = startang; viewpoint.Pos = startpos; viewpoint.Path[0] = savedpath[0]; viewpoint.Path[1] = savedpath[1]; }
// // A_PainShootSkull // Spawn a lost soul and launch it at the target // void A_PainShootSkull (AActor *self, angle_t angle, PClassActor *spawntype, int flags = 0, int limit = -1) { AActor *other; 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->Top() + 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 prestep = 4*FRACUNIT + 3*(self->radius + GetDefaultByType(spawntype)->radius)/2; // NOTE: The following code contains some advance work for line-to-line portals which is currenty inactive. fixedvec2 dist = Vec2Angle(prestep, angle); fixedvec3 pos = self->Vec3Offset(dist.x, dist.y, 8 * FRACUNIT, true); fixedvec3 src = self->Pos(); for (int i = 0; i < 2; i++) { // 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(src.x, pos.x), MIN(src.y, pos.y), MAX(src.x, pos.x), MAX(src.y, pos.y)); FBlockLinesIterator it(box); line_t *ld; bool inportal = false; while ((ld = it.Next())) { if (ld->isLinePortal() && i == 0) { if (P_PointOnLineSidePrecise(src.x, src.y, ld) == 0 && P_PointOnLineSidePrecise(pos.x, pos.y, ld) == 1) { // crossed a portal line from front to back, we need to repeat the check on the other side as well. inportal = true; } } else 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_PointOnLineSidePrecise(src.x, src.y, ld) != P_PointOnLineSidePrecise(pos.x, pos.y, ld)) return; // line blocks trajectory // ^ } } } if (!inportal) break; // recalculate position and redo the check on the other side of the portal pos = self->Vec3Offset(dist.x, dist.y, 8 * FRACUNIT, false); src.x = pos.x - dist.x; src.y = pos.y - dist.y; } other = Spawn (spawntype, pos.x, pos.y, pos.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->HighestCeiling(other) - other->height)) || (other->Z() < other->Sector->LowestFloor(other))) { // kill it immediately P_DamageMobj (other, self, self, TELEFRAG_DAMAGE, NAME_None);// ^ return; // | } // phares // Check for movements. if (!P_CheckPosition (other, other->Pos())) { // 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); }
bool FTraceInfo::TraceTraverse (int ptflags) { // Do a 3D floor check in the starting sector Setup3DFloors(); FPathTraverse it(Start.X, Start.Y, Vec.X * MaxDist, Vec.Y * MaxDist, ptflags | PT_DELTA, startfrac); intercept_t *in; int lastsplashsector = -1; while ((in = it.Next())) { // Deal with splashes in 3D floors (but only run once per sector, not each iteration - and stop if something was found.) if (Results->Crossed3DWater == NULL && lastsplashsector != CurSector->sectornum) { for (auto rover : CurSector->e->XFloor.ffloors) { if ((rover->flags & FF_EXISTS) && (rover->flags&FF_SWIMMABLE)) { if (Check3DFloorPlane(rover, false)) { // only consider if the plane is above the actual floor. if (rover->top.plane->ZatPoint(Results->HitPos) > CurSector->floorplane.ZatPoint(Results->HitPos)) { Results->Crossed3DWater = rover; Results->Crossed3DWaterPos = Results->HitPos; Results->Distance = 0; } } } } lastsplashsector = CurSector->sectornum; } double dist = MaxDist * in->frac; DVector3 hit = Start + Vec * dist; // Crossed a floor portal? if (Vec.Z < 0 && !CurSector->PortalBlocksMovement(sector_t::floor)) { // calculate position where the portal is crossed double portz = CurSector->GetPortalPlaneZ(sector_t::floor); if (hit.Z < portz && limitz > portz) { limitz = portz; EnterSectorPortal(it, sector_t::floor, (portz - Start.Z) / (Vec.Z * MaxDist), CurSector); continue; } } // ... or a ceiling portal? else if (Vec.Z > 0 && !CurSector->PortalBlocksMovement(sector_t::ceiling)) { // calculate position where the portal is crossed double portz = CurSector->GetPortalPlaneZ(sector_t::ceiling); if (hit.Z > portz && limitz < portz) { limitz = portz; EnterSectorPortal(it, sector_t::ceiling, (portz - Start.Z) / (Vec.Z * MaxDist), CurSector); continue; } } if (in->isaline) { if (in->d.line->isLinePortal() && P_PointOnLineSidePrecise(Start, in->d.line) == 0) { sector_t *entersector = in->d.line->backsector; if (entersector == NULL || (hit.Z >= entersector->floorplane.ZatPoint(hit) && hit.Z <= entersector->ceilingplane.ZatPoint(hit))) { FLinePortal *port = in->d.line->getPortal(); // The caller cannot handle portals without global offset. if (port->mType == PORTT_LINKED || !(TraceFlags & TRACE_PortalRestrict)) { EnterLinePortal(it, in); continue; } } } if (!LineCheck(in, dist, hit)) break; } else if ((in->d.thing->flags & ActorMask) && in->d.thing != IgnoreThis) { if (!ThingCheck(in, dist, hit)) break; } } if (Results->HitType == TRACE_HitNone) { if (CurSector->PortalBlocksMovement(sector_t::floor) && CheckSectorPlane(CurSector, true)) { Results->HitType = TRACE_HitFloor; Results->HitTexture = CurSector->GetTexture(sector_t::floor); } else if (CurSector->PortalBlocksMovement(sector_t::ceiling) && CheckSectorPlane(CurSector, false)) { Results->HitType = TRACE_HitCeiling; Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); } } // check for intersection with floor/ceiling Results->Sector = &level.sectors[CurSector->sectornum]; if (Results->CrossedWater == NULL && CurSector->heightsec != NULL && CurSector->heightsec->floorplane.ZatPoint(Start) < Start.Z && CurSector->heightsec->floorplane.ZatPoint(Results->HitPos) >= Results->HitPos.Z) { // Save the result so that the water check doesn't destroy it. FTraceResults *res = Results; FTraceResults hsecResult; Results = &hsecResult; if (CheckSectorPlane(CurSector->heightsec, true)) { Results->CrossedWater = &level.sectors[CurSector->sectornum]; Results->CrossedWaterPos = Results->HitPos; Results->Distance = 0; } Results = res; } if (Results->HitType == TRACE_HitNone && Results->Distance == 0) { Results->HitPos = Start + Vec * MaxDist; SetSourcePosition(); Results->Distance = MaxDist; Results->Fraction = 1.; } return Results->HitType != TRACE_HitNone; }