mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2026-08-29 16:19:36 +01:00
[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:
co-authored by
Guti
Kashargul
parent
bee24c2091
commit
3b6d04550c
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user