Changes flux anomalies and gravity anomalies to be a little less horrible to deal with. (#92216)

## About The Pull Request

Flux anomalies, when they detonate, do a minor explosion and emit a wide
EMP pulse. This can cause a lot of damage in its own right, but it won't
syndicate bomb a random section of the station anymore.

Gravity anomalies

A) No longer have the click catching giant image floating above it
anymore.
B) No longer chain hardstuns you if you get too close every time it
ticks. Instead, it knocks you down. You can also avoid this effect by
wearing magboots.
C) Also doesn't throw literally every object in an area around it
straight into you every time it ticks. Instead, it does so only 20% of
the time.

Long range gas analyzers can now scan anomalies.

## Why It's Good For The Game

> Flux

A really annoying anomaly and one that I think has caused several
shuttle calls in its time. If you don't catch it, it blows up a segment
of the station entirely at random. This thing can almost be as bad as a
vortex in some contexts. A giant EMP can still be pretty destructive,
but it isn't quite at the same level as a giant explosion and I suspect
not shuttle call worthy. The minor explosion will at most trash a room.

> Gravity

I literally do not understand how anyone was supposed to disable these.
I literally gave up years ago even trying to catch them. They're
practically designed to kill you for trying. The click catching warp
effect not only looked kind of lame, but it just straight up stopped you
being able to disable them (mouse opacity doesn't work here and I tried,
it redirects clicks to other tiles for some godawful reason, probably
byond weirdness). You had to spam click a LOT to even try.

On top of that, it just stuns you? It literally just stuns you if you
approach it? And magboots can't help you to avoid that? What the fuck
are you _supposed_ to do to avoid that?

Also it can just instantly kill you if you approach it and it gathered
enough stuff because it will just keep slapping you with every loose
object within a radius around itself? At speed 5 so things that don't
normally embed will embed?

So it mulches you for approaching it, chain hardstuns you for
approaching it, and it deliberately intercepts clicks in a completely
unintuitive fashion even if you do manage to click it. You need to
approach it to stop it. A recipe for disaster. Fuck that.

> Analyzer

It was annoying this wasn't able to do this already!

## Changelog
🆑
balance: Gravity anomalies can be properly clicked. Rather than chain
stunning you, the anomaly will knock you down. You can avoid the
negatives of gravity anomalies by wearing magboots. (Yes, it did not
really protect you until now)
balance: Flux anomalies detonate in a much smaller explosion, but
release a wide EMP as well.
balance: Long range gas analyzers can now scan anomalies.
/🆑

