Armor Goes First (#1334)

* conditions where infantry suffer only armor damage until it is depleted (not just as a max); valid for spitfire damage and collision

* no bonus damage on armor depletion
This commit is contained in:
Fate-JH 2025-12-23 13:03:24 -05:00 committed by GitHub
parent b8ea569b1c
commit 3182c79024
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 44 additions and 5 deletions

View file

@ -1781,6 +1781,8 @@ object GlobalDefinitionsProjectile {
spitfire_aa_ammo_projectile.ProjectileDamageTypeSecondary = DamageType.Splash
spitfire_aa_ammo_projectile.InitialVelocity = 100
spitfire_aa_ammo_projectile.Lifespan = 5f
spitfire_aa_ammo_projectile.DamageToArmorFirst = true
spitfire_aa_ammo_projectile.DamageToBattleframeOnly = true
ProjectileDefinition.CalculateDerivedFields(spitfire_aa_ammo_projectile)
spitfire_aa_ammo_projectile.Modifiers = List(
CerberusTurretWrongTarget,
@ -1796,6 +1798,7 @@ object GlobalDefinitionsProjectile {
spitfire_ammo_projectile.DegradeMultiplier = 0.5f
spitfire_ammo_projectile.InitialVelocity = 100
spitfire_ammo_projectile.Lifespan = .5f
spitfire_ammo_projectile.DamageToArmorFirst = true
spitfire_ammo_projectile.DamageToBattleframeOnly = true
ProjectileDefinition.CalculateDerivedFields(spitfire_ammo_projectile)

View file

@ -99,5 +99,6 @@ object CollisionReason {
* Damage is considered `Direct`, however, which defines some resistance. */
val noDamage = new DamageProperties {
CausesDamageType = DamageType.Direct
DamageToArmorFirst = true
}
}

View file

@ -21,7 +21,11 @@ trait DamageProperties
private var damageTypeSecondary: DamageType.Value = DamageType.None
/** against Infantry targets, damage does not apply to armor damage */
private var damageToHealthOnly: Boolean = false
/** against Vehicle targets, damage does not apply to vehicle shield */
/** against Infantry targets, damage does not apply to armor damage */
private var damageToArmorFirst: Boolean = false
/** against Infantry targets, damage applies to armor before it does health;
* regardless of other resistance conditions, non-zero armor is reduced before health;
* should not have priority over the flag for infantry health only */
private var damageToVehicleOnly: Boolean = false
/** against battleframe targets, damage does not apply to battleframe robotics shield;
* this is equivalent to the property "bfr_permeate_shield" */
@ -81,6 +85,13 @@ trait DamageProperties
DamageToHealthOnly
}
def DamageToArmorFirst : Boolean = damageToArmorFirst
def DamageToArmorFirst_=(armorFirst: Boolean) : Boolean = {
damageToArmorFirst = armorFirst
DamageToArmorFirst
}
def DamageToVehicleOnly : Boolean = damageToVehicleOnly
def DamageToVehicleOnly_=(vehicleOnly: Boolean) : Boolean = {

View file

@ -8,13 +8,15 @@ import net.psforever.objects.serverobject.damage.Damageable
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
import net.psforever.objects.vehicles.VehicleSubsystemEntry
import net.psforever.objects.vital.base.DamageResolution
import net.psforever.objects.vital.{DamagingActivity, Vitality, InGameHistory}
import net.psforever.objects.vital.{DamagingActivity, InGameHistory, Vitality}
import net.psforever.objects.vital.damage.DamageCalculations
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
import net.psforever.objects.vital.projectile.ProjectileReason
import net.psforever.objects.vital.resistance.ResistanceSelection
import net.psforever.types.{ExoSuitType, ImplantType}
import scala.annotation.unused
/**
* The base for the combining step of all projectile-induced damage calculation function literals.
*/
@ -37,13 +39,16 @@ object ResolutionCalculations {
type Output = PlanetSideGameObject with FactionAffinity => DamageResult
type Form = (DamageCalculations.Selector, ResistanceSelection.Format, DamageInteraction) => Output
def NoDamage(data: DamageInteraction)(a: Int, b: Int): Int = 0
def NoDamage(@unused data: DamageInteraction)(@unused a: Int, @unused b: Int): Int = 0
def InfantryDamage(data: DamageInteraction): (Int, Int) => (Int, Int) = {
data.target match {
case target: PlayerSource =>
if(data.cause.source.DamageToHealthOnly) {
if (data.cause.source.DamageToHealthOnly) {
DamageToHealthOnly(target.health)
} else if (data.cause.source.DamageToArmorFirst) {
InfantryArmorDamageFirst(target.health, target.armor)
} else {
InfantryDamageAfterResist(target.health, target.armor)
}
@ -84,6 +89,25 @@ object ResolutionCalculations {
}
}
def InfantryArmorDamageFirst(currentHP: Int, currentArmor: Int)(damages: Int, resistance: Int): (Int, Int) = {
if (damages > 0 && currentHP > 0) {
if (currentArmor <= 0) {
(damages, 0) //no armor; health damage
} else if (damages > resistance) {
val resistedDam = damages - resistance
if (resistedDam <= currentArmor) {
(0, resistedDam) //armor damage
} else {
(resistedDam, currentArmor) //deplete armor; health damage
}
} else {
(0, damages) //too weak; armor damage (less than resistance)
}
} else {
(0, 0) //no damage
}
}
def MaxDamage(data: DamageInteraction): (Int, Int) => (Int, Int) = {
data.target match {
case target: PlayerSource =>
@ -131,7 +155,7 @@ object ResolutionCalculations {
}
}
def NoApplication(damageValue: Int, data: DamageInteraction)(target: PlanetSideGameObject with FactionAffinity): DamageResult = {
def NoApplication(@unused damageValue: Int, data: DamageInteraction)(target: PlanetSideGameObject with FactionAffinity): DamageResult = {
val sameTarget = SourceEntry(target)
DamageResult(sameTarget, sameTarget, data)
}