Files
Bubberstation/code/game/objects/effects/anomalies.dm
T
4d6a8bc537 515 Compatibility (#71161)
Makes the code compatible with 515.1594+

Few simple changes and one very painful one.
Let's start with the easy:
* puts call behind `LIBCALL` define, so call_ext is properly used in 515
* Adds `NAMEOF_STATIC(_,X)` macro for nameof in static definitions since
src is now invalid there.
* Fixes tgui and devserver. From 515 onward the tmp3333{procid} cache
directory is not appened to base path in browser controls so we don't
check for it in base js and put the dev server dummy window file in
actual directory not the byond root.
* Renames the few things that had /final/ in typepath to ultimate since
final is a new keyword

And the very painful change:
`.proc/whatever` format is no longer valid, so we're replacing it with
new nameof() function. All this wrapped in three new macros.
`PROC_REF(X)`,`TYPE_PROC_REF(TYPE,X)`,`GLOBAL_PROC_REF(X)`. Global is
not actually necessary but if we get nameof that does not allow globals
it would be nice validation.
This is pretty unwieldy but there's no real alternative.
If you notice anything weird in the commits let me know because majority
was done with regex replace.

@tgstation/commit-access Since the .proc/stuff is pretty big change.

Co-authored-by: san7890 <the@san7890.com>
Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com>
2022-11-15 03:50:11 +00:00

646 lines
19 KiB
Plaintext

//Anomalies, used for events. Note that these DO NOT work by themselves; their procs are called by the event datum.
/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
if(immortal)
return
countdown.start()
/obj/effect/anomaly/vv_edit_var(vname, vval)
. = ..()
if(vname == NAMEOF(src, immortal))
if(vval)
countdown.stop()
else
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_REF(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/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(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_REF(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_REF(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, src, 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")
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, TYPE_PROC_REF(/mob/, 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_REF(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/arm/left)
if(!r_arms)
r_arms = typesof(/obj/item/bodypart/arm/right)
if(!l_legs)
l_legs = typesof(/obj/item/bodypart/leg/left)
if(!r_legs)
r_legs = typesof(/obj/item/bodypart/leg/right)
/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
/// Messages sent to people feeling the pulses
var/static/list/messages = list(
span_warning("You feel your conscious mind fall apart!"),
span_warning("Reality warps around you!"),
span_warning("Something's wispering around you!"),
span_warning("You are going insane!"),
)
/obj/effect/anomaly/hallucination/anomalyEffect(delta_time)
. = ..()
ticks += delta_time
if(ticks < release_delay)
return
ticks -= release_delay
if(!isturf(loc))
return
visible_hallucination_pulse(
center = get_turf(src),
radius = 5,
hallucination_duration = 50 SECONDS,
hallucination_max_duration = 300 SECONDS,
optional_messages = messages,
)
/obj/effect/anomaly/hallucination/detonate()
if(!isturf(loc))
return
visible_hallucination_pulse(
center = get_turf(src),
radius = 10,
hallucination_duration = 50 SECONDS,
hallucination_max_duration = 300 SECONDS,
optional_messages = 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