Beispiel #1
0
// Returns the amount of damage actually inflicted upon the target, or -1 if
// the damage was cancelled.
int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, FName mod, int flags)
{
	unsigned ang;
	player_t *player = NULL;
	fixed_t thrust;
	int temp;
	int painchance = 0;
	FState * woundstate = NULL;
	PainChanceList * pc = NULL;
	bool justhit = false;
	bool plrDontThrust = false;
	bool invulpain = false;
	bool fakedPain = false;
	bool forcedPain = false;
	int fakeDamage = 0;
	int holdDamage = 0;
	int rawdamage = damage; 
	
	if (damage < 0) damage = 0;

	if (target == NULL || !((target->flags & MF_SHOOTABLE) || (target->flags6 & MF6_VULNERABLE)))
	{ // Shouldn't happen
		return -1;
	}

	//Rather than unnecessarily call the function over and over again, let's be a little more efficient.
	fakedPain = (isFakePain(target, inflictor, damage)); 
	forcedPain = (MustForcePain(target, inflictor));

	// Spectral targets only take damage from spectral projectiles.
	if (target->flags4 & MF4_SPECTRAL && damage < TELEFRAG_DAMAGE)
	{
		if (inflictor == NULL || !(inflictor->flags4 & MF4_SPECTRAL))
		{
			return -1;
		}
	}
	if (target->health <= 0)
	{
		if (inflictor && mod == NAME_Ice)
		{
			return -1;
		}
		else if (target->flags & MF_ICECORPSE) // frozen
		{
			target->tics = 1;
			target->flags6 |= MF6_SHATTERING;
			target->velx = target->vely = target->velz = 0;
		}
		return -1;
	}
	// [MC] Changed it to check rawdamage here for consistency, even though that doesn't actually do anything
	// different here. At any rate, invulnerable is being checked before type factoring, which is then being 
	// checked by player cheats/invul/buddha followed by monster buddha. This is inconsistent. Don't let the 
	// original telefrag damage CHECK (rawdamage) be influenced by outside factors when looking at cheats/invul.
	if ((target->flags2 & MF2_INVULNERABLE) && (rawdamage < TELEFRAG_DAMAGE) && (!(flags & DMG_FORCED)))
	{ // actor is invulnerable
		if (target->player == NULL)
		{
			if (inflictor == NULL || (!(inflictor->flags3 & MF3_FOILINVUL) && !(flags & DMG_FOILINVUL)))
			{
				if (fakedPain)
				{
					// big mess here: What do we use for the pain threshold?
					// We cannot run the various damage filters below so for consistency it needs to be 0.
					damage = 0;
					invulpain = true;
					goto fakepain;
				}
				else
					return -1;
			}
		}
		else
		{
			// Players are optionally excluded from getting thrust by damage.
			if (static_cast<APlayerPawn *>(target)->PlayerFlags & PPF_NOTHRUSTWHENINVUL)
			{
				if (fakedPain)
					plrDontThrust = 1;
				else
					return -1;
			}
		}
		
	}

	if (inflictor != NULL)
	{
		if (inflictor->flags5 & MF5_PIERCEARMOR)
			flags |= DMG_NO_ARMOR;
	}
	
	MeansOfDeath = mod;
	FriendlyFire = false;
	// [RH] Andy Baker's Stealth monsters
	if (target->flags & MF_STEALTH)
	{
		target->alpha = OPAQUE;
		target->visdir = -1;
	}
	if (target->flags & MF_SKULLFLY)
	{
		target->velx = target->vely = target->velz = 0;
	}

	player = target->player;
	if (!(flags & DMG_FORCED))	// DMG_FORCED skips all special damage checks, TELEFRAG_DAMAGE may not be reduced at all
	{
		if (target->flags2 & MF2_DORMANT)
		{
			// Invulnerable, and won't wake up
			return -1;
		}

		if ((rawdamage < TELEFRAG_DAMAGE) || (target->flags7 & MF7_LAXTELEFRAGDMG)) // TELEFRAG_DAMAGE may only be reduced with NOTELEFRAGPIERCE or it may not guarantee its effect.
		{
			if (player && damage > 1)
			{
				// Take half damage in trainer mode
				damage = FixedMul(damage, G_SkillProperty(SKILLP_DamageFactor));
			}
			// Special damage types
			if (inflictor)
			{
				if (inflictor->flags4 & MF4_SPECTRAL)
				{
					if (player != NULL)
					{
						if (!deathmatch && inflictor->FriendPlayer > 0)
							return -1;
					}
					else if (target->flags4 & MF4_SPECTRAL)
					{
						if (inflictor->FriendPlayer == 0 && !target->IsHostile(inflictor))
							return -1;
					}
				}

				damage = inflictor->DoSpecialDamage(target, damage, mod);
				if (damage < 0)
				{
					return -1;
				}
			}

			int olddam = damage;

			if (damage > 0 && source != NULL)
			{
				damage = FixedMul(damage, source->DamageMultiply);

				// Handle active damage modifiers (e.g. PowerDamage)
				if (damage > 0 && source->Inventory != NULL)
				{
					source->Inventory->ModifyDamage(damage, mod, damage, false);
				}
			}
			// Handle passive damage modifiers (e.g. PowerProtection), provided they are not afflicted with protection penetrating powers.
			if (damage > 0 && (target->Inventory != NULL) && !(flags & DMG_NO_PROTECT))
			{
				target->Inventory->ModifyDamage(damage, mod, damage, true);
			}
			if (damage > 0 && !(flags & DMG_NO_FACTOR))
			{
				damage = FixedMul(damage, target->DamageFactor);
				if (damage > 0)
				{
					damage = DamageTypeDefinition::ApplyMobjDamageFactor(damage, mod, target->GetClass()->ActorInfo->DamageFactors);
				}
			}

			if (damage >= 0)
			{
				damage = target->TakeSpecialDamage(inflictor, source, damage, mod);
			}

			// '<0' is handled below. This only handles the case where damage gets reduced to 0.
			if (damage == 0 && olddam > 0)
			{
				{ // Still allow FORCEPAIN
					if (forcedPain)
					{
						goto dopain;
					}
					else if (fakedPain)
					{
						goto fakepain;
					}
					return -1;
				}
			}
		}
		if (target->flags5 & MF5_NODAMAGE)
		{
			damage = 0;
		}
	}
	if (damage < 0)
	{
		// any negative value means that something in the above chain has cancelled out all damage and all damage effects, including pain.
		return -1;
	}
	// Push the target unless the source's weapon's kickback is 0.
	// (i.e. Gauntlets/Chainsaw)
	if (!plrDontThrust && inflictor && inflictor != target	// [RH] Not if hurting own self
		&& !(target->flags & MF_NOCLIP)
		&& !(inflictor->flags2 & MF2_NODMGTHRUST)
		&& !(flags & DMG_THRUSTLESS)
		&& !(target->flags7 & MF7_DONTTHRUST)
		&& (source == NULL || source->player == NULL || !(source->flags2 & MF2_NODMGTHRUST)))
	{
		int kickback;

		if (inflictor && inflictor->projectileKickback)
			kickback = inflictor->projectileKickback;
		else if (!source || !source->player || !source->player->ReadyWeapon)
			kickback = gameinfo.defKickback;
		else
			kickback = source->player->ReadyWeapon->Kickback;

		if (kickback)
		{
			AActor *origin = (source && (flags & DMG_INFLICTOR_IS_PUFF))? source : inflictor;

			// If the origin and target are in exactly the same spot, choose a random direction.
			// (Most likely cause is from telefragging somebody during spawning because they
			// haven't moved from their spawn spot at all.)
			if (origin->x == target->x && origin->y == target->y)
			{
				ang = pr_kickbackdir.GenRand32();
			}
			else
			{
				ang = R_PointToAngle2 (origin->x, origin->y, target->x, target->y);
			}

			// Calculate this as float to avoid overflows so that the
			// clamping that had to be done here can be removed.
            double fltthrust;

            fltthrust = mod == NAME_MDK ? 10 : 32;
            if (target->Mass > 0)
            {
                fltthrust = clamp((damage * 0.125 * kickback) / target->Mass, 0., fltthrust);
            }

			thrust = FLOAT2FIXED(fltthrust);

			// Don't apply ultra-small damage thrust
			if (thrust < FRACUNIT/100) thrust = 0;

			// make fall forwards sometimes
			if ((damage < 40) && (damage > target->health)
				 && (target->z - origin->z > 64*FRACUNIT)
				 && (pr_damagemobj()&1)
				 // [RH] But only if not too fast and not flying
				 && thrust < 10*FRACUNIT
				 && !(target->flags & MF_NOGRAVITY)
				 && (inflictor == NULL || !(inflictor->flags5 & MF5_NOFORWARDFALL))
				 )
			{
				ang += ANG180;
				thrust *= 4;
			}
			ang >>= ANGLETOFINESHIFT;
			if (source && source->player && (flags & DMG_INFLICTOR_IS_PUFF)
				&& source->player->ReadyWeapon != NULL &&
				(source->player->ReadyWeapon->WeaponFlags & WIF_STAFF2_KICKBACK))
			{
				// Staff power level 2
				target->velx += FixedMul (10*FRACUNIT, finecosine[ang]);
				target->vely += FixedMul (10*FRACUNIT, finesine[ang]);
				if (!(target->flags & MF_NOGRAVITY))
				{
					target->velz += 5*FRACUNIT;
				}
			}
			else
			{
				target->velx += FixedMul (thrust, finecosine[ang]);
				target->vely += FixedMul (thrust, finesine[ang]);
			}
		}
	}
Beispiel #2
0
// Returns the amount of damage actually inflicted upon the target, or -1 if
// the damage was cancelled.
int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, FName mod, int flags)
{
	unsigned ang;
	player_t *player = NULL;
	fixed_t thrust;
	int temp;
	int painchance = 0;
	FState * woundstate = NULL;
	PainChanceList * pc = NULL;
	bool justhit = false;

	if (target == NULL || !((target->flags & MF_SHOOTABLE) || (target->flags6 & MF6_VULNERABLE)))
	{ // Shouldn't happen
		return -1;
	}

	// Spectral targets only take damage from spectral projectiles.
	if (target->flags4 & MF4_SPECTRAL && damage < TELEFRAG_DAMAGE)
	{
		if (inflictor == NULL || !(inflictor->flags4 & MF4_SPECTRAL))
		{
			return -1;
		}
	}
	if (target->health <= 0)
	{
		if (inflictor && mod == NAME_Ice)
		{
			return -1;
		}
		else if (target->flags & MF_ICECORPSE) // frozen
		{
			target->tics = 1;
			target->flags6 |= MF6_SHATTERING;
			target->velx = target->vely = target->velz = 0;
		}
		return -1;
	}
	if ((target->flags2 & MF2_INVULNERABLE) && damage < TELEFRAG_DAMAGE && !(flags & DMG_FORCED))
	{ // actor is invulnerable
		if (target->player == NULL)
		{
			if (inflictor == NULL || (!(inflictor->flags3 & MF3_FOILINVUL) && !(flags & DMG_FOILINVUL)))
			{
				return -1;
			}
		}
		else
		{
			// Players are optionally excluded from getting thrust by damage.
			if (static_cast<APlayerPawn *>(target)->PlayerFlags & PPF_NOTHRUSTWHENINVUL)
			{
				return -1;
			}
		}
		
	}
	if (inflictor != NULL)
	{
		if (inflictor->flags5 & MF5_PIERCEARMOR)
			flags |= DMG_NO_ARMOR;
	}
	
	MeansOfDeath = mod;
	FriendlyFire = false;
	// [RH] Andy Baker's Stealth monsters
	if (target->flags & MF_STEALTH)
	{
		target->alpha = OPAQUE;
		target->visdir = -1;
	}
	if (target->flags & MF_SKULLFLY)
	{
		target->velx = target->vely = target->velz = 0;
	}
	if (!(flags & DMG_FORCED))	// DMG_FORCED skips all special damage checks
	{
		if (target->flags2 & MF2_DORMANT)
		{
			// Invulnerable, and won't wake up
			return -1;
		}
		player = target->player;
		if (player && damage > 1 && damage < TELEFRAG_DAMAGE)
		{
			// Take half damage in trainer mode
			damage = FixedMul(damage, G_SkillProperty(SKILLP_DamageFactor));
		}
		// Special damage types
		if (inflictor)
		{
			if (inflictor->flags4 & MF4_SPECTRAL)
			{
				if (player != NULL)
				{
					if (!deathmatch && inflictor->FriendPlayer > 0)
						return -1;
				}
				else if (target->flags4 & MF4_SPECTRAL)
				{
					if (inflictor->FriendPlayer == 0 && !target->IsHostile(inflictor))
						return -1;
				}
			}

			damage = inflictor->DoSpecialDamage (target, damage, mod);
			if (damage == -1)
			{
				return -1;
			}
		}
		// Handle active damage modifiers (e.g. PowerDamage)
		if (source != NULL && source->Inventory != NULL)
		{
			int olddam = damage;
			source->Inventory->ModifyDamage(olddam, mod, damage, false);
			if (olddam != damage && damage <= 0)
			{ // Still allow FORCEPAIN
				if (MustForcePain(target, inflictor))
				{
					goto dopain;
				}
				return -1;
			}
		}
		// Handle passive damage modifiers (e.g. PowerProtection)
		if (target->Inventory != NULL)
		{
			int olddam = damage;
			target->Inventory->ModifyDamage(olddam, mod, damage, true);
			if (olddam != damage && damage <= 0)
			{ // Still allow FORCEPAIN
				if (MustForcePain(target, inflictor))
				{
					goto dopain;
				}
				return -1;
			}
		}

		if (!(flags & DMG_NO_FACTOR))
		{
			damage = FixedMul(damage, target->DamageFactor);
			if (damage >= 0)
			{
				damage = DamageTypeDefinition::ApplyMobjDamageFactor(damage, mod, target->GetClass()->ActorInfo->DamageFactors);
			}
			if (damage <= 0)
			{ // Still allow FORCEPAIN
				if (MustForcePain(target, inflictor))
				{
					goto dopain;
				}
				return -1;
			}
		}

		damage = target->TakeSpecialDamage (inflictor, source, damage, mod);
	}
	if (damage == -1)
	{
		return -1;
	}
	// Push the target unless the source's weapon's kickback is 0.
	// (i.e. Gauntlets/Chainsaw)
	if (inflictor && inflictor != target	// [RH] Not if hurting own self
		&& !(target->flags & MF_NOCLIP)
		&& !(inflictor->flags2 & MF2_NODMGTHRUST)
		&& !(flags & DMG_THRUSTLESS)
		&& (source == NULL || source->player == NULL || !(source->flags2 & MF2_NODMGTHRUST)))
	{
		int kickback;

		if (inflictor && inflictor->projectileKickback)
			kickback = inflictor->projectileKickback;
		else if (!source || !source->player || !source->player->ReadyWeapon)
			kickback = gameinfo.defKickback;
		else
			kickback = source->player->ReadyWeapon->Kickback;

		if (kickback)
		{
			AActor *origin = (source && (flags & DMG_INFLICTOR_IS_PUFF))? source : inflictor;

			// If the origin and target are in exactly the same spot, choose a random direction.
			// (Most likely cause is from telefragging somebody during spawning because they
			// haven't moved from their spawn spot at all.)
			if (origin->x == target->x && origin->y == target->y)
			{
				ang = pr_kickbackdir.GenRand32();
			}
			else
			{
				ang = R_PointToAngle2 (origin->x, origin->y, target->x, target->y);
			}

			// Calculate this as float to avoid overflows so that the
			// clamping that had to be done here can be removed.
            double fltthrust;

            fltthrust = mod == NAME_MDK ? 10 : 32;
            if (target->Mass > 0)
            {
                fltthrust = clamp((damage * 0.125 * kickback) / target->Mass, 0., fltthrust);
            }

			thrust = FLOAT2FIXED(fltthrust);

			// Don't apply ultra-small damage thrust
			if (thrust < FRACUNIT/100) thrust = 0;

			// make fall forwards sometimes
			if ((damage < 40) && (damage > target->health)
				 && (target->z - origin->z > 64*FRACUNIT)
				 && (pr_damagemobj()&1)
				 // [RH] But only if not too fast and not flying
				 && thrust < 10*FRACUNIT
				 && !(target->flags & MF_NOGRAVITY)
				 && (inflictor == NULL || !(inflictor->flags5 & MF5_NOFORWARDFALL))
				 )
			{
				ang += ANG180;
				thrust *= 4;
			}
			ang >>= ANGLETOFINESHIFT;
			if (source && source->player && (flags & DMG_INFLICTOR_IS_PUFF)
				&& source->player->ReadyWeapon != NULL &&
				(source->player->ReadyWeapon->WeaponFlags & WIF_STAFF2_KICKBACK))
			{
				// Staff power level 2
				target->velx += FixedMul (10*FRACUNIT, finecosine[ang]);
				target->vely += FixedMul (10*FRACUNIT, finesine[ang]);
				if (!(target->flags & MF_NOGRAVITY))
				{
					target->velz += 5*FRACUNIT;
				}
			}
			else
			{
				target->velx += FixedMul (thrust, finecosine[ang]);
				target->vely += FixedMul (thrust, finesine[ang]);
			}
		}
	}