(cherry picked from commit 2b0a824132)
This commit is contained in:
necromanceranne
2025-08-08 03:47:35 +10:00
committed by nevimer
parent d794bb36eb
commit 2a778cd510
5 changed files with 30 additions and 89 deletions
@@ -5,11 +5,11 @@
anomaly_core = /obj/item/assembly/signaler/anomaly/flux
var/canshock = FALSE
var/shockdamage = 20
var/explosive = FLUX_EXPLOSIVE
var/emp_zap = FLUX_EMP
/obj/effect/anomaly/flux/Initialize(mapload, new_lifespan, explosive = FLUX_EXPLOSIVE)
/obj/effect/anomaly/flux/Initialize(mapload, new_lifespan, emp_zap = FLUX_EMP)
. = ..()
src.explosive = explosive
src.emp_zap = emp_zap
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
@@ -42,20 +42,22 @@
M.electrocute_act(shockdamage, name, flags = SHOCK_NOGLOVES)
/obj/effect/anomaly/flux/detonate()
switch(explosive)
if(FLUX_EXPLOSIVE)
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(FLUX_LOW_EXPLOSIVE)
explosion(src, heavy_impact_range = 1, light_impact_range = 4, flash_range = 6)
if(FLUX_NO_EXPLOSION)
switch(emp_zap)
if(FLUX_EMP)
empulse(src, 4, 16)
explosion(src, heavy_impact_range = 1, light_impact_range = 4, flash_range = 6) //Trashes the room a bit, might blow a small hole in the hull.
if(FLUX_LIGHT_EMP)
empulse(src, 4, 6)
explosion(src, light_impact_range = 3, flash_range = 6)
if(FLUX_NO_EMP)
new /obj/effect/particle_effect/sparks(loc)
/// A flux anomaly which doesn't explode or produce a core
/// A flux anomaly which doesn't emp or produce a core
/obj/effect/anomaly/flux/minor
anomaly_core = null
// We need to override the default arguments here to achieve the desired effect
/obj/effect/anomaly/flux/minor/Initialize(mapload, new_lifespan, explosive = FLUX_NO_EXPLOSION)
/obj/effect/anomaly/flux/minor/Initialize(mapload, new_lifespan, emp_zap = FLUX_NO_EMP)
return ..()
///Bigger, meaner, immortal flux anomaly
@@ -1,50 +1,4 @@
/atom/movable/warp_effect
plane = DISPLACEMENT_PLANE
appearance_flags = PIXEL_SCALE|LONG_GLIDE // no tile bound so you can see it around corners and so
icon = 'icons/effects/light_overlays/light_352.dmi'
icon_state = "light"
pixel_x = -176
pixel_y = -176
/atom/movable/warp_effect/Initialize(mapload)
. = ..()
var/turf/new_turf = get_turf(src)
if(new_turf)
var/new_offset = GET_TURF_PLANE_OFFSET(new_turf)
ADD_TRAIT(GLOB, TRAIT_DISTORTION_IN_USE(new_offset), ref(src))
/atom/movable/warp_effect/Destroy(force)
// Just in case I've forgotten how the movement api works
var/offset = GET_TURF_PLANE_OFFSET(loc)
REMOVE_TRAIT(GLOB, TRAIT_DISTORTION_IN_USE(offset), ref(src))
return ..()
/atom/movable/warp_effect/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
. = ..()
var/turf/new_turf = get_turf(src)
var/turf/old_turf = get_turf(old_loc)
if(!new_turf)
var/old_offset = GET_TURF_PLANE_OFFSET(old_turf)
REMOVE_TRAIT(GLOB, TRAIT_DISTORTION_IN_USE(old_offset), ref(src))
return
else if(get_turf(old_loc))
return
// If we're in a thing on a turf we COUNT as a distortion source
var/new_offset = GET_TURF_PLANE_OFFSET(new_turf)
ADD_TRAIT(GLOB, TRAIT_DISTORTION_IN_USE(new_offset), ref(src))
/atom/movable/warp_effect/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
. = ..()
if(same_z_layer)
return
if(old_turf)
var/old_offset = GET_TURF_PLANE_OFFSET(old_turf)
REMOVE_TRAIT(GLOB, TRAIT_DISTORTION_IN_USE(old_offset), ref(src))
if(new_turf)
var/new_offset = GET_TURF_PLANE_OFFSET(new_turf)
ADD_TRAIT(GLOB, TRAIT_DISTORTION_IN_USE(new_offset), ref(src))
/obj/effect/anomaly/grav
name = "gravitational anomaly"
icon = 'icons/effects/effects.dmi'
@@ -52,8 +6,7 @@
density = FALSE
anomaly_core = /obj/item/assembly/signaler/anomaly/grav
var/boing = 0
///Warp effect holder for displacement filter to "pulse" the anomaly
var/atom/movable/warp_effect/warp
var/object_launch_prob = 20
/obj/effect/anomaly/grav/Initialize(mapload, new_lifespan)
. = ..()
@@ -63,21 +16,6 @@
AddElement(/datum/element/connect_loc, loc_connections)
apply_wibbly_filters(src)
warp = new(src)
vis_contents += warp
/obj/effect/anomaly/grav/Destroy()
vis_contents -= warp
warp = null
return ..()
/obj/effect/anomaly/grav/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
. = ..()
if(same_z_layer)
return
if(warp)
SET_PLANE(warp, PLANE_TO_TRUE(warp.plane), new_turf)
/obj/effect/anomaly/grav/anomalyEffect(seconds_per_tick)
..()
boing = 1
@@ -85,7 +23,8 @@
if(!O.anchored)
step_towards(O,src)
for(var/mob/living/M in range(0, src))
gravShock(M)
if(!M.mob_negates_gravity())
gravShock(M)
for(var/mob/living/M in orange(4, src))
if(!M.mob_negates_gravity())
step_towards(M,src)
@@ -93,13 +32,9 @@
if(O.anchored || HAS_TRAIT(O, TRAIT_UNDERFLOOR))
continue
var/mob/living/target = locate() in view(4,src)
if(target && !target.stat)
if(target && !target.stat && prob(object_launch_prob))
O.throw_at(target, 5, 10)
//anomaly quickly contracts then slowly expands its ring
animate(warp, time = seconds_per_tick*3, transform = matrix().Scale(0.5,0.5))
animate(time = seconds_per_tick*7, transform = matrix())
/obj/effect/anomaly/grav/proc/on_entered(datum/source, atom/movable/AM)
SIGNAL_HANDLER
gravShock(AM)
@@ -110,11 +45,11 @@
/obj/effect/anomaly/grav/Bumped(atom/movable/AM)
gravShock(AM)
/obj/effect/anomaly/grav/proc/gravShock(mob/living/A)
if(boing && isliving(A) && !A.stat)
A.Paralyze(40)
var/atom/target = get_edge_target_turf(A, get_dir(src, get_step_away(A, src)))
A.throw_at(target, 5, 1)
/obj/effect/anomaly/grav/proc/gravShock(mob/living/living_debris)
if(boing && isliving(living_debris) && !living_debris.stat && !living_debris.mob_negates_gravity())
living_debris.Knockdown(4 SECONDS)
var/atom/target = get_edge_target_turf(living_debris, get_dir(src, get_step_away(living_debris, src)))
living_debris.throw_at(target, 5, 1)
boing = 0
/obj/effect/anomaly/grav/detonate()
@@ -148,6 +148,10 @@
ui_interact(user)
/obj/item/analyzer/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(istype(interacting_with, /obj/effect/anomaly) && can_see(user, interacting_with, ranged_scan_distance))
var/obj/effect/anomaly/ranged_anomaly = interacting_with
ranged_anomaly.analyzer_act(user, src)
return ITEM_INTERACT_SUCCESS
return interact_with_atom(interacting_with, user, modifiers)
/obj/item/analyzer/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)