diff --git a/src/actionspecials.h b/src/actionspecials.h index 07a177bfa8..6d9d2feb8f 100644 --- a/src/actionspecials.h +++ b/src/actionspecials.h @@ -140,7 +140,7 @@ DEFINE_SPECIAL(Sector_ChangeSound, 140, 2, 2, 2) DEFINE_SPECIAL(Teleport_NoStop, 154, 2, 3, 3) // portal specials -DEFINE_SPECIAL(Line_SetPortal, 156, -1, -1, 3) +DEFINE_SPECIAL(Line_SetPortal, 156, -1, -1, 4) // GZDoom/Vavoom specials // Although ZDoom doesn't support them it's better to have them defined so that // WADs using them somewhere can at least be started without aborting due diff --git a/src/actor.h b/src/actor.h index 2da2c44cd0..9e9d71237a 100644 --- a/src/actor.h +++ b/src/actor.h @@ -743,7 +743,7 @@ public: inline bool IsNoClip2() const; void CheckPortalTransition(bool islinked); - fixedvec3 GetPortalTransition(fixed_t byoffset); + fixedvec3 GetPortalTransition(fixed_t byoffset, sector_t **pSec = NULL); // What species am I? virtual FName GetSpecies(); @@ -813,12 +813,6 @@ public: return bloodcls; } - bool intersects(AActor *other) const - { - fixed_t blockdist = radius + other->radius; - return ( abs(X() - other->X()) < blockdist && abs(Y() - other->Y()) < blockdist); - } - fixed_t AproxDistance(fixed_t otherx, fixed_t othery) { return P_AproxDistance(X() - otherx, Y() - othery); @@ -829,6 +823,11 @@ public: return R_PointToAngle2(X(), Y(), otherx, othery); } + fixed_t AngleTo(fixedvec2 other) + { + return R_PointToAngle2(X(), Y(), other.x, other.y); + } + // 'absolute' is reserved for a linked portal implementation which needs // to distinguish between portal-aware and portal-unaware distance calculation. fixed_t AproxDistance(AActor *other, bool absolute = false) @@ -1218,6 +1217,7 @@ public: return __pos; } + fixedvec3 PosRelative(int grp) const; fixedvec3 PosRelative(const AActor *other) const; fixedvec3 PosRelative(sector_t *sec) const; fixedvec3 PosRelative(line_t *line) const; diff --git a/src/basictypes.h b/src/basictypes.h index 356e197dd1..6c71fce9b5 100644 --- a/src/basictypes.h +++ b/src/basictypes.h @@ -112,30 +112,56 @@ struct fixedvec3 return *this; } + fixedvec3 &operator -=(const fixedvec2 &other) + { + x -= other.x; + y -= other.y; + return *this; + } + + fixedvec3 &operator -=(const fixedvec3 &other) + { + x -= other.x; + y -= other.y; + z -= other.z; + return *this; + } + operator fixedvec2() { - fixedvec2 ret = { x, y }; - return ret; + return { x, y }; } }; inline fixedvec2 operator +(const fixedvec2 &v1, const fixedvec2 &v2) { - fixedvec2 v = { v1.x + v2.x, v1.y + v2.y }; - return v; + return { v1.x + v2.x, v1.y + v2.y }; +} + +inline fixedvec2 operator -(const fixedvec2 &v1, const fixedvec2 &v2) +{ + return { v1.x - v2.x, v1.y - v2.y }; } 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; + return { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; } inline fixedvec3 operator +(const fixedvec3 &v1, const fixedvec2 &v2) { - fixedvec3 v = { v1.x + v2.x, v1.y + v2.y, v1.z }; - return v; + return { v1.x + v2.x, v1.y + v2.y, v1.z }; +} + +inline fixedvec3 operator -(const fixedvec3 &v1, const fixedvec2 &v2) +{ + return{ v1.x - v2.x, v1.y - v2.y, v1.z }; +} + +inline fixedvec3 operator -(const fixedvec3 &v1, const fixedvec3 &v2) +{ + return{ v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; } #define FIXED_MAX (signed)(0x7fffffff) diff --git a/src/d_main.cpp b/src/d_main.cpp index 9c0007bc34..accb6c2478 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -205,6 +205,7 @@ CUSTOM_CVAR (String, vid_cursor, "None", CVAR_ARCHIVE | CVAR_NOINITCALL) } } +bool wantToRestart; bool DrawFSHUD; // [RH] Draw fullscreen HUD? TArray allwads; bool devparm; // started game with -devparm @@ -1012,6 +1013,11 @@ void D_DoomLoop () I_StartTic (); D_Display (); S_UpdateMusic(); // OpenAL needs this to keep the music running, thanks to a complete lack of a sane streaming implementation using callbacks. :( + if (wantToRestart) + { + wantToRestart = false; + return; + } } catch (CRecoverableError &error) { @@ -2635,59 +2641,58 @@ void D_DoomMain (void) setmodeneeded = false; // This may be set to true here, but isn't needed for a restart } - try + D_DoomLoop (); // this only returns if a 'restart' CCMD is given. + + // + // Clean up after a restart + // + + // Music and sound should be stopped first + S_StopMusic(true); + S_StopAllChannels (); + + M_ClearMenus(); // close menu if open + F_EndFinale(); // If an intermission is active, end it now + + // clean up game state + ST_Clear(); + D_ErrorCleanup (); + P_FreeLevelData(); + P_FreeExtraLevelData(); + + M_SaveDefaults(NULL); // save config before the restart + + // delete all data that cannot be left until reinitialization + V_ClearFonts(); // must clear global font pointers + R_DeinitTranslationTables(); // some tables are initialized from outside the translation code. + gameinfo.~gameinfo_t(); + new (&gameinfo) gameinfo_t; // Reset gameinfo + S_Shutdown(); // free all channels and delete playlist + C_ClearAliases(); // CCMDs won't be reinitialized so these need to be deleted here + DestroyCVarsFlagged(CVAR_MOD); // Delete any cvar left by mods + + GC::FullGC(); // clean up before taking down the object list. + + // Delete the VM functions here. The garbage collector will not do this automatically because they are referenced from the global action function definitions. + FAutoSegIterator probe(ARegHead, ARegTail); + while (*++probe != NULL) { - D_DoomLoop (); // never returns + AFuncDesc *afunc = (AFuncDesc *)*probe; + *(afunc->VMPointer) = NULL; } - catch (CRestartException &) + + ReleaseGlobalSymbols(); + PClass::StaticShutdown(); + + GC::FullGC(); // perform one final garbage collection after shutdown + + for (DObject *obj = GC::Root; obj; obj = obj->ObjNext) { - // Music and sound should be stopped first - S_StopMusic(true); - S_StopAllChannels (); - - M_ClearMenus(); // close menu if open - F_EndFinale(); // If an intermission is active, end it now - - // clean up game state - ST_Clear(); - D_ErrorCleanup (); - P_FreeLevelData(); - P_FreeExtraLevelData(); - - M_SaveDefaults(NULL); // save config before the restart - - // delete all data that cannot be left until reinitialization - V_ClearFonts(); // must clear global font pointers - R_DeinitTranslationTables(); // some tables are initialized from outside the translation code. - gameinfo.~gameinfo_t(); - new (&gameinfo) gameinfo_t; // Reset gameinfo - S_Shutdown(); // free all channels and delete playlist - C_ClearAliases(); // CCMDs won't be reinitialized so these need to be deleted here - DestroyCVarsFlagged(CVAR_MOD); // Delete any cvar left by mods - - GC::FullGC(); // clean up before taking down the object list. - - // Delete the VM functions here. The garbage collector will not do this automatically because they are referenced from the global action function definitions. - FAutoSegIterator probe(ARegHead, ARegTail); - while (*++probe != NULL) - { - AFuncDesc *afunc = (AFuncDesc *)*probe; - *(afunc->VMPointer) = NULL; - } - - ReleaseGlobalSymbols(); - PClass::StaticShutdown(); - - GC::FullGC(); // perform one final garbage collection after shutdown - - for (DObject *obj = GC::Root; obj; obj = obj->ObjNext) - { - obj->ClearClass(); // Delete the Class pointer because the data it points to has been deleted. This will automatically be reset if needed. - } - - restart++; - PClass::bShutdown = false; + obj->ClearClass(); // Delete the Class pointer because the data it points to has been deleted. This will automatically be reset if needed. } + + restart++; + PClass::bShutdown = false; } while (1); } @@ -2715,14 +2720,13 @@ CCMD(restart) if (argv.argc() > 1) { - for(int i=1;iAppendArg(argv[i]); } } - // initiate the restart - throw CRestartException(); + wantToRestart = true; } //========================================================================== diff --git a/src/d_net.cpp b/src/d_net.cpp index faba5ef41b..4d8e33d3df 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -2382,9 +2382,7 @@ void Net_DoCommand (int type, BYTE **stream, int player) { if (trace.HitType == TRACE_HitWall) { - DImpactDecal::StaticCreate (s, - trace.X, trace.Y, trace.Z, - trace.Line->sidedef[trace.Side], NULL); + DImpactDecal::StaticCreate (s, trace.HitPos, trace.Line->sidedef[trace.Side], NULL); } } } diff --git a/src/doomdata.h b/src/doomdata.h index 469847eaaf..4d82547574 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -121,7 +121,7 @@ struct maplinedef2_t // LineDef attributes. // -enum ELineFlags +enum ELineFlags : unsigned { ML_BLOCKING =0x00000001, // solid, is an obstacle ML_BLOCKMONSTERS =0x00000002, // blocks monsters only @@ -163,6 +163,8 @@ enum ELineFlags ML_BLOCKSIGHT = 0x04000000, // blocks monster line of sight ML_BLOCKHITSCAN = 0x08000000, // blocks hitscan attacks ML_3DMIDTEX_IMPASS = 0x10000000, // [TP] if 3D midtex, behaves like a height-restricted ML_BLOCKING + + ML_PORTALCONNECT = 0x80000000, // for internal use only: This line connects to a sector with a linked portal (used to speed up sight checks.) }; diff --git a/src/g_doom/a_revenant.cpp b/src/g_doom/a_revenant.cpp index 1c034b372a..5b8ca293ce 100644 --- a/src/g_doom/a_revenant.cpp +++ b/src/g_doom/a_revenant.cpp @@ -64,7 +64,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Tracer) return 0; // spawn a puff of smoke behind the rocket - P_SpawnPuff (self, PClass::FindActor(NAME_BulletPuff), self->X(), self->Y(), self->Z(), 0, 3); + P_SpawnPuff (self, PClass::FindActor(NAME_BulletPuff), self->Pos(), self->angle, self->angle, 3); smoke = Spawn ("RevenantTracerSmoke", self->Vec3Offset(-self->velx, -self->vely, 0), ALLOW_REPLACE); diff --git a/src/g_hexen/a_spike.cpp b/src/g_hexen/a_spike.cpp index 0987d48d96..10444ecb91 100644 --- a/src/g_hexen/a_spike.cpp +++ b/src/g_hexen/a_spike.cpp @@ -153,24 +153,32 @@ DEFINE_ACTION_FUNCTION(AActor, A_ThrustImpale) { PARAM_ACTION_PROLOGUE; - AActor *thing; // This doesn't need to iterate through portals. - FBlockThingsIterator it(FBoundingBox(self->X(), self->Y(), self->radius)); - while ((thing = it.Next())) + + FPortalGroupArray check; + FMultiBlockThingsIterator it(check, self); + FMultiBlockThingsIterator::CheckResult cres; + while (it.Next(&cres)) { - if (!thing->intersects(self)) - { + fixed_t blockdist = self->radius + cres.thing->radius; + if (abs(self->X() - cres.position.x) >= blockdist || abs(self->Y() - cres.position.y) >= blockdist) continue; + + // Q: Make this z-aware for everything? It never was before. + if (cres.thing->Top() < self->Z() || cres.thing->Z() > self->Top()) + { + if (self->Sector->PortalGroup != cres.thing->Sector->PortalGroup) + continue; } - if (!(thing->flags & MF_SHOOTABLE) ) + if (!(cres.thing->flags & MF_SHOOTABLE) ) continue; - if (thing == self) + if (cres.thing == self) continue; // don't clip against self - int newdam = P_DamageMobj (thing, self, self, 10001, NAME_Crush); - P_TraceBleed (newdam > 0 ? newdam : 10001, thing); + int newdam = P_DamageMobj (cres.thing, self, self, 10001, NAME_Crush); + P_TraceBleed (newdam > 0 ? newdam : 10001, cres.thing); self->args[1] = 1; // Mark thrust thing as bloody } return 0; diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp index ef7b3eb8c9..d372508878 100644 --- a/src/g_shared/a_decals.cpp +++ b/src/g_shared/a_decals.cpp @@ -638,7 +638,7 @@ void DImpactDecal::CheckMax () } } -DImpactDecal *DImpactDecal::StaticCreate (const char *name, fixed_t x, fixed_t y, fixed_t z, side_t *wall, F3DFloor * ffloor, PalEntry color) +DImpactDecal *DImpactDecal::StaticCreate (const char *name, const fixedvec3 &pos, side_t *wall, F3DFloor * ffloor, PalEntry color) { if (cl_maxdecals > 0) { @@ -646,13 +646,13 @@ DImpactDecal *DImpactDecal::StaticCreate (const char *name, fixed_t x, fixed_t y if (tpl != NULL && (tpl = tpl->GetDecal()) != NULL) { - return StaticCreate (tpl, x, y, z, wall, ffloor, color); + return StaticCreate (tpl, pos, wall, ffloor, color); } } return NULL; } -DImpactDecal *DImpactDecal::StaticCreate (const FDecalTemplate *tpl, fixed_t x, fixed_t y, fixed_t z, side_t *wall, F3DFloor * ffloor, PalEntry color) +DImpactDecal *DImpactDecal::StaticCreate (const FDecalTemplate *tpl, const fixedvec3 &pos, side_t *wall, F3DFloor * ffloor, PalEntry color) { DImpactDecal *decal = NULL; if (tpl != NULL && cl_maxdecals > 0 && !(wall->Flags & WALLF_NOAUTODECALS)) @@ -666,16 +666,16 @@ DImpactDecal *DImpactDecal::StaticCreate (const FDecalTemplate *tpl, fixed_t x, // apply the custom color as well. if (tpl->ShadeColor != tpl_low->ShadeColor) lowercolor=0; else lowercolor = color; - StaticCreate (tpl_low, x, y, z, wall, ffloor, lowercolor); + StaticCreate (tpl_low, pos, wall, ffloor, lowercolor); } DImpactDecal::CheckMax(); - decal = new DImpactDecal (z); + decal = new DImpactDecal (pos.z); if (decal == NULL) { return NULL; } - if (!decal->StickToWall (wall, x, y, ffloor).isValid()) + if (!decal->StickToWall (wall, pos.x, pos.y, ffloor).isValid()) { return NULL; } @@ -692,7 +692,7 @@ DImpactDecal *DImpactDecal::StaticCreate (const FDecalTemplate *tpl, fixed_t x, } // Spread decal to nearby walls if it does not all fit on this one - decal->Spread (tpl, wall, x, y, z, ffloor); + decal->Spread (tpl, wall, pos.x, pos.y, pos.z, ffloor); } return decal; } @@ -779,22 +779,20 @@ DBaseDecal *ShootDecal(const FDecalTemplate *tpl, AActor *basisactor, sector_t * { if (permanent) { - decal = new DBaseDecal(trace.Z); + decal = new DBaseDecal(trace.HitPos.z); wall = trace.Line->sidedef[trace.Side]; - decal->StickToWall(wall, trace.X, trace.Y, trace.ffloor); + decal->StickToWall(wall, trace.HitPos.x, trace.HitPos.y, trace.ffloor); tpl->ApplyToDecal(decal, wall); // Spread decal to nearby walls if it does not all fit on this one if (cl_spreaddecals) { - decal->Spread(tpl, wall, trace.X, trace.Y, trace.Z, trace.ffloor); + decal->Spread(tpl, wall, trace.HitPos.x, trace.HitPos.y, trace.HitPos.z, trace.ffloor); } return decal; } else { - return DImpactDecal::StaticCreate(tpl, - trace.X, trace.Y, trace.Z, - trace.Line->sidedef[trace.Side], NULL); + return DImpactDecal::StaticCreate(tpl, trace.HitPos, trace.Line->sidedef[trace.Side], NULL); } } return NULL; diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index cfbe4526b4..1e59bf740d 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -63,8 +63,8 @@ public: DImpactDecal (fixed_t z); DImpactDecal (side_t *wall, const FDecalTemplate *templ); - static DImpactDecal *StaticCreate (const char *name, fixed_t x, fixed_t y, fixed_t z, side_t *wall, F3DFloor * ffloor, PalEntry color=0); - static DImpactDecal *StaticCreate (const FDecalTemplate *tpl, fixed_t x, fixed_t y, fixed_t z, side_t *wall, F3DFloor * ffloor, PalEntry color=0); + static DImpactDecal *StaticCreate (const char *name, const fixedvec3 &pos, side_t *wall, F3DFloor * ffloor, PalEntry color=0); + static DImpactDecal *StaticCreate (const FDecalTemplate *tpl, const fixedvec3 &pos, side_t *wall, F3DFloor * ffloor, PalEntry color=0); void BeginPlay (); void Destroy (); diff --git a/src/p_acs.cpp b/src/p_acs.cpp index b8976955b5..466d8793e1 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4546,7 +4546,7 @@ bool GetVarAddrType(AActor *self, FName varname, int index, void *&addr, PType * // unwrap contained type type = arraytype->ElementType; // offset by index (if in bounds) - if ((unsigned)index < arraytype->ElementCount) + if ((unsigned)index >= arraytype->ElementCount) { // out of bounds return false; } diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index bda71d4a15..7b4fb2118f 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -196,7 +196,6 @@ void DCeiling::Tick () { case ceilCrushAndRaise: case ceilLowerAndCrush: - case ceilLowerAndCrushDist: if (m_Speed1 == FRACUNIT && m_Speed2 == FRACUNIT) m_Speed = FRACUNIT / 8; break; @@ -259,16 +258,8 @@ DCeiling *DCeiling::Create(sector_t *sec, DCeiling::ECeiling type, line_t *line, case ceilCrushRaiseAndStay: ceiling->m_TopHeight = sec->ceilingplane.d; case ceilLowerAndCrush: - case ceilLowerAndCrushDist: targheight = sec->FindHighestFloorPoint (&spot); - if (type == ceilLowerAndCrush) - { - targheight += 8*FRACUNIT; - } - else if (type == ceilCrushAndRaise) - { - targheight += height; - } + targheight += height; ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); ceiling->m_Direction = -1; break; diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 030e854065..192ecd6faa 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -656,19 +656,19 @@ FUNC(LS_Ceiling_RaiseByValueTimes8) FUNC(LS_Ceiling_CrushAndRaise) // Ceiling_CrushAndRaise (tag, speed, crush, crushtype) { - return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg1)/2, 0, arg2, 0, 0, CRUSHTYPE(arg3)); + return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg1)/2, 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3)); } FUNC(LS_Ceiling_LowerAndCrush) // Ceiling_LowerAndCrush (tag, speed, crush, crushtype) { - return EV_DoCeiling (DCeiling::ceilLowerAndCrush, ln, arg0, SPEED(arg1), SPEED(arg1), 0, arg2, 0, 0, CRUSHTYPE(arg3)); + return EV_DoCeiling (DCeiling::ceilLowerAndCrush, ln, arg0, SPEED(arg1), SPEED(arg1), 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3)); } FUNC(LS_Ceiling_LowerAndCrushDist) // Ceiling_LowerAndCrush (tag, speed, crush, dist, crushtype) { - return EV_DoCeiling (DCeiling::ceilLowerAndCrushDist, ln, arg0, SPEED(arg1), SPEED(arg1), arg3*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg4)); + return EV_DoCeiling (DCeiling::ceilLowerAndCrush, ln, arg0, SPEED(arg1), SPEED(arg1), arg3*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg4)); } FUNC(LS_Ceiling_CrushStop) @@ -680,7 +680,7 @@ FUNC(LS_Ceiling_CrushStop) FUNC(LS_Ceiling_CrushRaiseAndStay) // Ceiling_CrushRaiseAndStay (tag, speed, crush, crushtype) { - return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg1)/2, 0, arg2, 0, 0, CRUSHTYPE(arg3)); + return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg1)/2, 8*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg3)); } FUNC(LS_Ceiling_MoveToValueTimes8) diff --git a/src/p_local.h b/src/p_local.h index 19315dedb4..abd759d2a3 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -141,18 +141,18 @@ enum EPuffFlags PF_NORANDOMZ = 16 }; -AActor *P_SpawnPuff (AActor *source, PClassActor *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags = 0, AActor *vict = NULL); -inline AActor *P_SpawnPuff(AActor *source, PClassActor *pufftype, const fixedvec3 &pos, angle_t dir, int updown, int flags = 0, AActor *vict = NULL) +AActor *P_SpawnPuff (AActor *source, PClassActor *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t hitdir, angle_t particledir, int updown, int flags = 0, AActor *vict = NULL); +inline AActor *P_SpawnPuff(AActor *source, PClassActor *pufftype, const fixedvec3 &pos, angle_t hitdir, angle_t particledir, int updown, int flags = 0, AActor *vict = NULL) { - return P_SpawnPuff(source, pufftype, pos.x, pos.y, pos.z, dir, updown, flags, vict); + return P_SpawnPuff(source, pufftype, pos.x, pos.y, pos.z, hitdir, particledir, updown, flags, vict); } void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AActor *originator); inline void P_SpawnBlood(const fixedvec3 &pos, angle_t dir, int damage, AActor *originator) { P_SpawnBlood(pos.x, pos.y, pos.z, dir, damage, originator); } -void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator); -void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator); +void P_BloodSplatter (fixedvec3 pos, AActor *originator); +void P_BloodSplatter2 (fixedvec3 pos, AActor *originator); void P_RipperBlood (AActor *mo, AActor *bleeder); int P_GetThingFloorType (AActor *thing); void P_ExplodeMissile (AActor *missile, line_t *explodeline, AActor *target); @@ -275,7 +275,7 @@ void P_PlayerStartStomp (AActor *actor, bool mononly=false); // [RH] Stomp on t void P_SlideMove (AActor* mo, fixed_t tryx, fixed_t tryy, int numsteps); bool P_BounceWall (AActor *mo); bool P_BounceActor (AActor *mo, AActor *BlockingMobj, bool ontop); -bool P_CheckSight (const AActor *t1, const AActor *t2, int flags=0); +bool P_CheckSight (AActor *t1, AActor *t2, int flags=0); enum ESightFlags { @@ -328,12 +328,20 @@ enum // P_LineAttack flags AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, PClassActor *pufftype, int flags = 0, FTranslatedLineTarget *victim = NULL, int *actualdamage = NULL); AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, FName pufftype, int flags = 0, FTranslatedLineTarget *victim = NULL, int *actualdamage = NULL); void P_TraceBleed (int damage, fixed_t x, fixed_t y, fixed_t z, AActor *target, angle_t angle, int pitch); +inline void P_TraceBleed(int damage, const fixedvec3 &pos, AActor *target, angle_t angle, int pitch) +{ + P_TraceBleed(damage, pos.x, pos.y, pos.z, target, angle, pitch); +} void P_TraceBleed (int damage, AActor *target, angle_t angle, int pitch); void P_TraceBleed (int damage, AActor *target, AActor *missile); // missile version void P_TraceBleed(int damage, FTranslatedLineTarget *t, AActor *puff); // hitscan version void P_TraceBleed (int damage, AActor *target); // random direction version bool P_HitFloor (AActor *thing); bool P_HitWater (AActor *thing, sector_t *sec, fixed_t splashx = FIXED_MIN, fixed_t splashy = FIXED_MIN, fixed_t splashz=FIXED_MIN, bool checkabove = false, bool alert = true, bool force = false); +inline bool P_HitWater(AActor *thing, sector_t *sec, const fixedvec3 &pos, bool checkabove = false, bool alert = true, bool force = false) +{ + return P_HitWater(thing, sec, pos.x, pos.y, pos.z, checkabove, alert, force); +} void P_CheckSplash(AActor *self, fixed_t distance); void P_RailAttack (AActor *source, int damage, int offset_xy, fixed_t offset_z = 0, int color1 = 0, int color2 = 0, double maxdiff = 0, int flags = 0, PClassActor *puff = NULL, angle_t angleoffset = 0, angle_t pitchoffset = 0, fixed_t distance = 8192*FRACUNIT, int duration = 0, double sparsity = 1.0, double drift = 1.0, PClassActor *spawnclass = NULL, int SpiralOffset = 270); // [RH] Shoot a railgun @@ -351,7 +359,7 @@ bool P_CheckMissileSpawn (AActor *missile, fixed_t maxdist); void P_PlaySpawnSound(AActor *missile, AActor *spawner); // [RH] Position the chasecam -void P_AimCamera (AActor *t1, fixed_t &x, fixed_t &y, fixed_t &z, sector_t *&sec); +void P_AimCamera (AActor *t1, fixed_t &x, fixed_t &y, fixed_t &z, sector_t *&sec, bool &unlinked); // [RH] Means of death enum diff --git a/src/p_map.cpp b/src/p_map.cpp index 7894e73829..e3ab370d00 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -65,8 +65,7 @@ CVAR(Bool, cl_doautoaim, false, CVAR_ARCHIVE) 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); +static void SpawnDeepSplash(AActor *t1, const FTraceResults &trace, AActor *puff); static FRandom pr_tracebleed("TraceBleed"); static FRandom pr_checkthing("CheckThing"); @@ -245,7 +244,7 @@ static bool PIT_FindFloorCeiling(FMultiBlockLinesIterator &mit, FMultiBlockLines if (open.top < tmf.ceilingz) { tmf.ceilingz = open.top; - if (open.topsec != NULL) tmf.floorsector = open.topsec; + if (open.topsec != NULL) tmf.ceilingsector = open.topsec; if (ffcf_verbose) Printf(" Adjust ceilingz to %f\n", FIXED2FLOAT(open.top)); mit.StopUp(); } @@ -1527,7 +1526,7 @@ bool PIT_CheckThing(FMultiBlockThingsIterator &it, FMultiBlockThingsIterator::Ch !(tm.thing->flags3 & MF3_BLOODLESSIMPACT) && (pr_checkthing() < 192)) { - P_BloodSplatter(tm.thing->X(), tm.thing->Y(), tm.thing->Z(), thing); + P_BloodSplatter(tm.thing->Pos(), thing); } if (!(tm.thing->flags3 & MF3_BLOODLESSIMPACT)) { @@ -3667,7 +3666,7 @@ struct aim_t void EnterSectorPortal(int position, fixed_t frac, sector_t *entersec, fixed_t newtoppitch, fixed_t newbottompitch) { AActor *portal = entersec->SkyBoxes[position]; - if (portal == NULL) + if (position == sector_t::ceiling && portal->threshold < limitz) return; else if (position == sector_t::floor && portal->threshold > limitz) return; aim_t newtrace = Clone(); @@ -3779,7 +3778,7 @@ struct aim_t if (aimdebug) Printf("Start AimTraverse, start = %f,%f,%f, vect = %f,%f,%f\n", - startpos.x / 65536., startpos.y / 65536., startpos.y / 65536., + startpos.x / 65536., startpos.y / 65536., startpos.z / 65536., aimtrace.x / 65536., aimtrace.y / 65536.); while ((in = it.Next())) @@ -4102,7 +4101,7 @@ fixed_t P_AimLineAttack(AActor *t1, angle_t angle, fixed_t distance, FTranslated //========================================================================== // -// +// Helper stuff for P_LineAttack // //========================================================================== @@ -4231,7 +4230,7 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, } if (puffDefaults != NULL && puffDefaults->flags3 & MF3_ALWAYSPUFF) { // Spawn the puff anyway - puff = P_SpawnPuff(t1, pufftype, trace.X, trace.Y, trace.Z, angle - ANG180, 2, puffFlags); + puff = P_SpawnPuff(t1, pufftype, trace.HitPos, trace.SrcAngleToTarget, trace.SrcAngleToTarget, 2, puffFlags); } else { @@ -4240,21 +4239,19 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, } else { - fixed_t hitx = 0, hity = 0, hitz = 0; - if (trace.HitType != TRACE_HitActor) { // position a bit closer for puffs if (trace.HitType != TRACE_HitWall || trace.Line->special != Line_Horizon) { - fixed_t closer = trace.Distance - 4 * FRACUNIT; - fixedvec2 pos = t1->Vec2Offset(FixedMul(vx, closer), FixedMul(vy, closer)); - puff = P_SpawnPuff(t1, pufftype, pos.x, pos.y, - shootz + FixedMul(vz, closer), angle - ANG90, 0, puffFlags); + fixedvec2 pos = P_GetOffsetPosition(trace.HitPos.x, trace.HitPos.y, -trace.HitVector.x * 4, -trace.HitVector.y * 4); + puff = P_SpawnPuff(t1, pufftype, pos.x, pos.y, trace.HitPos.z - trace.HitVector.z * 4, trace.SrcAngleToTarget, + trace.SrcAngleToTarget - ANGLE_90, 0, puffFlags); + puff->radius = 1; } // [RH] Spawn a decal - if (trace.HitType == TRACE_HitWall && trace.Line->special != Line_Horizon && !(flags & LAF_NOIMPACTDECAL) && !(puffDefaults->flags7 & MF7_NODECAL)) + if (trace.HitType == TRACE_HitWall && trace.Line->special != Line_Horizon && !trace.Line->isVisualPortal() && !(flags & LAF_NOIMPACTDECAL) && !(puffDefaults->flags7 & MF7_NODECAL)) { // [TN] If the actor or weapon has a decal defined, use that one. if (t1->DecalGenerator != NULL || @@ -4283,7 +4280,7 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, trace.Sector->heightsec == NULL && trace.HitType == TRACE_HitFloor) { - P_HitWater(puff, trace.Sector, trace.X, trace.Y, trace.Z); + P_HitWater(puff, trace.Sector, trace.HitPos); } } else @@ -4297,14 +4294,15 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, (t1->player->ReadyWeapon->WeaponFlags & WIF_AXEBLOOD)); // Hit a thing, so it could be either a puff or blood - fixed_t dist = trace.Distance; + fixedvec3 bleedpos = trace.HitPos; // position a bit closer for puffs/blood if using compatibility mode. - if (i_compatflags & COMPATF_HITSCAN) dist -= 10 * FRACUNIT; - hitx = t1->X() + FixedMul(vx, dist); - hity = t1->Y() + FixedMul(vy, dist); - hitz = shootz + FixedMul(vz, dist); - - + if (i_compatflags & COMPATF_HITSCAN) + { + fixedvec2 ofs = P_GetOffsetPosition(bleedpos.x, bleedpos.y, -10 * trace.HitVector.x, -10 * trace.HitVector.y); + bleedpos.x = ofs.x; + bleedpos.y = ofs.y; + bleedpos.z -= -10 * trace.HitVector.z; + } // Spawn bullet puffs or blood spots, depending on target type. if ((puffDefaults != NULL && puffDefaults->flags3 & MF3_PUFFONACTORS) || @@ -4315,7 +4313,7 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, puffFlags |= PF_HITTHINGBLEED; // We must pass the unreplaced puff type here - puff = P_SpawnPuff(t1, pufftype, hitx, hity, hitz, angle - ANG180, 2, puffFlags | PF_HITTHING, trace.Actor); + puff = P_SpawnPuff(t1, pufftype, bleedpos, trace.SrcAngleToTarget, trace.SrcAngleToTarget - ANGLE_90, 2, puffFlags | PF_HITTHING, trace.Actor); } // Allow puffs to inflict poison damage, so that hitscans can poison, too. @@ -4341,11 +4339,10 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, { // Since the puff is the damage inflictor we need it here // regardless of whether it is displayed or not. - puff = P_SpawnPuff(t1, pufftype, hitx, hity, hitz, angle - ANG180, 2, puffFlags | PF_HITTHING | PF_TEMPORARY); + puff = P_SpawnPuff(t1, pufftype, bleedpos, 0, 0, 2, puffFlags | PF_HITTHING | PF_TEMPORARY); killPuff = true; } -#pragma message("damage angle") - newdam = P_DamageMobj(trace.Actor, puff ? puff : t1, t1, damage, damageType, dmgflags); + newdam = P_DamageMobj(trace.Actor, puff ? puff : t1, t1, damage, damageType, dmgflags|DMG_USEANGLE, trace.SrcAngleToTarget); if (actualdamage != NULL) { *actualdamage = newdam; @@ -4357,7 +4354,7 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, !(trace.Actor->flags & MF_NOBLOOD) && !(trace.Actor->flags2 & (MF2_INVULNERABLE | MF2_DORMANT))) { - P_SpawnBlood(hitx, hity, hitz, angle - ANG180, newdam > 0 ? newdam : damage, trace.Actor); + P_SpawnBlood(bleedpos, trace.SrcAngleToTarget, newdam > 0 ? newdam : damage, trace.Actor); } if (damage) @@ -4369,35 +4366,34 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, { if (axeBlood) { - P_BloodSplatter2(hitx, hity, hitz, trace.Actor); + P_BloodSplatter2(bleedpos, trace.Actor); } if (pr_lineattack() < 192) { - P_BloodSplatter(hitx, hity, hitz, trace.Actor); + P_BloodSplatter(bleedpos, trace.Actor); } } } // [RH] Stick blood to walls - P_TraceBleed(newdam > 0 ? newdam : damage, trace.X, trace.Y, trace.Z, - trace.Actor, srcangle, srcpitch); + P_TraceBleed(newdam > 0 ? newdam : damage, trace.HitPos, + trace.Actor, trace.SrcAngleToTarget, srcpitch); } } if (victim != NULL) { victim->linetarget = trace.Actor; - victim->angleFromSource = R_PointToAngle2(t1->X(), t1->Y(), trace.Actor->X(), trace.Actor->Y()); - victim->unlinked = false; + victim->angleFromSource = trace.SrcAngleToTarget; + victim->unlinked = trace.unlinked; } } if (trace.Crossed3DWater || trace.CrossedWater) { - if (puff == NULL) { // Spawn puff just to get a mass for the splash - puff = P_SpawnPuff(t1, pufftype, hitx, hity, hitz, angle - ANG180, 2, puffFlags | PF_HITTHING | PF_TEMPORARY); + puff = P_SpawnPuff(t1, pufftype, trace.HitPos, 0, 0, 2, puffFlags | PF_HITTHING | PF_TEMPORARY); killPuff = true; } - SpawnDeepSplash(t1, trace, puff, vx, vy, vz, shootz, trace.Crossed3DWater != NULL); + SpawnDeepSplash(t1, trace, puff); } } if (killPuff && puff != NULL) @@ -4553,11 +4549,8 @@ void P_TraceBleed(int damage, fixed_t x, fixed_t y, fixed_t z, AActor *actor, an bloodcolor.a = 1; } - DImpactDecal::StaticCreate(bloodType, - bleedtrace.X, bleedtrace.Y, bleedtrace.Z, - bleedtrace.Line->sidedef[bleedtrace.Side], - bleedtrace.ffloor, - bloodcolor); + DImpactDecal::StaticCreate(bloodType, bleedtrace.HitPos, + bleedtrace.Line->sidedef[bleedtrace.Side], bleedtrace.ffloor, bloodcolor); } } } @@ -4645,7 +4638,8 @@ void P_TraceBleed(int damage, AActor *target) struct SRailHit { AActor *HitActor; - fixed_t Distance; + fixedvec3 HitPos; + angle_t HitAngle; }; struct RailData { @@ -4679,7 +4673,15 @@ static ETraceStatus ProcessRailHit(FTraceResults &res, void *userdata) // Save this thing for damaging later, and continue the trace SRailHit newhit; newhit.HitActor = res.Actor; - newhit.Distance = res.Distance - 10 * FRACUNIT; // put blood in front + newhit.HitPos = res.HitPos; + newhit.HitAngle = res.SrcAngleToTarget; + if (i_compatflags & COMPATF_HITSCAN) + { + fixedvec2 ofs = P_GetOffsetPosition(newhit.HitPos.x, newhit.HitPos.y, -10 * res.HitVector.x, -10 * res.HitVector.y); + newhit.HitPos.x = ofs.x; + newhit.HitPos.y = ofs.y; + newhit.HitPos.z -= -10 * res.HitVector.z; + } data->RailHits.Push(newhit); return data->StopAtOne ? TRACE_Stop : TRACE_Continue; @@ -4761,17 +4763,13 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i { - fixed_t x, y, z; bool spawnpuff; bool bleed = false; int puffflags = PF_HITTHING; AActor *hitactor = rail_data.RailHits[i].HitActor; - fixed_t hitdist = rail_data.RailHits[i].Distance; - - x = xy.x + FixedMul(hitdist, vx); - y = xy.y + FixedMul(hitdist, vy); - z = shootz + FixedMul(hitdist, vz); + fixedvec3 &hitpos = rail_data.RailHits[i].HitPos; + angle_t hitangle = rail_data.RailHits[i].HitAngle; if ((hitactor->flags & MF_NOBLOOD) || (hitactor->flags2 & MF2_DORMANT || ((hitactor->flags2 & MF2_INVULNERABLE) && !(puffDefaults->flags3 & MF3_FOILINVUL)))) @@ -4789,7 +4787,7 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i } if (spawnpuff) { - P_SpawnPuff(source, puffclass, x, y, z, (source->angle + angleoffset) - ANG90, 1, puffflags, hitactor); + P_SpawnPuff(source, puffclass, hitpos, trace.SrcAngleToTarget, trace.SrcAngleToTarget - ANGLE_90, 1, puffflags, hitactor); } int dmgFlagPass = DMG_INFLICTOR_IS_PUFF; @@ -4802,13 +4800,12 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i if (puffDefaults->flags3 & MF3_FOILINVUL) dmgFlagPass |= DMG_FOILINVUL; if (puffDefaults->flags7 & MF7_FOILBUDDHA) dmgFlagPass |= DMG_FOILBUDDHA; } -#pragma message("damage angle") - int newdam = P_DamageMobj(hitactor, thepuff ? thepuff : source, source, damage, damagetype, dmgFlagPass); + int newdam = P_DamageMobj(hitactor, thepuff ? thepuff : source, source, damage, damagetype, dmgFlagPass|DMG_USEANGLE, hitangle); if (bleed) { - P_SpawnBlood(x, y, z, (source->angle + angleoffset) - ANG180, newdam > 0 ? newdam : damage, hitactor); - P_TraceBleed(newdam > 0 ? newdam : damage, x, y, z, hitactor, source->angle, pitch); + P_SpawnBlood(hitpos, hitangle, newdam > 0 ? newdam : damage, hitactor); + P_TraceBleed(newdam > 0 ? newdam : damage, hitpos, hitactor, source->angle, pitch); } } @@ -4819,7 +4816,7 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i if (puffclass != NULL && puffDefaults->flags3 & MF3_ALWAYSPUFF) { - puff = P_SpawnPuff(source, puffclass, trace.X, trace.Y, trace.Z, (source->angle + angleoffset) - ANG90, 1, 0); + puff = P_SpawnPuff(source, puffclass, trace.HitPos, trace.SrcAngleToTarget, trace.SrcAngleToTarget - ANGLE_90, 1, 0); if (puff && (trace.Line != NULL) && (trace.Line->special == Line_Horizon) && !(puff->flags3 & MF3_SKYEXPLODE)) puff->Destroy(); } @@ -4834,7 +4831,7 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i AActor* puff = NULL; if (puffclass != NULL && puffDefaults->flags3 & MF3_ALWAYSPUFF) { - puff = P_SpawnPuff(source, puffclass, trace.X, trace.Y, trace.Z, (source->angle + angleoffset) - ANG90, 1, 0); + puff = P_SpawnPuff(source, puffclass, trace.HitPos, trace.SrcAngleToTarget, trace.SrcAngleToTarget - ANGLE_90, 1, 0); if (puff && !(puff->flags3 & MF3_SKYEXPLODE) && (((trace.HitType == TRACE_HitFloor) && (puff->floorpic == skyflatnum)) || ((trace.HitType == TRACE_HitCeiling) && (puff->ceilingpic == skyflatnum)))) @@ -4845,23 +4842,21 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i } if (thepuff != NULL) { - if (trace.HitType == TRACE_HitFloor && - trace.CrossedWater == NULL && - trace.Sector->heightsec == NULL) - { - P_HitWater(thepuff, trace.Sector, trace.X, trace.Y, trace.Z); - } if (trace.Crossed3DWater || trace.CrossedWater) { - SpawnDeepSplash(source, trace, thepuff, vx, vy, vz, shootz, trace.Crossed3DWater != NULL); + SpawnDeepSplash(source, trace, thepuff); + } + else if (trace.HitType == TRACE_HitFloor && trace.Sector->heightsec == NULL) + { + P_HitWater(thepuff, trace.Sector, trace.HitPos); } thepuff->Destroy(); } // Draw the slug's trail. - end.X = FIXED2DBL(trace.X); - end.Y = FIXED2DBL(trace.Y); - end.Z = FIXED2DBL(trace.Z); + end.X = FIXED2DBL(trace.HitPos.x); + end.Y = FIXED2DBL(trace.HitPos.y); + end.Z = FIXED2DBL(trace.HitPos.z); P_DrawRailTrail(source, start, end, color1, color2, maxdiff, railflags, spawnclass, source->angle + angleoffset, duration, sparsity, drift, SpiralOffset); } @@ -4874,7 +4869,7 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i CVAR(Float, chase_height, -8.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Float, chase_dist, 90.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -void P_AimCamera(AActor *t1, fixed_t &CameraX, fixed_t &CameraY, fixed_t &CameraZ, sector_t *&CameraSector) +void P_AimCamera(AActor *t1, fixed_t &CameraX, fixed_t &CameraY, fixed_t &CameraZ, sector_t *&CameraSector, bool &unlinked) { fixed_t distance = (fixed_t)(clamp(chase_dist, 0, 30000) * FRACUNIT); angle_t angle = (t1->angle - ANG180) >> ANGLETOFINESHIFT; @@ -4900,11 +4895,12 @@ void P_AimCamera(AActor *t1, fixed_t &CameraX, fixed_t &CameraY, fixed_t &Camera } else { - CameraX = trace.X; - CameraY = trace.Y; - CameraZ = trace.Z; + CameraX = trace.HitPos.x; + CameraY = trace.HitPos.y; + CameraZ = trace.HitPos.z; } CameraSector = trace.Sector; + unlinked = trace.unlinked; } @@ -5249,20 +5245,23 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo if (bombdistance <= 0) return; fulldamagedistance = clamp(fulldamagedistance, 0, bombdistance - 1); + fixed_t bombdistfix = bombdistance << FRACBITS; double bombdistancefloat = 1.f / (double)(bombdistance - fulldamagedistance); double bombdamagefloat = (double)bombdamage; - FBlockThingsIterator it(FBoundingBox(bombspot->X(), bombspot->Y(), bombdistance << FRACBITS)); - AActor *thing; + FPortalGroupArray grouplist; + FMultiBlockThingsIterator it(grouplist, bombspot->X(), bombspot->Y(), bombspot->Z() - bombdistfix, bombspot->height + bombdistfix*2, bombdistfix); + FMultiBlockThingsIterator::CheckResult cres; if (flags & RADF_SOURCEISSPOT) { // The source is actually the same as the spot, even if that wasn't what we received. bombsource = bombspot; } - while ((thing = it.Next())) + while ((it.Next(&cres))) { + AActor *thing = cres.thing; // Vulnerable actors can be damaged by radius attacks even if not shootable // Used to emulate MBF's vulnerability of non-missile bouncers to explosions. if (!((thing->flags & MF_SHOOTABLE) || (thing->flags6 & MF6_VULNERABLE))) @@ -5521,14 +5520,16 @@ void P_FindAboveIntersectors(AActor *actor) if (!(actor->flags & MF_SOLID)) return; - AActor *thing; - FBlockThingsIterator it(FBoundingBox(actor->X(), actor->Y(), actor->radius)); - while ((thing = it.Next())) + FPortalGroupArray check; + FMultiBlockThingsIterator it(check, actor); + FMultiBlockThingsIterator::CheckResult cres; + while (it.Next(&cres)) { - if (!thing->intersects(actor)) - { + AActor *thing = cres.thing; + fixed_t blockdist = actor->radius + thing->radius; + if (abs(actor->X() - cres.position.x) >= blockdist || abs(actor->Y() - cres.position.y) >= blockdist) continue; - } + if (!(thing->flags & MF_SOLID)) { // Can't hit thing continue; @@ -5575,14 +5576,16 @@ void P_FindBelowIntersectors(AActor *actor) if (!(actor->flags & MF_SOLID)) return; - AActor *thing; - FBlockThingsIterator it(FBoundingBox(actor->X(), actor->Y(), actor->radius)); - while ((thing = it.Next())) + FPortalGroupArray check; + FMultiBlockThingsIterator it(check, actor); + FMultiBlockThingsIterator::CheckResult cres; + while (it.Next(&cres)) { - if (!thing->intersects(actor)) - { + AActor *thing = cres.thing; + fixed_t blockdist = actor->radius + thing->radius; + if (abs(actor->X() - cres.position.x) >= blockdist || abs(actor->Y() - cres.position.y) >= blockdist) continue; - } + if (!(thing->flags & MF_SOLID)) { // Can't hit thing continue; @@ -5726,6 +5729,7 @@ int P_PushUp(AActor *thing, FChangePosition *cpos) return 2; } } + thing->CheckPortalTransition(true); return 0; } @@ -5773,6 +5777,7 @@ int P_PushDown(AActor *thing, FChangePosition *cpos) } } } + thing->CheckPortalTransition(true); return 0; } @@ -6425,7 +6430,7 @@ void SpawnShootDecal(AActor *t1, const FTraceResults &trace) if (decalbase != NULL) { DImpactDecal::StaticCreate(decalbase->GetDecal(), - trace.X, trace.Y, trace.Z, trace.Line->sidedef[trace.Side], trace.ffloor); + trace.HitPos, trace.Line->sidedef[trace.Side], trace.ffloor); } } @@ -6435,31 +6440,20 @@ 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) +static void SpawnDeepSplash(AActor *t1, const FTraceResults &trace, AActor *puff) { - const secplane_t *plane; - if (ffloor && trace.Crossed3DWater) - plane = trace.Crossed3DWater->top.plane; + const fixedvec3 *hitpos; + if (trace.Crossed3DWater) + { + hitpos = &trace.Crossed3DWaterPos; + } else if (trace.CrossedWater && trace.CrossedWater->heightsec) - plane = &trace.CrossedWater->heightsec->floorplane; + { + hitpos = &trace.CrossedWaterPos; + } else return; - fixed_t num, den, hitdist; - den = TMulScale16(plane->a, vx, plane->b, vy, plane->c, vz); - if (den != 0) - { - num = TMulScale16(plane->a, t1->X(), plane->b, t1->Y(), plane->c, shootz) + plane->d; - hitdist = FixedDiv(-num, den); - - if (hitdist >= 0 && hitdist <= trace.Distance) - { - fixedvec2 hitpos = t1->Vec2Offset(FixedMul(vx, hitdist), FixedMul(vy, hitdist)); - fixed_t hitz = shootz + FixedMul(vz, hitdist); - - P_HitWater(puff != NULL ? puff : t1, P_PointInSector(hitpos.x, hitpos.y), hitpos.x, hitpos.y, hitz); - } - } + P_HitWater(puff != NULL ? puff : t1, P_PointInSector(hitpos->x, hitpos->y), *hitpos); } //============================================================================= diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index ffaa2b4e43..20fe996a55 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -162,7 +162,7 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, front = linedef->frontsector; back = linedef->backsector; - if (!(flags & FFCF_NOPORTALS)) + if (!(flags & FFCF_NOPORTALS) && (linedef->flags & ML_PORTALCONNECT)) { if (!linedef->frontsector->PortalBlocksMovement(sector_t::ceiling)) fc = FIXED_MAX; if (!linedef->backsector->PortalBlocksMovement(sector_t::ceiling)) bc = FIXED_MAX; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 70ea6a0b9b..a9e23c2a8c 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1444,8 +1444,7 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) } } - DImpactDecal::StaticCreate (base->GetDecal (), - x, y, z, line->sidedef[side], ffloor); + DImpactDecal::StaticCreate(base->GetDecal(), { x, y, z }, line->sidedef[side], ffloor); } } } @@ -3286,7 +3285,7 @@ void AActor::SetRoll(angle_t r, bool interpolate) } -fixedvec3 AActor::GetPortalTransition(fixed_t byoffset) +fixedvec3 AActor::GetPortalTransition(fixed_t byoffset, sector_t **pSec) { bool moved = false; sector_t *sec = Sector; @@ -3317,6 +3316,7 @@ fixedvec3 AActor::GetPortalTransition(fixed_t byoffset) else break; } } + if (pSec) *pSec = sec; return pos; } @@ -5021,7 +5021,29 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) // save spots for respawning in network games FPlayerStart start(mthing, pnum+1); playerstarts[pnum] = start; - AllPlayerStarts.Push(start); + if (level.flags2 & LEVEL2_RANDOMPLAYERSTARTS) + { // When using random player starts, all starts count + AllPlayerStarts.Push(start); + } + else + { // When not using random player starts, later single player + // starts should override earlier ones, since the earlier + // ones are for voodoo dolls and not likely to be ideal for + // spawning regular players. + unsigned i; + for (i = 0; i < AllPlayerStarts.Size(); ++i) + { + if (AllPlayerStarts[i].type == pnum+1) + { + AllPlayerStarts[i] = start; + break; + } + } + if (i == AllPlayerStarts.Size()) + { + AllPlayerStarts.Push(start); + } + } if (!deathmatch && !(level.flags2 & LEVEL2_RANDOMPLAYERSTARTS)) { return P_SpawnPlayer(&start, pnum, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0); @@ -5228,7 +5250,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) // P_SpawnPuff // -AActor *P_SpawnPuff (AActor *source, PClassActor *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags, AActor *vict) +AActor *P_SpawnPuff (AActor *source, PClassActor *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t hitdir, angle_t particledir, int updown, int flags, AActor *vict) { AActor *puff; @@ -5256,8 +5278,8 @@ AActor *P_SpawnPuff (AActor *source, PClassActor *pufftype, fixed_t x, fixed_t y if ( puff && (puff->flags5 & MF5_PUFFGETSOWNER)) puff->target = source; - - if (source != NULL) puff->angle = puff->AngleTo(source); + // Angle is the opposite of the hit direction (i.e. the puff faces the source.) + puff->angle = hitdir + ANGLE_180; // If a puff has a crash state and an actor was not hit, // it will enter the crash state. This is used by the StrifeSpark @@ -5282,7 +5304,7 @@ AActor *P_SpawnPuff (AActor *source, PClassActor *pufftype, fixed_t x, fixed_t y { if (cl_pufftype && updown != 3 && (puff->flags4 & MF4_ALLOWPARTICLES)) { - P_DrawSplash2 (32, x, y, z, dir, updown, 1); + P_DrawSplash2 (32, x, y, z, particledir, updown, 1); puff->renderflags |= RF_INVISIBLE; } @@ -5402,7 +5424,7 @@ void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AAc // //--------------------------------------------------------------------------- -void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator) +void P_BloodSplatter (fixedvec3 pos, AActor *originator) { PalEntry bloodcolor = originator->GetBloodColor(); PClassActor *bloodcls = originator->GetBloodType(1); @@ -5416,7 +5438,7 @@ void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator) { AActor *mo; - mo = Spawn(bloodcls, x, y, z, NO_REPLACE); // GetBloodType already performed the replacement + mo = Spawn(bloodcls, pos, NO_REPLACE); // GetBloodType already performed the replacement mo->target = originator; mo->velx = pr_splatter.Random2 () << 10; mo->vely = pr_splatter.Random2 () << 10; @@ -5432,7 +5454,7 @@ void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator) } if (bloodtype >= 1) { - P_DrawSplash2 (40, x, y, z, 0u - originator->AngleTo(x, y), 2, bloodcolor); + P_DrawSplash2 (40, pos.x, pos.y, pos.z, 0u - originator->AngleTo(pos), 2, bloodcolor); } } @@ -5442,7 +5464,7 @@ void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator) // //=========================================================================== -void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator) +void P_BloodSplatter2 (fixedvec3 pos, AActor *originator) { PalEntry bloodcolor = originator->GetBloodColor(); PClassActor *bloodcls = originator->GetBloodType(2); @@ -5456,10 +5478,10 @@ void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator) { AActor *mo; - x += ((pr_splat()-128)<<11); - y += ((pr_splat()-128)<<11); + pos.x += ((pr_splat()-128)<<11); + pos.y += ((pr_splat()-128)<<11); - mo = Spawn (bloodcls, x, y, z, NO_REPLACE); // GetBloodType already performed the replacement + mo = Spawn (bloodcls, pos, NO_REPLACE); // GetBloodType already performed the replacement mo->target = originator; // colorize the blood! @@ -5472,7 +5494,7 @@ void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator) } if (bloodtype >= 1) { - P_DrawSplash2 (100, x, y, z, 0u - originator->AngleTo(x, y), 2, bloodcolor); + P_DrawSplash2 (100, pos.x, pos.y, pos.z, 0u - originator->AngleTo(pos), 2, bloodcolor); } } @@ -5733,7 +5755,7 @@ bool P_HitFloor (AActor *thing) { if (rover->top.plane->ZatPoint(pos.x, pos.y) == thing->Z()) { - return P_HitWater (thing, m->m_sector, pos.x, pos.y, pos.z); + return P_HitWater (thing, m->m_sector, pos); } } } @@ -5743,7 +5765,7 @@ bool P_HitFloor (AActor *thing) return false; } - return P_HitWater (thing, m->m_sector, pos.x, pos.y, pos.z); + return P_HitWater (thing, m->m_sector, pos); } //--------------------------------------------------------------------------- diff --git a/src/p_sight.cpp b/src/p_sight.cpp index 0b1afe4306..7669ac8880 100644 --- a/src/p_sight.cpp +++ b/src/p_sight.cpp @@ -48,43 +48,140 @@ static int sightcounts[6]; static cycle_t SightCycles; static cycle_t MaxSightCycles; +enum +{ + SO_TOPFRONT = 1, + SO_TOPBACK = 2, + SO_BOTTOMFRONT = 4, + SO_BOTTOMBACK = 8, + +}; + +struct SightOpening +{ + fixed_t top; + fixed_t bottom; + int range; + int portalflags; + + void SwapSides() + { + portalflags = ((portalflags & (SO_TOPFRONT | SO_BOTTOMFRONT)) << 1) | ((portalflags & (SO_TOPBACK | SO_BOTTOMBACK)) >> 1); + } +}; + +struct SightTask +{ + fixed_t frac; + fixed_t topslope; + fixed_t bottomslope; + int direction; + int portalgroup; +}; + + static TArray intercepts (128); +static TArray portals(32); class SightCheck { - fixed_t sightzstart; // eye z of looker - const AActor * sightthing; - const AActor * seeingthing; + fixedvec3 sightstart; + fixedvec2 sightend; + fixed_t startfrac; + + AActor * seeingthing; fixed_t lastztop; // z at last line fixed_t lastzbottom; // z at last line sector_t * lastsector; // last sector being entered by trace fixed_t topslope, bottomslope; // slopes to top and bottom of target int Flags; divline_t trace; + int portaldir; + int portalgroup; + bool portalfound; unsigned int myseethrough; + void P_SightOpening(SightOpening &open, const line_t *linedef, fixed_t x, fixed_t y); bool PTR_SightTraverse (intercept_t *in); bool P_SightCheckLine (line_t *ld); - bool P_SightBlockLinesIterator (int x, int y); + int P_SightBlockLinesIterator (int x, int y); bool P_SightTraverseIntercepts (); public: - bool P_SightPathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2); + bool P_SightPathTraverse (); - SightCheck(const AActor * t1, const AActor * t2, int flags) + void init(AActor * t1, AActor * t2, sector_t *startsector, SightTask *task, int flags) { - lastztop = lastzbottom = sightzstart = t1->Z() + t1->height - (t1->height>>2); - lastsector = t1->Sector; - sightthing=t1; + sightstart = t1->PosRelative(task->portalgroup); + sightend = t2->PosRelative(task->portalgroup); + sightstart.z += t1->height - (t1->height >> 2); + + startfrac = task->frac; + trace = { sightstart.x, sightstart.y, sightend.x - sightstart.x, sightend.y - sightstart.y }; + lastztop = lastzbottom = sightstart.z; + lastsector = startsector; seeingthing=t2; - bottomslope = t2->Z() - sightzstart; - topslope = bottomslope + t2->height; + topslope = task->topslope; + bottomslope = task->bottomslope; Flags = flags; + portaldir = task->direction; + portalfound = false; myseethrough = FF_SEETHROUGH; } }; +//========================================================================== +// +// P_SightOpening +// +// Simplified version that removes everything not needed for a sight check +// +//========================================================================== + +void SightCheck::P_SightOpening(SightOpening &open, const line_t *linedef, fixed_t x, fixed_t y) +{ + open.portalflags = 0; + sector_t *front = linedef->frontsector; + sector_t *back = linedef->backsector; + + if (back == NULL) + { + // single sided line + if (linedef->flags & ML_PORTALCONNECT) + { + if (!front->PortalBlocksSight(sector_t::ceiling)) open.portalflags |= SO_TOPFRONT; + if (!front->PortalBlocksSight(sector_t::floor)) open.portalflags |= SO_BOTTOMFRONT; + } + + open.range = 0; + return; + } + + + fixed_t fc = 0, ff = 0, bc = 0, bf = 0; + + if (linedef->flags & ML_PORTALCONNECT) + { + if (!front->PortalBlocksSight(sector_t::ceiling)) fc = FIXED_MAX, open.portalflags |= SO_TOPFRONT; + if (!back->PortalBlocksSight(sector_t::ceiling)) bc = FIXED_MAX, open.portalflags |= SO_TOPBACK; + if (!front->PortalBlocksSight(sector_t::floor)) ff = FIXED_MIN, open.portalflags |= SO_BOTTOMFRONT; + if (!back->PortalBlocksSight(sector_t::floor)) bf = FIXED_MIN, open.portalflags |= SO_BOTTOMBACK; + } + + if (fc == 0) fc = front->ceilingplane.ZatPoint(x, y); + if (bc == 0) bc = back->ceilingplane.ZatPoint(x, y); + if (ff == 0) ff = front->floorplane.ZatPoint(x, y); + if (bf == 0) bf = back->floorplane.ZatPoint(x, y); + + open.bottom = MAX(ff, bf); + open.top = MIN(fc, bc); + + // we only want to know if there is an opening, not how large it is. + open.range = open.bottom >= open.top ? 0 : 1; +} + + /* ============== = @@ -93,14 +190,12 @@ public: ============== */ -/* -static bool PTR_SightTraverse (intercept_t *in) -*/ bool SightCheck::PTR_SightTraverse (intercept_t *in) { line_t *li; fixed_t slope; - FLineOpening open; + SightOpening open; + int frontflag = -1; li = in->d.line; @@ -114,30 +209,68 @@ bool SightCheck::PTR_SightTraverse (intercept_t *in) fixed_t trX=trace.x + FixedMul (trace.dx, in->frac); fixed_t trY=trace.y + FixedMul (trace.dy, in->frac); - P_LineOpening (open, NULL, li, trX, trY); + P_SightOpening (open, li, trX, trY); - if (open.range <= 0) // quick test for totally closed doors + FLinePortal *lport = li->getPortal(); + + if (open.range == 0 && open.portalflags == 0 && (lport == NULL || lport->mType != PORTT_LINKED)) // quick test for totally closed doors (must be delayed if portal checks are needed, though) return false; // stop // check bottom - slope = FixedDiv (open.bottom - sightzstart, in->frac); - if (slope > bottomslope) - bottomslope = slope; + if (open.bottom > FIXED_MIN) + { + slope = FixedDiv(open.bottom - sightstart.z, in->frac); + if (slope > bottomslope) + bottomslope = slope; + } // check top - slope = FixedDiv (open.top - sightzstart, in->frac); - if (slope < topslope) - topslope = slope; + if (open.top < FIXED_MAX) + { + slope = FixedDiv(open.top - sightstart.z, in->frac); + if (slope < topslope) + topslope = slope; + } - if (topslope <= bottomslope) + if (open.portalflags) + { + sector_t *frontsec, *backsec; + frontflag = P_PointOnLineSidePrecise(sightstart.x, sightstart.y, li); + if (!frontflag) + { + frontsec = li->frontsector; + backsec = li->backsector; + } + else + { + frontsec = li->backsector; + if (!frontsec) return false; // We are looking through the backside of a one-sided line. Just abort if that happens. + backsec = li->frontsector; + open.SwapSides(); // swap flags to make the next checks simpler. + } + + if (portaldir != sector_t::floor && (open.portalflags & SO_TOPBACK) && !(open.portalflags & SO_TOPFRONT)) + { + portals.Push({ in->frac, topslope, bottomslope, sector_t::ceiling, backsec->SkyBoxes[sector_t::ceiling]->Sector->PortalGroup }); + } + if (portaldir != sector_t::ceiling && (open.portalflags & SO_BOTTOMBACK) && !(open.portalflags & SO_BOTTOMFRONT)) + { + portals.Push({ in->frac, topslope, bottomslope, sector_t::floor, backsec->SkyBoxes[sector_t::floor]->Sector->PortalGroup }); + } + } + if (lport) + { + portals.Push({ in->frac, topslope, bottomslope, portaldir, lport->mDestination->frontsector->PortalGroup }); + return false; + } + + if (topslope <= bottomslope || open.range == 0) return false; // stop // now handle 3D-floors if(li->frontsector->e->XFloor.ffloors.Size() || li->backsector->e->XFloor.ffloors.Size()) { - int frontflag; - - frontflag = P_PointOnLineSidePrecise(sightthing->X(), sightthing->Y(), li); + if (frontflag == -1) frontflag = P_PointOnLineSidePrecise(sightstart.x, sightstart.y, li); //Check 3D FLOORS! for(int i=1;i<=2;i++) @@ -145,8 +278,8 @@ bool SightCheck::PTR_SightTraverse (intercept_t *in) sector_t * s=i==1? li->frontsector:li->backsector; fixed_t highslope, lowslope; - fixed_t topz= FixedMul (topslope, in->frac) + sightzstart; - fixed_t bottomz= FixedMul (bottomslope, in->frac) + sightzstart; + fixed_t topz= FixedMul (topslope, in->frac) + sightstart.z; + fixed_t bottomz= FixedMul (bottomslope, in->frac) + sightstart.z; for(unsigned int j=0;je->XFloor.ffloors.Size();j++) { @@ -158,8 +291,8 @@ bool SightCheck::PTR_SightTraverse (intercept_t *in) fixed_t ff_bottom=rover->bottom.plane->ZatPoint(trX, trY); fixed_t ff_top=rover->top.plane->ZatPoint(trX, trY); - highslope = FixedDiv (ff_top - sightzstart, in->frac); - lowslope = FixedDiv (ff_bottom - sightzstart, in->frac); + highslope = FixedDiv (ff_top - sightstart.z, in->frac); + lowslope = FixedDiv (ff_bottom - sightstart.z, in->frac); if (highslope>=topslope) { @@ -221,8 +354,8 @@ bool SightCheck::PTR_SightTraverse (intercept_t *in) } else lastsector=NULL; // don't need it if there are no 3D-floors - lastztop= FixedMul (topslope, in->frac) + sightzstart; - lastzbottom= FixedMul (bottomslope, in->frac) + sightzstart; + lastztop= FixedMul (topslope, in->frac) + sightstart.z; + lastzbottom= FixedMul (bottomslope, in->frac) + sightstart.z; return true; // keep going } @@ -308,10 +441,11 @@ bool SightCheck::P_SightCheckLine (line_t *ld) =================== */ -bool SightCheck::P_SightBlockLinesIterator (int x, int y) +int SightCheck::P_SightBlockLinesIterator (int x, int y) { int offset; int *list; + int res = 1; polyblock_t *polyLink; unsigned int i; @@ -319,7 +453,12 @@ bool SightCheck::P_SightBlockLinesIterator (int x, int y) offset = y*bmapwidth+x; + // if any of the previous blocks may contain a portal we may abort the collection of lines here, but we may not abort the sight check. + // (We still try to delay activating this for as long as possible.) + portalfound = portalfound || PortalBlockmap(x, y).containsLinkedPortals; + polyLink = PolyBlockMap[offset]; + portalfound |= (polyLink && PortalBlockmap.hasLinkedPolyPortals); while (polyLink) { if (polyLink->polyobj) @@ -329,8 +468,11 @@ bool SightCheck::P_SightBlockLinesIterator (int x, int y) polyLink->polyobj->validcount = validcount; for (i = 0; i < polyLink->polyobj->Linedefs.Size(); i++) { - if (!P_SightCheckLine (polyLink->polyobj->Linedefs[i])) - return false; + if (!P_SightCheckLine(polyLink->polyobj->Linedefs[i])) + { + if (!portalfound) return 0; + else res = -1; + } } } } @@ -342,10 +484,13 @@ bool SightCheck::P_SightBlockLinesIterator (int x, int y) for (list = blockmaplump + offset + 1; *list != -1; list++) { if (!P_SightCheckLine (&lines[*list])) - return false; + { + if (!portalfound) return 0; + else res = -1; + } } - return true; // everything was checked + return res; // everything was checked } /* @@ -374,12 +519,16 @@ bool SightCheck::P_SightTraverseIntercepts () scan = &intercepts[scanpos]; P_MakeDivline (scan->d.line, &dl); scan->frac = P_InterceptVector (&trace, &dl); + if (scan->frac < startfrac) + { + scan->frac = FIXED_MAX; + count--; + } } // // go through in order -// [RH] Is it really necessary to go through in order? All we care about is if -// the trace is obstructed, not what specifically obstructed it. +// proper order is needed to handle 3D floors and portals. // in = NULL; @@ -408,8 +557,8 @@ bool SightCheck::P_SightTraverseIntercepts () { // we must do one last check whether the trace has crossed a 3D floor in the last sector - fixed_t topz= topslope + sightzstart; - fixed_t bottomz= bottomslope + sightzstart; + fixed_t topz= topslope + sightstart.z; + fixed_t bottomz= bottomslope + sightstart.z; for(unsigned int i=0;ie->XFloor.ffloors.Size();i++) { @@ -441,8 +590,9 @@ bool SightCheck::P_SightTraverseIntercepts () ================== */ -bool SightCheck::P_SightPathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) +bool SightCheck::P_SightPathTraverse () { + fixed_t x1, x2, y1, y2; fixed_t xt1,yt1,xt2,yt2; long long _x1,_y1,_x2,_y2; fixed_t xstep,ystep; @@ -453,34 +603,49 @@ bool SightCheck::P_SightPathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_ validcount++; intercepts.Clear (); + x1 = sightstart.x + FixedMul(startfrac, trace.dx); + y1 = sightstart.y + FixedMul(startfrac, trace.dy); + x2 = sightend.x; + y2 = sightend.y; + if (lastsector == NULL) lastsector = P_PointInSector(x1, y1); // for FF_SEETHROUGH the following rule applies: // If the viewer is in an area without FF_SEETHROUGH he can only see into areas without this flag // If the viewer is in an area with FF_SEETHROUGH he can only see into areas with this flag + bool checkfloor = true, checkceiling = true; for(unsigned int i=0;ie->XFloor.ffloors.Size();i++) { F3DFloor* rover = lastsector->e->XFloor.ffloors[i]; if(!(rover->flags & FF_EXISTS)) continue; - fixed_t ff_bottom=rover->bottom.plane->ZatPoint(sightthing); - fixed_t ff_top=rover->top.plane->ZatPoint(sightthing); + fixed_t ff_bottom=rover->bottom.plane->ZatPoint(sightstart); + fixed_t ff_top=rover->top.plane->ZatPoint(sightstart); - if (sightzstart < ff_top && sightzstart >= ff_bottom) + if (sightstart.z < ff_top) checkceiling = false; + if (sightstart.z >= ff_bottom) checkfloor = false; + + if (sightstart.z < ff_top && sightstart.z >= ff_bottom) { myseethrough = rover->flags & FF_SEETHROUGH; break; } } + // We also must check if the starting sector contains portals, and start sight checks in those as well. + if (portaldir != sector_t::floor && checkceiling && !lastsector->PortalBlocksSight(sector_t::ceiling)) + { + portals.Push({ 0, topslope, bottomslope, sector_t::ceiling, lastsector->SkyBoxes[sector_t::ceiling]->Sector->PortalGroup }); + } + if (portaldir != sector_t::ceiling && checkfloor && !lastsector->PortalBlocksSight(sector_t::floor)) + { + portals.Push({ 0, topslope, bottomslope, sector_t::floor, lastsector->SkyBoxes[sector_t::floor]->Sector->PortalGroup }); + } + if ( ((x1-bmaporgx)&(MAPBLOCKSIZE-1)) == 0) x1 += FRACUNIT; // don't side exactly on a line if ( ((y1-bmaporgy)&(MAPBLOCKSIZE-1)) == 0) y1 += FRACUNIT; // don't side exactly on a line - trace.x = x1; - trace.y = y1; - trace.dx = x2 - x1; - trace.dy = y2 - y1; _x1 = (long long)x1 - bmaporgx; _y1 = (long long)y1 - bmaporgy; @@ -496,12 +661,6 @@ bool SightCheck::P_SightPathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_ xt2 = int(_x2 >> MAPBLOCKSHIFT); yt2 = int(_y2 >> MAPBLOCKSHIFT); -// points should never be out of bounds, but check once instead of -// each block - if (xt1<0 || yt1<0 || xt1>=bmapwidth || yt1>=bmapheight - || xt2<0 || yt2<0 || xt2>=bmapwidth || yt2>=bmapheight) - return false; - if (xt2 > xt1) { mapxstep = 1; @@ -572,13 +731,21 @@ bool SightCheck::P_SightPathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_ for (count = 0 ; count < 100 ; count++) { - if (!P_SightBlockLinesIterator (mapx, mapy)) + // end traversing when reaching the end of the blockmap + // an early out is not possible because with portals a trace can easily land outside the map's bounds. + if (mapx < 0 || mapx >= bmapwidth || mapy < 0 || mapy >= bmapheight) { -sightcounts[1]++; + break; + } + int res = P_SightBlockLinesIterator(mapx, mapy); + if (res == 0) + { + sightcounts[1]++; return false; // early out } - if ((mapxstep | mapystep) == 0) + // either reached the end or had an early-out condition with portals left to check, + if (res == -1 || (mapxstep | mapystep) == 0) break; switch ((((yintercept >> FRACBITS) == mapy) << 1) | ((xintercept >> FRACBITS) == mapx)) @@ -649,7 +816,7 @@ sightcounts[2]++; ===================== */ -bool P_CheckSight (const AActor *t1, const AActor *t2, int flags) +bool P_CheckSight (AActor *t1, AActor *t2, int flags) { SightCycles.Clock(); @@ -716,9 +883,34 @@ sightcounts[0]++; // Now look from eyes of t1 to any part of t2. validcount++; + portals.Clear(); { - SightCheck s(t1, t2, flags); - res = s.P_SightPathTraverse (t1->X(), t1->Y(), t2->X(), t2->Y()); + sector_t *sec; + fixed_t lookheight = t1->height - (t1->height >> 2); + t1->GetPortalTransition(lookheight, &sec); + + fixed_t bottomslope = t2->Z() - (t1->Z() + lookheight); + fixed_t topslope = bottomslope + t2->height; + SightTask task = { 0, topslope, bottomslope, -1, sec->PortalGroup }; + + + SightCheck s; + s.init(t1, t2, sec, &task, flags); + res = s.P_SightPathTraverse (); + if (!res) + { + fixed_t dist = t1->AproxDistance(t2); + for (unsigned i = 0; i < portals.Size(); i++) + { + portals[i].frac += FixedDiv(FRACUNIT, dist); + s.init(t1, t2, NULL, &portals[i], flags); + if (s.P_SightPathTraverse()) + { + res = true; + break; + } + } + } } done: diff --git a/src/p_spec.h b/src/p_spec.h index f7bef6c42b..e3b1c05ad7 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -626,7 +626,7 @@ public: ceilRaiseInstant, ceilCrushAndRaise, ceilLowerAndCrush, - ceilLowerAndCrushDist, + ceil_placeholder, ceilCrushRaiseAndStay, ceilRaiseToNearest, ceilLowerToLowest, diff --git a/src/p_terrain.cpp b/src/p_terrain.cpp index 37c2bda0ec..3c6519393d 100644 --- a/src/p_terrain.cpp +++ b/src/p_terrain.cpp @@ -702,6 +702,7 @@ int P_FindTerrain (FName name) { unsigned int i; + if (name == NAME_Null) return -1; for (i = 0; i < Terrains.Size (); i++) { if (Terrains[i].Name == name) diff --git a/src/p_trace.cpp b/src/p_trace.cpp index 297bf52bc5..2dec3d9d5a 100644 --- a/src/p_trace.cpp +++ b/src/p_trace.cpp @@ -41,6 +41,12 @@ #include "r_defs.h" #include "p_spec.h" +//========================================================================== +// +// +// +//========================================================================== + struct FTraceInfo { fixed_t StartX, StartY, StartZ; @@ -49,6 +55,7 @@ struct FTraceInfo DWORD WallMask; AActor *IgnoreThis; FTraceResults *Results; + FTraceResults *TempResults; sector_t *CurSector; fixed_t MaxDist; fixed_t EnterDist; @@ -57,6 +64,8 @@ struct FTraceInfo DWORD TraceFlags; int inshootthrough; fixed_t startfrac; + int aimdir; + fixed_t limitz; // These are required for 3D-floor checking // to create a fake sector with a floor @@ -64,15 +73,44 @@ struct FTraceInfo sector_t DummySector[2]; int sectorsel; + void Setup3DFloors(); + bool LineCheck(intercept_t *in); + bool ThingCheck(intercept_t *in); bool TraceTraverse (int ptflags); bool CheckPlane(const secplane_t &plane); - bool CheckSectorPlane (const sector_t *sector, bool checkFloor); - bool Check3DFloorPlane(const F3DFloor *ffloor, bool checkBottom); + int EnterLinePortal(line_t *li, fixed_t frac); + void EnterSectorPortal(int position, fixed_t frac, sector_t *entersec); + + + bool CheckSectorPlane(const sector_t *sector, bool checkFloor) + { + return CheckPlane(checkFloor ? sector->floorplane : sector->ceilingplane); + } + + bool Check3DFloorPlane(const F3DFloor *ffloor, bool checkBottom) + { + return CheckPlane(checkBottom? *(ffloor->bottom.plane) : *(ffloor->top.plane)); + } + + void SetSourcePosition() + { + Results->SrcFromTarget = { StartX, StartY, StartZ }; + Results->HitVector = { Vx, Vy, Vz }; + Results->SrcAngleToTarget = R_PointToAngle2(0, 0, Results->HitPos.x - StartX, Results->HitPos.y - StartY); + } + + }; static bool EditTraceResult (DWORD flags, FTraceResults &res); +//========================================================================== +// +// Trace entry point +// +//========================================================================== + bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, fixed_t vx, fixed_t vy, fixed_t vz, fixed_t maxDist, ActorFlags actorMask, DWORD wallMask, AActor *ignore, @@ -81,6 +119,10 @@ bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, { int ptflags; FTraceInfo inf; + FTraceResults tempResult; + + memset(&tempResult, 0, sizeof(tempResult)); + tempResult.Fraction = tempResult.Distance = FIXED_MAX; ptflags = actorMask ? PT_ADDLINES|PT_ADDTHINGS|PT_COMPATIBLE : PT_ADDLINES; @@ -100,25 +142,23 @@ bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, inf.TraceCallbackData = callbackdata; inf.TraceFlags = flags; inf.Results = &res; + inf.TempResults = &tempResult; inf.inshootthrough = true; inf.sectorsel=0; + inf.aimdir = -1; + inf.startfrac = 0; memset(&res, 0, sizeof(res)); - /* // Redundant with the memset - res.HitType = TRACE_HitNone; - res.CrossedWater = NULL; - res.Crossed3DWater = NULL; - */ // check for overflows and clip if necessary SQWORD xd = (SQWORD)x + ((SQWORD(vx) * SQWORD(maxDist)) >> 16); if (xd>SQWORD(32767)*FRACUNIT) { - maxDist = inf.MaxDist = FixedDiv(FIXED_MAX - x, vx); + inf.MaxDist = FixedDiv(FIXED_MAX - x, vx); } else if (xd<-SQWORD(32767)*FRACUNIT) { - maxDist = inf.MaxDist = FixedDiv(FIXED_MIN - x, vx); + inf.MaxDist = FixedDiv(FIXED_MIN - x, vx); } @@ -126,62 +166,139 @@ bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, if (yd>SQWORD(32767)*FRACUNIT) { - maxDist = inf.MaxDist = FixedDiv(FIXED_MAX - y, vy); + inf.MaxDist = FixedDiv(FIXED_MAX - y, vy); } else if (yd<-SQWORD(32767)*FRACUNIT) { - maxDist = inf.MaxDist = FixedDiv(FIXED_MIN - y, vy); + inf.MaxDist = FixedDiv(FIXED_MIN - y, vy); } - // recalculate the trace's end points for robustness if (inf.TraceTraverse (ptflags)) - { // check for intersection with floor/ceiling - res.Sector = §ors[inf.CurSector->sectornum]; - - if (inf.CheckSectorPlane (inf.CurSector, true)) - { - res.HitType = TRACE_HitFloor; - res.HitTexture = inf.CurSector->GetTexture(sector_t::floor); - if (res.CrossedWater == NULL && - inf.CurSector->heightsec != NULL && - inf.CurSector->heightsec->floorplane.ZatPoint (res.X, res.Y) >= res.Z) - { - res.CrossedWater = §ors[inf.CurSector->sectornum]; - } - } - else if (inf.CheckSectorPlane (inf.CurSector, false)) - { - res.HitType = TRACE_HitCeiling; - res.HitTexture = inf.CurSector->GetTexture(sector_t::ceiling); - } - } - - if (res.HitType != TRACE_HitNone) - { - if (flags) - { - return EditTraceResult (flags, res); - } - else - { - return true; - } + { + return flags ? EditTraceResult(flags, res) : true; } else { - res.HitType = TRACE_HitNone; - res.X = x + FixedMul (vx, maxDist); - res.Y = y + FixedMul (vy, maxDist); - res.Z = z + FixedMul (vz, maxDist); - res.Distance = maxDist; - res.Fraction = FRACUNIT; return false; } } -bool FTraceInfo::TraceTraverse (int ptflags) + +//============================================================================ +// +// traverses a sector portal +// +//============================================================================ + +void FTraceInfo::EnterSectorPortal(int position, fixed_t frac, sector_t *entersec) +{ + if (aimdir != -1 && aimdir != position) return; + AActor *portal = entersec->SkyBoxes[position]; + + if (aimdir == sector_t::ceiling && portal->threshold < limitz) return; + else if (aimdir == sector_t::floor && portal->threshold > limitz) return; + + FTraceInfo newtrace; + FTraceResults results; + + memset(&results, 0, sizeof(results)); + + newtrace.StartX = StartX + portal->scaleX; + newtrace.StartY = StartY + portal->scaleY; + newtrace.StartZ = StartZ; + + frac += FixedDiv(FRACUNIT, MaxDist); + fixed_t enterdist = FixedMul(MaxDist, frac); + fixed_t enterX = newtrace.StartX + FixedMul(enterdist, Vx); + fixed_t enterY = newtrace.StartY + FixedMul(enterdist, Vy); + + newtrace.Vx = Vx; + newtrace.Vy = Vy; + newtrace.Vz = Vz; + newtrace.ActorMask = ActorMask; + newtrace.WallMask = WallMask; + newtrace.IgnoreThis = IgnoreThis; + newtrace.Results = &results; + newtrace.TempResults = TempResults; + newtrace.CurSector = P_PointInSector(enterX ,enterY); + newtrace.MaxDist = MaxDist; + newtrace.EnterDist = EnterDist; + newtrace.TraceCallback = TraceCallback; + newtrace.TraceCallbackData = TraceCallbackData; + newtrace.TraceFlags = TraceFlags; + newtrace.inshootthrough = true; + newtrace.startfrac = frac; + newtrace.aimdir = position; + newtrace.limitz = portal->threshold; + newtrace.sectorsel = 0; + + if (newtrace.TraceTraverse(ActorMask ? PT_ADDLINES | PT_ADDTHINGS | PT_COMPATIBLE : PT_ADDLINES)) + { + TempResults->CopyIfCloser(newtrace.Results); + } +} + +//============================================================================ +// +// traverses a line portal +// simply calling PortalRelocate does not work here because more needs to be set up +// +//============================================================================ + +int FTraceInfo::EnterLinePortal(line_t *li, fixed_t frac) +{ + FLinePortal *port = li->getPortal(); + + // The caller cannot handle portals without global offset. + if (port->mType != PORTT_LINKED && (TraceFlags & TRACE_PortalRestrict)) return -1; + + FTraceInfo newtrace; + + newtrace.StartX = StartX; + newtrace.StartY = StartY; + newtrace.StartZ = StartZ; + newtrace.Vx = Vx; + newtrace.Vy = Vy; + newtrace.Vz = Vz; + + P_TranslatePortalXY(li, newtrace.StartX, newtrace.StartY); + P_TranslatePortalZ(li, newtrace.StartZ); + P_TranslatePortalVXVY(li, newtrace.Vx, newtrace.Vy); + + frac += FixedDiv(FRACUNIT, MaxDist); + fixed_t enterdist = FixedMul(MaxDist, frac); + fixed_t enterX = newtrace.StartX + FixedMul(enterdist, Vx); + fixed_t enterY = newtrace.StartY + FixedMul(enterdist, Vy); + + newtrace.ActorMask = ActorMask; + newtrace.WallMask = WallMask; + newtrace.IgnoreThis = IgnoreThis; + newtrace.Results = Results; + newtrace.TempResults = TempResults; + newtrace.CurSector = P_PointInSector(enterX, enterY); + newtrace.MaxDist = MaxDist; + newtrace.EnterDist = EnterDist; + newtrace.TraceCallback = TraceCallback; + newtrace.TraceCallbackData = TraceCallbackData; + newtrace.TraceFlags = TraceFlags; + newtrace.inshootthrough = true; + newtrace.startfrac = frac; + newtrace.aimdir = aimdir; + newtrace.limitz = limitz; + P_TranslatePortalZ(li, newtrace.limitz); + newtrace.sectorsel = 0; + Results->unlinked = true; + return newtrace.TraceTraverse(ActorMask ? PT_ADDLINES | PT_ADDTHINGS | PT_COMPATIBLE : PT_ADDLINES); +} + +//========================================================================== +// +// Checks 3D floors at trace start and sets up related data +// +//========================================================================== + +void FTraceInfo::Setup3DFloors() { - // Do a 3D floor check in the starting sector TDeletingArray &ff = CurSector->e->XFloor.ffloors; if (ff.Size()) @@ -195,6 +312,7 @@ bool FTraceInfo::TraceTraverse (int ptflags) fixed_t y = StartY + FixedMul(Vy, sdist); fixed_t z = StartZ + FixedMul(Vz, sdist); + fixed_t bf = CurSector->floorplane.ZatPoint(x, y); fixed_t bc = CurSector->ceilingplane.ZatPoint(x, y); @@ -206,7 +324,10 @@ bool FTraceInfo::TraceTraverse (int ptflags) if (rover->flags&FF_SWIMMABLE && Results->Crossed3DWater == NULL) { if (Check3DFloorPlane(rover, false)) + { Results->Crossed3DWater = rover; + Results->Crossed3DWaterPos = Results->HitPos; + } } if (!(rover->flags&FF_SHOOTTHROUGH)) @@ -214,10 +335,10 @@ bool FTraceInfo::TraceTraverse (int ptflags) fixed_t ff_bottom = rover->bottom.plane->ZatPoint(x, y); fixed_t ff_top = rover->top.plane->ZatPoint(x, y); // clip to the part of the sector we are in - if (z>ff_top) + if (z > ff_top) { // above - if (bffloorplane = *rover->top.plane; CurSector->SetTexture(sector_t::floor, *rover->top.texture, false); @@ -225,10 +346,10 @@ bool FTraceInfo::TraceTraverse (int ptflags) bf = ff_top; } } - else if (zff_bottom) + if (bc > ff_bottom) { CurSector->ceilingplane = *rover->bottom.plane; CurSector->SetTexture(sector_t::ceiling, *rover->bottom.texture, false); @@ -239,7 +360,7 @@ bool FTraceInfo::TraceTraverse (int ptflags) else { // inside - if (bffloorplane = *rover->bottom.plane; CurSector->SetTexture(sector_t::floor, *rover->bottom.texture, false); @@ -247,7 +368,7 @@ bool FTraceInfo::TraceTraverse (int ptflags) bf = ff_bottom; } - if (bc>ff_top) + if (bc > ff_top) { CurSector->ceilingplane = *rover->top.plane; CurSector->SetTexture(sector_t::ceiling, *rover->top.texture, false); @@ -259,379 +380,394 @@ bool FTraceInfo::TraceTraverse (int ptflags) } } } - - - FPathTraverse it(StartX, StartY, FixedMul (Vx, MaxDist), FixedMul (Vy, MaxDist), ptflags | PT_DELTA); - intercept_t *in; - - while ((in = it.Next())) + if (!CurSector->PortalBlocksMovement(sector_t::ceiling)) { - fixed_t hitx, hity, hitz; - fixed_t dist; + EnterSectorPortal(sector_t::ceiling, startfrac, CurSector); + } + if (!CurSector->PortalBlocksMovement(sector_t::floor)) + { + EnterSectorPortal(sector_t::floor, startfrac, CurSector); + } +} - // Deal with splashes in 3D floors - if (CurSector->e->XFloor.ffloors.Size()) + +//========================================================================== +// +// Processes one line intercept +// +//========================================================================== + +bool FTraceInfo::LineCheck(intercept_t *in) +{ + int lineside; + sector_t *entersector; + + fixed_t dist = FixedMul(MaxDist, in->frac); + fixed_t hitx = StartX + FixedMul(Vx, dist); + fixed_t hity = StartY + FixedMul(Vy, dist); + fixed_t hitz = StartZ + FixedMul(Vz, dist); + + fixed_t ff, fc, bf = 0, bc = 0; + + if (in->d.line->frontsector->sectornum == CurSector->sectornum) + { + lineside = 0; + } + else if (in->d.line->backsector && in->d.line->backsector->sectornum == CurSector->sectornum) + { + lineside = 1; + } + else + { // Dammit. Why does Doom have to allow non-closed sectors? + if (in->d.line->backsector == NULL) { - for(unsigned int i=0;ie->XFloor.ffloors.Size();i++) - { - F3DFloor * rover=CurSector->e->XFloor.ffloors[i]; - if (!(rover->flags&FF_EXISTS)) - continue; + lineside = 0; + CurSector = in->d.line->frontsector; + } + else + { + lineside = P_PointOnLineSide(StartX, StartY, in->d.line); + CurSector = lineside ? in->d.line->backsector : in->d.line->frontsector; + } + } - // Deal with splashy stuff - if (rover->flags&FF_SWIMMABLE && Results->Crossed3DWater == NULL) - { - if (Check3DFloorPlane(rover, false)) - Results->Crossed3DWater = rover; - } + if (!(in->d.line->flags & ML_TWOSIDED)) + { + entersector = NULL; + } + else + { + entersector = (lineside == 0) ? in->d.line->backsector : in->d.line->frontsector; + + // For backwards compatibility: Ignore lines with the same sector on both sides. + // This is the way Doom.exe did it and some WADs (e.g. Alien Vendetta MAP15) need it. + if (i_compatflags & COMPATF_TRACE && in->d.line->backsector == in->d.line->frontsector) + { + // We must check special activation here because the code below is never reached. + if (TraceFlags & TRACE_PCross) + { + P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_PCross); + } + if (TraceFlags & TRACE_Impact) + { + P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); + } + return true; + } + } + + ff = CurSector->floorplane.ZatPoint(hitx, hity); + fc = CurSector->ceilingplane.ZatPoint(hitx, hity); + + if (entersector != NULL) + { + bf = entersector->floorplane.ZatPoint(hitx, hity); + bc = entersector->ceilingplane.ZatPoint(hitx, hity); + } + + sector_t *hsec = CurSector->GetHeightSec(); + if (Results->CrossedWater == NULL && + hsec != NULL && + //CurSector->heightsec->waterzone && + hitz <= hsec->floorplane.ZatPoint(hitx, hity)) + { + // hit crossed a water plane + if (CheckSectorPlane(hsec, true)) + { + Results->CrossedWater = §ors[CurSector->sectornum]; + Results->CrossedWaterPos = Results->HitPos; + } + } + + if (hitz <= ff) + { + if (CurSector->PortalBlocksMovement(sector_t::floor)) + { + // hit floor in front of wall + Results->HitType = TRACE_HitFloor; + Results->HitTexture = CurSector->GetTexture(sector_t::floor); + } + else if (entersector == NULL || entersector->PortalBlocksMovement(sector_t::floor)) + { + // hit beyond a portal plane. This needs to be taken care of by the trace spawned on the other side. + Results->HitType = TRACE_HitNone; + return false; + } + } + else if (hitz >= fc) + { + if (CurSector->PortalBlocksMovement(sector_t::ceiling)) + { + // hit ceiling in front of wall + Results->HitType = TRACE_HitCeiling; + Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); + } + else if (entersector == NULL || entersector->PortalBlocksMovement(sector_t::ceiling)) + { + // hit beyond a portal plane. This needs to be taken care of by the trace spawned on the other side. + Results->HitType = TRACE_HitNone; + return false; + } + } + else if (in->d.line->isLinePortal()) + { + if (entersector == NULL || (hitz >= bf && hitz <= bc)) + { + int res = EnterLinePortal(in->d.line, in->frac); + if (res != -1) + { + aimdir = INT_MAX; // flag for ending the traverse + return !!res; } } - - if (in->isaline) + goto normalline; // hit upper or lower tier. + } + else if (entersector == NULL || + hitz < bf || hitz > bc || + in->d.line->flags & WallMask) + { +normalline: + // hit the wall + Results->HitType = TRACE_HitWall; + Results->Tier = + entersector == NULL ? TIER_Middle : + hitz <= bf ? TIER_Lower : + hitz >= bc ? TIER_Upper : TIER_Middle; + if (TraceFlags & TRACE_Impact) { - int lineside; - sector_t *entersector; + P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); + } + } + else + { // made it past the wall + // check for 3D floors first + if (entersector->e->XFloor.ffloors.Size()) + { + memcpy(&DummySector[sectorsel], entersector, sizeof(sector_t)); + entersector = &DummySector[sectorsel]; + sectorsel ^= 1; - dist = FixedMul (MaxDist, in->frac); - hitx = StartX + FixedMul (Vx, dist); - hity = StartY + FixedMul (Vy, dist); - hitz = StartZ + FixedMul (Vz, dist); - - fixed_t ff, fc, bf = 0, bc = 0; - - // CurSector may be a copy so we must compare the sector number, not the index! - if (in->d.line->frontsector->sectornum == CurSector->sectornum) + for (auto rover : entersector->e->XFloor.ffloors) { - lineside = 0; - } - else if (in->d.line->backsector && in->d.line->backsector->sectornum == CurSector->sectornum) - { - lineside = 1; - } - else - { // Dammit. Why does Doom have to allow non-closed sectors? - if (in->d.line->backsector == NULL) + int entershootthrough = !!(rover->flags&FF_SHOOTTHROUGH); + + if (entershootthrough != inshootthrough && rover->flags&FF_EXISTS) { - lineside = 0; - CurSector = in->d.line->frontsector; - } - else - { - lineside = P_PointOnLineSide (StartX, StartY, in->d.line); - CurSector = lineside ? in->d.line->backsector : in->d.line->frontsector; - } - } + fixed_t ff_bottom = rover->bottom.plane->ZatPoint(hitx, hity); + fixed_t ff_top = rover->top.plane->ZatPoint(hitx, hity); - if (!(in->d.line->flags & ML_TWOSIDED)) - { - entersector = NULL; - } - else - { - entersector = (lineside == 0) ? in->d.line->backsector : in->d.line->frontsector; - - // For backwards compatibility: Ignore lines with the same sector on both sides. - // This is the way Doom.exe did it and some WADs (e.g. Alien Vendetta MAP15) need it. - if (i_compatflags & COMPATF_TRACE && in->d.line->backsector == in->d.line->frontsector) - { - // We must check special activation here because the code below is never reached. - if (TraceFlags & TRACE_PCross) + // clip to the part of the sector we are in + if (hitz > ff_top) { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_PCross); - } - if (TraceFlags & TRACE_Impact) - { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact); - } - continue; - } - } - - ff = CurSector->floorplane.ZatPoint (hitx, hity); - fc = CurSector->ceilingplane.ZatPoint (hitx, hity); - - if (entersector != NULL) - { - bf = entersector->floorplane.ZatPoint (hitx, hity); - bc = entersector->ceilingplane.ZatPoint (hitx, hity); - } - - sector_t *hsec = CurSector->GetHeightSec(); - if (Results->CrossedWater == NULL && - hsec != NULL && - //CurSector->heightsec->waterzone && - hitz <= hsec->floorplane.ZatPoint (hitx, hity)) - { - // hit crossed a water plane - Results->CrossedWater = §ors[CurSector->sectornum]; - } - - if (hitz <= ff) - { // hit floor in front of wall - Results->HitType = TRACE_HitFloor; - Results->HitTexture = CurSector->GetTexture(sector_t::floor); - } - else if (hitz >= fc) - { // hit ceiling in front of wall - Results->HitType = TRACE_HitCeiling; - Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); - } - else if (entersector == NULL || - hitz < bf || hitz > bc || - in->d.line->flags & WallMask) - { // hit the wall - Results->HitType = TRACE_HitWall; - Results->Tier = - entersector == NULL ? TIER_Middle : - hitz <= bf ? TIER_Lower : - hitz >= bc ? TIER_Upper : TIER_Middle; - if (TraceFlags & TRACE_Impact) - { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact); - } - } - else - { // made it past the wall - // check for 3D floors first - if (entersector->e->XFloor.ffloors.Size()) - { - memcpy(&DummySector[sectorsel],entersector,sizeof(sector_t)); - entersector=&DummySector[sectorsel]; - sectorsel^=1; - - for(unsigned int i=0;ie->XFloor.ffloors.Size();i++) - { - F3DFloor * rover=entersector->e->XFloor.ffloors[i]; - int entershootthrough = !!(rover->flags&FF_SHOOTTHROUGH); - - if (entershootthrough != inshootthrough && rover->flags&FF_EXISTS) + // above + if (bf < ff_top) { - fixed_t ff_bottom=rover->bottom.plane->ZatPoint(hitx, hity); - fixed_t ff_top=rover->top.plane->ZatPoint(hitx, hity); - - // clip to the part of the sector we are in - if (hitz>ff_top) - { - // above - if (bffloorplane=*rover->top.plane; - entersector->SetTexture(sector_t::floor, *rover->top.texture, false); - bf=ff_top; - } - } - else if (hitzff_bottom) - { - entersector->ceilingplane=*rover->bottom.plane; - entersector->SetTexture(sector_t::ceiling, *rover->bottom.texture, false); - bc=ff_bottom; - } - } - else - { - //hit the edge - equivalent to hitting the wall - Results->HitType = TRACE_HitWall; - Results->Tier = TIER_FFloor; - Results->ffloor = rover; - if ((TraceFlags & TRACE_Impact) && in->d.line->special) - { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact); - } - goto cont; - } + entersector->floorplane = *rover->top.plane; + entersector->SetTexture(sector_t::floor, *rover->top.texture, false); + entersector->SkyBoxes[sector_t::floor] = NULL; + bf = ff_top; } } - } - - Results->HitType = TRACE_HitNone; - if (TraceFlags & TRACE_PCross) - { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_PCross); - } - if (TraceFlags & TRACE_Impact) - { // This is incorrect for "impact", but Hexen did this, so - // we need to as well, for compatibility - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact); - } - } -cont: - - if (Results->HitType != TRACE_HitNone) - { - // We hit something, so figure out where exactly - Results->Sector = §ors[CurSector->sectornum]; - - if (Results->HitType != TRACE_HitWall && - !CheckSectorPlane (CurSector, Results->HitType == TRACE_HitFloor)) - { // trace is parallel to the plane (or right on it) - if (entersector == NULL) + else if (hitz < ff_bottom) { - Results->HitType = TRACE_HitWall; - Results->Tier = TIER_Middle; + //below + if (bc > ff_bottom) + { + entersector->ceilingplane = *rover->bottom.plane; + entersector->SetTexture(sector_t::ceiling, *rover->bottom.texture, false); + entersector->SkyBoxes[sector_t::ceiling] = NULL; + bc = ff_bottom; + } } else { - if (hitz <= bf || hitz >= bc) + //hit the edge - equivalent to hitting the wall + Results->HitType = TRACE_HitWall; + Results->Tier = TIER_FFloor; + Results->ffloor = rover; + if ((TraceFlags & TRACE_Impact) && in->d.line->special) { - Results->HitType = TRACE_HitWall; - Results->Tier = - hitz <= bf ? TIER_Lower : - hitz >= bc ? TIER_Upper : TIER_Middle; + P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); } - else - { - Results->HitType = TRACE_HitNone; - } - } - if (Results->HitType == TRACE_HitWall && TraceFlags & TRACE_Impact) - { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact); + goto cont; } } - - if (Results->HitType == TRACE_HitWall) - { - Results->X = hitx; - Results->Y = hity; - Results->Z = hitz; - Results->Distance = dist; - Results->Fraction = in->frac; - Results->Line = in->d.line; - Results->Side = lineside; - } } + } - if (Results->HitType == TRACE_HitNone) - { - CurSector = entersector; - EnterDist = dist; - continue; - } + Results->HitType = TRACE_HitNone; + if (TraceFlags & TRACE_PCross) + { + P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_PCross); + } + if (TraceFlags & TRACE_Impact) + { // This is incorrect for "impact", but Hexen did this, so + // we need to as well, for compatibility + P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); + } - if (TraceCallback != NULL) + if (!entersector->PortalBlocksMovement(sector_t::ceiling)) + { + EnterSectorPortal(sector_t::ceiling, in->frac, entersector); + } + if (!entersector->PortalBlocksMovement(sector_t::floor)) + { + EnterSectorPortal(sector_t::floor, in->frac, entersector); + } + } +cont: + + if (Results->HitType != TRACE_HitNone) + { + // We hit something, so figure out where exactly + Results->Sector = §ors[CurSector->sectornum]; + + if (Results->HitType != TRACE_HitWall && + !CheckSectorPlane(CurSector, Results->HitType == TRACE_HitFloor)) + { // trace is parallel to the plane (or right on it) + if (entersector == NULL) { - switch (TraceCallback(*Results, TraceCallbackData)) - { - case TRACE_Stop: return false; - case TRACE_Abort: Results->HitType = TRACE_HitNone; return false; - case TRACE_Skip: Results->HitType = TRACE_HitNone; break; - default: break; - } + Results->HitType = TRACE_HitWall; + Results->Tier = TIER_Middle; } else { - return false; - } - } - - // Encountered an actor - if (!(in->d.thing->flags & ActorMask) || in->d.thing == IgnoreThis) - { - continue; - } - - dist = FixedMul (MaxDist, in->frac); - hitx = StartX + FixedMul (Vx, dist); - hity = StartY + FixedMul (Vy, dist); - hitz = StartZ + FixedMul (Vz, dist); - - if (hitz > in->d.thing->Top()) - { // trace enters above actor - if (Vz >= 0) continue; // Going up: can't hit - - // Does it hit the top of the actor? - dist = FixedDiv(in->d.thing->Top() - StartZ, Vz); - - if (dist > MaxDist) continue; - in->frac = FixedDiv(dist, MaxDist); - - hitx = StartX + FixedMul (Vx, dist); - hity = StartY + FixedMul (Vy, dist); - hitz = StartZ + FixedMul (Vz, dist); - - // calculated coordinate is outside the actor's bounding box - if (abs(hitx - in->d.thing->X()) > in->d.thing->radius || - abs(hity - in->d.thing->Y()) > in->d.thing->radius) continue; - } - else if (hitz < in->d.thing->Z()) - { // trace enters below actor - if (Vz <= 0) continue; // Going down: can't hit - - // Does it hit the bottom of the actor? - dist = FixedDiv(in->d.thing->Z() - StartZ, Vz); - if (dist > MaxDist) continue; - in->frac = FixedDiv(dist, MaxDist); - - hitx = StartX + FixedMul (Vx, dist); - hity = StartY + FixedMul (Vy, dist); - hitz = StartZ + FixedMul (Vz, dist); - - // calculated coordinate is outside the actor's bounding box - if (abs(hitx - in->d.thing->X()) > in->d.thing->radius || - abs(hity - in->d.thing->Y()) > in->d.thing->radius) continue; - } - - // check for extrafloors first - if (CurSector->e->XFloor.ffloors.Size()) - { - fixed_t ff_floor=CurSector->floorplane.ZatPoint(hitx, hity); - fixed_t ff_ceiling=CurSector->ceilingplane.ZatPoint(hitx, hity); - - if (hitz>ff_ceiling) // actor is hit above the current ceiling - { - Results->HitType=TRACE_HitCeiling; - Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); - } - else if (hitzHitType=TRACE_HitFloor; - Results->HitTexture = CurSector->GetTexture(sector_t::floor); - } - else goto cont1; - - // the trace hit a 3D-floor before the thing. - // Calculate an intersection and abort. - Results->Sector = §ors[CurSector->sectornum]; - if (!CheckSectorPlane(CurSector, Results->HitType == TRACE_HitFloor)) - { - Results->HitType = TRACE_HitNone; - } - if (TraceCallback != NULL) - { - switch (TraceCallback(*Results, TraceCallbackData)) + if (hitz <= bf || hitz >= bc) { - case TRACE_Continue: return true; - case TRACE_Stop: return false; - case TRACE_Abort: Results->HitType = TRACE_HitNone; return false; - case TRACE_Skip: Results->HitType = TRACE_HitNone; return true; + Results->HitType = TRACE_HitWall; + Results->Tier = + hitz <= bf ? TIER_Lower : + hitz >= bc ? TIER_Upper : TIER_Middle; + } + else + { + Results->HitType = TRACE_HitNone; } } - else + if (Results->HitType == TRACE_HitWall && TraceFlags & TRACE_Impact) { - return false; + P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); } } -cont1: - Results->HitType = TRACE_HitActor; - Results->X = hitx; - Results->Y = hity; - Results->Z = hitz; - Results->Distance = dist; - Results->Fraction = in->frac; - Results->Actor = in->d.thing; + if (Results->HitType == TRACE_HitWall) + { + Results->HitPos = { hitx, hity, hitz }; + SetSourcePosition(); + Results->Distance = dist; + Results->Fraction = in->frac; + Results->Line = in->d.line; + Results->Side = lineside; + } + } + if (TraceCallback != NULL && Results->HitType != TRACE_HitNone) + { + switch (TraceCallback(*Results, TraceCallbackData)) + { + case TRACE_Stop: return false; + case TRACE_Abort: Results->HitType = TRACE_HitNone; return false; + case TRACE_Skip: Results->HitType = TRACE_HitNone; break; + default: break; + } + } + + if (Results->HitType == TRACE_HitNone) + { + CurSector = entersector; + EnterDist = dist; + return true; + } + else + { + return false; + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +bool FTraceInfo::ThingCheck(intercept_t *in) +{ + fixed_t dist = FixedMul(MaxDist, in->frac); + fixed_t hitx = StartX + FixedMul(Vx, dist); + fixed_t hity = StartY + FixedMul(Vy, dist); + fixed_t hitz = StartZ + FixedMul(Vz, dist); + + if (hitz > in->d.thing->Top()) + { + // trace enters above actor + if (Vz >= 0) return true; // Going up: can't hit + + // Does it hit the top of the actor? + dist = FixedDiv(in->d.thing->Top() - StartZ, Vz); + + if (dist > MaxDist) return true; + in->frac = FixedDiv(dist, MaxDist); + + hitx = StartX + FixedMul(Vx, dist); + hity = StartY + FixedMul(Vy, dist); + hitz = StartZ + FixedMul(Vz, dist); + + // calculated coordinate is outside the actor's bounding box + if (abs(hitx - in->d.thing->X()) > in->d.thing->radius || + abs(hity - in->d.thing->Y()) > in->d.thing->radius) return true; + } + else if (hitz < in->d.thing->Z()) + { // trace enters below actor + if (Vz <= 0) return true; // Going down: can't hit + + // Does it hit the bottom of the actor? + dist = FixedDiv(in->d.thing->Z() - StartZ, Vz); + if (dist > MaxDist) return true; + in->frac = FixedDiv(dist, MaxDist); + + hitx = StartX + FixedMul(Vx, dist); + hity = StartY + FixedMul(Vy, dist); + hitz = StartZ + FixedMul(Vz, dist); + + // calculated coordinate is outside the actor's bounding box + if (abs(hitx - in->d.thing->X()) > in->d.thing->radius || + abs(hity - in->d.thing->Y()) > in->d.thing->radius) return true; + } + + if (CurSector->e->XFloor.ffloors.Size()) + { + // check for 3D floor hits first. + fixed_t ff_floor = CurSector->floorplane.ZatPoint(hitx, hity); + fixed_t ff_ceiling = CurSector->ceilingplane.ZatPoint(hitx, hity); + + if (hitz > ff_ceiling && CurSector->PortalBlocksMovement(sector_t::ceiling)) // actor is hit above the current ceiling + { + Results->HitType = TRACE_HitCeiling; + Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); + } + else if (hitz < ff_floor && CurSector->PortalBlocksMovement(sector_t::floor)) // actor is hit below the current floor + { + Results->HitType = TRACE_HitFloor; + Results->HitTexture = CurSector->GetTexture(sector_t::floor); + } + else goto cont1; + + // the trace hit a 3D floor before the thing. + // Calculate an intersection and abort. + Results->Sector = §ors[CurSector->sectornum]; + if (!CheckSectorPlane(CurSector, Results->HitType == TRACE_HitFloor)) + { + Results->HitType = TRACE_HitNone; + } if (TraceCallback != NULL) { switch (TraceCallback(*Results, TraceCallbackData)) { - case TRACE_Stop: return false; - case TRACE_Abort: Results->HitType = TRACE_HitNone; return false; - case TRACE_Skip: Results->HitType = TRACE_HitNone; break; - default: break; + case TRACE_Continue: return true; + case TRACE_Stop: return false; + case TRACE_Abort: Results->HitType = TRACE_HitNone; return false; + case TRACE_Skip: Results->HitType = TRACE_HitNone; return true; } } else @@ -639,9 +775,160 @@ cont1: return false; } } - return true; +cont1: + + + Results->HitType = TRACE_HitActor; + Results->HitPos = { hitx, hity, hitz }; + SetSourcePosition(); + Results->Distance = dist; + Results->Fraction = in->frac; + Results->Actor = in->d.thing; + + if (TraceCallback != NULL) + { + switch (TraceCallback(*Results, TraceCallbackData)) + { + case TRACE_Stop: return false; + case TRACE_Abort: Results->HitType = TRACE_HitNone; return false; + case TRACE_Skip: Results->HitType = TRACE_HitNone; return true; + default: return true; + } + } + else + { + return false; + } } +//========================================================================== +// +// +// +//========================================================================== + +bool FTraceInfo::TraceTraverse (int ptflags) +{ + // Do a 3D floor check in the starting sector + Setup3DFloors(); + + FPathTraverse it(StartX, StartY, FixedMul (Vx, MaxDist), FixedMul (Vy, MaxDist), ptflags | PT_DELTA, startfrac); + intercept_t *in; + int lastsplashsector = -1; + + while ((in = it.Next())) + { + // Deal with splashes in 3D floors (but only run once per sector, not each iteration - and stop if something was found.) + if (Results->Crossed3DWater == NULL && lastsplashsector != CurSector->sectornum) + { + for (auto rover : CurSector->e->XFloor.ffloors) + { + if ((rover->flags & FF_EXISTS) && (rover->flags&FF_SWIMMABLE)) + { + if (Check3DFloorPlane(rover, false)) + { + Results->Crossed3DWater = rover; + Results->Crossed3DWaterPos = Results->HitPos; + } + } + } + lastsplashsector = CurSector->sectornum; + } + + // We have something closer in the storage for portal subtraces. + if (TempResults->HitType != TRACE_HitNone && in->frac > TempResults->Fraction) + { + break; + } + else if (in->isaline) + { + bool res = LineCheck(in); + if (aimdir == INT_MAX) return res; // signal for immediate abort + if (!res) break; + } + else if ((in->d.thing->flags & ActorMask) && in->d.thing != IgnoreThis) + { + if (!ThingCheck(in)) break; + } + } + + // Check if one subtrace through a portal yielded a better result. + if (TempResults->HitType != TRACE_HitNone && + (Results->HitType == TRACE_HitNone || Results->Fraction > TempResults->Fraction)) + { + // We still need to do a water check here or this may get missed on occasion + if (Results->CrossedWater == NULL && + CurSector->heightsec != NULL && + CurSector->heightsec->floorplane.ZatPoint(Results->HitPos) >= Results->HitPos.z) + { + // Save the result so that the water check doesn't destroy it. + FTraceResults *res = Results; + FTraceResults hsecResult; + Results = &hsecResult; + + if (CheckSectorPlane(CurSector->heightsec, true)) + { + Results->CrossedWater = §ors[CurSector->sectornum]; + Results->CrossedWaterPos = Results->HitPos; + } + Results = res; + } + + Results->CopyIfCloser(TempResults); + return true; + } + else if (Results->HitType == TRACE_HitNone) + { + if (CurSector->PortalBlocksMovement(sector_t::floor) && CheckSectorPlane(CurSector, true)) + { + Results->HitType = TRACE_HitFloor; + Results->HitTexture = CurSector->GetTexture(sector_t::floor); + } + else if (CurSector->PortalBlocksMovement(sector_t::ceiling) && CheckSectorPlane(CurSector, false)) + { + Results->HitType = TRACE_HitCeiling; + Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); + } + } + + // check for intersection with floor/ceiling + Results->Sector = §ors[CurSector->sectornum]; + + if (Results->CrossedWater == NULL && + CurSector->heightsec != NULL && + CurSector->heightsec->floorplane.ZatPoint(Results->HitPos) >= Results->HitPos.z) + { + // Save the result so that the water check doesn't destroy it. + FTraceResults *res = Results; + FTraceResults hsecResult; + Results = &hsecResult; + + if (CheckSectorPlane(CurSector->heightsec, true)) + { + Results->CrossedWater = §ors[CurSector->sectornum]; + Results->CrossedWaterPos = Results->HitPos; + } + Results = res; + } + if (Results->HitType == TRACE_HitNone && Results->Distance == 0) + { + Results->HitPos = { + StartX + FixedMul(Vx, MaxDist), + StartY + FixedMul(Vy, MaxDist), + StartZ + FixedMul(Vz, MaxDist) }; + SetSourcePosition(); + Results->Distance = MaxDist; + Results->Fraction = FRACUNIT; + } + return Results->HitType != TRACE_HitNone; +} + +//========================================================================== +// +// +// +//========================================================================== + bool FTraceInfo::CheckPlane (const secplane_t &plane) { fixed_t den = TMulScale16 (plane.a, Vx, plane.b, Vy, plane.c, Vz); @@ -656,9 +943,11 @@ bool FTraceInfo::CheckPlane (const secplane_t &plane) if (hitdist > EnterDist && hitdist < MaxDist) { - Results->X = StartX + FixedMul (Vx, hitdist); - Results->Y = StartY + FixedMul (Vy, hitdist); - Results->Z = StartZ + FixedMul (Vz, hitdist); + Results->HitPos = { + StartX + FixedMul(Vx, hitdist), + StartY + FixedMul(Vy, hitdist), + StartZ + FixedMul(Vz, hitdist) }; + SetSourcePosition(); Results->Distance = hitdist; Results->Fraction = FixedDiv (hitdist, MaxDist); return true; @@ -667,37 +956,11 @@ bool FTraceInfo::CheckPlane (const secplane_t &plane) return false; } -bool FTraceInfo::CheckSectorPlane (const sector_t *sector, bool checkFloor) -{ - secplane_t plane; - - if (checkFloor) - { - plane = sector->floorplane; - } - else - { - plane = sector->ceilingplane; - } - - return CheckPlane(plane); -} - -bool FTraceInfo::Check3DFloorPlane (const F3DFloor *ffloor, bool checkBottom) -{ - secplane_t plane; - - if (checkBottom) - { - plane = *(ffloor->bottom.plane); - } - else - { - plane = *(ffloor->top.plane); - } - - return CheckPlane(plane); -} +//========================================================================== +// +// +// +//========================================================================== static bool EditTraceResult (DWORD flags, FTraceResults &res) { diff --git a/src/p_trace.h b/src/p_trace.h index 1b08158333..43203f035b 100644 --- a/src/p_trace.h +++ b/src/p_trace.h @@ -36,6 +36,7 @@ #include #include "actor.h" +#include "cmdlib.h" #include "textures/textures.h" struct sector_t; @@ -64,23 +65,47 @@ struct FTraceResults { sector_t *Sector; FTextureID HitTexture; - fixed_t X, Y, Z; + fixedvec3 HitPos; + fixedvec3 HitVector; fixedvec3 SrcFromTarget; angle_t SrcAngleToTarget; fixed_t Distance; fixed_t Fraction; - + AActor *Actor; // valid if hit an actor line_t *Line; // valid if hit a line BYTE Side; BYTE Tier; + bool unlinked; // passed through a portal without static offset. ETraceResult HitType; - sector_t *CrossedWater; // For Boom-style, Transfer_Heights-based deep water - F3DFloor *Crossed3DWater; // For 3D floor-based deep water F3DFloor *ffloor; + + sector_t *CrossedWater; // For Boom-style, Transfer_Heights-based deep water + fixedvec3 CrossedWaterPos; // remember the position so that we can use it for spawning the splash + F3DFloor *Crossed3DWater; // For 3D floor-based deep water + fixedvec3 Crossed3DWaterPos; + + void CopyIfCloser(FTraceResults *other) + { + if (other->Distance < Distance || HitType == TRACE_HitNone) + { + memcpy(this, other, myoffsetof(FTraceResults, CrossedWater)); + } + if (CrossedWater == NULL && other->CrossedWater != NULL) + { + CrossedWater = other->CrossedWater; + CrossedWaterPos = other->CrossedWaterPos; + } + if (Crossed3DWater == NULL && other->Crossed3DWater != NULL) + { + Crossed3DWater = other->Crossed3DWater; + Crossed3DWaterPos = other->Crossed3DWaterPos; + } + } }; + enum { diff --git a/src/portal.cpp b/src/portal.cpp index 6f0baeaea0..a6446a82b6 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -122,15 +122,27 @@ static void BuildBlockmap() PortalBlockmap.containsLines = true; block.portallines.Push(ld); block.neighborContainsLines = true; + if (ld->getPortal()->mType == PORTT_LINKED) block.containsLinkedPortals = true; if (x > 0) PortalBlockmap(x - 1, y).neighborContainsLines = true; if (y > 0) PortalBlockmap(x, y - 1).neighborContainsLines = true; if (x < PortalBlockmap.dx - 1) PortalBlockmap(x + 1, y).neighborContainsLines = true; if (y < PortalBlockmap.dy - 1) PortalBlockmap(x, y + 1).neighborContainsLines = true; } + else + { + + bool yes = ld->frontsector->PortalIsLinked(sector_t::ceiling) || ld->frontsector->PortalIsLinked(sector_t::floor); + if (ld->backsector) + { + yes |= ld->backsector->PortalIsLinked(sector_t::ceiling) || ld->backsector->PortalIsLinked(sector_t::floor); + } + block.containsLinkedPortals |= yes; + PortalBlockmap.hasLinkedSectorPortals |= yes; + + } } } } - if (!PortalBlockmap.containsLines) PortalBlockmap.Clear(); } //=========================================================================== @@ -1066,6 +1078,34 @@ void P_CreateLinkedPortals() { sectors[i].CheckPortalPlane(sector_t::floor); sectors[i].CheckPortalPlane(sector_t::ceiling); + // set a flag on each line connecting to a plane portal sector. This is used to reduce the amount of checks in P_CheckSight. + if (sectors[i].PortalIsLinked(sector_t::floor) || sectors[i].PortalIsLinked(sector_t::ceiling)) + { + for (int j = 0; j < sectors[i].linecount; j++) + { + sectors[i].lines[j]->flags |= ML_PORTALCONNECT; + } + } + if (sectors[i].PortalIsLinked(sector_t::ceiling) && sectors[i].PortalIsLinked(sector_t::floor)) + { + fixed_t cz = sectors[i].SkyBoxes[sector_t::ceiling]->threshold; + fixed_t fz = sectors[i].SkyBoxes[sector_t::floor]->threshold; + if (cz < fz) + { + // This is a fatal condition. We have to remove one of the two portals. Choose the one that doesn't match the current plane + Printf("Error in sector %d: Ceiling portal at z=%d is below floor portal at z=%d\n", i, cz, fz); + fixed_t cp = sectors[i].ceilingplane.Zat0(); + fixed_t fp = sectors[i].ceilingplane.Zat0(); + if (cp < fp || fz == fp) + { + sectors[i].SkyBoxes[sector_t::ceiling] = NULL; + } + else + { + sectors[i].SkyBoxes[sector_t::floor] = NULL; + } + } + } } //BuildBlockmap(); } @@ -1078,11 +1118,6 @@ void P_CreateLinkedPortals() // //============================================================================ - -static bool ProcessLayer() -{ -} - 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 diff --git a/src/portal.h b/src/portal.h index b8a5d3ba38..d5a781f2d0 100644 --- a/src/portal.h +++ b/src/portal.h @@ -78,11 +78,13 @@ extern FDisplacementTable Displacements; struct FPortalBlock { bool neighborContainsLines; // this is for skipping the traverser and exiting early if we can quickly decide that there's no portals nearby. + bool containsLinkedPortals; // this is for sight check optimization. We can't early-out on an impenetrable line if there may be portals being found in the same block later on. TArray portallines; FPortalBlock() { neighborContainsLines = false; + containsLinkedPortals = false; } }; @@ -91,6 +93,8 @@ struct FPortalBlockmap TArray data; int dx, dy; bool containsLines; + bool hasLinkedSectorPortals; // global flag to shortcut portal checks if the map has none. + bool hasLinkedPolyPortals; // this means that any early-outs in P_CheckSight need to be disabled if a block contains polyobjects. void Create(int blockx, int blocky) { @@ -105,6 +109,8 @@ struct FPortalBlockmap data.ShrinkToFit(); dx = dy = 0; containsLines = false; + hasLinkedPolyPortals = false; + hasLinkedSectorPortals = false; } FPortalBlock &operator()(int x, int y) diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index 4befded77e..5351c722e6 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -101,7 +101,7 @@ WORD MirrorFlags; TArray WallPortals(1000); // note: this array needs to go away as reallocation can cause crashes. -static subsector_t *InSubsector; +subsector_t *InSubsector; CVAR (Bool, r_drawflat, false, 0) // [RH] Don't texture segs? diff --git a/src/r_defs.h b/src/r_defs.h index 9300ce71f5..c1e6b5d750 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -216,7 +216,6 @@ public: // class DSectorEffect; struct sector_t; -struct line_t; struct FRemapTable; enum @@ -822,7 +821,7 @@ struct sector_t 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)); + return !!(planes[plane].Flags & (PLANEF_NORENDER | PLANEF_NOPASS | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); } bool PortalBlocksMovement(int plane) @@ -837,6 +836,11 @@ struct sector_t return !!(planes[plane].Flags & (PLANEF_BLOCKSOUND | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); } + bool PortalIsLinked(int plane) + { + return (SkyBoxes[plane] != NULL && SkyBoxes[plane]->special1 == SKYBOX_LINKEDPORTAL); + } + // These may only be called if the portal has been validated fixedvec2 FloorDisplacement() { @@ -1375,6 +1379,11 @@ inline sector_t *P_PointInSector(fixed_t x, fixed_t y) return P_PointInSubsector(x, y)->sector; } +inline fixedvec3 AActor::PosRelative(int portalgroup) const +{ + return __pos + Displacements.getOffset(Sector->PortalGroup, portalgroup); +} + inline fixedvec3 AActor::PosRelative(const AActor *other) const { return __pos + Displacements.getOffset(Sector->PortalGroup, other->Sector->PortalGroup); diff --git a/src/r_main.cpp b/src/r_main.cpp index 43c22b0f99..44ed781bbe 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -86,6 +86,8 @@ static void R_ShutdownRenderer(); extern short *openings; extern bool r_fakingunderwater; extern "C" int fuzzviewheight; +extern subsector_t *InSubsector; +extern bool r_showviewer; // PRIVATE DATA DECLARATIONS ----------------------------------------------- @@ -93,7 +95,6 @@ extern "C" int fuzzviewheight; static float CurrentVisibility = 8.f; static fixed_t MaxVisForWall; static fixed_t MaxVisForFloor; -extern bool r_showviewer; bool r_dontmaplines; // PUBLIC DATA DEFINITIONS ------------------------------------------------- @@ -772,6 +773,7 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) memcpy (ceilingclip + pds->x1, &pds->ceilingclip[0], pds->len*sizeof(*ceilingclip)); memcpy (floorclip + pds->x1, &pds->floorclip[0], pds->len*sizeof(*floorclip)); + InSubsector = NULL; R_RenderBSPNode (nodes + numnodes - 1); R_3D_ResetClip(); // reset clips (floor/ceiling) @@ -916,6 +918,7 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) } // Link the polyobjects right before drawing the scene to reduce the amounts of calls to this function PO_LinkToSubsectors(); + InSubsector = NULL; R_RenderBSPNode (nodes + numnodes - 1); // The head node is the last node output. R_3D_ResetClip(); // reset clips (floor/ceiling) camera->renderflags = savedflags; diff --git a/src/r_plane.cpp b/src/r_plane.cpp index 4f906b1b60..15653a1e89 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -66,6 +66,8 @@ //EXTERN_CVAR (Int, tx) //EXTERN_CVAR (Int, ty) +extern subsector_t *InSubsector; + static void R_DrawSkyStriped (visplane_t *pl); planefunction_t floorfunc; @@ -1314,6 +1316,7 @@ void R_DrawSkyBoxes () viewzStack.Push (viewz); visplaneStack.Push (pl); + InSubsector = NULL; R_RenderBSPNode (nodes + numnodes - 1); R_3D_ResetClip(); // reset clips (floor/ceiling) R_DrawPlanes (); diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 7be6e0f825..3a4f9f8b1c 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -927,6 +927,7 @@ void R_SetupFrame (AActor *actor) player_t *player = actor->player; unsigned int newblend; InterpolationViewer *iview; + bool unlinked = false; if (player != NULL && player->mo == actor) { // [RH] Use camera instead of viewplayer @@ -962,9 +963,22 @@ void R_SetupFrame (AActor *actor) if (player != NULL && gamestate != GS_TITLELEVEL && ((player->cheats & CF_CHASECAM) || (r_deathcamera && camera->health <= 0))) { + sector_t *oldsector = R_PointInSubsector(iview->oviewx, iview->oviewy)->sector; // [RH] Use chasecam view - P_AimCamera (camera, iview->nviewx, iview->nviewy, iview->nviewz, viewsector); + P_AimCamera (camera, iview->nviewx, iview->nviewy, iview->nviewz, viewsector, unlinked); r_showviewer = true; + // Interpolating this is a very complicated thing because nothing keeps track of the aim camera's movement, so whenever we detect a portal transition + // it's probably best to just reset the interpolation for this move. + // Note that this can still cause problems with unusually linked portals + if (viewsector->PortalGroup != oldsector->PortalGroup || (unlinked && P_AproxDistance(iview->oviewx - iview->nviewx, iview->oviewy - iview->nviewy) > 256 * FRACUNIT)) + { + iview->otic = nowtic; + iview->oviewx = iview->nviewx; + iview->oviewy = iview->nviewy; + iview->oviewz = iview->nviewz; + iview->oviewpitch = iview->nviewpitch; + iview->oviewangle = iview->nviewangle; + } } else { diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index db1f722f1d..4cdf31747b 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2913,11 +2913,16 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetScale) PARAM_FIXED (scalex); PARAM_FIXED_OPT (scaley) { scaley = scalex; } PARAM_INT_OPT (ptr) { ptr = AAPTR_DEFAULT; } + PARAM_BOOL_OPT (usezero) { usezero = false; } AActor *ref = COPY_AAPTR(self, ptr); if (ref != NULL) { + if (scaley == 0 && !usezero) + { + scaley = scalex; + } ref->scaleX = scalex; ref->scaleY = scaley; } @@ -5178,7 +5183,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) if ((0 && dpuff->flags3 & MF3_PUFFONACTORS) || !spawnblood) { spawnblood = false; - P_SpawnPuff(self, pufftype, bloodpos, angle, 0); + P_SpawnPuff(self, pufftype, bloodpos, angle, angle, 0); } } else if (self->target->flags3 & MF3_GHOST) diff --git a/tools/zipdir/zipdir.c b/tools/zipdir/zipdir.c index b331021d1f..cb43f5f825 100644 --- a/tools/zipdir/zipdir.c +++ b/tools/zipdir/zipdir.c @@ -232,6 +232,9 @@ int Quiet; // PRIVATE DATA DEFINITIONS ------------------------------------------------ +static const UINT32 centralfile = ZIP_CENTRALFILE; +static const UINT32 endofdir = ZIP_ENDOFDIR; + static int no_mem; static ISzAlloc Alloc = { SzAlloc, SzFree }; @@ -1305,7 +1308,8 @@ int compress_ppmd(Byte *out, unsigned int *outlen, const Byte *in, unsigned int return -1; } - *(short *)out = LittleShort((maxorder - 1) + ((sasize - 1) << 4) + (cutoff << 12)); + const short outval = LittleShort((maxorder - 1) + ((sasize - 1) << 4) + (cutoff << 12)); + memcpy(out, (const Byte *)&outval, sizeof(short)); *outlen = *outlen - ppsout.buffersize; return 0; } @@ -1420,12 +1424,12 @@ BYTE *find_central_dir(FILE *fin) free(dir); return NULL; } - if (*(UINT32 *)dir != ZIP_CENTRALFILE) + if (memcmp(dir, (const BYTE *)¢ralfile, sizeof(UINT32)) != 0) { free(dir); return NULL; } - *(UINT32 *)(dir + LittleLong(eod.DirectorySize)) = ZIP_ENDOFDIR; + memcpy(dir + LittleLong(eod.DirectorySize), (const BYTE *)&endofdir, sizeof(UINT32)); return dir; } @@ -1444,7 +1448,7 @@ CentralDirectoryEntry *find_file_in_zip(BYTE *dir, const char *path, unsigned in CentralDirectoryEntry *ent; int flags; - while (*(UINT32 *)dir == ZIP_CENTRALFILE) + while (memcmp(dir, (const BYTE *)¢ralfile, sizeof(UINT32)) == 0) { ent = (CentralDirectoryEntry *)dir; if (pathlen == LittleShort(ent->NameLength) && @@ -1455,7 +1459,7 @@ CentralDirectoryEntry *find_file_in_zip(BYTE *dir, const char *path, unsigned in } dir += sizeof(*ent) + LittleShort(ent->NameLength) + LittleShort(ent->ExtraLength) + LittleShort(ent->CommentLength); } - if (*(UINT32 *)dir != ZIP_CENTRALFILE) + if (memcmp(dir, (const BYTE *)¢ralfile, sizeof(UINT32)) != 0) { return NULL; } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index d8f20dfe5c..b3401e8347 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -207,7 +207,7 @@ ACTOR Actor native //: Thinker action native A_FadeIn(float reduce = 0.1, int flags = 0); action native A_FadeOut(float reduce = 0.1, int flags = 1); //bool remove == true action native A_FadeTo(float target, float amount = 0.1, int flags = 0); - action native A_SetScale(float scalex, float scaley = 0, int ptr = AAPTR_DEFAULT); + action native A_SetScale(float scalex, float scaley = 0, int ptr = AAPTR_DEFAULT, bool usezero = false); action native A_SetMass(int mass); action native A_SpawnDebris(class spawntype, bool transfer_translation = false, float mult_h = 1, float mult_v = 1); action native A_SpawnParticle(color color1, int flags = 0, int lifetime = 35, int size = 1, float angle = 0, float xoff = 0, float yoff = 0, float zoff = 0, float velx = 0, float vely = 0, float velz = 0, float accelx = 0, float accely = 0, float accelz = 0, float startalphaf = 1, float fadestepf = -1);