Ports TG Anomalies (#18794)

* Anomalies

* Tiles

* Automatic changelog compile [ci skip]

* Bioscramble

* Reactive armour

* Reactive

* .

* Automatic changelog compile [ci skip]

* hallucination

* Snowy

* Bit of polishing

* change

* Cargo & Research

* fimx

* Anomaly stuffs

* Bioscrambler + Terrible fire armour

* Adjustment

* Less spammy

* Changes

---------

Co-authored-by: vorestation-ci[bot] <199609141+vorestation-ci[bot]@users.noreply.github.com>
This commit is contained in:
Guti
2025-12-11 06:47:48 -05:00
committed by GitHub
co-authored by vorestation-ci[bot] <199609141+vorestation-ci[bot]@users.noreply.github.com>
parent 81acf7df5c
commit d59ad9df0c
48 changed files with 1859 additions and 13 deletions
+47
View File
@@ -0,0 +1,47 @@
/**
* # Anomaly Defines
* This file contains defines for the random event anomaly subtypes.
*/
///Time in ticks before the anomaly goes poof/explodes depending on type.
#define ANOMALY_COUNTDOWN_TIMER (120 SECONDS)
/**
* Nuisance/funny anomalies
*/
///Time in seconds before anomaly spawns
#define ANOMALY_START_MEDIUM_TIME (6 EVENT_SECONDS)
///Time in seconds before anomaly is announced
#define ANOMALY_ANNOUNCE_MEDIUM_TIME (2 EVENT_SECONDS)
///Let them know how far away the anomaly is
#define ANOMALY_ANNOUNCE_MEDIUM_TEXT "long range scanners. Expected location:"
/**
* Chaotic but not harmful anomalies. Give the station a chance to find it on their own.
*/
///Time in seconds before anomaly spawns
#define ANOMALY_START_HARMFUL_TIME (2 EVENT_SECONDS)
///Time in seconds before anomaly is announced
#define ANOMALY_ANNOUNCE_HARMFUL_TIME (30 EVENT_SECONDS)
///Let them know how far away the anomaly is
#define ANOMALY_ANNOUNCE_HARMFUL_TEXT "localized scanners. Detected location:"
/**
* Anomalies that can fuck you up. Give them a bit of warning.
*/
///Time in seconds before anomaly spawns
#define ANOMALY_START_DANGEROUS_TIME (2 EVENT_SECONDS)
///Time in seconds before anomaly is announced
#define ANOMALY_ANNOUNCE_DANGEROUS_TIME (30 EVENT_SECONDS)
///Let them know how far away the anomaly is
#define ANOMALY_ANNOUNCE_DANGEROUS_TEXT "localized scanners. Detected location:"
#define GRAVITATIONAL_ANOMALY "gravitational_anomaly"
#define FLUX_ANOMALY "flux_anomaly"
#define PYRO_ANOMALY "pyro_anomaly"
#define BIOSCRAMBLER_ANOMALY "bioscrambler_anomaly"
#define HALLUCINATION_ANOMALY "hallucination_anomaly"
#define DIMENSIONAL_ANOMALY "dimensional_anomaly"
+2
View File
@@ -96,6 +96,8 @@
#define COLOR_NUKIES_YELLOW "#ffe900"
#define COLOR_DESATTI_PRPLOW "#3300cc"
#define COLOR_DESATTI_PRPHI "#6600cc"
#define COLOR_FULL_TONER_BLACK "#101010"
#define COLOR_VOID_PURPLE "#53277E"
#define PIPE_COLOR_GREY "#808080"
#define PIPE_COLOR_RED "#ff0000"
+2
View File
@@ -97,3 +97,5 @@ GLOBAL_VAR_INIT(refid_filter, TYPEID(filter(type="angular_blur")))
#define ismopable(A) (A && (A.plane <= OBJ_PLANE)) //If something can be cleaned by floor-cleaning devices such as mops or clean bots
#define isfloorturf(A) (istype(A, /turf/simulated/floor))
#define iseffect(O) (istype(O, /obj/effect))
+7
View File
@@ -237,3 +237,10 @@
return 1
else
return 0
/// Converts a probability/second chance to probability/seconds_per_tick chance
/// For example, if you want an event to happen with a 10% per second chance, but your proc only runs every 5 seconds, do `if(prob(100*SPT_PROB_RATE(0.1, 5)))`
#define SPT_PROB_RATE(prob_per_second, seconds_per_tick) (1 - (1 - (prob_per_second)) ** (seconds_per_tick))
/// Like SPT_PROB_RATE but easier to use, simply put `if(SPT_PROB(10, 5))`
#define SPT_PROB(prob_per_second_percent, seconds_per_tick) (prob(100*SPT_PROB_RATE((prob_per_second_percent)/100, (seconds_per_tick))))
+7
View File
@@ -0,0 +1,7 @@
///Defines for the different types of explosion a flux anomaly can have
#define FLUX_NO_EMP 0
#define FLUX_EMP 1
#define FLUX_LIGHT_EMP 2
/// Chance of anomalies moving every process tick
#define ANOMALY_MOVECHANCE 45
+2
View File
@@ -45,3 +45,5 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_TESLA_SHOCKIMMUNE "tesla_shock_immunity"
/// Do we show up as a changeling / the wrong body-mind pair to sleevmates
#define UNIQUE_MINDSTRUCTURE "unique_mindstructure"
// Owner is immune to hallucinations
#define TRAIT_MADNESS_IMMUNE "supermatter_madness_immune"
+1
View File
@@ -27,6 +27,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NOFIRE_SPREAD" = TRAIT_NOFIRE_SPREAD,
"TRAIT_NO_EXTINGUISH" = TRAIT_NO_EXTINGUISH,
"TRAIT_TESLA_SHOCKIMMUNE" = TRAIT_TESLA_SHOCKIMMUNE,
"TRAIT_MADNESS_IMMUNE" = TRAIT_MADNESS_IMMUNE,
),
/obj = list(
"TRAIT_CLIMBABLE" = TRAIT_CLIMBABLE,
+25 -1
View File
@@ -151,9 +151,17 @@
return 0
//Checks for specific paths in a list
/proc/is_path_in_list(var/atom/A, var/list/L)
/**
* Arguments:
* A : Typepath to check
* L : A list of typepath to check A against
* zebra: Wether to use the value of the path in the list instead of just returning TRUE when a match is found
*/
/proc/is_path_in_list(var/atom/A, var/list/L, zebra = FALSE)
for(var/path in L)
if(ispath(A, path))
if(ispath(A, path))
return !zebra || L[path]
return 1
return 0
@@ -1083,3 +1091,19 @@ GLOBAL_LIST_EMPTY(json_cache)
for(var/i in 1 to count)
retval += to_flatten[i]
return retval
/proc/pick_weight(list/list_to_pick)
var/total = 0
var/item
for(item in list_to_pick)
if(!list_to_pick[item])
list_to_pick[item] = 0
total += list_to_pick[item]
total = rand(1, total)
for(item in list_to_pick)
total -= list_to_pick[item]
if(total <= 0 && list_to_pick[item])
return item
return null
+54
View File
@@ -189,3 +189,57 @@
var/testdir = get_dir(target, origin)
return (dir & testdir)
return TRUE
///similar function to RANGE_TURFS(), but will search spiralling outwards from the center (like the above, but only turfs)
/proc/spiral_range_turfs(dist = 0, center = usr, orange = FALSE, list/outlist = list(), tick_checked)
outlist.Cut()
if(!dist)
outlist += center
return outlist
var/turf/t_center = get_turf(center)
if(!t_center)
return outlist
var/list/turf_list = outlist
var/turf/checked_turf
var/y
var/x
var/c_dist = 1
if(!orange)
turf_list += t_center
while( c_dist <= dist )
y = t_center.y + c_dist
x = t_center.x - c_dist + 1
for(x in x to t_center.x + c_dist)
checked_turf = locate(x, y, t_center.z)
if(checked_turf)
turf_list += checked_turf
y = t_center.y + c_dist - 1
x = t_center.x + c_dist
for(y in t_center.y - c_dist to y)
checked_turf = locate(x, y, t_center.z)
if(checked_turf)
turf_list += checked_turf
y = t_center.y - c_dist
x = t_center.x + c_dist - 1
for(x in t_center.x - c_dist to x)
checked_turf = locate(x, y, t_center.z)
if(checked_turf)
turf_list += checked_turf
y = t_center.y - c_dist + 1
x = t_center.x - c_dist
for(y in y to t_center.y + c_dist)
checked_turf = locate(x, y, t_center.z)
if(checked_turf)
turf_list += checked_turf
c_dist++
if(tick_checked)
CHECK_TICK
return turf_list
+8 -1
View File
@@ -44,7 +44,14 @@ SUBSYSTEM_DEF(research)
/obj/item/research_sample/common = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS*0.5),
/obj/item/research_sample/uncommon = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS*0.5),
/obj/item/research_sample/rare = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS*0.5),
/obj/item/research_sample/bluespace = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS*0.5)
/obj/item/research_sample/bluespace = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS*0.5),
/obj/item/assembly/signaler/anomaly/bioscrambler = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS),
/obj/item/assembly/signaler/anomaly/bluespace = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS),
/obj/item/assembly/signaler/anomaly/dimensional = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS),
/obj/item/assembly/signaler/anomaly/flux = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS),
/obj/item/assembly/signaler/anomaly/grav = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS),
/obj/item/assembly/signaler/anomaly/hallucination = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS),
/obj/item/assembly/signaler/anomaly/pyro = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
)
///Allows repeated deconstruction of these items for points. These items MUST be in techweb_point_items as well.
var/list/techweb_repeatable_items = list(
@@ -14,7 +14,7 @@
desc = "This armor has no inherent ability to absorb shock, as normal armor usually does. Instead, this emits a strong field \
around the wearer, designed to protect from most forms of harm, from lasers to bullets to close quarters combat. It appears to \
require a very potent supply of an energy of some kind in order to function."
icon_state = "shield_armor_0"
icon_state = "reactive"
blood_overlay_type = "armor"
slowdown = 0
armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0)
@@ -0,0 +1,121 @@
/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/anomaly_core = /obj/item/assembly/signaler/anomaly
var/area/impact_area
var/lifespan = ANOMALY_COUNTDOWN_TIMER
var/death_time
var/countdown_colour
var/obj/effect/countdown/anomaly/countdown
var/immortal = FALSE
var/move_chance = ANOMALY_MOVECHANCE
/obj/effect/anomaly/Initialize(mapload, new_lifespan, drops_core = TRUE)
. = ..()
START_PROCESSING(SSobj, src)
impact_area = get_area(src)
if(!impact_area)
return INITIALIZE_HINT_QDEL
if(!drops_core)
anomaly_core = null
if(anomaly_core)
anomaly_core = new anomaly_core(src)
anomaly_core.code = rand(1, 100)
anomaly_core.anomaly_type = type
anomaly_core.set_frequency(sanitize_frequency(get_rand_frequency()))
if(new_lifespan)
lifespan = new_lifespan
death_time = world.time + lifespan
countdown = new(src)
if(countdown_colour)
countdown_colour = countdown_colour
if(immortal)
return
countdown.start()
/obj/effect/anomaly/process(seconds_per_tick)
anomalyEffect(seconds_per_tick)
if(death_time < world.time && !immortal)
if(loc)
detonate()
qdel(src)
/obj/effect/anomaly/Destroy()
STOP_PROCESSING(SSobj, src)
QDEL_NULL(countdown)
QDEL_NULL(anomaly_core)
return ..()
/obj/effect/anomaly/proc/anomalyEffect(seconds_per_tick)
if(prob(move_chance))
move_anomaly()
/obj/effect/anomaly/proc/move_anomaly()
step(src, pick(GLOB.alldirs))
/obj/effect/anomaly/proc/detonate()
return
/obj/effect/anomaly/ex_act(strength)
if(strength <= 1)
qdel(src)
return TRUE
return FALSE
/obj/effect/anomaly/proc/anomalyNeutralize()
new /obj/effect/effect/smoke/bad(loc)
if(!isnull(anomaly_core))
anomaly_core.forceMove(get_turf(src))
anomaly_core = null
qdel(src)
/obj/effect/anomaly/proc/stabilize(anchor = FALSE, has_core = TRUE)
immortal = TRUE
name = (has_core ? "stable " : "hollow ") + name
if(!has_core)
QDEL_NULL(anomaly_core)
if(anchor)
move_chance = 0
/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
return ..()
/proc/generate_anomaly(turf/anomalycenter, type = FLUX_ANOMALY, anomalyrange = 5, has_changed_lifespan = TRUE)
var/turf/local_turf = pick(RANGE_TURFS(anomalyrange, anomalycenter))
if(!local_turf)
return
switch(type)
if(FLUX_ANOMALY)
var/explosive = has_changed_lifespan ? FLUX_NO_EMP : FLUX_LIGHT_EMP
new /obj/effect/anomaly/flux(local_turf, has_changed_lifespan ? rand(25 SECONDS, 35 SECONDS) : null, FALSE, explosive)
if(GRAVITATIONAL_ANOMALY)
new /obj/effect/anomaly/grav(local_turf, has_changed_lifespan ? rand(20 SECONDS, 30 SECONDS) : null, FALSE)
if(PYRO_ANOMALY)
new /obj/effect/anomaly/pyro(local_turf, has_changed_lifespan ? rand(15 SECONDS, 25 SECONDS) : null, FALSE)
if(HALLUCINATION_ANOMALY)
new /obj/effect/anomaly/hallucination/supermatter(local_turf, has_changed_lifespan ? rand(15 SECONDS, 25 SECONDS) : null, FALSE)
if(BIOSCRAMBLER_ANOMALY)
new /obj/effect/anomaly/bioscrambler/docile(local_turf, null, FALSE)
if(DIMENSIONAL_ANOMALY)
new /obj/effect/anomaly/dimensional(local_turf, null, FALSE)
@@ -0,0 +1,89 @@
/obj/effect/anomaly/bioscrambler
name = "bioscrambler anomaly"
icon_state = "bioscrambler"
anomaly_core = /obj/item/assembly/signaler/anomaly/bioscrambler
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
layer = ABOVE_MOB_LAYER
lifespan = ANOMALY_COUNTDOWN_TIMER * 2
/// Who are we moving towards?
var/datum/weakref/pursuit_target
/// Cooldown for every anomaly pulse
COOLDOWN_DECLARE(pulse_cooldown)
/// How many seconds between each anomaly pulse
var/pulse_delay = 10 SECONDS
/// Range of anomaly pulse
var/range = 2
/obj/effect/anomaly/bioscrambler/Initialize(mapload, new_lifespan, drops_core)
. = ..()
pursuit_target = WEAKREF(find_nearest_target())
/obj/effect/anomaly/bioscrambler/anomalyEffect(seconds_per_tick)
. = ..()
if(!COOLDOWN_FINISHED(src, pulse_cooldown))
return
new /obj/effect/temp_visual/circle_wave/bioscrambler(get_turf(src))
playsound(src, 'sound/effects/cosmic_energy.ogg', vol = 50, vary = TRUE)
COOLDOWN_START(src, pulse_cooldown, pulse_delay)
for(var/mob/living/carbon/human/nearby in viewers(range, src))
var/susceptibility = GetAnomalySusceptibility(nearby)
if(prob(susceptibility * 100))
randmutb(nearby)
domutcheck(nearby, null)
nearby.balloon_alert(nearby, "something has changed about you")
/obj/effect/anomaly/bioscrambler/move_anomaly()
update_target()
if(isnull(pursuit_target))
return ..()
var/turf/step_turf = get_step(src, get_dir(src, pursuit_target.resolve()))
step_to(src, step_turf)
/obj/effect/anomaly/bioscrambler/proc/update_target()
var/mob/living/current_target = pursuit_target?.resolve()
if(QDELETED(current_target))
pursuit_target = null
if(!isnull(pursuit_target) && prob(80))
return
var/mob/living/new_target = find_nearest_target()
if(isnull(new_target))
pursuit_target = null
return
if(new_target == current_target)
return
if(isbelly(new_target.loc) || istype(new_target.loc, /area/crew_quarters))
return
current_target = new_target
pursuit_target = WEAKREF(new_target)
/obj/effect/anomaly/bioscrambler/proc/find_nearest_target()
var/closest_distance = INFINITY
var/mob/living/carbon/closest_target = null
for(var/mob/living/carbon/target in GLOB.player_list)
if(target.z != z)
continue
if(SEND_SIGNAL(target, COMSIG_CHECK_FOR_GODMODE) & COMSIG_GODMODE_CANCEL)
continue
if(target.stat >= UNCONSCIOUS)
continue
if(istype(get_area(target), /area/crew_quarters))
continue
var/distance_from_target = get_dist(src, target)
if(distance_from_target >= closest_distance)
continue
closest_distance = distance_from_target
closest_target = target
return closest_target
/// A bioscrambler anomaly subtype which does not pursue people, for purposes of a space ruin
/obj/effect/anomaly/bioscrambler/docile
/obj/effect/anomaly/bioscrambler/docile/update_target()
return
/obj/effect/anomaly/bioscrambler/detonate()
COOLDOWN_RESET(src, pulse_cooldown)
anomalyEffect()
@@ -0,0 +1,94 @@
/obj/effect/anomaly/bluespace
name = "bluespace anomaly"
icon_state = "bluespace"
density = TRUE
anomaly_core = /obj/item/assembly/signaler/anomaly/bluespace
///range from which we can teleport someone
var/teleport_range = 1
///Distance we can teleport someone passively
var/teleport_distance = 4
/obj/effect/anomaly/bluespace/Initialize(mapload, new_lifespan)
. = ..()
apply_wibbly_filters(src)
/obj/effect/anomaly/bluespace/anomalyEffect()
..()
for(var/mob/living/M in range(teleport_range, src))
var/susceptibility = GetAnomalySusceptibility(M)
if(prob(100 * susceptibility))
do_teleport(M, locate(M.x, M.y, M.z), teleport_distance, channel = TELEPORT_CHANNEL_BLUESPACE)
/obj/effect/anomaly/bluespace/Bumped(atom/movable/AM)
if(isliving(AM) && prob(100 * GetAnomalySusceptibility(AM)))
do_teleport(AM, locate(AM.x, AM.y, AM.z), 8, channel = TELEPORT_CHANNEL_BLUESPACE)
/obj/effect/anomaly/bluespace/detonate()
playsound(src, 'sound/effects/cosmic_energy.ogg', vol = 50)
var/turf/impact_turf = pick(get_area_turfs(impact_area))
if(!impact_area)
return
var/obj/item/radio/beacon/chosen
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
if(possible.len > 0)
chosen = pick(possible)
if(!chosen)
return
var/turf/beacon_turf = get_turf(chosen)
playsound(beacon_turf, 'sound/effects/phasein.ogg', 100, TRUE)
var/datum/announcement/priority/announcement = new/datum/announcement/priority()
announcement.Announce("Massive bluespace translocation detected", "Anomaly Alert")
var/list/flashers = list()
for(var/mob/living/living in viewers(beacon_turf, null))
if(living.flash_eyes())
flashers += living
var/y_distance = beacon_turf.y - impact_turf.y
var/x_distance = beacon_turf.x - impact_turf.x
var/list/available_turfs = RANGE_TURFS(12, beacon_turf)
for(var/atom/movable/movable in urange(12, impact_turf))
if(istype(movable, /obj/item/radio/beacon) || iseffect(movable) || isEye(movable))
continue
if(movable.anchored)
continue
var/turf/newloc = locate(movable.x + x_distance, movable.y + y_distance, beacon_turf.z) || pick(available_turfs)
do_teleport(movable, newloc, no_effects = TRUE)
if(isliving(movable) && !(movable in flashers))
var/mob/living/give_sparkles = movable
give_sparkles.flash_eyes()
/obj/effect/anomaly/bluespace/stabilize(anchor, has_core)
. = ..()
teleport_range = 0
/obj/effect/anomaly/bluespace/big
immortal = TRUE
teleport_range = 2
teleport_distance = 12
anomaly_core = null
/obj/effect/anomaly/bluespace/big/Initialize(mapload, new_lifespan)
. = ..()
transform *= 3
/obj/effect/temp_visual/circle_wave/bluespace
color = COLOR_BLUE_LIGHT
duration = 1 SECONDS
amount_to_scale = 5
@@ -0,0 +1,90 @@
/obj/effect/anomaly/dimensional
name = "dimensional anomaly"
icon_state = "dimensional"
anomaly_core = /obj/item/assembly/signaler/anomaly/dimensional
lifespan = ANOMALY_COUNTDOWN_TIMER * 20
move_chance = 0
/// 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 = list()
/// 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
/// How many times we can still teleport. Delete self if it hits 0 and we try to teleport. If immortal, will simply stay where it is
var/teleports_left
/// Minimum teleports it will do before going away permanently
var/minimum_teleports = 1
/// Maximum teleports it will do before going away permanently
var/maximum_teleports = 4
/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)
teleports_left = rand(minimum_teleports, maximum_teleports)
/obj/effect/anomaly/dimensional/Destroy()
theme = null
target_turfs = null
return ..()
/obj/effect/anomaly/dimensional/anomalyEffect(seconds_per_tick)
. = ..()
transmute_area()
/obj/effect/anomaly/dimensional/proc/transmute_area()
if(!theme)
prepare_area()
if(!target_turfs.len)
if(teleports_left <= 0 && !immortal)
detonate()
return
teleports_left--
relocate()
return
var/turf/affected_turf = target_turfs[1]
theme.apply_theme(affected_turf, show_effect = TRUE)
target_turfs -= affected_turf
/obj/effect/anomaly/dimensional/proc/prepare_area(new_theme_path)
if(!new_theme_path)
new_theme_path = pick(subtypesof(/datum/dimension_theme))
theme = new new_theme_path
apply_theme_icon()
target_turfs = list()
for(var/turf/turf in spiral_range_turfs(range, src))
if(theme.can_convert(turf))
target_turfs += turf
/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
/obj/effect/anomaly/dimensional/proc/relocate()
var/datum/anomaly_placer/placer = new()
var/area/new_area = placer.find_valid_area()
var/turf/new_turf = placer.find_valid_turf(new_area)
var/datum/announcement/priority/announcement = new/datum/announcement/priority()
announcement.Announce("Dimensional instability relocated. Expected location: [new_area.name].", "Anomaly Alert")
src.forceMove(new_turf)
prepare_area()
/obj/effect/anomaly/dimensional/detonate()
qdel(src)
/obj/effect/temp_visual/transmute_tile_flash
icon = 'icons/effects/effects.dmi'
icon_state = "shieldsparkles"
duration = 3
@@ -0,0 +1,264 @@
/**
* Datum which describes a theme and replaces turfs and objects in specified locations to match that theme
*/
/datum/dimension_theme
/// Human readable name of the theme
var/name = "Unnamed Theme"
/// An icon to display to represent the theme
var/icon/icon = 'icons/obj/stacks.dmi'
/// Icon state to use to represent the theme
var/icon_state
/// Typepath of custom material to use for objects.
var/datum/material/material
/// Sound to play when transforming a tile
var/sound = 'sound/effects/blind.ogg'
/// Weighted list of turfs to replace the floor with.
var/list/replace_floors = list(/turf/simulated/floor/tiled = 1)
/// Typepath of turf to replace walls with.
var/turf/replace_walls = /turf/simulated/wall
/// List of weighted lists for object replacement. Key is an original typepath, value is a weighted list of typepaths to replace it with.
var/list/replace_objs = list(
/obj/structure/bed/chair = list(/obj/structure/bed/chair = 1),
/obj/machinery/door/airlock = list(/obj/machinery/door/airlock = 1, /obj/machinery/door/airlock/glass = 1),
/obj/structure/table = list(/obj/structure/table = 1),
/obj/structure/toilet = list(/obj/structure/toilet = 1)
)
/// List of random spawns to place in completely open turfs
var/list/random_spawns
/// Prob of placing a random spawn in a completely open turf
var/random_spawn_chance = 0
/// Typepath of full-size windows which will replace existing ones
/// These need to be separate from replace_objs because we don't want to replace dir windows with full ones and they share typepath
var/obj/structure/window/replace_window
/// Colour to recolour windows with, replaced by material colour if material was specified.
var/window_colour = "#ffffff"
/datum/dimension_theme/New()
if(material)
var/datum/material/using_mat = GET_MATERIAL_REF(material)
window_colour = using_mat.icon_colour
/**
* Applies themed transformation to the provided turf.
*
* Arguments
* * affected_turf - Turf to transform.
* * skip_sound - If the sound shouldn't be played.
* * show_effect - if the temp visual effect should be shown.
*/
/datum/dimension_theme/proc/apply_theme(turf/affected_turf, skip_sound = FALSE, show_effect = FALSE)
if(!replace_turf(affected_turf))
return
if(!skip_sound)
playsound(affected_turf, sound, 100, TRUE)
if(show_effect)
new /obj/effect/temp_visual/transmute_tile_flash(affected_turf)
for(var/obj/object in affected_turf)
replace_object(object)
if(length(random_spawns) && prob(random_spawn_chance))
var/random_spawn_picked = pick(random_spawns)
new random_spawn_picked(affected_turf)
/datum/dimension_theme/proc/can_convert(var/turf/affected_turf)
if(isspace(affected_turf))
return FALSE
if(isfloorturf(affected_turf))
if(istype(get_area(affected_turf), /area/holodeck))
return FALSE
return replace_floors.len > 0
if(iswall(affected_turf))
return TRUE
return FALSE
/datum/dimension_theme/proc/replace_turf(turf/affected_turf)
PROTECTED_PROC(TRUE)
if(isfloorturf(affected_turf))
if(istype(get_area(affected_turf), /area/holodeck) || istype(get_area(affected_turf), /area/crew_quarters))
return FALSE
return transform_floor(affected_turf)
if(!iswall(affected_turf))
return FALSE
affected_turf.ChangeTurf(replace_walls)
return TRUE
/**
* Replaces the provided floor turf with a different one.
*
* Arguments
* * affected_floor - Floor turf to transform.
*/
/datum/dimension_theme/proc/transform_floor(turf/affected_floor)
PROTECTED_PROC(TRUE)
if(replace_floors.len == 0)
return FALSE
affected_floor.ChangeTurf(pick_weight(replace_floors))
return TRUE
/**
* Replaces the provided object with a different one.
*
* Arguments
* * object - Object to replace.
*/
/datum/dimension_theme/proc/replace_object(obj/object)
PROTECTED_PROC(TRUE)
if(istype(object, /obj/structure/window) && window_colour)
object.color = window_colour
return
if(istype(object, /obj/structure/table) && material)
var/obj/structure/table/table = object
table.material = material
table.update_connections(TRUE)
table.update_icon()
table.update_desc()
table.update_material()
return
if(istype(object, /obj/machinery/light))
var/obj/machinery/light/light = object
light.brightness_color = window_colour
light.brightness_color_ns = window_colour
light.set_light(0)
light.update()
var/replace_path = get_replacement_object_typepath(object)
if(!replace_path)
return
var/obj/new_object = new replace_path(object.loc)
if(istype(object, /obj/machinery/door/airlock))
if(istype(new_object, /obj/machinery/door/airlock))
var/obj/machinery/door/airlock/airlock = object
var/obj/machinery/door/airlock/new_airlock = new_object
new_airlock.req_access = airlock.req_one_access?.Copy()
new_airlock.locked = airlock.locked
if(istype(object, /obj/machinery/door/airlock/multi_tile))
for(var/turf/location in object.locs)
if(location == object.loc)
continue
var/obj/machinery/door/airlock/long_airlock = new replace_path(location)
long_airlock.req_access = airlock.req_one_access?.Copy()
long_airlock.locked = airlock.locked
long_airlock.name = airlock.name
new_object.name = object.name
qdel(object)
/**
* Returns the typepath of an object to replace the provided object.
*
* Arguments
* * object - Object to transform.
*/
/datum/dimension_theme/proc/get_replacement_object_typepath(obj/object)
PROTECTED_PROC(TRUE)
for(var/type in replace_objs)
if(istype(object, type))
return pick_weight(replace_objs[type])
/datum/dimension_theme/gold
name = "Gold"
icon_state = "sheet-gold_2"
material = /datum/material/gold
replace_floors = list(/turf/simulated/floor/tiled/material/gold = 1)
replace_objs = list(
/obj/structure/bed/chair = list(/obj/structure/bed/chair = 1),
/obj/machinery/door/airlock = list(/obj/machinery/door/airlock/gold = 1),
/obj/structure/table = list(/obj/structure/table/gold = 1),
/obj/structure/toilet = list(/obj/structure/toilet = 1)
)
replace_walls = /turf/simulated/wall/gold
/datum/dimension_theme/radioactive
name = "Radioactive"
icon_state = "sheet-uranium_2"
material = /datum/material/uranium
replace_floors = list(/turf/simulated/floor/tiled/material/uranium = 1)
replace_objs = list(
/obj/machinery/door/airlock = list(/obj/machinery/door/airlock/uranium = 1),
)
replace_walls = /turf/simulated/wall/uranium
sound = 'sound/items/Welder.ogg'
/datum/dimension_theme/wood
name = "Wood"
icon_state = "sheet-plank"
material = /datum/material/wood
replace_floors = list(/turf/simulated/floor/wood = 1)
replace_objs = list(
/obj/structure/bed/chair = list(/obj/structure/bed/chair/wood = 1),
/obj/machinery/door/airlock = list(/obj/structure/simple_door/wood = 1),
/obj/structure/table = list(/obj/structure/table/woodentable = 1)
)
replace_walls = /turf/simulated/wall/wood
/*
/datum/dimension_theme/meat
name = "Meat"
icon = 'icons/obj/food.dmi'
icon_state = "meat"
material = /datum/material/flesh
/datum/dimension_theme/alien
name = "Alien"
icon = 'icons/obj/abductor.dmi'
icon_state = "circuit"
replace_floors = list(/turf/simulated/floor/redgrid = 1, /turf/simulated/floor/greengrid = 1, /turf/simulated/floor/bluegrid = 1)
replace_objs = list(
/obj/machinery/door/airlock = list(/obj/machinery/door/airlock/alien = 1),
/obj/structure/table = list(/obj/structure/table/alien = 1, /obj/structure/table/alien/blue = 1)
)
*/
/datum/dimension_theme/natural
name = "Natural"
icon = 'icons/obj/plants.dmi'
icon_state = "plant-24"
window_colour = "#0b8011ff"
replace_floors = list(/turf/simulated/floor/grass = 1)
replace_walls = /turf/simulated/wall/wood
replace_objs = list(
/obj/structure/bed/chair = list(/obj/structure/bed/chair/wood = 3, /obj/structure/bed/chair/wood/wings = 1),
/obj/machinery/door/airlock = list(/obj/structure/simple_door/wood = 1),
)
random_spawns = list(
/obj/structure/flora/grass/green = 3,
/obj/structure/flora/bush = 3,
/mob/living/simple_mob/vore/bee = 1,
)
random_spawn_chance = 10
/datum/dimension_theme/phoron
name = "Phoron"
icon_state = "sheet-phoron_2"
material = /datum/material/phoron
replace_floors = list(/turf/simulated/floor/tiled/material/phoron = 1)
replace_objs = list(
/obj/machinery/door/airlock = list(/obj/machinery/door/airlock/phoron = 1),
)
replace_walls = /turf/simulated/wall/phoron
/datum/dimension_theme/glass
name = "Glass"
icon_state = "sheet-glass_2"
material = /datum/material/glass
replace_floors = list(/turf/simulated/floor/glass = 1)
replace_objs = list(
/obj/machinery/door/airlock = list(/obj/machinery/door/airlock/glass = 1),
/obj/structure/table = list(/obj/structure/table/glass = 1)
)
replace_walls = /obj/structure/window/basic/full
/datum/dimension_theme/snow
name = "Snow"
icon_state = "sheet-snow_2"
material = /datum/material/snow
replace_floors = list(/turf/simulated/floor/snow = 10, /turf/simulated/floor/outdoors/ice = 1)
replace_objs = list(
/obj/machinery/door/airlock = list(/obj/structure/simple_door/)
)
@@ -0,0 +1,63 @@
/obj/effect/anomaly/flux
name = "flux wave anomaly"
icon_state = "flux"
density = TRUE
anomaly_core = /obj/item/assembly/signaler/anomaly/flux
var/canshock = FALSE
var/shockdamage = 20
var/emp_zap = FLUX_EMP
/obj/effect/anomaly/flux/Initialize(mapload, new_lifespan, emp_zap = FLUX_EMP)
. = ..()
src.emp_zap = emp_zap
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered)
)
AddElement(/datum/element/connect_loc, loc_connections)
apply_wibbly_filters(src)
/obj/effect/anomaly/flux/anomalyEffect()
..()
canshock = TRUE
for(var/mob/living/M in range(0, src))
mobShock(M)
/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/attack_hand(mob/living/user)
mobShock(user)
. = ..()
/obj/effect/anomaly/flux/attackby(obj/item/I, mob/user)
mobShock(user)
. = ..()
/obj/effect/anomaly/flux/proc/mobShock(mob/living/M)
if(canshock && istype(M))
canshock = FALSE
M.electrocute_act(shockdamage, name)
/obj/effect/anomaly/flux/detonate()
switch(emp_zap)
if(FLUX_EMP)
empulse(src, 4, 16)
explosion(src, heavy_impact_range = 1, light_impact_range = 4, flash_range = 6)
if(FLUX_LIGHT_EMP)
empulse(src, 4, 6)
explosion(src, light_impact_range = 3, flash_range = 6)
if(FLUX_NO_EMP)
new /obj/effect/effect/sparks(loc)
/obj/effect/anomaly/flux/minor
anomaly_core = null
/obj/effect/anomaly/flux/minor/Initialize(mapload, new_lifespan, emp_zap = FLUX_NO_EMP)
return ..()
@@ -0,0 +1,89 @@
/obj/effect/anomaly/grav
name = "gravitational anomaly"
icon_state = "gravity"
density = FALSE
anomaly_core = /obj/item/assembly/signaler/anomaly/grav
var/boing = FALSE
var/object_launch_prob = 20
/obj/effect/anomaly/grav/Initialize(mapload, new_lifespan)
. = ..()
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
apply_wibbly_filters(src)
/obj/effect/anomaly/grav/anomalyEffect(seconds_per_tick)
..()
boing = TRUE
for(var/obj/O in orange(4, src))
if(!O.anchored)
step_towards(O, src)
for(var/mob/living/M in range(0, src))
if(!M.can_overcome_gravity())
gravShock(M)
for(var/mob/living/M in range(4, src))
if(!M.can_overcome_gravity())
step_towards(M, src)
for(var/obj/O in range(0, src))
if(O.anchored)
continue
var/mob/living/target = locate() in view(4, src)
if(target && !target.stat && prob(object_launch_prob))
O.throw_at(target, 5, 10)
/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/living_debris)
if(boing && isliving(living_debris) && !living_debris.stat && !living_debris.can_overcome_gravity())
living_debris.SetStunned(2)
var/atom/target = get_edge_target_turf(living_debris, get_dir(src, get_step_away(living_debris, src)))
living_debris.throw_at(target, 5, 1)
boing = FALSE
/obj/effect/anomaly/grav/detonate()
new /obj/effect/temp_visual/circle_wave/gravity(get_turf(src))
playsound(src, 'sound/effects/cosmic_energy.ogg', vol = 50)
/* // Unfortunately couldn't get this one working. Maybe in the future.
/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 as anything in GLOB.machines)
if(is_on_same_plane_or_station(the_generator.z))
the_generator.breaker = FALSE
the_generator.set_power()
the_generator.charge_count = 10
/obj/effect/anomaly/grav/high/Destroy()
QDEL_NULL(grav_field)
. = ..()
/obj/effect/anomaly/grav/high/big
immortal = TRUE
anomaly_core = null
/obj/effect/anomaly/grav/high/big/Initialize(mapload, new_lifespan)
. = ..()
transform *= 3
*/
/obj/effect/temp_visual/circle_wave/gravity
color = COLOR_NAVY
@@ -0,0 +1,109 @@
/obj/effect/anomaly/hallucination
name = "hallucination anomaly"
icon_state = "hallucination"
anomaly_core = /obj/item/assembly/signaler/anomaly/hallucination
/// 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
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 whispering around you!"),
span_warning("You are going insane!"),
)
///Do we spawn misleading decoys?
var/spawn_decoys = TRUE
/obj/effect/anomaly/hallucination/Initialize(mapload, new_lifespan)
. = ..()
apply_wibbly_filters(src)
generate_decoys()
/obj/effect/anomaly/hallucination/anomalyEffect(seconds_per_tick)
. = ..()
ticks += seconds_per_tick
if(ticks < release_delay)
return
ticks -= release_delay
if(!isturf(loc))
return
for(var/mob/living/carbon/human/hallucinator in viewers(5, src))
var/susceptibility = GetAnomalySusceptibility(hallucinator)
if(prob(susceptibility * 100))
hallucinator.hallucination += 10
if(prob(20))
to_chat(hallucinator, pick(messages))
if(prob(10))
to_chat(hallucinator, span_danger("Your nose bleeds!"))
hallucinator.drip(1)
/obj/effect/anomaly/hallucination/detonate()
if(isturf(loc))
return
for(var/mob/living/carbon/human/hallucinator in viewers(10, src))
to_chat(hallucinator, pick(messages))
hallucinator.hallucination += 25
to_chat(hallucinator, span_danger("Your nose bleeds!"))
hallucinator.drip(1)
/obj/effect/anomaly/hallucination/proc/generate_decoys()
if(!spawn_decoys)
return
for(var/turf/floor in orange(1, src))
if(prob(35))
new /obj/effect/anomaly/hallucination/decoy(floor)
/obj/effect/anomaly/hallucination/decoy
anomaly_core = null
var/report_text
/obj/effect/anomaly/hallucination/decoy/Initialize(mapload, new_lifespan)
. = ..()
report_text = pick(
"[src]'s unstable field is fluctuating along frequency 9999999.99999, code 9999999.99999. No, no, that can't be right?",
"It doesn't detect anything. It awaits an input, as if you're pointing it towards nothing at all. What?",
"The interface displays [pick("a bad memory from your past", "the frequency numbers in a language you cannot read", "the first 15 digits of Pi", "yourself, from behind, angled at a 3/4ths isometric perspective")]. What the hell?",
"Nothing happens?",
"It reports that you are a [pick("moron", "idiot", "cretin", "lowlife", "worthless denthead", "gump")]. Huh?",
"It tells you to try again, because you're doing it all wrong. What?",
"It occurs to you that the anomaly you're scanning isn't actually there.",
"It's not working. You activate %TOOL% again. Still broken. You activate %TOOL%. You activate %TOOL%. Why isn't this working??",
"Something happens. You can't tell what. The interface on %TOOL% remains blank.",
"What are you even trying to accomplish here? Did you really think that was going to work?",
"Someone behind you whispers the frequency code to you, but you can't quite hear them. The interface on %TOOL% remains blank.",
"For a brief moment, you see yourself traversing a frozen forest, before snapping back to reality. The interface on %TOOL% remains blank.",
"Nothing interesting happens. Are you sure you're actually using it on anything?",
"For a moment you can feel your skin falling off, then blink as the sensation vanishes. What the hell did that mean?",
"The interface reports that you are a complete failure, and have screwed everything up again. Great work.",
"You realize that the formatting of this message is completely wrong, and get confused. Now why would that be?",
"%TOOL% stares back at you. It looks dissapointed, its screen practically saying 'You missed the anomaly, you dolt. There's nothing there!'",
"Nothing. Weird, maybe %TOOL% must be broken or something?",
"You activate %TOOL%. You activate %TOOL%. You activate %TOOL%. You activate %TOOL%. You activate %TOOL%. You activate %TOOL%. You activate %TOOL%. Why isn't it working??",
)
/obj/effect/anomaly/hallucination/decoy/anomalyEffect(seconds_per_tick)
if(SPT_PROB(move_chance, seconds_per_tick))
move_anomaly()
/obj/effect/anomaly/hallucination/decoy/attackby(obj/item/tool, mob/user, params)
if(istype(tool, /obj/item/analyzer))
to_chat(user, span_notice("You activate \the [tool]. [replacetext(report_text, "%TOOL%", "[tool]")]"))
return ..()
/obj/effect/anomaly/hallucination/decoy/detonate()
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread
sparks.set_up(3, 1, src)
sparks.start()
return
/obj/effect/anomaly/hallucination/decoy/generate_decoys()
return
///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
@@ -0,0 +1,34 @@
/obj/effect/anomaly/pyro
name = "pyroclastic anomaly"
icon_state = "pyroclastic"
var/ticks = 0
/// How many seconds between each gas release
var/releasedelay = 15 SECONDS
anomaly_core = /obj/item/assembly/signaler/anomaly/pyro
/obj/effect/anomaly/pyro/Initialize(mapload, new_lifespan, drops_core)
. = ..()
apply_wibbly_filters(src)
/obj/effect/anomaly/pyro/anomalyEffect(seconds_per_tick)
..()
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)
return TRUE
/obj/effect/anomaly/pyro/detonate()
var/turf/simulated/floor/tile = get_turf(src)
if(istype(tile))
tile.assume_gas(GAS_PHORON, 10, T20C)
tile.hotspot_expose(700, 400)
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)
pyro.enrage()
@@ -0,0 +1,45 @@
/datum/anomaly_placer
var/static/list/allowed_areas
var/list/excluded = list(
/area/crew_quarters,
/area/shuttle,
/area/space,
/area/solar,
/area/engineering/engine_room,
/area/maintenance,
/area/holodeck,
/area/ai
)
/datum/anomaly_placer/proc/find_valid_area()
var/list/possible_areas = get_station_areas(excluded)
if(!length(possible_areas))
CRASH("No valid areas for anomaly found.")
var/area/landing_area = pick(possible_areas)
var/list/turf_test = get_area_turfs(landing_area)
if(!turf_test.len)
CRASH("Anomaly : No valid turfs found for [landing_area] - [landing_area.type]")
return landing_area
/datum/anomaly_placer/proc/find_valid_turf(area/target_area)
var/list/valid_turfs = list()
for(var/turf/try_turf as anything in get_area_turfs(target_area))
if(!is_valid_destination(try_turf))
continue
valid_turfs += try_turf
if(!valid_turfs.len)
CRASH("Dimensional anomaly attempted to reach invalid locaton [target_area]")
return pick(valid_turfs)
/datum/anomaly_placer/proc/is_valid_destination(turf/tested)
if(isspace(tested))
return FALSE
if(tested.density)
return FALSE
if(isopenspace(tested))
return FALSE
return TRUE
@@ -0,0 +1,17 @@
/obj/effect/spawner/lootdrop/environmentally_safe_anomaly
name = "safe anomaly spawner"
icon_state = "instability"
loot = list(
/obj/effect/anomaly/flux,
/obj/effect/anomaly/bluespace,
/obj/effect/anomaly/hallucination,
/obj/effect/anomaly/bioscrambler/docile
)
var/anchor_anomaly = FALSE
/obj/effect/spawner/lootdrop/environmentally_safe_anomaly/Initialize(mapload)
. = ..()
var/obj/effect/anomaly/anomaly = .
anomaly.stabilize(anchor = anchor_anomaly)
+2 -3
View File
@@ -9,7 +9,7 @@
anchored = TRUE
plane = PLANE_GHOSTS
color = "#ff0000"
var/text_size = 3
var/text_size = 3 // Larger values clip when the displayed text is larger than 2 digits
var/started = FALSE
var/displayed_text
var/atom/attached_to
@@ -78,7 +78,7 @@
/obj/effect/countdown/singularity_act()
return
/*
/obj/effect/countdown/anomaly
name = "anomaly countdown"
@@ -91,4 +91,3 @@
else
var/time_left = max(0, (A.death_time - world.time)/10)
return round(time_left)
*/
@@ -113,3 +113,35 @@
pixel_y = rand(-9, 0)
. = ..()
// VOREStation Add End
/obj/effect/temp_visual/circle_wave
icon = 'icons/effects/64x64.dmi'
icon_state = "circle_wave"
pixel_x = -16
pixel_y = -16
duration = 0.5 SECONDS
color = COLOR_LIME
var/max_alpha = 255
var/amount_to_scale = 2
/obj/effect/temp_visual/circle_wave/Initialize(mapload)
transform = matrix().Scale(0.1)
animate(src, transform = matrix().Scale(amount_to_scale), time = duration, flags = ANIMATION_PARALLEL)
animate(src, alpha = max_alpha, time = duration * 0.6, flags = ANIMATION_PARALLEL)
animate(alpha = 0, time = duration * 0.4)
apply_wibbly_filters(src)
return ..()
/obj/effect/temp_visual/circle_wave/bioscrambler
color = COLOR_LIME
/obj/effect/temp_visual/circle_wave/bioscrambler/light
max_alpha = 128
/obj/effect/temp_visual/circle_wave/void_conduit
color = COLOR_FULL_TONER_BLACK
duration = 12 SECONDS
amount_to_scale = 12
/obj/effect/temp_visual/circle_wave/star_blast
color = COLOR_VOID_PURPLE
@@ -0,0 +1,43 @@
/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
+3 -2
View File
@@ -160,7 +160,8 @@
body_parts_covered = CHEST
armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0)
// Ooold, old reactive armor.
/*
//Reactive armor
//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!)
/obj/item/clothing/suit/armor/reactive
@@ -211,7 +212,7 @@
active = FALSE
icon_state = "reactiveoff"
..()
*/
// Alien armor has a chance to completely block attacks.
/obj/item/clothing/suit/armor/alien
name = "alien enhancement vest"
@@ -0,0 +1,356 @@
/obj/item/clothing/suit/armor/reactive_armor_shell
name = "reactive armor shell"
desc = "An experimental suit of armor, awaiting installation of an anomaly core."
icon_state = "reactiveoff"
w_class = ITEMSIZE_COST_LARGE
/obj/item/clothing/suit/armor/reactive_armor_shell/attackby(obj/item/I, mob/user)
. = ..()
var/static/list/anomaly_armour_types = list(
/obj/effect/anomaly/grav = /obj/item/clothing/suit/armor/reactive/repulse,
/obj/effect/anomaly/flux = /obj/item/clothing/suit/armor/reactive/tesla,
/obj/effect/anomaly/bluespace = /obj/item/clothing/suit/armor/reactive/teleport,
//obj/effect/anomaly/bioscrambler = /obj/item/clothing/suit/armor/reactive/bioscrambling,
/obj/effect/anomaly/hallucination = /obj/item/clothing/suit/armor/reactive/hallucinating,
/obj/effect/anomaly/dimensional = /obj/item/clothing/suit/armor/reactive/barricade,
/obj/effect/anomaly/pyro = /obj/item/clothing/suit/armor/reactive/fire
)
if(istype(I, /obj/item/assembly/signaler/anomaly))
var/obj/item/assembly/signaler/anomaly/anomaly = I
var/armour_path = is_path_in_list(anomaly.anomaly_type, anomaly_armour_types, TRUE)
if(!armour_path)
armour_path = /obj/item/clothing/suit/armor/reactive/stealth
to_chat(user, span_notice("You insert [anomaly] into the chest plate, and the armour gently hums to life."))
new armour_path(get_turf(src))
qdel(src)
qdel(anomaly)
return TRUE
/obj/item/clothing/suit/armor/reactive
name = "reactive armor"
desc = "Doesn't seem to do much for some reason."
icon_state = "reactiveoff"
blood_overlay_type = "armor"
armor = list(melee = 40, bullet = 35, laser = 35, energy = 10, bomb = 10, bio = 0, rad = 0)
var/hit_reaction_chance = 50
///Whether the armor will try to react to hits (is it on)
var/active = FALSE
///This will be true for 30 seconds after an EMP, it makes the reaction effect dangerous to the user.
var/bad_effect = FALSE
///Message sent when the armor is emp'd. It is not the message for when the emp effect goes off.
var/emp_message = span_warning("The reactive armor has been emp'd! Damn, now it's REALLY gonna not do much!")
///Message sent when the armor is still on cooldown, but activates.
var/cooldown_message = span_danger("The reactive armor fails to do much, as it is recharging! From what? Only the reactive armor knows.")
///Duration of the cooldown specific to reactive armor for when it can activate again.
var/reactivearmor_cooldown_duration = 10 SECONDS
///The cooldown itself of the reactive armor for when it can activate again.
var/reactivearmor_cooldown = 0
/obj/item/clothing/suit/armor/reactive/update_icon()
. = ..()
icon_state = "reactive[active ? null : "off"]"
/obj/item/clothing/suit/armor/reactive/attack_self(mob/user)
active = !active
to_chat(user, span_notice("[src] is now [active ? "active" : "inactive"]."))
update_icon()
add_fingerprint(user)
/obj/item/clothing/suit/armor/reactive/handle_shield(mob/user, damage, atom/damage_source, mob/attacker, def_zone, attack_text)
if(!active || !prob(hit_reaction_chance))
return FALSE
if(world.time < reactivearmor_cooldown)
cooldown_activation(user)
return FALSE
if(bad_effect)
return emp_activation(user, damage_source, attack_text, damage)
else
return reactive_activation(user, damage_source, attack_text, damage)
/obj/item/clothing/suit/armor/reactive/proc/cooldown_activation(var/mob/living/carbon/human/owner)
owner.visible_message(cooldown_message)
/obj/item/clothing/suit/armor/reactive/proc/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0)
owner.visible_message(span_danger("The reactive armor doesn't do much! No surprises here."))
return TRUE
/obj/item/clothing/suit/armor/reactive/proc/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0)
owner.visible_message(span_danger("The reactive armor doesn't do much, despite being emp'd! Besides giving off a special message, of course."))
return TRUE
/obj/item/clothing/suit/armor/reactive/emp_act(severity, recursive)
. = ..()
if(bad_effect || !active)
return
visible_message(emp_message)
bad_effect = TRUE
addtimer(VARSET_CALLBACK(src, bad_effect, FALSE), 30 SECONDS)
/obj/item/clothing/suit/armor/reactive/teleport
name = "reactive teleport armor"
desc = "Someone separated our Research Director from his own head!"
emp_message = span_warning("The reactive armor's teleportation calculations begin spewing errors!")
cooldown_message = span_danger("The reactive teleport system is still recharging! It fails to activate!")
reactivearmor_cooldown_duration = 10 SECONDS
var/tele_range = 6
/obj/item/clothing/suit/armor/reactive/teleport/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0)
owner.visible_message(span_danger("The reactive teleport system flings [owner] clear of [attack_text]!"))
playsound(get_turf(owner), 'sound/effects/phasein.ogg', 100, TRUE)
do_teleport(owner, get_turf(owner), tele_range, no_effects = TRUE, channel = TELEPORT_CHANNEL_BLUESPACE)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return TRUE
/obj/item/clothing/suit/armor/reactive/teleport/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0)
owner.visible_message(span_danger("The reactive teleport system flings itself clear of [attack_text], leaving someone behind in the process!"))
owner.drop_from_inventory(src, get_turf(src))
playsound(get_turf(owner),'sound/machines/buzz-sigh.ogg', 50, TRUE)
playsound(get_turf(owner), 'sound/effects/phasein.ogg', 100, TRUE)
do_teleport(src, get_turf(owner), tele_range, no_effects = TRUE, channel = TELEPORT_CHANNEL_BLUESPACE)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return FALSE
/obj/item/clothing/suit/armor/reactive/repulse
name = "reactive repulse armor"
desc = "An experimental suit of armor that violently throws back attackers."
cooldown_message = span_danger("The repulse generator is still recharging! It fails to generate a strong enough wave!")
emp_message = span_warning("The repulse generator is reset to default settings...")
/obj/item/clothing/suit/armor/reactive/repulse/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0)
playsound(get_turf(owner),'sound/effects/repulse.ogg', 100, TRUE)
owner.visible_message(span_danger("[src] blocks [attack_text], converting the attack into a wave of force!"))
var/turf/owner_turf = get_turf(owner)
var/list/thrown_items = list()
for(var/atom/movable/repulsed in range(owner_turf, 5))
if(repulsed == owner || repulsed.anchored || thrown_items[repulsed])
continue
var/throwtarget = get_edge_target_turf(owner_turf, get_dir(owner_turf, get_step_away(repulsed, owner_turf)))
repulsed.throw_at(throwtarget, 10, 1)
thrown_items[repulsed] = repulsed
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return TRUE
/obj/item/clothing/suit/armor/reactive/repulse/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0)
playsound(get_turf(owner),'sound/effects/repulse.ogg', 100, TRUE)
owner.visible_message(span_danger("[src] does not block [attack_text], and instead generates an attracting force!"))
var/turf/owner_turf = get_turf(owner)
var/list/thrown_items = list()
for(var/atom/movable/repulsed in range(owner_turf, 5))
if(repulsed == owner || repulsed.anchored || thrown_items[repulsed])
continue
repulsed.throw_at(owner, 10, 1)
thrown_items[repulsed] = repulsed
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return FALSE
// Tesla
/obj/item/clothing/suit/armor/reactive/tesla
name = "reactive tesla armor"
desc = "An experimental suit of armor with sensitive detectors hooked up to a huge capacitor grid, with emitters strutting out of it. Zap."
siemens_coefficient = -1
cooldown_message = span_danger("The tesla capacitors on the reactive tesla armor are still recharging! The armor merely emits some sparks.")
emp_message = span_warning("The tesla capacitors beep ominously for a moment.")
clothing_traits = list(TRAIT_TESLA_SHOCKIMMUNE)
/// How strong are the zaps we give off?
var/zap_power = 2.5e4
/// How far to the zaps we give off go?
var/zap_range = 20
/obj/item/clothing/suit/armor/reactive/tesla/cooldown_activation(mob/living/carbon/human/owner)
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread()
sparks.set_up(1, 1, src)
sparks.start()
..()
/obj/item/clothing/suit/armor/reactive/tesla/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0)
owner.visible_message(span_danger("[src] blocks [attack_text], sending out arcs of lightning!"))
tesla_zap(owner, zap_range, zap_power)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return TRUE
/obj/item/clothing/suit/armor/reactive/tesla/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0)
owner.visible_message(span_danger("[src] blocks [attack_text], but pulls a massive charge of energy into [owner] from the surrounding environment!"))
REMOVE_CLOTHING_TRAIT(owner, TRAIT_TESLA_SHOCKIMMUNE)
electrocute_mob(owner, get_area(src), src, 1)
ADD_CLOTHING_TRAIT(owner, TRAIT_TESLA_SHOCKIMMUNE)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return TRUE
// Sure we could, but- Not really THAT useful. Give them the stealth one.
/obj/item/clothing/suit/armor/reactive/bioscrambling
// Hallucinating
/obj/item/clothing/suit/armor/reactive/hallucinating
name = "reactive hallucinating armor"
desc = "An experimental suit of armor with sensitive detectors hooked up to the mind of the wearer, sending mind pulses that causes hallucinations around you."
cooldown_message = span_danger("The connection is currently out of sync... Recalibrating.")
emp_message = span_warning("You feel the backsurge of a mind pulse.")
clothing_traits = list(TRAIT_MADNESS_IMMUNE)
/obj/item/clothing/suit/armor/reactive/hallucinating/cooldown_activation(mob/living/carbon/human/owner)
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread()
sparks.set_up(1, 1, src)
sparks.start()
..()
/obj/item/clothing/suit/armor/reactive/hallucinating/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0)
owner.visible_message(span_danger("[src] blocks [attack_text], sending out mental pulses!"))
for(var/mob/living/carbon/human/hallucinator in viewers(5, get_turf(src)))
if(hallucinator == owner)
continue
hallucinator.hallucination += 50
if(prob(10))
to_chat(hallucinator, span_danger("Your nose bleeds!"))
hallucinator.drip(1)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return TRUE
/obj/item/clothing/suit/armor/reactive/hallucinating/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0)
owner.visible_message(span_danger("[src] blocks [attack_text], but pulls a massive charge of mental energy into [owner] from the surrounding environment!"))
owner.hallucination += 75
to_chat(owner, span_danger("Your nose bleeds!"))
owner.drip(1)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return TRUE
// When the wearer gets hit, this armor will push people nearby and spawn some blocking objects.
/obj/item/clothing/suit/armor/reactive/barricade
name = "reactive barricade armor"
desc = "An experimental suit of armor that generates barriers from another world when it detects its bearer is in danger."
emp_message = span_warning("The reactive armor's dimensional coordinates are scrambled!")
cooldown_message = span_danger("The reactive barrier system is still recharging! It fails to activate!")
reactivearmor_cooldown_duration = 10 SECONDS
/obj/item/clothing/suit/armor/reactive/barricade/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0)
playsound(get_turf(owner),'sound/effects/repulse.ogg', 100, TRUE)
owner.visible_message(span_danger("The reactive armor interposes matter from another world between [src] and [attack_text]!"))
for (var/atom/movable/target in repulse_targets(owner))
repulse(target, owner)
var/datum/armour_dimensional_theme/theme = new()
theme.apply_random(get_turf(owner), dangerous = FALSE)
qdel(theme)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return TRUE
/obj/item/clothing/suit/armor/reactive/barricade/proc/repulse_targets(atom/source)
var/list/push_targets = list()
for (var/atom/movable/nearby_movable in view(1, source))
if(nearby_movable == source)
continue
if(nearby_movable.anchored)
continue
push_targets += nearby_movable
return push_targets
/obj/item/clothing/suit/armor/reactive/barricade/proc/repulse(atom/movable/victim, atom/source)
var/dist_from_caster = get_dist(victim, source)
if(dist_from_caster == 0)
return
if (isliving(victim))
to_chat(victim, span_userdanger("You're thrown back by a wave of pressure!"))
var/turf/throwtarget = get_edge_target_turf(source, get_dir(source, get_step_away(victim, source, 1)))
victim.throw_at(throwtarget, 1, 1)
/obj/item/clothing/suit/armor/reactive/barricade/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0)
owner.visible_message(span_danger("The reactive armor shunts matter from an unstable dimension!"))
var/datum/armour_dimensional_theme/theme = new()
theme.apply_random(get_turf(owner), dangerous = TRUE)
qdel(theme)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return FALSE
// FIRE
/obj/item/clothing/suit/armor/reactive/fire
name = "reactive incendiary armor"
desc = "An experimental suit of armor with a reactive sensor array rigged to a flame emitter. For the stylish pyromaniac."
cooldown_message = span_danger("The reactive incendiary armor activates, but fails to send out flames as it is still recharging its flame jets!")
emp_message = span_warning("The reactive incendiary armor's targeting system begins rebooting...")
/obj/item/clothing/suit/armor/reactive/fire/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0)
owner.visible_message(span_danger("[src] blocks [attack_text], sending out jets of flame!"))
playsound(get_turf(owner), 'sound/magic/Fireball.ogg', 100, TRUE)
for(var/mob/living/carbon_victim in range(6, get_turf(src)))
if(carbon_victim != owner)
carbon_victim.adjust_fire_stacks(8)
carbon_victim.ignite_mob()
owner.set_wet_stacks(20)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return TRUE
/obj/item/clothing/suit/armor/reactive/fire/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0)
owner.visible_message(span_danger("[src] just makes [attack_text] worse by spewing molten death on [owner]!"))
playsound(get_turf(owner), 'sound/magic/Fireball.ogg', 100, TRUE)
owner.adjust_fire_stacks(12)
owner.ignite_mob()
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return FALSE
/obj/item/clothing/suit/armor/reactive/stealth
name = "reactive stealth armor"
desc = "An experimental suit of armor that renders the wearer invisible on detection of imminent harm, and creates a decoy that runs away from the owner. You can't fight what you can't see."
cooldown_message = span_danger("The reactive stealth system activates, but is not charged enough to fully cloak!")
emp_message = span_warning("The reactive stealth armor's threat assessment system crashes...")
///when triggering while on cooldown will only flicker the alpha slightly. this is how much it removes.
var/cooldown_alpha_removal = 50
///cooldown alpha flicker- how long it takes to return to the original alpha
var/cooldown_animation_time = 3 SECONDS
///how long they will be fully stealthed
var/stealth_time = 4 SECONDS
///how long it will animate back the alpha to the original
var/animation_time = 2 SECONDS
var/in_stealth = FALSE
/obj/item/clothing/suit/armor/reactive/stealth/cooldown_activation(mob/living/carbon/human/owner)
if(in_stealth)
return
owner.alpha = max(0, owner.alpha - cooldown_alpha_removal)
animate(owner, alpha = initial(owner.alpha), time = cooldown_animation_time)
..()
/obj/item/clothing/suit/armor/reactive/stealth/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text, final_block_chance, damage)
var/mob/living/simple_mob/illusion/decoy = new(owner.loc)
decoy.copy_appearance(owner)
decoy.copy_overlays(owner, TRUE)
var/datum/ai_holder/AI = decoy.ai_holder
var/turf/rand_turf = pick(get_turf(orange(5, 10)))
AI.give_destination(rand_turf)
owner.alpha = 0
in_stealth = TRUE
owner.visible_message(span_danger("[owner] is hit by [attack_text] in the chest!"))
addtimer(CALLBACK(src, PROC_REF(end_stealth), owner), stealth_time)
decoy.say("*sidestep")
addtimer(CALLBACK(src, PROC_REF(destroy_illusion), decoy), stealth_time)
QDEL_IN(decoy, stealth_time)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return TRUE
/obj/item/clothing/suit/armor/reactive/stealth/proc/end_stealth(mob/living/carbon/human/owner)
in_stealth = FALSE
animate(owner, alpha = initial(owner.alpha), time = animation_time)
/obj/item/clothing/suit/armor/reactive/stealth/proc/destroy_illusion(mob/illusion)
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread()
sparks.set_up(3, 3, illusion)
sparks.start()
QDEL_IN(illusion, animation_time)
/obj/item/clothing/suit/armor/reactive/stealth/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0)
if(!isliving(hitby))
return FALSE
var/mob/living/attacker = hitby
owner.visible_message(span_danger("[src] activates, cloaking the wrong person!"))
attacker.alpha = 0
addtimer(VARSET_CALLBACK(attacker, alpha, initial(attacker.alpha)), 4 SECONDS)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return FALSE
@@ -0,0 +1,96 @@
#define MAX_BARRIERS 4
#define MIN_BARRIERS 2
/datum/armour_dimensional_theme
var/datum/material/material
var/turf/replace_floor = /turf/simulated/floor/tiled
var/turf/replace_wall = /turf/simulated/wall
var/obj/barricade = /obj/structure/barricade
var/barricade_anchored = TRUE
/datum/armour_dimensional_theme/proc/apply_random(turf/source, dangerous = FALSE)
var/theme_type
if(dangerous)
theme_type = pick(subtypesof(/datum/armour_dimensional_theme/dangerous))
else
theme_type = pick(subtypesof(/datum/armour_dimensional_theme/safe))
var/datum/armour_dimensional_theme/theme = new theme_type()
theme.apply(source)
qdel(theme)
/datum/armour_dimensional_theme/proc/apply(turf/source)
var/obj/effect/effect/smoke/poof = new(source)
poof.time_to_live = 2 SECONDS
var/list/target_area = get_target_area(source)
for (var/turf/target in target_area)
convert_turf(target)
place_barriers(source, target_area)
/datum/armour_dimensional_theme/proc/get_target_area(turf/source)
var/list/target_area = RANGE_TURFS(1, source)
for (var/turf/check_turf as anything in target_area)
if(isspace(check_turf))
target_area -= check_turf
continue
if(istype(get_area(check_turf), /area/holodeck))
continue
return target_area
/datum/armour_dimensional_theme/proc/convert_turf(turf/to_convert)
if(isfloorturf(to_convert))
var/turf/simulated/floor/floor = to_convert
floor.ChangeTurf(replace_floor)
else if(iswall(to_convert))
to_convert.ChangeTurf(replace_wall)
/datum/armour_dimensional_theme/proc/place_barriers(turf/source, list/target_area)
target_area -= source
for(var/turf/check_turf as anything in target_area)
if (!check_turf.density)
continue
target_area -= check_turf
var/to_place = rand(MIN_BARRIERS, MAX_BARRIERS)
var/list/custom_materials = list()
if(material)
custom_materials = list(GET_MATERIAL_REF(material) = SHEET_MATERIAL_AMOUNT)
while(target_area.len > 0 && to_place > 0)
var/turf/place_turf = pick(target_area)
place_barrier(place_turf, custom_materials)
target_area -= place_turf
to_place--
/datum/armour_dimensional_theme/proc/place_barrier(turf/source)
var/obj/structure/barricade/placed_barricade = new barricade(source, material.name)
if(!barricade_anchored)
placed_barricade.anchored = FALSE
/datum/armour_dimensional_theme/safe
/datum/armour_dimensional_theme/safe/natural
replace_wall = /turf/simulated/wall/wood
replace_floor = /turf/simulated/floor/wood
barricade = /obj/structure/barricade
material = /datum/material/wood
/datum/armour_dimensional_theme/safe/snow
replace_wall = /turf/simulated/wall/snowbrick
replace_floor = /turf/simulated/floor/snow
material = /datum/material/snow
/datum/armour_dimensional_theme/dangerous
/datum/armour_dimensional_theme/dangerous/radioactive
replace_wall = /turf/simulated/wall/uranium
replace_floor = /turf/simulated/floor/tiled/material/uranium
material = /datum/material/uranium
/datum/armour_dimensional_theme/dangerous/phoron
replace_wall = /turf/simulated/wall/phoron
replace_floor = /turf/simulated/floor/tiled/material/phoron
material = /datum/material/phoron
#undef MAX_BARRIERS
#undef MIN_BARRIERS
+1 -1
View File
@@ -49,7 +49,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
// Traditional hallucinations
/////////////////////////////////////////////////////////////////////////////////////////////////////
/mob/living/carbon/proc/handle_hallucinations()
if(get_hallucination_component() || !client)
if(get_hallucination_component() || !client || HAS_TRAIT(src, TRAIT_MADNESS_IMMUNE))
return
LoadComponent(/datum/component/hallucinations)
+3
View File
@@ -663,6 +663,9 @@ var/list/mining_overlay_cache = list()
M.make_jittery(50) //SHAKY this used to be 1000(seizure) but I toned it to 50 to be less aggressive.
if(prob(25))
excavate_find(prob(25), finds[1])
if(prob(2))
var/anomaly = pick(FLUX_ANOMALY, GRAVITATIONAL_ANOMALY, PYRO_ANOMALY, HALLUCINATION_ANOMALY)
generate_anomaly(get_turf(src), anomaly, 1, FALSE)
else if(rand(1,500) == 1)
visible_message(span_notice("An old dusty crate was buried within!"))
new /obj/structure/closet/crate/secure/loot(src)
+1 -1
View File
@@ -1255,7 +1255,7 @@
Paralyse(3)
if(hallucination)
if(hallucination >= HALLUCINATION_THRESHOLD && !(species.flags & (NO_POISON|IS_PLANT|NO_HALLUCINATION)) )
if(hallucination >= HALLUCINATION_THRESHOLD && !(species.flags & (NO_POISON|IS_PLANT|NO_HALLUCINATION)) && !HAS_TRAIT(src, TRAIT_MADNESS_IMMUNE))
handle_hallucinations()
/* Stop spinning the view, it breaks too much.
if(client && prob(5))
@@ -301,6 +301,16 @@
if(grav_pulling)
supermatter_pull(src)
if(damage) // Start fucking things up
if(get_integrity() < 85 && prob(5))
generate_anomaly(get_ranged_target_turf(src, pick(GLOB.cardinal), rand(5, 10)), FLUX_ANOMALY)
if(get_integrity() < 75 && prob(5))
generate_anomaly(get_ranged_target_turf(src, pick(GLOB.cardinal), rand(5, 10)), HALLUCINATION_ANOMALY)
if(get_integrity() < 50 && prob(2))
generate_anomaly(get_ranged_target_turf(src, pick(GLOB.cardinal), rand(5, 10)), GRAVITATIONAL_ANOMALY)
if(get_integrity() < 25 && prob(0.3))
generate_anomaly(get_ranged_target_turf(src, pick(GLOB.cardinal), rand(5, 10)), PYRO_ANOMALY)
// Vary volume by power produced.
if(power)
// Volume will be 1 at no power, ~12.5 at ENERGY_NITROGEN, and 20+ at ENERGY_PHORON.
@@ -0,0 +1,92 @@
/obj/item/assembly/signaler/anomaly
name = "anomaly core"
desc = "The neutralized core of an anomaly. It'd probably be valuable for research."
icon_state = "anomaly_core"
var/anomaly_type = /obj/effect/anomaly
var/worth = 250 // Pricey... Should be hard-ish to obtain.
/obj/item/assembly/signaler/anomaly/Initialize(mapload)
. = ..()
if(worth)
AddElement(/datum/element/sellable)
/obj/item/assembly/signaler/anomaly/receive_signal(datum/signal/signal)
if(!signal)
return FALSE
if(signal.encryption != code)
return FALSE
for(var/obj/effect/anomaly/anomaly in get_turf(src))
anomaly.anomalyNeutralize()
return TRUE
/obj/item/assembly/signaler/anomaly/attack_self(mob/user)
return
/obj/item/assembly/signaler/anomaly/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/analyzer))
to_chat(user, span_notice("Analyzing... [src]'s stabilized field is fluctuating along frequency [format_frequency(frequency)], code [code]."))
return TRUE
if(istype(W, /obj/item/anomaly_releaser))
var/obj/item/anomaly_releaser/releaser = W
if(releaser.used)
return FALSE
if(!do_after(user, 3 SECONDS, src))
return FALSE
var/obj/item/assembly/signaler/anomaly/core = src
if(!core.anomaly_type)
return FALSE
var/obj/effect/anomaly/anomaly = new core.anomaly_type(get_turf(core))
anomaly.stabilize()
if(!releaser.infinite)
releaser.icon_state = releaser.used_icon_state
releaser.used = TRUE
releaser.name = "used " + name
qdel(src)
return ..()
/obj/item/assembly/signaler/anomaly/flux
name = "\improper flux anomaly core"
desc = "The neutralized core of a flux anomaly. Touching it makes your skin tingle. It'd probably be valuable for research."
icon_state = "flux_core"
anomaly_type = /obj/effect/anomaly/flux
/obj/item/assembly/signaler/anomaly/bluespace
name = "\improper bluespace anomaly core"
desc = "The neutralized core of a bluespace anomaly. It keeps phasing in and out of view. It'd probably be valuable for research."
icon_state = "anomaly_core"
anomaly_type = /obj/effect/anomaly/bluespace
/obj/item/assembly/signaler/anomaly/grav
name = "\improper gravitational anomaly core"
desc = "The neutralized core of a gravitational anomaly. It feels much heavier than it looks. It'd probably be valuable for research."
icon_state = "grav_core"
anomaly_type = /obj/effect/anomaly/grav
/obj/item/assembly/signaler/anomaly/dimensional
name = "\improper dimensional anomaly core"
desc = "The neutralized core of a dimensional anomaly. Objects reflected on its surface don't look quite right. It'd probably be valuable for research."
icon_state = "dimensional_core"
anomaly_type = /obj/effect/anomaly/dimensional
/obj/item/assembly/signaler/anomaly/bioscrambler
name = "\improper bioscrambler anomaly core"
desc = "The neutralized core of a bioscrambler anomaly. It's squirming, as if moving. It'd probably be valuable for research."
icon_state = "bioscrambler_core"
anomaly_type = /obj/effect/anomaly/bioscrambler
/obj/item/assembly/signaler/anomaly/hallucination
name = "\improper hallucination anomaly core"
desc = "The neutralized core of a hallucination anomaly. It seems to be moving, but it's probably your imagination. It'd probably be valuable for research."
icon_state = "hallucination_core"
anomaly_type = /obj/effect/anomaly/hallucination
/obj/item/assembly/signaler/anomaly/pyro
name = "\improper pyroclastic anomaly core"
desc = "The neutralized core of a pyroclastic anomaly. It feels warm to the touch. It'd probably be valuable for research."
icon_state = "pyro_core"
anomaly_type = /obj/effect/anomaly/pyro
@@ -276,3 +276,32 @@
RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_BLUESPACE
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE | DEPARTMENT_BITFLAG_CARGO
/datum/design_techweb/anomaly_neutralizer
name = "Anomaly Neutralizer"
desc = "An advanced tool capable of instantly neutralizing anomalies, designed to capture the fleeting aberrations created by the engine."
id = "anomaly_neutralizer"
build_type = PROTOLATHE
materials = list(MAT_STEEL = SHEET_MATERIAL_AMOUNT, MAT_GOLD = SHEET_MATERIAL_AMOUNT, MAT_PHORON = SHEET_MATERIAL_AMOUNT * 2.5, MAT_URANIUM = SHEET_MATERIAL_AMOUNT)
build_path = /obj/item/anomaly_neutralizer
category = list(
RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_SCIENCE
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
/datum/design_techweb/reactive_armour
name = "Reactive Armor Shell"
desc = "An experimental suit of armour capable of utilizing an implanted anomaly core to protect the user."
id = "reactive_armour"
build_type = PROTOLATHE
materials = list(
MAT_STEEL = SHEET_MATERIAL_AMOUNT*5,
MAT_URANIUM = SHEET_MATERIAL_AMOUNT*4,
MAT_DIAMOND = SHEET_MATERIAL_AMOUNT*2.5,
MAT_SILVER = SHEET_MATERIAL_AMOUNT*2.5,
MAT_GOLD = SHEET_MATERIAL_AMOUNT*2.5
)
build_path = /obj/item/clothing/suit/armor/reactive_armor_shell
category = list(
RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_SCIENCE
)
@@ -108,9 +108,9 @@
"xenoarch_multitool",
"excavationdrill",
"ano_scanner",
// "anomaly_refinery",
// "anomaly_neutralizer",
// "reactive_armour",
//"anomaly_refinery",
"anomaly_neutralizer",
"reactive_armour",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
announce_channels = list(CHANNEL_SCIENCE)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 KiB

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 KiB

After

Width:  |  Height:  |  Size: 792 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 KiB

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+17
View File
@@ -50,6 +50,7 @@
#include "code\__defines\airlock_control.dm"
#include "code\__defines\alerts.dm"
#include "code\__defines\ammunition.dm"
#include "code\__defines\anomaly.dm"
#include "code\__defines\appearance.dm"
#include "code\__defines\assemblies.dm"
#include "code\__defines\assert.dm"
@@ -338,6 +339,7 @@
#include "code\__defines\dcs\signals\signals_mob\signals_mob_simple.dm"
#include "code\__defines\dcs\signals\signals_mob\signals_mob_spawner.dm"
#include "code\__defines\dcs\signals\signals_mob\signals_mob_xenochimera.dm"
#include "code\__defines\research\anomalies.dm"
#include "code\__defines\research\research_categories.dm"
#include "code\__defines\research\techweb_nodes.dm"
#include "code\__defines\traits\_traits.dm"
@@ -1559,6 +1561,17 @@
#include "code\game\objects\effects\wire_deleter.dm"
#include "code\game\objects\effects\zone_divider.dm"
#include "code\game\objects\effects\alien\aliens.dm"
#include "code\game\objects\effects\anomalies\_anomalies.dm"
#include "code\game\objects\effects\anomalies\anomalies_bioscrambler.dm"
#include "code\game\objects\effects\anomalies\anomalies_bluespace.dm"
#include "code\game\objects\effects\anomalies\anomalies_dimensional.dm"
#include "code\game\objects\effects\anomalies\anomalies_dimensional_themes.dm"
#include "code\game\objects\effects\anomalies\anomalies_flux.dm"
#include "code\game\objects\effects\anomalies\anomalies_gravity.dm"
#include "code\game\objects\effects\anomalies\anomalies_hallucination.dm"
#include "code\game\objects\effects\anomalies\anomalies_pyroclastic.dm"
#include "code\game\objects\effects\anomalies\anomaly_placer.dm"
#include "code\game\objects\effects\anomalies\anomaly_spawner.dm"
#include "code\game\objects\effects\chem\chemsmoke.dm"
#include "code\game\objects\effects\chem\coating.dm"
#include "code\game\objects\effects\chem\foam.dm"
@@ -1640,6 +1653,7 @@
#include "code\game\objects\items\devices\advnifrepair.dm"
#include "code\game\objects\items\devices\ai_detector.dm"
#include "code\game\objects\items\devices\aicard.dm"
#include "code\game\objects\items\devices\anomaly.dm"
#include "code\game\objects\items\devices\binoculars.dm"
#include "code\game\objects\items\devices\body_snatcher_vr.dm"
#include "code\game\objects\items\devices\chameleonproj.dm"
@@ -2628,6 +2642,8 @@
#include "code\modules\clothing\suits\miscellaneous.dm"
#include "code\modules\clothing\suits\miscellaneous_vr.dm"
#include "code\modules\clothing\suits\neosuits.dm"
#include "code\modules\clothing\suits\reactive_armour.dm"
#include "code\modules\clothing\suits\reactive_armour_dimensional_themes.dm"
#include "code\modules\clothing\suits\shibari.dm"
#include "code\modules\clothing\suits\solgov.dm"
#include "code\modules\clothing\suits\solgov_vr.dm"
@@ -4442,6 +4458,7 @@
#include "code\modules\refinery\equipment\smart_centrifuge.dm"
#include "code\modules\research\message_server.dm"
#include "code\modules\research\part_replacer.dm"
#include "code\modules\research\anomaly\anomaly_core.dm"
#include "code\modules\research\tg\designs.dm"
#include "code\modules\research\tg\disks.dm"
#include "code\modules\research\tg\rdconsole.dm"