diff --git a/src/actor.h b/src/actor.h index 42865543c3..35afef397d 100644 --- a/src/actor.h +++ b/src/actor.h @@ -40,6 +40,7 @@ #include "memarena.h" #include "g_level.h" #include "tflags.h" +#include "portal.h" struct subsector_t; class PClassAmmo; @@ -1182,24 +1183,13 @@ public: } fixedvec3 Pos() const { - fixedvec3 ret = { X(), Y(), Z() }; - return ret; - } - fixedvec3 PosRelative(AActor *other) const - { - fixedvec3 ret = { X(), Y(), Z() }; - return ret; - } - fixedvec3 PosRelative(sector_t *sec) const - { - fixedvec3 ret = { X(), Y(), Z() }; - return ret; - } - fixedvec3 PosRelative(line_t *line) const - { - fixedvec3 ret = { X(), Y(), Z() }; - return ret; + return __pos; } + + fixedvec3 PosRelative(AActor *other) const; + fixedvec3 PosRelative(sector_t *sec) const; + fixedvec3 PosRelative(line_t *line) const; + fixed_t SoundX() const { return X(); @@ -1377,11 +1367,6 @@ inline fixedvec2 Vec2Angle(fixed_t length, angle_t angle) return ret; } -inline fixedvec3 PosRelative(const fixedvec3 &pos, line_t *line, sector_t *refsec = NULL) -{ - return pos; -} - void PrintMiscActorInfo(AActor * query); AActor *P_LinePickActor(AActor *t1, angle_t angle, fixed_t distance, int pitch, ActorFlags actorMask, DWORD wallMask); diff --git a/src/b_move.cpp b/src/b_move.cpp index 1fcf207ed9..dbecd2a48d 100644 --- a/src/b_move.cpp +++ b/src/b_move.cpp @@ -83,10 +83,12 @@ bool DBot::Move (ticcmd_t *cmd) player->mo->movedir = DI_NODIR; good = 0; + spechit_t spechit1; line_t *ld; - while (spechit.Pop (ld)) + while (spechit.Pop (spechit1)) { + ld = spechit1.line; bool tryit = true; if (ld->special == Door_LockedRaise && !P_CheckKeys (player->mo, ld->args[3], false)) diff --git a/src/basictypes.h b/src/basictypes.h index 0ae68f39b6..3159de4286 100644 --- a/src/basictypes.h +++ b/src/basictypes.h @@ -81,16 +81,62 @@ union QWORD_UNION typedef SDWORD fixed_t; typedef DWORD dsfixed_t; // fixedpt used by span drawer -struct fixedvec3 -{ - fixed_t x, y, z; -}; - struct fixedvec2 { fixed_t x, y; + + fixedvec2 &operator +=(const fixedvec2 &other) + { + x += other.x; + y += other.y; + return *this; + } }; +struct fixedvec3 +{ + fixed_t x, y, z; + + fixedvec3 &operator +=(const fixedvec3 &other) + { + x += other.x; + y += other.y; + z += other.z; + return *this; + } + + fixedvec3 &operator +=(const fixedvec2 &other) + { + x += other.x; + y += other.y; + return *this; + } + + operator fixedvec2() + { + fixedvec2 ret = { x, y }; + return ret; + } + +}; + +inline fixedvec2 operator +(const fixedvec2 &v1, const fixedvec2 &v2) +{ + fixedvec2 v = { v1.x + v2.x, v1.y + v2.y }; + return v; +} + +inline fixedvec3 operator +(const fixedvec3 &v1, const fixedvec3 &v2) +{ + fixedvec3 v = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; + return v; +} + +inline fixedvec3 operator +(const fixedvec3 &v1, const fixedvec2 &v2) +{ + fixedvec3 v = { v1.x + v2.x, v1.y + v2.y, v1.z }; + return v; +} #define FIXED_MAX (signed)(0x7fffffff) #define FIXED_MIN (signed)(0x80000000) diff --git a/src/g_doom/a_painelemental.cpp b/src/g_doom/a_painelemental.cpp index 6c02c6517c..1f88fa033a 100644 --- a/src/g_doom/a_painelemental.cpp +++ b/src/g_doom/a_painelemental.cpp @@ -111,7 +111,7 @@ void A_PainShootSkull (AActor *self, angle_t angle, PClassActor *spawntype, int 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); + pos = self->Vec3Offset(dist.x, dist.y, 8 * FRACUNIT); src.x = pos.x - dist.x; src.y = pos.y - dist.y; @@ -122,9 +122,9 @@ void A_PainShootSkull (AActor *self, angle_t angle, PClassActor *spawntype, int // 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))) + if ((other->Top() > + (other->Sector->HighestCeilingAt(other))) || + (other->Z() < other->Sector->LowestFloorAt(other))) { // kill it immediately P_DamageMobj (other, self, self, TELEFRAG_DAMAGE, NAME_None);// ^ diff --git a/src/g_shared/a_fastprojectile.cpp b/src/g_shared/a_fastprojectile.cpp index 29a72c23c2..ca57ad5572 100644 --- a/src/g_shared/a_fastprojectile.cpp +++ b/src/g_shared/a_fastprojectile.cpp @@ -82,7 +82,7 @@ void AFastProjectile::Tick () if (tm.ceilingline && tm.ceilingline->backsector && tm.ceilingline->backsector->GetTexture(sector_t::ceiling) == skyflatnum && - Z() >= tm.ceilingline->backsector->ceilingplane.ZatPoint (this)) + Z() >= tm.ceilingline->backsector->ceilingplane.ZatPoint(PosRelative(tm.ceilingline))) { // Hack to prevent missiles exploding against the sky. // Does not handle sky floors. diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 32a4167bbe..f66659df9d 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -414,7 +414,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialPosition) // Move item back to its original location fixed_t _x, _y; - sector_t *sec; _x = self->SpawnPoint[0]; _y = self->SpawnPoint[1]; @@ -422,12 +421,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialPosition) self->UnlinkFromWorld(); self->SetXY(_x, _y); self->LinkToWorld(true); - sec = self->Sector; - self->dropoffz = - self->floorz = sec->floorplane.ZatPoint(_x, _y); - self->ceilingz = sec->ceilingplane.ZatPoint(_x, _y); - self->SetZ(self->floorz); - P_FindFloorCeiling(self, FFCF_ONLYSPAWNPOS); + self->SetZ(self->Sector->floorplane.ZatPoint(_x, _y)); + P_FindFloorCeiling(self, FFCF_ONLYSPAWNPOS | FFCF_NOPORTALS); // no portal checks here so that things get spawned in this sector. if (self->flags & MF_SPAWNCEILING) { @@ -450,7 +445,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialPosition) { self->SetZ(self->SpawnPoint[2] + self->floorz); } - // Redo floor/ceiling check, in case of 3D floors + // Redo floor/ceiling check, in case of 3D floors and portals P_FindFloorCeiling(self, FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT); if (self->Z() < self->floorz) { // Do not reappear under the floor, even if that's where we were for the diff --git a/src/g_strife/a_strifestuff.cpp b/src/g_strife/a_strifestuff.cpp index 1c1d5311db..9cf2b09d87 100644 --- a/src/g_strife/a_strifestuff.cpp +++ b/src/g_strife/a_strifestuff.cpp @@ -659,7 +659,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CheckTerrain) sector_t *sec = self->Sector; - if (self->Z() == sec->floorplane.ZatPoint(self)) + if (self->Z() == sec->floorplane.ZatPoint(self) && sec->PortalBlocksMovement(sector_t::floor)) { if (sec->special == Damage_InstantDeath) { diff --git a/src/m_bbox.cpp b/src/m_bbox.cpp index f7827ef725..a8f55e4cf6 100644 --- a/src/m_bbox.cpp +++ b/src/m_bbox.cpp @@ -34,7 +34,7 @@ // //========================================================================== -FBoundingBox::FBoundingBox(fixed_t x, fixed_t y, fixed_t radius) +void FBoundingBox::setBox(fixed_t x, fixed_t y, fixed_t radius) { m_Box[BOXTOP] = (fixed_t)MIN((SQWORD)y + radius, FIXED_MAX); m_Box[BOXLEFT] = (fixed_t)MAX((SQWORD)x - radius, FIXED_MIN); diff --git a/src/m_bbox.h b/src/m_bbox.h index 174cd6ecfb..c43bb3c799 100644 --- a/src/m_bbox.h +++ b/src/m_bbox.h @@ -43,7 +43,12 @@ public: m_Box[BOXBOTTOM] = bottom; } - FBoundingBox(fixed_t x, fixed_t y, fixed_t radius); + FBoundingBox(fixed_t x, fixed_t y, fixed_t radius) + { + setBox(x, y, radius); + } + + void setBox(fixed_t x, fixed_t y, fixed_t radius); void ClearBox () { diff --git a/src/p_3dfloors.cpp b/src/p_3dfloors.cpp index 61c4564b91..cec3b1c509 100644 --- a/src/p_3dfloors.cpp +++ b/src/p_3dfloors.cpp @@ -775,6 +775,8 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li FTextureID highestfloorpic; int highestfloorterrain = -1; FTextureID lowestceilingpic; + sector_t *lowestceilingsec = NULL, *highestfloorsec = NULL; + secplane_t *highestfloorplanes[2] = { NULL, NULL }; highestfloorpic.SetInvalid(); lowestceilingpic.SetInvalid(); @@ -798,6 +800,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li { lowestceiling = ff_bottom; lowestceilingpic = *rover->bottom.texture; + lowestceilingsec = j == 0 ? linedef->frontsector : linedef->backsector; } if(ff_top > highestfloor && delta1 < delta2 && (!restrict || thing->Z() >= ff_top)) @@ -805,6 +808,8 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li highestfloor = ff_top; highestfloorpic = *rover->top.texture; highestfloorterrain = rover->model->GetTerrain(rover->top.isceiling); + highestfloorsec = j == 0 ? linedef->frontsector : linedef->backsector; + highestfloorplanes[j] = rover->top.plane; } if(ff_top > lowestfloor[j] && ff_top <= thing->Z() + thing->MaxStepHeight) lowestfloor[j] = ff_top; } @@ -815,12 +820,24 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li open.bottom = highestfloor; open.floorpic = highestfloorpic; open.floorterrain = highestfloorterrain; + open.bottomsec = highestfloorsec; + if (highestfloorplanes[0]) + { + open.frontfloorplane = *highestfloorplanes[0]; + if (open.frontfloorplane.c < 0) open.frontfloorplane.FlipVert(); + } + if (highestfloorplanes[1]) + { + open.backfloorplane = *highestfloorplanes[1]; + if (open.backfloorplane.c < 0) open.backfloorplane.FlipVert(); + } } if(lowestceiling < open.top) { open.top = lowestceiling; open.ceilingpic = lowestceilingpic; + open.topsec = lowestceilingsec; } open.lowfloor = MIN(lowestfloor[0], lowestfloor[1]); diff --git a/src/p_3dmidtex.cpp b/src/p_3dmidtex.cpp index 1c4ba7b7ca..c5a205891b 100644 --- a/src/p_3dmidtex.cpp +++ b/src/p_3dmidtex.cpp @@ -294,7 +294,9 @@ bool P_LineOpening_3dMidtex(AActor *thing, const line_t *linedef, FLineOpening & open.abovemidtex = true; open.floorpic = linedef->sidedef[0]->GetTexture(side_t::mid); open.floorterrain = TerrainTypes[open.floorpic]; - + open.frontfloorplane.SetAtHeight(tt, sector_t::floor); + open.backfloorplane.SetAtHeight(tt, sector_t::floor); + } // returns true if it touches the midtexture return (abs(thing->Z() - tt) <= thing->MaxStepHeight); diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 62118bd3f8..52e11ee8b3 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -76,7 +76,6 @@ #include "actorptrselect.h" #include "farchive.h" #include "decallib.h" -#include "portal.h" #include "p_terrain.h" #include "version.h" #include "p_effect.h" @@ -4179,49 +4178,19 @@ bool DLevelScript::DoCheckActorTexture(int tid, AActor *activator, int string, b // they're obviously not the same. return 0; } - int i, numff; FTextureID secpic; - sector_t *sec = actor->Sector; - numff = sec->e->XFloor.ffloors.Size(); + sector_t *resultsec; + F3DFloor *resffloor; if (floor) { - // Looking through planes from top to bottom - for (i = 0; i < numff; ++i) - { - F3DFloor *ff = sec->e->XFloor.ffloors[i]; - - if ((ff->flags & (FF_EXISTS | FF_SOLID)) == (FF_EXISTS | FF_SOLID) && - actor->Z() >= ff->top.plane->ZatPoint(actor)) - { // This floor is beneath our feet. - secpic = *ff->top.texture; - break; - } - } - if (i == numff) - { // Use sector's floor - secpic = sec->GetTexture(sector_t::floor); - } + actor->Sector->NextLowestFloorAt(actor, actor->Z(), 0, &resultsec, &resffloor); + secpic = resffloor ? *resffloor->top.texture : resultsec->planes[sector_t::floor].Texture; } else { - fixed_t z = actor->Top(); - // Looking through planes from bottom to top - for (i = numff-1; i >= 0; --i) - { - F3DFloor *ff = sec->e->XFloor.ffloors[i]; - - if ((ff->flags & (FF_EXISTS | FF_SOLID)) == (FF_EXISTS | FF_SOLID) && - z <= ff->bottom.plane->ZatPoint(actor)) - { // This floor is above our eyes. - secpic = *ff->bottom.texture; - break; - } - } - if (i < 0) - { // Use sector's ceiling - secpic = sec->GetTexture(sector_t::ceiling); - } + actor->Sector->NextHighestCeilingAt(actor, actor->Top(), 0, &resultsec, &resffloor); + secpic = resffloor ? *resffloor->bottom.texture : resultsec->planes[sector_t::ceiling].Texture; } return tex == TexMan[secpic]; } diff --git a/src/p_buildmap.cpp b/src/p_buildmap.cpp index 116c9ff6d9..8d7e750765 100644 --- a/src/p_buildmap.cpp +++ b/src/p_buildmap.cpp @@ -17,7 +17,6 @@ #include "g_level.h" #include "r_data/colormaps.h" #include "gi.h" -#include "portal.h" #include "p_spec.h" // MACROS ------------------------------------------------------------------ diff --git a/src/p_checkposition.h b/src/p_checkposition.h index 6eee5374a0..4ab67634e7 100644 --- a/src/p_checkposition.h +++ b/src/p_checkposition.h @@ -2,63 +2,6 @@ #define P_CHECKPOS_H -//============================================================================ -// -// This is a dynamic array which holds its first MAX_STATIC entries in normal -// variables to avoid constant allocations which this would otherwise -// require. -// -// When collecting touched portal groups the normal cases are either -// no portals == one group or -// two portals = two groups -// -// Anything with more can happen but far less infrequently, so this -// organization helps avoiding the overhead from heap allocations -// in the vast majority of situations. -// -//============================================================================ - -struct FPortalGroupArray -{ - enum - { - MAX_STATIC = 2 - }; - - FPortalGroupArray() - { - varused = 0; - } - - void Clear() - { - data.Clear(); - varused = 0; - } - - void Add(DWORD num) - { - if (varused < MAX_STATIC) entry[varused++] = num; - else data.Push(num); - } - - unsigned Size() - { - return varused + data.Size(); - } - - DWORD operator[](unsigned index) - { - return index < MAX_STATIC ? entry[index] : data[index - MAX_STATIC]; - } - -private: - DWORD entry[MAX_STATIC]; - unsigned varused; - TArray data; -}; - - //============================================================================ // // Used by P_CheckPosition and P_TryMove in place of the original @@ -95,7 +38,6 @@ struct FCheckPosition bool DoRipping; TMap LastRipped; - FPortalGroupArray Groups; int PushTime; FCheckPosition(bool rip=false) diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index fed155627e..2049d5f229 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -558,7 +558,7 @@ bool P_Move (AActor *actor) else { // The monster just hit the floor, so trigger any actions. if (actor->floorsector->SecActTarget != NULL && - actor->floorz == actor->floorsector->floorplane.ZatPoint(actor)) + actor->floorz == actor->floorsector->floorplane.ZatPoint(actor->PosRelative(actor->floorsector))) { actor->floorsector->SecActTarget->TriggerAction(actor, SECSPAC_HitFloor); } @@ -612,18 +612,18 @@ bool P_Move (AActor *actor) // Do NOT simply return false 1/4th of the time (causes monsters to // back out when they shouldn't, and creates secondary stickiness). - line_t *ld; + spechit_t spec; int good = 0; if (!(actor->flags6 & MF6_NOTRIGGER)) { - while (spechit.Pop (ld)) + while (spechit.Pop (spec)) { // [RH] let monsters push lines, as well as use them - if (((actor->flags4 & MF4_CANUSEWALLS) && P_ActivateLine (ld, actor, 0, SPAC_Use)) || - ((actor->flags2 & MF2_PUSHWALL) && P_ActivateLine (ld, actor, 0, SPAC_Push))) + if (((actor->flags4 & MF4_CANUSEWALLS) && P_ActivateLine (spec.line, actor, 0, SPAC_Use)) || + ((actor->flags2 & MF2_PUSHWALL) && P_ActivateLine (spec.line, actor, 0, SPAC_Push))) { - good |= ld == actor->BlockingLine ? 1 : 2; + good |= spec.line == actor->BlockingLine ? 1 : 2; } } } @@ -868,8 +868,8 @@ void P_NewChaseDir(AActor * actor) box.Bottom() < line->bbox[BOXTOP] && box.BoxOnLineSide(line) == -1) { - fixed_t front = line->frontsector->floorplane.ZatPoint(actor); - fixed_t back = line->backsector->floorplane.ZatPoint(actor); + fixed_t front = line->frontsector->floorplane.ZatPoint(actor->PosRelative(line)); + fixed_t back = line->backsector->floorplane.ZatPoint(actor->PosRelative(line)); angle_t angle; // The monster must contact one of the two floors, diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 1e45c0fe51..1af76d6d92 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -57,7 +57,6 @@ #include "d_net.h" #include "d_event.h" #include "gstrings.h" -#include "portal.h" #include "po_man.h" #include "d_player.h" #include "r_utility.h" diff --git a/src/p_local.h b/src/p_local.h index 44a430c27b..35dbb8b226 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -240,7 +240,14 @@ AActor *P_RoughMonsterSearch (AActor *mo, int distance, bool onlyseekable=false) // if within "tmfloorz - tmceilingz". extern msecnode_t *sector_list; // phares 3/16/98 -extern TArray spechit; +struct spechit_t +{ + line_t *line; + fixedvec2 refpos; +}; + +extern TArray spechit; +extern TArray portalhit; bool P_TestMobjLocation (AActor *mobj); @@ -287,6 +294,10 @@ enum FFCF_SAMESECTOR = 2, FFCF_ONLY3DFLOORS = 4, // includes 3D midtexes FFCF_3DRESTRICT = 8, // ignore 3D midtexes and floors whose floorz are above thing's z + FFCF_NOPORTALS = 16, // ignore portals (considers them impassable.) + FFCF_NOFLOOR = 32, + FFCF_NOCEILING = 64, + FFCF_RESTRICTEDPORTAL = 128, // current values in the iterator's return are through a restricted portal type (i.e. some features are blocked.) }; void P_FindFloorCeiling (AActor *actor, int flags=0); diff --git a/src/p_map.cpp b/src/p_map.cpp index 85cd110842..0b1d78f238 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -63,7 +63,7 @@ CVAR(Bool, cl_bloodsplats, true, CVAR_ARCHIVE) CVAR(Int, sv_smartaim, 0, CVAR_ARCHIVE | CVAR_SERVERINFO) CVAR(Bool, cl_doautoaim, false, CVAR_ARCHIVE) -static void CheckForPushSpecial(line_t *line, int side, AActor *mobj, bool windowcheck); +static void CheckForPushSpecial(line_t *line, int side, AActor *mobj, fixedvec2 * posforwindowcheck = NULL); static void SpawnShootDecal(AActor *t1, const FTraceResults &trace); static void SpawnDeepSplash(AActor *t1, const FTraceResults &trace, AActor *puff, fixed_t vx, fixed_t vy, fixed_t vz, fixed_t shootz, bool ffloor = false); @@ -75,11 +75,13 @@ static FRandom pr_crunch("DoCrunch"); // keep track of special lines as they are hit, // but don't process them until the move is proven valid -TArray spechit; +TArray spechit; +TArray portalhit; // Temporary holder for thing_sectorlist threads msecnode_t* sector_list = NULL; // phares 3/16/98 + //========================================================================== // // GetCoefficientClosestPointInLine24 @@ -91,8 +93,9 @@ msecnode_t* sector_list = NULL; // phares 3/16/98 // //========================================================================== -static fixed_t GetCoefficientClosestPointInLine24(line_t *ld, FCheckPosition &tm) +static inline fixed_t GetCoefficientClosestPointInLine24(line_t *ld, fixedvec2 pos) { +#ifndef USE_FLOAT // [EP] Use 64 bit integers in order to keep the exact result of the // multiplication, because in the case the vertexes have both the // distance coordinates equal to the map limit (32767 units, which is @@ -102,8 +105,8 @@ static fixed_t GetCoefficientClosestPointInLine24(line_t *ld, FCheckPosition &tm // fixed_t notation, which is 1.52587890625e-05 in float notation), the // product and the sum can be 1 in the worst case, which is very tiny. - SQWORD r_num = ((SQWORD(tm.x - ld->v1->x)*ld->dx) + - (SQWORD(tm.y - ld->v1->y)*ld->dy)); + SQWORD r_num = ((SQWORD(pos.x - ld->v1->x)*ld->dx) + + (SQWORD(pos.y - ld->v1->y)*ld->dy)); // The denominator is always positive. Use this to avoid useless // calculations. @@ -131,16 +134,58 @@ static fixed_t GetCoefficientClosestPointInLine24(line_t *ld, FCheckPosition &tm // Thanks to the fact that in this code path the denominator is greater // than the numerator, it's possible to avoid this bad situation by // just checking the last 24 bits of the numerator. - if ((r_num >> (63-24)) != 0) { + if ((r_num >> (63 - 24)) != 0) { // [EP] In fact, if the numerator is greater than // (1 << (63-24)), the denominator must be greater than // (1 << (63-24)), hence the denominator won't be zero after // the right shift by 24 places. - return (fixed_t)(r_num/(r_den >> 24)); + return (fixed_t)(r_num / (r_den >> 24)); } // [EP] Having the last 24 bits all zero allows left shifting // the numerator by 24 bits without overflow. - return (fixed_t)((r_num << 24)/r_den); + return (fixed_t)((r_num << 24) / r_den); +#else + double dx = ld->dx; + double dy = ld->dy; + return xs_CRoundToInt(((double)(pos.x - ld->v1->x) * dx + (double)(pos.y - ld->v1->y) * dy) / (dx*dx + dy*dy) * 16777216.f); +#endif +} + + +//========================================================================== +// +// FindRefPoint +// +// Finds the point on the line closest to the given coordinate +// +//========================================================================== + +static inline fixedvec2 FindRefPoint(line_t *ld, fixedvec2 pos) +{ + if (!((((ld->frontsector->floorplane.a | ld->frontsector->floorplane.b) | + (ld->backsector->floorplane.a | ld->backsector->floorplane.b) | + (ld->frontsector->ceilingplane.a | ld->frontsector->ceilingplane.b) | + (ld->backsector->ceilingplane.a | ld->backsector->ceilingplane.b)) == 0) + && ld->backsector->e->XFloor.ffloors.Size() == 0 && ld->frontsector->e->XFloor.ffloors.Size() == 0)) + { + fixed_t r = GetCoefficientClosestPointInLine24(ld, pos); + if (r <= 0) + { + pos.x = ld->v1->x; + pos.y = ld->v1->y; + } + else if (r >= (1 << 24)) + { + pos.x = ld->v2->x; + pos.y = ld->v2->y; + } + else + { + pos.x = ld->v1->x + MulScale24(r, ld->dx); + pos.y = ld->v1->y + MulScale24(r, ld->dy); + } + } + return pos; } //========================================================================== @@ -150,9 +195,12 @@ static fixed_t GetCoefficientClosestPointInLine24(line_t *ld, FCheckPosition &tm // only3d set means to only check against 3D floors and midtexes. // //========================================================================== +bool ffcf_verbose; -static bool PIT_FindFloorCeiling(line_t *ld, const FBoundingBox &box, FCheckPosition &tmf, int flags) +static bool PIT_FindFloorCeiling(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::CheckResult &cres, const FBoundingBox &box, FCheckPosition &tmf, int flags) { + line_t *ld = cres.line; + if (box.Right() <= ld->bbox[BOXLEFT] || box.Left() >= ld->bbox[BOXRIGHT] || box.Top() <= ld->bbox[BOXBOTTOM] @@ -164,122 +212,90 @@ static bool PIT_FindFloorCeiling(line_t *ld, const FBoundingBox &box, FCheckPosi // A line has been hit + if (ffcf_verbose) + { + Printf("Hit line %d at position %f,%f, group %d\n", + int(ld - lines), FIXED2FLOAT(cres.position.x), FIXED2FLOAT(cres.position.y), ld->frontsector->PortalGroup); + } + if (!ld->backsector) { // One sided line return true; } - fixed_t sx, sy; + fixedvec2 refpoint = FindRefPoint(ld, cres.position); FLineOpening open; - // set openrange, opentop, openbottom - if ((((ld->frontsector->floorplane.a | ld->frontsector->floorplane.b) | - (ld->backsector->floorplane.a | ld->backsector->floorplane.b) | - (ld->frontsector->ceilingplane.a | ld->frontsector->ceilingplane.b) | - (ld->backsector->ceilingplane.a | ld->backsector->ceilingplane.b)) == 0) - && ld->backsector->e->XFloor.ffloors.Size() == 0 && ld->frontsector->e->XFloor.ffloors.Size() == 0) - { - P_LineOpening(open, tmf.thing, ld, sx = tmf.x, sy = tmf.y, tmf.x, tmf.y, flags); - } - else - { // Find the point on the line closest to the actor's center, and use - // that to calculate openings - double dx = ld->dx; - double dy = ld->dy; - fixed_t r = xs_CRoundToInt(((double)(tmf.x - ld->v1->x) * dx + - (double)(tmf.y - ld->v1->y) * dy) / - (dx*dx + dy*dy) * 16777216.f); - if (r <= 0) - { - P_LineOpening(open, tmf.thing, ld, sx = ld->v1->x, sy = ld->v1->y, tmf.x, tmf.y, flags); - } - else if (r >= (1 << 24)) - { - P_LineOpening(open, tmf.thing, ld, sx = ld->v2->x, sy = ld->v2->y, tmf.thing->X(), tmf.thing->Y(), flags); - } - else - { - P_LineOpening(open, tmf.thing, ld, sx = ld->v1->x + MulScale24(r, ld->dx), - sy = ld->v1->y + MulScale24(r, ld->dy), tmf.x, tmf.y, flags); - } - } + P_LineOpening(open, tmf.thing, ld, refpoint.x, refpoint.y, cres.position.x, cres.position.y, flags); // adjust floor / ceiling heights - if (open.top < tmf.ceilingz) + if (!(flags & FFCF_NOCEILING)) { - tmf.ceilingz = open.top; + if (open.top < tmf.ceilingz) + { + tmf.ceilingz = open.top; + if (open.topsec != NULL) tmf.floorsector = open.topsec; + if (ffcf_verbose) Printf(" Adjust ceilingz to %f\n", FIXED2FLOAT(open.top)); + mit.StopUp(); + } } - if (open.bottom > tmf.floorz) + if (!(flags & FFCF_NOFLOOR)) { - tmf.floorz = open.bottom; - if (open.bottomsec != NULL) tmf.floorsector = open.bottomsec; - tmf.touchmidtex = open.touchmidtex; - tmf.abovemidtex = open.abovemidtex; - } - else if (open.bottom == tmf.floorz) - { - tmf.touchmidtex |= open.touchmidtex; - tmf.abovemidtex |= open.abovemidtex; - } - - if (open.lowfloor < tmf.dropoffz) - tmf.dropoffz = open.lowfloor; + if (open.bottom > tmf.floorz) + { + tmf.floorz = open.bottom; + if (open.bottomsec != NULL) tmf.floorsector = open.bottomsec; + tmf.touchmidtex = open.touchmidtex; + tmf.abovemidtex = open.abovemidtex; + if (ffcf_verbose) Printf(" Adjust floorz to %f\n", FIXED2FLOAT(open.bottom)); + if (tmf.floorz > tmf.dropoffz + tmf.thing->MaxDropOffHeight) mit.StopDown(); + } + else if (open.bottom == tmf.floorz) + { + tmf.touchmidtex |= open.touchmidtex; + tmf.abovemidtex |= open.abovemidtex; + } + if (open.lowfloor < tmf.dropoffz && open.lowfloor > FIXED_MIN) + { + tmf.dropoffz = open.lowfloor; + if (ffcf_verbose) Printf(" Adjust dropoffz to %f\n", FIXED2FLOAT(open.bottom)); + if (tmf.floorz > tmf.dropoffz + tmf.thing->MaxDropOffHeight) mit.StopDown(); + } + } return true; } //========================================================================== // -// +// calculates the actual floor and ceiling position at a given +// coordinate. Traverses through portals unless being told not to. // //========================================================================== void P_GetFloorCeilingZ(FCheckPosition &tmf, int flags) { - sector_t *sec; - if (!(flags & FFCF_ONLYSPAWNPOS)) - { - sec = !(flags & FFCF_SAMESECTOR) ? P_PointInSector(tmf.x, tmf.y) : tmf.thing->Sector; - tmf.floorsector = sec; - tmf.ceilingsector = sec; + sector_t *sec = (!(flags & FFCF_SAMESECTOR) || tmf.thing->Sector == NULL)? P_PointInSector(tmf.x, tmf.y) : tmf.sector; + F3DFloor *ffc, *fff; - tmf.floorz = tmf.dropoffz = sec->floorplane.ZatPoint(tmf.x, tmf.y); - tmf.ceilingz = sec->ceilingplane.ZatPoint(tmf.x, tmf.y); - tmf.floorpic = sec->GetTexture(sector_t::floor); - tmf.floorterrain = sec->GetTerrain(sector_t::floor); - tmf.ceilingpic = sec->GetTexture(sector_t::ceiling); + tmf.ceilingz = sec->NextHighestCeilingAt(tmf.x, tmf.y, tmf.z + tmf.thing->height, flags, &tmf.ceilingsector, &ffc); + tmf.floorz = tmf.dropoffz = sec->NextLowestFloorAt(tmf.x, tmf.y, tmf.z, flags, tmf.thing->MaxStepHeight, &tmf.floorsector, &fff); + + if (fff) + { + tmf.floorpic = *fff->top.texture; + tmf.floorterrain = fff->model->GetTerrain(fff->top.isceiling); } else { - sec = tmf.thing->Sector; + tmf.floorpic = tmf.floorsector->GetTexture(sector_t::floor); + tmf.floorterrain = tmf.floorsector->GetTerrain(sector_t::floor); } + tmf.ceilingpic = ffc ? *ffc->bottom.texture : tmf.ceilingsector->GetTexture(sector_t::ceiling); + tmf.sector = sec; - for (unsigned int i = 0; ie->XFloor.ffloors.Size(); i++) - { - F3DFloor* rover = sec->e->XFloor.ffloors[i]; - - if (!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue; - - fixed_t ff_bottom = rover->bottom.plane->ZatPoint(tmf.x, tmf.y); - fixed_t ff_top = rover->top.plane->ZatPoint(tmf.x, tmf.y); - - if (ff_top > tmf.floorz) - { - if (ff_top <= tmf.z || (!(flags & FFCF_3DRESTRICT) && (tmf.thing != NULL && ff_bottom < tmf.z && ff_top < tmf.z + tmf.thing->MaxStepHeight))) - { - tmf.dropoffz = tmf.floorz = ff_top; - tmf.floorpic = *rover->top.texture; - tmf.floorterrain = rover->model->GetTerrain(rover->top.isceiling); - } - } - if (ff_bottom <= tmf.ceilingz && ff_bottom > tmf.z + tmf.thing->height) - { - tmf.ceilingz = ff_bottom; - tmf.ceilingpic = *rover->bottom.texture; - } - } } //========================================================================== @@ -301,21 +317,13 @@ void P_FindFloorCeiling(AActor *actor, int flags) { flags |= FFCF_3DRESTRICT; } - if (!(flags & FFCF_ONLYSPAWNPOS)) + if (flags & FFCF_SAMESECTOR) { - P_GetFloorCeilingZ(tmf, flags); + tmf.sector = actor->Sector; } - else - { - tmf.ceilingsector = tmf.floorsector = actor->Sector; + P_GetFloorCeilingZ(tmf, flags); + assert(tmf.thing->Sector != NULL); - tmf.floorz = tmf.dropoffz = actor->floorz; - tmf.ceilingz = actor->ceilingz; - tmf.floorpic = actor->floorpic; - tmf.floorterrain = actor->floorterrain; - tmf.ceilingpic = actor->ceilingpic; - P_GetFloorCeilingZ(tmf, flags); - } actor->floorz = tmf.floorz; actor->dropoffz = tmf.dropoffz; actor->ceilingz = tmf.ceilingz; @@ -324,47 +332,55 @@ void P_FindFloorCeiling(AActor *actor, int flags) actor->floorsector = tmf.floorsector; actor->ceilingpic = tmf.ceilingpic; actor->ceilingsector = tmf.ceilingsector; - - FBoundingBox box(tmf.x, tmf.y, actor->radius); + if (ffcf_verbose) Printf("Starting with ceilingz = %f, floorz = %f\n", FIXED2FLOAT(tmf.ceilingz), FIXED2FLOAT(tmf.floorz)); tmf.touchmidtex = false; tmf.abovemidtex = false; validcount++; - FBlockLinesIterator it(box); - line_t *ld; + FPortalGroupArray grouplist; + FMultiBlockLinesIterator mit(grouplist, actor); + FMultiBlockLinesIterator::CheckResult cres; - while ((ld = it.Next())) + // if we already have a valid floor/ceiling sector within the current sector, + // we do not need to iterate through plane portals to find a floor or ceiling. + if (actor->floorsector == actor->Sector) mit.StopDown(); + if (actor->ceilingsector == actor->Sector) mit.StopUp(); + + while ((mit.Next(&cres))) { - PIT_FindFloorCeiling(ld, box, tmf, flags); + PIT_FindFloorCeiling(mit, cres, mit.Box(), tmf, flags|cres.portalflags); } if (tmf.touchmidtex) tmf.dropoffz = tmf.floorz; - if (!(flags & FFCF_ONLYSPAWNPOS) || (tmf.abovemidtex && (tmf.floorz <= actor->Z()))) + bool usetmf = !(flags & FFCF_ONLYSPAWNPOS) || (tmf.abovemidtex && (tmf.floorz <= actor->Z())); + + // when actual floor or ceiling are beyond a portal plane we also need to use the result of the blockmap iterator, regardless of the flags being specified. + if (usetmf || tmf.floorsector->PortalGroup != actor->Sector->PortalGroup) { actor->floorz = tmf.floorz; actor->dropoffz = tmf.dropoffz; - actor->ceilingz = tmf.ceilingz; actor->floorpic = tmf.floorpic; actor->floorterrain = tmf.floorterrain; actor->floorsector = tmf.floorsector; + } + + if (usetmf || tmf.ceilingsector->PortalGroup != actor->Sector->PortalGroup) + { + actor->ceilingz = tmf.ceilingz; actor->ceilingpic = tmf.ceilingpic; actor->ceilingsector = tmf.ceilingsector; } - else - { - actor->floorsector = actor->ceilingsector = actor->Sector; - // [BB] Don't forget to update floorpic and ceilingpic. - if (actor->Sector != NULL) - { - actor->floorpic = actor->Sector->GetTexture(sector_t::floor); - actor->floorterrain = actor->Sector->GetTerrain(sector_t::floor); - actor->ceilingpic = actor->Sector->GetTexture(sector_t::ceiling); - } - } } +// Debug CCMD for checking errors in the MultiBlockLinesIterator (needs to be removed when this code is complete) +CCMD(ffcf) +{ + ffcf_verbose = true; + P_FindFloorCeiling(players[0].mo, 0); + ffcf_verbose = false; +} //========================================================================== // // TELEPORT MOVE @@ -400,30 +416,31 @@ bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefra tmf.abovemidtex = false; P_GetFloorCeilingZ(tmf, 0); - spechit.Clear(); - bool StompAlwaysFrags = ((thing->flags2 & MF2_TELESTOMP) || (level.flags & LEVEL_MONSTERSTELEFRAG) || telefrag) && !(thing->flags7 & MF7_NOTELESTOMP); - FBoundingBox box(x, y, thing->radius); - FBlockLinesIterator it(box); - line_t *ld; - // P_LineOpening requires the thing's z to be the destination z in order to work. fixed_t savedz = thing->Z(); thing->SetZ(z); - while ((ld = it.Next())) + + FPortalGroupArray grouplist; + FMultiBlockLinesIterator mit(grouplist, x, y, z, thing->height, thing->radius); + FMultiBlockLinesIterator::CheckResult cres; + + while (mit.Next(&cres)) { - PIT_FindFloorCeiling(ld, box, tmf, 0); + PIT_FindFloorCeiling(mit, cres, mit.Box(), tmf, 0); } thing->SetZ(savedz); if (tmf.touchmidtex) tmf.dropoffz = tmf.floorz; - FBlockThingsIterator it2(FBoundingBox(x, y, thing->radius)); - AActor *th; + FMultiBlockThingsIterator mit2(grouplist, x, y, z, thing->height, thing->radius); + FMultiBlockThingsIterator::CheckResult cres2; - while ((th = it2.Next())) + while (mit2.Next(&cres2)) { + AActor *th = cres2.thing; + if (!(th->flags & MF_SHOOTABLE)) continue; @@ -432,7 +449,7 @@ bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefra continue; fixed_t blockdist = th->radius + tmf.thing->radius; - if (abs(th->X() - tmf.x) >= blockdist || abs(th->Y() - tmf.y) >= blockdist) + if (abs(th->X() - cres2.position.x) >= blockdist || abs(th->Y() - cres2.position.y) >= blockdist) continue; if ((th->flags2 | tmf.thing->flags2) & MF2_THRUACTORS) @@ -517,11 +534,14 @@ bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefra void P_PlayerStartStomp(AActor *actor, bool mononly) { - AActor *th; - FBlockThingsIterator it(FBoundingBox(actor->X(), actor->Y(), actor->radius)); + FPortalGroupArray grouplist; + FMultiBlockThingsIterator mit(grouplist, actor); + FMultiBlockThingsIterator::CheckResult cres; - while ((th = it.Next())) + while ((mit.Next(&cres))) { + AActor *th = cres.thing; + if (!(th->flags & MF_SHOOTABLE)) continue; @@ -529,7 +549,8 @@ void P_PlayerStartStomp(AActor *actor, bool mononly) if (th == actor || (th->player == actor->player && th->player != NULL)) continue; - if (!th->intersects(actor)) + fixed_t blockdist = th->radius + actor->radius; + if (abs(th->X() - cres.position.x) >= blockdist || abs(th->Y() - cres.position.y) >= blockdist) continue; // only kill monsters and other players @@ -584,7 +605,7 @@ int P_GetFriction(const AActor *mo, int *frictionfactor) int movefactor = ORIG_FRICTION_FACTOR; fixed_t newfriction; const msecnode_t *m; - const sector_t *sec; + sector_t *sec; if (mo->IsNoClip2()) { @@ -627,6 +648,7 @@ int P_GetFriction(const AActor *mo, int *frictionfactor) for (m = mo->touching_sectorlist; m; m = m->m_tnext) { sec = m->m_sector; + fixedvec3 pos = mo->PosRelative(sec); // 3D floors must be checked, too for (unsigned i = 0; i < sec->e->XFloor.ffloors.Size(); i++) @@ -637,13 +659,13 @@ int P_GetFriction(const AActor *mo, int *frictionfactor) if (rover->flags & FF_SOLID) { // Must be standing on a solid floor - if (mo->Z() != rover->top.plane->ZatPoint(mo)) continue; + if (mo->Z() != rover->top.plane->ZatPoint(pos)) continue; } else if (rover->flags & FF_SWIMMABLE) { // Or on or inside a swimmable floor (e.g. in shallow water) - if (mo->Z() > rover->top.plane->ZatPoint(mo) || - (mo->Top()) < rover->bottom.plane->ZatPoint(mo)) + if (mo->Z() > rover->top.plane->ZatPoint(pos) || + (mo->Top()) < rover->bottom.plane->ZatPoint(pos)) continue; } else @@ -664,9 +686,9 @@ int P_GetFriction(const AActor *mo, int *frictionfactor) } newfriction = secfriction(sec); if ((newfriction < friction || friction == ORIG_FRICTION) && - (mo->Z() <= sec->floorplane.ZatPoint(mo) || + (mo->Z() <= sec->floorplane.ZatPoint(pos) || (sec->GetHeightSec() != NULL && - mo->Z() <= sec->heightsec->floorplane.ZatPoint(mo)))) + mo->Z() <= sec->heightsec->floorplane.ZatPoint(pos)))) { friction = newfriction; movefactor = secmovefac(sec); @@ -725,6 +747,7 @@ int P_GetMoveFactor(const AActor *mo, int *frictionp) return movefactor; } + // // MOVEMENT ITERATOR FUNCTIONS // @@ -739,8 +762,9 @@ int P_GetMoveFactor(const AActor *mo, int *frictionp) //========================================================================== static // killough 3/26/98: make static -bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) +bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::CheckResult &cres, const FBoundingBox &box, FCheckPosition &tm) { + line_t *ld = cres.line; bool rail = false; if (box.Right() <= ld->bbox[BOXLEFT] @@ -769,7 +793,7 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) P_DamageMobj(tm.thing, NULL, NULL, tm.thing->Mass >> 5, NAME_Melee); } tm.thing->BlockingLine = ld; - CheckForPushSpecial(ld, 0, tm.thing, false); + CheckForPushSpecial(ld, 0, tm.thing); return false; } @@ -782,7 +806,6 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) bool NotBlocked = ((tm.thing->flags3 & MF3_NOBLOCKMONST) || ((i_compatflags & COMPATF_NOBLOCKFRIENDS) && (tm.thing->flags & MF_FRIENDLY))); - fixedvec3 pos = tm.thing->PosRelative(ld); if (!(Projectile) || (ld->flags & (ML_BLOCKEVERYTHING | ML_BLOCKPROJECTILE))) { if (ld->flags & ML_RAILING) @@ -801,91 +824,32 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) } tm.thing->BlockingLine = ld; // Calculate line side based on the actor's original position, not the new one. - CheckForPushSpecial(ld, P_PointOnLineSide(pos.x, pos.y, ld), tm.thing, false); + CheckForPushSpecial(ld, P_PointOnLineSide(cres.position.x, cres.position.y, ld), tm.thing); return false; } } - // [RH] Steep sectors count as dropoffs (unless already in one) + fixedvec2 ref = FindRefPoint(ld, cres.position); + FLineOpening open; + + P_LineOpening(open, tm.thing, ld, ref.x, ref.y, cres.position.x, cres.position.y, cres.portalflags); + + // [RH] Steep sectors count as dropoffs, if the actor touches the boundary between a steep slope and something else if (!(tm.thing->flags & MF_DROPOFF) && !(tm.thing->flags & (MF_NOGRAVITY | MF_NOCLIP))) { - secplane_t frontplane, backplane; - // Check 3D floors as well - frontplane = P_FindFloorPlane(ld->frontsector, pos.x, pos.y, tm.thing->floorz); - backplane = P_FindFloorPlane(ld->backsector, pos.x, pos.y, tm.thing->floorz); - if (frontplane.c < STEEPSLOPE || backplane.c < STEEPSLOPE) + if ((open.frontfloorplane.c < STEEPSLOPE) != (open.backfloorplane.c < STEEPSLOPE)) { - const msecnode_t *node = tm.thing->touching_sectorlist; - bool allow = false; - int count = 0; - while (node != NULL) - { - count++; - if (node->m_sector->floorplane.c < STEEPSLOPE) - { - allow = true; - break; - } - node = node->m_tnext; - } - if (!allow) - { - return false; - } + // on the boundary of a steep slope + return false; } } - fixed_t sx = 0, sy = 0; - FLineOpening open; - - // set openrange, opentop, openbottom - if ((((ld->frontsector->floorplane.a | ld->frontsector->floorplane.b) | - (ld->backsector->floorplane.a | ld->backsector->floorplane.b) | - (ld->frontsector->ceilingplane.a | ld->frontsector->ceilingplane.b) | - (ld->backsector->ceilingplane.a | ld->backsector->ceilingplane.b)) == 0) - && ld->backsector->e->XFloor.ffloors.Size() == 0 && ld->frontsector->e->XFloor.ffloors.Size() == 0) + // If the floor planes on both sides match we should recalculate open.bottom at the actual position we are checking + // This is to avoid bumpy movement when crossing a linedef with the same slope on both sides. + if (open.frontfloorplane == open.backfloorplane) { - P_LineOpening(open, tm.thing, ld, sx = tm.x, sy = tm.y, tm.x, tm.y); - } - else - { // Find the point on the line closest to the actor's center, and use - // that to calculate openings - fixed_t r = GetCoefficientClosestPointInLine24(ld, tm); - - /* Printf ("%d:%d: %d (%d %d %d %d) (%d %d %d %d)\n", level.time, ld-lines, r, - ld->frontsector->floorplane.a, - ld->frontsector->floorplane.b, - ld->frontsector->floorplane.c, - ld->frontsector->floorplane.ic, - ld->backsector->floorplane.a, - ld->backsector->floorplane.b, - ld->backsector->floorplane.c, - ld->backsector->floorplane.ic);*/ - if (r <= 0) - { - P_LineOpening(open, tm.thing, ld, sx = ld->v1->x, sy = ld->v1->y, tm.x, tm.y); - } - else if (r >= (1 << 24)) - { - P_LineOpening(open, tm.thing, ld, sx = ld->v2->x, sy = ld->v2->y, pos.x, pos.y); - } - else - { - P_LineOpening(open, tm.thing, ld, sx = ld->v1->x + MulScale24(r, ld->dx), - sy = ld->v1->y + MulScale24(r, ld->dy), tm.x, tm.y); - } - - // the floorplane on both sides is identical with the current one - // so don't mess around with the z-position - if (ld->frontsector->floorplane == ld->backsector->floorplane && - ld->frontsector->floorplane == tm.thing->Sector->floorplane && - !ld->frontsector->e->XFloor.ffloors.Size() && !ld->backsector->e->XFloor.ffloors.Size() && - !open.abovemidtex) - { - open.bottom = INT_MIN; - } - /* Printf (" %d %d %d\n", sx, sy, openbottom);*/ + open.bottom = open.frontfloorplane.ZatPoint(cres.position.x, cres.position.y); } if (rail && @@ -897,7 +861,7 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) // from either side. How long until somebody reports this as a bug and I'm // forced to say, "It's not a bug. It's a feature?" Ugh. (!(level.flags2 & LEVEL2_RAILINGHACK) || - open.bottom == tm.thing->Sector->floorplane.ZatPoint(sx, sy))) + open.bottom == tm.thing->Sector->floorplane.ZatPoint(ref.x, ref.y))) { open.bottom += 32 * FRACUNIT; } @@ -932,9 +896,18 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) tm.dropoffz = open.lowfloor; // if contacted a special line, add it to the list + spechit_t spec; if (ld->special) { - spechit.Push(ld); + spec.line = ld; + spec.refpos = cres.position; + spechit.Push(spec); + } + if (ld->portalindex >= 0) + { + spec.line = ld; + spec.refpos = cres.position; + portalhit.Push(spec); } return true; @@ -1033,8 +1006,9 @@ static bool CanAttackHurt(AActor *victim, AActor *shooter) // //========================================================================== -bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) +bool PIT_CheckThing(FMultiBlockThingsIterator &it, FMultiBlockThingsIterator::CheckResult &cres, const FBoundingBox &box, FCheckPosition &tm) { + AActor *thing = cres.thing; fixed_t topz; bool solid; int damage; @@ -1046,9 +1020,8 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) if (!((thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE)) || thing->flags6 & MF6_TOUCHY)) return true; // can't hit thing - fixedvec3 thingpos = thing->PosRelative(tm.thing); fixed_t blockdist = thing->radius + tm.thing->radius; - if (abs(thingpos.x - tm.x) >= blockdist || abs(thingpos.y - tm.y) >= blockdist) + if (abs(thing->X() - cres.position.x) >= blockdist || abs(thing->Y() - cres.position.y) >= blockdist) return true; if ((thing->flags2 | tm.thing->flags2) & MF2_THRUACTORS) @@ -1059,50 +1032,55 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) tm.thing->BlockingMobj = thing; topz = thing->Top(); - if (!(i_compatflags & COMPATF_NO_PASSMOBJ) && !(tm.thing->flags & (MF_FLOAT | MF_MISSILE | MF_SKULLFLY | MF_NOGRAVITY)) && - (thing->flags & MF_SOLID) && (thing->flags4 & MF4_ACTLIKEBRIDGE)) - { - // [RH] Let monsters walk on actors as well as floors - if ((tm.thing->flags3 & MF3_ISMONSTER) && - topz >= tm.floorz && topz <= tm.thing->Z() + tm.thing->MaxStepHeight) - { - // The commented-out if is an attempt to prevent monsters from walking off a - // thing further than they would walk off a ledge. I can't think of an easy - // way to do this, so I restrict them to only walking on bridges instead. - // Uncommenting the if here makes it almost impossible for them to walk on - // anything, bridge or otherwise. - // if (abs(thing->x - tmx) <= thing->radius && - // abs(thing->y - tmy) <= thing->radius) - { - tm.stepthing = thing; - tm.floorz = topz; - } - } - } // Both things overlap in x or y direction bool unblocking = false; - if ((tm.FromPMove || tm.thing->player != NULL) && thing->flags&MF_SOLID) + // walking on other actors and unblocking is too messy through restricted portal types so disable it. + if (!(cres.portalflags & FFCF_RESTRICTEDPORTAL)) { - // Both actors already overlap. To prevent them from remaining stuck allow the move if it - // takes them further apart or the move does not change the position (when called from P_ChangeSector.) - if (tm.x == tm.thing->X() && tm.y == tm.thing->Y()) + if (!(i_compatflags & COMPATF_NO_PASSMOBJ) && !(tm.thing->flags & (MF_FLOAT | MF_MISSILE | MF_SKULLFLY | MF_NOGRAVITY)) && + (thing->flags & MF_SOLID) && (thing->flags4 & MF4_ACTLIKEBRIDGE)) { - unblocking = true; - } - else if (abs(thingpos.x - tm.thing->X()) < (thing->radius+tm.thing->radius) && - abs(thingpos.y - tm.thing->Y()) < (thing->radius+tm.thing->radius)) - - { - fixed_t newdist = thing->AproxDistance(tm.x, tm.y, tm.thing); - fixed_t olddist = thing->AproxDistance(tm.thing); - - if (newdist > olddist) + // [RH] Let monsters walk on actors as well as floors + if ((tm.thing->flags3 & MF3_ISMONSTER) && + topz >= tm.floorz && topz <= tm.thing->Z() + tm.thing->MaxStepHeight) { - // ... but not if they did not overlap in z-direction before but would after the move. - unblocking = !((tm.thing->Z() >= topz && tm.z < topz) || - (tm.thing->Top() <= thingpos.z && tm.thing->Top() > thingpos.z)); + // The commented-out if is an attempt to prevent monsters from walking off a + // thing further than they would walk off a ledge. I can't think of an easy + // way to do this, so I restrict them to only walking on bridges instead. + // Uncommenting the if here makes it almost impossible for them to walk on + // anything, bridge or otherwise. + // if (abs(thing->x - tmx) <= thing->radius && + // abs(thing->y - tmy) <= thing->radius) + { + tm.stepthing = thing; + tm.floorz = topz; + } + } + } + + if (((tm.FromPMove || tm.thing->player != NULL) && thing->flags&MF_SOLID)) + { + fixedvec3 oldpos = tm.thing->PosRelative(thing); + // Both actors already overlap. To prevent them from remaining stuck allow the move if it + // takes them further apart or the move does not change the position (when called from P_ChangeSector.) + if (oldpos.x == thing->X() && oldpos.y == thing->Y()) + { + unblocking = true; + } + else if (abs(thing->X() - oldpos.x) < (thing->radius + tm.thing->radius) && + abs(thing->Y() - oldpos.y) < (thing->radius + tm.thing->radius)) + + { + fixed_t newdist = thing->AproxDistance(cres.position.x, cres.position.y); + fixed_t olddist = thing->AproxDistance(oldpos.x, oldpos.y); + + if (newdist > olddist) + { + // unblock only if there's already a vertical overlap (or both actors are flagged not to overlap) + unblocking = (tm.thing->Top() > thing->Z() && tm.thing->Z() < topz) || (tm.thing->flags3 & thing->flags3 & MF3_DONTOVERLAP); + } } } } @@ -1136,7 +1114,7 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) // or different species if DONTHARMSPECIES (!(thing->flags6 & MF6_DONTHARMSPECIES) || thing->GetSpecies() != tm.thing->GetSpecies()) && // touches vertically - topz >= tm.thing->Z() && tm.thing->Z() + tm.thing->height >= thingpos.z && + topz >= tm.thing->Z() && tm.thing->Top() >= thing->Z() && // prevents lost souls from exploding when fired by pain elementals (thing->master != tm.thing && tm.thing->master != thing)) // Difference with MBF: MBF hardcodes the LS/PE check and lets actors of the same species @@ -1239,11 +1217,11 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) } // Check if it went over / under - if (tm.thing->Z() > thingpos.z + clipheight) + if (tm.thing->Z() > thing->Z() + clipheight) { // Over thing return true; } - if (tm.thing->Top() < thingpos.z) + if (tm.thing->Top() < thing->Z()) { // Under thing return true; } @@ -1394,7 +1372,7 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) // [RH] The next condition is to compensate for the extra height // that gets added by P_CheckPosition() so that you cannot pick // up things that are above your true height. - && thingpos.z < tm.thing->Top() - tm.thing->MaxStepHeight) + && thing->Z() < tm.thing->Top() - tm.thing->MaxStepHeight) { // Can be picked up by tmthing P_TouchSpecialThing(thing, tm.thing); // can remove thing } @@ -1456,58 +1434,29 @@ bool P_CheckPosition(AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm, bo tm.x = x; tm.y = y; + tm.z = thing->Z(); - newsec = P_PointInSector(x, y); + newsec = tm.sector = P_PointInSector(x, y); tm.ceilingline = thing->BlockingLine = NULL; - // The base floor / ceiling is from the subsector that contains the point. + // Retrieve the base floor / ceiling from the target location. // Any contacted lines the step closer together will adjust them. - tm.floorz = tm.dropoffz = newsec->floorplane.ZatPoint(x, y); - tm.ceilingz = newsec->ceilingplane.ZatPoint(x, y); - tm.floorpic = newsec->GetTexture(sector_t::floor); - tm.floorterrain = newsec->GetTerrain(sector_t::floor); - tm.floorsector = newsec; - tm.ceilingpic = newsec->GetTexture(sector_t::ceiling); - tm.ceilingsector = newsec; - tm.touchmidtex = false; - tm.abovemidtex = false; - - //Added by MC: Fill the tmsector. - tm.sector = newsec; - - //Check 3D floors - if (!thing->IsNoClip2() && newsec->e->XFloor.ffloors.Size()) + if (!thing->IsNoClip2()) { - F3DFloor* rover; - fixed_t delta1; - fixed_t delta2; - int thingtop = thing->Z() + (thing->height == 0 ? 1 : thing->height); - - for (unsigned i = 0; ie->XFloor.ffloors.Size(); i++) - { - rover = newsec->e->XFloor.ffloors[i]; - if (!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue; - - fixed_t ff_bottom = rover->bottom.plane->ZatPoint(x, y); - fixed_t ff_top = rover->top.plane->ZatPoint(x, y); - - delta1 = thing->Z() - (ff_bottom + ((ff_top - ff_bottom) / 2)); - delta2 = thingtop - (ff_bottom + ((ff_top - ff_bottom) / 2)); - - if (ff_top > tm.floorz && abs(delta1) < abs(delta2)) - { - tm.floorz = tm.dropoffz = ff_top; - tm.floorpic = *rover->top.texture; - tm.floorterrain = rover->model->GetTerrain(rover->top.isceiling); - } - if (ff_bottom < tm.ceilingz && abs(delta1) >= abs(delta2)) - { - tm.ceilingz = ff_bottom; - tm.ceilingpic = *rover->bottom.texture; - } - } + P_GetFloorCeilingZ(tm, FFCF_SAMESECTOR); + } + else + { + // With noclip2, we must ignore 3D floors and go right to the uppermost ceiling and lowermost floor. + tm.floorz = tm.dropoffz = newsec->LowestFloorAt(x, y, &tm.floorsector); + tm.ceilingz = newsec->HighestCeilingAt(x, y, &tm.ceilingsector); + tm.floorpic = tm.floorsector->GetTexture(sector_t::floor); + tm.floorterrain = tm.floorsector->GetTerrain(sector_t::floor); + tm.ceilingpic = tm.ceilingsector->GetTexture(sector_t::ceiling); } + tm.touchmidtex = false; + tm.abovemidtex = false; validcount++; spechit.Clear(); @@ -1525,51 +1474,52 @@ bool P_CheckPosition(AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm, bo tm.stepthing = NULL; FBoundingBox box(x, y, thing->radius); - { - FBlockThingsIterator it2(box); - AActor *th; - while ((th = it2.Next())) - { - if (!PIT_CheckThing(th, tm)) - { // [RH] If a thing can be stepped up on, we need to continue checking - // other things in the blocks and see if we hit something that is - // definitely blocking. Otherwise, we need to check the lines, or we - // could end up stuck inside a wall. - AActor *BlockingMobj = thing->BlockingMobj; + FPortalGroupArray pcheck; + FMultiBlockThingsIterator it2(pcheck, x, y, thing->Z(), thing->height, thing->radius); + FMultiBlockThingsIterator::CheckResult tcres; - if (BlockingMobj == NULL || (i_compatflags & COMPATF_NO_PASSMOBJ)) - { // Thing slammed into something; don't let it move now. - thing->height = realheight; - return false; - } - else if (!BlockingMobj->player && !(thing->flags & (MF_FLOAT | MF_MISSILE | MF_SKULLFLY)) && - BlockingMobj->Top() - thing->Z() <= thing->MaxStepHeight) - { - if (thingblocker == NULL || - BlockingMobj->Z() > thingblocker->Z()) - { - thingblocker = BlockingMobj; - } - thing->BlockingMobj = NULL; - } - else if (thing->player && - thing->Top() - BlockingMobj->Z() <= thing->MaxStepHeight) - { - if (thingblocker) - { // There is something to step up on. Return this thing as - // the blocker so that we don't step up. - thing->height = realheight; - return false; - } - // Nothing is blocking us, but this actor potentially could - // if there is something else to step on. - thing->BlockingMobj = NULL; - } - else - { // Definitely blocking + while ((it2.Next(&tcres))) + { + if (!PIT_CheckThing(it2, tcres, it2.Box(), tm)) + { // [RH] If a thing can be stepped up on, we need to continue checking + // other things in the blocks and see if we hit something that is + // definitely blocking. Otherwise, we need to check the lines, or we + // could end up stuck inside a wall. + AActor *BlockingMobj = thing->BlockingMobj; + + // If this blocks through a restricted line portal, it will always completely block. + if (BlockingMobj == NULL || (i_compatflags & COMPATF_NO_PASSMOBJ) || (tcres.portalflags & FFCF_RESTRICTEDPORTAL)) + { // Thing slammed into something; don't let it move now. + thing->height = realheight; + return false; + } + else if (!BlockingMobj->player && !(thing->flags & (MF_FLOAT | MF_MISSILE | MF_SKULLFLY)) && + BlockingMobj->Top() - thing->Z() <= thing->MaxStepHeight) + { + if (thingblocker == NULL || + BlockingMobj->Z() > thingblocker->Z()) + { + thingblocker = BlockingMobj; + } + thing->BlockingMobj = NULL; + } + else if (thing->player && + thing->Top() - BlockingMobj->Z() <= thing->MaxStepHeight) + { + if (thingblocker) + { // There is something to step up on. Return this thing as + // the blocker so that we don't step up. thing->height = realheight; return false; } + // Nothing is blocking us, but this actor potentially could + // if there is something else to step on. + thing->BlockingMobj = NULL; + } + else + { // Definitely blocking + thing->height = realheight; + return false; } } } @@ -1592,8 +1542,8 @@ bool P_CheckPosition(AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm, bo if (actorsonly || (thing->flags & MF_NOCLIP)) return (thing->BlockingMobj = thingblocker) == NULL; - FBlockLinesIterator it(box); - line_t *ld; + FMultiBlockLinesIterator it(pcheck, x, y, thing->Z(), thing->height, thing->radius); + FMultiBlockLinesIterator::CheckResult lcres; fixed_t thingdropoffz = tm.floorz; //bool onthing = (thingdropoffz != tmdropoffz); @@ -1601,9 +1551,9 @@ bool P_CheckPosition(AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm, bo bool good = true; - while ((ld = it.Next())) + while (it.Next(&lcres)) { - good &= PIT_CheckLine(ld, box, tm); + good &= PIT_CheckLine(it, lcres, it.Box(), tm); } if (!good) { @@ -1696,12 +1646,16 @@ bool P_TestMobjZ(AActor *actor, bool quick, AActor **pOnmobj) return true; } - FBlockThingsIterator it(FBoundingBox(actor->X(), actor->Y(), actor->radius)); - AActor *thing; + FPortalGroupArray check; + FMultiBlockThingsIterator it(check, actor, -1, true); + FMultiBlockThingsIterator::CheckResult cres; - while ((thing = it.Next())) + while (it.Next(&cres)) { - if (!thing->intersects(actor)) + AActor *thing = cres.thing; + + fixed_t blockdist = thing->radius + actor->radius; + if (abs(thing->X() - cres.position.x) >= blockdist || abs(thing->Y() - cres.position.y) >= blockdist) { continue; } @@ -1808,17 +1762,18 @@ void P_FakeZMovement(AActor *mo) // //=========================================================================== -static void CheckForPushSpecial(line_t *line, int side, AActor *mobj, bool windowcheck) +static void CheckForPushSpecial(line_t *line, int side, AActor *mobj, fixedvec2 *posforwindowcheck) { if (line->special && !(mobj->flags6 & MF6_NOTRIGGER)) { - if (windowcheck && !(ib_compatflags & BCOMPATF_NOWINDOWCHECK) && line->backsector != NULL) + if (posforwindowcheck && !(ib_compatflags & BCOMPATF_NOWINDOWCHECK) && line->backsector != NULL) { // Make sure this line actually blocks us and is not a window // or similar construct we are standing inside of. - fixed_t fzt = line->frontsector->ceilingplane.ZatPoint(mobj); - fixed_t fzb = line->frontsector->floorplane.ZatPoint(mobj); - fixed_t bzt = line->backsector->ceilingplane.ZatPoint(mobj); - fixed_t bzb = line->backsector->floorplane.ZatPoint(mobj); + fixedvec3 pos = mobj->PosRelative(line); + fixed_t fzt = line->frontsector->ceilingplane.ZatPoint(*posforwindowcheck); + fixed_t fzb = line->frontsector->floorplane.ZatPoint(*posforwindowcheck); + fixed_t bzt = line->backsector->ceilingplane.ZatPoint(*posforwindowcheck); + fixed_t bzb = line->backsector->floorplane.ZatPoint(*posforwindowcheck); if (fzt >= mobj->Top() && bzt >= mobj->Top() && fzb <= mobj->Z() && bzb <= mobj->Z()) { @@ -1829,8 +1784,8 @@ static void CheckForPushSpecial(line_t *line, int side, AActor *mobj, bool windo if (!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue; - fixed_t ff_bottom = rover->bottom.plane->ZatPoint(mobj); - fixed_t ff_top = rover->top.plane->ZatPoint(mobj); + fixed_t ff_bottom = rover->bottom.plane->ZatPoint(*posforwindowcheck); + fixed_t ff_top = rover->top.plane->ZatPoint(*posforwindowcheck); if (ff_bottom < mobj->Top() && ff_top > mobj->Z()) { @@ -1880,7 +1835,6 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y, fixed_t oldz; int side; int oldside; - line_t* ld; sector_t* oldsec = thing->Sector; // [RH] for sector actions sector_t* newsec; @@ -2135,12 +2089,13 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y, // if any special lines were hit, do the effect if (!(thing->flags & (MF_TELEPORT | MF_NOCLIP))) { - while (spechit.Pop(ld)) + spechit_t spec; + while (spechit.Pop(spec)) { - fixedvec3 thingpos = thing->PosRelative(ld); + line_t *ld = spec.line; fixedvec3 oldrelpos = PosRelative(oldpos, ld, oldsector); // see if the line was crossed - side = P_PointOnLineSide(thingpos.x, thingpos.y, ld); + side = P_PointOnLineSide(spec.refpos.x, spec.refpos.y, ld); oldside = P_PointOnLineSide(oldrelpos.x, oldrelpos.y, ld); if (side != oldside && ld->special && !(thing->flags6 & MF6_NOTRIGGER)) { @@ -2239,10 +2194,9 @@ pushline: while (numSpecHitTemp > 0) { // see which lines were pushed - ld = spechit[--numSpecHitTemp]; - fixedvec3 pos = thing->PosRelative(ld); - side = P_PointOnLineSide(pos.x, pos.y, ld); - CheckForPushSpecial(ld, side, thing, true); + spechit_t &spec = spechit[--numSpecHitTemp]; + side = P_PointOnLineSide(spec.refpos.x, spec.refpos.y, spec.line); + CheckForPushSpecial(spec.line, side, thing, &spec.refpos); } } return false; @@ -2760,15 +2714,16 @@ const secplane_t * P_CheckSlopeWalk(AActor *actor, fixed_t &xmove, fixed_t &ymov return NULL; } + fixedvec3 pos = actor->PosRelative(actor->floorsector); const secplane_t *plane = &actor->floorsector->floorplane; - fixed_t planezhere = plane->ZatPoint(actor); + fixed_t planezhere = plane->ZatPoint(pos); for (unsigned int i = 0; ifloorsector->e->XFloor.ffloors.Size(); i++) { F3DFloor * rover = actor->floorsector->e->XFloor.ffloors[i]; if (!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue; - fixed_t thisplanez = rover->top.plane->ZatPoint(actor); + fixed_t thisplanez = rover->top.plane->ZatPoint(pos); if (thisplanez>planezhere && thisplanez <= actor->Z() + actor->MaxStepHeight) { @@ -2836,10 +2791,14 @@ const secplane_t * P_CheckSlopeWalk(AActor *actor, fixed_t &xmove, fixed_t &ymov { for (node = actor->touching_sectorlist; node; node = node->m_tnext) { - const sector_t *sec = node->m_sector; + sector_t *sec = node->m_sector; if (sec->floorplane.c >= STEEPSLOPE) { - if (sec->floorplane.ZatPoint(destx, desty) >= actor->Z() - actor->MaxStepHeight) + fixedvec3 pos = actor->PosRelative(sec); + pos.x += xmove; + pos.y += ymove; + + if (sec->floorplane.ZatPoint(pos) >= actor->Z() - actor->MaxStepHeight) { dopush = false; break; diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index e09c86eb42..1f32d6faa1 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -162,10 +162,28 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, 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); + if (!(flags & FFCF_NOPORTALS) && !linedef->frontsector->PortalBlocksMovement(sector_t::ceiling) && + linedef->backsector->SkyBoxes[sector_t::ceiling] && + linedef->frontsector->SkyBoxes[sector_t::ceiling]->Sector->PortalGroup == linedef->backsector->SkyBoxes[sector_t::ceiling]->Sector->PortalGroup) + { + fc = bc = FIXED_MAX; + } + else + { + fc = front->ceilingplane.ZatPoint(x, y); + bc = back->ceilingplane.ZatPoint(x, y); + } + if (!(flags & FFCF_NOPORTALS) && !linedef->frontsector->PortalBlocksMovement(sector_t::floor) && + linedef->backsector->SkyBoxes[sector_t::floor] && + linedef->frontsector->SkyBoxes[sector_t::floor]->Sector->PortalGroup == linedef->backsector->SkyBoxes[sector_t::floor]->Sector->PortalGroup) + { + ff = bf = FIXED_MIN; + } + else + { + ff = front->floorplane.ZatPoint(x, y); + bf = back->floorplane.ZatPoint(x, y); + } /*Printf ("]]]]]] %d %d\n", ff, bf);*/ @@ -211,6 +229,8 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, open.floorterrain = back->GetTerrain(sector_t::floor); open.lowfloor = ff; } + open.frontfloorplane = front->floorplane; + open.backfloorplane = back->floorplane; } else { // Dummy stuff to have some sort of opening for the 3D checks to modify @@ -222,6 +242,8 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, open.floorterrain = -1; open.bottom = FIXED_MIN; open.lowfloor = FIXED_MAX; + open.frontfloorplane.SetAtHeight(FIXED_MIN, sector_t::floor); + open.backfloorplane.SetAtHeight(FIXED_MIN, sector_t::floor); } // Check 3D floors @@ -513,11 +535,15 @@ void AActor::SetOrigin (fixed_t ix, fixed_t iy, fixed_t iz, bool moving) SetXYZ(ix, iy, iz); if (moving) SetMovement(ix - X(), iy - Y(), iz - Z()); LinkToWorld (); - floorz = Sector->floorplane.ZatPoint (ix, iy); - ceilingz = Sector->ceilingplane.ZatPoint (ix, iy); P_FindFloorCeiling(this, FFCF_ONLYSPAWNPOS); } +//=========================================================================== +// +// FBlockNode - allows to link actors into multiple blocks in the blockmap +// +//=========================================================================== + FBlockNode *FBlockNode::FreeBlocks = NULL; FBlockNode *FBlockNode::Create (AActor *who, int x, int y, int group) @@ -574,7 +600,7 @@ FBlockLinesIterator::FBlockLinesIterator(int _minx, int _miny, int _maxx, int _m Reset(); } -FBlockLinesIterator::FBlockLinesIterator(const FBoundingBox &box) +void FBlockLinesIterator::init(const FBoundingBox &box) { validcount++; maxy = GetSafeBlockY(box.Top() - bmaporgy); @@ -584,6 +610,10 @@ FBlockLinesIterator::FBlockLinesIterator(const FBoundingBox &box) Reset(); } +FBlockLinesIterator::FBlockLinesIterator(const FBoundingBox &box) +{ + init(box); +} //=========================================================================== // @@ -683,6 +713,169 @@ line_t *FBlockLinesIterator::Next() } } +//=========================================================================== +// +// FMultiBlockLinesIterator :: FMultiBlockLinesIterator +// +// An iterator that can check multiple portal groups. +// +//=========================================================================== + +FMultiBlockLinesIterator::FMultiBlockLinesIterator(FPortalGroupArray &check, AActor *origin, fixed_t checkradius) + : checklist(check) +{ + checkpoint = origin->Pos(); + if (!check.inited) P_CollectConnectedGroups(origin->Sector->PortalGroup, checkpoint, origin->Top(), checkradius, checklist); + checkpoint.z = checkradius == -1? origin->radius : checkradius; + basegroup = origin->Sector->PortalGroup; + Reset(); +} + +FMultiBlockLinesIterator::FMultiBlockLinesIterator(FPortalGroupArray &check, fixed_t checkx, fixed_t checky, fixed_t checkz, fixed_t checkh, fixed_t checkradius) + : checklist(check) +{ + checkpoint.x = checkx; + checkpoint.y = checky; + checkpoint.z = checkz; + basegroup = P_PointInSector(checkx, checky)->PortalGroup; + if (!check.inited) P_CollectConnectedGroups(basegroup, checkpoint, checkz + checkh, checkradius, checklist); + checkpoint.z = checkradius; + Reset(); +} + +//=========================================================================== +// +// Go up a ceiling portal +// +//=========================================================================== + +bool FMultiBlockLinesIterator::GoUp(fixed_t x, fixed_t y) +{ + if (continueup) + { + sector_t *sector = P_PointInSector(x, y); + if (!sector->PortalBlocksMovement(sector_t::ceiling)) + { + startIteratorForGroup(sector->SkyBoxes[sector_t::ceiling]->Sector->PortalGroup); + portalflags = FFCF_NOFLOOR; + return true; + } + else continueup = false; + } + return false; +} + +//=========================================================================== +// +// Go down a floor portal +// +//=========================================================================== + +bool FMultiBlockLinesIterator::GoDown(fixed_t x, fixed_t y) +{ + if (continuedown) + { + sector_t *sector = P_PointInSector(x, y); + if (!sector->PortalBlocksMovement(sector_t::floor)) + { + startIteratorForGroup(sector->SkyBoxes[sector_t::floor]->Sector->PortalGroup); + portalflags = FFCF_NOCEILING; + return true; + } + else continuedown = false; + } + return false; +} + +//=========================================================================== +// +// Gets the next line - also manages switching between portal groups +// +//=========================================================================== + +bool FMultiBlockLinesIterator::Next(FMultiBlockLinesIterator::CheckResult *item) +{ + line_t *line = blockIterator.Next(); + if (line != NULL) + { + item->line = line; + item->position.x = offset.x; + item->position.y = offset.y; + item->portalflags = portalflags; + return true; + } + bool onlast = unsigned(index + 1) >= checklist.Size(); + int nextflags = onlast ? 0 : checklist[index + 1] & FPortalGroupArray::FLAT; + + if (portalflags == FFCF_NOFLOOR && nextflags != FPortalGroupArray::UPPER) + { + // if this is the last upper portal in the list, check if we need to go further up to find the real ceiling. + if (GoUp(offset.x, offset.y)) return Next(item); + } + else if (portalflags == FFCF_NOCEILING && nextflags != FPortalGroupArray::LOWER) + { + // if this is the last lower portal in the list, check if we need to go further down to find the real floor. + if (GoDown(offset.x, offset.y)) return Next(item); + } + if (onlast) + { + // We reached the end of the list. Check if we still need to check up- and downwards. + if (GoUp(checkpoint.x, checkpoint.y) || + GoDown(checkpoint.x, checkpoint.y)) + { + return Next(item); + } + return false; + } + + index++; + startIteratorForGroup(checklist[index] & ~FPortalGroupArray::FLAT); + switch (nextflags) + { + case FPortalGroupArray::UPPER: + portalflags = FFCF_NOFLOOR; + break; + + case FPortalGroupArray::LOWER: + portalflags = FFCF_NOCEILING; + break; + + default: + portalflags = 0; + } + + return Next(item); +} + +//=========================================================================== +// +// start iterating a new group +// +//=========================================================================== + +void FMultiBlockLinesIterator::startIteratorForGroup(int group) +{ + offset = Displacements(basegroup, group); + offset.x += checkpoint.x; + offset.y += checkpoint.y; + bbox.setBox(offset.x, offset.y, checkpoint.z); + blockIterator.init(bbox); +} + +//=========================================================================== +// +// Resets the iterator +// +//=========================================================================== + +void FMultiBlockLinesIterator::Reset() +{ + continueup = continueup = true; + index = -1; + portalflags = 0; + startIteratorForGroup(basegroup); +} + //=========================================================================== // // FBlockThingsIterator :: FBlockThingsIterator @@ -709,8 +902,7 @@ FBlockThingsIterator::FBlockThingsIterator(int _minx, int _miny, int _maxx, int Reset(); } -FBlockThingsIterator::FBlockThingsIterator(const FBoundingBox &box) -: DynHash(0) +void FBlockThingsIterator::init(const FBoundingBox &box) { maxy = GetSafeBlockY(box.Top() - bmaporgy); miny = GetSafeBlockY(box.Bottom() - bmaporgy); @@ -852,6 +1044,108 @@ AActor *FBlockThingsIterator::Next(bool centeronly) } + +//=========================================================================== +// +// FMultiBlockThingsIterator :: FMultiBlockThingsIterator +// +// An iterator that can check multiple portal groups. +// +//=========================================================================== + +FMultiBlockThingsIterator::FMultiBlockThingsIterator(FPortalGroupArray &check, AActor *origin, fixed_t checkradius, bool ignorerestricted) + : checklist(check) +{ + checkpoint = origin->Pos(); + if (!check.inited) P_CollectConnectedGroups(origin->Sector->PortalGroup, checkpoint, origin->Top(), checkradius, checklist); + checkpoint.z = checkradius == -1? origin->radius : checkradius; + basegroup = origin->Sector->PortalGroup; + Reset(); +} + +FMultiBlockThingsIterator::FMultiBlockThingsIterator(FPortalGroupArray &check, fixed_t checkx, fixed_t checky, fixed_t checkz, fixed_t checkh, fixed_t checkradius, bool ignorerestricted) + : checklist(check) +{ + checkpoint.x = checkx; + checkpoint.y = checky; + checkpoint.z = checkz; + basegroup = P_PointInSector(checkx, checky)->PortalGroup; + if (!check.inited) P_CollectConnectedGroups(basegroup, checkpoint, checkz + checkh, checkradius, checklist); + checkpoint.z = checkradius; + Reset(); +} + +//=========================================================================== +// +// Gets the next line - also manages switching between portal groups +// +//=========================================================================== + +bool FMultiBlockThingsIterator::Next(FMultiBlockThingsIterator::CheckResult *item) +{ + AActor *thing = blockIterator.Next(); + if (thing != NULL) + { + item->thing = thing; + item->position = checkpoint + Displacements(basegroup, thing->Sector->PortalGroup); + item->portalflags = portalflags; + return true; + } + bool onlast = unsigned(index + 1) >= checklist.Size(); + int nextflags = onlast ? 0 : checklist[index + 1] & FPortalGroupArray::FLAT; + + if (onlast) + { + return false; + } + + index++; + startIteratorForGroup(checklist[index] & ~FPortalGroupArray::FLAT); + switch (nextflags) + { + case FPortalGroupArray::UPPER: + portalflags = FFCF_NOFLOOR; + break; + + case FPortalGroupArray::LOWER: + portalflags = FFCF_NOCEILING; + break; + + default: + portalflags = 0; + } + + return Next(item); +} + +//=========================================================================== +// +// start iterating a new group +// +//=========================================================================== + +void FMultiBlockThingsIterator::startIteratorForGroup(int group) +{ + fixedvec2 offset = Displacements(basegroup, group); + offset.x += checkpoint.x; + offset.y += checkpoint.y; + bbox.setBox(offset.x, offset.y, checkpoint.z); + blockIterator.init(bbox); +} + +//=========================================================================== +// +// Resets the iterator +// +//=========================================================================== + +void FMultiBlockThingsIterator::Reset() +{ + index = -1; + portalflags = 0; + startIteratorForGroup(basegroup); +} + //=========================================================================== // // FPathTraverse :: Intercepts diff --git a/src/p_maputl.h b/src/p_maputl.h index dbc4eaf9af..452f99b3d0 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -3,6 +3,7 @@ #include "r_defs.h" #include "doomstat.h" +#include "m_bbox.h" extern int validcount; @@ -98,6 +99,8 @@ struct FLineOpening sector_t *topsec; FTextureID ceilingpic; FTextureID floorpic; + secplane_t frontfloorplane; + secplane_t backfloorplane; int floorterrain; bool touchmidtex; bool abovemidtex; @@ -108,8 +111,76 @@ void P_LineOpening (FLineOpening &open, AActor *thing, const line_t *linedef, fi class FBoundingBox; struct polyblock_t; +//============================================================================ +// +// This is a dynamic array which holds its first MAX_STATIC entries in normal +// variables to avoid constant allocations which this would otherwise +// require. +// +// When collecting touched portal groups the normal cases are either +// no portals == one group or +// two portals = two groups +// +// Anything with more can happen but far less infrequently, so this +// organization helps avoiding the overhead from heap allocations +// in the vast majority of situations. +// +//============================================================================ + +struct FPortalGroupArray +{ + enum + { + LOWER = 0x4000, + UPPER = 0x8000, + FLAT = 0xc000, + }; + + enum + { + MAX_STATIC = 4 + }; + + FPortalGroupArray() + { + varused = 0; + inited = false; + } + + void Clear() + { + data.Clear(); + varused = 0; + inited = false; + } + + void Add(DWORD num) + { + if (varused < MAX_STATIC) entry[varused++] = (WORD)num; + else data.Push((WORD)num); + } + + unsigned Size() + { + return varused + data.Size(); + } + + DWORD operator[](unsigned index) + { + return index < MAX_STATIC ? entry[index] : data[index - MAX_STATIC]; + } + + bool inited; + +private: + WORD entry[MAX_STATIC]; + BYTE varused; + TArray data; +}; + class FBlockLinesIterator { + friend class FMultiBlockLinesIterator; int minx, maxx; int miny, maxy; @@ -120,6 +191,8 @@ class FBlockLinesIterator void StartBlock(int x, int y); + FBlockLinesIterator() {} + void init(const FBoundingBox &box); public: FBlockLinesIterator(int minx, int miny, int maxx, int maxy, bool keepvalidcount = false); FBlockLinesIterator(const FBoundingBox &box); @@ -127,6 +200,52 @@ public: void Reset() { StartBlock(minx, miny); } }; +class FMultiBlockLinesIterator +{ + FPortalGroupArray &checklist; + fixedvec3 checkpoint; + fixedvec2 offset; + short basegroup; + short portalflags; + short index; + bool continueup; + bool continuedown; + FBlockLinesIterator blockIterator; + FBoundingBox bbox; + + bool GoUp(fixed_t x, fixed_t y); + bool GoDown(fixed_t x, fixed_t y); + void startIteratorForGroup(int group); + +public: + + struct CheckResult + { + line_t *line; + fixedvec3 position; + int portalflags; + }; + + FMultiBlockLinesIterator(FPortalGroupArray &check, AActor *origin, fixed_t checkradius = -1); + FMultiBlockLinesIterator(FPortalGroupArray &check, fixed_t checkx, fixed_t checky, fixed_t checkz, fixed_t checkh, fixed_t checkradius); + bool Next(CheckResult *item); + void Reset(); + // for stopping group traversal through portals. Only the calling code can decide whether this is needed so this needs to be set from the outside. + void StopUp() + { + continueup = false; + } + void StopDown() + { + continuedown = false; + } + const FBoundingBox &Box() const + { + return bbox; + } +}; + + class FBlockThingsIterator { int minx, maxx; @@ -158,14 +277,52 @@ class FBlockThingsIterator FBlockThingsIterator(); friend class FPathTraverse; + friend class FMultiBlockThingsIterator; public: FBlockThingsIterator(int minx, int miny, int maxx, int maxy); - FBlockThingsIterator(const FBoundingBox &box); + FBlockThingsIterator(const FBoundingBox &box) + { + init(box); + } + void init(const FBoundingBox &box); AActor *Next(bool centeronly = false); void Reset() { StartBlock(minx, miny); } }; +class FMultiBlockThingsIterator +{ + FPortalGroupArray &checklist; + fixedvec3 checkpoint; + short basegroup; + short portalflags; + short index; + FBlockThingsIterator blockIterator; + FBoundingBox bbox; + + void startIteratorForGroup(int group); + +public: + + struct CheckResult + { + AActor *thing; + fixedvec3 position; + int portalflags; + }; + + FMultiBlockThingsIterator(FPortalGroupArray &check, AActor *origin, fixed_t checkradius = -1, bool ignorerestricted = false); + FMultiBlockThingsIterator(FPortalGroupArray &check, fixed_t checkx, fixed_t checky, fixed_t checkz, fixed_t checkh, fixed_t checkradius, bool ignorerestricted = false); + bool Next(CheckResult *item); + void Reset(); + const FBoundingBox &Box() const + { + return bbox; + } +}; + + + class FPathTraverse { static TArray intercepts; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 6baea65e25..7228ffbffd 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -2187,7 +2187,7 @@ explode: if (tm.ceilingline && tm.ceilingline->backsector && tm.ceilingline->backsector->GetTexture(sector_t::ceiling) == skyflatnum && - mo->Z() >= tm.ceilingline->backsector->ceilingplane.ZatPoint(mo)) + mo->Z() >= tm.ceilingline->backsector->ceilingplane.ZatPoint(mo->PosRelative(tm.ceilingline))) { // Hack to prevent missiles exploding against the sky. // Does not handle sky floors. @@ -3559,7 +3559,7 @@ void AActor::Tick () for (node = touching_sectorlist; node; node = node->m_tnext) { - const sector_t *sec = node->m_sector; + sector_t *sec = node->m_sector; fixed_t scrollx, scrolly; if (level.Scrolls != NULL) @@ -3641,7 +3641,8 @@ void AActor::Tick () { continue; } - height = sec->floorplane.ZatPoint (this); + fixedvec3 pos = PosRelative(sec); + height = sec->floorplane.ZatPoint (pos); if (Z() > height) { if (heightsec == NULL) @@ -3649,7 +3650,7 @@ void AActor::Tick () continue; } - waterheight = heightsec->floorplane.ZatPoint (this); + waterheight = heightsec->floorplane.ZatPoint (pos); if (waterheight > height && Z() >= waterheight) { continue; @@ -3690,7 +3691,7 @@ void AActor::Tick () floorplane = P_FindFloorPlane(floorsector, X(), Y(), floorz); if (floorplane.c < STEEPSLOPE && - floorplane.ZatPoint (this) <= floorz) + floorplane.ZatPoint (PosRelative(floorsector)) <= floorz) { const msecnode_t *node; bool dopush = true; @@ -3702,7 +3703,7 @@ void AActor::Tick () const sector_t *sec = node->m_sector; if (sec->floorplane.c >= STEEPSLOPE) { - if (floorplane.ZatPoint (this) >= Z() - MaxStepHeight) + if (floorplane.ZatPoint (PosRelative(node->m_sector)) >= Z() - MaxStepHeight) { dopush = false; break; @@ -4478,15 +4479,16 @@ void AActor::AdjustFloorClip () const msecnode_t *m; // possibly standing on a 3D-floor - if (Sector->e->XFloor.ffloors.Size() && Z()>Sector->floorplane.ZatPoint(this)) floorclip=0; + if (Sector->e->XFloor.ffloors.Size() && Z() > Sector->floorplane.ZatPoint(this)) floorclip = 0; // [RH] clip based on shallowest floor player is standing on // If the sector has a deep water effect, then let that effect // do the floorclipping instead of the terrain type. for (m = touching_sectorlist; m; m = m->m_tnext) { + fixedvec3 pos = PosRelative(m->m_sector); sector_t *hsec = m->m_sector->GetHeightSec(); - if (hsec == NULL && m->m_sector->floorplane.ZatPoint (this) == Z()) + if (hsec == NULL && m->m_sector->floorplane.ZatPoint (pos) == Z()) { fixed_t clip = Terrains[m->m_sector->GetTerrain(sector_t::floor)].FootClip; if (clip < shallowestclip) @@ -5356,7 +5358,7 @@ void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator) } if (bloodtype >= 1) { - P_DrawSplash2 (40, x, y, z, R_PointToAngle2 (x, y, originator->X(), originator->Y()), 2, bloodcolor); + P_DrawSplash2 (40, x, y, z, 0u - originator->AngleTo(x, y), 2, bloodcolor); } } @@ -5396,7 +5398,7 @@ void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator) } if (bloodtype >= 1) { - P_DrawSplash2 (100, x, y, z, R_PointToAngle2 (0, 0, originator->X() - x, originator->Y() - y), 2, bloodcolor); + P_DrawSplash2 (100, x, y, z, 0u - originator->AngleTo(x, y), 2, bloodcolor); } } @@ -5524,9 +5526,9 @@ bool P_HitWater (AActor * thing, sector_t * sec, fixed_t x, fixed_t y, fixed_t z fixed_t planez = rover->top.plane->ZatPoint(x, y); if (z > planez - FRACUNIT / 2 && z < planez + FRACUNIT / 2) // allow minor imprecisions { - if (rover->flags & (FF_SOLID | FF_SWIMMABLE)) + if (rover->flags & (FF_SOLID | FF_SWIMMABLE)) { - terrainnum = rover->model->GetTerrain(rover->top.isceiling); + terrainnum = rover->model->GetTerrain(rover->top.isceiling); goto foundone; } } @@ -5639,9 +5641,11 @@ bool P_HitFloor (AActor *thing) return false; // don't splash if landing on the edge above water/lava/etc.... + fixedvec3 pos; for (m = thing->touching_sectorlist; m; m = m->m_tnext) { - if (thing->Z() == m->m_sector->floorplane.ZatPoint(thing)) + pos = thing->PosRelative(m->m_sector); + if (thing->Z() == m->m_sector->floorplane.ZatPoint(pos.x, pos.y)) { break; } @@ -5653,9 +5657,9 @@ bool P_HitFloor (AActor *thing) if (!(rover->flags & FF_EXISTS)) continue; if (rover->flags & (FF_SOLID|FF_SWIMMABLE)) { - if (rover->top.plane->ZatPoint(thing) == thing->Z()) + if (rover->top.plane->ZatPoint(pos.x, pos.y) == thing->Z()) { - return P_HitWater (thing, m->m_sector); + return P_HitWater (thing, m->m_sector, pos.x, pos.y, pos.z); } } } @@ -5665,7 +5669,7 @@ bool P_HitFloor (AActor *thing) return false; } - return P_HitWater (thing, m->m_sector); + return P_HitWater (thing, m->m_sector, pos.x, pos.y, pos.z); } //--------------------------------------------------------------------------- @@ -5678,12 +5682,15 @@ bool P_HitFloor (AActor *thing) void P_CheckSplash(AActor *self, fixed_t distance) { - if (self->Z() <= self->floorz + (distance<floorsector == self->Sector && self->Sector->GetHeightSec() == NULL) + sector_t *floorsec; + self->Sector->LowestFloorAt(self, &floorsec); + if (self->Z() <= self->floorz + (distance<floorsector == floorsec && self->Sector->GetHeightSec() == NULL && floorsec->heightsec == NULL) { // Explosion splashes never alert monsters. This is because A_Explode has // a separate parameter for that so this would get in the way of proper // behavior. - P_HitWater (self, self->Sector, self->X(), self->Y(), self->floorz, false, false); + fixedvec3 pos = self->PosRelative(floorsec); + P_HitWater (self, floorsec, pos.x, pos.y, self->floorz, false, false); } } diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index a72c9d8974..2f113810d5 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -55,7 +55,6 @@ #include "p_lnspec.h" #include "p_acs.h" #include "p_terrain.h" -#include "portal.h" static void CopyPlayer (player_t *dst, player_t *src, const char *name); static void ReadOnePlayer (FArchive &arc, bool skipload); diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index a10568388d..756495ac81 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -31,6 +31,7 @@ #include "farchive.h" #include "r_utility.h" #include "a_sharedglobal.h" +#include "p_local.h" #include "r_data/colormaps.h" @@ -805,7 +806,7 @@ int sector_t::GetCeilingLight () const ASkyViewpoint *sector_t::GetSkyBox(int which) { - if (SkyBoxes[which] != NULL) return SkyBoxes[which]; + if (SkyBoxes[which] != NULL) return barrier_cast(SkyBoxes[which]); if (MoreFlags & (SECF_NOFLOORSKYBOX << which)) return NULL; return level.DefaultSkybox; } @@ -873,7 +874,7 @@ int sector_t::GetTerrain(int pos) const void sector_t::CheckPortalPlane(int plane) { - ASkyViewpoint *portal = SkyBoxes[plane]; + AActor *portal = SkyBoxes[plane]; if (!portal || portal->special1 != SKYBOX_LINKEDPORTAL) return; fixed_t planeh = planes[plane].TexZ; @@ -882,7 +883,140 @@ void sector_t::CheckPortalPlane(int plane) planes[plane].Flags = (planes[plane].Flags & ~PLANEF_OBSTRUCTED) | obstructed; } +//=========================================================================== +// +// Finds the highest ceiling at the given position, all portals considered +// +//=========================================================================== +fixed_t sector_t::HighestCeilingAt(fixed_t x, fixed_t y, sector_t **resultsec) +{ + sector_t *check = this; + fixed_t planeheight = FIXED_MIN; + + // Continue until we find a blocking portal or a portal below where we actually are. + while (!check->PortalBlocksMovement(ceiling) && planeheight < check->SkyBoxes[ceiling]->threshold) + { + FDisplacement &disp = check->CeilingDisplacement(); + x += disp.pos.x; + y += disp.pos.y; + planeheight = check->SkyBoxes[ceiling]->threshold; + check = P_PointInSector(x, y); + } + if (resultsec) *resultsec = check; + return check->ceilingplane.ZatPoint(x, y); +} + +//=========================================================================== +// +// Finds the lowest floor at the given position, all portals considered +// +//=========================================================================== + +fixed_t sector_t::LowestFloorAt(fixed_t x, fixed_t y, sector_t **resultsec) +{ + sector_t *check = this; + fixed_t planeheight = FIXED_MAX; + + // Continue until we find a blocking portal or a portal above where we actually are. + while (!check->PortalBlocksMovement(floor) && planeheight > check->SkyBoxes[floor]->threshold) + { + FDisplacement &disp = check->FloorDisplacement(); + x += disp.pos.x; + y += disp.pos.y; + planeheight = check->SkyBoxes[floor]->threshold; + check = P_PointInSector(x, y); + } + if (resultsec) *resultsec = check; + return check->floorplane.ZatPoint(x, y); +} + + +fixed_t sector_t::NextHighestCeilingAt(fixed_t x, fixed_t y, fixed_t z, int flags, sector_t **resultsec, F3DFloor **resultffloor) +{ + sector_t *sec = this; + fixed_t planeheight = FIXED_MIN; + + while (true) + { + // Looking through planes from bottom to top + for (int i = sec->e->XFloor.ffloors.Size() - 1; i >= 0; --i) + { + F3DFloor *ff = sec->e->XFloor.ffloors[i]; + + fixed_t ffz = ff->bottom.plane->ZatPoint(x, y); + if ((ff->flags & (FF_EXISTS | FF_SOLID)) == (FF_EXISTS | FF_SOLID) && z <= ffz) + { // This floor is above our eyes. + if (resultsec) *resultsec = sec; + if (resultffloor) *resultffloor = ff; + return ffz; + } + } + if ((flags & FFCF_NOPORTALS) || sec->PortalBlocksMovement(ceiling) || planeheight >= sec->SkyBoxes[ceiling]->threshold) + { // Use sector's floor + if (resultffloor) *resultffloor = NULL; + if (resultsec) *resultsec = sec; + return sec->ceilingplane.ZatPoint(x, y); + } + else + { + FDisplacement &disp = sec->CeilingDisplacement(); + x += disp.pos.x; + y += disp.pos.y; + planeheight = sec->SkyBoxes[ceiling]->threshold; + sec = P_PointInSector(x, y); + } + } +} + +fixed_t sector_t::NextLowestFloorAt(fixed_t x, fixed_t y, fixed_t z, int flags, fixed_t steph, sector_t **resultsec, F3DFloor **resultffloor) +{ + sector_t *sec = this; + fixed_t planeheight = FIXED_MAX; + while (true) + { + // Looking through planes from top to bottom + unsigned numff = sec->e->XFloor.ffloors.Size(); + for (unsigned i = 0; i < numff; ++i) + { + F3DFloor *ff = sec->e->XFloor.ffloors[i]; + + fixed_t ffz = ff->top.plane->ZatPoint(x, y); + fixed_t ffb = ff->bottom.plane->ZatPoint(x, y); + + // either with feet above the 3D floor or feet with less than 'stepheight' map units inside + if ((ff->flags & (FF_EXISTS | FF_SOLID)) == (FF_EXISTS | FF_SOLID)) + { + if (z >= ffz || (!(flags & FFCF_3DRESTRICT) && (ffb < z && ffz < z + steph))) + { // This floor is beneath our feet. + if (resultsec) *resultsec = sec; + if (resultffloor) *resultffloor = ff; + return ffz; + } + } + } + if ((flags & FFCF_NOPORTALS) || sec->PortalBlocksMovement(sector_t::floor) || planeheight <= sec->SkyBoxes[floor]->threshold) + { // Use sector's floor + if (resultffloor) *resultffloor = NULL; + if (resultsec) *resultsec = sec; + return sec->floorplane.ZatPoint(x, y); + } + else + { + FDisplacement &disp = sec->FloorDisplacement(); + x += disp.pos.x; + y += disp.pos.y; + planeheight = sec->SkyBoxes[floor]->threshold; + sec = P_PointInSector(x, y); + } + } +} + +//=========================================================================== +// +// +// +//=========================================================================== FArchive &operator<< (FArchive &arc, secspecial_t &p) { @@ -908,6 +1042,11 @@ FArchive &operator<< (FArchive &arc, secspecial_t &p) } +//=========================================================================== +// +// +// +//=========================================================================== bool secplane_t::CopyPlaneIfValid (secplane_t *dest, const secplane_t *opp) const { @@ -1085,4 +1224,3 @@ int side_t::GetLightLevel (bool foggy, int baselight, bool is3dlight, int *pfake } return baselight; } - diff --git a/src/p_setup.cpp b/src/p_setup.cpp index c5780e912f..b1cb43c25f 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -69,7 +69,6 @@ #include "po_man.h" #include "r_renderer.h" #include "r_data/colormaps.h" -#include "portal.h" #include "p_blockmap.h" #include "r_utility.h" #include "p_spec.h" @@ -3393,12 +3392,12 @@ void P_FreeLevelData () FPolyObj::ClearAllSubsectorLinks(); // can't be done as part of the polyobj deletion process. SN_StopAllSequences (); DThinker::DestroyAllThinkers (); + P_ClearPortals(); tagManager.Clear(); level.total_monsters = level.total_items = level.total_secrets = level.killed_monsters = level.found_items = level.found_secrets = wminfo.maxfrags = 0; - linePortals.Clear(); FBehavior::StaticUnloadModules (); if (vertexes != NULL) { diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 45ed51be2e..e18f78b294 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -67,7 +67,6 @@ #include "c_dispatch.h" #include "r_sky.h" #include "d_player.h" -#include "portal.h" #include "p_maputl.h" #include "p_blockmap.h" #ifndef NO_EDATA @@ -440,7 +439,7 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector) { // Falling, not all the way down yet? sector = player->mo->Sector; - if (player->mo->Z() != sector->floorplane.ZatPoint(player->mo) + if (player->mo->Z() != sector->LowestFloorAt(player->mo) && !player->mo->waterlevel) { return; @@ -480,7 +479,7 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector) } if (sector->Flags & SECF_DMGTERRAINFX) { - P_HitWater(player->mo, sector, INT_MIN, INT_MIN, INT_MIN, false, true, true); + P_HitWater(player->mo, player->mo->Sector, INT_MIN, INT_MIN, INT_MIN, false, true, true); } } } @@ -919,10 +918,11 @@ static void SetupFloorPortal (AStackPoint *point) { NActorIterator it (NAME_LowerStackLookOnly, point->tid); sector_t *Sector = point->Sector; - Sector->SkyBoxes[sector_t::floor] = static_cast(it.Next()); - if (Sector->SkyBoxes[sector_t::floor] != NULL && Sector->SkyBoxes[sector_t::floor]->bAlways) + ASkyViewpoint *skyv = static_cast(it.Next()); + Sector->SkyBoxes[sector_t::floor] = skyv; + if (skyv != NULL && skyv->bAlways) { - Sector->SkyBoxes[sector_t::floor]->Mate = point; + skyv->Mate = point; if (Sector->GetAlpha(sector_t::floor) == OPAQUE) Sector->SetAlpha(sector_t::floor, Scale (point->args[0], OPAQUE, 255)); } @@ -932,12 +932,13 @@ static void SetupCeilingPortal (AStackPoint *point) { NActorIterator it (NAME_UpperStackLookOnly, point->tid); sector_t *Sector = point->Sector; - Sector->SkyBoxes[sector_t::ceiling] = static_cast(it.Next()); - if (Sector->SkyBoxes[sector_t::ceiling] != NULL && Sector->SkyBoxes[sector_t::ceiling]->bAlways) + ASkyViewpoint *skyv = static_cast(it.Next()); + Sector->SkyBoxes[sector_t::ceiling] = skyv; + if (skyv != NULL && skyv->bAlways) { - Sector->SkyBoxes[sector_t::ceiling]->Mate = point; + skyv->Mate = point; if (Sector->GetAlpha(sector_t::ceiling) == OPAQUE) - Sector->SetAlpha(sector_t::ceiling, Scale (point->args[0], OPAQUE, 255)); + Sector->SetAlpha(sector_t::ceiling, Scale(point->args[0], OPAQUE, 255)); } } @@ -968,7 +969,7 @@ static void SetPortal(sector_t *sector, int plane, ASkyViewpoint *portal, fixed_ // plane: 0=floor, 1=ceiling, 2=both if (plane > 0) { - if (sector->SkyBoxes[sector_t::ceiling] == NULL || !sector->SkyBoxes[sector_t::ceiling]->bAlways) + if (sector->SkyBoxes[sector_t::ceiling] == NULL || !barrier_cast(sector->SkyBoxes[sector_t::ceiling])->bAlways) { sector->SkyBoxes[sector_t::ceiling] = portal; if (sector->GetAlpha(sector_t::ceiling) == OPAQUE) @@ -979,7 +980,7 @@ static void SetPortal(sector_t *sector, int plane, ASkyViewpoint *portal, fixed_ } if (plane == 2 || plane == 0) { - if (sector->SkyBoxes[sector_t::floor] == NULL || !sector->SkyBoxes[sector_t::floor]->bAlways) + if (sector->SkyBoxes[sector_t::floor] == NULL || !barrier_cast(sector->SkyBoxes[sector_t::floor])->bAlways) { sector->SkyBoxes[sector_t::floor] = portal; } @@ -2299,6 +2300,7 @@ void DPusher::Tick () continue; sector_t *hsec = sec->GetHeightSec(); + fixedvec3 pos = thing->PosRelative(sec); if (m_Type == p_wind) { if (hsec == NULL) @@ -2316,7 +2318,7 @@ void DPusher::Tick () } else // special water sector { - ht = hsec->floorplane.ZatPoint(thing); + ht = hsec->floorplane.ZatPoint(pos); if (thing->Z() > ht) // above ground { xspeed = m_Xmag; // full force @@ -2345,7 +2347,7 @@ void DPusher::Tick () { // special water sector floor = &hsec->floorplane; } - if (thing->Z() > floor->ZatPoint(thing)) + if (thing->Z() > floor->ZatPoint(pos)) { // above ground xspeed = yspeed = 0; // no force } diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 2b01be3721..e86093a8c9 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -47,7 +47,6 @@ #include "r_data/colormaps.h" #include "w_wad.h" #include "p_tags.h" -#include "portal.h" #include "p_terrain.h" //=========================================================================== diff --git a/src/p_user.cpp b/src/p_user.cpp index 5294203476..fbcf412387 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -769,10 +769,9 @@ void APlayerPawn::PostBeginPlay() // Voodoo dolls: restore original floorz/ceilingz logic if (player == NULL || player->mo != this) { - dropoffz = floorz = Sector->floorplane.ZatPoint(this); - ceilingz = Sector->ceilingplane.ZatPoint(this); - P_FindFloorCeiling(this, FFCF_ONLYSPAWNPOS); + P_FindFloorCeiling(this, FFCF_ONLYSPAWNPOS|FFCF_NOPORTALS); SetZ(floorz); + P_FindFloorCeiling(this, FFCF_ONLYSPAWNPOS); } else { diff --git a/src/portal.cpp b/src/portal.cpp index a525b22f00..ef22eade34 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -37,7 +37,6 @@ */ -#include "portal.h" #include "p_local.h" #include "p_lnspec.h" #include "r_bsp.h" @@ -360,6 +359,20 @@ bool P_ChangePortal(line_t *ln, int thisid, int destid) return res; } +//============================================================================ +// +// clears all portal dat for a new level start +// +//============================================================================ + +void P_ClearPortals() +{ + Displacements.Create(1); + linePortals.Clear(); + linkedPortals.Clear(); +} + + //============================================================================ // // Calculate the intersection between two lines. @@ -774,13 +787,13 @@ static void AddDisplacementForPortal(AStackPoint *portal) FDisplacement & disp = Displacements(thisgroup, othergroup); if (!disp.isSet) { - disp.x = portal->scaleX; - disp.y = portal->scaleY; + disp.pos.x = portal->scaleX; + disp.pos.y = portal->scaleY; disp.isSet = true; } else { - if (disp.x != portal->scaleX || disp.y != portal->scaleY) + if (disp.pos.x != portal->scaleX || disp.pos.y != portal->scaleY) { Printf("Portal between sectors %d and %d has displacement mismatch and will be disabled\n", portal->Sector->sectornum, portal->Mate->Sector->sectornum); portal->special1 = portal->Mate->special1 = SKYBOX_PORTAL; @@ -810,13 +823,13 @@ static void AddDisplacementForPortal(FLinePortal *portal) FDisplacement & disp = Displacements(thisgroup, othergroup); if (!disp.isSet) { - disp.x = portal->mXDisplacement; - disp.y = portal->mYDisplacement; + disp.pos.x = portal->mXDisplacement; + disp.pos.y = portal->mYDisplacement; disp.isSet = true; } else { - if (disp.x != portal->mXDisplacement || disp.y != portal->mYDisplacement) + if (disp.pos.x != portal->mXDisplacement || disp.pos.y != portal->mYDisplacement) { Printf("Portal between lines %d and %d has displacement mismatch\n", int(portal->mOrigin - lines), int(portal->mDestination - lines)); portal->mType = linePortals[portal->mDestination->portalindex].mType = PORTT_TELEPORT; @@ -857,15 +870,14 @@ static bool ConnectGroups() FDisplacement &dispxz = Displacements(x, z); if (dispxz.isSet) { - if (dispxy.x + dispyz.x != dispxz.x || dispxy.y + dispyz.y != dispxz.y) + if (dispxy.pos.x + dispyz.pos.x != dispxz.pos.x || dispxy.pos.y + dispyz.pos.y != dispxz.pos.y) { bogus = true; } } else { - dispxz.x = dispxy.x + dispyz.x; - dispxz.y = dispxy.y + dispyz.y; + dispxz.pos = dispxy.pos + dispyz.pos; dispxz.isSet = true; dispxz.indirect = indirect; changed = true; @@ -920,13 +932,15 @@ void P_CreateLinkedPortals() } if (orgs.Size() == 0) { + // Create the 0->0 translation which is always needed. + Displacements.Create(1); return; } for (int i = 0; i < numsectors; i++) { for (int j = 0; j < 2; j++) { - ASkyViewpoint *box = sectors[i].SkyBoxes[j]; + AActor *box = sectors[i].SkyBoxes[j]; if (box != NULL && box->special1 == SKYBOX_LINKEDPORTAL) { secplane_t &plane = j == 0 ? sectors[i].floorplane : sectors[i].ceilingplane; @@ -962,7 +976,7 @@ void P_CreateLinkedPortals() { for (int j = 0; j < 2; j++) { - ASkyViewpoint *box = sectors[i].SkyBoxes[j]; + ASkyViewpoint *box = barrier_cast(sectors[i].SkyBoxes[j]); if (box != NULL) { if (box->special1 == SKYBOX_LINKEDPORTAL && sectors[i].PortalGroup == 0) @@ -995,7 +1009,7 @@ void P_CreateLinkedPortals() FDisplacement &dispxy = Displacements(x, y); FDisplacement &dispyx = Displacements(y, x); if (dispxy.isSet && dispyx.isSet && - (dispxy.x != -dispyx.x || dispxy.y != -dispyx.y)) + (dispxy.pos.x != -dispyx.pos.x || dispxy.pos.y != -dispyx.pos.y)) { int sec1 = -1, sec2 = -1; for (int i = 0; i < numsectors && (sec1 == -1 || sec2 == -1); i++) @@ -1054,7 +1068,7 @@ void P_CreateLinkedPortals() // //============================================================================ -bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortalGroupArray &out) +bool P_CollectConnectedGroups(int startgroup, const fixedvec3 &position, fixed_t upperz, fixed_t checkradius, FPortalGroupArray &out) { // Keep this temporary work stuff static. This function can never be called recursively // and this would have to be reallocated for each call otherwise. @@ -1062,19 +1076,19 @@ bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortal static TArray foundPortals; bool retval = false; + out.inited = true; if (linkedPortals.Size() == 0) { // If there are no portals, all sectors are in group 0. - out.Add(0); return false; } processMask.setSize(linkedPortals.Size()); processMask.clear(); foundPortals.Clear(); - int thisgroup = actor->Sector->PortalGroup; + int thisgroup = startgroup; processMask.setBit(thisgroup); - out.Add(thisgroup); + //out.Add(thisgroup); for (unsigned i = 0; i < linkedPortals.Size(); i++) { @@ -1083,7 +1097,7 @@ bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortal FDisplacement &disp = Displacements(thisgroup, othergroup); if (!disp.isSet) continue; // no connection. - FBoundingBox box(newx + disp.x, newy + disp.y, actor->radius); + FBoundingBox box(position.x + disp.pos.x, position.y + disp.pos.y, checkradius); if (box.Right() <= ld->bbox[BOXLEFT] || box.Left() >= ld->bbox[BOXRIGHT] @@ -1111,27 +1125,27 @@ bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortal } } } - sector_t *sec = P_PointInSector(newx, newy); + sector_t *sec = P_PointInSector(position.x, position.y); sector_t *wsec = sec; - while (!wsec->PortalBlocksMovement(sector_t::ceiling) && actor->Top() > wsec->SkyBoxes[sector_t::ceiling]->threshold) + while (!wsec->PortalBlocksMovement(sector_t::ceiling) && upperz > wsec->SkyBoxes[sector_t::ceiling]->threshold) { sector_t *othersec = wsec->SkyBoxes[sector_t::ceiling]->Sector; - FDisplacement &disp = Displacements(actor->Sector->PortalGroup, othersec->PortalGroup); - fixed_t dx = newx + disp.x; - fixed_t dy = newx + disp.y; + FDisplacement &disp = Displacements(startgroup, othersec->PortalGroup); + fixed_t dx = position.x + disp.pos.x; + fixed_t dy = position.y + disp.pos.y; processMask.setBit(othersec->PortalGroup); - out.Add(othersec->PortalGroup); + out.Add(othersec->PortalGroup|FPortalGroupArray::UPPER); wsec = P_PointInSector(dx, dy); // get upper sector at the exact spot we want to check and repeat retval = true; } wsec = sec; - while (!wsec->PortalBlocksMovement(sector_t::floor) && actor->Z() < wsec->SkyBoxes[sector_t::floor]->threshold) + while (!wsec->PortalBlocksMovement(sector_t::floor) && position.z < wsec->SkyBoxes[sector_t::floor]->threshold) { - sector_t *othersec = wsec->SkyBoxes[sector_t::ceiling]->Sector; - FDisplacement &disp = Displacements(actor->Sector->PortalGroup, othersec->PortalGroup); - fixed_t dx = newx + disp.x; - fixed_t dy = newx + disp.y; - processMask.setBit(othersec->PortalGroup); + sector_t *othersec = wsec->SkyBoxes[sector_t::floor]->Sector; + FDisplacement &disp = Displacements(startgroup, othersec->PortalGroup); + fixed_t dx = position.x + disp.pos.x; + fixed_t dy = position.y + disp.pos.y; + processMask.setBit(othersec->PortalGroup|FPortalGroupArray::LOWER); out.Add(othersec->PortalGroup); wsec = P_PointInSector(dx, dy); // get lower sector at the exact spot we want to check and repeat retval = true; @@ -1153,7 +1167,7 @@ CCMD(dumplinktable) for (int y = 1; y < Displacements.size; y++) { FDisplacement &disp = Displacements(x, y); - Printf("%c%c(%6d, %6d)", TEXTCOLOR_ESCAPE, 'C' + disp.indirect, disp.x >> FRACBITS, disp.y >> FRACBITS); + Printf("%c%c(%6d, %6d)", TEXTCOLOR_ESCAPE, 'C' + disp.indirect, disp.pos.x >> FRACBITS, disp.pos.y >> FRACBITS); } Printf("\n"); } diff --git a/src/portal.h b/src/portal.h index 40c4a30fc5..5ea0293ac4 100644 --- a/src/portal.h +++ b/src/portal.h @@ -3,11 +3,7 @@ #include "basictypes.h" #include "v_video.h" -#include "r_defs.h" -#include "actor.h" -#include "p_local.h" #include "m_bbox.h" -#include "a_sharedglobal.h" struct FPortalGroupArray; //============================================================================ @@ -30,9 +26,14 @@ struct FPortalGroupArray; struct FDisplacement { - fixed_t x, y; + fixedvec2 pos; bool isSet; BYTE indirect; // just for illustration. + + operator fixedvec2() + { + return pos; + } }; struct FDisplacementTable @@ -40,6 +41,11 @@ struct FDisplacementTable TArray data; int size; + FDisplacementTable() + { + Create(1); + } + void Create(int numgroups) { data.Resize(numgroups*numgroups); @@ -49,6 +55,7 @@ struct FDisplacementTable FDisplacement &operator()(int x, int y) { + if (x == y) return data[0]; // shortcut for the most common case return data[x + size*y]; } }; @@ -116,11 +123,12 @@ struct FLinePortal extern TArray linePortals; +void P_ClearPortals(); void P_SpawnLinePortal(line_t* line); void P_FinalizePortals(); bool P_ChangePortal(line_t *ln, int thisid, int destid); void P_CreateLinkedPortals(); -bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortalGroupArray &out); +bool P_CollectConnectedGroups(int startgroup, const fixedvec3 &position, fixed_t upperz, fixed_t checkradius, FPortalGroupArray &out); void P_CollectLinkedPortals(); inline int P_NumPortalGroups() { @@ -175,51 +183,4 @@ public: /* new code */ fixed_t P_PointLineDistance(line_t* line, fixed_t x, fixed_t y); -//============================================================================ -// -// some wrappers around the portal data. -// -//============================================================================ - - -// returns true if the portal is crossable by actors -inline bool line_t::isLinePortal() const -{ - return portalindex >= linePortals.Size() ? false : !!(linePortals[portalindex].mFlags & PORTF_PASSABLE); -} - -// returns true if the portal needs to be handled by the renderer -inline bool line_t::isVisualPortal() const -{ - return portalindex >= linePortals.Size() ? false : !!(linePortals[portalindex].mFlags & PORTF_VISIBLE); -} - -inline line_t *line_t::getPortalDestination() const -{ - return portalindex >= linePortals.Size() ? (line_t*)NULL : linePortals[portalindex].mDestination; -} - -inline int line_t::getPortalAlignment() const -{ - return portalindex >= linePortals.Size() ? 0 : linePortals[portalindex].mAlign; -} - -inline bool sector_t::PortalBlocksView(int plane) -{ - if (SkyBoxes[plane] == NULL || SkyBoxes[plane]->special1 != SKYBOX_LINKEDPORTAL) return true; - return !!(planes[plane].Flags & (PLANEF_NORENDER | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); -} - -inline bool sector_t::PortalBlocksMovement(int plane) -{ - if (SkyBoxes[plane] == NULL || SkyBoxes[plane]->special1 != SKYBOX_LINKEDPORTAL) return true; - return !!(planes[plane].Flags & (PLANEF_NOPASS | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); -} - -inline bool sector_t::PortalBlocksSound(int plane) -{ - if (SkyBoxes[plane] == NULL || SkyBoxes[plane]->special1 != SKYBOX_LINKEDPORTAL) return true; - return !!(planes[plane].Flags & (PLANEF_BLOCKSOUND | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); -} - #endif \ No newline at end of file diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index 8fba4a8e36..a95bd97a0e 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -56,7 +56,6 @@ #include "r_sky.h" #include "po_man.h" #include "r_data/colormaps.h" -#include "portal.h" seg_t* curline; side_t* sidedef; diff --git a/src/r_bsp.h b/src/r_bsp.h index 8c19a26703..30ddf3b0e3 100644 --- a/src/r_bsp.h +++ b/src/r_bsp.h @@ -25,7 +25,7 @@ #include "tarray.h" #include -#include "portal.h" +#include "r_defs.h" // The 3072 below is just an arbitrary value picked to avoid // drawing lines the player is too close to that would overflow diff --git a/src/r_defs.h b/src/r_defs.h index 6913d868ba..ab55201f91 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -55,6 +55,7 @@ enum }; extern size_t MaxDrawSegs; +struct FDisplacement; enum @@ -242,6 +243,16 @@ struct secplane_t return ic < 0 ? d : -d; } + fixed_t ZatPoint(const fixedvec2 &spot) const + { + return FixedMul(ic, -d - DMulScale16(a, spot.x, b, spot.y)); + } + + fixed_t ZatPoint(const fixedvec3 &spot) const + { + return FixedMul(ic, -d - DMulScale16(a, spot.x, b, spot.y)); + } + // Returns the value of z at (x,y) fixed_t ZatPoint (fixed_t x, fixed_t y) const { @@ -334,6 +345,21 @@ struct secplane_t return -TMulScale16 (a, v->x, b, v->y, z, c); } + void SetAtHeight(fixed_t height, int ceiling) + { + a = b = 0; + if (ceiling) + { + c = ic = -FRACUNIT; + d = height; + } + else + { + c = ic = FRACUNIT; + d = -height; + } + } + bool CopyPlaneIfValid (secplane_t *dest, const secplane_t *opp) const; }; @@ -702,17 +728,6 @@ struct sector_t return pos == floor? floorplane:ceilingplane; } - fixed_t HighestCeiling(AActor *a) const - { - return ceilingplane.ZatPoint(a); - } - - fixed_t LowestFloor(AActor *a) const - { - return floorplane.ZatPoint(a); - } - - bool isSecret() const { return !!(Flags & SECF_SECRET); @@ -739,9 +754,41 @@ struct sector_t Flags &= ~SECF_SPECIALFLAGS; } - bool PortalBlocksView(int plane); - bool PortalBlocksMovement(int plane); - bool PortalBlocksSound(int plane); + bool PortalBlocksView(int plane) + { + if (SkyBoxes[plane] == NULL) return true; + if (SkyBoxes[plane]->special1 != SKYBOX_LINKEDPORTAL) return false; + return !!(planes[plane].Flags & (PLANEF_NORENDER | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); + } + + bool PortalBlocksSight(int plane) + { + if (SkyBoxes[plane] == NULL || SkyBoxes[plane]->special1 != SKYBOX_LINKEDPORTAL) return true; + return !!(planes[plane].Flags & (PLANEF_NORENDER | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); + } + + bool PortalBlocksMovement(int plane) + { + if (SkyBoxes[plane] == NULL || SkyBoxes[plane]->special1 != SKYBOX_LINKEDPORTAL) return true; + return !!(planes[plane].Flags & (PLANEF_NOPASS | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); + } + + bool PortalBlocksSound(int plane) + { + if (SkyBoxes[plane] == NULL || SkyBoxes[plane]->special1 != SKYBOX_LINKEDPORTAL) return true; + return !!(planes[plane].Flags & (PLANEF_BLOCKSOUND | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); + } + + // These may only be called if the portal has been validated + FDisplacement &FloorDisplacement() + { + return Displacements(PortalGroup, SkyBoxes[sector_t::floor]->Sector->PortalGroup); + } + + FDisplacement &CeilingDisplacement() + { + return Displacements(PortalGroup, SkyBoxes[sector_t::ceiling]->Sector->PortalGroup); + } int GetTerrain(int pos) const; @@ -750,6 +797,32 @@ struct sector_t void SetSpecial(const secspecial_t *spec); bool PlaneMoving(int pos); + // Portal-aware height calculation + fixed_t HighestCeilingAt(fixed_t x, fixed_t y, sector_t **resultsec = NULL); + fixed_t LowestFloorAt(fixed_t x, fixed_t y, sector_t **resultsec = NULL); + + fixed_t HighestCeilingAt(AActor *a, sector_t **resultsec = NULL) + { + return HighestCeilingAt(a->X(), a->Y(), resultsec); + } + + fixed_t LowestFloorAt(AActor *a, sector_t **resultsec = NULL) + { + return LowestFloorAt(a->X(), a->Y(), resultsec); + } + + fixed_t NextHighestCeilingAt(fixed_t x, fixed_t y, fixed_t z, int flags = 0, sector_t **resultsec = NULL, F3DFloor **resultffloor = NULL); + fixed_t NextLowestFloorAt(fixed_t x, fixed_t y, fixed_t z, int flags = 0, fixed_t steph = 0, sector_t **resultsec = NULL, F3DFloor **resultffloor = NULL); + + fixed_t NextHighestCeilingAt(AActor *a, fixed_t z, int flags = 0, sector_t **resultsec = NULL, F3DFloor **resultffloor = NULL) + { + return NextHighestCeilingAt(a->X(), a->Y(), z, flags, resultsec, resultffloor); + } + + fixed_t NextLowestFloorAt(AActor *a, fixed_t z, int flags, sector_t **resultsec = NULL, F3DFloor **resultffloor = NULL) + { + return NextLowestFloorAt(a->X(), a->Y(), z, flags, a->MaxStepHeight, resultsec, resultffloor); + } // Member variables fixed_t CenterFloor () const { return floorplane.ZatPoint (soundorg[0], soundorg[1]); } @@ -834,7 +907,7 @@ struct sector_t // [RH] The sky box to render for this sector. NULL means use a // regular sky. - TObjPtr SkyBoxes[2]; + TObjPtr SkyBoxes[2]; int PortalGroup; int sectornum; // for comparing sector copies @@ -1015,11 +1088,26 @@ struct line_t unsigned portalindex; // returns true if the portal is crossable by actors - bool isLinePortal() const; + bool isLinePortal() const + { + return portalindex >= linePortals.Size() ? false : !!(linePortals[portalindex].mFlags & PORTF_PASSABLE); + } + // returns true if the portal needs to be handled by the renderer - bool isVisualPortal() const; - line_t *getPortalDestination() const; - int getPortalAlignment() const; + bool isVisualPortal() const + { + return portalindex >= linePortals.Size() ? false : !!(linePortals[portalindex].mFlags & PORTF_VISIBLE); + } + + line_t *getPortalDestination() const + { + return portalindex >= linePortals.Size() ? (line_t*)NULL : linePortals[portalindex].mDestination; + } + + int getPortalAlignment() const + { + return portalindex >= linePortals.Size() ? 0 : linePortals[portalindex].mAlign; + } }; // phares 3/14/98 @@ -1165,5 +1253,24 @@ inline sector_t *P_PointInSector(fixed_t x, fixed_t y) return P_PointInSubsector(x, y)->sector; } +inline fixedvec3 AActor::PosRelative(AActor *other) const +{ + return __pos + Displacements(Sector->PortalGroup, other->Sector->PortalGroup); +} + +inline fixedvec3 AActor::PosRelative(sector_t *sec) const +{ + return __pos + Displacements(Sector->PortalGroup, sec->PortalGroup); +} + +inline fixedvec3 AActor::PosRelative(line_t *line) const +{ + return __pos + Displacements(Sector->PortalGroup, line->frontsector->PortalGroup); +} + +inline fixedvec3 PosRelative(const fixedvec3 &pos, line_t *line, sector_t *refsec = NULL) +{ + return pos + Displacements(refsec->PortalGroup, line->frontsector->PortalGroup); +} #endif diff --git a/src/r_main.cpp b/src/r_main.cpp index 90b8d865bd..02a3349a3d 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -58,7 +58,6 @@ #include "v_font.h" #include "r_data/colormaps.h" #include "farchive.h" -#include "portal.h" // MACROS ------------------------------------------------------------------ diff --git a/src/r_plane.cpp b/src/r_plane.cpp index f1deceb9fa..862eed3fbb 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -58,7 +58,6 @@ #include "r_3dfloors.h" #include "v_palette.h" #include "r_data/colormaps.h" -#include "portal.h" #ifdef _MSC_VER #pragma warning(disable:4244) diff --git a/src/r_segs.cpp b/src/r_segs.cpp index 96a0595d7b..ae38b39785 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -52,7 +52,6 @@ #include "r_3dfloors.h" #include "v_palette.h" #include "r_data/colormaps.h" -#include "portal.h" #define WALLYREPEAT 8 diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 45760a5ce1..10a39ba57a 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -34,7 +34,6 @@ #include "doomstat.h" #include "m_random.h" #include "m_bbox.h" -#include "portal.h" #include "r_sky.h" #include "st_stuff.h" #include "c_cvars.h" @@ -57,7 +56,7 @@ #include "farchive.h" #include "r_utility.h" #include "d_player.h" -#include "portal.h" +#include "p_local.h" // EXTERNAL DATA DECLARATIONS ----------------------------------------------