Squashed commit of the following:

commit 553c4c12abd15e5f8fa02496ccfde81bdfdb53b0
Merge: cb03de5d2 add4ff12a
Author: nashmuhandes <nashbackslash@gmail.com>
Date:   Mon Feb 14 18:55:52 2022 +0800

    Merge branch 'master' of https://github.com/coelckers/gzdoom into ActorFallAndSink

commit cb03de5d2fdcba8a32b83be32579be935c8b6600
Author: nashmuhandes <nashbackslash@gmail.com>
Date:   Tue Jan 25 12:54:33 2022 +0800

    Re-add WaterDepth to the custom namedef file

commit 8d1ff3c16018dd7c73c8950aa4c51f2bc0207837
Merge: f201e6307 a9eaae074
Author: nashmuhandes <nashbackslash@gmail.com>
Date:   Tue Jan 25 12:52:57 2022 +0800

    Merge branch 'master' of https://github.com/coelckers/gzdoom into ActorFallAndSink

    # Conflicts:
    #	src/common/engine/namedef.h

commit f201e630706fc0ea7628b8a32bd5d88afb712208
Author: nashmuhandes <nashbackslash@gmail.com>
Date:   Sun Jan 16 20:45:27 2022 +0800

    Fix compile error (MAX() -> max())

commit fe292ebd4312afe34b1e35e4c1eafba0dc168920
Merge: 3f9150c4e 12ed24d06
Author: nashmuhandes <nashbackslash@gmail.com>
Date:   Sun Jan 16 20:37:58 2022 +0800

    Merge branch 'master' of https://github.com/coelckers/gzdoom into ActorFallAndSink

    # Conflicts:
    #	src/playsim/p_mobj.cpp

commit 3f9150c4eae1c5c5510e8f9b1c4bfaab46a96b8f
Author: nashmuhandes <nashbackslash@gmail.com>
Date:   Sat May 29 05:32:54 2021 +0800

    - Implement virtual Actor.FallAndSink for scriptable falling and water physics
    - Added Actor.WaterDepth to retrieve how submerged and actor is, in map units

    Original work credited to "gzdoomthrowaway"
This commit is contained in:
nashmuhandes 2022-06-01 00:42:58 +08:00 committed by Christoph Oelckers
parent cda6394a95
commit f235dcc38e
6 changed files with 193 additions and 207 deletions

View file

