diff --git a/code/__defines/species.dm b/code/__defines/species.dm
index 0ff09486031..791e81cd17d 100644
--- a/code/__defines/species.dm
+++ b/code/__defines/species.dm
@@ -8,6 +8,7 @@
#define SPECIES_FLAG_NO_BLOCK BITFLAG(6) // Unable to block or defend itself from attackers.
#define SPECIES_FLAG_NEED_DIRECT_ABSORB BITFLAG(7) // This species can only have their DNA taken by direct absorption.
#define SPECIES_FLAG_LOW_GRAV_ADAPTED BITFLAG(8) // This species is used to lower than standard gravity, affecting stamina in high-grav
+#define SPECIES_FLAG_ABSORB_ELECTRICITY BITFLAG(9) // This species can absorb electricity; snowflake flag for old slime people.
// Species spawn flags
#define SPECIES_IS_WHITELISTED BITFLAG(0) // Must be whitelisted to play.
diff --git a/code/datums/wires/smes.dm b/code/datums/wires/smes.dm
index 15eff146d15..7cc90b2d664 100644
--- a/code/datums/wires/smes.dm
+++ b/code/datums/wires/smes.dm
@@ -9,68 +9,68 @@
new /datum/wire_description(SMES_WIRE_FAILSAFES, "This wire appears to connect to a failsafe mechanism.")
)
-var/global/const/SMES_WIRE_RCON = 1 // Remote control (AI and consoles), cut to disable
-var/global/const/SMES_WIRE_INPUT = 2 // Input wire, cut to disable input, pulse to disable for 60s
-var/global/const/SMES_WIRE_OUTPUT = 4 // Output wire, cut to disable output, pulse to disable for 60s
-var/global/const/SMES_WIRE_GROUNDING = 8 // Cut to quickly discharge causing sparks, pulse to only create few sparks
-var/global/const/SMES_WIRE_FAILSAFES = 16 // Cut to disable failsafes, mend to reenable
-
-
-/datum/wires/smes/CanUse(var/mob/living/L)
- var/obj/machinery/power/smes/buildable/S = holder
- if(!S.grounding && S.powernet && S.powernet.avail)
- electrocute_mob(L, S.powernet, S, S.safeties_enabled? 0.1 : 1)
- if(S.panel_open)
- return 1
- return 0
+/// Remote control (AI and consoles), cut to disable
+var/global/const/SMES_WIRE_RCON = BITFLAG(0)
+/// Input wire, cut to disable input, pulse to disable for 60s
+var/global/const/SMES_WIRE_INPUT = BITFLAG(1)
+/// Output wire, cut to disable output, pulse to disable for 60s
+var/global/const/SMES_WIRE_OUTPUT = BITFLAG(2)
+/// Cut to quickly discharge causing sparks, pulse to only create few sparks
+var/global/const/SMES_WIRE_GROUNDING = BITFLAG(3)
+/// Cut to disable failsafes, mend to reenable
+var/global/const/SMES_WIRE_FAILSAFES = BITFLAG(4)
+/datum/wires/smes/CanUse(var/mob/living/user)
+ var/obj/machinery/power/smes/buildable/storage = holder
+ if(!storage.grounding && storage.powernet && storage.powernet.avail)
+ electrocute_mob(user, storage.powernet, storage, (storage.safeties_enabled? 0.1 : 1))
+ return storage.panel_open
/datum/wires/smes/GetInteractWindow(mob/user)
- var/obj/machinery/power/smes/buildable/S = holder
+ var/obj/machinery/power/smes/buildable/storage = holder
. += ..()
- . += "The green light is [(S.input_cut || S.input_pulsed || S.output_cut || S.output_pulsed) ? "off" : "on"]
"
- . += "The red light is [(S.safeties_enabled || S.grounding) ? "off" : "blinking"]
"
- . += "The blue light is [S.RCon ? "on" : "off"]"
-
+ . += "The green light is [(storage.input_cut || storage.input_pulsed || storage.output_cut || storage.output_pulsed) ? "off" : "on"]
"
+ . += "The red light is [(storage.safeties_enabled || storage.grounding) ? "off" : "blinking"]
"
+ . += "The blue light is [storage.RCon ? "on" : "off"]"
/datum/wires/smes/UpdateCut(var/index, var/mended)
- var/obj/machinery/power/smes/buildable/S = holder
+ var/obj/machinery/power/smes/buildable/storage = holder
switch(index)
if(SMES_WIRE_RCON)
- S.RCon = mended
+ storage.RCon = mended
if(SMES_WIRE_INPUT)
- S.input_cut = !mended
+ storage.input_cut = !mended
if(SMES_WIRE_OUTPUT)
- S.output_cut = !mended
+ storage.output_cut = !mended
if(SMES_WIRE_GROUNDING)
- S.grounding = mended
+ storage.grounding = mended
if(SMES_WIRE_FAILSAFES)
- S.safeties_enabled = mended
+ storage.safeties_enabled = mended
/datum/wires/smes/proc/reset_rcon()
- var/obj/machinery/power/smes/buildable/S = holder
- if(S)
- S.RCon = TRUE
+ var/obj/machinery/power/smes/buildable/storage = holder
+ if(storage)
+ storage.RCon = TRUE
/datum/wires/smes/proc/reset_safeties()
- var/obj/machinery/power/smes/buildable/S = holder
- if(S)
- S.safeties_enabled = TRUE
+ var/obj/machinery/power/smes/buildable/storage = holder
+ if(storage)
+ storage.safeties_enabled = TRUE
/datum/wires/smes/UpdatePulsed(var/index)
- var/obj/machinery/power/smes/buildable/S = holder
+ var/obj/machinery/power/smes/buildable/storage = holder
switch(index)
if(SMES_WIRE_RCON)
- if(S.RCon)
- S.RCon = 0
+ if(storage.RCon)
+ storage.RCon = 0
addtimer(CALLBACK(src, PROC_REF(reset_rcon)), 1 SECOND)
if(SMES_WIRE_INPUT)
- S.toggle_input()
+ storage.toggle_input()
if(SMES_WIRE_OUTPUT)
- S.toggle_output()
+ storage.toggle_output()
if(SMES_WIRE_GROUNDING)
- S.grounding = 0
+ storage.grounding = 0
if(SMES_WIRE_FAILSAFES)
- if(S.safeties_enabled)
- S.safeties_enabled = 0
+ if(storage.safeties_enabled)
+ storage.safeties_enabled = 0
addtimer(CALLBACK(src, PROC_REF(reset_safeties)), 1 SECOND)
diff --git a/code/game/objects/items/weapons/material/shards.dm b/code/game/objects/items/weapons/material/shards.dm
index cfad08854a4..5035124e1b9 100644
--- a/code/game/objects/items/weapons/material/shards.dm
+++ b/code/game/objects/items/weapons/material/shards.dm
@@ -104,7 +104,7 @@
playsound(src.loc, 'sound/effects/glass_step.ogg', 50, 1) // not sure how to handle metal shards with sounds
var/decl/species/walker_species = M.get_species()
- if(walker_species && (walker_species.siemens_coefficient<0.5 || (walker_species.species_flags & (SPECIES_FLAG_NO_EMBED|SPECIES_FLAG_NO_MINOR_CUT)))) //Thick skin.
+ if(walker_species?.species_flags & (SPECIES_FLAG_NO_EMBED|SPECIES_FLAG_NO_MINOR_CUT)) //Thick skin.
return
var/obj/item/shoes = M.get_equipped_item(slot_shoes_str)
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index c5231af0360..43a18ba6657 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -85,7 +85,8 @@
add_overlay(I)
/obj/structure/grille/Bumped(atom/user)
- if(ismob(user)) shock(user, 70)
+ if(ismob(user))
+ shock(user, 70)
/obj/structure/grille/attack_hand(mob/user)
@@ -226,25 +227,23 @@
// returns 1 if shocked, 0 otherwise
/obj/structure/grille/proc/shock(mob/user, prb)
if(!anchored || destroyed) // anchored/destroyed grilles are never connected
- return 0
+ return FALSE
if(!(material.conductive))
- return 0
+ return FALSE
if(!prob(prb))
- return 0
+ return FALSE
if(!in_range(src, user))//To prevent TK and exosuit users from getting shocked
- return 0
- var/turf/T = get_turf(src)
- var/obj/structure/cable/C = T.get_cable_node()
- if(C)
- if(electrocute_mob(user, C, src))
- if(C.powernet)
- C.powernet.trigger_warning()
- spark_at(src, cardinal_only = TRUE)
- if(HAS_STATUS(user, STAT_STUN))
- return 1
- else
- return 0
- return 0
+ return FALSE
+ var/turf/my_turf = get_turf(src)
+ var/obj/structure/cable/cable = my_turf.get_cable_node()
+ if(!cable)
+ return FALSE
+ if(!electrocute_mob(user, cable, src))
+ return FALSE
+ if(cable.powernet)
+ cable.powernet.trigger_warning()
+ spark_at(src, cardinal_only = TRUE)
+ return !!HAS_STATUS(user, STAT_STUN)
/obj/structure/grille/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
if(!destroyed)
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index 1cde56a39dc..6c737fb8fee 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -405,3 +405,9 @@
var/org = GET_EXTERNAL_ORGAN(src, hand_slot)
if(org)
LAZYDISTINCTADD(., org)
+
+/mob/proc/get_active_hand_bodypart_flags()
+ var/datum/inventory_slot/gripper/inv_slot = get_inventory_slot_datum(get_active_held_item_slot())
+ if(istype(inv_slot))
+ . = inv_slot.covering_slot_flags
+ . ||= SLOT_HANDS
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index bcfa4ea3505..018ea79c0dc 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -66,29 +66,26 @@
gib()
/mob/living/carbon/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null)
- if(status_flags & GODMODE) return 0 //godmode
- shock_damage = apply_shock(shock_damage, def_zone, siemens_coeff)
-
- if(!shock_damage)
+ shock_damage = ..()
+ if(shock_damage <= 0)
return 0
+ shock_damage = apply_shock(shock_damage, def_zone, siemens_coeff)
stun_effect_act(agony_amount=shock_damage, def_zone=def_zone)
-
playsound(loc, "sparks", 50, 1, -1)
if (shock_damage > 15)
- src.visible_message(
- "[src] was electrocuted[source ? " by the [source]" : ""]!", \
- "You feel a powerful shock course through your body!", \
- "You hear a heavy electrical crack." \
+ visible_message(
+ SPAN_DANGER("\The [src] was electrocuted[source ? " by the [source]" : ""]!"),
+ SPAN_DANGER("You feel a powerful shock course through your body!"),
+ SPAN_DANGER("You hear a heavy electrical crack.")
)
else
- src.visible_message(
- "[src] was shocked[source ? " by the [source]" : ""].", \
- "You feel a shock course through your body.", \
- "You hear a zapping sound." \
+ visible_message(
+ SPAN_DANGER("\The [src] was shocked[source ? " by the [source]" : ""]."),
+ SPAN_DANGER("You feel a shock course through your body."),
+ SPAN_DANGER("You hear a zapping sound.")
)
-
switch(shock_damage)
if(11 to 15)
SET_STATUS_MAX(src, STAT_STUN, 1)
@@ -100,11 +97,8 @@
SET_STATUS_MAX(src, STAT_WEAK, 5)
if(31 to INFINITY)
SET_STATUS_MAX(src, STAT_WEAK, 10) //This should work for now, more is really silly and makes you lay there forever
-
set_status(STAT_JITTER, min(shock_damage*5, 200))
-
spark_at(loc, amount=5, cardinal_only = TRUE)
-
return shock_damage
/mob/living/carbon/proc/apply_shock(var/shock_damage, var/def_zone, var/siemens_coeff = 1.0)
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 7d337970d53..26bb3c64f94 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -85,19 +85,6 @@ meteor_act
// Add inherent armor to the end of list so that protective equipment is checked first
. += ..()
-//this proc returns the Siemens coefficient of electrical resistivity for a particular external organ.
-/mob/living/carbon/human/proc/get_siemens_coefficient_organ(var/obj/item/organ/external/def_zone)
- if (!def_zone)
- return 1.0
-
- var/siemens_coefficient = max(species.siemens_coefficient,0)
- for(var/slot in global.standard_clothing_slots)
- var/obj/item/clothing/C = get_equipped_item(slot)
- if(istype(C) && (C.body_parts_covered & def_zone.body_part)) // Is that body part being targeted covered?
- siemens_coefficient *= C.siemens_coefficient
-
- return siemens_coefficient
-
/mob/living/carbon/human/proc/check_head_coverage()
for(var/slot in global.standard_headgear_slots)
var/obj/item/clothing/clothes = get_equipped_item(slot)
@@ -373,24 +360,6 @@ meteor_act
fire_act(air, temperature)
return FALSE
-//Removed the horrible safety parameter. It was only being used by ninja code anyways.
-//Now checks siemens_coefficient of the affected area by default
-/mob/living/carbon/human/electrocute_act(var/shock_damage, var/obj/source, var/base_siemens_coeff = 1.0, var/def_zone = null)
-
- if(status_flags & GODMODE) return 0 //godmode
-
- if(species.siemens_coefficient == -1)
- if(stored_shock_by_ref["\ref[src]"])
- stored_shock_by_ref["\ref[src]"] += shock_damage
- else
- stored_shock_by_ref["\ref[src]"] = shock_damage
- return
-
- if (!def_zone)
- def_zone = pick(BP_L_HAND, BP_R_HAND)
-
- return ..(shock_damage, source, base_siemens_coeff, def_zone)
-
/mob/living/carbon/human/explosion_act(severity)
..()
if(QDELETED(src))
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index ddc6d17d8b0..a33044b3ad5 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -97,7 +97,15 @@
apply_effect(agony_amount/10, EYE_BLUR)
/mob/living/proc/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, def_zone = null)
- return 0 //only carbon liveforms have this proc
+ SHOULD_CALL_PARENT(TRUE)
+ if(status_flags & GODMODE)
+ return 0
+ var/decl/species/my_species = get_species()
+ if(my_species?.species_flags & SPECIES_FLAG_ABSORB_ELECTRICITY)
+ spark_at(loc, amount=5, cardinal_only = TRUE)
+ LAZYADD(global.stored_shock_by_ref["\ref[src]"], shock_damage)
+ return 0
+ return shock_damage
/mob/living/emp_act(severity)
var/list/L = src.get_contents()
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index c23a0e0faa4..12ddf623401 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -124,17 +124,21 @@
/mob/living/silicon/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, def_zone = null)
- if (istype(source, /obj/effect/containment_field))
- spark_at(loc, amount=5, cardinal_only = TRUE)
-
- shock_damage *= 0.75 //take reduced damage
- take_overall_damage(0, shock_damage)
- visible_message("\The [src] was shocked by \the [source]!", \
- "Energy pulse detected, system damaged!", \
- "You hear an electrical crack")
- if(prob(20))
- SET_STATUS_MAX(src, STAT_STUN, 2)
- return
+ shock_damage = ..()
+ if(shock_damage <= 0 || !istype(source, /obj/effect/containment_field))
+ return 0
+
+ spark_at(loc, amount=5, cardinal_only = TRUE)
+ shock_damage *= 0.75 //take reduced damage
+ take_overall_damage(0, shock_damage)
+ visible_message(
+ SPAN_DANGER("\The [src] was shocked by \the [source]!"),
+ SPAN_DANGER("Energy pulse detected, system damaged!"),
+ SPAN_DANGER("You hear an electrical crack.")
+ )
+ if(prob(20))
+ SET_STATUS_MAX(src, STAT_STUN, 2)
+ return shock_damage
/mob/living/silicon/proc/damage_mob(var/brute = 0, var/fire = 0, var/tox = 0)
return
diff --git a/code/modules/mob/mob_damage.dm b/code/modules/mob/mob_damage.dm
index 94adae932a9..f52cc527f4c 100644
--- a/code/modules/mob/mob_damage.dm
+++ b/code/modules/mob/mob_damage.dm
@@ -58,3 +58,24 @@
SHOULD_CALL_PARENT(TRUE)
if(do_update_health)
update_health()
+
+// Calculates the Siemen's coefficient for a given area of the body.
+// 1 is 100% vulnerability, 0 is immune.
+/mob/proc/get_siemens_coefficient_for_coverage(coverage_flags = SLOT_HANDS)
+ var/decl/species/my_species = get_species()
+ . = my_species ? my_species.siemens_coefficient : 1
+ if(. <= 0)
+ return 0
+ if(coverage_flags)
+ for(var/obj/item/clothing/clothes in get_equipped_items(include_carried = FALSE))
+ if(clothes.body_parts_covered & coverage_flags)
+ if(clothes.siemens_coefficient <= 0)
+ return 0
+ . *= clothes.siemens_coefficient
+ if(. <= 0)
+ return 0
+ . = max(round(., 0.1), 0)
+
+//this proc returns the Siemens coefficient of electrical resistivity for a particular external organ.
+/mob/proc/get_siemens_coefficient_organ(var/obj/item/organ/external/def_zone)
+ return (istype(def_zone) && def_zone.body_part) ? get_siemens_coefficient_for_coverage(def_zone.body_part) : 1
diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm
index d2db88a1164..9642a5abcc7 100644
--- a/code/modules/power/power.dm
+++ b/code/modules/power/power.dm
@@ -183,7 +183,10 @@
//power_source is a source of electricity, can be powercell, area, apc, cable, powernet or null
//source is an object caused electrocuting (airlock, grille, etc)
//No animations will be performed by this proc.
-/proc/electrocute_mob(mob/living/carbon/M, var/power_source, var/obj/source, var/siemens_coeff = 1.0)
+/proc/electrocute_mob(mob/living/carbon/M, power_source, obj/source, siemens_coeff = 1.0, coverage_flags = SLOT_HANDS)
+
+ coverage_flags = M?.get_active_hand_bodypart_flags() || coverage_flags
+
var/area/source_area
if(istype(power_source,/area))
source_area = power_source
@@ -214,13 +217,9 @@
//If following checks determine user is protected we won't alarm for long.
if(PN)
PN.trigger_warning(5)
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(H.species.siemens_coefficient <= 0)
- return
- var/obj/item/clothing/gloves/G = H.get_equipped_item(slot_gloves_str)
- if(istype(G) && G.siemens_coefficient == 0)
- return 0 //to avoid spamming with insulated glvoes on
+
+ if(M.get_siemens_coefficient_for_coverage(coverage_flags) <= 0)
+ return
//Checks again. If we are still here subject will be shocked, trigger standard 20 tick warning
//Since this one is longer it will override the original one.
diff --git a/code/modules/species/species_helpers.dm b/code/modules/species/species_helpers.dm
index 5ac85615ad4..6be3e01dd47 100644
--- a/code/modules/species/species_helpers.dm
+++ b/code/modules/species/species_helpers.dm
@@ -1,9 +1,9 @@
var/global/list/stored_shock_by_ref = list()
/mob/living/proc/apply_stored_shock_to(var/mob/living/target)
- if(stored_shock_by_ref["\ref[src]"])
- target.electrocute_act(stored_shock_by_ref["\ref[src]"]*0.9, src)
- stored_shock_by_ref["\ref[src]"] = 0
+ if(global.stored_shock_by_ref["\ref[src]"])
+ target.electrocute_act(global.stored_shock_by_ref["\ref[src]"]*0.9, src)
+ global.stored_shock_by_ref["\ref[src]"] = 0
/decl/species/proc/toggle_stance(var/mob/living/carbon/human/H)
if(!H.incapacitated())