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>
@@ -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"
|
||||
@@ -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"
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))))
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 282 KiB After Width: | Height: | Size: 284 KiB |
|
Before Width: | Height: | Size: 798 KiB After Width: | Height: | Size: 792 KiB |
|
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 289 KiB |
|
Before Width: | Height: | Size: 378 KiB After Width: | Height: | Size: 378 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
@@ -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"
|
||||
|
||||