@ -1583,6 +1583,7 @@ int FLevelLocals::FinishTravel ()
pawn->ceilingpic = pawndup->ceilingpic; pawn->ceilingpic = pawndup->ceilingpic;
pawn->Floorclip = pawndup->Floorclip; pawn->Floorclip = pawndup->Floorclip;
pawn->waterlevel = pawndup->waterlevel; pawn->waterlevel = pawndup->waterlevel;
pawn->waterdepth = pawndup->waterdepth;
} }
else if (failnum == 0) // In the failure case this may run into some undefined data. else if (failnum == 0) // In the failure case this may run into some undefined data.
{ {

View file

@ -497,6 +497,7 @@ xx(Special)
xx(TID) xx(TID)
xx(TIDtoHate) xx(TIDtoHate)
xx(WaterLevel) xx(WaterLevel)
xx(WaterDepth)
xx(MomX) xx(MomX)
xx(MomY) xx(MomY)
xx(MomZ) xx(MomZ)

View file

@ -773,6 +773,10 @@ public:
virtual void Touch(AActor *toucher); virtual void Touch(AActor *toucher);
void CallTouch(AActor *toucher); void CallTouch(AActor *toucher);
// Apply gravity and/or make actor sink in water.
virtual void FallAndSink(double grav, double oldfloorz);
void CallFallAndSink(double grav, double oldfloorz);
// Centaurs and ettins squeal when electrocuted, poisoned, or "holy"-ed // Centaurs and ettins squeal when electrocuted, poisoned, or "holy"-ed
// Made a metadata property so no longer virtual // Made a metadata property so no longer virtual
void Howl (); void Howl ();
@ -1137,6 +1141,7 @@ public:
AActor *inext, **iprev;// Links to other mobjs in same bucket AActor *inext, **iprev;// Links to other mobjs in same bucket
TObjPtr<AActor*> goal; // Monster's goal if not chasing anything TObjPtr<AActor*> goal; // Monster's goal if not chasing anything
int waterlevel; // 0=none, 1=feet, 2=waist, 3=eyes int waterlevel; // 0=none, 1=feet, 2=waist, 3=eyes
double waterdepth; // Stores how deep into water you are, in map units
uint8_t boomwaterlevel; // splash information for non-swimmable water sectors uint8_t boomwaterlevel; // splash information for non-swimmable water sectors
uint8_t MinMissileChance;// [RH] If a random # is > than this, then missile attack. uint8_t MinMissileChance;// [RH] If a random # is > than this, then missile attack.
int8_t LastLookPlayerNumber;// Player number last looked for (if TIDtoHate == 0) int8_t LastLookPlayerNumber;// Player number last looked for (if TIDtoHate == 0)
@ -1275,6 +1280,7 @@ public:
bool IsMapActor(); bool IsMapActor();
int GetTics(FState * newstate); int GetTics(FState * newstate);
bool SetState (FState *newstate, bool nofunction=false); bool SetState (FState *newstate, bool nofunction=false);
int UpdateWaterDepth(bool splash);
virtual void SplashCheck(); virtual void SplashCheck();
virtual bool UpdateWaterLevel (bool splash=true); virtual bool UpdateWaterLevel (bool splash=true);
bool isFast(); bool isFast();

View file

@ -2397,84 +2397,7 @@ void P_ZMovement (AActor *mo, double oldfloorz)
mo->AddZ(mo->Vel.Z); mo->AddZ(mo->Vel.Z);
// mo->CallFallAndSink(grav, oldfloorz);
// apply gravity
//
if (mo->Z() > mo->floorz && !(mo->flags & MF_NOGRAVITY))
{
double startvelz = mo->Vel.Z;
if (mo->waterlevel == 0 || (mo->player &&
!(mo->player->cmd.ucmd.forwardmove | mo->player->cmd.ucmd.sidemove)))
{
// [RH] Double gravity only if running off a ledge. Coming down from
// an upward thrust (e.g. a jump) should not double it.
if (mo->Vel.Z == 0 && oldfloorz > mo->floorz && mo->Z() == oldfloorz)
{
mo->Vel.Z -= grav + grav;
}
else
{
mo->Vel.Z -= grav;
}
}
if (mo->player == NULL)
{
if (mo->waterlevel >= 1)
{
double sinkspeed;
if ((mo->flags & MF_SPECIAL) && !(mo->flags3 & MF3_ISMONSTER))
{ // Pickup items don't sink if placed and drop slowly if dropped
sinkspeed = (mo->flags & MF_DROPPED) ? -WATER_SINK_SPEED / 8 : 0;
}
else
{
sinkspeed = -WATER_SINK_SPEED;
// If it's not a player, scale sinkspeed by its mass, with
// 100 being equivalent to a player.
if (mo->player == NULL)
{
sinkspeed = sinkspeed * clamp(mo->Mass, 1, 4000) / 100;
}
}
if (mo->Vel.Z < sinkspeed)
{ // Dropping too fast, so slow down toward sinkspeed.
mo->Vel.Z -= max(sinkspeed*2, -8.);
if (mo->Vel.Z > sinkspeed)
{
mo->Vel.Z = sinkspeed;
}
}
else if (mo->Vel.Z > sinkspeed)
{ // Dropping too slow/going up, so trend toward sinkspeed.
mo->Vel.Z = startvelz + max(sinkspeed/3, -8.);
if (mo->Vel.Z < sinkspeed)
{
mo->Vel.Z = sinkspeed;
}
}
}
}
else
{
if (mo->waterlevel > 1)
{
double sinkspeed = -WATER_SINK_SPEED;
if (mo->Vel.Z < sinkspeed)
{
mo->Vel.Z = (startvelz < sinkspeed) ? startvelz : sinkspeed;
}
else
{
mo->Vel.Z = startvelz + ((mo->Vel.Z - startvelz) *
(mo->waterlevel == 1 ? WATER_SINK_SMALL_FACTOR : WATER_SINK_FACTOR));
}
}
}
}
// Hexen compatibility handling for floatbobbing. Ugh... // Hexen compatibility handling for floatbobbing. Ugh...
// Hexen yanked all items to the floor, except those being spawned at map start in the air. // Hexen yanked all items to the floor, except those being spawned at map start in the air.
@ -2808,7 +2731,112 @@ static void PlayerLandedOnThing (AActor *mo, AActor *onmobj)
// mo->player->centering = true; // mo->player->centering = true;
} }
//==========================================================================
//
// AActor :: FallAndSink
//
//==========================================================================
void AActor::FallAndSink(double grav, double oldfloorz)
{
if (Z() > floorz && !(flags & MF_NOGRAVITY))
{
double startvelz = Vel.Z;
if (waterlevel == 0 || (player &&
!(player->cmd.ucmd.forwardmove | player->cmd.ucmd.sidemove)))
{
// [RH] Double gravity only if running off a ledge. Coming down from
// an upward thrust (e.g. a jump) should not double it.
if (Vel.Z == 0 && oldfloorz > floorz && Z() == oldfloorz)
{
Vel.Z -= grav + grav;
}
else
{
Vel.Z -= grav;
}
}
if (player == NULL)
{
if (waterlevel >= 1)
{
double sinkspeed;
if ((flags & MF_SPECIAL) && !(flags3 & MF3_ISMONSTER))
{ // Pickup items don't sink if placed and drop slowly if dropped
sinkspeed = (flags & MF_DROPPED) ? -WATER_SINK_SPEED / 8 : 0;
}
else
{
sinkspeed = -WATER_SINK_SPEED;
// If it's not a player, scale sinkspeed by its mass, with
// 100 being equivalent to a player.
if (player == NULL)
{
sinkspeed = sinkspeed * clamp(Mass, 1, 4000) / 100;
}
}
if (Vel.Z < sinkspeed)
{ // Dropping too fast, so slow down toward sinkspeed.
Vel.Z -= max(sinkspeed * 2, -8.);
if (Vel.Z > sinkspeed)
{
Vel.Z = sinkspeed;
}
}
else if (Vel.Z > sinkspeed)
{ // Dropping too slow/going up, so trend toward sinkspeed.
Vel.Z = startvelz + max(sinkspeed / 3, -8.);
if (Vel.Z < sinkspeed)
{
Vel.Z = sinkspeed;
}
}
}
}
else
{
if (waterlevel > 1)
{
double sinkspeed = -WATER_SINK_SPEED;
if (Vel.Z < sinkspeed)
{
Vel.Z = (startvelz < sinkspeed) ? startvelz : sinkspeed;
}
else
{
Vel.Z = startvelz + ((Vel.Z - startvelz) *
(waterlevel == 1 ? WATER_SINK_SMALL_FACTOR : WATER_SINK_FACTOR));
}
}
}
}
}
DEFINE_ACTION_FUNCTION(AActor, FallAndSink)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_FLOAT(grav);
PARAM_FLOAT(oldfloorz);
self->FallAndSink(grav, oldfloorz);
return 0;
}
void AActor::CallFallAndSink(double grav, double oldfloorz)
{
IFVIRTUAL(AActor, FallAndSink)
{
VMValue params[3] = { (DObject*)this, grav, oldfloorz };
VMCall(func, params, 3, nullptr, 0);
}
else
{
FallAndSink(grav, oldfloorz);
}
}
// //
// P_NightmareRespawn // P_NightmareRespawn
@ -4233,27 +4261,32 @@ void AActor::CheckSectorTransition(sector_t *oldsec)
//========================================================================== //==========================================================================
// //
// AActor::SplashCheck // AActor::UpdateWaterDepth
// //
// Returns true if actor should splash // Updates the actor's current waterlevel and waterdepth.
// Consolidates common code in UpdateWaterLevel and SplashCheck.
//
// Returns the floor height used for the depth check, or -FLT_MAX
// if the actor wasn't in a sector.
// //
//========================================================================== //==========================================================================
void AActor::SplashCheck() int AActor::UpdateWaterDepth(bool splash)
{ {
double fh = -FLT_MAX; double fh = -FLT_MAX;
bool reset = false; bool reset = false;
waterlevel = 0; waterlevel = 0;
waterdepth = 0;
if (Sector == NULL) if (Sector == NULL)
{ {
return; return fh;
} }
if (Sector->MoreFlags & SECMF_UNDERWATER) // intentionally not SECMF_UNDERWATERMASK if (Sector->MoreFlags & SECMF_UNDERWATER) // intentionally not SECMF_UNDERWATERMASK
{ {
waterlevel = 3; waterdepth = Height;
} }
else else
{ {
@ -4261,28 +4294,16 @@ void AActor::SplashCheck()
if (hsec != NULL) if (hsec != NULL)
{ {
fh = hsec->floorplane.ZatPoint(this); fh = hsec->floorplane.ZatPoint(this);
//if (hsec->MoreFlags & SECMF_UNDERWATERMASK) // also check Boom-style non-swimmable sectors
// splash checks also check Boom-style non-swimmable sectors
// as well as non-solid, visible 3D floors (below)
if (splash || hsec->MoreFlags & SECMF_UNDERWATERMASK)
{ {
if (Z() < fh) waterdepth = fh - Z();
if (waterdepth <= 0 && !(hsec->MoreFlags & SECMF_FAKEFLOORONLY) && (Top() > hsec->ceilingplane.ZatPoint(this)))
{ {
waterlevel = 1; waterdepth = Height;
if (Center() < fh)
{
waterlevel = 2;
if ((player && Z() + player->viewheight <= fh) ||
(Top() <= fh))
{
waterlevel = 3;
}
}
}
else if (!(hsec->MoreFlags & SECMF_FAKEFLOORONLY) && (Top() > hsec->ceilingplane.ZatPoint(this)))
{
waterlevel = 3;
}
else
{
waterlevel = 0;
} }
} }
} }
@ -4295,32 +4316,56 @@ void AActor::SplashCheck()
if (rover->flags & FF_SOLID) continue; if (rover->flags & FF_SOLID) continue;
bool reset = !(rover->flags & FF_SWIMMABLE); bool reset = !(rover->flags & FF_SWIMMABLE);
if (reset && rover->alpha == 0) continue; if (splash) { reset &= rover->alpha == 0; }
if (reset) continue;
double ff_bottom = rover->bottom.plane->ZatPoint(this); double ff_bottom = rover->bottom.plane->ZatPoint(this);
double ff_top = rover->top.plane->ZatPoint(this); double ff_top = rover->top.plane->ZatPoint(this);
if (ff_top <= Z() || ff_bottom > (Center())) continue; if (ff_top <= Z() || ff_bottom > Center()) continue;
fh = ff_top; fh = ff_top;
if (Z() < fh) waterdepth = ff_top - Z();
{
waterlevel = 1;
if (Center() < fh)
{
waterlevel = 2;
if ((player && Z() + player->viewheight <= fh) ||
(Top() <= fh))
{
waterlevel = 3;
}
}
}
break; break;
} }
} }
} }
if (waterdepth < 0) { waterdepth = 0; }
if (waterdepth > (Height / 2))
{
// When noclipping around and going from low to high sector, your view height
// can go negative, which is why this is nested inside here
if ((player && (waterdepth >= player->viewheight)) || (waterdepth >= Height))
{
waterlevel = 3;
}
else
{
waterlevel = 2;
}
}
else if (waterdepth > 0)
{
waterlevel = 1;
}
return fh;
}
//==========================================================================
//
// AActor::SplashCheck
//
// Returns true if actor should splash
//
//==========================================================================
void AActor::SplashCheck()
{
double fh = UpdateWaterDepth(true);
// some additional checks to make deep sectors like Boom's splash without setting // some additional checks to make deep sectors like Boom's splash without setting
// the water flags. // the water flags.
if (boomwaterlevel == 0 && waterlevel != 0) if (boomwaterlevel == 0 && waterlevel != 0)
@ -4341,87 +4386,17 @@ void AActor::SplashCheck()
bool AActor::UpdateWaterLevel(bool dosplash) bool AActor::UpdateWaterLevel(bool dosplash)
{ {
int oldlevel = waterlevel;
if (dosplash) SplashCheck(); if (dosplash) SplashCheck();
double fh = -FLT_MAX; int oldlevel = waterlevel;
bool reset = false; UpdateWaterDepth(false);
waterlevel = 0;
if (Sector != nullptr)
{
if (Sector->MoreFlags & SECMF_UNDERWATER) // intentionally not SECMF_UNDERWATERMASK
{
waterlevel = 3;
}
else
{
const sector_t *hsec = Sector->GetHeightSec();
if (hsec != NULL)
{
fh = hsec->floorplane.ZatPoint(this);
if (hsec->MoreFlags & SECMF_UNDERWATERMASK) // also check Boom-style non-swimmable sectors
{
if (Z() < fh)
{
waterlevel = 1;
if (Center() < fh)
{
waterlevel = 2;
if ((player && Z() + player->viewheight <= fh) ||
(Top() <= fh))
{
waterlevel = 3;
}
}
}
else if (!(hsec->MoreFlags & SECMF_FAKEFLOORONLY) && (Top() > hsec->ceilingplane.ZatPoint(this)))
{
waterlevel = 3;
}
else
{
waterlevel = 0;
}
}
}
else
{
// Check 3D floors as well!
for (auto rover : Sector->e->XFloor.ffloors)
{
if (!(rover->flags & FF_EXISTS)) continue;
if (rover->flags & FF_SOLID) continue;
if (!(rover->flags & FF_SWIMMABLE)) continue;
double ff_bottom = rover->bottom.plane->ZatPoint(this);
double ff_top = rover->top.plane->ZatPoint(this);
if (ff_top <= Z() || ff_bottom > (Center())) continue;
fh = ff_top;
if (Z() < fh)
{
waterlevel = 1;
if (Center() < fh)
{
waterlevel = 2;
if ((player && Z() + player->viewheight <= fh) ||
(Top() <= fh))
{
waterlevel = 3;
}
}
}
break;
}
}
}
// Play surfacing and diving sounds, as appropriate. // Play surfacing and diving sounds, as appropriate.
//
// (used to be that this code was wrapped around a "Sector != nullptr" check,
// but actors should always be within a sector, and besides, this is just
// sound stuff)
if (player != nullptr) if (player != nullptr)
{ {
if (oldlevel < 3 && waterlevel == 3) if (oldlevel < 3 && waterlevel == 3)
@ -4440,7 +4415,7 @@ bool AActor::UpdateWaterLevel(bool dosplash)
// If we were running out of air, then ResetAirSupply() will play *gasp. // If we were running out of air, then ResetAirSupply() will play *gasp.
} }
} }
}
return false; // we did the splash ourselves return false; // we did the splash ourselves
} }

