[MIRROR] Anomaly Harvesting (#12512)

Co-authored-by: Guti <32563288+TheCaramelion@users.noreply.github.com>
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2026-03-11 22:18:58 +01:00
committed by GitHub
co-authored by Guti Kashargul
parent bee24c2091
commit 3b6d04550c
50 changed files with 1484 additions and 94 deletions
@@ -19,6 +19,10 @@
var/immortal = FALSE
var/move_chance = ANOMALY_MOVECHANCE
// Anomaly harvesting stuff
var/datum/anomaly_stats/stats
var/danger_mult = 1
/obj/effect/anomaly/Initialize(mapload, new_lifespan, drops_core = TRUE)
. = ..()
@@ -51,6 +55,7 @@
/obj/effect/anomaly/process(seconds_per_tick)
anomalyEffect(seconds_per_tick)
anomalyPulse()
if(death_time < world.time && !immortal)
if(loc)
detonate()
@@ -60,12 +65,27 @@
STOP_PROCESSING(SSobj, src)
QDEL_NULL(countdown)
QDEL_NULL(anomaly_core)
if(stats)
QDEL_NULL(stats)
return ..()
/obj/effect/anomaly/proc/anomalyEffect(seconds_per_tick)
if(prob(move_chance))
if(prob(move_chance) && !locate(/obj/effect/suspension_field) in get_turf(src))
move_anomaly()
// Used in anomaly harvesting - Normal anomalies shouldn't pulse
/obj/effect/anomaly/proc/anomalyPulse()
if(!stats)
return FALSE
if(world.time < stats.next_activation)
return FALSE
stats.pulse_effect()
if(QDELETED(src))
return FALSE
stats.next_activation = world.time + rand(stats.min_activation, stats.max_activation)
return TRUE
/obj/effect/anomaly/proc/move_anomaly()
step(src, pick(GLOB.alldirs))
@@ -85,21 +105,56 @@
anomaly_core = null
qdel(src)
/obj/effect/anomaly/proc/stabilize(anchor = FALSE, has_core = TRUE)
/obj/effect/anomaly/proc/stabilize(anchor = FALSE, has_core = TRUE, add_stats = FALSE)
immortal = TRUE
name = (has_core ? "stable " : "hollow ") + name
if(!has_core)
QDEL_NULL(anomaly_core)
if(anchor)
move_chance = 0
if(!stats && add_stats)
stats = new /datum/anomaly_stats
stats.attached_anomaly = WEAKREF(src)
stats.calculate_points()
density = TRUE
return
/obj/effect/anomaly/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/analyzer))
if(anomaly_core)
to_chat(user, span_notice("Analyzing... [src]'s stabilized field is fluctuating along frequency [format_frequency(anomaly_core.frequency)], code [anomaly_core.code]."))
return TRUE
if(istype(I, /obj/item/anomaly_scanner))
var/obj/item/anomaly_scanner/scanner = I
if(stats)
if(!do_after(user, 1 SECOND, src))
return
scanner.buffered_anomaly = WEAKREF(src)
scanner.tgui_interact(user)
return TRUE
return ..()
/obj/effect/anomaly/bullet_act(obj/item/projectile/proj)
if(stats && istype(stats.modifier, /datum/anomaly_modifiers/reflective) && prob(stats.severity/1.5))
balloon_alert_visible("reflected!")
var/new_x = proj.x = pick(0, 0, 0, -1, 1, -2, 2)
var/new_y = proj.y = pick(0, 0, 0, -1, 1, -2, 2)
var/turf/curloc = get_step(proj, get_dir(proj, proj.starting))
proj.penetrating += 1
proj.redirect(new_x, new_y, curloc, null)
return FALSE
if(istype(proj, /obj/item/projectile/energy/anomaly))
var/obj/item/projectile/energy/anomaly/anom_proj = proj
if(stats)
stats.particle_hit(anom_proj.particle_type)
return FALSE
/proc/generate_anomaly(turf/anomalycenter, type = FLUX_ANOMALY, anomalyrange = 5, has_changed_lifespan = TRUE, drops_core = TRUE)
var/turf/local_turf = pick(RANGE_TURFS(anomalyrange, anomalycenter))
if(!local_turf)
@@ -5,6 +5,7 @@
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
layer = ABOVE_MOB_LAYER
lifespan = ANOMALY_COUNTDOWN_TIMER * 2
danger_mult = 1.1
/// Who are we moving towards?
var/datum/weakref/pursuit_target
@@ -21,6 +22,8 @@
/obj/effect/anomaly/bioscrambler/anomalyEffect(seconds_per_tick)
. = ..()
if(stats)
return
if(!COOLDOWN_FINISHED(src, pulse_cooldown))
return
@@ -87,3 +90,15 @@
/obj/effect/anomaly/bioscrambler/detonate()
COOLDOWN_RESET(src, pulse_cooldown)
anomalyEffect()
/obj/effect/anomaly/bioscrambler/anomalyPulse()
if(!..())
return
switch(stats.severity)
if(0 to 15)
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread
sparks.set_up(3, 1, src)
sparks.start()
else
anomalyEffect()
@@ -7,6 +7,7 @@
var/teleport_range = 1
///Distance we can teleport someone passively
var/teleport_distance = 4
danger_mult = 1.1
/obj/effect/anomaly/bluespace/Initialize(mapload, new_lifespan)
. = ..()
@@ -92,3 +93,43 @@
color = COLOR_BLUE_LIGHT
duration = 1 SECONDS
amount_to_scale = 5
/obj/effect/anomaly/bluespace/anomalyPulse()
if(!..())
return
switch(stats.severity)
if(0 to 15)
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread
sparks.set_up(3, 1, src)
sparks.start()
if(16 to 33)
pulse_teleport(4, 1)
if(34 to 65)
pulse_teleport(5, 2)
else
pulse_teleport(6, 3)
/obj/effect/anomaly/bluespace/proc/pulse_teleport(range, count)
var/list/possible = list()
for(var/obj/item/radio/beacon in GLOB.all_beacons)
var/turf/turf = get_turf(beacon)
if(!turf)
continue
if(!check_teleport_valid(src, turf))
continue
possible += beacon
var/chosen = pick(possible)
var/list/things = list()
for(var/atom/movable/thing in orange(range, get_turf(src)))
if(istype(thing, /obj/item/radio/beacon) || iseffect(thing) || isEye(thing))
continue
if(thing.anchored)
continue
things += thing
for(var/i in 1 to count)
do_teleport(pick(things), get_turf(chosen))
@@ -4,6 +4,7 @@
anomaly_core = /obj/item/assembly/signaler/anomaly/dimensional
lifespan = ANOMALY_COUNTDOWN_TIMER * 20
move_chance = 0
danger_mult = 0.8
/// 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
@@ -40,6 +41,8 @@
/obj/effect/anomaly/dimensional/proc/transmute_area()
if(!theme)
prepare_area()
if(stats)
return
if(!target_turfs.len)
if(teleports_left <= 0 && !immortal)
detonate()
@@ -88,3 +91,26 @@
icon = 'icons/effects/effects.dmi'
icon_state = "shieldsparkles"
duration = 3
/obj/effect/anomaly/dimensional/anomalyPulse()
if(!..())
return
switch(stats.severity)
if(0 to 15)
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread
sparks.set_up(3, 1, src)
sparks.start()
if(16 to 33)
pulse_change(2)
if(34 to 65)
pulse_change(4)
else
pulse_change(8)
/obj/effect/anomaly/dimensional/proc/pulse_change(count)
var/turf/turf
for(var/i in 1 to count)
turf = pick(target_turfs)
theme.apply_theme(turf, show_effect = TRUE)
@@ -6,6 +6,7 @@
pass_flags = PASSTABLE | PASSGRILLE
layer = ABOVE_MOB_LAYER
lifespan = ANOMALY_COUNTDOWN_TIMER * 1.5
danger_mult = 0.75
move_chance = 80
@@ -71,3 +72,15 @@
/obj/effect/anomaly/dust/detonate()
COOLDOWN_RESET(src, pulse_cooldown)
anomalyEffect()
/obj/effect/anomaly/dust/anomalyPulse()
if(!..())
return
switch(stats.severity)
if(0 to 15)
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread
sparks.set_up(3, 1, src)
sparks.start()
else
anomalyEffect()
@@ -61,3 +61,24 @@
/obj/effect/anomaly/flux/minor/Initialize(mapload, new_lifespan, emp_zap = FLUX_NO_EMP)
return ..()
/obj/effect/anomaly/flux/anomalyPulse()
if(!..())
return
switch(stats.severity)
if(0 to 15)
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread
sparks.set_up(3, 1, src)
sparks.start()
if(16 to 33)
tesla_zap(src, 2, 1000, FALSE, FALSE)
if(34 to 65)
tesla_zap(src, 3, 1000, FALSE, FALSE)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(tesla_zap), src, 3, 1500, FALSE, FALSE), 3 SECONDS)
else
tesla_zap(src, 4, 1000, FALSE, TRUE)
addtimer(CALLBACK(src, PROC_REF(highSevPulse)), 3 SECONDS)
/obj/effect/anomaly/flux/proc/highSevPulse()
tesla_zap(src, 4, 1250, FALSE, FALSE)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(tesla_zap), src, 4, 1500, FALSE, FALSE), 3 SECONDS)
@@ -16,6 +16,8 @@
/obj/effect/anomaly/grav/anomalyEffect(seconds_per_tick)
..()
if(stats)
return
boing = TRUE
for(var/obj/O in orange(4, src))
if(!O.anchored)
@@ -93,3 +95,14 @@
*/
/obj/effect/temp_visual/circle_wave/gravity
color = COLOR_NAVY
/obj/effect/anomaly/grav/anomalyPulse()
if(!..())
return
switch(stats.severity)
if(0 to 15)
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread
sparks.set_up(3, 1, src)
sparks.start()
else
anomalyEffect()
@@ -2,6 +2,7 @@
name = "hallucination anomaly"
icon_state = "hallucination"
anomaly_core = /obj/item/assembly/signaler/anomaly/hallucination
danger_mult = 0.9
/// Time passed since the last effect, increased by seconds_per_tick of the SSobj
var/ticks = 0
/// How many seconds between each small hallucination pulses
@@ -23,6 +24,8 @@
/obj/effect/anomaly/hallucination/anomalyEffect(seconds_per_tick)
. = ..()
if(stats)
return
ticks += seconds_per_tick
if(ticks < release_delay)
return
@@ -109,3 +112,14 @@
///Subtype for the SM that doesn't spawn decoys, because otherwise the whole area gets flooded with dummies.
/obj/effect/anomaly/hallucination/supermatter
spawn_decoys = FALSE
/obj/effect/anomaly/hallucination/anomalyPulse()
if(!..())
return
switch(stats.severity)
if(0 to 15)
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread
sparks.set_up(3, 1, src)
sparks.start()
else
anomalyEffect()
@@ -1,6 +1,7 @@
/obj/effect/anomaly/pyro
name = "pyroclastic anomaly"
icon_state = "pyroclastic"
danger_mult = 1.3
var/ticks = 0
/// How many seconds between each gas release
var/releasedelay = 15 SECONDS
@@ -12,23 +13,40 @@
/obj/effect/anomaly/pyro/anomalyEffect(seconds_per_tick)
..()
if(stats)
return
ticks += seconds_per_tick
if(ticks < releasedelay)
return FALSE
else
ticks -= releasedelay
var/turf/simulated/floor/tile = get_turf(src)
if(istype(tile))
tile.assume_gas(GAS_PHORON, 10, T20C)
tile.hotspot_expose(700, 400)
burst()
return TRUE
/obj/effect/anomaly/pyro/detonate()
/obj/effect/anomaly/pyro/proc/burst(moles = 10, temperature = 700, volume = 400)
var/turf/simulated/floor/tile = get_turf(src)
if(istype(tile))
tile.assume_gas(GAS_PHORON, 10, T20C)
tile.hotspot_expose(700, 400)
tile.assume_gas(GAS_PHORON, moles, T20C)
tile.hotspot_expose(temperature, volume)
/obj/effect/anomaly/pyro/detonate()
burst()
var/new_colour = pick(/mob/living/simple_mob/slime/xenobio/red, /mob/living/simple_mob/slime/xenobio/orange)
var/mob/living/simple_mob/slime/xenobio/pyro = new new_colour(tile)
var/mob/living/simple_mob/slime/xenobio/pyro = new new_colour(get_turf(src))
pyro.enrage()
/obj/effect/anomaly/pyro/anomalyPulse()
if(!..())
return
switch(stats.severity)
if(0 to 15)
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread
sparks.set_up(3, 1, src)
sparks.start()
if(16 to 33)
burst()
if(34 to 65)
burst(20, 900, 600)
else
burst(30, 110, 800)
@@ -44,6 +44,12 @@
addtimer(CALLBACK(src, PROC_REF(start_weather)), telegraph, TIMER_DELETE_ME)
/obj/effect/anomaly/weather/proc/add_turfs(list/turf/to_add)
for(var/turf/turf in to_add)
if(isspace(turf))
continue
affected_turfs.Add(turf)
/obj/effect/anomaly/weather/proc/find_adjacent_impacted_area(check_dir)
var/limit = 10
var/turf/next_turf = get_step(src, check_dir)
@@ -73,6 +79,9 @@
turf = GetBelow(turf)
selected_weather.affect_turf(turf)
if(stats)
return
for(var/mob/mob as anything in GLOB.player_list)
if(get_area(mob) in affected_areas)
selected_weather.hear_sounds(mob, TRUE)
@@ -104,13 +113,13 @@
if(selected_weather.sounds)
selected_weather.loop_sounds.stop()
for(var/turf/turf in affected_turfs)
selected_weather.remove_from_turf(turf)
for(var/area in affected_areas)
for(var/mob/mob in area)
selected_weather.hear_sounds(mob, FALSE)
for(var/turf/turf in affected_turfs)
selected_weather.remove_from_turf(turf)
/obj/effect/anomaly/weather/Destroy()
clear_weather()
. = ..()
@@ -118,6 +127,47 @@
/obj/effect/anomaly/weather/proc/update_reagent(reagent)
selected_weather.update_reagent(reagent)
/obj/effect/anomaly/weather/anomalyPulse()
if(!..())
return
switch(stats.severity)
if(0 to 15)
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread
sparks.set_up(3, 1, src)
sparks.start()
affected_areas.Cut()
affected_turfs.Cut()
if(16 to 33)
clear_weather()
affected_turfs.Cut()
if(!istype(selected_weather, /datum/anomalous_weather/rain))
selected_weather = new /datum/anomalous_weather/rain
update_reagent(REAGENT_ID_WATER)
add_turfs(circleviewturfs(src, 3))
start_weather()
if(34 to 65)
clear_weather()
affected_turfs.Cut()
if(!istype(selected_weather, /datum/anomalous_weather/rain))
selected_weather = new /datum/anomalous_weather/rain
update_reagent(pick(REAGENT_ID_WATER, REAGENT_ID_ICE, REAGENT_ID_ORANGEJUICE))
add_turfs(circlerangeturfs(src, 4))
start_weather()
else
clear_weather()
affected_turfs.Cut()
if(!istype(selected_weather, /datum/anomalous_weather/rain/storm))
selected_weather = new /datum/anomalous_weather/rain/storm
var/reagent_id = pick(SSchemistry.chemical_reagents)
if(reagent_id in GLOB.obtainable_chemical_blacklist)
update_reagent(REAGENT_ID_WATER) // You get WATER.
else
update_reagent(reagent_id)
add_turfs(circlerangeturfs(src, 5))
start_weather()
/obj/effect/anomaly/weather/rain
selected_weather = /datum/anomalous_weather/rain
@@ -57,7 +57,7 @@
T.vis_contents -= visuals
/datum/anomalous_weather/proc/affect_turf(turf/to_affect)
if(iswall(to_affect))
if(iswall(to_affect) || isopenturf(to_affect))
return
for(var/atom/thing in to_affect)
@@ -130,7 +130,7 @@
drench_message = "Rain falls on you, drenching you in water."
/datum/anomalous_weather/rain/do_special(turf/simulated/T)
if(prob(2))
if(prob(2) && !isopenturf(T))
T.wet_floor(1)
/datum/anomalous_weather/rain/blood
@@ -153,9 +153,9 @@
drench_message = "Rain falls on you, completely soaking you."
/datum/anomalous_weather/rain/storm/do_special(turf/simulated/T)
if(prob(3))
if(prob(3) && !isopenturf(T))
T.wet_floor(1)
if(prob(0.025))
if(prob(0.025) && !isopenturf(T))
lightning_strike(T)
/datum/anomalous_weather/rain/acid
@@ -0,0 +1,191 @@
/datum/anomaly_stats
var/severity = 1
var/stability
var/max_health
var/danger_type
var/unstable_type
var/containment_type
var/transformation_type
var/datum/anomaly_modifiers/modifier
var/datum/weakref/attached_anomaly
var/datum/weakref/attached_harvester
var/next_activation
// Total of points we'll get once the anomaly does a pulse
var/points
var/curr_health
var/flags
var/min_activation = 45 SECONDS
var/max_activation = 90 SECONDS
/datum/anomaly_stats/New()
randomize_particle_types()
severity = rand(5, 15)
max_health = rand(50, 150)
curr_health = max_health
stability = ANOMALY_STABLE
/datum/anomaly_stats/Destroy(force)
QDEL_NULL(modifier)
return ..()
/datum/anomaly_stats/proc/randomize_particle_types()
var/list/particles = list(ANOMALY_PARTICLE_SIGMA, ANOMALY_PARTICLE_DELTA, ANOMALY_PARTICLE_ZETA, ANOMALY_PARTICLE_EPSILON)
particles = shuffle(particles)
danger_type = particles[1]
unstable_type = particles[2]
containment_type = particles[3]
transformation_type = particles[4]
/datum/anomaly_stats/proc/calculate_points()
var/total = 10
var/obj/effect/anomaly/anomaly = attached_anomaly.resolve()
if(!anomaly)
return
total += severity/5
total *= curr_health/max_health
total *= anomaly.danger_mult
if(modifier)
total *= modifier.get_value()
points = total
return points
/datum/anomaly_stats/proc/particle_hit(particle)
// I don't really like this, but switch() expects a constant expression
if(particle == danger_type)
update_severity(1, 5)
return
else if(particle == unstable_type)
update_state(TRUE)
return
else if(particle == containment_type)
update_state(FALSE)
update_health(-1, -10)
return
else if(particle == transformation_type)
update_modifiers()
return
return
/datum/anomaly_stats/proc/update_severity(lower, upper)
var/obj/effect/anomaly/anom = attached_anomaly.resolve()
if(!istype(anom))
attached_anomaly = null
return
var/sev_change = rand(lower, upper)
severity += sev_change
if(severity >= 100)
kill_anomaly(TRUE)
return
var/scale_factor = 1.0+(severity/100)
var/matrix/M = matrix()
M.Scale(scale_factor, scale_factor)
animate(anom, transform = M, time = 1 SECOND)
// These don't have the wibbly filter
if(!istype(anom, /obj/effect/anomaly/bioscrambler) && !istype(anom, /obj/effect/anomaly/dimensional))
apply_wibbly_filters(anom)
calculate_points()
return
/datum/anomaly_stats/proc/update_health(lower, upper)
var/health_change = rand(lower, upper)
curr_health += health_change
if(curr_health <= 0)
kill_anomaly(FALSE)
return
calculate_points()
return
/datum/anomaly_stats/proc/kill_anomaly(critical)
var/obj/effect/anomaly/anom = attached_anomaly.resolve()
if(!istype(anom))
attached_anomaly = null
return
if(critical)
anom.detonate()
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(5, 1, src)
s.start()
QDEL_NULL(anom)
return
/datum/anomaly_stats/proc/update_state(unstable)
var/obj/effect/anomaly/anom = attached_anomaly.resolve()
if(!istype(anom))
return
if(locate(/obj/effect/suspension_field) in get_turf(anom))
return
switch(stability)
if(ANOMALY_STABLE)
if(unstable && prob(15))
stability = ANOMALY_GROWING
else if(prob(15))
stability = ANOMALY_DECAYING
return
if(ANOMALY_GROWING)
if(!unstable && prob(15))
stability = ANOMALY_STABLE
return
if(ANOMALY_DECAYING)
if(unstable && prob(15))
stability = ANOMALY_STABLE
else
return
return
/datum/anomaly_stats/proc/update_modifiers()
var/picked_mod = pick(subtypesof(/datum/anomaly_modifiers))
// Tough luck. No mod for you.
if(istype(modifier, picked_mod))
return
if(modifier)
modifier.on_remove(attached_anomaly)
modifier = null
modifier = new picked_mod
modifier.on_add(attached_anomaly)
calculate_points()
return
/datum/anomaly_stats/proc/get_activation_countdown()
return (next_activation - world.time)/10
/datum/anomaly_stats/proc/pulse_effect()
if(stability == ANOMALY_DECAYING)
update_health(-1, -10)
else if(stability == ANOMALY_GROWING)
update_severity(1, 10)
if(prob(5))
if(stability == ANOMALY_STABLE)
stability = pick(ANOMALY_DECAYING, ANOMALY_GROWING)
else
stability = ANOMALY_STABLE
if(attached_harvester)
var/obj/machinery/anomaly_harvester/harvester = attached_harvester.resolve()
if(!istype(harvester))
return
harvester.add_points(points)
@@ -0,0 +1,87 @@
/datum/anomaly_modifiers
var/name
var/description
var/value
var/obj/effect/anomaly/attached_anomaly = null
/datum/anomaly_modifiers/proc/get_description()
return description
/datum/anomaly_modifiers/proc/get_value()
return value
/datum/anomaly_modifiers/proc/on_add(datum/weakref/anomaly)
attached_anomaly = anomaly.resolve()
if(!istype(attached_anomaly))
return FALSE
return TRUE
/datum/anomaly_modifiers/proc/on_remove(datum/weakref/anomaly)
attached_anomaly = anomaly.resolve()
if(!istype(attached_anomaly))
return FALSE
return TRUE
/datum/anomaly_modifiers/reflective
name = "Reflective"
description = "A protective coating was detected."
value = 1.2
/datum/anomaly_modifiers/invisible
name = "Invisible"
description = "Light wave distortion was detected."
value = 1.5
/datum/anomaly_modifiers/invisible/on_add(datum/weakref/anomaly)
if(!..())
return
addtimer(CALLBACK(attached_anomaly, TYPE_PROC_REF(/atom/movable, cloak)), 2 SECONDS)
/datum/anomaly_modifiers/invisible/on_remove(datum/weakref/anomaly)
if(!..())
return
addtimer(CALLBACK(attached_anomaly, TYPE_PROC_REF(/atom/movable, uncloak)), 2 SECONDS)
/*
/datum/anomaly_modifiers/hidden
name = "Hidden"
description = "Interference detected. Some data cannot be read."
value = 0.15
*/
/datum/anomaly_modifiers/move
name = "Move"
description = "Anomalous anchoring could not be detected."
value = 1.4
/datum/anomaly_modifiers/move/on_add(datum/weakref/anomaly)
if(!..())
return
attached_anomaly.move_chance = ANOMALY_MOVECHANCE
/datum/anomaly_modifiers/move/on_remove(datum/weakref/anomaly)
if(!..())
return
attached_anomaly.move_chance = 0
/datum/anomaly_modifiers/fast
name = "Faster Pulses"
description = "Anomalous pulses are more common."
value = 0.9
/datum/anomaly_modifiers/fast/on_add(datum/weakref/anomaly)
if(!..())
return
var/datum/anomaly_stats/stats = attached_anomaly.stats
stats.min_activation = 25 SECONDS
stats.max_activation = 45 SECONDS
/datum/anomaly_modifiers/fast/on_remove(datum/weakref/anomaly)
if(!..())
return
var/datum/anomaly_stats/stats = attached_anomaly.stats
stats.min_activation = initial(stats.min_activation)
stats.max_activation = initial(stats.max_activation)
@@ -1,43 +0,0 @@
/obj/item/anomaly_neutralizer
name = "anomaly neutralizer"
desc = "A one-use device capable of instantly neutralizing anomalous or otherworldly entities."
icon = 'icons/obj/devices/tool.dmi'
icon_state = "neutralyzer"
w_class = ITEMSIZE_SMALL
slot_flags = SLOT_BELT
item_flags = NOBLUDGEON
/obj/item/anomaly_neutralizer/Initialize(mapload)
. = ..()
AddComponent(/datum/component/effect_remover, \
success_feedback = "You neutralize %THEEFFECT with %THEWEAPON, frying its circuitry in the process.", \
on_clear_callback = CALLBACK(src, PROC_REF(on_anomaly_neutralized)), \
effects_we_clear = list(/obj/effect/anomaly))
/obj/item/anomaly_neutralizer/proc/on_anomaly_neutralized(obj/effect/anomaly/target, mob/living/user)
target.anomalyNeutralize()
on_use(target, user)
/obj/item/anomaly_neutralizer/proc/on_use(obj/effect/target, mob/living/user)
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread
sparks.set_up(3, 1, src)
sparks.start()
qdel(src)
/obj/item/anomaly_releaser
icon = 'icons/obj/devices/syndie_gadget.dmi'
icon_state = "anomaly_releaser"
name = "anomaly releaser"
desc = "Single-use injector that releases and stabilizes anomalies by injecting an unknown substance."
throwforce = 0
w_class = ITEMSIZE_SMALL
throw_speed = 3
throw_range = 5
///icon state after being used up
var/used_icon_state = "anomaly_releaser_used"
///are we used? if used we can't be used again
var/used = FALSE
///Can we be used infinitely?
var/infinite = FALSE
@@ -100,3 +100,10 @@
icon_state = "multitool"
toolspeed = 0.1
origin_tech = list(TECH_MAGNET = 5, TECH_ENGINEERING = 5)
// Alien multitool only has those icon states
/obj/item/multitool/alien/update_icon()
if(accepting_refs)
icon_state = "multitool_ref_scan"
return
icon_state = "multitool"
@@ -546,6 +546,29 @@
to_chat(R, span_notice("Letter compartment upgraded!"))
return TRUE
// Anomaly Gun
/obj/item/borg/upgrade/restricted/anomalygun
name = "robot mounted particle gun"
desc = "A anomaly particle gun adapted for installation in cyborgs, allows them to shoot different particles upon anomalies."
icon_state = "cyborg_upgrade2"
item_state = "cyborg_upgrade"
module_flags = BORG_MODULE_SCIENCE
require_module = TRUE
/obj/item/borg/upgrade/restricted/anomalygun/action(mob/user, mob/living/silicon/robot/R)
if(..()) return FALSE
if(!R.supports_upgrade(type))
generic_error(user, R, type)
return FALSE
if(R.has_restricted_upgrade(type))
generic_error(user, R, type)
return FALSE
R.module.modules += new/obj/item/gun/energy/anomaly/mounted(R.module)
return TRUE
/* ###############################################
# Unsorted section. All cargo modules go here.#
###############################################*/
@@ -0,0 +1,17 @@
#ifndef T_BOARD
#error T_BOARD macro is not defined but we need it!
#endif
/obj/item/circuitboard/anomaly_harvester
name = T_BOARD("anomaly harvester")
build_path = /obj/machinery/anomaly_harvester
board_type = new /datum/frame/frame_types/machine
origin_tech = list(TECH_DATA = 1)
req_components = list(
/obj/item/stock_parts/matter_bin = 2,
/obj/item/stock_parts/manipulator = 1,
/obj/item/stock_parts/micro_laser = 1,
/obj/item/stack/cable_coil = 5,
/obj/item/stack/material/phoron = 5,
/obj/item/stack/material/diamond = 1
)
@@ -86,7 +86,9 @@
/obj/item/stack/nanopaste,
/obj/item/geiger,
/obj/item/reagent_scanner,
/obj/item/lightpainter
/obj/item/lightpainter,
/obj/item/anomaly_releaser,
/obj/item/anomaly_scanner
)
/obj/item/storage/belt/utility/full