diff --git a/code/__DEFINES/anomalies.dm b/code/__DEFINES/anomalies.dm
new file mode 100644
index 000000000000..59bcbf05c638
--- /dev/null
+++ b/code/__DEFINES/anomalies.dm
@@ -0,0 +1,22 @@
+///Defines for anomaly types
+#define ANOMALY_FLUX "flux_anomaly"
+#define ANOMALY_FLUX_EXPLOSIVE "flux_explosive_anomaly"
+#define ANOMALY_GRAVITATIONAL "gravitational_anomaly"
+#define ANOMALY_HALLUCINATION "hallucination_anomaly"
+#define ANOMALY_PYRO "pyro_anomaly"
+#define ANOMALY_BLUESPACE "bluespace_anomaly"
+#define ANOMALY_VORTEX "vortex_anomaly"
+#define ANOMALY_RADIATION "radiation_anomaly"
+#define ANOMALY_RADIATION_X "radiation_goat_anomaly"
+
+///Defines for area allowances
+#define ANOMALY_AREA_BLACKLIST list(/area/ai_monitored/turret_protected/ai,/area/ai_monitored/turret_protected/ai_upload,/area/engine,/area/solar,/area/holodeck,/area/shuttle)
+#define ANOMALY_AREA_SUBTYPE_WHITELIST list(/area/engine/break_room)
+
+///Defines for the different types of explosion a flux anomaly can have
+#define ANOMALY_FLUX_NO_EXPLOSION 0
+#define ANOMALY_FLUX_EXPLOSION 1
+
+///Defines if rad anomaly can spawn rad goat can have
+#define ANOMALY_RADIATION_NO_GOAT 0
+#define ANOMALY_RADIATION_YES_GOAT 1
diff --git a/code/__DEFINES/power.dm b/code/__DEFINES/power.dm
index 58edfeb91baa..bf481517156a 100644
--- a/code/__DEFINES/power.dm
+++ b/code/__DEFINES/power.dm
@@ -2,6 +2,7 @@
#define SOLAR_TRACK_TIMED 1
#define SOLAR_TRACK_AUTO 2
+#define TESLA_HYPERCHARGED_POWER TESLA_DEFAULT_POWER*2
#define TESLA_DEFAULT_POWER 1738260
#define TESLA_MINI_POWER 869130
//Multiplier of all power consumed.
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index 6530b7475f18..88683c70aaa9 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -609,7 +609,7 @@ Class Procs:
/obj/machinery/proc/can_be_overridden()
. = 1
-/obj/machinery/tesla_act(power, tesla_flags, shocked_objects)
+/obj/machinery/tesla_act(power, tesla_flags, shocked_objects, zap_gib = FALSE)
..()
if((tesla_flags & TESLA_MACHINE_EXPLOSIVE) && !(resistance_flags & INDESTRUCTIBLE))
if(prob(60))
diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm
index 003fa74a0a19..3ca575ba7cb1 100644
--- a/code/game/objects/effects/anomalies.dm
+++ b/code/game/objects/effects/anomalies.dm
@@ -2,6 +2,8 @@
/// Chance of taking a step per second
#define ANOMALY_MOVECHANCE 45
+/////////////////////
+
/obj/effect/anomaly
name = "anomaly"
desc = "A mysterious anomaly, seen commonly only in the region of space that the station orbits..."
@@ -10,6 +12,7 @@
anchored = TRUE
light_range = 3
var/obj/item/assembly/signaler/anomaly/aSignal
+ var/core_type
var/area/impact_area
var/lifespan = 990
@@ -18,16 +21,32 @@
var/countdown_colour
var/obj/effect/countdown/anomaly/countdown
+ /// Do we keep on living forever?
+ var/immortal = FALSE
+
/obj/effect/anomaly/Initialize(mapload, new_lifespan)
. = ..()
GLOB.poi_list |= src
START_PROCESSING(SSobj, src)
impact_area = get_area(src)
- aSignal = new(src)
- aSignal.name = "[name] core"
+ switch(core_type)
+ if(ANOMALY_RADIATION)
+ aSignal = new /obj/item/assembly/signaler/anomaly/radiation(src)
+ if(ANOMALY_HALLUCINATION)
+ aSignal = new /obj/item/assembly/signaler/anomaly/hallucination(src)
+ if(ANOMALY_FLUX)
+ aSignal = new /obj/item/assembly/signaler/anomaly/flux(src)
+ if(ANOMALY_GRAVITATIONAL)
+ aSignal = new /obj/item/assembly/signaler/anomaly/grav(src)
+ if(ANOMALY_PYRO)
+ aSignal = new /obj/item/assembly/signaler/anomaly/pyro(src)
+ if(ANOMALY_BLUESPACE)
+ aSignal = new /obj/item/assembly/signaler/anomaly/bluespace(src)
+ if(ANOMALY_VORTEX)
+ aSignal = new /obj/item/assembly/signaler/anomaly/vortex(src)
+
aSignal.code = rand(1,100)
- aSignal.anomaly_type = type
var/frequency = rand(MIN_FREE_FREQ, MAX_FREE_FREQ)
if(ISMULTIPLE(frequency, 2))//signaller frequencies are always uneven!
@@ -37,6 +56,8 @@
if(new_lifespan)
lifespan = new_lifespan
death_time = world.time + lifespan
+ if(immortal)
+ return // no countdown for forever anomalies
countdown = new(src)
if(countdown_colour)
countdown.color = countdown_colour
@@ -44,7 +65,7 @@
/obj/effect/anomaly/process(delta_time)
anomalyEffect(delta_time)
- if(death_time < world.time)
+ if(death_time < world.time && !immortal)
if(loc)
detonate()
qdel(src)
@@ -84,6 +105,7 @@
/obj/effect/anomaly/grav
name = "gravitational anomaly"
icon_state = "shield2"
+ core_type = ANOMALY_GRAVITATIONAL
density = FALSE
var/boing = 0
@@ -140,10 +162,14 @@
/obj/effect/anomaly/flux
name = "flux wave anomaly"
icon_state = "electricity2"
+ core_type = ANOMALY_FLUX
density = FALSE // so it doesn't awkwardly block movement when it doesn't stun you
var/canshock = 0
var/shockdamage = 30
- var/explosive = TRUE
+ var/explosive = ANOMALY_FLUX_NO_EXPLOSION
+
+/obj/effect/anomaly/flux/explosion
+ explosive = ANOMALY_FLUX_EXPLOSION
/obj/effect/anomaly/flux/anomalyEffect(delta_time)
..()
@@ -184,13 +210,11 @@
span_italics("You hear a heavy electrical crack."))
/obj/effect/anomaly/flux/detonate()
- if(explosive)
- message_admins("An anomaly has detonated.") //yogs
- log_game("An anomaly has detonated.") //yogs
- explosion(src, 1, 4, 16, 18) //Low devastation, but hits a lot of stuff.
- else
- new /obj/effect/particle_effect/sparks(loc)
-
+ switch(explosive)
+ if(ANOMALY_FLUX_EXPLOSION)
+ explosion(src, devastation_range = 1, heavy_impact_range = 4, light_impact_range = 16, flash_range = 18) //Low devastation, but hits a lot of stuff.
+ if(ANOMALY_FLUX_NO_EXPLOSION)
+ new /obj/effect/particle_effect/sparks(loc)
/////////////////////
@@ -198,6 +222,7 @@
name = "bluespace anomaly"
icon = 'icons/obj/projectiles.dmi'
icon_state = "bluespace"
+ core_type = ANOMALY_BLUESPACE
density = TRUE
/obj/effect/anomaly/bluespace/anomalyEffect()
@@ -269,6 +294,7 @@
name = "pyroclastic anomaly"
icon_state = "pyro"
color = "#ffa952"
+ core_type = ANOMALY_PYRO
var/ticks = 0
/// How many seconds between each gas release
var/releasedelay = 10
@@ -308,6 +334,7 @@
/obj/effect/anomaly/bhole
name = "vortex anomaly"
icon_state = "bhole3"
+ core_type = ANOMALY_VORTEX
desc = "That's a nice station you have there. It'd be a shame if something happened to it."
/obj/effect/anomaly/bhole/anomalyEffect()
@@ -371,51 +398,89 @@
/////////////////////////
/obj/effect/anomaly/radiation
name = "radiation anomaly"
- icon = 'icons/obj/projectiles.dmi'
icon_state = "radiation_anomaly"
+ core_type = ANOMALY_RADIATION
density = TRUE
- var/spawn_goat = FALSE //For goat spawning
+ var/spawn_goat = ANOMALY_RADIATION_NO_GOAT //For goat spawning
-/obj/effect/anomaly/radiation/admin //bussing
- spawn_goat = TRUE
+/obj/effect/anomaly/radiation/goat //bussing
+ spawn_goat = ANOMALY_RADIATION_YES_GOAT
/obj/effect/anomaly/radiation/anomalyEffect()
..()
- for(var/i = 1 to 15)
+ for(var/i = 1 to 5)
fire_nuclear_particle()
- radiation_pulse(src, 500, 5)
+ radiation_pulse(src, 10000, 5)
/obj/effect/anomaly/radiation/proc/makegoat()
- var/turf/open/T = get_turf(src)
- var/mob/living/simple_animal/hostile/retaliate/goat/radioactive/S = new(T)
-
- var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a radioactive goat?", ROLE_SENTIENCE, null, null, 100, S, POLL_IGNORE_PYROSLIME)
- if(LAZYLEN(candidates))
- var/mob/dead/observer/chosen = pick(candidates)
- S.key = chosen.key
- var/datum/action/cooldown/spell/conjure/radiation_anomaly/spell
- spell.Grant(S)
- log_game("[key_name(S.key)] was made into a radioactive goat by radiation anomaly at [AREACOORD(T)].")
+ for(var/i=1 to 15)
+ fire_nuclear_particle()
+ radiation_pulse(src, 20000, 7)
+ if(spawn_goat == ANOMALY_RADIATION_YES_GOAT)
+ var/turf/open/T = get_turf(src)
+ var/mob/living/simple_animal/hostile/retaliate/goat/radioactive/S = new(T)
+
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a radioactive goat?", ROLE_SENTIENCE, null, null, 100, S, POLL_IGNORE_PYROSLIME)
+ if(LAZYLEN(candidates))
+ var/mob/dead/observer/chosen = pick(candidates)
+ S.key = chosen.key
+ var/datum/action/cooldown/spell/conjure/radiation_anomaly/spell
+ spell.Grant(S)
+ log_game("[key_name(S.key)] was made into a radioactive goat by radiation anomaly at [AREACOORD(T)].")
/obj/effect/anomaly/radiation/detonate()
- INVOKE_ASYNC(src, PROC_REF(rad_Spin))
+ INVOKE_ASYNC(src, PROC_REF(makegoat))
-/obj/effect/anomaly/radiation/proc/rad_Spin(increment = 1)
- if(increment > 100)
- if(spawn_goat)//only spawn the goat once, when the anomaly explodes
- INVOKE_ASYNC(src, PROC_REF(makegoat))
- qdel(src)
- radiation_pulse(src, 5000, 7)
- var/turf/T = get_turf(src)
- var/angle = increment * 10
- T.fire_nuclear_particle(angle)
- addtimer(CALLBACK(src, PROC_REF(rad_Spin), increment + 1), 0.7)
-
+/////////////////////
-/obj/effect/anomaly/radiation/process(delta_time)
- anomalyEffect(delta_time)
- if(death_time < world.time)
- if(loc)
- detonate()
+/obj/effect/anomaly/hallucination
+ name = "hallucination anomaly"
+ icon_state = "hallucination_anomaly"
+ core_type = ANOMALY_HALLUCINATION
+ /// Time passed since the last effect, increased by delta_time of the SSobj
+ var/ticks = 0
+ /// How many seconds between each small hallucination pulses
+ var/release_delay = 5
+
+/obj/effect/anomaly/hallucination/anomalyEffect(delta_time)
+ . = ..()
+ ticks += delta_time
+ if(ticks < release_delay)
+ return
+ ticks -= release_delay
+ var/turf/open/our_turf = get_turf(src)
+ if(istype(our_turf))
+ hallucination_pulse(our_turf, 5)
+
+/obj/effect/anomaly/hallucination/detonate()
+ var/turf/open/our_turf = get_turf(src)
+ if(istype(our_turf))
+ hallucination_pulse(our_turf, 10)
+
+/proc/hallucination_pulse(turf/location, range, strength = 50)
+ for(var/mob/living/carbon/human/near in view(location, range))
+ // If they are immune to hallucinations
+ if (HAS_TRAIT(near, TRAIT_MESONS) || (near.mind && HAS_TRAIT(near.mind, TRAIT_MESONS)))
+ continue
+
+ // Blind people don't get hallucinations
+ if (is_blind(near))
+ continue
+
+ // Everyone else
+ var/dist = sqrt(1 / max(1, get_dist(near, location)))
+ near.adjust_hallucinations(max(150, strength * dist))
+ near.adjust_jitter(10 SECONDS)
+ near.adjust_confusion(10 SECONDS)
+ near.adjust_dizzy(10 SECONDS)
+ near.adjust_drowsiness(10 SECONDS)
+ var/static/list/messages = list(
+ "You feel your conscious mind fall apart!",
+ "Reality warps around you!",
+ "Something's wispering around you!",
+ "You are going insane!",
+ "What was that?!"
+ )
+ to_chat(near, span_warning("[pick(messages)]"))
#undef ANOMALY_MOVECHANCE
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index 48d7010af310..52e7e793e80a 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -240,13 +240,13 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
SSfire_burning.processing -= src
///Called when the obj is hit by a tesla bolt.
-/obj/proc/tesla_act(power, tesla_flags, shocked_targets)
+/obj/proc/tesla_act(power, tesla_flags, shocked_targets, zap_gib = FALSE)
obj_flags |= BEING_SHOCKED
addtimer(CALLBACK(src, PROC_REF(reset_shocked)), 10)
if(power < TESLA_MINI_POWER) //tesla bolts bounce twice, tesla miniball bolts bounce only once
return
var/power_bounced = power / 2
- tesla_zap(src, 3, power_bounced, tesla_flags, shocked_targets)
+ tesla_zap(src, 3, power_bounced, tesla_flags, shocked_targets, zap_gib)
//The surgeon general warns that being buckled to certain objects receiving powerful shocks is greatly hazardous to your health
///Only tesla coils and grounding rods currently call this because mobs are already targeted over all other objects, but this might be useful for more things later.
diff --git a/code/modules/antagonists/blob/structures/_blob.dm b/code/modules/antagonists/blob/structures/_blob.dm
index f6cd06afe898..7178befd2ad3 100644
--- a/code/modules/antagonists/blob/structures/_blob.dm
+++ b/code/modules/antagonists/blob/structures/_blob.dm
@@ -209,7 +209,7 @@
if(prob(100 - severity * 30))
new /obj/effect/temp_visual/emp(get_turf(src))
-/obj/structure/blob/tesla_act(power)
+/obj/structure/blob/tesla_act(power, tesla_flags, shocked_targets, zap_gib = FALSE)
..()
if(overmind)
if(overmind.blobstrain.tesla_reaction(src, power))
diff --git a/code/modules/antagonists/clockcult/clock_mobs.dm b/code/modules/antagonists/clockcult/clock_mobs.dm
index c2bd915cc3aa..f51d9430640f 100644
--- a/code/modules/antagonists/clockcult/clock_mobs.dm
+++ b/code/modules/antagonists/clockcult/clock_mobs.dm
@@ -37,7 +37,7 @@
/mob/living/simple_animal/hostile/clockwork/ratvar_act()
fully_heal(TRUE)
-/mob/living/simple_animal/hostile/clockwork/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
+/mob/living/simple_animal/hostile/clockwork/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE, gib = FALSE)
return 0 //ouch, my metal-unlikely-to-be-damaged-by-electricity-body
/mob/living/simple_animal/hostile/clockwork/examine(mob/user)
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index 6d18e95b65df..d4c6bdef3518 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -428,7 +428,7 @@
return
qdel(src)
-/obj/machinery/nuclearbomb/tesla_act(power, tesla_flags)
+/obj/machinery/nuclearbomb/tesla_act(power, tesla_flags, shocked_targets, zap_gib = FALSE)
..()
if(tesla_flags & TESLA_MACHINE_EXPLOSIVE)
qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over
diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm
index 999bc9ac0c40..b13868abde02 100644
--- a/code/modules/assembly/signaler.dm
+++ b/code/modules/assembly/signaler.dm
@@ -185,7 +185,7 @@
/obj/item/assembly/signaler/anomaly
name = "anomaly core"
desc = "The neutralized core of an anomaly. It'd probably be valuable for research."
- icon_state = "anomaly core"
+ icon_state = "anomaly_core"
item_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
@@ -217,10 +217,42 @@
/obj/item/assembly/signaler/anomaly/attack_self()
return
+//Anomaly cores
+/obj/item/assembly/signaler/anomaly/pyro
+ name = "\improper pyroclastic anomaly core"
+ desc = "The neutralized core of a pyroclastic anomaly. It feels warm to the touch. It'd probably be valuable for research."
+ anomaly_type = /obj/effect/anomaly/pyro
+
+/obj/item/assembly/signaler/anomaly/grav
+ name = "\improper gravitational anomaly core"
+ desc = "The neutralized core of a gravitational anomaly. It feels much heavier than it looks. It'd probably be valuable for research."
+ anomaly_type = /obj/effect/anomaly/grav
+
+/obj/item/assembly/signaler/anomaly/flux
+ name = "\improper flux anomaly core"
+ desc = "The neutralized core of a flux anomaly. Touching it makes your skin tingle. It'd probably be valuable for research."
+ anomaly_type = /obj/effect/anomaly/flux
+
+/obj/item/assembly/signaler/anomaly/bluespace
+ name = "\improper bluespace anomaly core"
+ desc = "The neutralized core of a bluespace anomaly. It keeps phasing in and out of view. It'd probably be valuable for research."
+ anomaly_type = /obj/effect/anomaly/bluespace
+
+/obj/item/assembly/signaler/anomaly/vortex
+ name = "\improper vortex anomaly core"
+ desc = "The neutralized core of a vortex anomaly. It won't sit still, as if some invisible force is acting on it. It'd probably be valuable for research."
+ anomaly_type = /obj/effect/anomaly/bhole
+
+/obj/item/assembly/signaler/anomaly/hallucination
+ name = "\improper hallucination anomaly core"
+ desc = "The neutralized core of a hallucination anomaly. It seems to be moving, but it's probably your imagination. It'd probably be valuable for research."
+ icon_state = "hallucination_core"
+ anomaly_type = /obj/effect/anomaly/hallucination
+
/obj/item/assembly/signaler/anomaly/radiation
name = "\improper radiation anomaly core"
desc = "The neutralized core of a radiation anomaly. It keeps pulsing an ominous green. It'd probably be valuable for research."
- icon_state = "anomaly core"
+ icon_state = "radiation_core"
anomaly_type = /obj/effect/anomaly/radiation
/obj/item/assembly/signaler/cyborg
diff --git a/code/modules/clothing/suits/reactive_armour.dm b/code/modules/clothing/suits/reactive_armour.dm
index 67038a58a391..ac89e60fddb8 100644
--- a/code/modules/clothing/suits/reactive_armour.dm
+++ b/code/modules/clothing/suits/reactive_armour.dm
@@ -11,7 +11,9 @@
/obj/effect/anomaly/grav = /obj/item/clothing/suit/armor/reactive/repulse,
/obj/effect/anomaly/flux = /obj/item/clothing/suit/armor/reactive/tesla,
/obj/effect/anomaly/pyro = /obj/item/clothing/suit/armor/reactive/fire,
- /obj/effect/anomaly/bluespace = /obj/item/clothing/suit/armor/reactive/teleport
+ /obj/effect/anomaly/bluespace = /obj/item/clothing/suit/armor/reactive/teleport,
+ /obj/effect/anomaly/radiation = /obj/item/clothing/suit/armor/reactive/radiation,
+ /obj/effect/anomaly/hallucination = /obj/item/clothing/suit/armor/reactive/hallucinating,
)
if(istype(I, /obj/item/assembly/signaler/anomaly))
@@ -28,9 +30,6 @@
/obj/item/clothing/suit/armor/reactive
name = "reactive armor"
desc = "Doesn't seem to do much for some reason."
- var/active = 0
- var/reactivearmor_cooldown_duration = 0 //cooldown specific to reactive armor
- var/reactivearmor_cooldown = 0
icon_state = "reactiveoff"
item_state = "reactiveoff"
blood_overlay_type = "armor"
@@ -38,6 +37,18 @@
actions_types = list(/datum/action/item_action/toggle)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
hit_reaction_chance = 50
+ ///Whether the armor will try to react to hits (is it on)
+ var/active = 0
+ ///This will be true for 30 seconds after an EMP, it makes the reaction effect dangerous to the user.
+ var/bad_effect = FALSE
+ ///Message sent when the armor is emp'd. It is not the message for when the emp effect goes off.
+ var/emp_message = span_warning("The reactive armor has been emp'd! Damn, now it's REALLY gonna not do much!")
+ ///Message sent when the armor is still on cooldown, but activates.
+ var/cooldown_message = span_danger("The reactive armor fails to do much, as it is recharging! From what? Only the reactive armor knows.")
+ ///Duration of the cooldown specific to reactive armor for when it can activate again.
+ var/reactivearmor_cooldown_duration = 5 SECONDS
+ ///The cooldown itself of the reactive armor for when it can activate again.
+ COOLDOWN_DECLARE(reactivearmor_cooldown)
/obj/item/clothing/suit/armor/reactive/attack_self(mob/user)
active = !(active)
@@ -50,94 +61,161 @@
icon_state = "reactiveoff"
item_state = "reactiveoff"
add_fingerprint(user)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ H.update_inv_wear_suit()
+ for(var/datum/action/A in actions)
+ A.build_all_button_icons()
return
+/obj/item/clothing/suit/armor/reactive/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text, damage, attack_type)
+ . = ..()
+
+ if(!active || !prob(hit_reaction_chance))
+ return FALSE
+ if(reactivearmor_cooldown_duration && !COOLDOWN_FINISHED(src, reactivearmor_cooldown))
+ cooldown_activation(owner)
+ return FALSE
+ if(reactivearmor_cooldown_duration)
+ COOLDOWN_START(src, reactivearmor_cooldown, reactivearmor_cooldown_duration)
+
+ if(bad_effect)
+ return emp_activation(owner, hitby, attack_text, damage, attack_type)
+ else
+ return reactive_activation(owner, hitby, attack_text, damage, attack_type)
+
+/**
+ * A proc for doing cooldown effects (like the sparks on the tesla armor, or the semi-stealth on stealth armor)
+ * Called from the suit activating whilst on cooldown.
+ * You should be calling ..()
+ */
+/obj/item/clothing/suit/armor/reactive/proc/cooldown_activation(mob/living/carbon/human/owner)
+ owner.visible_message(cooldown_message)
+
+/**
+ * A proc for doing reactive armor effects.
+ * Called from the suit activating while off cooldown, with no emp.
+ * Returning TRUE will block the attack that triggered this
+ */
+/obj/item/clothing/suit/armor/reactive/proc/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ owner.visible_message(span_danger("The reactive armor doesn't do much! No surprises here."))
+ return TRUE
+
+/**
+ * A proc for doing owner unfriendly reactive armor effects.
+ * Called from the suit activating while off cooldown, while the armor is still suffering from the effect of an EMP.
+ * Returning TRUE will block the attack that triggered this
+ */
+/obj/item/clothing/suit/armor/reactive/proc/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ owner.visible_message(span_danger("The reactive armor doesn't do much, despite being emp'd! Besides giving off a special message, of course."))
+ return TRUE
+
/obj/item/clothing/suit/armor/reactive/emp_act(severity)
. = ..()
- if(. & EMP_PROTECT_SELF)
+ if(. & EMP_PROTECT_SELF || bad_effect || !active) //didn't get hit or already emp'd, or off
return
- active = 0
- icon_state = "reactiveoff"
- item_state = "reactiveoff"
- reactivearmor_cooldown = world.time + 200
+ if(ismob(loc))
+ to_chat(loc, emp_message)
+ bad_effect = TRUE
+ addtimer(VARSET_CALLBACK(src, bad_effect, FALSE), 30 SECONDS)
//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!)
/obj/item/clothing/suit/armor/reactive/teleport
name = "reactive teleport armor"
desc = "Someone separated our Research Director from his own head!"
+ emp_message = span_warning("The reactive armor's teleportation calculations begin spewing errors!")
+ cooldown_message = span_danger("The reactive teleport system is still recharging! It fails to activate!")
+ reactivearmor_cooldown_duration = 10 SECONDS
var/tele_range = 6
var/rad_amount= 15
- reactivearmor_cooldown_duration = 100
-
-/obj/item/clothing/suit/armor/reactive/teleport/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- if(!active)
- return 0
- if(!damage)
- return 0
- if(prob(hit_reaction_chance))
- var/mob/living/carbon/human/H = owner
- if(world.time < reactivearmor_cooldown)
- owner.visible_message(span_danger("The reactive teleport system is still recharging! It fails to teleport [H]!"))
- return
- owner.visible_message(span_danger("The reactive teleport system flings [H] clear of [attack_text], shutting itself off in the process!"))
- do_teleport(H, get_turf(H), tele_range, asoundin = 'sound/magic/blink.ogg', channel = TELEPORT_CHANNEL_BLUESPACE)
- H.rad_act(rad_amount)
- reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
- return 1
- return 0
+
+/obj/item/clothing/suit/armor/reactive/teleport/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ owner.visible_message(span_danger("The reactive teleport system flings [owner] clear of [attack_text], shutting itself off in the process!"))
+ playsound(get_turf(owner),'sound/magic/blink.ogg', 100, 1)
+ do_teleport(teleatom = owner, destination = get_turf(owner), no_effects = TRUE, precision = tele_range, channel = TELEPORT_CHANNEL_BLUESPACE)
+ owner.rad_act(rad_amount)
+ return TRUE
+
+/obj/item/clothing/suit/armor/reactive/teleport/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ owner.visible_message(span_danger("The reactive teleport system flings itself clear of [attack_text], leaving someone behind in the process!"))
+ owner.dropItemToGround(src, TRUE, TRUE)
+ playsound(get_turf(owner), 'sound/machines/buzz-sigh.ogg', 50, 1)
+ playsound(get_turf(owner), 'sound/magic/blink.ogg', 100, 1)
+ do_teleport(teleatom = src, destination = get_turf(owner), no_effects = TRUE, precision = tele_range, channel = TELEPORT_CHANNEL_BLUESPACE)
+ owner.rad_act(rad_amount)
+ return FALSE //you didn't actually evade the attack now did you
//Fire
/obj/item/clothing/suit/armor/reactive/fire
name = "reactive incendiary armor"
desc = "An experimental suit of armor with a reactive sensor array rigged to a flame emitter. For the stylish pyromaniac."
+ cooldown_message = span_danger("The reactive incendiary armor activates, but fails to send out flames as it is still recharging its flame jets!")
+ emp_message = span_warning("The reactive incendiary armor's targeting system begins rebooting...")
+
+/obj/item/clothing/suit/armor/reactive/fire/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ owner.visible_message(span_danger("[src] blocks [attack_text], sending out jets of flame!"))
+ playsound(get_turf(owner),'sound/magic/fireball.ogg', 100, 1)
+ for(var/mob/living/carbon/C in ohearers(6, owner))
+ C.fire_stacks += 8
+ C.ignite_mob()
+ owner.fire_stacks = -20
+ return TRUE
-/obj/item/clothing/suit/armor/reactive/fire/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- if(!active)
- return 0
- if(!damage)
- return 0
- if(prob(hit_reaction_chance))
- if(world.time < reactivearmor_cooldown)
- owner.visible_message(span_danger("The reactive incendiary armor on [owner] activates, but fails to send out flames as it is still recharging its flame jets!"))
- return
- owner.visible_message(span_danger("[src] blocks [attack_text], sending out jets of flame!"))
- playsound(get_turf(owner),'sound/magic/fireball.ogg', 100, 1)
- for(var/mob/living/carbon/C in range(6, owner))
- if(C != owner)
- C.adjust_fire_stacks(8)
- C.ignite_mob()
- owner.fire_stacks = -1
- reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
- return 1
- return 0
+/obj/item/clothing/suit/armor/reactive/fire/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ owner.visible_message(span_danger("[src] just makes [attack_text] worse by spewing fire on [owner]!"))
+ playsound(get_turf(owner),'sound/magic/fireball.ogg', 100, 1)
+ owner.fire_stacks += 12
+ owner.ignite_mob()
+ return FALSE
//Stealth
/obj/item/clothing/suit/armor/reactive/stealth
name = "reactive stealth armor"
desc = "An experimental suit of armor that renders the wearer invisible on detection of imminent harm, and creates a decoy that runs away from the owner. You can't fight what you can't see."
- reactivearmor_cooldown_duration = 80
-
-/obj/item/clothing/suit/armor/reactive/stealth/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- if(!active)
- return 0
- if(!damage)
- return 0
- if(prob(hit_reaction_chance))
- if(world.time < reactivearmor_cooldown)
- owner.visible_message(span_danger("The reactive stealth system on [owner] activates, but is still recharging its holographic emitters!"))
- return
- var/mob/living/simple_animal/hostile/illusion/escape/E = new(owner.loc)
- E.Copy_Parent(owner, 50)
- E.GiveTarget(owner) //so it starts running right away
- E.Goto(owner, E.move_to_delay, E.minimum_distance)
- owner.alpha = 0
- owner.visible_message(span_danger("[owner] is hit by [attack_text] in the chest!")) //We pretend to be hit, since blocking it would stop the message otherwise
- spawn(40)
- owner.alpha = initial(owner.alpha)
- reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
- return 1
+ cooldown_message = span_danger("The reactive stealth system activates, but is not charged enough to fully cloak!")
+ emp_message = span_warning("The reactive stealth armor's threat assessment system crashes...")
+ ///when triggering while on cooldown will only flicker the alpha slightly. this is how much it removes.
+ var/cooldown_alpha_removal = 50
+ ///cooldown alpha flicker- how long it takes to return to the original alpha
+ var/cooldown_animation_time = 3 SECONDS
+ ///how long they will be fully stealthed
+ var/stealth_time = 4 SECONDS
+ ///how long it will animate back the alpha to the original
+ var/animation_time = 2 SECONDS
+ var/in_stealth = FALSE
+
+/obj/item/clothing/suit/armor/reactive/stealth/cooldown_activation(mob/living/carbon/human/owner)
+ if(in_stealth)
+ return //we don't want the cooldown message either)
+ owner.alpha = max(0, owner.alpha - cooldown_alpha_removal)
+ animate(owner, alpha = initial(owner.alpha), time = cooldown_animation_time)
+ ..()
+
+/obj/item/clothing/suit/armor/reactive/stealth/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ var/mob/living/simple_animal/hostile/illusion/escape/decoy = new(owner.loc)
+ decoy.Copy_Parent(owner, 50)
+ decoy.GiveTarget(owner) //so it starts running right away
+ decoy.Goto(owner, decoy.move_to_delay, decoy.minimum_distance)
+ in_stealth = TRUE
+ owner.visible_message(span_danger("[owner] is hit by [attack_text] in the chest!")) //We pretend to be hit, since blocking it would stop the message otherwise
+ owner.alpha = 0
+ addtimer(CALLBACK(src, PROC_REF(end_stealth), owner), stealth_time)
+ return TRUE
+
+/obj/item/clothing/suit/armor/reactive/stealth/proc/end_stealth(mob/living/carbon/human/owner)
+ in_stealth = FALSE
+ animate(owner, alpha = initial(owner.alpha), time = animation_time)
+
+/obj/item/clothing/suit/armor/reactive/stealth/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ if(!isliving(hitby))
+ return FALSE //it just doesn't activate
+ var/mob/living/attacker = hitby
+ owner.visible_message(span_danger("[src] activates, cloaking the wrong person!"))
+ attacker.alpha = 0
+ addtimer(VARSET_CALLBACK(attacker, alpha, initial(attacker.alpha)), 4 SECONDS)
+ return FALSE
//Tesla
@@ -149,6 +227,8 @@
var/tesla_power = 25000
var/tesla_range = 20
var/tesla_flags = TESLA_MOB_DAMAGE | TESLA_OBJ_DAMAGE
+ cooldown_message = span_danger("The tesla capacitors on the reactive tesla armor are still recharging! The armor merely emits some sparks.")
+ emp_message = span_warning("The tesla capacitors beep ominously for a moment.")
/obj/item/clothing/suit/armor/reactive/tesla/dropped(mob/user)
..()
@@ -160,24 +240,24 @@
if(slot_flags & slot) //Was equipped to a valid slot for this item?
user.flags_1 |= TESLA_IGNORE_1
-/obj/item/clothing/suit/armor/reactive/tesla/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- if(!active)
- return FALSE
- if(!damage)
- return FALSE
- if(owner.stat == DEAD)
- return FALSE
- if(prob(hit_reaction_chance))
- if(world.time < reactivearmor_cooldown)
- var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread
- sparks.set_up(1, 1, src)
- sparks.start()
- owner.visible_message(span_danger("The tesla capacitors on [owner]'s reactive tesla armor are still recharging! The armor merely emits some sparks."))
- return
- owner.visible_message(span_danger("[src] blocks [attack_text], sending out arcs of lightning!"))
- tesla_zap(owner, tesla_range, tesla_power, tesla_flags)
- reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
- return TRUE
+/obj/item/clothing/suit/armor/reactive/tesla/cooldown_activation(mob/living/carbon/human/owner)
+ var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread
+ sparks.set_up(1, 1, src)
+ sparks.start()
+ ..()
+
+/obj/item/clothing/suit/armor/reactive/tesla/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ owner.visible_message(span_danger("[src] blocks [attack_text], sending out arcs of lightning!"))
+ tesla_zap(owner, tesla_range, tesla_power, tesla_flags)
+ return TRUE
+
+/obj/item/clothing/suit/armor/reactive/tesla/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ owner.visible_message(span_danger("[src] blocks [attack_text], but pulls a massive charge of energy into [owner] from the surrounding environment!"))
+ if(istype(owner))
+ owner.flags_1 &= ~TESLA_IGNORE_1
+ electrocute_mob(owner, get_area(src), src, 1)
+ owner.flags_1 |= TESLA_IGNORE_1
+ return FALSE
//Repulse
@@ -186,67 +266,117 @@
desc = "An experimental suit of armor that violently throws back attackers."
reactivearmor_cooldown_duration = 5 SECONDS
var/repulse_force = MOVE_FORCE_EXTREMELY_STRONG
+ cooldown_message = span_danger("The repulse generator is still recharging! It fails to generate a strong enough wave!")
+ emp_message = span_warning("The repulse generator is reset to default settings...")
+
+/obj/item/clothing/suit/armor/reactive/repulse/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ playsound(get_turf(owner),'sound/magic/repulse.ogg', 100, 1)
+ owner.visible_message(span_danger("[src] blocks [attack_text], converting the attack into a wave of force!"))
+ var/turf/T = get_turf(owner)
+ var/list/thrown_items = list()
+ for(var/atom/movable/A in orange(7, T))
+ if(A.anchored || thrown_items[A])
+ continue
+ var/throwtarget = get_edge_target_turf(T, get_dir(T, get_step_away(A, T)))
+ A.safe_throw_at(throwtarget, 10, 1, force = repulse_force)
+ thrown_items[A] = A
-/obj/item/clothing/suit/armor/reactive/repulse/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- if(!active)
- return 0
- if(!damage)
- return 0
- if(prob(hit_reaction_chance))
- if(world.time < reactivearmor_cooldown)
- owner.visible_message(span_danger("The repulse generator is still recharging!"))
- return 0
- playsound(get_turf(owner),'sound/magic/repulse.ogg', 100, 1)
- owner.visible_message(span_danger("[src] blocks [attack_text], converting the attack into a wave of force!"))
- var/turf/T = get_turf(owner)
- var/list/thrown_items = list()
- for(var/atom/movable/A in range(T, 7))
- if(A == owner || A.anchored || thrown_items[A])
- continue
- var/throwtarget = get_edge_target_turf(T, get_dir(T, get_step_away(A, T)))
- A.safe_throw_at(throwtarget, 10, 1, force = repulse_force)
- thrown_items[A] = A
-
- reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
- return 1
+ return TRUE
+
+/obj/item/clothing/suit/armor/reactive/repulse/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ playsound(get_turf(owner),'sound/magic/repulse.ogg', 100, 1)
+ owner.visible_message(span_danger("[src] does not block [attack_text], and instead generates an attracting force!"))
+ var/turf/T = get_turf(owner)
+ var/list/thrown_items = list()
+ for(var/atom/movable/A as mob|obj in orange(7, T))
+ if(A.anchored || thrown_items[A])
+ continue
+ A.safe_throw_at(owner, 10, 1, force = repulse_force)
+ thrown_items[A] = A
+
+ return FALSE
+
+//Table
/obj/item/clothing/suit/armor/reactive/table
name = "reactive table armor"
desc = "If you can't beat the memes, embrace them."
var/tele_range = 10
+ cooldown_message = span_danger("The reactive table armor's fabricators are still on cooldown!")
+ emp_message = span_danger("The reactive table armor's fabricators click and whirr ominously for a moment...")
-/obj/item/clothing/suit/armor/reactive/table/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- if(!active)
- return 0
- if(!damage)
- return 0
- if(prob(hit_reaction_chance))
- var/mob/living/carbon/human/H = owner
- if(world.time < reactivearmor_cooldown)
- owner.visible_message(span_danger("The reactive table armor's fabricators are still on cooldown!"))
- return
- owner.visible_message(span_danger("The reactive teleport system flings [H] clear of [attack_text] and slams [H.p_them()] into a fabricated table!"))
- owner.visible_message("[H] GOES ON THE TABLE!!!")
- owner.Paralyze(40)
- var/list/turfs = new/list()
- for(var/turf/T in orange(tele_range, H))
- if(T.density)
- continue
- if(T.x>world.maxx-tele_range || T.xworld.maxy-tele_range || T.y[owner] GOES ON THE TABLE!!!")
+ owner.Paralyze(40)
+ SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "table", /datum/mood_event/table)
+ do_teleport(teleatom = owner, destination = get_turf(owner), no_effects = TRUE, precision = tele_range, channel = TELEPORT_CHANNEL_BLUESPACE)
+ new /obj/structure/table(get_turf(owner))
+ return TRUE
+
+/obj/item/clothing/suit/armor/reactive/table/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ owner.visible_message(span_danger("The reactive teleport system flings [owner] clear of [attack_text] and slams [owner.p_them()] into a fabricated glass table!"))
+ owner.visible_message("[owner] GOES ON THE TABLE!!!")
+ SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "table", /datum/mood_event/table)
+ do_teleport(teleatom = owner, destination = get_turf(owner), no_effects = TRUE, precision = tele_range, channel = TELEPORT_CHANNEL_BLUESPACE)
+ var/obj/structure/table/glass/table = new(get_turf(owner))
+ table.table_shatter(owner)
+ return TRUE
+
+//Hallucinating
+
+/obj/item/clothing/suit/armor/reactive/hallucinating
+ name = "reactive hallucinating armor"
+ desc = "An experimental suit of armor with sensitive detectors hooked up to the mind of the wearer, sending mind pulses that causes hallucinations around you."
+ cooldown_message = span_warning("The connection is currently out of sync... Recalibrating.")
+ emp_message = span_warning("You feel the backsurge of a mind pulse.")
+ var/effect_range = 3
+ clothing_traits = list(TRAIT_MESONS)
+
+/obj/item/clothing/suit/armor/reactive/hallucinating/cooldown_activation(mob/living/carbon/human/owner)
+ var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread
+ sparks.set_up(1, 1, src)
+ sparks.start()
+ ..()
+
+/obj/item/clothing/suit/armor/reactive/hallucinating/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ owner.visible_message(span_danger("[src] blocks [attack_text], sending out mental pulses!"))
+ var/turf/location = get_turf(owner)
+ if(location)
+ hallucination_pulse(location, effect_range, strength = 25)
+ return TRUE
+
+/obj/item/clothing/suit/armor/reactive/hallucinating/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ owner.visible_message(span_danger("[src] blocks [attack_text], but pulls a massive charge of mental energy into [owner] from the surrounding environment!"))
+ owner.adjust_hallucinations(150)
+ return TRUE
+
+//Radiation
+
+/obj/item/clothing/suit/armor/reactive/radiation
+ name = "reactive radiation armor"
+ desc = "An experimental suit of armor thats give the owner radiation proof and on activation releases a wave of radiation around the owner."
+ cooldown_message = span_warning("The connection is currently out of sync... Recalibrating.")
+ emp_message = span_warning("You feel the radiation wave within you.")
+ var/effect_range = 3
+ clothing_traits = list(TRAIT_RADIMMUNE)
+ armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 100, FIRE = 100, ACID = 100)
+ flags_1 = RAD_PROTECT_CONTENTS_1
+
+/obj/item/clothing/suit/armor/reactive/radiation/cooldown_activation(mob/living/carbon/human/owner)
+ var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread
+ sparks.set_up(1, 1, src)
+ sparks.start()
+ ..()
+
+/obj/item/clothing/suit/armor/reactive/radiation/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ owner.visible_message(span_danger("[src] blocks [attack_text], sending out radiation pulses and nuclear particles!"))
+ radiation_pulse(src, 1000, effect_range)
+ for(var/i = 1 to 15)
+ fire_nuclear_particle()
+ return TRUE
+
+/obj/item/clothing/suit/armor/reactive/radiation/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ owner.visible_message(span_danger("[src] blocks [attack_text], but pulls a massive charge of radiation wave into [owner] from the surrounding environment!"))
+ owner.adjustToxLoss(10)
+ return TRUE
diff --git a/code/modules/events/anomaly.dm b/code/modules/events/anomaly.dm
index 558ddbb6a220..abb1aaca83a6 100644
--- a/code/modules/events/anomaly.dm
+++ b/code/modules/events/anomaly.dm
@@ -16,17 +16,10 @@
var/static/list/allowed_areas
if(!allowed_areas)
//Places that shouldn't explode
- var/list/safe_area_types = typecacheof(list(
- /area/ai_monitored/turret_protected/ai,
- /area/ai_monitored/turret_protected/ai_upload,
- /area/engine,
- /area/solar,
- /area/holodeck,
- /area/shuttle)
- )
+ var/list/safe_area_types = typecacheof(ANOMALY_AREA_BLACKLIST)
//Subtypes from the above that actually should explode.
- var/list/unsafe_area_subtypes = typecacheof(list(/area/engine/break_room))
+ var/list/unsafe_area_subtypes = typecacheof(ANOMALY_AREA_SUBTYPE_WHITELIST)
allowed_areas = make_associative(GLOB.the_station_areas) - safe_area_types + unsafe_area_subtypes
@@ -45,6 +38,10 @@
/datum/round_event/anomaly/start()
var/turf/T = pick(get_area_turfs(impact_area))
+ var/max_rolls=0 //In case all the turfs in the area are walls, will break out of rolling for a new turf forever and will just spawn the anomaly inside a wall
+ while(is_anchored_dense_turf(T) && max_rolls<15) //Will roll for a new turf if the selected turf is a wall until it's not a wall
+ T = safepick(get_area_turfs(impact_area))
+ max_rolls++
var/newAnomaly
if(T)
newAnomaly = new anomaly_path(T)
diff --git a/code/modules/events/anomaly_flux.dm b/code/modules/events/anomaly_flux.dm
index 4737ad9bb416..4ecd8a57e6d6 100644
--- a/code/modules/events/anomaly_flux.dm
+++ b/code/modules/events/anomaly_flux.dm
@@ -9,7 +9,7 @@
/datum/round_event/anomaly/anomaly_flux
startWhen = 10
announceWhen = 3
- anomaly_path = /obj/effect/anomaly/flux
+ anomaly_path = /obj/effect/anomaly/flux/explosion
/datum/round_event/anomaly/anomaly_flux/announce(fake)
priority_announce("Localized hyper-energetic flux wave detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")
diff --git a/code/modules/events/anomaly_hallucination.dm b/code/modules/events/anomaly_hallucination.dm
new file mode 100644
index 000000000000..0f3628c7e0b8
--- /dev/null
+++ b/code/modules/events/anomaly_hallucination.dm
@@ -0,0 +1,15 @@
+/datum/round_event_control/anomaly/anomaly_hallucination
+ name = "Anomaly: Hallucination"
+ typepath = /datum/round_event/anomaly/anomaly_hallucination
+
+ min_players = 10
+ max_occurrences = 5
+ weight = 20
+
+/datum/round_event/anomaly/anomaly_hallucination
+ startWhen = 10
+ announceWhen = 3
+ anomaly_path = /obj/effect/anomaly/hallucination
+
+/datum/round_event/anomaly/anomaly_hallucination/announce(fake)
+ priority_announce("Hallucinatory event hitting the station. Expected location: [impact_area.name].", "Anomaly Alert")
diff --git a/code/modules/events/anomaly_radiation.dm b/code/modules/events/anomaly_radiation.dm
index aa105573cf5e..e5d631b17b98 100644
--- a/code/modules/events/anomaly_radiation.dm
+++ b/code/modules/events/anomaly_radiation.dm
@@ -12,6 +12,6 @@
/datum/round_event/anomaly/anomaly_radiation/announce(fake)
if(prob(90))
- priority_announce("Radioactive anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")
+ priority_announce("Large radiation pulse detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")
else
- print_command_report("Radioactive anomaly detected on long range scanners. Expected location: [impact_area.name].", "Radioactive anomaly")
+ print_command_report("Large radiation pulse detected on long range scanners. Expected location: [impact_area.name].", "Radioactive anomaly")
diff --git a/code/modules/events/portal_storm.dm b/code/modules/events/portal_storm.dm
index 86c2095e4c48..b59f85d41392 100644
--- a/code/modules/events/portal_storm.dm
+++ b/code/modules/events/portal_storm.dm
@@ -131,17 +131,51 @@
/datum/round_event/portal_storm/resonance_cascade
boss_types = list(
- /mob/living/simple_animal/hostile/asteroid/goliath/beast = 12,
- /mob/living/simple_animal/hostile/asteroid/marrowweaver = 12
+ /mob/living/simple_animal/hostile/megafauna/bubblegum = 1,
+ /mob/living/simple_animal/hostile/megafauna/dragon = 1,
+ /mob/living/simple_animal/hostile/megafauna/stalwart = 1,
+ /mob/living/simple_animal/hostile/megafauna/colossus = 1,
+ /mob/living/simple_animal/hostile/megafauna/hierophant = 1,
+ /mob/living/simple_animal/hostile/megafauna/legion = 1,
+ /mob/living/simple_animal/hostile/retaliate/goat/king = 1,
+ /mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon = 4,
+ /mob/living/simple_animal/hostile/megafauna/blood_drunk_miner = 6,
+ /mob/living/simple_animal/hostile/megafauna/demonic_frost_miner = 6,
+ /mob/living/simple_animal/hostile/asteroid/elite/broodmother = 5,
+ /mob/living/simple_animal/hostile/asteroid/elite/herald = 5,
+ /mob/living/simple_animal/hostile/asteroid/elite/legionnaire = 5,
+ /mob/living/simple_animal/hostile/asteroid/elite/pandora = 5,
+ /mob/living/simple_animal/hostile/space_dragon = 5
)
hostile_types = list(
- /mob/living/simple_animal/hostile/asteroid/hivelord/legion/tendril = 24,
- /mob/living/simple_animal/hostile/asteroid/basilisk/watcher = 24,
+ /mob/living/simple_animal/hostile/asteroid/hivelord/legion/tendril = 25,
+ /mob/living/simple_animal/hostile/asteroid/basilisk/watcher = 25,
+ /mob/living/simple_animal/hostile/asteroid/goliath/beast = 25,
+ /mob/living/simple_animal/hostile/asteroid/marrowweaver = 25
+ )
+ var/list/anomaly_types = list(
+ ANOMALY_FLUX_EXPLOSIVE = 150,
+ ANOMALY_RADIATION = 130,
+ ANOMALY_RADIATION_X = 5,
+ ANOMALY_VORTEX = 130,
+ ANOMALY_PYRO = 140,
+ ANOMALY_HALLUCINATION = 140,
+ ANOMALY_GRAVITATIONAL = 160,
)
endWhen = INFINITY // keep going until it's done
/datum/round_event/portal_storm/resonance_cascade/start()
. = ..()
+ var/list/nether_areas = GLOB.generic_event_spawns
+ for(var/i in 1 to 50)
+ var/area/target_event_spawn = pick_n_take(nether_areas)
+ if(!target_event_spawn)
+ return
+
+ var/obj/structure/spawner/nether/doom = new(target_event_spawn.loc)
+ doom.max_integrity = 250
+ doom.spawn_time = 20 SECONDS
+
for(var/obj/machinery/power/apc/A in GLOB.apcs_list)
if(!is_station_level(A.z))
continue
@@ -156,12 +190,32 @@
else
A.visible_message(span_userdanger("[A] overloads and makes a huge arc!"))
tesla_zap(A, 5, 10000) // woe
- SSshuttle.emergency.request(null) // can't call the shuttle if all the APCs blew up, so give the crew some help
+ message_centcom("Alert, a large scale of abnormal activity has been detected on [station_name()]. Investigate and send the special forces to the station immediately.", "Central Command Higher Dimensional Affairs")
+ priority_announce("Unknown anomalous portals detected on a large scale of the station. There is no additional data.", "Central Command Higher Dimensional Affairs", ANNOUNCER_SPANOMALIES)
+ addtimer(CALLBACK(src, PROC_REF(call_shuttle)), 4 SECONDS) //Wait till the annoucement finishes till the the next one so the sounds dont overlap each other
+
+/datum/round_event/portal_storm/resonance_cascade/proc/call_shuttle()
+ SSshuttle.emergency.request(null, reason = "Shuttle has been automatically called due to the event, standing by.") // can't call the shuttle if all the APCs blew up, so give the crew some help
/datum/round_event/portal_storm/resonance_cascade/announce(fake)
if(fake) // no point in trying to fake it, has much more impact if it's only the real thing
return
- priority_announce(readable_corrupted_text("Massive energy surge detected on [station_name()]. Immediate evacuation is recommended."), sound='sound/misc/airraid.ogg')
+ priority_announce("Attention all personnel, this is an emergency announcement on [station_name()]. \
+ An evacuation is immediately underway due to abnormal hostile activity detected on the premises. \
+ A distress signal has been sent to Central Command to alert them of the situation. In addition to that, \
+ we have observed a substantial number of meteors approaching the station on a large scale. \
+ Please remain calm and follow the evacuation procedures provided. \
+ Proceed to the designated evacuation points swiftly and orderly, To ensure your safety, \
+ please avoid areas with abnormal activity and refrain from going outside the station to minimize the risk of collisions with meteors. \
+ Security personnel are present to assist and ensure your safety. \
+ Cooperate with their instructions and refrain from engaging with any hostiles. \
+ Central Command is actively responding and coordinating a comprehensive emergency response. \
+ Your safety is our utmost priority during this evacuation. \
+ Stay vigilant, report any suspicious activity, and await further instructions at the designated evacuation points. \
+ Assistance is on the way.",
+ title = "Central Command Higher Dimensional Affairs",
+ sound = 'sound/misc/airraid.ogg',
+ )
/datum/round_event/portal_storm/resonance_cascade/tick()
var/turf/T = get_safe_random_station_turf()
@@ -180,4 +234,10 @@
if(!boss_types[type])
boss_types -= type
+ var/anomaly = pick(anomaly_types)
+ anomaly_types[anomaly] = anomaly_types[anomaly] - 1
+ supermatter_anomaly_gen(T, anomaly, rand(5, 10), has_weak_lifespan = TRUE)
+ if(!anomaly_types[anomaly])
+ anomaly_types -= anomaly
+
time_to_end()
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 5e7771f117e5..bf69491e297b 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -380,7 +380,7 @@
var/obj/item/organ/O = X
O.emp_act(severity)
-/mob/living/carbon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, override = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
+/mob/living/carbon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, override = 0, tesla_shock = 0, illusion = 0, stun = TRUE, gib = FALSE)
if(tesla_shock && (flags_1 & TESLA_IGNORE_1))
return FALSE
if(HAS_TRAIT(src, TRAIT_SHOCKIMMUNE))
@@ -418,7 +418,14 @@
revive()
INVOKE_ASYNC(src, PROC_REF(emote), "gasp")
adjust_jitter(10 SECONDS)
- adjustOrganLoss(ORGAN_SLOT_BRAIN, 100, 199) //yogs end
+ adjustOrganLoss(ORGAN_SLOT_BRAIN, 100, 199)
+ if(gib && siemens_coeff > 0)
+ visible_message(
+ span_danger("[src] body is emitting a loud noise!"), \
+ span_userdanger("You feel like you are about to explode!"), \
+ span_italics("You hear a loud noise!"), \
+ )
+ addtimer(CALLBACK(src, PROC_REF(supermatter_tesla_gib)), 4 SECONDS) //yogs end
if(override)
return override
else
diff --git a/code/modules/mob/living/carbon/death.dm b/code/modules/mob/living/carbon/death.dm
index 31ff51729fd7..ed7a8ed12094 100644
--- a/code/modules/mob/living/carbon/death.dm
+++ b/code/modules/mob/living/carbon/death.dm
@@ -48,7 +48,7 @@
if(org_zone == BODY_ZONE_CHEST)
O.Remove(src)
O.forceMove(Tsec)
- O.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
+ O.throw_at(get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 5)
else
for(var/X in internal_organs)
var/obj/item/organ/I = X
@@ -60,18 +60,29 @@
continue
I.Remove(src)
I.forceMove(Tsec)
- I.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
+ I.throw_at(get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 5)
if(!no_brain && !no_organs)//drop other heads/brains carried if your own would be dropped
for(var/X in src.get_all_contents())
if(istype(X, /obj/item/organ/brain) || istype(X, /obj/item/bodypart/head))
var/obj/item/H = X
if(H)
H.forceMove(Tsec)
- H.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
+ H.throw_at(get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 5)
/mob/living/carbon/spread_bodyparts()
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
BP.drop_limb()
- BP.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
+ BP.throw_at(get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 5)
+
+/mob/living/carbon/proc/supermatter_tesla_gib() //leave chest behind and vital organs
+ for(var/obj/item/carbon_contents in src)
+ dropItemToGround(carbon_contents)
+ if(prob(50))
+ carbon_contents.throw_at(get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 5)
+ adjustFireLoss(1000)
+ ADD_TRAIT(src, TRAIT_DISFIGURED, TRAIT_GENERIC)
+ spill_organs()
+ spread_bodyparts()
+ spawn_gibs()
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index ce5c35340e5f..21eff3f12126 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -275,7 +275,7 @@
return 1
/mob/living/carbon/human/attack_alien(mob/living/carbon/alien/humanoid/M)
- if(check_shields(M, 0, "the M.name"))
+ if(check_shields(M, 0, "the [M.name]"))
visible_message(span_danger("[M] attempted to touch [src]!"))
return 0
@@ -500,7 +500,7 @@
//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 = 0, override = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
+/mob/living/carbon/human/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, override = 0, tesla_shock = 0, illusion = 0, stun = TRUE, gib = FALSE)
if(tesla_shock)
var/total_coeff = 1
if(gloves)
@@ -537,7 +537,7 @@
siemens_coeff *= physiology.siemens_coeff
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)
+ . = ..(shock_damage,source,siemens_coeff,safety,override,tesla_shock, illusion, stun, gib)
if(.)
electrocution_animation(40)
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index ee9f775a52af..71280ac679c4 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -360,7 +360,7 @@
take_bodypart_damage(acidpwr * min(1, acid_volume * 0.1))
return 1
-/mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
+/mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE, gib = FALSE)
SEND_SIGNAL(src, COMSIG_LIVING_ELECTROCUTE_ACT, shock_damage)
if(tesla_shock && (flags_1 & TESLA_IGNORE_1))
return FALSE
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 22bb114f4910..d873e563013f 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -725,8 +725,8 @@
add_overlay(head_overlay)
update_fire()
-/mob/living/silicon/robot/proc/self_destruct()
- if(emagged || module.syndicate_module)
+/mob/living/silicon/robot/proc/self_destruct(explode_override = FALSE)
+ if(emagged || module.syndicate_module || explode_override)
if(mmi)
qdel(mmi)
explosion(src.loc,1,2,4,flame_range = 2)
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index fc08a0dba7b2..80e710a1518e 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -200,3 +200,12 @@
updatehealth()
if(prob(75) && Proj.damage > 0)
spark_system.start()
+
+/mob/living/silicon/robot/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE, gib = FALSE)
+ if(gib)
+ visible_message(
+ span_danger("[src] begins to heat up!"), \
+ span_userdanger("You begin to heat up!"), \
+ )
+ addtimer(CALLBACK(src, PROC_REF(self_destruct), TRUE), 4 SECONDS)
+ return ..()
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index 52737a74543b..3942d298949d 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -97,11 +97,11 @@
return
return ..()
-/mob/living/silicon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
+/mob/living/silicon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE, gib = FALSE)
if(buckled_mobs)
for(var/mob/living/M in buckled_mobs)
unbuckle_mob(M)
- M.electrocute_act(shock_damage/100, source, siemens_coeff, safety, tesla_shock, illusion, stun) //Hard metal shell conducts!
+ M.electrocute_act(shock_damage/100, source, siemens_coeff, safety, tesla_shock, illusion, stun, gib) //Hard metal shell conducts!
return 0 //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/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm
index 7378886bf841..148022aec96e 100644
--- a/code/modules/mob/living/simple_animal/animal_defense.dm
+++ b/code/modules/mob/living/simple_animal/animal_defense.dm
@@ -179,3 +179,8 @@
else
visual_effect_icon = ATTACK_EFFECT_SMASH
..()
+
+/mob/living/simple_animal/electrocute_act(shock_damage, obj/source, siemens_coeff, safety, tesla_shock, illusion, stun, gib)
+ if(gib)
+ gib()
+ return ..()
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index 22d332c7946d..5334e54e44d1 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -112,7 +112,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 = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
+/mob/living/simple_animal/hostile/construct/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE, gib = FALSE)
return 0
/mob/living/simple_animal/hostile/construct/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
index 45c2791364f6..fd63fa71afb3 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
@@ -288,5 +288,5 @@
// Why would bees pay attention to drones?
return 1
-/mob/living/simple_animal/drone/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
+/mob/living/simple_animal/drone/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE, gib = FALSE)
return 0 //So they don't die trying to fix wiring
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
index 5b39274ba275..e736be830e23 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
@@ -70,14 +70,6 @@ Difficulty: Hard
small_sprite_type = /datum/action/small_sprite/megafauna/bubblegum
music_component = /datum/component/music_player/battle
music_path = /datum/music/sourced/battle/bubblegum
-
-/mob/living/simple_animal/hostile/megafauna/bubblegum/Initialize(mapload)
- . = ..()
- if(true_spawn)
- for(var/mob/living/simple_animal/hostile/megafauna/bubblegum/B in GLOB.mob_living_list)
- if(B != src)
- return INITIALIZE_HINT_QDEL //There can be only one
-
/datum/action/innate/megafauna_attack/triple_charge
name = "Triple Charge"
button_icon = 'icons/mob/actions/actions_items.dmi'
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index ef18ec4231a3..0c7ab0eaa9c2 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -38,7 +38,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
if(prob(20))
set_broken()
-/obj/machinery/gravity_generator/tesla_act(power, tesla_flags)
+/obj/machinery/gravity_generator/tesla_act(power, tesla_flags, shocked_targets, zap_gib = FALSE)
..()
if(tesla_flags & TESLA_MACHINE_EXPLOSIVE)
qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index f067bf88904e..61580719f0be 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -786,7 +786,7 @@
on = TRUE && !forced_off
update()
-/obj/machinery/light/tesla_act(power, tesla_flags)
+/obj/machinery/light/tesla_act(power, tesla_flags, shocked_targets, zap_gib = FALSE)
if(tesla_flags & TESLA_MACHINE_EXPLOSIVE)
explosion(src,0,0,0,flame_range = 5, adminlog = 0)
qdel(src)
diff --git a/code/modules/power/rtg.dm b/code/modules/power/rtg.dm
index 580bcb142e8a..c0d58a290d9f 100644
--- a/code/modules/power/rtg.dm
+++ b/code/modules/power/rtg.dm
@@ -96,6 +96,6 @@
/obj/machinery/power/rtg/abductor/fire_act(exposed_temperature, exposed_volume)
overload()
-/obj/machinery/power/rtg/abductor/tesla_act()
+/obj/machinery/power/rtg/abductor/tesla_act(power, tesla_flags, shocked_targets, zap_gib = FALSE)
..() //extend the zap
overload()
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index 262e24993d01..3689d4eb9d73 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -303,6 +303,9 @@
current_size = STAGE_SIX
icon = 'icons/effects/352x352.dmi'
icon_state = "singularity_s11"
+ desc = "[initial(desc)] It glows fiercely with inner fire."
+ name = "supermatter-charged [initial(name)]"
+ set_light(10)
pixel_x = -160
pixel_y = -160
grav_pull = 15
@@ -371,10 +374,7 @@
var/gain = A.singularity_act(current_size, src)
src.energy += gain
if(istype(A, /obj/machinery/power/supermatter_crystal) && !consumedSupermatter)
- desc = "[initial(desc)] It glows fiercely with inner fire."
- name = "supermatter-charged [initial(name)]"
consumedSupermatter = 1
- set_light(10)
return
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index ed6de19c7df6..3e658011ab0e 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -63,8 +63,6 @@
#define DAMAGE_HARDCAP 0.002
#define DAMAGE_INCREASE_MULTIPLIER 0.25
-#define MIASMA_DELAM_PERCENTAGE 50 ///The percentage of miasma before it is classed as a miasma delamination, causing a blob to spawn.
-
#define THERMAL_RELEASE_MODIFIER 5 //Higher == less heat released during reaction, not to be confused with the above values
#define PLASMA_RELEASE_MODIFIER 750 //Higher == less plasma released by reaction
#define OXYGEN_RELEASE_MODIFIER 325 //Higher == less oxygen released at high temperature/power
@@ -82,11 +80,6 @@
#define HALLUCINATION_RANGE(P) (min(7, round(P ** 0.25)))
-#define GRAVITATIONAL_ANOMALY "gravitational_anomaly"
-#define FLUX_ANOMALY "flux_anomaly"
-#define PYRO_ANOMALY "pyro_anomaly"
-#define RADIATION_ANOMALY "radiation_anomaly"
-
//If integrity percent remaining is less than these values, the monitor sets off the relevant alarm.
#define SUPERMATTER_DELAM_PERCENT 5
#define SUPERMATTER_EMERGENCY_PERCENT 25
@@ -165,6 +158,9 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
var/resonance_cascading = FALSE // IT'S NOT SHUTTING DOWN!!!!!!!
var/noblium_suppressed = FALSE // or is it?
+ // Blob related shit
+ var/supermatter_blob = FALSE // Well say sike rn
+
// Radio related variables
var/obj/item/radio/radio
var/radio_key = /obj/item/encryptionkey/headset_eng
@@ -223,7 +219,6 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
var/damage_mod = 1
var/heal_mod = 1
-
/obj/machinery/power/supermatter_crystal/Initialize(mapload)
. = ..()
uid = gl_uid++
@@ -254,9 +249,9 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
/obj/machinery/power/supermatter_crystal/examine(mob/user)
. = ..()
- if (istype(user, /mob/living/carbon))
- if ((!HAS_TRAIT(user, TRAIT_MESONS)) && (get_dist(user, src) < HALLUCINATION_RANGE(power)))
- . += span_danger("You get headaches just from looking at it.")
+ var/immune = HAS_TRAIT(user, TRAIT_MESONS) || HAS_TRAIT(user.mind, TRAIT_MESONS)
+ if (isliving(user) && !immune && (get_dist(user, src) < HALLUCINATION_RANGE(power)))
+ . += span_danger("You get headaches just from looking at it.")
/obj/machinery/power/supermatter_crystal/proc/get_status()
var/turf/T = get_turf(src)
@@ -319,7 +314,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
var/speaking
if(corruptor_attached)
- speaking = "SUPERMATTER CRITICAL FAILURE ENGAGING FAILSAFE" //technically the failsafe is fail-danger, but whatever.
+ speaking = "SUPERMATTER CRITICAL FAILURE ENGAGING FAILSAFE." //technically the failsafe is fail-danger, but whatever.
else
speaking = "[emergency_alert] The supermatter has reached critical integrity failure. Emergency causality destabilization field has been activated."
radio.talk_into(src, speaking, common_channel, language = get_selected_language())
@@ -348,8 +343,11 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
if(i == 10 SECONDS)
playsound(src, 'yogstation/sound/voice/sm/fcitadel_10sectosingularity.ogg', 100, FALSE, 100, pressure_affected=FALSE)
if(antinoblium_attached && !resonance_cascading) // yogs- resonance cascade!
+ priority_announce("RESONANCE CASCADE IMMINENT.", "Anomaly Alert", 'sound/misc/notice1.ogg')
resonance_cascading = TRUE
sound_to_playing_players('sound/magic/lightning_chargeup.ogg', 50, FALSE) // yogs end
+ if(supermatter_blob)
+ priority_announce("LEVEL 5 BIOHAZARD OUTBREAK IMMINENT.", "Anomaly Alert", 'sound/misc/notice1.ogg')
else
if(corruptor_attached)
speaking = "[round(i*0.1*rand(),1)]..."
@@ -361,85 +359,24 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
radio.talk_into(src, speaking, common_channel)
sleep(1 SECONDS)
- explode()
+ delamination_event()
-/obj/machinery/power/supermatter_crystal/proc/explode()
+/obj/machinery/power/supermatter_crystal/proc/delamination_event()
if (is_main_engine)
SSpersistence.rounds_since_engine_exploded = ROUNDCOUNT_ENGINE_JUST_EXPLODED
for (var/obj/structure/sign/delamination_counter/sign as anything in GLOB.map_delamination_counters)
sign.update_count(ROUNDCOUNT_ENGINE_JUST_EXPLODED)
- for(var/mob in GLOB.alive_mob_list)
- var/mob/living/M = mob
- var/turf/T2 = get_turf_global(M)
- if(istype(M) && T2 && T2.z == z)
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- H.adjust_hallucinations(max(50, min(300, DETONATION_HALLUCINATION * sqrt(1 / (get_dist(M, src) + 1)) ) ) )
- var/rads = DETONATION_RADS * sqrt( 1 / (get_dist(M, src) + 1) )
- M.rad_act(rads)
+ new /datum/supermatter_delamination(power, combined_gas, get_turf(src), explosion_power, gasmix_power_ratio, antinoblium_attached, resonance_cascading, last_rads, supermatter_blob)
- var/turf/T = get_turf(src)
- for(var/_M in GLOB.player_list)
- var/mob/M = _M
- var/turf/T2 = get_turf(M)
- if(T2.z == z)
- SEND_SOUND(M, 'sound/magic/charge.ogg')
- to_chat(M, span_boldannounce("You feel reality distort for a moment..."))
- SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "delam", /datum/mood_event/delam)
-
- if (T)
- var/datum/gas_mixture/air = T.return_air()
- if (air)
- if (((air.get_moles(/datum/gas/miasma) / air.total_moles()) * 100) > MIASMA_DELAM_PERCENTAGE && air.total_moles() > 5000)
- var/list/candidates = pollGhostCandidates("Do you wish to be considered for the special role of Supermatter Blob?", ROLE_BLOB, null, ROLE_BLOB)
- if(candidates.len)
- var/mob/dead/observer/new_blob = pick(candidates)
- var/mob/camera/blob/BC = new_blob.become_overmind(350, 1.5, 1)
- BC.forceMove(T)
- BC.place_blob_core(BLOB_FORCE_PLACEMENT)
-
- message_admins("[src] has created a blob. [ADMIN_JMP(src)].")
- investigate_log("has created a blob.", INVESTIGATE_SUPERMATTER)
- qdel(src)
- return
-
- if(resonance_cascading)
- sound_to_playing_players('sound/magic/lightningbolt.ogg', volume = 50)
- var/datum/round_event_control/resonance_cascade/xen = new
- xen.runEvent()
- message_admins("[src] has caused a resonance cascade.")
- investigate_log("has caused a resonance cascade.", INVESTIGATE_SUPERMATTER)
-
- if(combined_gas > MOLE_PENALTY_THRESHOLD && !resonance_cascading)
- message_admins("[src] has collapsed into a singularity. [ADMIN_JMP(src)].")
- investigate_log("has collapsed into a singularity.", INVESTIGATE_SUPERMATTER)
- if(T)
- var/obj/singularity/S = new(T)
- if(antinoblium_attached)
- S.energy = 2400
- else
- S.energy = 800
- S.consume(src)
- else
- if(power < 0) // in case of negative energy, make it positive
- power = -power
- var/explosion_mod = clamp((1.001**power) / ((1.001**power) + SUPERMATTER_EXPLOSION_LAMBDA), 0.1, 1)
- //trying to cheat by spacing the crystal? YOU FOOL THERE ARE NO LOOPHOLES TO ESCAPE YOUR UPCOMING DEATH
- if(istype(T, /turf/open/space) || combined_gas < MOLE_SPACE_THRESHOLD)
- message_admins("[src] has exploded in empty space.")
- investigate_log("has exploded in empty space.", INVESTIGATE_SUPERMATTER)
- explosion_mod = max(explosion_mod, 0.5)
- else
- message_admins("[src] has exploded")
- investigate_log("has exploded.", INVESTIGATE_SUPERMATTER)
- INVOKE_ASYNC(GLOBAL_PROC, /proc/empulse, T, explosion_power * explosion_mod, (explosion_power * explosion_mod * 2) + (explosion_power/4), TRUE, FALSE, FALSE, TRUE)
- explosion(T, explosion_power * explosion_mod * 0.5, explosion_power * explosion_mod + 2, explosion_power * explosion_mod + 4, explosion_power * explosion_mod + 6, 1, 1)
- radiation_pulse(T, (last_rads + 2400) * explosion_power)
- if(power > POWER_PENALTY_THRESHOLD && !resonance_cascading)
- investigate_log("has spawned additional energy balls.", INVESTIGATE_SUPERMATTER)
- var/obj/singularity/energy_ball/E = new(T)
- E.energy = power
- qdel(src)
+ qdel(src)
+
+/obj/machinery/power/supermatter_crystal/proc/pulsewave()
+ var/atom/movable/gravity_lens/shockwave = new(get_turf(src))
+ shockwave.transform = matrix().Scale(0.5)
+ shockwave.pixel_x = -240
+ shockwave.pixel_y = -240
+ animate(shockwave, alpha = 0, transform = matrix().Scale(20), time = 10 SECONDS, easing = QUAD_EASING)
+ QDEL_IN(shockwave, 10.5 SECONDS)
/obj/machinery/power/supermatter_crystal/proc/surge(amount)
surging = amount
@@ -536,6 +473,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
var/nobliumcomp = max(removed.get_moles(/datum/gas/hypernoblium)/combined_gas, 0)
var/antinobliumcomp = max(removed.get_moles(/datum/gas/antinoblium)/combined_gas, 0)
var/nitriumcomp = max(removed.get_moles(/datum/gas/nitrium)/combined_gas, 0)
+ var/miasmacomp = max(removed.get_moles(/datum/gas/miasma)/combined_gas, 0)
if (healcomp >= 0.1)
heal_mod = (healcomp * HEALIUM_HEAL_MOD) + 1 //Increases healing and healing cap
@@ -551,6 +489,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
var/bzmol = max(removed.get_moles(/datum/gas/bz), 0)
var/nitriummol = max(removed.get_moles(/datum/gas/nitrium), 0)
var/antinobmol = max(removed.get_moles(/datum/gas/antinoblium), 0)
+ var/miasmol = max(removed.get_moles(/datum/gas/miasma), 0)
// Power of the gas. Scale of 0 to 1
gasmix_power_ratio = clamp(plasmacomp + o2comp + co2comp + tritiumcomp + bzcomp + nitriumcomp + antinobliumcomp - pluoxiumcomp - n2comp, 0, 1)
@@ -568,7 +507,9 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
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)
- powerloss_inhibitor = clamp(1-(powerloss_dynamic_scaling * clamp(combined_gas/POWERLOSS_INHIBITION_MOLE_BOOST_THRESHOLD,1 ,1.5)),0 ,1)
+
+ if(support_integrity >= 10 && !supermatter_blob)
+ powerloss_inhibitor = clamp(1-(powerloss_dynamic_scaling * clamp(combined_gas/POWERLOSS_INHIBITION_MOLE_BOOST_THRESHOLD,1 ,1.5)),0 ,1)
if(matter_power)
var/removed_matter = max(matter_power/MATTER_POWER_CONVERSION, 40)
@@ -613,16 +554,23 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
message_admins("[src] has reached criticial antinoblium concentration and started a resonance cascade.")
antinoblium_attached = TRUE // oh god oh fuck
+ if (miasmacomp >= 0.5 && miasmol > 500 && !supermatter_blob) //requires around 4500 mol of miasma for the blob
+ supermatter_blob = TRUE // you are fucked
+
// adding enough hypernoblium can save it, but only if it hasn't gotten too bad and it wasn't corrupted using the traitor kit
if(nobliumcomp >= 0.5 && antinoblium_attached && !corruptor_attached && support_integrity > 10 && damage <= damage_archived)
support_integrity += 2
if(support_integrity >= 100)
support_integrity = 100
antinoblium_attached = FALSE
+ radio.use_command = FALSE
noblium_suppressed = TRUE
else
noblium_suppressed = FALSE
+ if((supermatter_blob || antinoblium_attached) && !radio.use_command)
+ radio.use_command = TRUE
+
var/device_energy = power * REACTION_POWER_MODIFIER
//To figure out how much temperature to add each tick, consider that at one atmosphere's worth
@@ -638,7 +586,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
//Calculate how much gas to release, antinoblium seeded SM produces much more gas
- if(antinoblium_attached)
+ if(antinoblium_attached || supermatter_blob)
removed.adjust_moles(/datum/gas/plasma, max(((device_energy * dynamic_heat_modifier) / PLASMA_RELEASE_MODIFIER) * (1+(100-support_integrity)/25), 0))
removed.adjust_moles(/datum/gas/oxygen, max((((device_energy + removed.return_temperature() * dynamic_heat_modifier) - T0C) / OXYGEN_RELEASE_MODIFIER) * (1+(100-support_integrity)/25), 0))
else if(haloncomp >= 0.15)
@@ -676,16 +624,18 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
playsound(src.loc, 'sound/weapons/emitter2.ogg', 100, 1, extrarange = 10)
supermatter_zap(src, 5, clamp(power*2, 4000, 20000))
- if(prob(15) && (power > POWER_PENALTY_THRESHOLD || combined_gas > MOLE_PENALTY_THRESHOLD || antinoblium_attached))
+ if(prob(15) && (power > POWER_PENALTY_THRESHOLD || combined_gas > MOLE_PENALTY_THRESHOLD || antinoblium_attached || supermatter_blob))
supermatter_pull(src, power/750)
- if(prob(5))
- supermatter_anomaly_gen(src, FLUX_ANOMALY, rand(5, 10))
- if(power > SEVERE_POWER_PENALTY_THRESHOLD && prob(5) || prob(1))
- supermatter_anomaly_gen(src, GRAVITATIONAL_ANOMALY, rand(5, 10))
- if(power > SEVERE_POWER_PENALTY_THRESHOLD && prob(2) || prob(0.3) && power > POWER_PENALTY_THRESHOLD)
- supermatter_anomaly_gen(src, PYRO_ANOMALY, rand(5, 10))
- if(power > SEVERE_POWER_PENALTY_THRESHOLD && prob(3) || prob(0.5))
- supermatter_anomaly_gen(src, RADIATION_ANOMALY, rand(5, 10))
+ if(prob(5) || (antinoblium_attached || supermatter_blob) && prob(10))
+ supermatter_anomaly_gen(src, ANOMALY_FLUX, rand(5, 10))
+ if(prob(5) || (antinoblium_attached || supermatter_blob) && prob(10))
+ supermatter_anomaly_gen(src, ANOMALY_HALLUCINATION, rand(5, 10))
+ if(power > SEVERE_POWER_PENALTY_THRESHOLD && prob(5) || prob(1) || (antinoblium_attached || supermatter_blob) && prob(10))
+ supermatter_anomaly_gen(src, ANOMALY_GRAVITATIONAL, rand(5, 10))
+ if(power > SEVERE_POWER_PENALTY_THRESHOLD && prob(2) || prob(0.3) && power > POWER_PENALTY_THRESHOLD || (antinoblium_attached || supermatter_blob) && prob(10))
+ supermatter_anomaly_gen(src, ANOMALY_PYRO, rand(5, 10))
+ if(power > SEVERE_POWER_PENALTY_THRESHOLD && prob(5) || prob(0.5) || (antinoblium_attached || supermatter_blob) && prob(10))
+ supermatter_anomaly_gen(src, ANOMALY_RADIATION, rand(5, 10))
if(damage > warning_point) // while the core is still damaged and it's still worth noting its status
if(damage_archived < warning_point) //If damage_archive is under the warning point, this is the very first cycle that we've reached said point.
@@ -697,7 +647,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
if(corruptor_attached)
radio.talk_into(src, "[warning_alert] Integrity: [get_fake_integrity()]%!", common_channel)
else
- radio.talk_into(src, "[emergency_alert] Integrity: [get_integrity()]%", common_channel)
+ radio.talk_into(src, "[emergency_alert] Integrity: [get_integrity()]%.", common_channel)
SEND_SIGNAL(src, COMSIG_SUPERMATTER_DELAM_ALARM)
log_game("The supermatter crystal: [emergency_alert] Integrity: [get_integrity()]%") // yogs start - Logs SM chatter
investigate_log("The supermatter crystal: [emergency_alert] Integrity: [get_integrity()]%", INVESTIGATE_SUPERMATTER) // yogs end
@@ -710,7 +660,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
if(corruptor_attached)
radio.talk_into(src, "[warning_alert] Integrity: [get_fake_integrity()]%!", engineering_channel)
else
- radio.talk_into(src, "[warning_alert] Integrity: [get_integrity()]%", engineering_channel)
+ radio.talk_into(src, "[warning_alert] Integrity: [get_integrity()]%.", engineering_channel)
SEND_SIGNAL(src, COMSIG_SUPERMATTER_DELAM_ALARM)
log_game("The supermatter crystal: [warning_alert] Integrity: [get_integrity()]%") // yogs start - Logs SM chatter
investigate_log("The supermatter crystal: [warning_alert] Integrity: [get_integrity()]%", INVESTIGATE_SUPERMATTER) // yogs end
@@ -737,6 +687,15 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
radio.talk_into(src, "Warning: Critical coolant mass reached.", engineering_channel)
log_game("The supermatter crystal: Warning: Critical coolant mass reached.") // yogs start - Logs SM chatter
investigate_log("The supermatter crystal: Warning: Critical coolant mass reached.", INVESTIGATE_SUPERMATTER) // yogs end
+
+ if(supermatter_blob)
+ radio.talk_into(src, "DANGER: BIOHAZARD DETECTED, VACATE THE CHAMBER IMMEDIATELY.", engineering_channel)
+ log_game("The supermatter crystal: DANGER: BIOHAZARD DETECTED, VACATE THE CHAMBER IMMEDIATELY.") // yogs start - Logs SM chatter
+ investigate_log("The supermatter crystal: DANGER: BIOHAZARD DETECTED, VACATE THE CHAMBER IMMEDIATELY.", INVESTIGATE_SUPERMATTER) // yogs end
+ if(damage >= emergency_point)
+ radio.talk_into(src, "DANGER: SUPERMATTER BIOHAZARD LEVELS HAVE EXCEEDED SAFETY THRESHOLDS.", common_channel)
+ log_game("DANGER: SUPERMATTER BIOHAZARD LEVELS HAVE EXCEEDED SAFETY THRESHOLDS.") // yogs start - Logs SM chatter
+ investigate_log("DANGER: SUPERMATTER BIOHAZARD LEVELS HAVE EXCEEDED SAFETY THRESHOLDS.", INVESTIGATE_SUPERMATTER) // yogs end
if(antinoblium_attached)
if(support_integrity <= 10)
@@ -751,6 +710,23 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
if(damage > explosion_point)
countdown()
+ //blob SM HAMMM
+ if(supermatter_blob)
+ powerloss_inhibitor = 0.01
+ power += 10000
+ if(prob(30))
+ radiation_pulse(src, 5000, 4)
+ pulsewave()
+ T.hotspot_expose(max(500+FIRE_MINIMUM_TEMPERATURE_TO_EXIST,T.return_air().return_temperature()), 100)
+ playsound(src.loc, 'sound/weapons/emitter2.ogg', 100, 1, extrarange = 10)
+ supermatter_zap(src, 5, power)
+ for(var/i = 1 to 20)
+ fire_nuclear_particle()
+ if(istype(T, /turf/open/space) || T.return_air().total_moles() < MOLE_SPACE_THRESHOLD)
+ damage += DAMAGE_HARDCAP * explosion_point
+ if(prob(2))
+ empulse(src, 10, 5)
+
//emagged SM go BRRRRRRR here
if(antinoblium_attached && !noblium_suppressed)
if(prob(10+round(damage/(explosion_point/20),1)*3) & support_integrity>0)//radio chatter to make people panic
@@ -762,23 +738,25 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
if(80)
radio.talk_into(src, "COMPLETE FAILURE OF CHARGE SEQUESTRATION IMMINENT, ACTIVATING EMERGENCY CHARGE DISPERSION SYSTEM!", engineering_channel)
if(70)
- radio.talk_into(src, "CHARGE DISPERSION SYSTEM ACTIVE. CORRUPTION OF PARANOBLIUM INTERFACE SYSTEM DETECTED, MATTER EMISSION LEVELS RISING", engineering_channel)
+ radio.talk_into(src, "CHARGE DISPERSION SYSTEM ACTIVE. CORRUPTION OF PARANOBLIUM INTERFACE SYSTEM DETECTED, MATTER EMISSION LEVELS RISING.", engineering_channel)
if(60)
- radio.talk_into(src, "PARANOBLIUM INTERFACE OPERATING AT [round(75+ rand()*10,0.01)]% CAPACITY, MATTER EMISSION FACTOR RISING", engineering_channel)
+ radio.talk_into(src, "PARANOBLIUM INTERFACE OPERATING AT [round(75+ rand()*10,0.01)]% CAPACITY, MATTER EMISSION FACTOR RISING.", engineering_channel)
if(50)
- radio.talk_into(src, "COMPLETE FAILURE OF GAMMA RADIATION SUPPRESSION SYSTEM DETECTED, ACTIVATING GAMMA EMISSION BUNDLING AND DISPERSION SYSTEM", engineering_channel)
+ radio.talk_into(src, "COMPLETE FAILURE OF GAMMA RADIATION SUPPRESSION SYSTEM DETECTED, ACTIVATING GAMMA EMISSION BUNDLING AND DISPERSION SYSTEM.", engineering_channel)
if(40)
- radio.talk_into(src, "DISPERSION SYSTEM ACTIVATION FAILED, BUNDLER NOW FIRING WITHOUT GUIDANCE", engineering_channel)
+ radio.talk_into(src, "DISPERSION SYSTEM ACTIVATION FAILED, BUNDLER NOW FIRING WITHOUT GUIDANCE.", engineering_channel)
+ priority_announce("SUPERMATTER INSTABILITY IS AT 60%, PULSEWAVE IMMINENT.", "Anomaly Alert", 'sound/misc/notice1.ogg')
if(30)
- radio.talk_into(src, "WARNING ENERGY SPIKE IN CRYSTAL WELL DETECTED, ESTIMATED ENERGY OUTPUT EXCEEDS PEAK CHARGE DISPERSION CAPACITY", engineering_channel)
+ radio.talk_into(src, "WARNING ENERGY SPIKE IN CRYSTAL WELL DETECTED, ESTIMATED ENERGY OUTPUT EXCEEDS PEAK CHARGE DISPERSION CAPACITY.", engineering_channel)
if(20)
- radio.talk_into(src, "CRYSTAL WELL DESTABILIZED, ELECTROMAGNETIC PULSES INBOUND, PARANOBLIUM INTERFACE OPERATING AT [round(15+ rand()*10,0.01)]% CAPACITY", common_channel)
+ radio.talk_into(src, "CRYSTAL WELL DESTABILIZED, ELECTROMAGNETIC PULSES INBOUND, PARANOBLIUM INTERFACE OPERATING AT [round(15+ rand()*10,0.01)]% CAPACITY.", common_channel)
if(10)
- radio.talk_into(src, "ELECTROMAGNETIC FIELD CONTAINMENT FAILED, PARANOBLIUM INTERFACE NONFUNCTIONAL, RESONANCE CASCADE IMMINENT", common_channel)
+ radio.talk_into(src, "ELECTROMAGNETIC FIELD CONTAINMENT FAILED, PARANOBLIUM INTERFACE NONFUNCTIONAL, RESONANCE CASCADE IMMINENT.", common_channel)
+ priority_announce("SUPERMATTER INSTABILITY IS AT 90%, SUPERMATTER SURGE DETECTED, VACATE THE AREA IMMEDIATELY.", "Anomaly Alert", 'sound/misc/notice1.ogg')
if(6)
- radio.talk_into(src, "ELECTROMAGNETIC PULSES IMMINENT, CONTAINMENT AND COOLING FAILURE IMMINENT", common_channel)
- if(1) //after those emps, anyone who can hear this must be lucky.
- radio.talk_into(src, "COMPLETE DESTABILIZATION OF ALL MAJOR SUPPORT SYSTEMS, MATTER EMISSION FACTOR AT 600%, COMPLETE EVACUATION IS ADVISED", common_channel)
+ radio.talk_into(src, "ELECTROMAGNETIC PULSES IMMINENT, CONTAINMENT AND COOLING FAILURE IMMINENT.", common_channel)
+ if(1)
+ priority_announce("COMPLETE DESTABILIZATION OF ALL MAJOR SUPPORT SYSTEMS, MATTER EMISSION FACTOR AT 600%, COMPLETE EVACUATION IS ADVISED.", "Anomaly Alert", 'sound/misc/notice1.ogg')
support_integrity -= 1
radiation_pulse(src, (100-support_integrity)*2, 4)
if(support_integrity<3)
@@ -794,10 +772,13 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
if(prob(10))
T.hotspot_expose(max(((100-support_integrity)*2)+FIRE_MINIMUM_TEMPERATURE_TO_EXIST,T.return_air().return_temperature()), 100)
if(prob(10+round(support_integrity/10,1)))
+ pulsewave()
var/ballcount = round(10-(support_integrity/10), 1) // Cause more radballs to be spawned
for(var/i = 1 to ballcount)
fire_nuclear_particle()
if(support_integrity<10)
+ powerloss_inhibitor = 0.01 //ensure big explosion
+ surging = 10000
if(istype(T, /turf/open/space) || T.return_air().total_moles() < MOLE_SPACE_THRESHOLD)
damage += DAMAGE_HARDCAP * explosion_point //Can't cheat by spacing the crystal to buy time, it will just delaminate faster
if(prob(2))
@@ -1025,7 +1006,6 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
message_admins("Antinoblium shard has been attached to the SM and is now going BRRRRRR.")
to_chat(user, "You attach the antinoblium shard to the [src], moving your hand away before a sudden gravitational wave pulls the [W] into the crystal as it flashes to ash!")
playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, 1)
- radio.use_command = TRUE
radiation_pulse(src, 150, 4)
empulse(src, 3,6)
qdel(W)
@@ -1163,19 +1143,28 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
step_towards(P,center)
step_towards(P,center)
-/obj/machinery/power/supermatter_crystal/proc/supermatter_anomaly_gen(turf/anomalycenter, type = FLUX_ANOMALY, anomalyrange = 5)
- var/turf/L = pick(orange(anomalyrange, anomalycenter))
- if(L)
- switch(type)
- if(FLUX_ANOMALY)
- var/obj/effect/anomaly/flux/A = new(L, 300)
- A.explosive = FALSE
- if(GRAVITATIONAL_ANOMALY)
- new /obj/effect/anomaly/grav(L, 250)
- if(PYRO_ANOMALY)
- new /obj/effect/anomaly/pyro(L, 400)
- if(RADIATION_ANOMALY)
- new /obj/effect/anomaly/radiation(L, 400)
+/proc/supermatter_anomaly_gen(turf/anomalycenter, type = ANOMALY_FLUX, anomalyrange = 5, has_weak_lifespan = TRUE)
+ var/turf/local_turf = pick(RANGE_TURFS(anomalyrange, anomalycenter) - anomalycenter)
+ if(!local_turf)
+ return
+
+ switch(type)
+ if(ANOMALY_FLUX)
+ new /obj/effect/anomaly/flux(local_turf, has_weak_lifespan ? rand(250, 300) : null)
+ if(ANOMALY_FLUX_EXPLOSIVE)
+ new /obj/effect/anomaly/flux/explosion(local_turf, has_weak_lifespan ? rand(250, 300) : null)
+ if(ANOMALY_GRAVITATIONAL)
+ new /obj/effect/anomaly/grav(local_turf, has_weak_lifespan ? rand(250, 300) : null)
+ if(ANOMALY_HALLUCINATION)
+ new /obj/effect/anomaly/hallucination(local_turf, has_weak_lifespan ? rand(250, 300) : null)
+ if(ANOMALY_PYRO)
+ new /obj/effect/anomaly/pyro(local_turf, has_weak_lifespan ? rand(250, 300) : null)
+ if(ANOMALY_VORTEX)
+ new /obj/effect/anomaly/bhole(local_turf, has_weak_lifespan ? rand(250, 300) : null)
+ if(ANOMALY_RADIATION)
+ new /obj/effect/anomaly/radiation(local_turf, has_weak_lifespan ? rand(250, 300) : null)
+ if(ANOMALY_RADIATION_X)
+ new /obj/effect/anomaly/radiation/goat(local_turf, has_weak_lifespan ? rand(250, 300) : null)
/obj/machinery/proc/supermatter_zap(atom/zapstart, range = 3, power)
. = zapstart.dir
@@ -1293,8 +1282,3 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
return ..()
#undef HALLUCINATION_RANGE
-#undef GRAVITATIONAL_ANOMALY
-#undef SUPERMATTER_EXPLOSION_LAMBDA
-#undef FLUX_ANOMALY
-#undef PYRO_ANOMALY
-#undef RADIATION_ANOMALY
diff --git a/code/modules/power/supermatter/supermatter_delamination.dm b/code/modules/power/supermatter/supermatter_delamination.dm
new file mode 100644
index 000000000000..5893ca5d2282
--- /dev/null
+++ b/code/modules/power/supermatter/supermatter_delamination.dm
@@ -0,0 +1,143 @@
+/datum/supermatter_delamination
+ ///Power amount of the SM at the moment of death
+ var/supermatter_power = 0
+ ///Amount of total gases interacting with the SM
+ var/supermatter_gas_amount = 0
+ ///Reference to the supermatter turf
+ var/turf/supermatter_turf
+ ///Baseline strenght of the explosion caused by the SM
+ var/supermatter_explosion_power = 0
+ ///Amount the gasmix will affect the explosion size
+ var/supermatter_gasmix_power_ratio = 0
+ ///Antinoblium inside sm
+ var/supermatter_antinoblium = FALSE
+ ///Trigger resonance cascading
+ var/supermatter_cascading = FALSE
+ ///Radiation amount
+ var/supermatter_radiation = 0
+ ///Blob shit
+ var/supermatter_blob = FALSE
+
+/datum/supermatter_delamination/New(supermatter_power, supermatter_gas_amount, turf/supermatter_turf, supermatter_explosion_power, supermatter_gasmix_power_ratio, supermatter_antinoblium, supermatter_cascading, supermatter_radiation, supermatter_blob)
+ . = ..()
+
+ src.supermatter_power = supermatter_power
+ src.supermatter_gas_amount = supermatter_gas_amount
+ src.supermatter_turf = supermatter_turf
+ src.supermatter_explosion_power = supermatter_explosion_power
+ src.supermatter_gasmix_power_ratio = supermatter_gasmix_power_ratio
+ src.supermatter_antinoblium = supermatter_antinoblium
+ src.supermatter_cascading = supermatter_cascading
+ src.supermatter_radiation = supermatter_radiation
+ src.supermatter_blob = supermatter_blob
+
+ setup_mob_interaction()
+ setup_delamination_type()
+
+/datum/supermatter_delamination/proc/setup_mob_interaction()
+ for(var/mob/living/victim as anything in GLOB.alive_mob_list)
+ if(!istype(victim) || victim.z != supermatter_turf.z)
+ continue
+
+ if(ishuman(victim))
+ //Hilariously enough, running into a closet should make you get hit the hardest.
+ var/mob/living/carbon/human/human = victim
+ human.adjust_hallucinations(max(50, min(300, DETONATION_HALLUCINATION * sqrt(1 / (get_dist(victim, src) + 1)) ) ) )
+
+ var/rads = DETONATION_RADS * sqrt( 1 / (get_dist(victim, src) + 1) )
+ victim.rad_act(rads)
+
+ for(var/mob/victim as anything in GLOB.player_list)
+ var/turf/mob_turf = get_turf(victim)
+ if(supermatter_turf.z != mob_turf.z)
+ continue
+
+ SEND_SOUND(victim, 'sound/magic/charge.ogg')
+
+ if (victim.z != supermatter_turf.z)
+ to_chat(victim, span_boldannounce("You hold onto \the [victim.loc] as hard as you can, as reality distorts around you. You feel safe."))
+ continue
+
+ to_chat(victim, span_boldannounce("You feel reality distort for a moment..."))
+ SEND_SIGNAL(victim, COMSIG_ADD_MOOD_EVENT, "delam", /datum/mood_event/delam)
+
+/datum/supermatter_delamination/proc/setup_delamination_type()
+ if(supermatter_blob)
+ call_blob()
+ return
+ if(supermatter_cascading && !supermatter_blob)
+ call_cascading()
+ call_cascadetesla()
+ call_explosion()
+ return
+ if(supermatter_gas_amount > MOLE_PENALTY_THRESHOLD && !supermatter_cascading && !supermatter_blob)
+ call_singulo()
+ return
+ if(supermatter_power > POWER_PENALTY_THRESHOLD && !supermatter_cascading && !supermatter_blob)
+ call_tesla()
+ call_explosion()
+ return
+
+/datum/supermatter_delamination/proc/shockwave() //borrowed ynot's code
+ var/atom/movable/gravity_lens/shockwave = new(supermatter_turf)
+ shockwave.transform = matrix().Scale(0.5)
+ shockwave.pixel_x = -240
+ shockwave.pixel_y = -240
+ animate(shockwave, alpha = 0, transform = matrix().Scale(20), time = 10 SECONDS, easing = QUAD_EASING)
+ QDEL_IN(shockwave, 10.5 SECONDS)
+
+/datum/supermatter_delamination/proc/call_cascading()
+ sound_to_playing_players('sound/magic/lightningbolt.ogg', volume = 50)
+ shockwave() //a pulse when sm is blown up
+ var/datum/round_event_control/resonance_cascade/cascade_roundevent = locate(/datum/round_event_control/resonance_cascade) in SSevents.control
+ cascade_roundevent.runEvent()
+ message_admins("The Supermatter Crystal has caused a resonance cascade.")
+
+/datum/supermatter_delamination/proc/call_singulo()
+ if(!supermatter_turf)
+ return
+ var/obj/singularity/created_singularity = new(supermatter_turf)
+ created_singularity.energy = 2400
+ created_singularity.consumedSupermatter = 1
+ message_admins("The Supermatter Crystal has created a singularity [ADMIN_JMP(created_singularity)].")
+
+/datum/supermatter_delamination/proc/call_explosion()
+ if(supermatter_power < 0) // in case of negative energy, make it positive
+ supermatter_power = -supermatter_power
+ var/explosion_mod = clamp((1.001**supermatter_power) / ((1.001**supermatter_power) + SUPERMATTER_EXPLOSION_LAMBDA), 0.1, 1)
+ //trying to cheat by spacing the crystal? YOU FOOL THERE ARE NO LOOPHOLES TO ESCAPE YOUR UPCOMING DEATH
+ if(istype(supermatter_turf, /turf/open/space) || supermatter_gas_amount < MOLE_SPACE_THRESHOLD)
+ message_admins("[src] has exploded in empty space.")
+ explosion_mod = max(explosion_mod, 0.5)
+ else
+ message_admins("[src] has exploded")
+ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(empulse), supermatter_turf, supermatter_explosion_power * explosion_mod, (supermatter_explosion_power * explosion_mod * 2) + (supermatter_explosion_power/4), TRUE, FALSE, FALSE, TRUE)
+ explosion(supermatter_turf, supermatter_explosion_power * explosion_mod * 0.5, supermatter_explosion_power * explosion_mod + 2, supermatter_explosion_power * explosion_mod + 4, supermatter_explosion_power * explosion_mod + 6, 1, 1)
+ radiation_pulse(supermatter_turf, (supermatter_radiation + 2400) * supermatter_explosion_power)
+
+/datum/supermatter_delamination/proc/call_tesla()
+ if(supermatter_turf)
+ var/obj/singularity/energy_ball/supermatter_tesla = new(supermatter_turf)
+ supermatter_tesla.energy = supermatter_power
+ message_admins("The Supermatter Crystal has created an energy ball [ADMIN_JMP(supermatter_tesla)].")
+
+/datum/supermatter_delamination/proc/call_cascadetesla()
+ if(supermatter_turf)
+ var/obj/singularity/energy_ball/supermatter/supermatter_tesla = new(supermatter_turf)
+ supermatter_tesla.energy += supermatter_power * 100 // god
+ message_admins("The Supermatter Crystal has created an energy ball [ADMIN_JMP(supermatter_tesla)].")
+
+/datum/supermatter_delamination/proc/call_blob()
+ var/list/candidates = pollGhostCandidates("Do you wish to be considered for the special role of Blazing Oil Blob?", ROLE_BLOB, null, ROLE_BLOB)
+ if(candidates.len)
+ var/mob/dead/observer/new_blob = pick(candidates)
+ var/mob/camera/blob/BC = new_blob.become_overmind(350, 1.5, 1)
+ var/explosion_mod = clamp((1.001**supermatter_power) / ((1.001**supermatter_power) + SUPERMATTER_EXPLOSION_LAMBDA), 0.1, 1)
+ BC.set_strain(/datum/blobstrain/reagent/blazing_oil) //to protect against the fire around the blob when sm shitting itself
+ BC.forceMove(supermatter_turf)
+ BC.place_blob_core(BLOB_FORCE_PLACEMENT)
+ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(empulse), supermatter_turf, supermatter_explosion_power * explosion_mod, (supermatter_explosion_power * explosion_mod * 2) + (supermatter_explosion_power/4), TRUE, FALSE, FALSE, TRUE)
+ message_admins("Supermatter has created a blob. [ADMIN_JMP(BC)].")
+ else
+ supermatter_blob = FALSE
+ setup_delamination_type() //No blob? :( do other delams then
diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm
index e4396c6a1a83..f4a6a731d77e 100644
--- a/code/modules/power/tesla/coil.dm
+++ b/code/modules/power/tesla/coil.dm
@@ -78,7 +78,7 @@
return ..()
-/obj/machinery/power/tesla_coil/tesla_act(power, tesla_flags, shocked_targets)
+/obj/machinery/power/tesla_coil/tesla_act(power, tesla_flags, shocked_targets, zap_gib = FALSE)
if(anchored && !panel_open)
obj_flags |= BEING_SHOCKED
add_avail((power * (1 - percentage_power_loss))*input_power_multiplier)
@@ -114,7 +114,7 @@
research_points_per_zap = 6 // level 1 coil: 44/m, level coil 2: 60/m, level coil 3: 90/m, level coil 4: 180/m
money_per_zap = 6
-/obj/machinery/power/tesla_coil/research/tesla_act(power, tesla_flags, shocked_things)
+/obj/machinery/power/tesla_coil/research/tesla_act(power, tesla_flags, shocked_targets, zap_gib = FALSE)
if(anchored && !panel_open)
obj_flags |= BEING_SHOCKED
add_avail((power * (1 - percentage_power_loss))*input_power_multiplier)
@@ -178,7 +178,7 @@
return ..()
-/obj/machinery/power/grounding_rod/tesla_act(power)
+/obj/machinery/power/grounding_rod/tesla_act(power, tesla_flags, shocked_targets, zap_gib = FALSE)
if(anchored && !panel_open)
flick("grounding_rodhit", src)
tesla_buckle_check(power)
diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm
index 932dd6e24bf4..5065b33f64b3 100644
--- a/code/modules/power/tesla/energy_ball.dm
+++ b/code/modules/power/tesla/energy_ball.dm
@@ -10,24 +10,36 @@
grav_pull = 0
contained = 0
density = TRUE
- energy = 0
+ energy = 50
dissipate = 1
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/max_balls = 10
var/zap_range = 7
+ ///Boolean that, if TRUE, will not lose energy and tesla zap will dust you
+ var/hypercharged = FALSE
-/obj/singularity/energy_ball/Initialize(mapload, starting_energy = 50, is_miniball = FALSE)
+ ///List of all energy balls that's orbiting this one.
+ var/list/obj/singularity/energy_ball/orbiting_balls = list()
+
+/obj/singularity/energy_ball/Initialize(mapload, starting_energy = energy, is_miniball = FALSE)
miniball = is_miniball
. = ..()
if(!is_miniball)
set_light(10, 7, "#EEEEFF")
+/obj/singularity/energy_ball/supermatter
+ name = "supermatter energy ball"
+ color = "#ffe800"
+ energy = 10000
+ max_balls = 20
+ zap_range = 20
+ hypercharged = TRUE
+
/obj/singularity/energy_ball/ex_act(severity, target)
return
@@ -36,45 +48,47 @@
var/obj/singularity/energy_ball/EB = orbiting.parent
EB.orbiting_balls -= src
- for(var/ball in orbiting_balls)
- var/obj/singularity/energy_ball/EB = ball
- qdel(EB)
-
- . = ..()
+ QDEL_LIST(orbiting_balls)
+ return ..()
/obj/singularity/energy_ball/admin_investigate_setup()
if(miniball)
return //don't annnounce miniballs
- ..()
+ return ..()
/obj/singularity/energy_ball/process()
- if(!orbiting)
- handle_energy()
-
- move_the_basket_ball(4 + orbiting_balls.len * 1.5)
+ if(orbiting)
+ energy = 0 // ensure we dont have miniballs of miniballs
+ return
- playsound(src.loc, 'sound/magic/lightningbolt.ogg', 100, 1, extrarange = 30)
+ handle_energy()
+ move_the_basket_ball(4 + orbiting_balls.len * 1.5)
+ playsound(loc, 'sound/magic/lightningbolt.ogg', 100, 1, extrarange = 30)
- pixel_x = 0
- pixel_y = 0
+ pixel_x = 0
+ pixel_y = 0
+ if(hypercharged)
+ tesla_zap(src, zap_range, TESLA_HYPERCHARGED_POWER, TESLA_DEFAULT_FLAGS | TESLA_ALLOW_DUPLICATES, zap_gib = TRUE)
+ else
tesla_zap(src, zap_range, TESLA_DEFAULT_POWER)
- pixel_x = -32
- pixel_y = -32
+ pixel_x = -32
+ pixel_y = -32
- var/list/RG = range(1, src)
- for(var/obj/singularity/energy_ball/E in RG)
- if(!E.miniball && E != src)
- collide(E)
+ var/list/around_us = range(1, src)
+ for(var/obj/singularity/energy_ball/E in around_us)
+ if(!E.miniball && E != src)
+ collide(E)
- for (var/ball in orbiting_balls)
- if(prob(80)) //tesla nerf/reducing lag, each miniball now has only 20% to trigger the zap
- continue
+ for (var/obj/singularity/energy_ball/ball as anything in orbiting_balls)
+ if(prob(80)) //tesla nerf/reducing lag, each miniball now has only 20% to trigger the zap
+ continue
+ if(ball.hypercharged)
+ tesla_zap(ball, zap_range, TESLA_DEFAULT_POWER, TESLA_DEFAULT_FLAGS | TESLA_ALLOW_DUPLICATES, zap_gib = TRUE)
+ else
tesla_zap(ball, rand(2, zap_range), TESLA_MINI_POWER)
- else
- energy = 0 // ensure we dont have miniballs of miniballs
/obj/singularity/energy_ball/examine(mob/user)
. = ..()
@@ -113,7 +127,7 @@
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(loc, 'sound/magic/lightning_chargeup.ogg', 100, 1, extrarange = 30)
addtimer(CALLBACK(src, PROC_REF(new_mini_ball)), 100)
else if(energy < energy_to_lower && orbiting_balls.len)
@@ -124,6 +138,8 @@
qdel(Orchiectomy_target)
else if(orbiting_balls.len)
+ if(hypercharged)
+ dissipate = 0
dissipate() //sing code has a much better system.
/obj/singularity/energy_ball/proc/new_mini_ball()
@@ -134,7 +150,11 @@
if(orbiting_balls.len >= max_balls)
return
- var/obj/singularity/energy_ball/EB = new(loc, 0, TRUE)
+ var/obj/singularity/energy_ball/EB
+ if(hypercharged)
+ EB = new /obj/singularity/energy_ball/supermatter(loc, 0, TRUE)
+ else
+ EB = new /obj/singularity/energy_ball(loc, 0, TRUE)
EB.transform *= pick(0.3, 0.4, 0.5, 0.6, 0.7)
var/icon/I = icon(icon,icon_state,dir)
@@ -144,7 +164,6 @@
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)
@@ -171,14 +190,17 @@
/obj/singularity/energy_ball/orbit(obj/singularity/energy_ball/target)
if (istype(target))
target.orbiting_balls += src
+ color = target.color
GLOB.poi_list -= src
target.dissipate_strength = target.orbiting_balls.len
- . = ..()
+ return ..()
+
/obj/singularity/energy_ball/stop_orbit()
if (orbiting && istype(orbiting.parent, /obj/singularity/energy_ball))
var/obj/singularity/energy_ball/orbitingball = orbiting.parent
orbitingball.orbiting_balls -= src
+ color = initial(color)
orbitingball.dissipate_strength = orbitingball.orbiting_balls.len
. = ..()
if (!QDELETED(src))
@@ -198,7 +220,7 @@
var/mob/living/carbon/C = A
C.dust()
-/proc/tesla_zap(atom/source, zap_range = 3, power, tesla_flags = TESLA_DEFAULT_FLAGS, list/shocked_targets)
+/proc/tesla_zap(atom/source, zap_range = 3, power, tesla_flags = TESLA_DEFAULT_FLAGS, list/shocked_targets, zap_gib = FALSE)
. = source.dir
if(power < 1000)
return
@@ -211,29 +233,35 @@
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))
- var/static/blacklisted_tesla_types = typecacheof(list(/obj/machinery/atmospherics,
- /obj/machinery/power/emitter,
- /obj/machinery/field/generator,
- /mob/living/simple_animal,
- /obj/machinery/particle_accelerator/control_box,
- /obj/structure/particle_accelerator/fuel_chamber,
- /obj/structure/particle_accelerator/particle_emitter/center,
- /obj/structure/particle_accelerator/particle_emitter/left,
- /obj/structure/particle_accelerator/particle_emitter/right,
- /obj/structure/particle_accelerator/power_box,
- /obj/structure/particle_accelerator/end_cap,
- /obj/machinery/field/containment,
- /obj/structure/disposalpipe,
- /obj/structure/disposaloutlet,
- /obj/machinery/disposal/deliveryChute,
- /obj/machinery/camera,
- /obj/structure/sign,
- /obj/machinery/gateway,
- /obj/structure/lattice,
- /obj/structure/grille,
- /obj/machinery/the_singularitygen/tesla,
- /obj/structure/frame/machine))
+ var/static/things_to_shock = typecacheof(list(
+ /obj/machinery,
+ /mob/living,
+ /obj/structure,
+ ))
+ var/static/blacklisted_tesla_types = typecacheof(list(
+ /obj/machinery/atmospherics/pipe,
+ /obj/machinery/power/emitter,
+ /obj/machinery/field/generator,
+ /mob/living/simple_animal/hostile,
+ /mob/living/simple_animal/slime,
+ /obj/machinery/particle_accelerator/control_box,
+ /obj/structure/particle_accelerator/fuel_chamber,
+ /obj/structure/particle_accelerator/particle_emitter/center,
+ /obj/structure/particle_accelerator/particle_emitter/left,
+ /obj/structure/particle_accelerator/particle_emitter/right,
+ /obj/structure/particle_accelerator/power_box,
+ /obj/structure/particle_accelerator/end_cap,
+ /obj/machinery/field/containment,
+ /obj/structure/disposalpipe,
+ /obj/structure/disposaloutlet,
+ /obj/machinery/disposal/deliveryChute,
+ /obj/machinery/camera,
+ /obj/structure/sign,
+ /obj/machinery/gateway,
+ /obj/structure/lattice,
+ /obj/machinery/the_singularitygen/tesla,
+ /obj/structure/frame/machine,
+ ))
// +3 to range specifically to include grounding rods that are zap_range+3 away
for(var/A in typecache_filter_multi_list_exclusion(oview(source, zap_range+3), things_to_shock, blacklisted_tesla_types))
@@ -266,14 +294,11 @@
else if(isliving(A))
var/mob/living/L = A
- if(dist <= zap_range && (dist < closest_dist || !closest_mob) && L.stat != DEAD && !(L.flags_1 & TESLA_IGNORE_1))
+ if(dist <= zap_range && (dist < closest_dist || !closest_mob) && L.stat != DEAD && !(L.status_flags & GODMODE) && !(L.mob_biotypes & MOB_SPIRIT) && !(L.flags_1 & TESLA_IGNORE_1))
closest_mob = L
closest_atom = A
closest_dist = dist
- else if(closest_mob)
- continue
-
else if(ismachinery(A))
var/obj/machinery/M = A
if(dist <= zap_range && (dist < closest_dist || !closest_machine) && !(M.obj_flags & BEING_SHOCKED))
@@ -281,9 +306,6 @@
closest_atom = A
closest_dist = dist
- else if(closest_mob)
- continue
-
else if(istype(A, /obj/structure/blob))
var/obj/structure/blob/B = A
if(dist <= zap_range && (dist < closest_dist || !closest_tesla_coil) && !(B.obj_flags & BEING_SHOCKED))
@@ -291,9 +313,6 @@
closest_atom = A
closest_dist = dist
- else if(closest_blob)
- continue
-
else if(isstructure(A))
var/obj/structure/S = A
if(dist <= zap_range && (dist < closest_dist || !closest_tesla_coil) && !(S.obj_flags & BEING_SHOCKED))
@@ -304,7 +323,10 @@
//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)]", time=5, maxdistance = INFINITY)
+ if(zap_gib)
+ source.Beam(closest_atom, icon_state = "solar_beam", time = 5, maxdistance = INFINITY)
+ else
+ source.Beam(closest_atom, icon_state = "lightning[rand(1,12)]", time = 5, maxdistance = INFINITY)
if(!(tesla_flags & TESLA_ALLOW_DUPLICATES))
LAZYSET(shocked_targets, closest_atom, TRUE)
var/zapdir = get_dir(source, closest_atom)
@@ -320,20 +342,20 @@
else if(closest_mob)
var/shock_damage = (tesla_flags & TESLA_MOB_DAMAGE)? (min(round(power/600), 90) + rand(-5, 5)) : 0
- closest_mob.electrocute_act(shock_damage, source, 1, tesla_shock = 1, stun = (tesla_flags & TESLA_MOB_STUN))
+ closest_mob.electrocute_act(shock_damage, source, 1, tesla_shock = 1, stun = (tesla_flags & TESLA_MOB_STUN), gib = zap_gib)
if(issilicon(closest_mob))
var/mob/living/silicon/S = closest_mob
if((tesla_flags & TESLA_MOB_STUN) && (tesla_flags & TESLA_MOB_DAMAGE))
S.emp_act(EMP_LIGHT)
- tesla_zap(S, 7, power / 1.5, tesla_flags, shocked_targets) // metallic folks bounce it further
+ tesla_zap(S, 7, power / 1.5, tesla_flags, shocked_targets, zap_gib) // metallic folks bounce it further
else
- tesla_zap(closest_mob, 5, power / 1.5, tesla_flags, shocked_targets)
+ tesla_zap(closest_mob, 5, power / 1.5, tesla_flags, shocked_targets, zap_gib)
else if(closest_machine)
- closest_machine.tesla_act(power, tesla_flags, shocked_targets)
+ closest_machine.tesla_act(power*1.9, tesla_flags, shocked_targets, zap_gib)
else if(closest_blob)
- closest_blob.tesla_act(power, tesla_flags, shocked_targets)
+ closest_blob.tesla_act(power, tesla_flags, shocked_targets, zap_gib)
else if(closest_structure)
- closest_structure.tesla_act(power, tesla_flags, shocked_targets)
+ closest_structure.tesla_act(power, tesla_flags, shocked_targets, zap_gib)
diff --git a/code/modules/power/tesla/generator.dm b/code/modules/power/tesla/generator.dm
index 53d7010806be..e38490f491f3 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, tesla_flags)
+/obj/machinery/the_singularitygen/tesla/tesla_act(power, tesla_flags, shocked_targets, zap_gib = FALSE)
if(tesla_flags & TESLA_MACHINE_EXPLOSIVE)
energy += power
diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm
index 060216b2c47c..357ebd1f6a58 100644
--- a/code/modules/reagents/reagent_dispenser.dm
+++ b/code/modules/reagents/reagent_dispenser.dm
@@ -77,7 +77,7 @@
/obj/structure/reagent_dispensers/fueltank/fire_act(exposed_temperature, exposed_volume)
boom()
-/obj/structure/reagent_dispensers/fueltank/tesla_act()
+/obj/structure/reagent_dispensers/fueltank/tesla_act(power, tesla_flags, shocked_targets, zap_gib = FALSE)
..() //extend the zap
boom()
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index eabed4943521..ea1054a6a9d9 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -251,7 +251,7 @@
else
SSshuttle.emergencyLastCallLoc = null
- priority_announce("The emergency shuttle has been called. [GLOB.security_level >= SEC_LEVEL_RED ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [timeLeft(600)] minutes.[reason][SSshuttle.emergencyLastCallLoc ? "\n\nCall signal traced. Results can be viewed on any communications console." : "" ]", null, ANNOUNCER_SHUTTLECALLED, "Priority")
+ priority_announce("The emergency shuttle has been called. [GLOB.security_level >= SEC_LEVEL_RED ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [timeLeft(600)] minutes.\nNature of emergency:\n\n[reason][SSshuttle.emergencyLastCallLoc ? "\n\nCall signal traced. Results can be viewed on any communications console." : "" ]", null, ANNOUNCER_SHUTTLECALLED, "Priority")
/obj/docking_port/mobile/emergency/cancel(area/signalOrigin)
if(mode != SHUTTLE_CALL)
diff --git a/code/modules/spells/spell_types/conjure/rad_goat.dm b/code/modules/spells/spell_types/conjure/rad_goat.dm
index c85c4510191e..7103209ab04b 100644
--- a/code/modules/spells/spell_types/conjure/rad_goat.dm
+++ b/code/modules/spells/spell_types/conjure/rad_goat.dm
@@ -1,7 +1,7 @@
/datum/action/cooldown/spell/conjure/radiation_anomaly
name = "Spawn Radiation Anomaly"
desc = "Spawn a radiation anomaly!"
- button_icon = 'icons/obj/projectiles.dmi'
+ button_icon = 'icons/effects/effects.dmi'
button_icon_state = "radiation_anomaly"
sound = 'sound/weapons/resonator_fire.ogg'
diff --git a/code/modules/swarmers/swarmer.dm b/code/modules/swarmers/swarmer.dm
index 1fe92c9b868b..fdfad769000b 100644
--- a/code/modules/swarmers/swarmer.dm
+++ b/code/modules/swarmers/swarmer.dm
@@ -258,7 +258,7 @@
playsound(src, 'sound/effects/sparks4.ogg', 50, TRUE)
do_teleport(target, safe_turf , 0, channel = TELEPORT_CHANNEL_BLUESPACE)
-/mob/living/simple_animal/hostile/swarmer/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
+/mob/living/simple_animal/hostile/swarmer/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE, gib = FALSE)
if(!tesla_shock)
return FALSE
return ..()
diff --git a/code/modules/vehicles/bicycle.dm b/code/modules/vehicles/bicycle.dm
index cc42c122ac97..cfbf585ec6a7 100644
--- a/code/modules/vehicles/bicycle.dm
+++ b/code/modules/vehicles/bicycle.dm
@@ -10,7 +10,7 @@
D.set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 4), TEXT_SOUTH = list(0, 4), TEXT_EAST = list(0, 4), TEXT_WEST = list( 0, 4)))
D.vehicle_move_delay = 0
-/obj/vehicle/ridden/bicycle/tesla_act() // :::^^^)))
+/obj/vehicle/ridden/bicycle/tesla_act(power, tesla_flags, shocked_targets, zap_gib = FALSE) // :::^^^)))
name = "fried bicycle"
desc = "Well spent."
color = rgb(63, 23, 4)
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index 9f197ef7b8bf..eca845f3fd81 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/obj/assemblies/new_assemblies.dmi b/icons/obj/assemblies/new_assemblies.dmi
index 1c00e9511046..7b0c9a434449 100644
Binary files a/icons/obj/assemblies/new_assemblies.dmi and b/icons/obj/assemblies/new_assemblies.dmi differ
diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi
index 2850636df4f2..9a42039a46df 100644
Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ
diff --git a/sound/misc/airraid.ogg b/sound/misc/airraid.ogg
index 820eaccf91d7..cc9913becdde 100644
Binary files a/sound/misc/airraid.ogg and b/sound/misc/airraid.ogg differ
diff --git a/yogstation.dme b/yogstation.dme
index 5cf92d66c9a6..8eb77c73e0f8 100644
--- a/yogstation.dme
+++ b/yogstation.dme
@@ -27,6 +27,7 @@
#include "code\__DEFINES\actions.dm"
#include "code\__DEFINES\admin.dm"
#include "code\__DEFINES\ai.dm"
+#include "code\__DEFINES\anomalies.dm"
#include "code\__DEFINES\antagonists.dm"
#include "code\__DEFINES\art.dm"
#include "code\__DEFINES\assembly.dm"
@@ -2245,6 +2246,7 @@
#include "code\modules\events\anomaly_bluespace.dm"
#include "code\modules\events\anomaly_flux.dm"
#include "code\modules\events\anomaly_grav.dm"
+#include "code\modules\events\anomaly_hallucination.dm"
#include "code\modules\events\anomaly_pyro.dm"
#include "code\modules\events\anomaly_radiation.dm"
#include "code\modules\events\anomaly_vortex.dm"
@@ -3171,6 +3173,7 @@
#include "code\modules\power\singularity\particle_accelerator\particle_control.dm"
#include "code\modules\power\singularity\particle_accelerator\particle_emitter.dm"
#include "code\modules\power\supermatter\supermatter.dm"
+#include "code\modules\power\supermatter\supermatter_delamination.dm"
#include "code\modules\power\tesla\coil.dm"
#include "code\modules\power\tesla\energy_ball.dm"
#include "code\modules\power\tesla\generator.dm"
diff --git a/yogstation/code/game/gamemodes/gangs/dominator.dm b/yogstation/code/game/gamemodes/gangs/dominator.dm
index 7f1c896a0522..d4b82ec6c01c 100644
--- a/yogstation/code/game/gamemodes/gangs/dominator.dm
+++ b/yogstation/code/game/gamemodes/gangs/dominator.dm
@@ -51,7 +51,7 @@
/obj/machinery/dominator/hulk_damage()
return (max_integrity - integrity_failure) / DOM_HULK_HITS_REQUIRED
-/obj/machinery/dominator/tesla_act()
+/obj/machinery/dominator/tesla_act(power, tesla_flags, shocked_targets, zap_gib = FALSE)
qdel(src)
/obj/machinery/dominator/update_overlays()
diff --git a/yogstation/code/modules/assembly/signaler.dm b/yogstation/code/modules/assembly/signaler.dm
index 08dc33171402..b2e322f8c71b 100644
--- a/yogstation/code/modules/assembly/signaler.dm
+++ b/yogstation/code/modules/assembly/signaler.dm
@@ -1,5 +1,4 @@
/obj/item/assembly/signaler
- icon = 'yogstation/icons/obj/assemblies/new_assemblies.dmi'
var/static/list/label_colors = list("red", "green", "blue", "cyan", "magenta", "yellow", "white")
var/label_color = "green"
diff --git a/yogstation/code/modules/mob/living/simple_animal/hostile/floor_cluwne.dm b/yogstation/code/modules/mob/living/simple_animal/hostile/floor_cluwne.dm
index d0b41df6733d..9f12c8ccc5a8 100644
--- a/yogstation/code/modules/mob/living/simple_animal/hostile/floor_cluwne.dm
+++ b/yogstation/code/modules/mob/living/simple_animal/hostile/floor_cluwne.dm
@@ -151,7 +151,7 @@ GLOBAL_VAR_INIT(floor_cluwnes, 0)
return
-/mob/living/simple_animal/hostile/floor_cluwne/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)//prevents runtimes with machine fuckery
+/mob/living/simple_animal/hostile/floor_cluwne/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE, gib = FALSE)//prevents runtimes with machine fuckery
return FALSE
/mob/living/simple_animal/hostile/floor_cluwne/proc/Found_You()
diff --git a/yogstation/icons/obj/assemblies/new_assemblies.dmi b/yogstation/icons/obj/assemblies/new_assemblies.dmi
deleted file mode 100644
index 37ad19bbc885..000000000000
Binary files a/yogstation/icons/obj/assemblies/new_assemblies.dmi and /dev/null differ