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/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. 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. var/vision_flags // Vision flags to add to the mob. SEE_MOB, SEE_OBJ, etc.
/datum/modifier/New(var/new_holder, var/new_origin) /datum/modifier/New(var/new_holder, var/new_origin)
@@ -186,10 +194,14 @@
// Checks if the mob has a modifier type. // Checks if the mob has a modifier type.
/mob/living/proc/has_modifier_of_type(var/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) for(var/datum/modifier/M in modifiers)
if(istype(M, modifier_type)) if(istype(M, modifier_type))
return TRUE return M
return FALSE return null
// This displays the actual 'numbers' that a modifier is doing. Should only be shown in OOC contexts. // 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. // When adding new effects, be sure to update this as well.

View File

@@ -397,4 +397,33 @@ the artifact triggers the rage.
/datum/modifier/outline_test/tick() /datum/modifier/outline_test/tick()
animate(filter_instance, size = 3, time = 0.25 SECONDS) animate(filter_instance, size = 3, time = 0.25 SECONDS)
animate(size = 1, 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? if(istype(C) && (C.body_parts_covered & def_zone.body_part)) // Is that body part being targeted covered?
siemens_coefficient *= C.siemens_coefficient 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 return siemens_coefficient
// Similar to above but is for the mob's overall protection, being the average of all slots. // 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. if(fire_stacks < 0) // Water makes you more conductive.
siemens_value *= 1.5 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. // Returns a number between 0 to 1, with 1 being total protection.
/mob/living/carbon/human/get_shock_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. // 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) /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() var/list/protective_gear = def_zone.get_covering_clothing()
for(var/obj/item/clothing/gear in protective_gear) for(var/obj/item/clothing/gear in protective_gear)
protection += gear.armor[type] 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 return protection
/mob/living/carbon/human/proc/getsoak_organ(var/obj/item/organ/external/def_zone, var/type) /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() var/list/protective_gear = def_zone.get_covering_clothing()
for(var/obj/item/clothing/gear in protective_gear) for(var/obj/item/clothing/gear in protective_gear)
soaked += gear.armorsoak[type] 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 return soaked
// Checked in borer code // Checked in borer code

View File

@@ -717,7 +717,7 @@
cold_dam = COLD_DAMAGE_LEVEL_1 cold_dam = COLD_DAMAGE_LEVEL_1
take_overall_damage(burn=cold_dam, used_weapon = "Low Body Temperature") take_overall_damage(burn=cold_dam, used_weapon = "Low Body Temperature")
else clear_alert("temp") else clear_alert("temp")
// Account for massive pressure differences. Done by Polymorph // Account for massive pressure differences. Done by Polymorph
@@ -836,7 +836,19 @@
/mob/living/carbon/human/get_heat_protection(temperature) //Temperature is the temperature you're being exposed to. /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) 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) /mob/living/carbon/human/get_cold_protection(temperature)
if(COLD_RESISTANCE in mutations) 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. 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) 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) /mob/living/carbon/human/proc/get_thermal_protection(var/flags)
.=0 .=0

View File

@@ -140,7 +140,18 @@
// Cold stuff. // Cold stuff.
/mob/living/simple_mob/get_cold_protection() /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. // Fire stuff. Not really exciting at the moment.
@@ -154,7 +165,18 @@
return return
/mob/living/simple_mob/get_heat_protection() /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 // Electricity
/mob/living/simple_mob/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null) /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() s.start()
/mob/living/simple_mob/get_shock_protection() /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 // Shot with taser/stunvolver
/mob/living/simple_mob/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon=null) /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 // Armor
/mob/living/simple_mob/getarmor(def_zone, attack_flag) /mob/living/simple_mob/getarmor(def_zone, attack_flag)
var/armorval = armor[attack_flag] var/armorval = armor[attack_flag]
if(!armorval) if(isnull(armorval))
return 0 armorval = 0
else
return armorval 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) /mob/living/simple_mob/getsoak(def_zone, attack_flag)
var/armorval = armor_soak[attack_flag] var/armorval = armor_soak[attack_flag]
if(!armorval) if(isnull(armorval))
return 0 armorval = 0
else
return armorval 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 // Lightning
/mob/living/simple_mob/lightning_act() /mob/living/simple_mob/lightning_act()

View File

@@ -27,3 +27,120 @@
qdel(H) qdel(H)
return 1 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