Adds Modifier Armor (#7416)

This commit is contained in:
Neerti
2020-08-07 00:24:10 -04:00
committed by VirgoBot
parent dd7754514c
commit bc1db6310e
6 changed files with 267 additions and 19 deletions

View File

@@ -55,6 +55,14 @@
var/emp_modifier // Added to the EMP strength, which is an inverse scale from 1 to 4, with 1 being the strongest EMP. 5 is a nullification.
var/explosion_modifier // Added to the bomb strength, which is an inverse scale from 1 to 3, with 1 being gibstrength. 4 is a nullification.
// Note that these are combined with the mob's real armor values additatively. You can also omit specific armor types.
var/list/armor_percent = null // List of armor values to add to the holder when doing armor calculations. This is for percentage based armor. E.g. 50 = half damage.
var/list/armor_flat = null // Same as above but only for flat armor calculations. E.g. 5 = 5 less damage (this comes after percentage).
// Unlike armor, this is multiplicative. Two 50% protection modifiers will be combined into 75% protection (assuming no base protection on the mob).
var/heat_protection = null // Modifies how 'heat' protection is calculated, like wearing a firesuit. 1 = full protection.
var/cold_protection = null // Ditto, but for cold, like wearing a winter coat.
var/siemens_coefficient = null // Similar to above two vars but 0 = full protection, to be consistant with siemens numbers everywhere else.
var/vision_flags // Vision flags to add to the mob. SEE_MOB, SEE_OBJ, etc.
/datum/modifier/New(var/new_holder, var/new_origin)
@@ -186,10 +194,14 @@
// Checks if the mob has a modifier type.
/mob/living/proc/has_modifier_of_type(var/modifier_type)
return get_modifier_of_type(modifier_type) ? TRUE : FALSE
// Gets the first instance of a specific modifier type or subtype.
/mob/living/proc/get_modifier_of_type(var/modifier_type)
for(var/datum/modifier/M in modifiers)
if(istype(M, modifier_type))
return TRUE
return FALSE
return M
return null
// This displays the actual 'numbers' that a modifier is doing. Should only be shown in OOC contexts.
// When adding new effects, be sure to update this as well.

View File

@@ -398,3 +398,32 @@ the artifact triggers the rage.
/datum/modifier/outline_test/tick()
animate(filter_instance, size = 3, time = 0.25 SECONDS)
animate(size = 1, 0.25 SECONDS)
// Acts as a psuedo-godmode, yet probably is more reliable than the actual var for it nowdays.
// Can't protect from instantly killing things like singulos.
/datum/modifier/invulnerable
name = "invulnerable"
desc = "You are almost immune to harm, for a little while at least."
stacks = MODIFIER_STACK_EXTEND
disable_duration_percent = 0
incoming_damage_percent = 0
// bleeding_rate_percent = 0
pain_immunity = TRUE
armor_percent = list("melee" = 2000, "bullet" = 2000, "laser" = 2000, "bomb" = 2000, "energy" = 2000, "bio" = 2000, "rad" = 2000)
heat_protection = 1.0
cold_protection = 1.0
siemens_coefficient = 0.0
// Reduces resistance to "elements".
// Note that most things that do give resistance gives 100% protection,
// and due to multiplicitive stacking, this modifier won't do anything to change that.
/datum/modifier/elemental_vulnerability
name = "elemental vulnerability"
desc = "You're more vulnerable to extreme temperatures and electricity."
stacks = MODIFIER_STACK_EXTEND
heat_protection = -0.5
cold_protection = -0.5
siemens_coefficient = 1.5

View File

@@ -133,6 +133,12 @@ emp_act
if(istype(C) && (C.body_parts_covered & def_zone.body_part)) // Is that body part being targeted covered?
siemens_coefficient *= C.siemens_coefficient
// Modifiers.
for(var/thing in modifiers)
var/datum/modifier/M = thing
if(!isnull(M.siemens_coefficient))
siemens_coefficient *= M.siemens_coefficient
return siemens_coefficient
// Similar to above but is for the mob's overall protection, being the average of all slots.
@@ -150,11 +156,11 @@ emp_act
if(fire_stacks < 0) // Water makes you more conductive.
siemens_value *= 1.5
return (siemens_value/max(total, 1))
return (siemens_value / max(total, 1))
// Returns a number between 0 to 1, with 1 being total protection.
/mob/living/carbon/human/get_shock_protection()
return between(0, 1-get_siemens_coefficient_average(), 1)
return min(1 - get_siemens_coefficient_average(), 1) // Don't go above 1, but negatives are fine.
// Returns a list of clothing that is currently covering def_zone.
/mob/living/carbon/human/proc/get_clothing_list_organ(var/obj/item/organ/external/def_zone, var/type)
@@ -173,6 +179,13 @@ emp_act
var/list/protective_gear = def_zone.get_covering_clothing()
for(var/obj/item/clothing/gear in protective_gear)
protection += gear.armor[type]
for(var/thing in modifiers)
var/datum/modifier/M = thing
var/modifier_armor = LAZYACCESS(M.armor_percent, type)
if(modifier_armor)
protection += modifier_armor
return protection
/mob/living/carbon/human/proc/getsoak_organ(var/obj/item/organ/external/def_zone, var/type)
@@ -182,6 +195,13 @@ emp_act
var/list/protective_gear = def_zone.get_covering_clothing()
for(var/obj/item/clothing/gear in protective_gear)
soaked += gear.armorsoak[type]
for(var/thing in modifiers)
var/datum/modifier/M = thing
var/modifier_armor = LAZYACCESS(M.armor_flat, type)
if(modifier_armor)
soaked += modifier_armor
return soaked
// Checked in borer code

View File

@@ -836,7 +836,19 @@
/mob/living/carbon/human/get_heat_protection(temperature) //Temperature is the temperature you're being exposed to.
var/thermal_protection_flags = get_heat_protection_flags(temperature)
return get_thermal_protection(thermal_protection_flags)
. = get_thermal_protection(thermal_protection_flags)
. = 1 - . // Invert from 1 = immunity to 0 = immunity.
// Doing it this way makes multiplicative stacking not get out of hand, so two modifiers that give 0.5 protection will be combined to 0.75 in the end.
for(var/thing in modifiers)
var/datum/modifier/M = thing
if(!isnull(M.heat_protection))
. *= 1 - M.heat_protection
// Code that calls this expects 1 = immunity so we need to invert again.
. = 1 - .
. = min(., 1.0)
/mob/living/carbon/human/get_cold_protection(temperature)
if(COLD_RESISTANCE in mutations)
@@ -844,7 +856,20 @@
temperature = max(temperature, 2.7) //There is an occasional bug where the temperature is miscalculated in ares with a small amount of gas on them, so this is necessary to ensure that that bug does not affect this calculation. Space's temperature is 2.7K and most suits that are intended to protect against any cold, protect down to 2.0K.
var/thermal_protection_flags = get_cold_protection_flags(temperature)
return get_thermal_protection(thermal_protection_flags)
. = get_thermal_protection(thermal_protection_flags)
. = 1 - . // Invert from 1 = immunity to 0 = immunity.
// Doing it this way makes multiplicative stacking not get out of hand, so two modifiers that give 0.5 protection will be combined to 0.75 in the end.
for(var/thing in modifiers)
var/datum/modifier/M = thing
if(!isnull(M.cold_protection))
// Invert the modifier values so they align with the current working value.
. *= 1 - M.cold_protection
// Code that calls this expects 1 = immunity so we need to invert again.
. = 1 - .
. = min(., 1.0)
/mob/living/carbon/human/proc/get_thermal_protection(var/flags)
.=0

View File

@@ -140,7 +140,18 @@
// Cold stuff.
/mob/living/simple_mob/get_cold_protection()
return cold_resist
. = cold_resist
. = 1 - . // Invert from 1 = immunity to 0 = immunity.
// Doing it this way makes multiplicative stacking not get out of hand, so two modifiers that give 0.5 protection will be combined to 0.75 in the end.
for(var/thing in modifiers)
var/datum/modifier/M = thing
if(!isnull(M.cold_protection))
. *= 1 - M.cold_protection
// Code that calls this expects 1 = immunity so we need to invert again.
. = 1 - .
. = min(., 1.0)
// Fire stuff. Not really exciting at the moment.
@@ -154,7 +165,18 @@
return
/mob/living/simple_mob/get_heat_protection()
return heat_resist
. = heat_resist
. = 1 - . // Invert from 1 = immunity to 0 = immunity.
// Doing it this way makes multiplicative stacking not get out of hand, so two modifiers that give 0.5 protection will be combined to 0.75 in the end.
for(var/thing in modifiers)
var/datum/modifier/M = thing
if(!isnull(M.heat_protection))
. *= 1 - M.heat_protection
// Code that calls this expects 1 = immunity so we need to invert again.
. = 1 - .
. = min(., 1.0)
// Electricity
/mob/living/simple_mob/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null)
@@ -170,7 +192,18 @@
s.start()
/mob/living/simple_mob/get_shock_protection()
return shock_resist
. = shock_resist
. = 1 - . // Invert from 1 = immunity to 0 = immunity.
// Doing it this way makes multiplicative stacking not get out of hand, so two modifiers that give 0.5 protection will be combined to 0.75 in the end.
for(var/thing in modifiers)
var/datum/modifier/M = thing
if(!isnull(M.siemens_coefficient))
. *= M.siemens_coefficient
// Code that calls this expects 1 = immunity so we need to invert again.
. = 1 - .
. = min(., 1.0)
// Shot with taser/stunvolver
/mob/living/simple_mob/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon=null)
@@ -218,17 +251,29 @@
// Armor
/mob/living/simple_mob/getarmor(def_zone, attack_flag)
var/armorval = armor[attack_flag]
if(!armorval)
return 0
else
return armorval
if(isnull(armorval))
armorval = 0
for(var/thing in modifiers)
var/datum/modifier/M = thing
var/modifier_armor = LAZYACCESS(M.armor_percent, attack_flag)
if(modifier_armor)
armorval += modifier_armor
return armorval
/mob/living/simple_mob/getsoak(def_zone, attack_flag)
var/armorval = armor_soak[attack_flag]
if(!armorval)
return 0
else
return armorval
if(isnull(armorval))
armorval = 0
for(var/thing in modifiers)
var/datum/modifier/M = thing
var/modifier_armor = LAZYACCESS(M.armor_flat, attack_flag)
if(modifier_armor)
armorval += modifier_armor
return armorval
// Lightning
/mob/living/simple_mob/lightning_act()

