bool PIT_CheckThing()

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
}