View file

@ -1930,6 +1930,7 @@ DEFINE_FIELD(AActor, special)
DEFINE_FIELD(AActor, tid) DEFINE_FIELD(AActor, tid)
DEFINE_FIELD(AActor, TIDtoHate) DEFINE_FIELD(AActor, TIDtoHate)
DEFINE_FIELD(AActor, waterlevel) DEFINE_FIELD(AActor, waterlevel)
DEFINE_FIELD(AActor, waterdepth)
DEFINE_FIELD(AActor, Score) DEFINE_FIELD(AActor, Score)
DEFINE_FIELD(AActor, accuracy) DEFINE_FIELD(AActor, accuracy)
DEFINE_FIELD(AActor, stamina) DEFINE_FIELD(AActor, stamina)

View file

@ -167,6 +167,7 @@ class Actor : Thinker native
native readonly int TID; native readonly int TID;
native readonly int TIDtoHate; native readonly int TIDtoHate;
native readonly int WaterLevel; native readonly int WaterLevel;
native readonly double WaterDepth;
native int Score; native int Score;
native int Accuracy; native int Accuracy;
native int Stamina; native int Stamina;
@ -484,6 +485,7 @@ class Actor : Thinker native
virtual native void Die(Actor source, Actor inflictor, int dmgflags = 0, Name MeansOfDeath = 'none'); virtual native void Die(Actor source, Actor inflictor, int dmgflags = 0, Name MeansOfDeath = 'none');
virtual native bool Slam(Actor victim); virtual native bool Slam(Actor victim);
virtual native void Touch(Actor toucher); virtual native void Touch(Actor toucher);
virtual native void FallAndSink(double grav, double oldfloorz);
private native void Substitute(Actor replacement); private native void Substitute(Actor replacement);
native ui void DisplayNameTag(); native ui void DisplayNameTag();