diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm
index cdcfba077b3..0abd008556d 100644
--- a/code/__DEFINES/flags.dm
+++ b/code/__DEFINES/flags.dm
@@ -45,8 +45,8 @@
// those restrictions.
#define OMNITONGUE_2 256
-// TESLA_IGNORE grants immunity from being targeted by tesla-style electricity
-#define TESLA_IGNORE_2 512
+/// Prevents mobs from getting chainshocked by teslas and the supermatter
+#define SHOCKED_2 512
// Stops you from putting things like an RCD or other items into an ORM or protolathe for materials.
#define NO_MAT_REDEMPTION_2 1024
@@ -146,4 +146,16 @@
#define INDESTRUCTIBLE (1<<6) //doesn't take damage
#define FREEZE_PROOF (1<<7) //can't be frozen
+//tesla_zap
+#define ZAP_MACHINE_EXPLOSIVE (1<<0)
+#define ZAP_ALLOW_DUPLICATES (1<<1)
+#define ZAP_OBJ_DAMAGE (1<<2)
+#define ZAP_MOB_DAMAGE (1<<3)
+#define ZAP_MOB_STUN (1<<4)
+#define ZAP_GENERATES_POWER (1<<5)
+
+#define ZAP_DEFAULT_FLAGS ZAP_MOB_STUN | ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE
+#define ZAP_FUSION_FLAGS ZAP_OBJ_DAMAGE | ZAP_MOB_DAMAGE | ZAP_MOB_STUN
+#define ZAP_SUPERMATTER_FLAGS ZAP_GENERATES_POWER
+
GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768))
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 8eeb65e51ce..9c8a197d84f 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -139,6 +139,17 @@
#define ENVIRONMENT_SMASH_WALLS 2 //walls
#define ENVIRONMENT_SMASH_RWALLS 4 //rwalls
+///Flags used by the flags parameter of electrocute act.
+
+///Makes it so that the shock doesn't take gloves into account.
+#define SHOCK_NOGLOVES (1 << 0)
+///Used when the shock is from a tesla bolt.
+#define SHOCK_TESLA (1 << 1)
+///Used when an illusion shocks something. Makes the shock deal stamina damage and not trigger certain secondary effects.
+#define SHOCK_ILLUSION (1 << 2)
+///The shock doesn't stun.
+#define SHOCK_NOSTUN (1 << 3)
+
#define POCKET_STRIP_DELAY 40 //time taken (in deciseconds) to search somebody's pockets
#define DEFAULT_ITEM_STRIP_DELAY 40 //time taken (in deciseconds) to strip somebody
diff --git a/code/__HELPERS/traits.dm b/code/__HELPERS/traits.dm
index d91254595c5..4cd02401b43 100644
--- a/code/__HELPERS/traits.dm
+++ b/code/__HELPERS/traits.dm
@@ -66,7 +66,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_BLOODCRAWL "bloodcrawl"
#define TRAIT_BLOODCRAWL_EAT "bloodcrawl_eat"
#define TRAIT_JESTER "jester"
-
+#define TRAIT_TESLA_SHOCKIMMUNE "tesla_shock_immunity"
//
// common trait sources
#define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index ccc31e5c501..820d65f1970 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -8,8 +8,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_PACIFISM" = TRAIT_PACIFISM,
"TRAIT_WATERBREATH" = TRAIT_WATERBREATH,
"BLOODCRAWL" = TRAIT_BLOODCRAWL,
- "BLOODCRAWL_EAT" = TRAIT_BLOODCRAWL_EAT
- )))
+ "BLOODCRAWL_EAT" = TRAIT_BLOODCRAWL_EAT,
+ "TRAIT_TESLA_SHOCKIMMUNE" = TRAIT_TESLA_SHOCKIMMUNE)))
/// value -> trait name, generated on use from trait_by_type global
GLOBAL_LIST(trait_name_map)
diff --git a/code/_onclick/click_override.dm b/code/_onclick/click_override.dm
index fc1d09628e8..1136af3eae4 100644
--- a/code/_onclick/click_override.dm
+++ b/code/_onclick/click_override.dm
@@ -74,7 +74,7 @@
L.Weaken(3)
else
if(P.unlimited_power)
- L.electrocute_act(1000, P, safety = TRUE, override = TRUE) //Just kill them
+ L.electrocute_act(1000, P, flags = SHOCK_NOGLOVES) //Just kill them
else
electrocute_mob(L, C, P)
break
diff --git a/code/datums/spells/lightning.dm b/code/datums/spells/lightning.dm
index 20cd8dccdf5..639ba34766f 100644
--- a/code/datums/spells/lightning.dm
+++ b/code/datums/spells/lightning.dm
@@ -88,7 +88,7 @@
var/mob/living/current = target
if(bounces < 1)
if(damaging)
- current.electrocute_act(bolt_energy, "Lightning Bolt", safety = TRUE)
+ current.electrocute_act(bolt_energy, "Lightning Bolt", flags = SHOCK_NOGLOVES)
else
current.AdjustJitter(1000) //High numbers for violent convulsions
current.do_jitter_animation(current.jitteriness)
@@ -99,7 +99,7 @@
playsound(get_turf(current), 'sound/magic/lightningshock.ogg', 50, 1, -1)
else
if(damaging)
- current.electrocute_act(bolt_energy, "Lightning Bolt", safety = TRUE)
+ current.electrocute_act(bolt_energy, "Lightning Bolt", flags = SHOCK_NOGLOVES)
else
current.AdjustJitter(1000) //High numbers for violent convulsions
current.do_jitter_animation(current.jitteriness)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 04a4b96f79c..74fc4b12458 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -944,6 +944,14 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
/atom/proc/ratvar_act()
return
+/**
+ * Respond to an electric bolt action on our item
+ *
+ * Default behaviour is to return, we define here to allow for cleaner code later on
+ */
+/atom/proc/zap_act(power, zap_flags)
+ return
+
/atom/proc/handle_ricochet(obj/item/projectile/P)
return
diff --git a/code/game/gamemodes/blob/theblob.dm b/code/game/gamemodes/blob/theblob.dm
index 762ab785fff..2864854a1e9 100644
--- a/code/game/gamemodes/blob/theblob.dm
+++ b/code/game/gamemodes/blob/theblob.dm
@@ -143,9 +143,10 @@
..()
L.blob_act(src)
-/obj/structure/blob/tesla_act(power)
- ..()
- take_damage(power / 400, BURN, "energy")
+/obj/structure/blob/zap_act(power, zap_flags)
+ take_damage(power * 0.0025, BURN, "energy")
+ power -= power * 0.0025 //You don't get to do it for free
+ return ..() //You don't get to do it for free
/obj/structure/blob/hulk_damage()
return 15
diff --git a/code/game/gamemodes/miniantags/abduction/gland.dm b/code/game/gamemodes/miniantags/abduction/gland.dm
index b242802c9cf..2096eb0d162 100644
--- a/code/game/gamemodes/miniantags/abduction/gland.dm
+++ b/code/game/gamemodes/miniantags/abduction/gland.dm
@@ -297,7 +297,7 @@
addtimer(CALLBACK(src, .proc/zap), rand(30, 100))
/obj/item/organ/internal/heart/gland/electric/proc/zap()
- tesla_zap(owner, 4, 8000)
+ tesla_zap(owner, 4, 8000, ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_MOB_STUN)
playsound(get_turf(owner), 'sound/magic/lightningshock.ogg', 50, 1)
/obj/item/organ/internal/heart/gland/chem
diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
index 3744ed17aec..c2d92ee8ec7 100644
--- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
+++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
@@ -518,8 +518,8 @@
playsound(src,'sound/effects/sparks4.ogg', 50, TRUE)
do_teleport(target, F, 0)
-/mob/living/simple_animal/hostile/swarmer/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
- if(!tesla_shock)
+/mob/living/simple_animal/hostile/swarmer/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
+ if(!(flags & SHOCK_TESLA))
return FALSE
return ..()
@@ -609,7 +609,7 @@
var/mob/living/L = AM
if(!istype(L, /mob/living/simple_animal/hostile/swarmer))
playsound(loc,'sound/effects/snap.ogg',50, 1, -1)
- L.electrocute_act(0, src, 1, TRUE, TRUE)
+ L.electrocute_act(100, src, 1, flags = SHOCK_NOGLOVES | SHOCK_ILLUSION)
if(isrobot(L) || ismachineperson(L))
L.Weaken(5)
qdel(src)
diff --git a/code/game/gamemodes/miniantags/guardian/types/lightning.dm b/code/game/gamemodes/miniantags/guardian/types/lightning.dm
index e2ab6360ae5..901ba9162d0 100644
--- a/code/game/gamemodes/miniantags/guardian/types/lightning.dm
+++ b/code/game/gamemodes/miniantags/guardian/types/lightning.dm
@@ -25,7 +25,7 @@
if(!(NO_SHOCK in summoner.mutations))
summoner.mutations.Add(NO_SHOCK)
-/mob/living/simple_animal/hostile/guardian/beam/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
+/mob/living/simple_animal/hostile/guardian/beam/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
return FALSE //You are lightning, you should not be hurt by such things.
/mob/living/simple_animal/hostile/guardian/beam/AttackingTarget()
diff --git a/code/game/gamemodes/miniantags/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm
index 993542a3b36..6121a3df808 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant.dm
@@ -87,7 +87,7 @@
return //most humans will now be either bones or harvesters, but we're still un-alive.
-/mob/living/simple_animal/revenant/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
+/mob/living/simple_animal/revenant/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
return FALSE //You are a ghost, atmos and grill makes sparks, and you make your own shocks with lights.
/mob/living/simple_animal/revenant/adjustHealth(amount, updating_health = TRUE)
diff --git a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm
index 47e5e759c77..7b7276e7cf7 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm
@@ -217,7 +217,7 @@
if(M == user)
return
M.Beam(L,icon_state="purple_lightning",icon='icons/effects/effects.dmi',time=5)
- M.electrocute_act(shock_damage, L, safety = TRUE)
+ M.electrocute_act(shock_damage, L, flags = SHOCK_NOGLOVES)
do_sparks(4, 0, M)
playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1)
diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm
index afdb819eccd..f57da2eabaf 100644
--- a/code/game/gamemodes/nuclear/nuclearbomb.dm
+++ b/code/game/gamemodes/nuclear/nuclearbomb.dm
@@ -330,9 +330,9 @@ GLOBAL_VAR(bomb_set)
return
qdel(src)
-/obj/machinery/nuclearbomb/tesla_act(power, explosive)
- ..()
- if(explosive)
+/obj/machinery/nuclearbomb/zap_act(power, zap_flags)
+ . = ..()
+ if(zap_flags & ZAP_MACHINE_EXPLOSIVE)
qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over
#define NUKERANGE 80
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index e174bd5fca8..1d4b59acdf9 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -104,12 +104,6 @@
to_chat(M, "The screen bursts into static.")
..()
-/obj/machinery/camera/tesla_act(power)//EMP proof upgrade also makes it tesla immune
- if(isEmpProof())
- return
- ..()
- qdel(src)//to prevent bomb testing camera from exploding over and over forever
-
/obj/machinery/camera/ex_act(severity)
if(invuln)
return
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index dea6cd510cd..3d3b1eec098 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -192,7 +192,7 @@ About the new airlock wires panel:
else
return
else if(user.hallucination > 50 && prob(10) && !operating)
- if(user.electrocute_act(50, src, 1, illusion = TRUE)) // We'll just go with a flat 50 damage, instead of doing powernet checks
+ if(user.electrocute_act(50, src, flags = SHOCK_ILLUSION)) // We'll just go with a flat 50 damage, instead of doing powernet checks
return
..(user)
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index d9568f69ac1..c1f4fd22044 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -383,3 +383,7 @@
/obj/machinery/door/GetExplosionBlock()
return density ? real_explosion_block : 0
+
+/obj/machinery/door/zap_act(power, zap_flags)
+ zap_flags &= ~ZAP_OBJ_DAMAGE
+ . = ..()
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index 7420c366c3d..b7d961cc35d 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -636,14 +636,15 @@ Class Procs:
/obj/machinery/proc/can_be_overridden()
. = 1
-/obj/machinery/tesla_act(power, explosive = FALSE)
- ..()
- if(prob(85) && explosive)
- explosion(loc, 1, 2, 4, flame_range = 2, adminlog = 0, smoke = 0)
- else if(prob(50))
- emp_act(EMP_LIGHT)
- else
- ex_act(EXPLODE_HEAVY)
+/obj/machinery/zap_act(power, zap_flags)
+ if(prob(85) && (zap_flags & ZAP_MACHINE_EXPLOSIVE) && !(resistance_flags & INDESTRUCTIBLE))
+ explosion(src, 1, 2, 4, flame_range = 2, adminlog = FALSE, smoke = FALSE)
+ else if(zap_flags & ZAP_OBJ_DAMAGE)
+ take_damage(power * 0.0005, BURN, "energy")
+ if(prob(40))
+ emp_act(EMP_LIGHT)
+ power -= power * 0.0005
+ return ..()
/obj/machinery/proc/adjust_item_drop_location(atom/movable/AM) // Adjust item drop location to a 3x3 grid inside the tile, returns slot id from 0 to 8
var/md5 = md5(AM.name) // Oh, and it's deterministic too. A specific item will always drop from the same slot.
diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm
index a1211ec9aa1..ba1d107c94e 100644
--- a/code/game/objects/effects/anomalies.dm
+++ b/code/game/objects/effects/anomalies.dm
@@ -168,7 +168,7 @@
/obj/effect/anomaly/flux/proc/mobShock(mob/living/M)
if(canshock && istype(M))
canshock = FALSE //Just so you don't instakill yourself if you slam into the anomaly five times in a second.
- M.electrocute_act(shockdamage, "[name]", safety = TRUE)
+ M.electrocute_act(shockdamage, name, flags = SHOCK_NOGLOVES)
/obj/effect/anomaly/flux/detonate()
if(explosive)
diff --git a/code/game/objects/effects/effects.dm b/code/game/objects/effects/effects.dm
index 625d2ad8fd9..9053786e554 100644
--- a/code/game/objects/effects/effects.dm
+++ b/code/game/objects/effects/effects.dm
@@ -69,9 +69,6 @@
/obj/effect/abstract/decompile_act(obj/item/matter_decompiler/C, mob/user)
return
-/obj/effect/abstract/tesla_act(power)
- return
-
/obj/effect/abstract/singularity_act()
return
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index e41d2173c23..b796740a60b 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -224,15 +224,24 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
SSfires.processing -= src
///Called when the obj is hit by a tesla bolt.
-/obj/proc/tesla_act(power)
+/obj/zap_act(power, zap_flags)
+ if(QDELETED(src))
+ return FALSE
being_shocked = TRUE
- var/power_bounced = power * 0.5
- tesla_zap(src, 3, power_bounced)
- addtimer(CALLBACK(src, .proc/reset_shocked), 10)
+ addtimer(CALLBACK(src, .proc/reset_shocked), 1 SECONDS)
+ return power / 2
/obj/proc/reset_shocked()
being_shocked = FALSE
+//The surgeon general warns that being buckled to certain objects receiving powerful shocks is greatly hazardous to your health
+///Only tesla coils, vehicles, and grounding rods currently call this because mobs are already targeted over all other objects, but this might be useful for more things later.
+/obj/proc/zap_buckle_check(strength)
+ if(has_buckled_mobs())
+ for(var/m in buckled_mobs)
+ var/mob/living/buckled_mob = m
+ buckled_mob.electrocute_act((clamp(round(strength / 400), 10, 90) + rand(-5, 5)), src, flags = SHOCK_TESLA)
+
//the obj is deconstructed into pieces, whether through careful disassembly or when destroyed.
/obj/proc/deconstruct(disassembled = TRUE)
SEND_SIGNAL(src, COMSIG_OBJ_DECONSTRUCT, disassembled)
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index bd8ca8c024b..0f63a5315ad 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -166,3 +166,9 @@
/obj/structure/proc/prevents_buckled_mobs_attacking()
return FALSE
+
+/obj/structure/zap_act(power, zap_flags)
+ if(zap_flags & ZAP_OBJ_DAMAGE)
+ take_damage(power / 8000, BURN, "energy")
+ power -= power / 2000 //walls take a lot out of ya
+ . = ..()
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 9f0daed2fbe..fef7fee544c 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -349,11 +349,6 @@
BD.attack_hand(usr)
open()
-/obj/structure/closet/tesla_act(var/power)
- ..()
- visible_message("[src] is blown apart by the bolt of electricity!", "You hear a metallic screeching sound.")
- qdel(src)
-
/obj/structure/closet/get_remote_view_fullscreens(mob/user)
if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS)))
user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1)
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 3e2a96ff75a..1b7e86571de 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -254,7 +254,7 @@
var/obj/structure/cable/C = T.get_cable_node()
if(C)
playsound(src, 'sound/magic/lightningshock.ogg', 100, TRUE, extrarange = 5)
- tesla_zap(src, 3, C.newavail() * 0.01) //Zap for 1/100 of the amount of power. At a million watts in the grid, it will be as powerful as a tesla revolver shot.
+ tesla_zap(src, 3, C.newavail() * 0.01, ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_MOB_STUN | ZAP_ALLOW_DUPLICATES) //Zap for 1/100 of the amount of power. At a million watts in the grid, it will be as powerful as a tesla revolver shot.
C.add_delayedload(C.newavail() * 0.0375) // you can gain up to 3.5 via the 4x upgrades power is halved by the pole so thats 2x then 1X then .5X for 3.5x the 3 bounces shock.
return ..()
diff --git a/code/game/objects/structures/holosign.dm b/code/game/objects/structures/holosign.dm
index 06bd8237cda..211dec106b3 100644
--- a/code/game/objects/structures/holosign.dm
+++ b/code/game/objects/structures/holosign.dm
@@ -122,7 +122,7 @@
if(!shockcd)
if(isliving(user))
var/mob/living/M = user
- M.electrocute_act(15, "Energy Barrier", safety = TRUE)
+ M.electrocute_act(15, "Energy Barrier")
shockcd = TRUE
addtimer(CALLBACK(src, .proc/cooldown), 5)
@@ -134,6 +134,6 @@
return
var/mob/living/M = AM
- M.electrocute_act(15, "Energy Barrier", safety = TRUE)
+ M.electrocute_act(15, "Energy Barrier")
shockcd = TRUE
addtimer(CALLBACK(src, .proc/cooldown), 5)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index cfcc216187d..5347a55135e 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -2194,7 +2194,7 @@
switch(punishment)
// These smiting types are valid for all living mobs
if("Lightning bolt")
- M.electrocute_act(5, "Lightning Bolt", safety = TRUE, override = TRUE)
+ M.electrocute_act(5, "Lightning Bolt", flags = SHOCK_NOGLOVES)
playsound(get_turf(M), 'sound/magic/lightningshock.ogg', 50, 1, -1)
M.adjustFireLoss(75)
M.Weaken(5)
diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm
index 39051b1cc0d..868e52c4eca 100644
--- a/code/modules/hydroponics/plant_genes.dm
+++ b/code/modules/hydroponics/plant_genes.dm
@@ -233,14 +233,14 @@
/datum/plant_gene/trait/cell_charge/on_slip(obj/item/reagent_containers/food/snacks/grown/G, mob/living/carbon/C)
var/power = G.seed.potency*rate
if(prob(power))
- C.electrocute_act(round(power), G, 1, TRUE)
+ C.electrocute_act(round(power), G, 1, SHOCK_NOGLOVES)
/datum/plant_gene/trait/cell_charge/on_squash(obj/item/reagent_containers/food/snacks/grown/G, atom/target)
if(isliving(target))
var/mob/living/carbon/C = target
var/power = G.seed.potency*rate
if(prob(power))
- C.electrocute_act(round(power), G, 1, TRUE)
+ C.electrocute_act(round(power), G, 1, SHOCK_NOGLOVES)
/datum/plant_gene/trait/cell_charge/on_consume(obj/item/reagent_containers/food/snacks/grown/G, mob/living/carbon/target)
if(!G.reagents.total_volume)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index cb00e02dd12..e4a711546a4 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -144,54 +144,44 @@
M.forceMove(drop_location())
visible_message("[M] bursts out of [src]!")
-/mob/living/carbon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
- SEND_SIGNAL(src, COMSIG_LIVING_ELECTROCUTE_ACT, shock_damage)
- if(status_flags & GODMODE) //godmode
- return FALSE
- if(NO_SHOCK in mutations) //shockproof
- return FALSE
- if(tesla_shock && tesla_ignore)
- return FALSE
- shock_damage *= siemens_coeff
- if(dna && dna.species)
- shock_damage *= dna.species.siemens_coeff
- if(shock_damage < 1 && !override)
- return FALSE
- if(reagents.has_reagent("teslium"))
- shock_damage *= 1.5 //If the mob has teslium in their body, shocks are 50% more damaging!
- if(illusion)
- adjustStaminaLoss(shock_damage)
- else
- take_overall_damage(0, shock_damage, TRUE, used_weapon = "Electrocution")
- shock_internal_organs(shock_damage)
- visible_message(
- "[src] was shocked by \the [source]!",
- "You feel a powerful shock coursing through your body!",
- "You hear a heavy electrical crack.")
- AdjustJitter(1000) //High numbers for violent convulsions
+///Adds to the parent by also adding functionality to propagate shocks through pulling and doing some fluff effects.
+/mob/living/carbon/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
+ . = ..()
+ if(!.)
+ return
+ //Propagation through pulling
+ if(!(flags & SHOCK_ILLUSION))
+ var/list/shocking_queue = list()
+ if(iscarbon(pulling) && source != pulling)
+ shocking_queue += pulling
+ if(iscarbon(pulledby) && source != pulledby)
+ shocking_queue += pulledby
+ if(iscarbon(buckled) && source != buckled)
+ shocking_queue += buckled
+ for(var/mob/living/carbon/carried in buckled_mobs)
+ if(source != carried)
+ shocking_queue += carried
+ //Found our victims, now lets shock them all
+ for(var/victim in shocking_queue)
+ var/mob/living/carbon/C = victim
+ C.electrocute_act(shock_damage * 0.75, src, 1, flags)
+ //Stun
+ var/should_stun = (!(flags & SHOCK_TESLA) || siemens_coeff > 0.5) && !(flags & SHOCK_NOSTUN)
+ if(should_stun)
+ Stun(2)
+ //Jitter and other fluff.
+ AdjustJitter(1000)
do_jitter_animation(jitteriness)
AdjustStuttering(2)
- if((!tesla_shock || (tesla_shock && siemens_coeff > 0.5)) && stun)
- Stun(2)
- spawn(20)
- AdjustJitter(-1000, bound_lower = 10) //Still jittery, but vastly less
- if((!tesla_shock || (tesla_shock && siemens_coeff > 0.5)) && stun)
- Stun(3)
- Weaken(3)
- if(shock_damage > 200)
- src.visible_message(
- "[src] was arc flashed by the [source]!",
- "The [source] arc flashes and electrocutes you!",
- "You hear a lightning-like crack!")
- playsound(loc, 'sound/effects/eleczap.ogg', 50, 1, -1)
- explosion(loc, -1, 0, 2, 2)
-
- if(override)
- return override
- else
- return shock_damage
-
+ shock_internal_organs(shock_damage)
+ addtimer(CALLBACK(src, .proc/secondary_shock, should_stun), 20)
+ return shock_damage
+///Called slightly after electrocute act to reduce jittering and apply a secondary stun.
+/mob/living/carbon/proc/secondary_shock(should_stun)
+ AdjustJitter(-1000, bound_lower = 10) //Still jittery, but vastly less
+ if(should_stun)
+ Weaken(3)
/mob/living/carbon/swap_hand()
var/obj/item/item_in_hand = src.get_active_hand()
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 5e37c1b49b4..4e0069dcfae 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -581,37 +581,35 @@
SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT)
sync_lighting_plane_alpha()
-//Added a safety check in case you want to shock a human mob directly through electrocute_act.
-/mob/living/carbon/human/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
- if(tesla_shock)
- var/total_coeff = 1
- if(gloves)
- var/obj/item/clothing/gloves/G = gloves
- if(G.siemens_coefficient <= 0)
- total_coeff -= 0.5
+///Calculates the siemens coeff based on clothing and species, can also restart hearts.
+/mob/living/carbon/human/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
+ //Calculates the siemens coeff based on clothing. Completely ignores the arguments
+ if(flags & SHOCK_TESLA) //I hate this entire block. This gets the siemens_coeff for tesla shocks
+ if(gloves && gloves.siemens_coefficient <= 0)
+ siemens_coeff -= 0.5
if(wear_suit)
- var/obj/item/clothing/suit/S = wear_suit
- if(S.siemens_coefficient <= 0)
- total_coeff -= 0.95
- else if(S.siemens_coefficient == (-1))
- total_coeff -= 1
- siemens_coeff = total_coeff
- if(tesla_ignore)
- siemens_coeff = 0
- else if(!safety)
- var/gloves_siemens_coeff = 1
+ if(wear_suit.siemens_coefficient == -1)
+ siemens_coeff -= 1
+ else if(wear_suit.siemens_coefficient <= 0)
+ siemens_coeff -= 0.95
+ siemens_coeff = max(siemens_coeff, 0)
+ else if(!(flags & SHOCK_NOGLOVES)) //This gets the siemens_coeff for all non tesla shocks
if(gloves)
- var/obj/item/clothing/gloves/G = gloves
- gloves_siemens_coeff = G.siemens_coefficient
- siemens_coeff = gloves_siemens_coeff
- if(undergoing_cardiac_arrest() && !illusion)
+ siemens_coeff *= gloves.siemens_coefficient
+ siemens_coeff *= dna.species.siemens_coeff
+ . = ..()
+ //Don't go further if the shock was blocked/too weak.
+ if(!.)
+ return
+ //Note we both check that the user is in cardiac arrest and can actually heartattack
+ //If they can't, they're missing their heart and this would runtime
+ if(undergoing_cardiac_arrest() && !(flags & SHOCK_ILLUSION))
if(shock_damage * siemens_coeff >= 1 && prob(25))
set_heartattack(FALSE)
if(stat == CONSCIOUS)
to_chat(src, "You feel your heart beating again!")
- dna.species.spec_electrocute_act(src, shock_damage, source, siemens_coeff, safety, override, tesla_shock, illusion, stun)
- . = ..(shock_damage, source, siemens_coeff, safety, override, tesla_shock, illusion, stun)
+ dna.species.spec_electrocute_act(src, shock_damage, source, siemens_coeff, flags = NONE)
/mob/living/carbon/human/Topic(href, href_list)
if(!usr.stat && usr.canmove && !usr.restrained() && in_range(src, usr))
diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm
index e47514b1094..ff7db215f3e 100644
--- a/code/modules/mob/living/carbon/human/species/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/_species.dm
@@ -370,7 +370,7 @@
/datum/species/proc/spec_stun(mob/living/carbon/human/H,amount)
return
-/datum/species/proc/spec_electrocute_act(mob/living/carbon/human/H, shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
+/datum/species/proc/spec_electrocute_act(mob/living/carbon/human/H, shock_damage, source, siemens_coeff = 1, flags = NONE)
return
/datum/species/proc/help(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index ddf5fb9875b..e2aeadf1963 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -55,22 +55,35 @@
/mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone)
return 0
-/mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
- SEND_SIGNAL(src, COMSIG_LIVING_ELECTROCUTE_ACT, shock_damage)
+///As the name suggests, this should be called to apply electric shocks.
+/mob/living/proc/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
+ SEND_SIGNAL(src, COMSIG_LIVING_ELECTROCUTE_ACT, shock_damage, source, siemens_coeff, flags)
if(status_flags & GODMODE) //godmode
return FALSE
+ if((flags & SHOCK_TESLA) && HAS_TRAIT(src, TRAIT_TESLA_SHOCKIMMUNE))
+ return FALSE
if(NO_SHOCK in mutations) //shockproof
return FALSE
- if(tesla_shock && tesla_ignore)
+ shock_damage *= siemens_coeff
+ if(shock_damage < 1)
return FALSE
- if(shock_damage > 0)
- if(!illusion)
- adjustFireLoss(shock_damage)
- visible_message(
- "[src] was shocked by \the [source]!",
- "You feel a powerful shock coursing through your body!",
- "You hear a heavy electrical crack.")
- return shock_damage
+ if(!(flags & SHOCK_ILLUSION))
+ take_overall_damage(0, shock_damage, TRUE, used_weapon = "Electrocution")
+ if(shock_damage > 200)
+ visible_message(
+ "[src] was arc flashed by the [source]!",
+ "The [source] arc flashes and electrocutes you!",
+ "You hear a lightning-like crack!")
+ playsound(loc, 'sound/effects/eleczap.ogg', 50, 1, -1)
+ explosion(loc, -1, 0, 2, 2)
+ else
+ adjustStaminaLoss(shock_damage)
+ visible_message(
+ "[src] was shocked by \the [source]!", \
+ "You feel a powerful shock coursing through your body!", \
+ "You hear a heavy electrical crack." \
+ )
+ return shock_damage
/mob/living/emp_act(severity)
..()
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 65ef4943059..49c4dbfca63 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -57,9 +57,6 @@
var/gene_stability = DEFAULT_GENE_STABILITY
var/ignore_gene_stability = 0
-
- var/tesla_ignore = FALSE
-
var/list/say_log = list() //a log of what we've said, plain text, no spans or junk, essentially just each individual "message"
var/list/emote_log = list() //like say_log but for emotes
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index cfecbdae193..afaa2f21e57 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -162,7 +162,7 @@
/mob/living/silicon/drop_item()
return
-/mob/living/silicon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
+/mob/living/silicon/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
return FALSE //So borgs they don't die trying to fix wiring
/mob/living/silicon/emp_act(severity)
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index 131c4f8160b..bafed690f1a 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -86,7 +86,7 @@
/mob/living/simple_animal/hostile/construct/narsie_act()
return
-/mob/living/simple_animal/hostile/construct/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
+/mob/living/simple_animal/hostile/construct/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
return FALSE
/////////////////Juggernaut///////////////
diff --git a/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm b/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm
index 77fb0cd0c5b..196de37457c 100644
--- a/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm
+++ b/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm
@@ -144,7 +144,7 @@
return
-/mob/living/simple_animal/hostile/floor_cluwne/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) //prevents runtimes with machine fuckery
+/mob/living/simple_animal/hostile/floor_cluwne/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE) //prevents runtimes with machine fuckery
return FALSE
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
index 34b0a3d51a4..77c710359bb 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
@@ -281,7 +281,7 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa
else
var/mob/living/L = target
L.attack_animal(src)
- L.electrocute_act(10, src, safety = TRUE) //safety = TRUE means we don't check gloves... Ok?
+ L.electrocute_act(10, src, flags = SHOCK_NOGLOVES)
return TRUE
else
return ..()
diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm
index d4cca1b985d..4afd47b839d 100644
--- a/code/modules/mob/living/status_procs.dm
+++ b/code/modules/mob/living/status_procs.dm
@@ -588,3 +588,9 @@
to_chat(src, "[priority_absorb_key["self_message"]]")
priority_absorb_key["stuns_absorbed"] += amount
return TRUE
+
+/mob/living/proc/set_shocked()
+ flags_2 |= SHOCKED_2
+
+/mob/living/proc/reset_shocked()
+ flags_2 &= ~ SHOCKED_2
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index 591f152f15c..d69ad514b4d 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -32,9 +32,9 @@ GLOBAL_LIST_EMPTY(gravity_generators)
if(prob(20))
set_broken()
-/obj/machinery/gravity_generator/tesla_act(power, explosive)
- ..()
- if(explosive)
+/obj/machinery/gravity_generator/zap_act(power, zap_flags)
+ . = ..()
+ if(zap_flags & ZAP_MACHINE_EXPLOSIVE)
qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over
/obj/machinery/gravity_generator/update_icon()
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index d6089a421d0..fb669390c6c 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -652,10 +652,13 @@
on = 1
update()
-/obj/machinery/light/tesla_act(power, explosive = FALSE)
+/obj/machinery/light/zap_act(power, zap_flags)
+ var/explosive = zap_flags & ZAP_MACHINE_EXPLOSIVE
+ zap_flags &= ~(ZAP_MACHINE_EXPLOSIVE | ZAP_OBJ_DAMAGE)
+ . = ..()
if(explosive)
- explosion(loc,0,0,0,flame_range = 5, adminlog = 0)
- qdel(src)
+ explosion(src, 0, 0, 0, flame_range = 5, adminlog = FALSE)
+ qdel(src)
// timed process
// use power
diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm
index 257d98fffc1..89faae60631 100644
--- a/code/modules/power/singularity/narsie.dm
+++ b/code/modules/power/singularity/narsie.dm
@@ -5,7 +5,6 @@
pixel_x = -89
pixel_y = -85
current_size = 9 //It moves/eats like a max-size singulo, aside from range. --NEO
- contained = FALSE
dissipate = FALSE
move_self = TRUE
grav_pull = 5
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index b1d69257484..26ad13014a1 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -3,20 +3,19 @@
desc = "A gravitational singularity."
icon = 'icons/obj/singularity.dmi'
icon_state = "singularity_s1"
- anchored = 1
- density = 1
+ anchored = TRUE
+ density = TRUE
layer = MASSIVE_OBJ_LAYER
light_range = 6
- appearance_flags = 0
+ appearance_flags = LONG_GLIDE
var/current_size = 1
var/allowed_size = 1
- var/contained = 1 //Are we going to move around?
var/energy = 100 //How strong are we?
var/dissipate = 1 //Do we lose energy over time?
var/dissipate_delay = 10
var/dissipate_track = 0
var/dissipate_strength = 1 //How much energy do we lose?
- var/move_self = 1 //Do we move on our own?
+ var/move_self = TRUE //Do we move on our own?
var/grav_pull = 4 //How many tiles out do we pull?
move_resist = INFINITY //no, you don't get to push the singulo. Not even you OP wizard gateway statues
var/consume_range = 0 //How many tiles out do we eat
@@ -28,12 +27,13 @@
allow_spin = 0
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
-/obj/singularity/New(loc, var/starting_energy = 50, var/temp = 0)
+/obj/singularity/Initialize(mapload, starting_energy = 50)
+ . = ..()
//CARN: admin-alert for chuckle-fuckery.
admin_investigate_setup()
- src.energy = starting_energy
- ..()
+ energy = starting_energy
+
START_PROCESSING(SSobj, src)
GLOB.poi_list |= src
GLOB.singularities += src
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index 2abfc3d3154..a7486e11fa3 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -2,6 +2,15 @@
//Please do not bother them with bugs from this port, however, as it has been modified quite a bit.
//Modifications include removing the world-ending full supermatter variation, and leaving only the shard.
+//Zap constants, speeds up targeting
+
+#define COIL (ROD + 1)
+#define ROD (LIVING + 1)
+#define LIVING (MACHINERY + 1)
+#define MACHINERY (OBJECT + 1)
+#define OBJECT (LOWEST + 1)
+#define LOWEST (1)
+
#define PLASMA_HEAT_PENALTY 15 // Higher == Bigger heat and waste penalty from having the crystal surrounded by this gas. Negative numbers reduce penalty.
#define OXYGEN_HEAT_PENALTY 1
#define CO2_HEAT_PENALTY 0.1
@@ -145,6 +154,8 @@
var/mole_heat_penalty = 0
///Takes the energy throwing things into the sm generates and slowly turns it into actual power
var/matter_power = 0
+ ///The cutoff for a bolt jumping, grows with heat, lowers with higher mol count,
+ var/zap_cutoff = 1500
///How much the bullets damage should be multiplied by when it is added to the internal variables
var/bullet_energy = 2
///How much hallucination should we produce per unit of power?
@@ -321,7 +332,7 @@
investigate_log("has spawned additional energy balls.", "supermatter")
if(T)
var/obj/singularity/energy_ball/E = new(T)
- E.energy = power
+ E.energy = 200 //Gets us about 9 balls
// else if(power > EVENT_POWER_PENALTY_THRESHOLD && prob(power/50) && !istype(src, /obj/machinery/power/supermatter_crystal/shard))
// var/datum/round_event_control/crystal_invasion/crystals = new/datum/round_event_control/crystal_invasion
// crystals.runEvent()
@@ -444,13 +455,15 @@
powerloss_dynamic_scaling = clamp(powerloss_dynamic_scaling + clamp(co2comp - powerloss_dynamic_scaling, -0.02, 0.02), 0, 1)
else
powerloss_dynamic_scaling = clamp(powerloss_dynamic_scaling - 0.05, 0, 1)
+ //Ranges from 0 to 1(1-(value between 0 and 1 * ranges from 1 to 1.5(mol / 500)))
+ //We take the mol count, and scale it to be our inhibitor
powerloss_inhibitor = clamp(1 - (powerloss_dynamic_scaling * clamp(combined_gas / POWERLOSS_INHIBITION_MOLE_BOOST_THRESHOLD, 1 , 1.5)), 0 , 1)
//Releases stored power into the general pool
//We get this by consuming shit or being scalpeled
if(matter_power && power_changes)
//We base our removed power off one 10th of the matter_power.
- var/removed_matter = max(matter_power/MATTER_POWER_CONVERSION, 40)
+ var/removed_matter = max(matter_power / MATTER_POWER_CONVERSION, 40)
//Adds at least 40 power
power = max(power + removed_matter, 0)
//Removes at least 40 matter power
@@ -458,13 +471,14 @@
var/temp_factor = 50
if(gasmix_power_ratio > 0.8)
- // with a perfect gas mix, make the power less based on heat
+ //with a perfect gas mix, make the power less based on heat
icon_state = "[base_icon_state]_glow"
else
- // in normal mode, base the produced energy around the heat
+ //in normal mode, base the produced energy around the heat
temp_factor = 30
icon_state = base_icon_state
+
if(power_changes)
power = max((removed.temperature * temp_factor / T0C) * gasmix_power_ratio + power, 0)
@@ -516,21 +530,40 @@
//Handle high power zaps/anomaly generation
if(power > POWER_PENALTY_THRESHOLD || damage > damage_penalty_point) //If the power is above 5000 or if the damage is above 550
var/range = 4
+ zap_cutoff = 1500
if(removed && removed.return_pressure() > 0 && removed.return_temperature() > 0)
+ //You may be able to freeze the zapstate of the engine with good planning, we'll see
+ zap_cutoff = clamp(3000 - (power * (removed.total_moles()) / 10) / removed.return_temperature(), 350, 3000)//If the core is cold, it's easier to jump, ditto if there are a lot of mols
//We should always be able to zap our way out of the default enclosure
//See supermatter_zap() for more details
- range = clamp(power / env.return_pressure() * 10, 2, 7)
- if(power > POWER_PENALTY_THRESHOLD)
- playsound(loc, 'sound/weapons/emitter2.ogg', 100, TRUE, extrarange = 10)
- supermatter_zap(src, range, min(power * 2, 20000))
- supermatter_zap(src, range, min(power * 2, 20000))
- if(power > SEVERE_POWER_PENALTY_THRESHOLD)
- supermatter_zap(src, range, min(power * 2, 20000))
- if(power > CRITICAL_POWER_PENALTY_THRESHOLD)
- supermatter_zap(src, range, min(power * 2, 20000))
- else if (damage > damage_penalty_point && prob(20))
- playsound(loc, 'sound/weapons/emitter2.ogg', 100, TRUE, extrarange = 10)
- supermatter_zap(src, range, clamp(power * 2, 4000, 20000))
+ range = clamp(power / removed.return_pressure() * 10, 2, 7)
+ var/flags = ZAP_SUPERMATTER_FLAGS
+ var/zap_count = 0
+ //Deal with power zaps
+ switch(power)
+ if(POWER_PENALTY_THRESHOLD to SEVERE_POWER_PENALTY_THRESHOLD)
+ zap_icon = DEFAULT_ZAP_ICON_STATE
+ zap_count = 2
+ if(SEVERE_POWER_PENALTY_THRESHOLD to CRITICAL_POWER_PENALTY_THRESHOLD)
+ zap_icon = SLIGHTLY_CHARGED_ZAP_ICON_STATE
+ //Uncaps the zap damage, it's maxed by the input power
+ //Objects take damage now
+ flags |= (ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE)
+ zap_count = 3
+ if(CRITICAL_POWER_PENALTY_THRESHOLD to INFINITY)
+ zap_icon = OVER_9000_ZAP_ICON_STATE
+ //It'll stun more now, and damage will hit harder, gloves are no garentee.
+ //Machines go boom
+ flags |= (ZAP_MOB_STUN | ZAP_MACHINE_EXPLOSIVE | ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE)
+ zap_count = 4
+ //Now we deal with damage shit
+ if(damage > damage_penalty_point && prob(20))
+ zap_count += 1
+
+ if(zap_count >= 1)
+ playsound(src.loc, 'sound/weapons/emitter2.ogg', 100, TRUE, extrarange = 10)
+ for(var/i in 1 to zap_count)
+ supermatter_zap(src, range, clamp(power*2, 4000, 20000), flags)
if(prob(5))
supermatter_anomaly_gen(src, FLUX_ANOMALY, rand(5, 10))
@@ -792,89 +825,142 @@
if(PYRO_ANOMALY)
new /obj/effect/anomaly/pyro(L, 200, FALSE)
-
-
-/obj/machinery/power/supermatter_crystal/proc/supermatter_zap(atom/zapstart, range = 3, power)
+/obj/machinery/power/supermatter_crystal/proc/supermatter_zap(atom/zapstart = src, range = 5, zap_str = 4000, zap_flags = ZAP_SUPERMATTER_FLAGS, list/targets_hit = list())
if(QDELETED(zapstart))
return
. = zapstart.dir
- if(power < 1000)
+ //If the strength of the zap decays past the cutoff, we stop
+ if(zap_str < zap_cutoff)
return
+ var/atom/target
+ var/target_type = LOWEST
+ var/list/arctargets = list()
+ //Making a new copy so additons further down the recursion do not mess with other arcs
+ //Lets put this ourself into the do not hit list, so we don't curve back to hit the same thing twice with one arc
+ for(var/test in oview(zapstart, range))
+ if(!(zap_flags & ZAP_ALLOW_DUPLICATES) && LAZYACCESS(targets_hit, test))
+ continue
- var/target_atom
- var/mob/living/target_mob
- var/obj/machinery/target_machine
- var/obj/structure/target_structure
- var/list/arctargetsmob = list()
- var/list/arctargetsmachine = list()
- var/list/arctargetsstructure = list()
+ if(target_type > COIL)
+ continue
- if(prob(20)) //let's not hit all the engineers with every beam and/or segment of the arc
- for(var/mob/living/Z in oview(zapstart, range+2))
- arctargetsmob += Z
- if(arctargetsmob.len)
- var/mob/living/H = pick(arctargetsmob)
- var/atom/A = H
- target_mob = H
- target_atom = A
+ if(istype(test, /obj/machinery/power/tesla_coil))
+ var/obj/machinery/power/tesla_coil/coil = test
+ if(coil.anchored && !coil.being_shocked && !coil.panel_open && prob(70)) //Diversity of death
+ if(target_type != COIL)
+ arctargets = list()
+ arctargets += test
+ target_type = COIL
- else
- for(var/obj/machinery/X in oview(zapstart, range + 2))
- arctargetsmachine += X
- if(arctargetsmachine.len)
- var/obj/machinery/M = pick(arctargetsmachine)
- var/atom/A = M
- target_machine = M
- target_atom = A
+ if(target_type > ROD)
+ continue
- else
- for(var/obj/structure/Y in oview(zapstart, range + 2))
- arctargetsstructure += Y
- if(arctargetsstructure.len)
- var/obj/structure/O = pick(arctargetsstructure)
- var/atom/A = O
- target_structure = O
- target_atom = A
+ if(istype(test, /obj/machinery/power/grounding_rod))
+ var/obj/machinery/power/grounding_rod/rod = test
+ //We're adding machine damaging effects, rods need to be surefire
+ if(rod.anchored && !rod.panel_open)
+ if(target_type != ROD)
+ arctargets = list()
+ arctargets += test
+ target_type = ROD
- if(target_atom)
- switch(power)
- if(POWER_PENALTY_THRESHOLD to SEVERE_POWER_PENALTY_THRESHOLD)
- zap_icon = DEFAULT_ZAP_ICON_STATE
- if(SEVERE_POWER_PENALTY_THRESHOLD to CRITICAL_POWER_PENALTY_THRESHOLD)
- zap_icon = SLIGHTLY_CHARGED_ZAP_ICON_STATE
- if(CRITICAL_POWER_PENALTY_THRESHOLD to INFINITY)
- zap_icon = OVER_9000_ZAP_ICON_STATE
- zapstart.Beam(target_atom, icon_state = zap_icon, time = 5)
- var/zapdir = get_dir(zapstart, target_atom)
+ if(target_type > LIVING)
+ continue
+
+ if(isliving(test))
+ var/mob/living/alive = test
+ if(!(HAS_TRAIT(alive, TRAIT_TESLA_SHOCKIMMUNE)) && !(alive.flags_2 & SHOCKED_2) && alive.stat != DEAD && prob(20)) //let's not hit all the engineers with every beam and/or segment of the arc
+ if(target_type != LIVING)
+ arctargets = list()
+ arctargets += test
+ target_type = LIVING
+
+ if(target_type > MACHINERY)
+ continue
+
+ if(ismachinery(test))
+ var/obj/machinery/machine = test
+ if(!machine.being_shocked && prob(40))
+ if(target_type != MACHINERY)
+ arctargets = list()
+ arctargets += test
+ target_type = MACHINERY
+
+ if(target_type > OBJECT)
+ continue
+
+ if(isobj(test))
+ var/obj/object = test
+ if(!object.being_shocked)
+ if(target_type != OBJECT)
+ arctargets = list()
+ arctargets += test
+ target_type = OBJECT
+
+ if(length(arctargets)) //Pick from our pool
+ target = pick(arctargets)
+
+ if(!QDELETED(target)) //If we found something
+ //Do the animation to zap to it from here
+ if(!(zap_flags & ZAP_ALLOW_DUPLICATES))
+ LAZYSET(targets_hit, target, TRUE)
+ zapstart.Beam(target, icon_state = zap_icon, time = 5)
+ var/zapdir = get_dir(zapstart, target)
if(zapdir)
. = zapdir
- if(target_mob)
- target_mob.electrocute_act(rand(5, 10), "Supermatter Discharge Bolt", 1, stun = FALSE)
- if(prob(15))
- for(var/i in 1 to 2)
- supermatter_zap(target_mob, 5, power / 2)
- supermatter_zap(target_mob, 5, power / 2)
- else
- supermatter_zap(target_mob, 5, power / 1.5)
+ //Going boom should be rareish
+ if(prob(80))
+ zap_flags &= ~ZAP_MACHINE_EXPLOSIVE
+ if(target_type == COIL)
+ //In the best situation we can expect this to grow up to 2120kw before a delam/IT'S GONE TOO FAR FRED SHUT IT DOWN
+ //The formula for power gen is zap_str * zap_mod / 2 * capacitor rating, between 1 and 4
+ var/multi = 10
+ switch(power)//Between 7k and 9k it's 20, above that it's 40
+ if(SEVERE_POWER_PENALTY_THRESHOLD to CRITICAL_POWER_PENALTY_THRESHOLD)
+ multi = 20
+ if(CRITICAL_POWER_PENALTY_THRESHOLD to INFINITY)
+ multi = 40
+ target.zap_act(zap_str * multi, zap_flags)
+ zap_str /= 3 //Coils should take a lot out of the power of the zap
- else if(target_machine)
- if(prob(15))
- for(var/i in 1 to 2)
- supermatter_zap(target_machine, 5, power / 2)
- supermatter_zap(target_machine, 5, power / 2)
- else
- supermatter_zap(target_machine, 5, power / 1.5)
+ else if(isliving(target))//If we got a fleshbag on our hands
+ var/mob/living/creature = target
+ creature.set_shocked()
+ addtimer(CALLBACK(creature, /mob/living/proc/reset_shocked), 10)
+ //3 shots a human with no resistance. 2 to crit, one to death. This is at at least 10000 power.
+ //There's no increase after that because the input power is effectivly capped at 10k
+ //Does 1.5 damage at the least
+ var/shock_damage = ((zap_flags & ZAP_MOB_DAMAGE) ? (power / 200) - 10 : rand(5, 10))
+ creature.electrocute_act(shock_damage, "Supermatter Discharge Bolt", 1, ((zap_flags & ZAP_MOB_STUN) ? SHOCK_TESLA : SHOCK_NOSTUN))
+ zap_str /= 1.5 //Meatsacks are conductive, makes working in pairs more destructive
- else if(target_structure)
- if(prob(15))
- for(var/i in 1 to 2)
- supermatter_zap(target_structure, 5, power / 2)
- supermatter_zap(target_structure, 5, power / 2)
else
- supermatter_zap(target_structure, 5, power / 1.5)
+ zap_str = target.zap_act(zap_str, zap_flags)
+ //This gotdamn variable is a boomer and keeps giving me problems
+ var/turf/T = get_turf(target)
+ var/pressure = 1
+ if(T?.return_air())
+ var/datum/gas_mixture/G = T.return_air()
+ pressure = max(1, G.return_pressure())
+ //We get our range with the strength of the zap and the pressure, the higher the former and the lower the latter the better
+ var/new_range = clamp(zap_str / pressure * 10, 2, 7)
+ var/zap_count = 1
+ if(prob(5))
+ zap_str -= (zap_str / 10)
+ zap_count += 1
+ for(var/j in 1 to zap_count)
+ if(zap_count > 1)
+ targets_hit = targets_hit.Copy() //Pass by ref begone
+ supermatter_zap(target, new_range, zap_str, zap_flags, targets_hit)
#undef HALLUCINATION_RANGE
#undef GRAVITATIONAL_ANOMALY
#undef FLUX_ANOMALY
#undef PYRO_ANOMALY
+#undef COIL
+#undef ROD
+#undef LIVING
+#undef MACHINERY
+#undef OBJECT
+#undef LOWEST
diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm
index 5743bea8475..53be32d51c2 100644
--- a/code/modules/power/tesla/coil.dm
+++ b/code/modules/power/tesla/coil.dm
@@ -3,10 +3,15 @@
desc = "For the union!"
icon = 'icons/obj/tesla_engine/tesla_coil.dmi'
icon_state = "coil0"
- anchored = 0
- density = 1
+ anchored = FALSE
+ density = TRUE
- var/power_loss = 2
+ // Executing a traitor caught releasing tesla was never this fun!
+ can_buckle = TRUE
+ buckle_lying = 0
+ buckle_requires_restraints = TRUE
+
+ var/zap_flags = ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE
var/input_power_multiplier = 1
var/zap_cooldown = 100
var/last_zap = 0
@@ -31,7 +36,12 @@
for(var/obj/item/stock_parts/capacitor/C in component_parts)
power_multiplier += C.rating
zap_cooldown -= (C.rating * 20)
- input_power_multiplier = power_multiplier
+ input_power_multiplier = (0.85 * (power_multiplier / 4)) //Max out at 85% efficency.
+
+/obj/machinery/power/tesla_coil/examine(mob/user)
+ . = ..()
+ if(in_range(user, src) || isobserver(user))
+ . += "The status display reads: Power generation at [input_power_multiplier*100]%.
Shock interval at [zap_cooldown*0.1] seconds."
/obj/machinery/power/tesla_coil/attackby(obj/item/W, mob/user, params)
if(exchange_parts(user, W))
@@ -77,38 +87,45 @@
else
connect_to_network()
-/obj/machinery/power/tesla_coil/tesla_act(var/power)
+/obj/machinery/power/tesla_coil/zap_act(power, zap_flags)
if(anchored && !panel_open)
- being_shocked = 1
//don't lose arc power when it's not connected to anything
//please place tesla coils all around the station to maximize effectiveness
- var/power_produced = powernet ? power / power_loss : power
- add_avail(power_produced*input_power_multiplier)
+ being_shocked = TRUE
+ addtimer(CALLBACK(src, .proc/reset_shocked), 1 SECONDS)
+ zap_buckle_check(power)
+ if(zap_flags & ZAP_GENERATES_POWER) //I don't want no tesla revolver making 8GW you hear
+ return power / 2
+ var/power_produced = powernet ? power * input_power_multiplier : power
+ add_avail(power_produced)
flick("coilhit", src)
- playsound(src.loc, 'sound/magic/lightningshock.ogg', 100, 1, extrarange = 5)
- tesla_zap(src, 5, power_produced)
- addtimer(CALLBACK(src, .proc/reset_shocked), 10)
+ playsound(loc, 'sound/magic/lightningshock.ogg', 100, TRUE, extrarange = 5)
+ return power - power_produced //You get back the amount we didn't use
else
- ..()
+ . = ..()
/obj/machinery/power/tesla_coil/proc/zap()
if((last_zap + zap_cooldown) > world.time || !powernet)
return FALSE
last_zap = world.time
- var/coeff = (20 - ((input_power_multiplier - 1) * 3))
- coeff = max(coeff, 10)
- var/power = (powernet.avail/2)
+ var/power = (powernet.avail) * 0.2 * input_power_multiplier //Always always always use more then you output for the love of god
+ power = min(surplus(), power) //Take the smaller of the two
add_load(power)
- playsound(src.loc, 'sound/magic/lightningshock.ogg', 100, 1, extrarange = 5)
- tesla_zap(src, 10, power/(coeff/2))
+ playsound(loc, 'sound/magic/lightningshock.ogg', 100, TRUE, extrarange = 5)
+ tesla_zap(src, 10, power, zap_flags)
+ zap_buckle_check(power)
/obj/machinery/power/grounding_rod
name = "grounding rod"
- desc = "Keep an area from being fried from Edison's Bane."
+ desc = "Keeps an area from being fried by Edison's Bane."
icon = 'icons/obj/tesla_engine/tesla_coil.dmi'
icon_state = "grounding_rod0"
- anchored = 0
- density = 1
+ anchored = FALSE
+ density = TRUE
+
+ can_buckle = TRUE
+ buckle_lying = 0
+ buckle_requires_restraints = TRUE
/obj/machinery/power/grounding_rod/Initialize(mapload)
. = ..()
@@ -133,8 +150,10 @@
. = TRUE
default_deconstruction_crowbar(user, I)
-/obj/machinery/power/grounding_rod/tesla_act(var/power)
+/obj/machinery/power/grounding_rod/zap_act(power, zap_flags)
if(anchored && !panel_open)
flick("grounding_rodhit", src)
+ zap_buckle_check(power)
+ return FALSE
else
- ..()
+ . = ..()
diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm
index 36faa49b533..a840c583128 100644
--- a/code/modules/power/tesla/energy_ball.dm
+++ b/code/modules/power/tesla/energy_ball.dm
@@ -1,6 +1,15 @@
#define TESLA_DEFAULT_POWER 1738260
#define TESLA_MINI_POWER 869130
+//Zap constants, speeds up targeting
+#define COIL (ROD + 1)
+#define ROD (RIDE + 1)
+#define RIDE (LIVING + 1)
+#define LIVING (MACHINERY + 1)
+#define MACHINERY (BLOB + 1)
+#define BLOB (STRUCTURE + 1)
+#define STRUCTURE (1)
+/// The Tesla engine
/obj/singularity/energy_ball
name = "energy ball"
desc = "An energy ball."
@@ -9,20 +18,30 @@
pixel_x = -32
pixel_y = -32
current_size = STAGE_TWO
- move_self = 1
+ move_self = TRUE
grav_pull = 0
- contained = 0
- density = 1
+ density = TRUE
energy = 0
- dissipate = 1
+ dissipate = FALSE
dissipate_delay = 5
dissipate_strength = 1
var/list/orbiting_balls = list()
+ var/miniball = FALSE
var/produced_power
var/energy_to_raise = 32
var/energy_to_lower = -20
+ var/list/shocked_things = list()
-/obj/singularity/energy_ball/ex_act(severity)
+/obj/singularity/energy_ball/Initialize(mapload, starting_energy = 50, is_miniball = FALSE)
+ miniball = is_miniball
+ . = ..()
+ if(!is_miniball)
+ set_light(10, 7, "#5e5edd")
+
+/obj/singularity/energy_ball/ex_act(severity, target)
+ return
+
+/obj/singularity/energy_ball/consume(severity, target)
return
/obj/singularity/energy_ball/Destroy()
@@ -32,43 +51,54 @@
orbiting = null
QDEL_LIST(orbiting_balls)
-
+ shocked_things.Cut()
return ..()
+/obj/singularity/energy_ball/admin_investigate_setup()
+ if(miniball)
+ return //don't annnounce miniballs
+ ..()
+
/obj/singularity/energy_ball/process()
if(!orbiting)
handle_energy()
- move_the_basket_ball(4 + orbiting_balls.len * 1.5)
+ move_the_basket_ball(4 + length(orbiting_balls) * 1.5)
- playsound(src.loc, 'sound/magic/lightningbolt.ogg', 100, 1, extrarange = 30)
+ playsound(loc, 'sound/magic/lightningbolt.ogg', 100, TRUE, extrarange = 30)
pixel_x = 0
pixel_y = 0
-
- setDir(tesla_zap(src, 7, TESLA_DEFAULT_POWER, TRUE))
+ shocked_things.Cut(1, length(shocked_things) / 1.3)
+ var/list/shocking_info = list()
+ tesla_zap(src, 3, TESLA_DEFAULT_POWER, shocked_targets = shocking_info)
pixel_x = -32
pixel_y = -32
for(var/ball in orbiting_balls)
- var/range = rand(1, clamp(orbiting_balls.len, 3, 7))
- tesla_zap(ball, range, TESLA_MINI_POWER/7*range, TRUE)
+ var/range = rand(1, clamp(length(orbiting_balls), 2, 3))
+ var/list/temp_shock = list()
+ //We zap off the main ball instead of ourselves to make things looks proper
+ tesla_zap(src, range, TESLA_MINI_POWER / 7 * range, shocked_targets = temp_shock)
+ shocking_info += temp_shock
+ shocked_things += shocking_info
else
- energy = 0 // ensure we dont have miniballs of miniballs
+ energy = 0 // ensure we dont have miniballs of miniballs //But it'll be cool broooooooooooooooo
/obj/singularity/energy_ball/examine(mob/user)
. = ..()
- var/len = LAZYLEN(orbiting_balls)
- if(len)
- . += "The amount of orbiting mini-balls is [len]."
+ if(length(orbiting_balls))
+ . += "There are [length(orbiting_balls)] mini-balls orbiting it."
-
-/obj/singularity/energy_ball/proc/move_the_basket_ball(var/move_amount)
- //we face the last thing we zapped, so this lets us favor that direction a bit
- var/first_move = dir
+/obj/singularity/energy_ball/proc/move_the_basket_ball(move_amount)
+ var/list/dirs = GLOB.alldirs.Copy()
+ if(length(shocked_things))
+ for(var/i in 1 to 30)
+ var/atom/real_thing = pick(shocked_things)
+ dirs += get_dir(src, real_thing) //Carry some momentum yeah? Just a bit tho
for(var/i in 0 to move_amount)
- var/move_dir = pick(GLOB.alldirs + first_move) //give the first move direction a bit of favoring.
- if(target && prob(60))
+ var/move_dir = pick(dirs) //ensures teslas don't just sit around
+ if(target && prob(10))
move_dir = get_dir(src,target)
var/turf/T = get_step(src, move_dir)
if(can_move(T))
@@ -77,29 +107,28 @@
for(var/mob/living/carbon/C in loc)
dust_mobs(C)
-
/obj/singularity/energy_ball/proc/handle_energy()
if(energy >= energy_to_raise)
energy_to_lower = energy_to_raise - 20
energy_to_raise = energy_to_raise * 1.25
- playsound(src.loc, 'sound/magic/lightning_chargeup.ogg', 100, 1, extrarange = 30)
+ playsound(src.loc, 'sound/magic/lightning_chargeup.ogg', 100, TRUE, extrarange = 30)
addtimer(CALLBACK(src, .proc/new_mini_ball), 100)
- else if(energy < energy_to_lower && orbiting_balls.len)
+ else if(energy < energy_to_lower && length(orbiting_balls))
energy_to_raise = energy_to_raise / 1.25
energy_to_lower = (energy_to_raise / 1.25) - 20
var/Orchiectomy_target = pick(orbiting_balls)
qdel(Orchiectomy_target)
- else if(orbiting_balls.len)
+ else if(length(orbiting_balls))
dissipate() //sing code has a much better system.
/obj/singularity/energy_ball/proc/new_mini_ball()
if(!loc)
return
- var/obj/singularity/energy_ball/EB = new(loc)
+ var/obj/singularity/energy_ball/EB = new(loc, 0, TRUE)
EB.transform *= pick(0.3, 0.4, 0.5, 0.6, 0.7)
var/icon/I = icon(icon,icon_state,dir)
@@ -109,12 +138,11 @@
EB.orbit(src, orbitsize, pick(FALSE, TRUE), rand(10, 25), pick(3, 4, 5, 6, 36))
-
/obj/singularity/energy_ball/Bump(atom/A)
dust_mobs(A)
-/obj/singularity/energy_ball/Bumped(atom/A)
- dust_mobs(A)
+/obj/singularity/energy_ball/Bumped(atom/movable/AM)
+ dust_mobs(AM)
/obj/singularity/energy_ball/attack_tk(mob/user)
if(iscarbon(user))
@@ -130,13 +158,12 @@
if(istype(target))
target.orbiting_balls += src
GLOB.poi_list -= src
- target.dissipate_strength = target.orbiting_balls.len
-
+ target.dissipate_strength = length(target.orbiting_balls)
. = ..()
if(istype(target))
target.orbiting_balls -= src
- target.dissipate_strength = target.orbiting_balls.len
+ target.dissipate_strength = length(target.orbiting_balls)
if(!loc)
qdel(src)
@@ -153,23 +180,26 @@
var/mob/living/carbon/C = A
C.dust()
-/proc/tesla_zap(atom/source, zap_range = 3, power, explosive = FALSE, stun_mobs = TRUE)
+/proc/tesla_zap(atom/source, zap_range = 3, power, zap_flags = ZAP_DEFAULT_FLAGS, list/shocked_targets = list())
+ if(QDELETED(source))
+ return
+ if(!(zap_flags & ZAP_ALLOW_DUPLICATES))
+ LAZYSET(shocked_targets, source, TRUE) //I don't want no null refs in my list yeah?
. = source.dir
if(power < 1000)
return
- var/closest_dist = 0
- var/closest_atom
- var/obj/machinery/power/tesla_coil/closest_tesla_coil
- var/obj/machinery/power/grounding_rod/closest_grounding_rod
- var/mob/living/closest_mob
- var/obj/machinery/closest_machine
- var/obj/structure/closest_structure
- var/obj/structure/blob/closest_blob
- var/static/things_to_shock = typecacheof(list(/obj/machinery, /mob/living, /obj/structure))
+ /*
+ THIS IS SO FUCKING UGLY AND I HATE IT, but I can't make it nice without making it slower, check*N rather then n. So we're stuck with it.
+ */
+ var/atom/closest_atom
+ var/closest_type = 0
+ var/static/things_to_shock = typecacheof(list(/obj/machinery, /mob/living, /obj/structure, /obj/vehicle))
var/static/blacklisted_tesla_types = typecacheof(list(/obj/machinery/atmospherics,
+ /obj/machinery/portable_atmospherics,
/obj/machinery/power/emitter,
/obj/machinery/field/generator,
+ /mob/living/simple_animal/slime,
/obj/machinery/particle_accelerator/control_box,
/obj/structure/particle_accelerator/fuel_chamber,
/obj/structure/particle_accelerator/particle_emitter/center,
@@ -184,109 +214,124 @@
/obj/machinery/camera,
/obj/structure/sign,
/obj/machinery/gateway,
+ /obj/structure/lattice,
/obj/structure/grille,
/obj/machinery/the_singularitygen/tesla,
- /mob/living/simple_animal/slime))
+ /obj/machinery/constructable_frame/machine_frame))
+ //Ok so we are making an assumption here. We assume that view() still calculates from the center out.
+ //This means that if we find an object we can assume it is the closest one of its type. This is somewhat of a speed increase.
+ //This also means we have no need to track distance, as the doview() proc does it all for us.
- for(var/A in typecache_filter_multi_list_exclusion(oview(source, zap_range+2), things_to_shock, blacklisted_tesla_types))
- if(istype(A, /obj/machinery/power/tesla_coil))
- var/dist = get_dist(source, A)
- var/obj/machinery/power/tesla_coil/C = A
- if(dist <= zap_range && (dist < closest_dist || !closest_tesla_coil) && !C.being_shocked)
- closest_dist = dist
-
- //we use both of these to save on istype and typecasting overhead later on
- //while still allowing common code to run before hand
- closest_tesla_coil = C
- closest_atom = C
-
-
- else if(closest_tesla_coil)
+ //Darkness fucks oview up hard. I've tried dview() but it doesn't seem to work
+ //I hate existance
+ for(var/a in typecache_filter_multi_list_exclusion(oview(zap_range + 2, source), things_to_shock, blacklisted_tesla_types))
+ var/atom/A = a
+ if(!(zap_flags & ZAP_ALLOW_DUPLICATES) && LAZYACCESS(shocked_targets, A))
+ continue
+ if(closest_type >= COIL)
continue //no need checking these other things
- else if(istype(A, /obj/machinery/power/grounding_rod))
- var/dist = get_dist(source, A)-2
- if(dist <= zap_range && (dist < closest_dist || !closest_grounding_rod))
- closest_grounding_rod = A
- closest_atom = A
- closest_dist = dist
+ else if(istype(A, /obj/machinery/power/tesla_coil))
+ var/obj/machinery/power/tesla_coil/C = A
+ if(!C.being_shocked)
+ closest_type = COIL
+ closest_atom = C
- else if(closest_grounding_rod)
+ else if(closest_type >= ROD)
+ continue
+
+ else if(istype(A, /obj/machinery/power/grounding_rod))
+ closest_type = ROD
+ closest_atom = A
+
+ else if(closest_type >= RIDE)
+ continue
+
+ else if(istype(A, /obj/vehicle))
+ var/obj/vehicle/R = A
+ if(R.can_buckle && !R.being_shocked)
+ closest_type = RIDE
+ closest_atom = A
+
+ else if(closest_type >= LIVING)
continue
else if(isliving(A))
- var/dist = get_dist(source, A)
var/mob/living/L = A
- if(dist <= zap_range && (dist < closest_dist || !closest_mob) && L.stat != DEAD && !L.tesla_ignore)
- closest_mob = L
+ if(L.stat != DEAD && !(HAS_TRAIT(L, TRAIT_TESLA_SHOCKIMMUNE)) && !(L.flags_2 & SHOCKED_2))
+ closest_type = LIVING
closest_atom = A
- closest_dist = dist
- else if(closest_mob)
+ else if(closest_type >= MACHINERY)
continue
- else if(istype(A, /obj/machinery))
+ else if(ismachinery(A))
var/obj/machinery/M = A
- var/dist = get_dist(source, A)
- if(dist <= zap_range && (dist < closest_dist || !closest_machine) && !M.being_shocked)
- closest_machine = M
+ if(!M.being_shocked)
+ closest_type = MACHINERY
closest_atom = A
- closest_dist = dist
- else if(closest_mob)
+ else if(closest_type >= BLOB)
continue
else if(istype(A, /obj/structure/blob))
var/obj/structure/blob/B = A
- var/dist = get_dist(source, A)
- if(dist <= zap_range && (dist < closest_dist || !closest_tesla_coil) && !B.being_shocked)
- closest_blob = B
+ if(!B.being_shocked)
+ closest_type = BLOB
closest_atom = A
- closest_dist = dist
- else if(closest_blob)
+ else if(closest_type >= STRUCTURE)
continue
else if(isstructure(A))
var/obj/structure/S = A
- var/dist = get_dist(source, A)
- if(dist <= zap_range && (dist < closest_dist || !closest_tesla_coil) && !S.being_shocked)
- closest_structure = S
+ if(!S.being_shocked)
+ closest_type = STRUCTURE
closest_atom = A
- closest_dist = dist
//Alright, we've done our loop, now lets see if was anything interesting in range
- if(closest_atom)
- //common stuff
- source.Beam(closest_atom, icon_state="lightning[rand(1,12)]", icon='icons/effects/effects.dmi', time=5)
- var/zapdir = get_dir(source, closest_atom)
- if(zapdir)
- . = zapdir
+ if(!closest_atom)
+ return
+ //common stuff
+ source.Beam(closest_atom, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5, maxdistance = INFINITY)
+ var/zapdir = get_dir(source, closest_atom)
+ if(zapdir)
+ . = zapdir
- //per type stuff:
- if(closest_tesla_coil)
- closest_tesla_coil.tesla_act(power, explosive)
+ var/next_range = 2
+ if(closest_type == COIL)
+ next_range = 5
- else if(closest_grounding_rod)
- closest_grounding_rod.tesla_act(power, explosive)
-
- else if(closest_mob)
- var/shock_damage = clamp(round(power/400), 10, 90) + rand(-5, 5)
- closest_mob.electrocute_act(shock_damage, source, 1, tesla_shock = TRUE)
+ if(closest_type == LIVING)
+ var/mob/living/closest_mob = closest_atom
+ closest_mob.set_shocked()
+ addtimer(CALLBACK(closest_mob, /mob/living/proc/reset_shocked), 10)
+ var/shock_damage = (zap_flags & ZAP_MOB_DAMAGE) ? (min(round(power / 600), 90) + rand(-5, 5)) : 0
+ closest_mob.electrocute_act(shock_damage, source, 1, SHOCK_TESLA | ((zap_flags & ZAP_MOB_STUN) ? NONE : SHOCK_NOSTUN))
if(issilicon(closest_mob))
var/mob/living/silicon/S = closest_mob
- if(stun_mobs)
- S.emp_act(2)
- tesla_zap(S, 7, power / 1.5, explosive, stun_mobs) // metallic folks bounce it further
+ if((zap_flags & ZAP_MOB_STUN) && (zap_flags & ZAP_MOB_DAMAGE))
+ S.emp_act(EMP_LIGHT)
+ next_range = 7 // metallic folks bounce it further
else
- tesla_zap(closest_mob, 5, power / 1.5, explosive, stun_mobs)
+ next_range = 5
+ power /= 1.5
- else if(closest_machine)
- closest_machine.tesla_act(power, explosive)
+ else
+ power = closest_atom.zap_act(power, zap_flags)
+ if(prob(20)) //I know I know
+ var/list/shocked_copy = shocked_targets.Copy()
+ tesla_zap(closest_atom, next_range, power * 0.5, zap_flags, shocked_copy) //Normally I'd copy here so grounding rods work properly, but it fucks with movement
+ tesla_zap(closest_atom, next_range, power * 0.5, zap_flags, shocked_targets)
+ shocked_targets += shocked_copy
+ else
+ tesla_zap(closest_atom, next_range, power, zap_flags, shocked_targets)
- else if(closest_blob)
- closest_blob.tesla_act(power, explosive)
-
- else if(closest_structure)
- closest_structure.tesla_act(power, explosive)
+#undef COIL
+#undef ROD
+#undef RIDE
+#undef LIVING
+#undef MACHINERY
+#undef BLOB
+#undef STRUCTURE
diff --git a/code/modules/power/tesla/generator.dm b/code/modules/power/tesla/generator.dm
index 63bce099fab..260f4128bcf 100644
--- a/code/modules/power/tesla/generator.dm
+++ b/code/modules/power/tesla/generator.dm
@@ -5,6 +5,6 @@
icon_state = "TheSingGen"
creation_type = /obj/singularity/energy_ball
-/obj/machinery/the_singularitygen/tesla/tesla_act(power, explosive = FALSE)
- if(explosive)
+/obj/machinery/the_singularitygen/tesla/zap_act(power, zap_flags)
+ if(zap_flags & ZAP_MACHINE_EXPLOSIVE)
energy += power
diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm
index 1781e1970eb..2a2131cc308 100644
--- a/code/modules/projectiles/projectile/energy.dm
+++ b/code/modules/projectiles/projectile/energy.dm
@@ -75,19 +75,22 @@
name = "shock bolt"
icon_state = "purple_laser"
impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser
+ damage = 10 //A worse lasergun
+ var/zap_flags = ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE
+ var/zap_range = 3
+ var/power = 10000
var/chain
/obj/item/ammo_casing/energy/shock_revolver/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
..()
var/obj/item/projectile/energy/shock_revolver/P = BB
spawn(1)
- P.chain = P.Beam(user,icon_state="purple_lightning",icon = 'icons/effects/effects.dmi',time=1000, maxdistance = 30)
+ P.chain = P.Beam(user, icon_state = "purple_lightning", icon = 'icons/effects/effects.dmi', time = 1000, maxdistance = 30)
/obj/item/projectile/energy/shock_revolver/on_hit(atom/target)
. = ..()
- if(isliving(target))
- tesla_zap(src, 3, 10000)
- qdel(chain)
+ tesla_zap(src, zap_range, power, zap_flags)
+ qdel(src)
/obj/item/projectile/energy/toxplasma
name = "plasma bolt"
diff --git a/code/modules/reagents/chemistry/reagents/blob.dm b/code/modules/reagents/chemistry/reagents/blob.dm
index d3a157c9e2d..430586d5654 100644
--- a/code/modules/reagents/chemistry/reagents/blob.dm
+++ b/code/modules/reagents/chemistry/reagents/blob.dm
@@ -182,7 +182,7 @@
M.apply_damage(0.4 * volume, BURN)
if(M.reagents)
if(M.reagents.has_reagent("teslium") && prob(0.6 * volume))
- M.electrocute_act((0.5 * volume), "the blob's electrical discharge", 1, TRUE)
+ M.electrocute_act((0.5 * volume), "the blob's electrical discharge", 1, SHOCK_NOGLOVES)
M.reagents.del_reagent("teslium")
return //don't add more teslium after you shock it out of someone.
M.reagents.add_reagent("teslium", 0.125 * volume) // a little goes a long way
diff --git a/code/modules/reagents/chemistry/reagents/toxins.dm b/code/modules/reagents/chemistry/reagents/toxins.dm
index 49a462b4d86..73e393ea66a 100644
--- a/code/modules/reagents/chemistry/reagents/toxins.dm
+++ b/code/modules/reagents/chemistry/reagents/toxins.dm
@@ -1233,10 +1233,22 @@
shock_timer++
if(shock_timer >= rand(5,30)) //Random shocks are wildly unpredictable
shock_timer = 0
- M.electrocute_act(rand(5, 20), "Teslium in their body", 1, TRUE) //Override because it's caused from INSIDE of you
+ M.electrocute_act(rand(5, 20), "Teslium in their body", 1, SHOCK_NOGLOVES) //Override because it's caused from INSIDE of you
playsound(M, "sparks", 50, 1)
return ..()
+/datum/reagent/teslium/on_mob_add(mob/living/M)
+ ..()
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ H.dna.species.siemens_coeff *= 2
+
+/datum/reagent/teslium/on_mob_delete(mob/living/M)
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ H.dna.species.siemens_coeff *= 0.5
+ ..()
+
/datum/reagent/gluttonytoxin
name = "Gluttony's Blessing"
id = "gluttonytoxin"
diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm
index 690f0688b62..7e376b0ebaa 100644
--- a/code/modules/reagents/reagent_dispenser.dm
+++ b/code/modules/reagents/reagent_dispenser.dm
@@ -110,9 +110,10 @@
..()
boom()
-/obj/structure/reagent_dispensers/fueltank/tesla_act()
- ..() //extend the zap
- boom()
+/obj/structure/reagent_dispensers/fueltank/zap_act(power, zap_flags)
+ . = ..() //extend the zap
+ if(ZAP_OBJ_DAMAGE & zap_flags)
+ boom()
/obj/structure/reagent_dispensers/fueltank/examine(mob/user)
. = ..()
diff --git a/code/modules/vehicle/vehicle.dm b/code/modules/vehicle/vehicle.dm
index 308967cdf82..06a93bda215 100644
--- a/code/modules/vehicle/vehicle.dm
+++ b/code/modules/vehicle/vehicle.dm
@@ -230,3 +230,7 @@
/obj/vehicle/space/Process_Spacemove(direction)
return TRUE
+
+/obj/vehicle/zap_act(power, zap_flags)
+ zap_buckle_check(power)
+ return ..()