diff --git a/src/g_shared/a_armor.cpp b/src/g_shared/a_armor.cpp index bcd48d804b..664a56307e 100644 --- a/src/g_shared/a_armor.cpp +++ b/src/g_shared/a_armor.cpp @@ -115,7 +115,7 @@ void ABasicArmor::AbsorbDamage (int damage, FName damageType, int &newdamage) { int saved; - if (damageType != NAME_Drowning) + if (!DamageTypeDefinition::IgnoreArmor(damageType)) { int full = MAX(0, MaxFullAbsorb - AbsorbCount); if (damage < full) @@ -490,7 +490,7 @@ bool AHexenArmor::AddArmorToSlot (AActor *actor, int slot, int amount) void AHexenArmor::AbsorbDamage (int damage, FName damageType, int &newdamage) { - if (damageType != NAME_Drowning) + if (!DamageTypeDefinition::IgnoreArmor(damageType)) { fixed_t savedPercent = Slots[0] + Slots[1] + Slots[2] + Slots[3] + Slots[4]; diff --git a/src/info.cpp b/src/info.cpp index fe06e6f6b5..c15bf0a947 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -55,7 +55,6 @@ extern void LoadActors (); extern void InitBotStuff(); extern void ClearStrifeTypes(); - //========================================================================== // // @@ -545,3 +544,84 @@ CCMD (summonfoe) { SummonActor (DEM_SUMMONFOE, DEM_SUMMONFOE2, argv); } + + +// Damage type defaults / global settings + +TMap GlobalDamageDefinitions; + +void DamageTypeDefinition::Apply(FName const type) +{ + GlobalDamageDefinitions[type] = *this; +} + +DamageTypeDefinition *DamageTypeDefinition::Get(FName const type) +{ + return GlobalDamageDefinitions.CheckKey(type); +} + +bool DamageTypeDefinition::IgnoreArmor(FName const type) +{ + if (type == NAME_Drowning) return true; + DamageTypeDefinition *dtd = Get(type); + if (dtd) return dtd->NoArmor; + return false; +} + +//========================================================================== +// +// DamageTypeDefinition :: ApplyMobjDamageFactor +// +// Calculates mobj damage based on original damage, defined damage factors +// and damage type. +// +// If the specific damage type is not defined, the damage factor for +// type 'None' will be used (with 1.0 as a default value). +// +// Globally declared damage types may override or multiply the damage +// factor when 'None' is used as a fallback in this function. +// +//========================================================================== + +int DamageTypeDefinition::ApplyMobjDamageFactor(int damage, FName const type, DmgFactors const * const factors) +{ + if (factors) + { + // If the actor has named damage factors, look for a specific factor + fixed_t const *pdf = factors->CheckKey(type); + if (pdf) return FixedMul(damage, *pdf); // type specific damage type + + // If this was nonspecific damage, don't fall back to nonspecific search + if (type == NAME_None) return damage; + } + + // If this was nonspecific damage, don't fall back to nonspecific search + else if (type == NAME_None) + { + return damage; + } + else + { + // Normal is unsupplied / 1.0, so there's no difference between modifying and overriding + DamageTypeDefinition *dtd = Get(type); + return dtd ? FixedMul(damage, dtd->DefaultFactor) : damage; + } + + { + fixed_t const *pdf = factors->CheckKey(NAME_None); + // Here we are looking for modifications to untyped damage + // If the calling actor defines untyped damage factor, that is contained in "pdf". + if (pdf) // normal damage available + { + DamageTypeDefinition *dtd = Get(type); + + if (dtd) + { + if (dtd->ReplaceFactor) return FixedMul(damage, dtd->DefaultFactor); // use default instead of untyped factor + return FixedMul(damage, FixedMul(*pdf, dtd->DefaultFactor)); // use default as modification of untyped factor + } + return FixedMul(damage, *pdf); // there was no default, so actor default is used + } + } + return damage; +} diff --git a/src/info.h b/src/info.h index 60397d7f9b..7fcdeca9bb 100644 --- a/src/info.h +++ b/src/info.h @@ -44,6 +44,8 @@ #include "dobject.h" #include "doomdef.h" +#include "m_fixed.h" + struct Baggage; class FScanner; struct FActorInfo; @@ -185,6 +187,31 @@ typedef TMap PainChanceList; typedef TMap PainFlashList; typedef TMap FPlayerColorSetMap; + + +struct DamageTypeDefinition +{ +public: + DamageTypeDefinition() { Clear(); } + + fixed_t DefaultFactor; + bool ReplaceFactor; + bool NoArmor; + + void Apply(FName const type); + void Clear() + { + DefaultFactor = (fixed_t)1; + ReplaceFactor = false; + NoArmor = false; + } + + static DamageTypeDefinition *Get(FName const type); + static bool IgnoreArmor(FName const type); + static int ApplyMobjDamageFactor(int damage, FName const type, DmgFactors const * const factors); +}; + + struct FActorInfo { static void StaticInit (); diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index bc563dced2..1947eaf303 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -997,20 +997,12 @@ void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage if (!(flags & DMG_NO_FACTOR)) { - DmgFactors *df = target->GetClass()->ActorInfo->DamageFactors; - if (df != NULL) - { - fixed_t *pdf = df->CheckFactor(mod); - if (pdf != NULL) - { - damage = FixedMul(damage, *pdf); - if (damage <= 0) - return; - } - } damage = FixedMul(damage, target->DamageFactor); if (damage < 0) return; + damage = DamageTypeDefinition::ApplyMobjDamageFactor(damage, mod, target->GetClass()->ActorInfo->DamageFactors); + if (damage <= 0) + return; } damage = target->TakeSpecialDamage (inflictor, source, damage, mod); diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index ae236a58c3..1b820f9bb4 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -1179,7 +1179,50 @@ static void ParseActor(FScanner &sc) sc.SetCMode (false); } +//========================================================================== +// +// Reads a damage definition +// +//========================================================================== +static void ParseDamageDefinition(FScanner &sc) +{ + sc.SetCMode (true); // This may be 100% irrelevant for such a simple syntax, but I don't know + + // Get DamageType + + sc.MustGetString(); + FName damageType = sc.String; + + DamageTypeDefinition dtd; + + sc.MustGetToken('{'); + while (sc.MustGetAnyToken(), sc.TokenType != '}') + { + if (sc.Compare("FACTOR")) + { + sc.MustGetFloat(); + dtd.DefaultFactor = (fixed_t)sc.Float; + if (!dtd.DefaultFactor) dtd.ReplaceFactor = true; // Multiply by 0 yields 0: FixedMul(damage, FixedMul(factor, 0)) is more wasteful than FixedMul(factor, 0) + } + else if (sc.Compare("REPLACEFACTOR")) + { + dtd.ReplaceFactor = true; + } + else if (sc.Compare("NOARMOR")) + { + dtd.NoArmor = true; + } + else + { + sc.ScriptError("Unexpected data (%s) in damagetype definition.", sc.String); + } + } + + dtd.Apply(damageType); + + sc.SetCMode (false); // (set to true earlier in function) +} //========================================================================== // @@ -1263,6 +1306,11 @@ void ParseDecorate (FScanner &sc) ParseOldDecoration (sc, DEF_Projectile); break; } + else if (sc.Compare("DAMAGETYPE")) + { + ParseDamageDefinition(sc); + break; + } default: sc.RestorePos(pos); ParseOldDecoration(sc, DEF_Decoration);