by Gez » Sun Apr 29, 2012 6:57 am
Enjay wrote:I take your arms race point but all I'm saying is that there is now no way for any actor to be excluded from the effects of a forcepain missile and it feels to me like there should be.
Don't give it a pain state? Force pain should force pain. Finer-tuned controls should be done with damage-specific pain chances.
Enjay wrote:Presumably the A_RadiusGive idea would be to define an inventory item that puts any actor that has it into their pain state (how would such an item look? - can monsters have inventory items activate in their own inventory?) and then have another actor (in my case the sonic grenade) repeatedly give out the inventory item to any actor within the specified range, using the RGF_MONSTERS flag?
In the broad lines, yeah. You can give CustomInventory items to monsters and they will perform the actions there. However, you can't use an A_Jump or Goto in the custom inventory Use code since it will not actually change the actor's state. If I were coding the sonic grenade effect from scratch, I'd probably use some sort of timed powerup, like maybe PowerIronFeet which won't affect the monster otherwise. Then have in the monster's states A_JumpIfInventory checks for the presence of that token powerup.
Though I understand not wanting to update a ton of already-existing actors with rewritten sonic grenade code. It's just that I don't see a 100% satisfying way to solve your problem. Piling up new complexity to the already nigh-incomprehensible pain code doesn't appeal to me.
Spoiler:
Code: Select all
void 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;
}
// Spectral targets only take damage from spectral projectiles.
if (target->flags4 & MF4_SPECTRAL && damage < TELEFRAG_DAMAGE)
{
if (inflictor == NULL || !(inflictor->flags4 & MF4_SPECTRAL))
{
return;
}
}
if (target->health <= 0)
{
if (inflictor && mod == NAME_Ice)
{
return;
}
else if (target->flags & MF_ICECORPSE) // frozen
{
target->tics = 1;
target->flags6 |= MF6_SHATTERING;
target->velx = target->vely = target->velz = 0;
}
return;
}
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))
{
return;
}
}
else
{
// Players are optionally excluded from getting thrust by damage.
if (static_cast<APlayerPawn *>(target)->PlayerFlags & PPF_NOTHRUSTWHENINVUL)
{
return;
}
}
}
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;
}
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->health == -1)
return;
}
else if (target->flags4 & MF4_SPECTRAL)
{
if (inflictor->health == -2 && !target->IsHostile(inflictor))
return;
}
}
damage = inflictor->DoSpecialDamage (target, damage);
if (damage == -1)
{
return;
}
}
// 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 (!(target->flags5 & MF5_NOPAIN) && inflictor != NULL &&
(inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS))
{
goto dopain;
}
return;
}
}
// 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 (!(target->flags5 & MF5_NOPAIN) && inflictor != NULL &&
(inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS))
{
goto dopain;
}
return;
}
}
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 (!(target->flags5 & MF5_NOPAIN) && inflictor != NULL &&
(inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS))
{
goto dopain;
}
return;
}
}
damage = target->TakeSpecialDamage (inflictor, source, damage, mod);
}
if (damage == -1)
{
return;
}
// 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;
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))
{
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]);
}
}
}
// [RH] Avoid friendly fire if enabled
if (!(flags & DMG_FORCED) && source != NULL &&
((player && player != source->player) || (!player && target != source)) &&
target->IsTeammate (source))
{
if (player)
FriendlyFire = true;
if (damage < TELEFRAG_DAMAGE)
{ // Still allow telefragging :-(
damage = (int)((float)damage * level.teamdamage);
if (damage <= 0)
return;
}
}
//
// player specific
//
if (player)
{
//Added by MC: Lets bots look allround for enemies if they survive an ambush.
if (player->isbot)
{
player->allround = true;
}
// end of game hell hack
if ((target->Sector->special & 255) == dDamage_End
&& damage >= target->health)
{
damage = target->health - 1;
}
if (!(flags & DMG_FORCED))
{
// check the real player, not a voodoo doll here for invulnerability effects
if (damage < TELEFRAG_DAMAGE && ((player->mo->flags2 & MF2_INVULNERABLE) ||
(player->cheats & CF_GODMODE)))
{ // player is invulnerable, so don't hurt him
return;
}
if (!(flags & DMG_NO_ARMOR) && player->mo->Inventory != NULL)
{
int newdam = damage;
player->mo->Inventory->AbsorbDamage (damage, mod, newdam);
damage = newdam;
if (damage <= 0)
{
// If MF6_FORCEPAIN is set, make the player enter the pain state.
if (!(target->flags5 & MF5_NOPAIN) && inflictor != NULL &&
(inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS))
{
goto dopain;
}
return;
}
}
if (damage >= player->health
&& (G_SkillProperty(SKILLP_AutoUseHealth) || deathmatch)
&& !player->morphTics)
{ // Try to use some inventory health
P_AutoUseHealth (player, damage - player->health + 1);
}
}
player->health -= damage; // mirror mobj health here for Dave
// [RH] Make voodoo dolls and real players record the same health
target->health = player->mo->health -= damage;
if (player->health < 50 && !deathmatch && !(flags & DMG_FORCED))
{
P_AutoUseStrifeHealth (player);
player->mo->health = player->health;
}
if (player->health <= 0)
{
// [SP] Buddha cheat: if the player is about to die, rescue him to 1 health.
// This does not save the player if damage >= TELEFRAG_DAMAGE, still need to
// telefrag him right? ;) (Unfortunately the damage is "absorbed" by armor,
// but telefragging should still do enough damage to kill the player)
if ((player->cheats & CF_BUDDHA) && damage < TELEFRAG_DAMAGE)
{
// If this is a voodoo doll we need to handle the real player as well.
player->mo->health = target->health = player->health = 1;
}
else
{
player->health = 0;
}
}
player->LastDamageType = mod;
player->attacker = source;
player->damagecount += damage; // add damage after armor / invuln
if (player->damagecount > 100)
{
player->damagecount = 100; // teleport stomp does 10k points...
}
temp = damage < 100 ? damage : 100;
if (player == &players[consoleplayer])
{
I_Tactile (40,10,40+temp*2);
}
}
else
{
// Armor for monsters.
if (!(flags & (DMG_NO_ARMOR|DMG_FORCED)) && target->Inventory != NULL && damage > 0)
{
int newdam = damage;
target->Inventory->AbsorbDamage (damage, mod, newdam);
damage = newdam;
if (damage <= 0)
{
return;
}
}
target->health -= damage;
}
//
// the damage has been dealt; now deal with the consequences
//
target->DamageTypeReceived = mod;
// If the damaging player has the power of drain, give the player 50% of the damage
// done in health.
if ( source && source->player && source->player->cheats & CF_DRAIN)
{
if (!target->player || target->player != source->player)
{
if ( P_GiveBody( source, damage / 2 ))
{
S_Sound( source, CHAN_ITEM, "*drainhealth", 1, ATTN_NORM );
}
}
}
if (target->health <= 0)
{ // Death
target->special1 = damage;
// check for special fire damage or ice damage deaths
if (mod == NAME_Fire)
{
if (player && !player->morphTics)
{ // Check for flame death
if (!inflictor ||
((target->health > -50) && (damage > 25)) ||
!(inflictor->flags5 & MF5_SPECIALFIREDAMAGE))
{
target->DamageType = NAME_Fire;
}
}
else
{
target->DamageType = NAME_Fire;
}
}
else
{
target->DamageType = mod;
}
if (source && source->tracer && (source->flags5 & MF5_SUMMONEDMONSTER))
{ // Minotaur's kills go to his master
// Make sure still alive and not a pointer to fighter head
if (source->tracer->player && (source->tracer->player->mo == source->tracer))
{
source = source->tracer;
}
}
target->Die (source, inflictor);
return;
}
woundstate = target->FindState(NAME_Wound, mod);
if (woundstate != NULL)
{
int woundhealth = RUNTIME_TYPE(target)->Meta.GetMetaInt (AMETA_WoundHealth, 6);
if (target->health <= woundhealth)
{
target->SetState (woundstate);
return;
}
}
if (!(target->flags5 & MF5_NOPAIN) && (inflictor == NULL || !(inflictor->flags5 & MF5_PAINLESS)) &&
!G_SkillProperty(SKILLP_NoPain) && !(target->flags & MF_SKULLFLY))
{
pc = target->GetClass()->ActorInfo->PainChances;
painchance = target->PainChance;
if (pc != NULL)
{
int *ppc = pc->CheckKey(mod);
if (ppc != NULL)
{
painchance = *ppc;
}
}
if ((damage >= target->PainThreshold && pr_damagemobj() < painchance) ||
(inflictor != NULL && (inflictor->flags6 & MF6_FORCEPAIN)))
{
dopain:
if (mod == NAME_Electric)
{
if (pr_lightning() < 96)
{
justhit = true;
FState *painstate = target->FindState(NAME_Pain, mod);
if (painstate != NULL)
target->SetState(painstate);
}
else
{ // "electrocute" the target
target->renderflags |= RF_FULLBRIGHT;
if ((target->flags3 & MF3_ISMONSTER) && pr_lightning() < 128)
{
target->Howl ();
}
}
}
else
{
justhit = true;
FState *painstate = target->FindState(NAME_Pain, ((inflictor && inflictor->PainType != NAME_None) ? inflictor->PainType : mod));
if (painstate != NULL)
target->SetState(painstate);
if (mod == NAME_PoisonCloud)
{
if ((target->flags3 & MF3_ISMONSTER) && pr_poison() < 128)
{
target->Howl ();
}
}
}
}
}
target->reactiontime = 0; // we're awake now...
if (source)
{
if (source == target->target)
{
target->threshold = BASETHRESHOLD;
if (target->state == target->SpawnState && target->SeeState != NULL)
{
target->SetState (target->SeeState);
}
}
else if (source != target->target && target->OkayToSwitchTarget (source))
{
// Target actor is not intent on another actor,
// so make him chase after source
// killough 2/15/98: remember last enemy, to prevent
// sleeping early; 2/21/98: Place priority on players
if (target->lastenemy == NULL ||
(target->lastenemy->player == NULL && target->TIDtoHate == 0) ||
target->lastenemy->health <= 0)
{
target->lastenemy = target->target; // remember last enemy - killough
}
target->target = source;
target->threshold = BASETHRESHOLD;
if (target->state == target->SpawnState && target->SeeState != NULL)
{
target->SetState (target->SeeState);
}
}
}
// killough 11/98: Don't attack a friend, unless hit by that friend.
if (justhit && (target->target == source || !target->target || !target->IsFriend(target->target)))
target->flags |= MF_JUSTHIT; // fight back!
}
Argh! Can you follow that code?
[quote="Enjay"]I take your arms race point but all I'm saying is that there is now no way for any actor to be excluded from the effects of a forcepain missile and it feels to me like there should be.[/quote]
Don't give it a pain state? Force pain should force pain. Finer-tuned controls should be done with damage-specific pain chances.
[quote="Enjay"]Presumably the A_RadiusGive idea would be to define an inventory item that puts any actor that has it into their pain state (how would such an item look? - can monsters have inventory items activate in their own inventory?) and then have another actor (in my case the sonic grenade) repeatedly give out the inventory item to any actor within the specified range, using the RGF_MONSTERS flag?[/quote]
In the broad lines, yeah. You can give CustomInventory items to monsters and they will perform the actions there. However, you can't use an A_Jump or Goto in the custom inventory Use code since it will not actually change the actor's state. If I were coding the sonic grenade effect from scratch, I'd probably use some sort of timed powerup, like maybe PowerIronFeet which won't affect the monster otherwise. Then have in the monster's states A_JumpIfInventory checks for the presence of that token powerup.
Though I understand not wanting to update a ton of already-existing actors with rewritten sonic grenade code. It's just that I don't see a 100% satisfying way to solve your problem. Piling up new complexity to the already nigh-incomprehensible pain code doesn't appeal to me.
[spoiler][code]void 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;
}
// Spectral targets only take damage from spectral projectiles.
if (target->flags4 & MF4_SPECTRAL && damage < TELEFRAG_DAMAGE)
{
if (inflictor == NULL || !(inflictor->flags4 & MF4_SPECTRAL))
{
return;
}
}
if (target->health <= 0)
{
if (inflictor && mod == NAME_Ice)
{
return;
}
else if (target->flags & MF_ICECORPSE) // frozen
{
target->tics = 1;
target->flags6 |= MF6_SHATTERING;
target->velx = target->vely = target->velz = 0;
}
return;
}
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))
{
return;
}
}
else
{
// Players are optionally excluded from getting thrust by damage.
if (static_cast<APlayerPawn *>(target)->PlayerFlags & PPF_NOTHRUSTWHENINVUL)
{
return;
}
}
}
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;
}
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->health == -1)
return;
}
else if (target->flags4 & MF4_SPECTRAL)
{
if (inflictor->health == -2 && !target->IsHostile(inflictor))
return;
}
}
damage = inflictor->DoSpecialDamage (target, damage);
if (damage == -1)
{
return;
}
}
// 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 (!(target->flags5 & MF5_NOPAIN) && inflictor != NULL &&
(inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS))
{
goto dopain;
}
return;
}
}
// 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 (!(target->flags5 & MF5_NOPAIN) && inflictor != NULL &&
(inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS))
{
goto dopain;
}
return;
}
}
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 (!(target->flags5 & MF5_NOPAIN) && inflictor != NULL &&
(inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS))
{
goto dopain;
}
return;
}
}
damage = target->TakeSpecialDamage (inflictor, source, damage, mod);
}
if (damage == -1)
{
return;
}
// 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;
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))
{
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]);
}
}
}
// [RH] Avoid friendly fire if enabled
if (!(flags & DMG_FORCED) && source != NULL &&
((player && player != source->player) || (!player && target != source)) &&
target->IsTeammate (source))
{
if (player)
FriendlyFire = true;
if (damage < TELEFRAG_DAMAGE)
{ // Still allow telefragging :-(
damage = (int)((float)damage * level.teamdamage);
if (damage <= 0)
return;
}
}
//
// player specific
//
if (player)
{
//Added by MC: Lets bots look allround for enemies if they survive an ambush.
if (player->isbot)
{
player->allround = true;
}
// end of game hell hack
if ((target->Sector->special & 255) == dDamage_End
&& damage >= target->health)
{
damage = target->health - 1;
}
if (!(flags & DMG_FORCED))
{
// check the real player, not a voodoo doll here for invulnerability effects
if (damage < TELEFRAG_DAMAGE && ((player->mo->flags2 & MF2_INVULNERABLE) ||
(player->cheats & CF_GODMODE)))
{ // player is invulnerable, so don't hurt him
return;
}
if (!(flags & DMG_NO_ARMOR) && player->mo->Inventory != NULL)
{
int newdam = damage;
player->mo->Inventory->AbsorbDamage (damage, mod, newdam);
damage = newdam;
if (damage <= 0)
{
// If MF6_FORCEPAIN is set, make the player enter the pain state.
if (!(target->flags5 & MF5_NOPAIN) && inflictor != NULL &&
(inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS))
{
goto dopain;
}
return;
}
}
if (damage >= player->health
&& (G_SkillProperty(SKILLP_AutoUseHealth) || deathmatch)
&& !player->morphTics)
{ // Try to use some inventory health
P_AutoUseHealth (player, damage - player->health + 1);
}
}
player->health -= damage; // mirror mobj health here for Dave
// [RH] Make voodoo dolls and real players record the same health
target->health = player->mo->health -= damage;
if (player->health < 50 && !deathmatch && !(flags & DMG_FORCED))
{
P_AutoUseStrifeHealth (player);
player->mo->health = player->health;
}
if (player->health <= 0)
{
// [SP] Buddha cheat: if the player is about to die, rescue him to 1 health.
// This does not save the player if damage >= TELEFRAG_DAMAGE, still need to
// telefrag him right? ;) (Unfortunately the damage is "absorbed" by armor,
// but telefragging should still do enough damage to kill the player)
if ((player->cheats & CF_BUDDHA) && damage < TELEFRAG_DAMAGE)
{
// If this is a voodoo doll we need to handle the real player as well.
player->mo->health = target->health = player->health = 1;
}
else
{
player->health = 0;
}
}
player->LastDamageType = mod;
player->attacker = source;
player->damagecount += damage; // add damage after armor / invuln
if (player->damagecount > 100)
{
player->damagecount = 100; // teleport stomp does 10k points...
}
temp = damage < 100 ? damage : 100;
if (player == &players[consoleplayer])
{
I_Tactile (40,10,40+temp*2);
}
}
else
{
// Armor for monsters.
if (!(flags & (DMG_NO_ARMOR|DMG_FORCED)) && target->Inventory != NULL && damage > 0)
{
int newdam = damage;
target->Inventory->AbsorbDamage (damage, mod, newdam);
damage = newdam;
if (damage <= 0)
{
return;
}
}
target->health -= damage;
}
//
// the damage has been dealt; now deal with the consequences
//
target->DamageTypeReceived = mod;
// If the damaging player has the power of drain, give the player 50% of the damage
// done in health.
if ( source && source->player && source->player->cheats & CF_DRAIN)
{
if (!target->player || target->player != source->player)
{
if ( P_GiveBody( source, damage / 2 ))
{
S_Sound( source, CHAN_ITEM, "*drainhealth", 1, ATTN_NORM );
}
}
}
if (target->health <= 0)
{ // Death
target->special1 = damage;
// check for special fire damage or ice damage deaths
if (mod == NAME_Fire)
{
if (player && !player->morphTics)
{ // Check for flame death
if (!inflictor ||
((target->health > -50) && (damage > 25)) ||
!(inflictor->flags5 & MF5_SPECIALFIREDAMAGE))
{
target->DamageType = NAME_Fire;
}
}
else
{
target->DamageType = NAME_Fire;
}
}
else
{
target->DamageType = mod;
}
if (source && source->tracer && (source->flags5 & MF5_SUMMONEDMONSTER))
{ // Minotaur's kills go to his master
// Make sure still alive and not a pointer to fighter head
if (source->tracer->player && (source->tracer->player->mo == source->tracer))
{
source = source->tracer;
}
}
target->Die (source, inflictor);
return;
}
woundstate = target->FindState(NAME_Wound, mod);
if (woundstate != NULL)
{
int woundhealth = RUNTIME_TYPE(target)->Meta.GetMetaInt (AMETA_WoundHealth, 6);
if (target->health <= woundhealth)
{
target->SetState (woundstate);
return;
}
}
if (!(target->flags5 & MF5_NOPAIN) && (inflictor == NULL || !(inflictor->flags5 & MF5_PAINLESS)) &&
!G_SkillProperty(SKILLP_NoPain) && !(target->flags & MF_SKULLFLY))
{
pc = target->GetClass()->ActorInfo->PainChances;
painchance = target->PainChance;
if (pc != NULL)
{
int *ppc = pc->CheckKey(mod);
if (ppc != NULL)
{
painchance = *ppc;
}
}
if ((damage >= target->PainThreshold && pr_damagemobj() < painchance) ||
(inflictor != NULL && (inflictor->flags6 & MF6_FORCEPAIN)))
{
dopain:
if (mod == NAME_Electric)
{
if (pr_lightning() < 96)
{
justhit = true;
FState *painstate = target->FindState(NAME_Pain, mod);
if (painstate != NULL)
target->SetState(painstate);
}
else
{ // "electrocute" the target
target->renderflags |= RF_FULLBRIGHT;
if ((target->flags3 & MF3_ISMONSTER) && pr_lightning() < 128)
{
target->Howl ();
}
}
}
else
{
justhit = true;
FState *painstate = target->FindState(NAME_Pain, ((inflictor && inflictor->PainType != NAME_None) ? inflictor->PainType : mod));
if (painstate != NULL)
target->SetState(painstate);
if (mod == NAME_PoisonCloud)
{
if ((target->flags3 & MF3_ISMONSTER) && pr_poison() < 128)
{
target->Howl ();
}
}
}
}
}
target->reactiontime = 0; // we're awake now...
if (source)
{
if (source == target->target)
{
target->threshold = BASETHRESHOLD;
if (target->state == target->SpawnState && target->SeeState != NULL)
{
target->SetState (target->SeeState);
}
}
else if (source != target->target && target->OkayToSwitchTarget (source))
{
// Target actor is not intent on another actor,
// so make him chase after source
// killough 2/15/98: remember last enemy, to prevent
// sleeping early; 2/21/98: Place priority on players
if (target->lastenemy == NULL ||
(target->lastenemy->player == NULL && target->TIDtoHate == 0) ||
target->lastenemy->health <= 0)
{
target->lastenemy = target->target; // remember last enemy - killough
}
target->target = source;
target->threshold = BASETHRESHOLD;
if (target->state == target->SpawnState && target->SeeState != NULL)
{
target->SetState (target->SeeState);
}
}
}
// killough 11/98: Don't attack a friend, unless hit by that friend.
if (justhit && (target->target == source || !target->target || !target->IsFriend(target->target)))
target->flags |= MF_JUSTHIT; // fight back!
}[/code][/spoiler]
Argh! Can you follow that code?