mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-28 15:02:01 +00:00
DamageMobj Refactor
- Split off all reactive functionality (pain, infighting, etc) into its own function, ReactToDamage. - Refactored all DamageMobj's damage <= 0 values. - Any unconditional cancellations now return -1. ReactToDamage will not be called if values < 0. - All pain/wound/target changing allowances return 0.
This commit is contained in:
parent
d2d684a35a
commit
2f7fae2fb0
1 changed files with 184 additions and 221 deletions
|
@ -783,6 +783,158 @@ void P_AutoUseStrifeHealth (player_t *player)
|
|||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ReactToDamage
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static inline bool MustForcePain(AActor *target, AActor *inflictor)
|
||||
{
|
||||
return (inflictor && (inflictor->flags6 & MF6_FORCEPAIN));
|
||||
}
|
||||
|
||||
static inline bool isFakePain(AActor *target, AActor *inflictor, int damage)
|
||||
{
|
||||
return ((target->flags7 & MF7_ALLOWPAIN && damage > 0) || (inflictor && (inflictor->flags7 & MF7_CAUSEPAIN)));
|
||||
}
|
||||
|
||||
// [MC] Completely ripped out of DamageMobj to make it less messy.
|
||||
static void ReactToDamage(AActor *target, AActor *inflictor, AActor *source, int damage, FName mod, int flags)
|
||||
{
|
||||
bool justhit = false;
|
||||
int painchance = 0;
|
||||
FState *woundstate = nullptr;
|
||||
bool fakedPain = false;
|
||||
bool forcedPain = false;
|
||||
bool noPain = false;
|
||||
|
||||
// Dead or non-existent entity, do not react.
|
||||
if (target == nullptr || target->health < 1 || damage < 0)
|
||||
return;
|
||||
|
||||
player_t *player = target->player;
|
||||
if (player)
|
||||
{
|
||||
if ((player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NOPAIN) ||
|
||||
((player->cheats & CF_GODMODE) && damage < TELEFRAG_DAMAGE))
|
||||
return;
|
||||
}
|
||||
|
||||
noPain = (flags & DMG_NO_PAIN) || (target->flags5 & MF5_NOPAIN) || (inflictor && (inflictor->flags5 & MF5_PAINLESS));
|
||||
|
||||
// Are we attempting to cause pain?
|
||||
if (!noPain)
|
||||
{
|
||||
fakedPain = (isFakePain(target, inflictor, damage));
|
||||
forcedPain = (MustForcePain(target, inflictor));
|
||||
}
|
||||
|
||||
// [MC] No forced or faked pain so skip it.
|
||||
// However the rest of the function must carry on.
|
||||
if (!noPain && damage < 1 && !fakedPain && !forcedPain)
|
||||
noPain = true;
|
||||
|
||||
woundstate = target->FindState(NAME_Wound, mod);
|
||||
if (woundstate != NULL)
|
||||
{
|
||||
int woundhealth = target->WoundHealth;
|
||||
|
||||
if (target->health <= woundhealth)
|
||||
{
|
||||
target->SetState(woundstate);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!noPain &&
|
||||
(target->player != nullptr || !G_SkillProperty(SKILLP_NoPain)) && !(target->flags & MF_SKULLFLY))
|
||||
{
|
||||
painchance = target->PainChance;
|
||||
for (auto & pc : target->GetInfo()->PainChances)
|
||||
{
|
||||
if (pc.first == mod)
|
||||
{
|
||||
painchance = pc.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (forcedPain || ((damage >= target->PainThreshold) && (pr_damagemobj() < painchance)))
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (target->player == nullptr) target->reactiontime = 0; // we're awake now...
|
||||
if (source)
|
||||
{
|
||||
if (source == target->target)
|
||||
{
|
||||
target->threshold = target->DefThreshold;
|
||||
if (target->state == target->SpawnState && target->SeeState != NULL)
|
||||
{
|
||||
target->SetState(target->SeeState);
|
||||
}
|
||||
}
|
||||
else if (source != target->target && target->CallOkayToSwitchTarget(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 = target->DefThreshold;
|
||||
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!
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
=
|
||||
|
@ -798,16 +950,6 @@ void P_AutoUseStrifeHealth (player_t *player)
|
|||
==================
|
||||
*/
|
||||
|
||||
static inline bool MustForcePain(AActor *target, AActor *inflictor)
|
||||
{
|
||||
return (inflictor && (inflictor->flags6 & MF6_FORCEPAIN));
|
||||
}
|
||||
|
||||
static inline bool isFakePain(AActor *target, AActor *inflictor, int damage)
|
||||
{
|
||||
return ((target->flags7 & MF7_ALLOWPAIN && damage > 0) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN)));
|
||||
}
|
||||
|
||||
|
||||
// Returns the amount of damage actually inflicted upon the target, or -1 if
|
||||
// the damage was cancelled.
|
||||
|
@ -815,16 +957,8 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
{
|
||||
player_t *player = NULL;
|
||||
int temp;
|
||||
int painchance = 0;
|
||||
FState * woundstate = NULL;
|
||||
bool justhit = false;
|
||||
bool plrDontThrust = false;
|
||||
bool invulpain = false;
|
||||
bool fakedPain = false;
|
||||
bool forcedPain = false;
|
||||
bool noPain = false;
|
||||
int fakeDamage = 0;
|
||||
int holdDamage = 0;
|
||||
const int rawdamage = damage;
|
||||
const bool telefragDamage = (rawdamage >= TELEFRAG_DAMAGE);
|
||||
|
||||
|
@ -832,32 +966,23 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
|
||||
if (target == NULL || !((target->flags & MF_SHOOTABLE) || (target->flags6 & MF6_VULNERABLE)))
|
||||
{ // Shouldn't happen
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
FName MeansOfDeath = mod;
|
||||
|
||||
// Rather than unnecessarily call the function over and over again, let's be a little more efficient.
|
||||
// But first, check and see if it's even needed, which it won't be if pain must not be triggered.
|
||||
noPain = ((flags & DMG_NO_PAIN) || (target->flags5 & MF5_NOPAIN) || (inflictor && (inflictor->flags5 & MF5_PAINLESS)));
|
||||
if (!noPain)
|
||||
{
|
||||
fakedPain = (isFakePain(target, inflictor, damage));
|
||||
forcedPain = (MustForcePain(target, inflictor));
|
||||
}
|
||||
|
||||
// Spectral targets only take damage from spectral projectiles.
|
||||
if (target->flags4 & MF4_SPECTRAL && !telefragDamage)
|
||||
{
|
||||
if (inflictor == NULL || !(inflictor->flags4 & MF4_SPECTRAL))
|
||||
{
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (target->health <= 0)
|
||||
{
|
||||
if (inflictor && mod == NAME_Ice && !(inflictor->flags7 & MF7_ICESHATTER))
|
||||
{
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
else if (target->flags & MF_ICECORPSE) // frozen
|
||||
{
|
||||
|
@ -865,7 +990,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
target->flags6 |= MF6_SHATTERING;
|
||||
target->Vel.Zero();
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
if (target == source && (!telefragDamage || target->flags7 & MF7_LAXTELEFRAGDMG))
|
||||
{
|
||||
|
@ -882,15 +1007,6 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
{
|
||||
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 0;
|
||||
}
|
||||
}
|
||||
|
@ -899,9 +1015,6 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
// Players are optionally excluded from getting thrust by damage.
|
||||
if (static_cast<APlayerPawn *>(target)->PlayerFlags & PPF_NOTHRUSTWHENINVUL)
|
||||
{
|
||||
if (fakedPain)
|
||||
plrDontThrust = 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -931,7 +1044,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
if (target->flags2 & MF2_DORMANT)
|
||||
{
|
||||
// Invulnerable, and won't wake up
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!telefragDamage || (target->flags7 & MF7_LAXTELEFRAGDMG)) // TELEFRAG_DAMAGE may only be reduced with LAXTELEFRAGDMG or it may not guarantee its effect.
|
||||
|
@ -959,19 +1072,19 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
if (player != NULL)
|
||||
{
|
||||
if (!deathmatch && inflictor->FriendPlayer > 0)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
else if (target->flags4 & MF4_SPECTRAL)
|
||||
{
|
||||
if (inflictor->FriendPlayer == 0 && !target->IsHostile(inflictor))
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
damage = inflictor->CallDoSpecialDamage(target, damage, mod);
|
||||
if (damage < 0)
|
||||
{
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1005,19 +1118,9 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
// '<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 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target->flags5 & MF5_NODAMAGE)
|
||||
{
|
||||
damage = 0;
|
||||
|
@ -1026,7 +1129,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
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 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1092,21 +1195,9 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
if (!telefragDamage || (target->flags7 & MF7_LAXTELEFRAGDMG))
|
||||
{ // Still allow telefragging :-(
|
||||
damage = (int)(damage * level.teamdamage);
|
||||
if (damage < 0)
|
||||
if (damage <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (damage == 0)
|
||||
{
|
||||
if (forcedPain)
|
||||
{
|
||||
goto dopain;
|
||||
}
|
||||
else if (fakedPain)
|
||||
{
|
||||
goto fakepain;
|
||||
}
|
||||
return 0;
|
||||
return (damage < 0) ? -1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1141,16 +1232,6 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
(player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NODAMAGE))
|
||||
//Absolutely no hurting if NODAMAGE is involved. Same for GODMODE2.
|
||||
{ // player is invulnerable, so don't hurt him
|
||||
//Make sure no godmodes and NOPAIN flags are found first.
|
||||
//Then, check to see if the player has NODAMAGE or ALLOWPAIN, or inflictor has CAUSEPAIN.
|
||||
if ((flags & DMG_NO_PAIN) || (player->cheats & CF_GODMODE) || (player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NOPAIN))
|
||||
return 0;
|
||||
else if ((((player->mo->flags7 & MF7_ALLOWPAIN) || (player->mo->flags5 & MF5_NODAMAGE)) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))))
|
||||
{
|
||||
invulpain = true;
|
||||
goto fakepain;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
// Armor for players.
|
||||
|
@ -1169,20 +1250,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
|
||||
if (damage <= 0)
|
||||
{
|
||||
// [MC] Godmode doesn't need checking here, it's already being handled above.
|
||||
if (noPain)
|
||||
return 0;
|
||||
|
||||
// If MF6_FORCEPAIN is set, make the player enter the pain state.
|
||||
if ((inflictor && (inflictor->flags6 & MF6_FORCEPAIN)))
|
||||
goto dopain;
|
||||
else if (((player->mo->flags7 & MF7_ALLOWPAIN) && (rawdamage > 0)) ||
|
||||
(inflictor && (inflictor->flags7 & MF7_CAUSEPAIN)))
|
||||
{
|
||||
invulpain = true;
|
||||
goto fakepain;
|
||||
}
|
||||
return 0;
|
||||
return (damage < 0) ? -1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1246,10 +1314,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
damage = newdam;
|
||||
if (damage <= 0)
|
||||
{
|
||||
if (fakedPain)
|
||||
goto fakepain;
|
||||
else
|
||||
return 0;
|
||||
return (damage < 0) ? -1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1295,7 +1360,6 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
if (target->health <= 0)
|
||||
{
|
||||
//[MC]Buddha flag for monsters.
|
||||
|
@ -1350,115 +1414,6 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
|||
return realdamage;
|
||||
}
|
||||
}
|
||||
|
||||
woundstate = target->FindState(NAME_Wound, mod);
|
||||
if (woundstate != NULL)
|
||||
{
|
||||
int woundhealth = target->WoundHealth;
|
||||
|
||||
if (target->health <= woundhealth)
|
||||
{
|
||||
target->SetState (woundstate);
|
||||
return MAX(0, damage);
|
||||
}
|
||||
}
|
||||
|
||||
fakepain: //Needed so we can skip the rest of the above, but still obey the original rules.
|
||||
|
||||
if (!noPain &&
|
||||
(target->player != NULL || !G_SkillProperty(SKILLP_NoPain)) && !(target->flags & MF_SKULLFLY))
|
||||
{
|
||||
painchance = target->PainChance;
|
||||
for (auto & pc : target->GetInfo()->PainChances)
|
||||
{
|
||||
if (pc.first == mod)
|
||||
{
|
||||
painchance = pc.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//ALLOWPAIN and CAUSEPAIN can still trigger infighting, even if no pain state is worked out.
|
||||
if (target->player == nullptr) target->reactiontime = 0; // we're awake now...
|
||||
if (source)
|
||||
{
|
||||
if (source == target->target)
|
||||
{
|
||||
target->threshold = target->DefThreshold;
|
||||
if (target->state == target->SpawnState && target->SeeState != NULL)
|
||||
{
|
||||
target->SetState (target->SeeState);
|
||||
}
|
||||
}
|
||||
else if (source != target->target && target->CallOkayToSwitchTarget (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 = target->DefThreshold;
|
||||
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!
|
||||
|
||||
if (invulpain) //Note that this takes into account all the cheats a player has, in terms of invulnerability.
|
||||
{
|
||||
return 0; //NOW we return -1!
|
||||
}
|
||||
return MAX(0, damage);
|
||||
}
|
||||
|
||||
|
@ -1475,11 +1430,15 @@ DEFINE_ACTION_FUNCTION(AActor, DamageMobj)
|
|||
// [ZZ] event handlers need the result.
|
||||
bool needevent = true;
|
||||
int realdamage = DamageMobj(self, inflictor, source, damage, mod, flags, angle, needevent);
|
||||
if (realdamage && needevent)
|
||||
if (realdamage >= 0)
|
||||
ReactToDamage(self, inflictor, source, realdamage, mod, flags);
|
||||
|
||||
if (realdamage > 0 && needevent)
|
||||
{
|
||||
E_WorldThingDamaged(self, inflictor, source, realdamage, mod, flags, angle);
|
||||
}
|
||||
ACTION_RETURN_INT(realdamage);
|
||||
|
||||
ACTION_RETURN_INT(MAX(0,realdamage));
|
||||
}
|
||||
|
||||
int P_DamageMobj(AActor *target, AActor *inflictor, AActor *source, int damage, FName mod, int flags, DAngle angle)
|
||||
|
@ -1497,12 +1456,16 @@ int P_DamageMobj(AActor *target, AActor *inflictor, AActor *source, int damage,
|
|||
{
|
||||
bool needevent = true;
|
||||
int realdamage = DamageMobj(target, inflictor, source, damage, mod, flags, angle, needevent);
|
||||
if (realdamage && needevent)
|
||||
if (realdamage >= 0) //Keep this check separated. Mods relying upon negative numbers may break otherwise.
|
||||
ReactToDamage(target, inflictor, source, realdamage, mod, flags);
|
||||
|
||||
if (realdamage > 0 && needevent)
|
||||
{
|
||||
// [ZZ] event handlers only need the resultant damage (they can't do anything about it anyway)
|
||||
E_WorldThingDamaged(target, inflictor, source, realdamage, mod, flags, angle);
|
||||
}
|
||||
return realdamage;
|
||||
|
||||
return MAX(0,realdamage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue