From 8947e97f3b2d49075eab25afd91a6b701276b6e7 Mon Sep 17 00:00:00 2001 From: Fox McCloud Date: Wed, 3 Feb 2021 15:52:03 -0500 Subject: [PATCH] Refactors Electrocution and Tesla Zapping (#15357) * Refactors Electrocution and Tesla Zapping---Improves Supermatter and Tesla * forgot this one * fixes and styling * last tidbits hopefully * machine frames * use flags * styling --- code/__DEFINES/flags.dm | 16 +- code/__DEFINES/mobs.dm | 11 + code/__HELPERS/traits.dm | 2 +- code/_globalvars/traits.dm | 4 +- code/_onclick/click_override.dm | 2 +- code/datums/spells/lightning.dm | 4 +- code/game/atoms.dm | 8 + code/game/gamemodes/blob/theblob.dm | 7 +- .../gamemodes/miniantags/abduction/gland.dm | 2 +- .../gamemodes/miniantags/bot_swarm/swarmer.dm | 6 +- .../miniantags/guardian/types/lightning.dm | 2 +- .../gamemodes/miniantags/revenant/revenant.dm | 2 +- .../miniantags/revenant/revenant_abilities.dm | 2 +- code/game/gamemodes/nuclear/nuclearbomb.dm | 6 +- code/game/machinery/camera/camera.dm | 6 - code/game/machinery/doors/airlock.dm | 2 +- code/game/machinery/doors/door.dm | 4 + code/game/machinery/machinery.dm | 17 +- code/game/objects/effects/anomalies.dm | 2 +- code/game/objects/effects/effects.dm | 3 - code/game/objects/obj_defense.dm | 17 +- code/game/objects/structures.dm | 6 + .../structures/crates_lockers/closets.dm | 5 - code/game/objects/structures/grille.dm | 2 +- code/game/objects/structures/holosign.dm | 4 +- code/modules/admin/topic.dm | 2 +- code/modules/hydroponics/plant_genes.dm | 4 +- code/modules/mob/living/carbon/carbon.dm | 80 +++--- code/modules/mob/living/carbon/human/human.dm | 46 ++- .../living/carbon/human/species/_species.dm | 2 +- code/modules/mob/living/living_defense.dm | 35 ++- code/modules/mob/living/living_defines.dm | 3 - code/modules/mob/living/silicon/silicon.dm | 2 +- .../mob/living/simple_animal/constructs.dm | 2 +- .../simple_animal/hostile/floorcluwne.dm | 2 +- .../hostile/megafauna/swarmer.dm | 2 +- code/modules/mob/living/status_procs.dm | 6 + code/modules/power/gravitygenerator.dm | 6 +- code/modules/power/lighting.dm | 9 +- code/modules/power/singularity/narsie.dm | 1 - code/modules/power/singularity/singularity.dm | 16 +- code/modules/power/supermatter/supermatter.dm | 250 ++++++++++------ code/modules/power/tesla/coil.dm | 63 +++-- code/modules/power/tesla/energy_ball.dm | 267 ++++++++++-------- code/modules/power/tesla/generator.dm | 4 +- code/modules/projectiles/projectile/energy.dm | 11 +- .../reagents/chemistry/reagents/blob.dm | 2 +- .../reagents/chemistry/reagents/toxins.dm | 14 +- code/modules/reagents/reagent_dispenser.dm | 7 +- code/modules/vehicle/vehicle.dm | 4 + 50 files changed, 598 insertions(+), 384 deletions(-) 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 ..()