Files
Bubberstation/code/game/objects/effects/anomalies.dm
SkyratBot a4bfe65cb1 [MIRROR] Dimensional Anomaly [MDB IGNORE] (#15974)
* Dimensional Anomaly (#69512)

About The Pull Request

Everyone has been asking: "When will there be an anomaly like the bioscrambler, but for the space station? Please, we need more things which replace objects with different objects from the same typepath."
Well I made it and it looked like ass because non-tiling floor and walls look terrible, so then I made this instead.
Dimensional.mp4

The "dimensional anomaly" shifts matter into a parallel dimension where objects are made out of something else.
Like the Bioscrambler anomaly, it does not expire on its own and only leaves when someone signals it or uses an anomaly remover.
When it spawns it picks a "theme" and converts terrain around it until it covers a 7x7 square, then it teleports somewhere else and picks a new theme.

A lot of these themes are relatively benign like "meat", "fancy carpet", or "gold". Some of them are kind of annoying like "icebox" because it creates floor which slows you down, or "clown" because bananium is intentionally annoying. Some of them are actively dangerous, mostly "uranium" and "plasma".
The main problem this will usually cause for crewmembers is decreasing area security. When it replaces doors it replaces them with ones which don't have any access control, and it will also replace RWalls with normal and much more vulnerable walls which will make breaking and entering significantly easier until someone has taken the time to fix the damage. But also sometimes it will irradiate them, you never know.

The fact that sometimes the changes are benign (or provide uncommon materials) and might be happening in places you don't care about access to might encourage people to push their luck and leave it alone until it starts turning the captain's office into a bamboo room or repainting medbay a fetching shade of flammable purple, which I would consider a success.
Armour.mp4

If you successfully harvest the anomaly core you can place it into the reactive armour to get Reactive Barricade Armour, which shifts your dimension when you take damage and attempts to place some randomised (not terribly durable) objects between you and hopefully your attacker (it really just picks up to four random unoccupied tiles next to you). If you're EMPed then the changes it make to the environment will often be as unpleasant for you as they are for a pursuer, and significantly more likely to harm both of you rather than just provide obstacles.

Other changes:
I split anomalies out into their own dmi file, seems to be all the rage lately.
I moved the anomaly placing code into a datum instead of the event because I wanted to reuse it but if you have a better idea about where I could have put it let me know.
This also fixes a bug where the material spreader component wasn't working when I applied plasma materials to something, the extra whitespace was parsing as another argument for some reason and meant it would runtime.
Supermatter delamination was still pointing to Delimber anomalies instead of Bioscrambler.

* Dimensional Anomaly

* Fixes the upstream merge skew

Co-authored-by: Jacquerel <hnevard@gmail.com>
Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com>
Co-authored-by: GoldenAlpharex <jerego1234@hotmail.com>
2022-09-05 17:59:34 -04:00

636 lines
19 KiB
Plaintext

//Anomalies, used for events. Note that these DO NOT work by themselves; their procs are called by the event datum.
/// 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..."
icon = 'icons/effects/anomalies.dmi'
icon_state = "vortex"
density = FALSE
anchored = TRUE
light_range = 3
var/obj/item/assembly/signaler/anomaly/aSignal = /obj/item/assembly/signaler/anomaly
var/area/impact_area
var/lifespan = 990
var/death_time
var/countdown_colour
var/obj/effect/countdown/anomaly/countdown
/// Do we drop a core when we're neutralized?
var/drops_core = TRUE
///Do we keep on living forever?
var/immortal = FALSE
///Do we stay in one place?
var/immobile = FALSE
/obj/effect/anomaly/Initialize(mapload, new_lifespan, drops_core = TRUE)
. = ..()
SSpoints_of_interest.make_point_of_interest(src)
START_PROCESSING(SSobj, src)
impact_area = get_area(src)
if (!impact_area)
return INITIALIZE_HINT_QDEL
src.drops_core = drops_core
aSignal = new aSignal(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!
frequency++
aSignal.set_frequency(frequency)
if(new_lifespan)
lifespan = new_lifespan
death_time = world.time + lifespan
countdown = new(src)
if(countdown_colour)
countdown.color = countdown_colour
countdown.start()
/obj/effect/anomaly/process(delta_time)
anomalyEffect(delta_time)
if(death_time < world.time && !immortal)
if(loc)
detonate()
qdel(src)
/obj/effect/anomaly/Destroy()
STOP_PROCESSING(SSobj, src)
QDEL_NULL(countdown)
if(aSignal)
QDEL_NULL(aSignal)
return ..()
/obj/effect/anomaly/proc/anomalyEffect(delta_time)
if(!immobile && DT_PROB(ANOMALY_MOVECHANCE, delta_time))
step(src,pick(GLOB.alldirs))
/obj/effect/anomaly/proc/detonate()
return
/obj/effect/anomaly/ex_act(severity, target)
if(severity >= EXPLODE_DEVASTATE)
qdel(src)
/obj/effect/anomaly/proc/anomalyNeutralize()
new /obj/effect/particle_effect/fluid/smoke/bad(loc)
if(drops_core)
aSignal.forceMove(drop_location())
aSignal = null
// else, anomaly core gets deleted by qdel(src).
qdel(src)
/obj/effect/anomaly/attackby(obj/item/weapon, mob/user, params)
if(weapon.tool_behaviour == TOOL_ANALYZER)
to_chat(user, span_notice("Analyzing... [src]'s unstable field is fluctuating along frequency [format_frequency(aSignal.frequency)], code [aSignal.code]."))
return TRUE
return ..()
///////////////////////
/atom/movable/warp_effect
plane = GRAVITY_PULSE_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
/obj/effect/anomaly/grav
name = "gravitational anomaly"
icon = 'icons/effects/effects.dmi'
icon_state = "shield2"
density = FALSE
aSignal = /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
/obj/effect/anomaly/grav/Initialize(mapload, new_lifespan, drops_core)
. = ..()
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = .proc/on_entered,
)
AddElement(/datum/element/connect_loc, loc_connections)
warp = new(src)
vis_contents += warp
/obj/effect/anomaly/grav/Destroy()
vis_contents -= warp
warp = null
return ..()
/obj/effect/anomaly/grav/anomalyEffect(delta_time)
..()
boing = 1
for(var/obj/O in orange(4, src))
if(!O.anchored)
step_towards(O,src)
for(var/mob/living/M in range(0, src))
gravShock(M)
for(var/mob/living/M in orange(4, src))
if(!M.mob_negates_gravity())
step_towards(M,src)
for(var/obj/O in range(0,src))
if(!O.anchored)
if(isturf(O.loc))
var/turf/T = O.loc
if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && HAS_TRAIT(O, TRAIT_T_RAY_VISIBLE))
continue
var/mob/living/target = locate() in view(4,src)
if(target && !target.stat)
O.throw_at(target, 5, 10)
//anomaly quickly contracts then slowly expands it's ring
animate(warp, time = delta_time*3, transform = matrix().Scale(0.5,0.5))
animate(time = delta_time*7, transform = matrix())
/obj/effect/anomaly/grav/proc/on_entered(datum/source, atom/movable/AM)
SIGNAL_HANDLER
gravShock(AM)
/obj/effect/anomaly/grav/Bump(atom/A)
gravShock(A)
/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)
boing = 0
/obj/effect/anomaly/grav/high
var/datum/proximity_monitor/advanced/gravity/grav_field
/obj/effect/anomaly/grav/high/Initialize(mapload, new_lifespan)
. = ..()
INVOKE_ASYNC(src, .proc/setup_grav_field)
/obj/effect/anomaly/grav/high/proc/setup_grav_field()
grav_field = new(src, 7, TRUE, rand(0, 3))
/obj/effect/anomaly/grav/high/detonate()
for(var/obj/machinery/gravity_generator/main/the_generator in GLOB.machines)
if(is_station_level(the_generator.z))
the_generator.blackout()
/obj/effect/anomaly/grav/high/Destroy()
QDEL_NULL(grav_field)
. = ..()
/////////////////////
/obj/effect/anomaly/flux
name = "flux wave anomaly"
icon_state = "flux"
density = TRUE
aSignal = /obj/item/assembly/signaler/anomaly/flux
var/canshock = FALSE
var/shockdamage = 20
var/explosive = FLUX_EXPLOSIVE
/obj/effect/anomaly/flux/Initialize(mapload, new_lifespan, drops_core = TRUE, explosive = FLUX_EXPLOSIVE)
. = ..()
src.explosive = explosive
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = .proc/on_entered,
)
AddElement(/datum/element/connect_loc, loc_connections)
/obj/effect/anomaly/flux/anomalyEffect()
..()
canshock = TRUE
for(var/mob/living/M in range(0, src))
mobShock(M)
/obj/effect/anomaly/flux/update_overlays()
. = ..()
. += emissive_appearance(icon, icon_state, alpha=src.alpha)
/obj/effect/anomaly/flux/proc/on_entered(datum/source, atom/movable/AM)
SIGNAL_HANDLER
mobShock(AM)
/obj/effect/anomaly/flux/Bump(atom/A)
mobShock(A)
/obj/effect/anomaly/flux/Bumped(atom/movable/AM)
mobShock(AM)
/obj/effect/anomaly/flux/proc/mobShock(mob/living/M)
if(canshock && istype(M))
canshock = FALSE
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)
new /obj/effect/particle_effect/sparks(loc)
/////////////////////
/obj/effect/anomaly/bluespace
name = "bluespace anomaly"
icon = 'icons/obj/weapons/guns/projectiles.dmi'
icon_state = "bluespace"
density = TRUE
aSignal = /obj/item/assembly/signaler/anomaly/bluespace
/obj/effect/anomaly/bluespace/anomalyEffect()
..()
for(var/mob/living/M in range(1,src))
do_teleport(M, locate(M.x, M.y, M.z), 4, channel = TELEPORT_CHANNEL_BLUESPACE)
/obj/effect/anomaly/bluespace/Bumped(atom/movable/AM)
if(isliving(AM))
do_teleport(AM, locate(AM.x, AM.y, AM.z), 8, channel = TELEPORT_CHANNEL_BLUESPACE)
/obj/effect/anomaly/bluespace/detonate()
var/turf/T = pick(get_area_turfs(impact_area))
if(!T)
return
// Calculate new position (searches through beacons in world)
var/obj/item/beacon/chosen
var/list/possible = list()
for(var/obj/item/beacon/W in GLOB.teleportbeacons)
possible += W
if(possible.len > 0)
chosen = pick(possible)
if(!chosen)
return
// Calculate previous position for transition
var/turf/FROM = T // the turf of origin we're travelling FROM
var/turf/TO = get_turf(chosen) // the turf of origin we're travelling TO
playsound(TO, 'sound/effects/phasein.ogg', 100, TRUE)
priority_announce("Massive bluespace translocation detected.", "Anomaly Alert", ANNOUNCER_MASSIVEBSPACEANOMALIES) //SKYRAT EDIT CHANGE
var/list/flashers = list()
for(var/mob/living/carbon/C in viewers(TO, null))
if(C.flash_act())
flashers += C
var/y_distance = TO.y - FROM.y
var/x_distance = TO.x - FROM.x
for (var/atom/movable/A in urange(12, FROM )) // iterate thru list of mobs in the area
if(istype(A, /obj/item/beacon))
continue // don't teleport beacons because that's just insanely stupid
if(iscameramob(A))
continue // Don't mess with AI eye, blob eye, xenobio or advanced cameras
if(A.anchored)
continue
var/turf/newloc = locate(A.x + x_distance, A.y + y_distance, TO.z) // calculate the new place
if(!A.Move(newloc) && newloc) // if the atom, for some reason, can't move, FORCE them to move! :) We try Move() first to invoke any movement-related checks the atom needs to perform after moving
A.forceMove(newloc)
if(ismob(A) && !(A in flashers)) // don't flash if we're already doing an effect
var/mob/give_sparkles = A
if(give_sparkles.client)
blue_effect(give_sparkles)
/obj/effect/anomaly/bluespace/proc/blue_effect(mob/make_sparkle)
make_sparkle.overlay_fullscreen("bluespace_flash", /atom/movable/screen/fullscreen/bluespace_sparkle, 1)
addtimer(CALLBACK(make_sparkle, /mob/.proc/clear_fullscreen, "bluespace_flash"), 2 SECONDS)
/////////////////////
/obj/effect/anomaly/pyro
name = "pyroclastic anomaly"
icon_state = "pyroclastic"
var/ticks = 0
/// How many seconds between each gas release
var/releasedelay = 10
aSignal = /obj/item/assembly/signaler/anomaly/pyro
/obj/effect/anomaly/pyro/anomalyEffect(delta_time)
..()
ticks += delta_time
if(ticks < releasedelay)
return
else
ticks -= releasedelay
var/turf/open/T = get_turf(src)
if(istype(T))
T.atmos_spawn_air("o2=5;plasma=5;TEMP=1000")
/obj/effect/anomaly/pyro/detonate()
INVOKE_ASYNC(src, .proc/makepyroslime)
/obj/effect/anomaly/pyro/proc/makepyroslime()
var/turf/open/T = get_turf(src)
if(istype(T))
T.atmos_spawn_air("o2=500;plasma=500;TEMP=1000") //Make it hot and burny for the new slime
var/new_colour = pick("red", "orange")
var/mob/living/simple_animal/slime/S = new(T, new_colour)
S.rabid = TRUE
S.amount_grown = SLIME_EVOLUTION_THRESHOLD
S.Evolve()
var/datum/action/innate/slime/reproduce/A = new
A.Grant(S)
var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as a pyroclastic anomaly slime?", ROLE_SENTIENCE, null, 10 SECONDS, S, POLL_IGNORE_PYROSLIME)
if(LAZYLEN(candidates))
var/mob/dead/observer/chosen = pick(candidates)
S.key = chosen.key
S.mind.special_role = ROLE_PYROCLASTIC_SLIME
var/policy = get_policy(ROLE_PYROCLASTIC_SLIME)
if (policy)
to_chat(S, policy)
S.log_message("was made into a slime by pyroclastic anomaly", LOG_GAME)
/////////////////////
/obj/effect/anomaly/bhole
name = "vortex anomaly"
icon_state = "vortex"
desc = "That's a nice station you have there. It'd be a shame if something happened to it."
aSignal = /obj/item/assembly/signaler/anomaly/vortex
/obj/effect/anomaly/bhole/anomalyEffect()
..()
if(!isturf(loc)) //blackhole cannot be contained inside anything. Weird stuff might happen
qdel(src)
return
grav(rand(0,3), rand(2,3), 50, 25)
//Throwing stuff around!
for(var/obj/O in range(2,src))
if(O == src)
return //DON'T DELETE YOURSELF GOD DAMN
if(!O.anchored)
var/mob/living/target = locate() in view(4,src)
if(target && !target.stat)
O.throw_at(target, 7, 5)
else
SSexplosions.med_mov_atom += O
/obj/effect/anomaly/bhole/proc/grav(r, ex_act_force, pull_chance, turf_removal_chance)
for(var/t = -r, t < r, t++)
affect_coord(x+t, y-r, ex_act_force, pull_chance, turf_removal_chance)
affect_coord(x-t, y+r, ex_act_force, pull_chance, turf_removal_chance)
affect_coord(x+r, y+t, ex_act_force, pull_chance, turf_removal_chance)
affect_coord(x-r, y-t, ex_act_force, pull_chance, turf_removal_chance)
/obj/effect/anomaly/bhole/proc/affect_coord(x, y, ex_act_force, pull_chance, turf_removal_chance)
//Get turf at coordinate
var/turf/T = locate(x, y, z)
if(isnull(T))
return
//Pulling and/or ex_act-ing movable atoms in that turf
if(prob(pull_chance))
for(var/obj/O in T.contents)
if(O.anchored)
switch(ex_act_force)
if(EXPLODE_DEVASTATE)
SSexplosions.high_mov_atom += O
if(EXPLODE_HEAVY)
SSexplosions.med_mov_atom += O
if(EXPLODE_LIGHT)
SSexplosions.low_mov_atom += O
else
step_towards(O,src)
for(var/mob/living/M in T.contents)
step_towards(M,src)
//Damaging the turf
if( T && prob(turf_removal_chance) )
switch(ex_act_force)
if(EXPLODE_DEVASTATE)
SSexplosions.highturf += T
if(EXPLODE_HEAVY)
SSexplosions.medturf += T
if(EXPLODE_LIGHT)
SSexplosions.lowturf += T
/obj/effect/anomaly/bioscrambler
name = "bioscrambler anomaly"
icon_state = "bioscrambler"
aSignal = /obj/item/assembly/signaler/anomaly/bioscrambler
immortal = TRUE
/// Cooldown for every anomaly pulse
COOLDOWN_DECLARE(pulse_cooldown)
/// How many seconds between each anomaly pulses
var/pulse_delay = 15 SECONDS
/// Range of the anomaly pulse
var/range = 5
///Lists for zones and bodyparts to swap and randomize
var/static/list/zones = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
var/static/list/chests
var/static/list/heads
var/static/list/l_arms
var/static/list/r_arms
var/static/list/l_legs
var/static/list/r_legs
/obj/effect/anomaly/bioscrambler/Initialize(mapload, new_lifespan, drops_core)
. = ..()
if(!chests)
chests = typesof(/obj/item/bodypart/chest)
if(!heads)
heads = typesof(/obj/item/bodypart/head)
if(!l_arms)
l_arms = typesof(/obj/item/bodypart/l_arm)
if(!r_arms)
r_arms = typesof(/obj/item/bodypart/r_arm)
if(!l_legs)
l_legs = typesof(/obj/item/bodypart/l_leg)
if(!r_legs)
r_legs = typesof(/obj/item/bodypart/r_leg)
/obj/effect/anomaly/bioscrambler/anomalyEffect(delta_time)
. = ..()
if(!COOLDOWN_FINISHED(src, pulse_cooldown))
return
COOLDOWN_START(src, pulse_cooldown, pulse_delay)
swap_parts(range)
/obj/effect/anomaly/bioscrambler/proc/swap_parts(swap_range)
for(var/mob/living/carbon/nearby in range(swap_range, src))
if(nearby.run_armor_check(attack_flag = BIO, absorb_text = "Your armor protects you from [src]!") >= 100)
continue //We are protected
var/picked_zone = pick(zones)
var/obj/item/bodypart/picked_user_part = nearby.get_bodypart(picked_zone)
var/obj/item/bodypart/picked_part
switch(picked_zone)
if(BODY_ZONE_HEAD)
picked_part = pick(heads)
if(BODY_ZONE_CHEST)
picked_part = pick(chests)
if(BODY_ZONE_L_ARM)
picked_part = pick(l_arms)
if(BODY_ZONE_R_ARM)
picked_part = pick(r_arms)
if(BODY_ZONE_L_LEG)
picked_part = pick(l_legs)
if(BODY_ZONE_R_LEG)
picked_part = pick(r_legs)
var/obj/item/bodypart/new_part = new picked_part()
new_part.replace_limb(nearby, TRUE)
if(picked_user_part)
qdel(picked_user_part)
nearby.update_body(TRUE)
balloon_alert(nearby, "something has changed about you")
/obj/effect/anomaly/hallucination
name = "hallucination anomaly"
icon_state = "hallucination"
aSignal = /obj/item/assembly/signaler/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)
/obj/effect/anomaly/hallucination/proc/hallucination_pulse(turf/open/location, range)
for(var/mob/living/carbon/human/near in view(location, range))
// If they are immune to hallucinations.
if (HAS_TRAIT(near, TRAIT_MADNESS_IMMUNE) || (near.mind && HAS_TRAIT(near.mind, TRAIT_MADNESS_IMMUNE)))
continue
// Blind people don't get hallucinations.
if (near.is_blind())
continue
// Everyone else gets hallucinations.
var/dist = sqrt(1 / max(1, get_dist(near, location)))
near.hallucination += 50 * dist
near.hallucination = clamp(near.hallucination, 0, 150)
var/list/messages = list(
"You feel your conscious mind fall apart!",
"Reality warps around you!",
"Something's wispering around you!",
"You are going insane!",
)
to_chat(near, span_warning(pick(messages)))
/////////////////////
/obj/effect/anomaly/dimensional
name = "dimensional anomaly"
icon_state = "dimensional"
aSignal = /obj/item/assembly/signaler/anomaly/dimensional
immortal = TRUE
immobile = TRUE
/// Range of effect, if left alone anomaly will convert a 2(range)+1 squared area.
var/range = 3
/// List of turfs this anomaly will try to transform before relocating
var/list/turf/target_turfs = new()
/// Current anomaly 'theme', dictates what tiles to create.
var/datum/dimension_theme/theme
/// Effect displaying on the anomaly to represent the theme.
var/mutable_appearance/theme_icon
/obj/effect/anomaly/dimensional/Initialize(mapload, new_lifespan, drops_core)
. = ..()
overlays += mutable_appearance('icons/effects/effects.dmi', "dimensional_overlay")
animate(src, transform = matrix()*0.85, time = 3, loop = -1)
animate(transform = matrix(), time = 3, loop = -1)
/obj/effect/anomaly/dimensional/anomalyEffect(delta_time)
. = ..()
transmute_area()
/**
* Transforms a turf in our prepared area.
*/
/obj/effect/anomaly/dimensional/proc/transmute_area()
if (!theme)
prepare_area()
if (!target_turfs.len)
relocate()
return
var/turf/affected_turf = target_turfs[1]
new /obj/effect/temp_visual/transmute_tile_flash(affected_turf)
theme.apply_theme(affected_turf)
target_turfs.Remove(affected_turf)
/**
* Prepare a new area for transformation into a new theme.
*/
/obj/effect/anomaly/dimensional/proc/prepare_area()
var/datum/dimension_theme/themes = new()
theme = themes.get_random_theme()
apply_theme_icon()
target_turfs = new()
var/list/turfs = spiral_range_turfs(range, src)
for (var/turf/turf in turfs)
if (theme.can_convert(turf))
target_turfs.Add(turf)
/**
* Applies an overlay icon based on the current theme.
*/
/obj/effect/anomaly/dimensional/proc/apply_theme_icon()
overlays -= theme_icon
theme_icon = mutable_appearance(theme.icon, theme.icon_state, FLOAT_LAYER - 1, appearance_flags = appearance_flags | RESET_TRANSFORM)
theme_icon.blend_mode = BLEND_INSET_OVERLAY
overlays += theme_icon
/**
* Moves the anomaly somewhere else and announces it.
*/
/obj/effect/anomaly/dimensional/proc/relocate()
var/datum/anomaly_placer/placer = new()
var/area/new_area = placer.findValidArea()
var/turf/new_turf = placer.findValidTurf(new_area)
priority_announce("Dimensional instability relocated. Expected location: [new_area.name].", "Anomaly Alert")
src.forceMove(new_turf)
prepare_area()
/obj/effect/temp_visual/transmute_tile_flash
icon = 'icons/effects/effects.dmi'
icon_state = "shield-flash"
duration = 3
#undef ANOMALY_MOVECHANCE