// 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]); } } }
// 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]); } } }