View File

@@ -27,3 +27,120 @@
qdel(H)
return 1
/datum/modifier/unit_test
/datum/unit_test/modifier
name = "modifier test template"
var/mob/living/subject = null
var/subject_type = /mob/living/carbon/human
var/list/inputs = list(1.00, 0.75, 0.50, 0.25, 0.00, -0.50, -1.0, -2.0)
var/list/expected_outputs = list(1.00, 0.75, 0.50, 0.25, 0.00, -0.50, -1.0, -2.0)
var/datum/modifier/test_modifier = null
var/issues = 0
/datum/unit_test/modifier/start_test()
// Arrange.
subject = new subject_type(get_standard_turf())
subject.add_modifier(/datum/modifier/unit_test)
test_modifier = subject.get_modifier_of_type(/datum/modifier/unit_test)
// Act,
for(var/i = 1 to inputs.len)
set_tested_variable(test_modifier, inputs[i])
var/actual = round(get_test_value(subject), 0.01) // Rounding because floating point schannigans.
if(actual != expected_outputs[i])
issues++
log_bad("Input '[inputs[i]]' did not match expected output '[expected_outputs[i]]', but was instead '[actual]'.")
// Assert.
if(issues)
fail("[issues] issues were found.")
else
pass("No issues found.")
qdel(subject)
return TRUE
// Override for subtypes.
/datum/unit_test/modifier/proc/set_tested_variable(datum/modifier/M, new_value)
return
/datum/unit_test/modifier/proc/get_test_value(mob/living/L)
return
/datum/unit_test/modifier/heat_protection
name = "MOB: human mob heat protection is calculated correctly"
/datum/unit_test/modifier/heat_protection/set_tested_variable(datum/modifier/M, new_value)
M.heat_protection = new_value
/datum/unit_test/modifier/heat_protection/get_test_value(mob/living/L)
return L.get_heat_protection(1000)
/datum/unit_test/modifier/heat_protection/simple_mob
name = "MOB: simple mob heat protection is calculated correctly"
subject_type = /mob/living/simple_mob
/datum/unit_test/modifier/cold_protection
name = "MOB: human mob cold protection is calculated correctly"
/datum/unit_test/modifier/cold_protection/set_tested_variable(datum/modifier/M, new_value)
M.cold_protection = new_value
/datum/unit_test/modifier/cold_protection/get_test_value(mob/living/L)
return L.get_cold_protection(50)
/datum/unit_test/modifier/cold_protection/simple_mob
name = "MOB: simple mob cold protection is calculated correctly"
subject_type = /mob/living/simple_mob
/datum/unit_test/modifier/shock_protection
name = "MOB: human mob shock protection is calculated correctly"
inputs = list(3.00, 2.00, 1.50, 1.00, 0.75, 0.50, 0.25, 0.00)
expected_outputs = list(-2.00, -1.00, -0.50, 0.00, 0.25, 0.50, 0.75, 1.00)
/datum/unit_test/modifier/shock_protection/set_tested_variable(datum/modifier/M, new_value)
M.siemens_coefficient = new_value
/datum/unit_test/modifier/shock_protection/get_test_value(mob/living/L)
return L.get_shock_protection()
/datum/unit_test/modifier/shock_protection/simple_mob
name = "MOB: simple mob shock protection is calculated correctly"
subject_type = /mob/living/simple_mob
/datum/unit_test/modifier/percentage_armor
name = "MOB: human mob percentage armor is calculated correctly"
inputs = list(100, 75, 50, 25, 0)
expected_outputs = list(100, 75, 50, 25, 0)
/datum/unit_test/modifier/percentage_armor/set_tested_variable(datum/modifier/M, new_value)
M.armor_percent = list("melee" = new_value)
/datum/unit_test/modifier/percentage_armor/get_test_value(mob/living/L)
return L.getarmor(null, "melee")
/datum/unit_test/modifier/percentage_armor/simple_mob
name = "MOB: simple mob percentage armor is calculated correctly"
subject_type = /mob/living/simple_mob
/datum/unit_test/modifier/percentage_flat
name = "MOB: human mob flat armor is calculated correctly"
inputs = list(100, 75, 50, 25, 0)
expected_outputs = list(100, 75, 50, 25, 0)
/datum/unit_test/modifier/percentage_flat/set_tested_variable(datum/modifier/M, new_value)
M.armor_flat = list("melee" = new_value)
/datum/unit_test/modifier/percentage_flat/get_test_value(mob/living/L)
return L.getsoak(null, "melee")
/datum/unit_test/modifier/percentage_flat/simple_mob
name = "MOB: simple mob flat armor is calculated correctly"
subject_type = /mob/living/simple_mob