in doom_py/src/vizdoom/src/p_map.cpp [1867:2244]
bool P_TryMove(AActor *thing, fixed_t x, fixed_t y,
int dropoff, // killough 3/15/98: allow dropoff as option
const secplane_t *onfloor, // [RH] Let P_TryMove keep the thing on the floor
FCheckPosition &tm,
bool missileCheck) // [GZ] Fired missiles ignore the drop-off test
{
fixedvec3 oldpos;
sector_t *oldsector;
fixed_t oldz;
int side;
int oldside;
line_t* ld;
sector_t* oldsec = thing->Sector; // [RH] for sector actions
sector_t* newsec;
tm.floatok = false;
oldz = thing->Z();
if (onfloor)
{
thing->SetZ(onfloor->ZatPoint(x, y));
}
thing->flags6 |= MF6_INTRYMOVE;
if (!P_CheckPosition(thing, x, y, tm))
{
AActor *BlockingMobj = thing->BlockingMobj;
// Solid wall or thing
if (!BlockingMobj || BlockingMobj->player || !thing->player)
{
goto pushline;
}
else
{
if (BlockingMobj->player || !thing->player)
{
goto pushline;
}
else if (BlockingMobj->Top() - thing->Z() > thing->MaxStepHeight
|| (BlockingMobj->Sector->ceilingplane.ZatPoint(x, y) - (BlockingMobj->Top()) < thing->height)
|| (tm.ceilingz - (BlockingMobj->Top()) < thing->height))
{
goto pushline;
}
}
if (!(tm.thing->flags2 & MF2_PASSMOBJ) || (i_compatflags & COMPATF_NO_PASSMOBJ))
{
thing->SetZ(oldz);
thing->flags6 &= ~MF6_INTRYMOVE;
return false;
}
}
if (thing->flags3 & MF3_FLOORHUGGER)
{
thing->SetZ(tm.floorz);
}
else if (thing->flags3 & MF3_CEILINGHUGGER)
{
thing->SetZ(tm.ceilingz - thing->height);
}
if (onfloor && tm.floorsector == thing->floorsector)
{
thing->SetZ(tm.floorz);
}
if (!(thing->flags & MF_NOCLIP))
{
if (tm.ceilingz - tm.floorz < thing->height)
{
goto pushline; // doesn't fit
}
tm.floatok = true;
if (!(thing->flags & MF_TELEPORT)
&& tm.ceilingz - thing->Z() < thing->height
&& !(thing->flags3 & MF3_CEILINGHUGGER)
&& (!(thing->flags2 & MF2_FLY) || !(thing->flags & MF_NOGRAVITY)))
{
goto pushline; // mobj must lower itself to fit
}
if (thing->flags2 & MF2_FLY && thing->flags & MF_NOGRAVITY)
{
#if 1
if (thing->Top() > tm.ceilingz)
goto pushline;
#else
// When flying, slide up or down blocking lines until the actor
// is not blocked.
if (thing->Top() > tm.ceilingz)
{
thing->velz = -8 * FRACUNIT;
goto pushline;
}
else if (thing->Z() < tm.floorz && tm.floorz - tm.dropoffz > thing->MaxDropOffHeight)
{
thing->velz = 8 * FRACUNIT;
goto pushline;
}
#endif
}
if (!(thing->flags & MF_TELEPORT) && !(thing->flags3 & MF3_FLOORHUGGER))
{
if ((thing->flags & MF_MISSILE) && !(thing->flags6 & MF6_STEPMISSILE) && tm.floorz > thing->Z())
{ // [RH] Don't let normal missiles climb steps
goto pushline;
}
if (tm.floorz - thing->Z() > thing->MaxStepHeight)
{ // too big a step up
goto pushline;
}
else if (thing->Z() < tm.floorz)
{ // [RH] Check to make sure there's nothing in the way for the step up
fixed_t savedz = thing->Z();
bool good;
thing->SetZ(tm.floorz);
good = P_TestMobjZ(thing);
thing->SetZ(savedz);
if (!good)
{
goto pushline;
}
if (thing->flags6 & MF6_STEPMISSILE)
{
thing->SetZ(tm.floorz);
// If moving down, cancel vertical component of the velocity
if (thing->velz < 0)
{
// If it's a bouncer, let it bounce off its new floor, too.
if (thing->BounceFlags & BOUNCE_Floors)
{
thing->FloorBounceMissile(tm.floorsector->floorplane);
}
else
{
thing->velz = 0;
}
}
}
}
}
// compatibility check: Doom originally did not allow monsters to cross dropoffs at all.
// If the compatibility flag is on, only allow this when the velocity comes from a scroller
if ((i_compatflags & COMPATF_CROSSDROPOFF) && !(thing->flags4 & MF4_SCROLLMOVE))
{
dropoff = false;
}
if (dropoff == 2 && // large jump down (e.g. dogs)
(tm.floorz - tm.dropoffz > 128 * FRACUNIT || thing->target == NULL || thing->target->Z() >tm.dropoffz))
{
dropoff = false;
}
// killough 3/15/98: Allow certain objects to drop off
if ((!dropoff && !(thing->flags & (MF_DROPOFF | MF_FLOAT | MF_MISSILE))) || (thing->flags5&MF5_NODROPOFF))
{
if (!(thing->flags5&MF5_AVOIDINGDROPOFF))
{
fixed_t floorz = tm.floorz;
// [RH] If the thing is standing on something, use its current z as the floorz.
// This is so that it does not walk off of things onto a drop off.
if (thing->flags2 & MF2_ONMOBJ)
{
floorz = MAX(thing->Z(), tm.floorz);
}
if (floorz - tm.dropoffz > thing->MaxDropOffHeight &&
!(thing->flags2 & MF2_BLASTED) && !missileCheck)
{ // Can't move over a dropoff unless it's been blasted
// [GZ] Or missile-spawned
thing->SetZ(oldz);
thing->flags6 &= ~MF6_INTRYMOVE;
return false;
}
}
else
{
// special logic to move a monster off a dropoff
// this intentionally does not check for standing on things.
if (thing->floorz - tm.floorz > thing->MaxDropOffHeight ||
thing->dropoffz - tm.dropoffz > thing->MaxDropOffHeight)
{
thing->flags6 &= ~MF6_INTRYMOVE;
return false;
}
}
}
if (thing->flags2 & MF2_CANTLEAVEFLOORPIC
&& (tm.floorpic != thing->floorpic
|| tm.floorz - thing->Z() != 0))
{ // must stay within a sector of a certain floor type
thing->SetZ(oldz);
thing->flags6 &= ~MF6_INTRYMOVE;
return false;
}
//Added by MC: To prevent bot from getting into dangerous sectors.
if (thing->player && thing->player->Bot != NULL && thing->flags & MF_SHOOTABLE)
{
if (tm.sector != thing->Sector
&& bglobal.IsDangerous(tm.sector))
{
thing->player->Bot->prev = thing->player->Bot->dest;
thing->player->Bot->dest = NULL;
thing->velx = 0;
thing->vely = 0;
thing->SetZ(oldz);
thing->flags6 &= ~MF6_INTRYMOVE;
return false;
}
}
}
// [RH] Check status of eyes against fake floor/ceiling in case
// it slopes or the player's eyes are bobbing in and out.
bool oldAboveFakeFloor, oldAboveFakeCeiling;
fixed_t viewheight;
viewheight = thing->player ? thing->player->viewheight : thing->height / 2;
oldAboveFakeFloor = oldAboveFakeCeiling = false; // pacify GCC
if (oldsec->heightsec)
{
fixed_t eyez = oldz + viewheight;
oldAboveFakeFloor = eyez > oldsec->heightsec->floorplane.ZatPoint(thing);
oldAboveFakeCeiling = eyez > oldsec->heightsec->ceilingplane.ZatPoint(thing);
}
// Borrowed from MBF:
if (thing->BounceFlags & BOUNCE_MBF && // killough 8/13/98
!(thing->flags & (MF_MISSILE | MF_NOGRAVITY)) &&
!thing->IsSentient() && tm.floorz - thing->Z() > 16 * FRACUNIT)
{ // too big a step up for MBF bouncers under gravity
thing->flags6 &= ~MF6_INTRYMOVE;
return false;
}
// the move is ok, so link the thing into its new position
thing->UnlinkFromWorld();
oldpos = thing->Pos();
oldsector = thing->Sector;
thing->floorz = tm.floorz;
thing->ceilingz = tm.ceilingz;
thing->dropoffz = tm.dropoffz; // killough 11/98: keep track of dropoffs
thing->floorpic = tm.floorpic;
thing->floorterrain = tm.floorterrain;
thing->floorsector = tm.floorsector;
thing->ceilingpic = tm.ceilingpic;
thing->ceilingsector = tm.ceilingsector;
thing->SetXY(x, y);
thing->LinkToWorld();
if (thing->flags2 & MF2_FLOORCLIP)
{
thing->AdjustFloorClip();
}
// if any special lines were hit, do the effect
if (!(thing->flags & (MF_TELEPORT | MF_NOCLIP)))
{
while (spechit.Pop(ld))
{
fixedvec3 thingpos = thing->PosRelative(ld);
fixedvec3 oldrelpos = PosRelative(oldpos, ld, oldsector);
// see if the line was crossed
side = P_PointOnLineSide(thingpos.x, thingpos.y, ld);
oldside = P_PointOnLineSide(oldrelpos.x, oldrelpos.y, ld);
if (side != oldside && ld->special && !(thing->flags6 & MF6_NOTRIGGER))
{
if (thing->player && (thing->player->cheats & CF_PREDICTING))
{
P_PredictLine(ld, thing, oldside, SPAC_Cross);
}
else if (thing->player)
{
P_ActivateLine(ld, thing, oldside, SPAC_Cross);
}
else if (thing->flags2 & MF2_MCROSS)
{
P_ActivateLine(ld, thing, oldside, SPAC_MCross);
}
else if (thing->flags2 & MF2_PCROSS)
{
P_ActivateLine(ld, thing, oldside, SPAC_PCross);
}
else if ((ld->special == Teleport ||
ld->special == Teleport_NoFog ||
ld->special == Teleport_Line))
{ // [RH] Just a little hack for BOOM compatibility
P_ActivateLine(ld, thing, oldside, SPAC_MCross);
}
else
{
P_ActivateLine(ld, thing, oldside, SPAC_AnyCross);
}
}
}
}
// [RH] Don't activate anything if just predicting
if (thing->player && (thing->player->cheats & CF_PREDICTING))
{
thing->flags6 &= ~MF6_INTRYMOVE;
return true;
}
// [RH] Check for crossing fake floor/ceiling
newsec = thing->Sector;
if (newsec->heightsec && oldsec->heightsec && newsec->SecActTarget)
{
const sector_t *hs = newsec->heightsec;
fixed_t eyez = thing->Z() + viewheight;
fixed_t fakez = hs->floorplane.ZatPoint(x, y);
if (!oldAboveFakeFloor && eyez > fakez)
{ // View went above fake floor
newsec->SecActTarget->TriggerAction(thing, SECSPAC_EyesSurface);
}
else if (oldAboveFakeFloor && eyez <= fakez)
{ // View went below fake floor
newsec->SecActTarget->TriggerAction(thing, SECSPAC_EyesDive);
}
if (!(hs->MoreFlags & SECF_FAKEFLOORONLY))
{
fakez = hs->ceilingplane.ZatPoint(x, y);
if (!oldAboveFakeCeiling && eyez > fakez)
{ // View went above fake ceiling
newsec->SecActTarget->TriggerAction(thing, SECSPAC_EyesAboveC);
}
else if (oldAboveFakeCeiling && eyez <= fakez)
{ // View went below fake ceiling
newsec->SecActTarget->TriggerAction(thing, SECSPAC_EyesBelowC);
}
}
}
// [RH] If changing sectors, trigger transitions
thing->CheckSectorTransition(oldsec);
thing->flags6 &= ~MF6_INTRYMOVE;
return true;
pushline:
thing->flags6 &= ~MF6_INTRYMOVE;
// [RH] Don't activate anything if just predicting
if (thing->player && (thing->player->cheats & CF_PREDICTING))
{
return false;
}
thing->SetZ(oldz);
if (!(thing->flags&(MF_TELEPORT | MF_NOCLIP)))
{
int numSpecHitTemp;
if (tm.thing->flags2 & MF2_BLASTED)
{
P_DamageMobj(tm.thing, NULL, NULL, tm.thing->Mass >> 5, NAME_Melee);
}
numSpecHitTemp = (int)spechit.Size();
while (numSpecHitTemp > 0)
{
// see which lines were pushed
ld = spechit[--numSpecHitTemp];
fixedvec3 pos = thing->PosRelative(ld);
side = P_PointOnLineSide(pos.x, pos.y, ld);
CheckForPushSpecial(ld, side, thing, true);
}
}
return false;
}