in doom_py/src/vizdoom/src/p_map.cpp [1031:1405]
bool PIT_CheckThing(AActor *thing, FCheckPosition &tm)
{
fixed_t topz;
bool solid;
int damage;
// don't clip against self
if (thing == tm.thing)
return true;
if (!((thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE)) || thing->flags6 & MF6_TOUCHY))
return true; // can't hit thing
fixedvec3 thingpos = thing->PosRelative(tm.thing);
fixed_t blockdist = thing->radius + tm.thing->radius;
if (abs(thingpos.x - tm.x) >= blockdist || abs(thingpos.y - tm.y) >= blockdist)
return true;
if ((thing->flags2 | tm.thing->flags2) & MF2_THRUACTORS)
return true;
if ((tm.thing->flags6 & MF6_THRUSPECIES) && (tm.thing->GetSpecies() == thing->GetSpecies()))
return true;
tm.thing->BlockingMobj = thing;
topz = thing->Top();
if (!(i_compatflags & COMPATF_NO_PASSMOBJ) && !(tm.thing->flags & (MF_FLOAT | MF_MISSILE | MF_SKULLFLY | MF_NOGRAVITY)) &&
(thing->flags & MF_SOLID) && (thing->flags4 & MF4_ACTLIKEBRIDGE))
{
// [RH] Let monsters walk on actors as well as floors
if ((tm.thing->flags3 & MF3_ISMONSTER) &&
topz >= tm.floorz && topz <= tm.thing->Z() + tm.thing->MaxStepHeight)
{
// The commented-out if is an attempt to prevent monsters from walking off a
// thing further than they would walk off a ledge. I can't think of an easy
// way to do this, so I restrict them to only walking on bridges instead.
// Uncommenting the if here makes it almost impossible for them to walk on
// anything, bridge or otherwise.
// if (abs(thing->x - tmx) <= thing->radius &&
// abs(thing->y - tmy) <= thing->radius)
{
tm.stepthing = thing;
tm.floorz = topz;
}
}
}
// Both things overlap in x or y direction
bool unblocking = false;
if ((tm.FromPMove || tm.thing->player != NULL) && thing->flags&MF_SOLID)
{
// Both actors already overlap. To prevent them from remaining stuck allow the move if it
// takes them further apart or the move does not change the position (when called from P_ChangeSector.)
if (tm.x == tm.thing->X() && tm.y == tm.thing->Y())
{
unblocking = true;
}
else if (abs(thingpos.x - tm.thing->X()) < (thing->radius+tm.thing->radius) &&
abs(thingpos.y - tm.thing->Y()) < (thing->radius+tm.thing->radius))
{
fixed_t newdist = thing->AproxDistance(tm.x, tm.y, tm.thing);
fixed_t olddist = thing->AproxDistance(tm.thing);
if (newdist > olddist)
{
// ... but not if they did not overlap in z-direction before but would after the move.
unblocking = !((tm.thing->Z() >= topz && tm.z < topz) ||
(tm.thing->Top() <= thingpos.z && tm.thing->Top() > thingpos.z));
}
}
}
// [RH] If the other thing is a bridge, then treat the moving thing as if it had MF2_PASSMOBJ, so
// you can use a scrolling floor to move scenery items underneath a bridge.
if ((tm.thing->flags2 & MF2_PASSMOBJ || thing->flags4 & MF4_ACTLIKEBRIDGE) && !(i_compatflags & COMPATF_NO_PASSMOBJ))
{ // check if a mobj passed over/under another object
if (!(tm.thing->flags & MF_MISSILE) ||
!(tm.thing->flags2 & MF2_RIP) ||
(thing->flags5 & MF5_DONTRIP) ||
((tm.thing->flags6 & MF6_NOBOSSRIP) && (thing->flags2 & MF2_BOSS)))
{
if (tm.thing->flags3 & thing->flags3 & MF3_DONTOVERLAP)
{ // Some things prefer not to overlap each other, if possible
return unblocking;
}
if ((tm.thing->Z() >= topz) || (tm.thing->Top() <= thing->Z()))
return true;
}
}
if (tm.thing->player == NULL || !(tm.thing->player->cheats & CF_PREDICTING))
{
// touchy object is alive, toucher is solid
if (thing->flags6 & MF6_TOUCHY && tm.thing->flags & MF_SOLID && thing->health > 0 &&
// Thing is an armed mine or a sentient thing
(thing->flags6 & MF6_ARMED || thing->IsSentient()) &&
// either different classes or players
(thing->player || thing->GetClass() != tm.thing->GetClass()) &&
// or different species if DONTHARMSPECIES
(!(thing->flags6 & MF6_DONTHARMSPECIES) || thing->GetSpecies() != tm.thing->GetSpecies()) &&
// touches vertically
topz >= tm.thing->Z() && tm.thing->Z() + tm.thing->height >= thingpos.z &&
// prevents lost souls from exploding when fired by pain elementals
(thing->master != tm.thing && tm.thing->master != thing))
// Difference with MBF: MBF hardcodes the LS/PE check and lets actors of the same species
// but different classes trigger the touchiness, but that seems less straightforwards.
{
thing->flags6 &= ~MF6_ARMED; // Disarm
P_DamageMobj(thing, NULL, NULL, thing->health, NAME_None, DMG_FORCED); // kill object
return true;
}
// Check for MF6_BUMPSPECIAL
// By default, only players can activate things by bumping into them
if ((thing->flags6 & MF6_BUMPSPECIAL) && ((tm.thing->player != NULL)
|| ((thing->activationtype & THINGSPEC_MonsterTrigger) && (tm.thing->flags3 & MF3_ISMONSTER))
|| ((thing->activationtype & THINGSPEC_MissileTrigger) && (tm.thing->flags & MF_MISSILE))
) && (level.maptime > thing->lastbump)) // Leave the bumper enough time to go away
{
if (P_ActivateThingSpecial(thing, tm.thing))
thing->lastbump = level.maptime + TICRATE;
}
}
// Check for skulls slamming into things
if (tm.thing->flags & MF_SKULLFLY)
{
bool res = tm.thing->Slam(tm.thing->BlockingMobj);
tm.thing->BlockingMobj = NULL;
return res;
}
// [ED850] Player Prediction ends here. There is nothing else they could/should do.
if (tm.thing->player != NULL && (tm.thing->player->cheats & CF_PREDICTING))
{
solid = (thing->flags & MF_SOLID) &&
!(thing->flags & MF_NOCLIP) &&
((tm.thing->flags & MF_SOLID) || (tm.thing->flags6 & MF6_BLOCKEDBYSOLIDACTORS));
return !solid || unblocking;
}
// Check for blasted thing running into another
if ((tm.thing->flags2 & MF2_BLASTED) && (thing->flags & MF_SHOOTABLE))
{
if (!(thing->flags2 & MF2_BOSS) && (thing->flags3 & MF3_ISMONSTER) && !(thing->flags3 & MF3_DONTBLAST))
{
// ideally this should take the mass factor into account
thing->velx += tm.thing->velx;
thing->vely += tm.thing->vely;
if ((thing->velx + thing->vely) > 3 * FRACUNIT)
{
int newdam;
damage = (tm.thing->Mass / 100) + 1;
newdam = P_DamageMobj(thing, tm.thing, tm.thing, damage, tm.thing->DamageType);
P_TraceBleed(newdam > 0 ? newdam : damage, thing, tm.thing);
damage = (thing->Mass / 100) + 1;
newdam = P_DamageMobj(tm.thing, thing, thing, damage >> 2, tm.thing->DamageType);
P_TraceBleed(newdam > 0 ? newdam : damage, tm.thing, thing);
}
return false;
}
}
// Check for missile or non-solid MBF bouncer
if (tm.thing->flags & MF_MISSILE || ((tm.thing->BounceFlags & BOUNCE_MBF) && !(tm.thing->flags & MF_SOLID)))
{
// Check for a non-shootable mobj
if (thing->flags2 & MF2_NONSHOOTABLE)
{
return true;
}
// Check for passing through a ghost
if ((thing->flags3 & MF3_GHOST) && (tm.thing->flags2 & MF2_THRUGHOST))
{
return true;
}
if ((tm.thing->flags6 & MF6_MTHRUSPECIES)
&& tm.thing->target // NULL pointer check
&& (tm.thing->target->GetSpecies() == thing->GetSpecies()))
return true;
// Check for rippers passing through corpses
if ((thing->flags & MF_CORPSE) && (tm.thing->flags2 & MF2_RIP) && !(thing->flags & MF_SHOOTABLE))
{
return true;
}
int clipheight;
if (thing->projectilepassheight > 0)
{
clipheight = thing->projectilepassheight;
}
else if (thing->projectilepassheight < 0 && (i_compatflags & COMPATF_MISSILECLIP))
{
clipheight = -thing->projectilepassheight;
}
else
{
clipheight = thing->height;
}
// Check if it went over / under
if (tm.thing->Z() > thingpos.z + clipheight)
{ // Over thing
return true;
}
if (tm.thing->Top() < thingpos.z)
{ // Under thing
return true;
}
// [RH] What is the point of this check, again? In Hexen, it is unconditional,
// but here we only do it if the missile's damage is 0.
// MBF bouncer might have a non-0 damage value, but they must not deal damage on impact either.
if ((tm.thing->BounceFlags & BOUNCE_Actors) && (tm.thing->Damage == 0 || !(tm.thing->flags & MF_MISSILE)))
{
return (tm.thing->target == thing || !(thing->flags & MF_SOLID));
}
switch (tm.thing->SpecialMissileHit(thing))
{
case 0: return false;
case 1: return true;
default: break;
}
// [RH] Extend DeHacked infighting to allow for monsters
// to never fight each other
if (tm.thing->target != NULL)
{
if (thing == tm.thing->target)
{ // Don't missile self
return true;
}
if (!CanAttackHurt(thing, tm.thing->target))
{
return false;
}
}
if (!(thing->flags & MF_SHOOTABLE))
{ // Didn't do any damage
return !(thing->flags & MF_SOLID);
}
if ((thing->flags4 & MF4_SPECTRAL) && !(tm.thing->flags4 & MF4_SPECTRAL))
{
return true;
}
if ((tm.DoRipping && !(thing->flags5 & MF5_DONTRIP)) && CheckRipLevel(thing, tm.thing))
{
if (!(tm.thing->flags6 & MF6_NOBOSSRIP) || !(thing->flags2 & MF2_BOSS))
{
bool *check = tm.LastRipped.CheckKey(thing);
if (check == NULL || !*check)
{
tm.LastRipped[thing] = true;
if (!(thing->flags & MF_NOBLOOD) &&
!(thing->flags2 & MF2_REFLECTIVE) &&
!(tm.thing->flags3 & MF3_BLOODLESSIMPACT) &&
!(thing->flags2 & (MF2_INVULNERABLE | MF2_DORMANT)))
{ // Ok to spawn blood
P_RipperBlood(tm.thing, thing);
}
S_Sound(tm.thing, CHAN_BODY, "misc/ripslop", 1, ATTN_IDLE);
// Do poisoning (if using new style poison)
if (tm.thing->PoisonDamage > 0 && tm.thing->PoisonDuration != INT_MIN)
{
P_PoisonMobj(thing, tm.thing, tm.thing->target, tm.thing->PoisonDamage, tm.thing->PoisonDuration, tm.thing->PoisonPeriod, tm.thing->PoisonDamageType);
}
damage = tm.thing->GetMissileDamage(3, 2);
int newdam = P_DamageMobj(thing, tm.thing, tm.thing->target, damage, tm.thing->DamageType);
if (!(tm.thing->flags3 & MF3_BLOODLESSIMPACT))
{
P_TraceBleed(newdam > 0 ? newdam : damage, thing, tm.thing);
}
if (thing->flags2 & MF2_PUSHABLE
&& !(tm.thing->flags2 & MF2_CANNOTPUSH))
{ // Push thing
if (thing->lastpush != tm.PushTime)
{
thing->velx += FixedMul(tm.thing->velx, thing->pushfactor);
thing->vely += FixedMul(tm.thing->vely, thing->pushfactor);
thing->lastpush = tm.PushTime;
}
}
}
spechit.Clear();
return true;
}
}
// Do poisoning (if using new style poison)
if (tm.thing->PoisonDamage > 0 && tm.thing->PoisonDuration != INT_MIN)
{
P_PoisonMobj(thing, tm.thing, tm.thing->target, tm.thing->PoisonDamage, tm.thing->PoisonDuration, tm.thing->PoisonPeriod, tm.thing->PoisonDamageType);
}
// Do damage
damage = tm.thing->GetMissileDamage((tm.thing->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1);
if ((damage > 0) || (tm.thing->flags6 & MF6_FORCEPAIN) || (tm.thing->flags7 & MF7_CAUSEPAIN))
{
int newdam = P_DamageMobj(thing, tm.thing, tm.thing->target, damage, tm.thing->DamageType);
if (damage > 0)
{
if ((tm.thing->flags5 & MF5_BLOODSPLATTER) &&
!(thing->flags & MF_NOBLOOD) &&
!(thing->flags2 & MF2_REFLECTIVE) &&
!(thing->flags2 & (MF2_INVULNERABLE | MF2_DORMANT)) &&
!(tm.thing->flags3 & MF3_BLOODLESSIMPACT) &&
(pr_checkthing() < 192))
{
P_BloodSplatter(tm.thing->X(), tm.thing->Y(), tm.thing->Z(), thing);
}
if (!(tm.thing->flags3 & MF3_BLOODLESSIMPACT))
{
P_TraceBleed(newdam > 0 ? newdam : damage, thing, tm.thing);
}
}
}
else
{
P_GiveBody(thing, -damage);
}
if ((thing->flags7 & MF7_THRUREFLECT) && (thing->flags2 & MF2_REFLECTIVE) && (tm.thing->flags & MF_MISSILE))
{
if (tm.thing->flags2 & MF2_SEEKERMISSILE)
{
tm.thing->tracer = tm.thing->target;
}
tm.thing->target = thing;
return true;
}
return false; // don't traverse any more
}
if (thing->flags2 & MF2_PUSHABLE && !(tm.thing->flags2 & MF2_CANNOTPUSH))
{ // Push thing
if (thing->lastpush != tm.PushTime)
{
thing->velx += FixedMul(tm.thing->velx, thing->pushfactor);
thing->vely += FixedMul(tm.thing->vely, thing->pushfactor);
thing->lastpush = tm.PushTime;
}
}
solid = (thing->flags & MF_SOLID) &&
!(thing->flags & MF_NOCLIP) &&
((tm.thing->flags & MF_SOLID) || (tm.thing->flags6 & MF6_BLOCKEDBYSOLIDACTORS));
// Check for special pickup
if ((thing->flags & MF_SPECIAL) && (tm.thing->flags & MF_PICKUP)
// [RH] The next condition is to compensate for the extra height
// that gets added by P_CheckPosition() so that you cannot pick
// up things that are above your true height.
&& thingpos.z < tm.thing->Top() - tm.thing->MaxStepHeight)
{ // Can be picked up by tmthing
P_TouchSpecialThing(thing, tm.thing); // can remove thing
}
// killough 3/16/98: Allow non-solid moving objects to move through solid
// ones, by allowing the moving thing (tmthing) to move if it's non-solid,
// despite another solid thing being in the way.
// killough 4/11/98: Treat no-clipping things as not blocking
return !solid || unblocking;
// return !(thing->flags & MF_SOLID); // old code -- killough
}