Compare commits
11 Commits
nanomaps_g
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6dc148bf2 | ||
|
|
93d51618fb | ||
|
|
ae684bd969 | ||
|
|
574d960872 | ||
|
|
ab8e6d3921 | ||
|
|
7971b00c5d | ||
|
|
e7feab24f7 | ||
|
|
d92c790876 | ||
|
|
989cae1d22 | ||
|
|
f4fe45f8d3 | ||
|
|
a8ad45d7b8 |
@@ -110,3 +110,20 @@
|
||||
**Creator / Copyright:** Toriate<br>
|
||||
**License Holders:** Matica<br>
|
||||
**License:** [CC BY-NC-SA 3.0](https://creativecommons.org/licenses/by-nc-sa/3.0/)<br>
|
||||
<br>
|
||||
**File:** `icons/obj/borkmedigun.dmi`<br>
|
||||
**Creator:** Commissioned by Cross_Exonar from Toriate<br>
|
||||
**Link:** https://github.com/TS-Rogue-Star/Rogue-Star/pull/1010<br>
|
||||
**License:** [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/)<br>
|
||||
<br>
|
||||
**File:** `icons/mob/items/lefthand_medigun.dmi`<br>
|
||||
**Icon-States:**medblaster-wielded, medblaster, medblaster_cmo<br>
|
||||
**Creator:** Commissioned by Cross_Exonar from Toriate<br>
|
||||
**Link:** https://github.com/TS-Rogue-Star/Rogue-Star/pull/1010<br>
|
||||
**License:** [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/)<br>
|
||||
<br>
|
||||
**File:** `icons/mob/items/righthand_medigun.dmi`<br>
|
||||
**Icon-States:**medblaster-wielded, medblaster, medblaster_cmo<br>
|
||||
**Creator:** Commissioned by Cross_Exonar from Toriate<br>
|
||||
**Link:** https://github.com/TS-Rogue-Star/Rogue-Star/pull/1010<br>
|
||||
**License:** [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/)<br>
|
||||
|
||||
47
code/__defines/anomaly.dm
Normal 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"
|
||||
@@ -168,7 +168,8 @@ NOTICE: Do not leave trailing commas!!!!
|
||||
#define POCKET_BAYSUIT \
|
||||
/obj/item/storage/backpack, \
|
||||
/obj/item/bluespaceradio, \
|
||||
/obj/item/defib_kit
|
||||
/obj/item/defib_kit, \
|
||||
/obj/item/medigun_backpack
|
||||
|
||||
/// Wrapper for adding clothing based traits
|
||||
#define ADD_CLOTHING_TRAIT(mob, trait) ADD_TRAIT(mob, trait, "[CLOTHING_TRAIT]_[REF(src)]")
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -98,3 +98,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))))
|
||||
|
||||
3
code/__defines/medigun.dm
Normal file
@@ -0,0 +1,3 @@
|
||||
#define MEDIGUN_IDLE 0
|
||||
#define MEDIGUN_CANCELLED 1
|
||||
#define MEDIGUN_BUSY 2
|
||||
7
code/__defines/research/anomalies.dm
Normal 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
|
||||
@@ -85,6 +85,7 @@
|
||||
#define TECHWEB_NODE_MEDBAY_EQUIP_ADV "medbay_equip_adv"
|
||||
#define TECHWEB_NODE_MEDBAY_EQUIP_HIGH_TECH "medbay_equip_high_tech"
|
||||
#define TECHWEB_NODE_MEDIGUN "medbay_medigun"
|
||||
#define TECHWEB_NODE_MEDIGUN_CONSTANT "medbay_medigun_constant"
|
||||
#define TECHWEB_NODE_MINING "mining"
|
||||
#define TECHWEB_NODE_MINING_ADV "mining_adv"
|
||||
#define TECHWEB_NODE_MOD_ANOMALY "mod_anomaly"
|
||||
|
||||
@@ -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"
|
||||
|
||||
32
code/__defines/ventcrawl.dm
Normal file
@@ -0,0 +1,32 @@
|
||||
// DO NOT LEAVE A TRAILING COMMA!
|
||||
|
||||
/// Things that are always considered legal to ventcrawl with. Usually because they are internal objects related to mob or game functionality.
|
||||
#define VENTCRAWL_BASE_WHITELIST /atom/movable/screen, \
|
||||
/atom/movable/emissive_blocker, \
|
||||
/obj/machinery/camera, \
|
||||
/obj/item/radio/headset/mob_headset, \
|
||||
/obj/item/radio/borg, \
|
||||
/obj/item/rig/protean, \
|
||||
/obj/item/implant
|
||||
//mob/living/simple_mob/borer, //VORESTATION AI TEMPORARY REMOVAL REPLACE BACK IN LIST WHEN RESOLVED
|
||||
|
||||
/// Vore unique objects
|
||||
#define VENTCRAWL_VORE_WHITELIST /obj/belly, \
|
||||
/obj/soulgem, \
|
||||
/obj/item/holder
|
||||
|
||||
/// Reasonable items with a low chance of causing exploits, mostly for catslugs but allowed by default on other vent crawlers
|
||||
#define VENTCRAWL_SMALLITEM_WHITELIST /obj/item/coin, \
|
||||
/obj/item/aliencoin, \
|
||||
/obj/item/toy, \
|
||||
/obj/item/clipboard, \
|
||||
/obj/item/paper, \
|
||||
/obj/item/pen, \
|
||||
/obj/item/canvas, \
|
||||
/obj/item/paint_palette, \
|
||||
/obj/item/paint_brush, \
|
||||
/obj/item/camera, \
|
||||
/obj/item/photo, \
|
||||
/obj/item/camera_film, \
|
||||
/obj/item/taperecorder, \
|
||||
/obj/item/rectape
|
||||
@@ -28,6 +28,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
|
||||
|
||||
@@ -1066,7 +1074,6 @@ GLOBAL_LIST_EMPTY(json_cache)
|
||||
retval += to_flatten[i]
|
||||
return retval
|
||||
|
||||
//CHOMPAdd start
|
||||
/proc/pick_weight(list/list_to_pick)
|
||||
var/total = 0
|
||||
var/item
|
||||
@@ -1082,22 +1089,3 @@ GLOBAL_LIST_EMPTY(json_cache)
|
||||
return item
|
||||
|
||||
return null
|
||||
|
||||
///Converts a bitfield to a list of numbers (or words if a wordlist is provided)
|
||||
/proc/bitfield_to_list(bitfield = 0, list/wordlist)
|
||||
var/list/return_list = list()
|
||||
if(islist(wordlist))
|
||||
var/max = min(wordlist.len, 24)
|
||||
var/bit = 1
|
||||
for(var/i in 1 to max)
|
||||
if(bitfield & bit)
|
||||
return_list += wordlist[i]
|
||||
bit = bit << 1
|
||||
else
|
||||
for(var/bit_number = 0 to 23)
|
||||
var/bit = 1 << bit_number
|
||||
if(bitfield & bit)
|
||||
return_list += bit
|
||||
|
||||
return return_list
|
||||
//CHOMPAdd end
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -80,7 +80,7 @@ var/list/outfits_decls_by_type_
|
||||
if(1) l_ear = headset
|
||||
if(2) l_ear = headset_alt
|
||||
if(3) l_ear = headset_earbud
|
||||
if(flags && OUTFIT_HAS_BACKPACK)
|
||||
if(flags & OUTFIT_HAS_BACKPACK)
|
||||
switch(H.backbag)
|
||||
if(2) back = backpack
|
||||
if(3) back = satchel_one
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -16,7 +16,8 @@ GLOBAL_LIST_INIT(allowed_recharger_devices, list(
|
||||
/obj/item/paicard,
|
||||
/obj/item/personal_shield_generator,
|
||||
/obj/item/gun/projectile/cell_loaded,
|
||||
/obj/item/ammo_magazine/cell_mag
|
||||
/obj/item/ammo_magazine/cell_mag,
|
||||
/obj/item/medigun_backpack
|
||||
))
|
||||
|
||||
GLOBAL_LIST_INIT(allowed_wallcharger_devices, list(
|
||||
|
||||
121
code/game/objects/effects/anomalies/_anomalies.dm
Normal file
@@ -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()
|
||||
94
code/game/objects/effects/anomalies/anomalies_bluespace.dm
Normal file
@@ -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
|
||||
90
code/game/objects/effects/anomalies/anomalies_dimensional.dm
Normal file
@@ -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/)
|
||||
)
|
||||
63
code/game/objects/effects/anomalies/anomalies_flux.dm
Normal file
@@ -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 ..()
|
||||
89
code/game/objects/effects/anomalies/anomalies_gravity.dm
Normal file
@@ -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
|
||||
109
code/game/objects/effects/anomalies/anomalies_hallucination.dm
Normal file
@@ -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
|
||||
34
code/game/objects/effects/anomalies/anomalies_pyroclastic.dm
Normal file
@@ -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()
|
||||
45
code/game/objects/effects/anomalies/anomaly_placer.dm
Normal file
@@ -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
|
||||
17
code/game/objects/effects/anomalies/anomaly_spawner.dm
Normal file
@@ -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
|
||||
|
||||
43
code/game/objects/items/devices/anomaly.dm
Normal file
@@ -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
|
||||
115
code/game/objects/items/weapons/medigun/bork_medigun.dm
Normal file
@@ -0,0 +1,115 @@
|
||||
/obj/item/bork_medigun
|
||||
name = "prototype bluespace medigun"
|
||||
desc = "The Bork BSM-92 or 'Blue Space Medigun' utilizes advanced bluespace technology to transfer beneficial reagents directly to torn tissue. This way, even larger wounds can be mended efficiently in short periods of time"
|
||||
icon = 'icons/obj/borkmedigun.dmi'
|
||||
icon_state = "medblaster"
|
||||
var/wielded_item_state = "medblaster-wielded"
|
||||
var/base_icon_state = "medblaster"
|
||||
item_icons = list(
|
||||
slot_l_hand_str = 'icons/mob/items/lefthand_medigun.dmi',
|
||||
slot_r_hand_str = 'icons/mob/items/righthand_medigun.dmi',
|
||||
)
|
||||
w_class = ITEMSIZE_HUGE
|
||||
force = 0
|
||||
var/beam_range = 3 // How many tiles away it can scan. Changing this also changes the box size.
|
||||
var/busy = MEDIGUN_IDLE // Set to true when scanning, to stop multiple scans.
|
||||
var/action_cancelled = FALSE
|
||||
var/wielded = FALSE
|
||||
var/mob/current_target
|
||||
var/mgcmo
|
||||
canremove = FALSE
|
||||
|
||||
/obj/item/bork_medigun/update_twohanding()
|
||||
var/mob/living/M = loc
|
||||
if(istype(M) && M.item_is_in_hands(src) && !M.hands_are_full())
|
||||
wielded = TRUE
|
||||
name = "[initial(name)] (wielded)"
|
||||
else
|
||||
wielded = FALSE
|
||||
name = initial(name)
|
||||
..()
|
||||
|
||||
/obj/item/bork_medigun/update_held_icon()
|
||||
if(wielded_item_state)
|
||||
var/mob/living/M = loc
|
||||
if(istype(M))
|
||||
if(M.can_wield_item(src) && src.is_held_twohanded(M))
|
||||
LAZYSET(item_state_slots, slot_l_hand_str, wielded_item_state)
|
||||
LAZYSET(item_state_slots, slot_r_hand_str, wielded_item_state)
|
||||
else
|
||||
LAZYSET(item_state_slots, slot_l_hand_str, initial(item_state))
|
||||
LAZYSET(item_state_slots, slot_r_hand_str, initial(item_state))
|
||||
..()
|
||||
|
||||
// Draws a box showing the limits of movement while scanning something.
|
||||
// Only the client supplied will see the box.
|
||||
/obj/item/bork_medigun/proc/draw_box(atom/A, box_size, client/C)
|
||||
var/list/our_box = list()
|
||||
// Things moved with pixel_[x|y] will move the box, so this is to correct that.
|
||||
var/pixel_x_correction = -A.pixel_x
|
||||
var/pixel_y_correction = -A.pixel_y
|
||||
|
||||
var/our_icon_size = world.icon_size
|
||||
// First, place the bottom-left corner.
|
||||
our_box += draw_line(A, SOUTHWEST, (-box_size * our_icon_size) + pixel_x_correction, (-box_size * our_icon_size) + pixel_y_correction, C)
|
||||
|
||||
var/rendered_size = (box_size * 2) - 1
|
||||
// Make a line on the bottom, going right.
|
||||
for(var/i = 1 to rendered_size)
|
||||
var/x_displacement = (-box_size * our_icon_size) + (our_icon_size * i) + pixel_x_correction
|
||||
var/y_displacement = (-box_size * our_icon_size) + pixel_y_correction
|
||||
our_box += draw_line(A, SOUTH, x_displacement, y_displacement, C)
|
||||
|
||||
// Bottom-right corner.
|
||||
our_box += draw_line(A, SOUTHEAST, (box_size * our_icon_size) + pixel_x_correction, (-box_size * our_icon_size) + pixel_y_correction, C)
|
||||
|
||||
// Second line, for the right side going up.
|
||||
for(var/i = 1 to rendered_size)
|
||||
var/x_displacement = (box_size * our_icon_size) + pixel_x_correction
|
||||
var/y_displacement = (-box_size * our_icon_size) + (our_icon_size * i) + pixel_y_correction
|
||||
our_box += draw_line(A, EAST, x_displacement, y_displacement, C)
|
||||
|
||||
// Top-right corner.
|
||||
our_box += draw_line(A, NORTHEAST, (box_size * our_icon_size) + pixel_x_correction, (box_size * our_icon_size) + pixel_y_correction, C)
|
||||
|
||||
// Third line, for the top, going right.
|
||||
for(var/i = 1 to rendered_size)
|
||||
var/x_displacement = (-box_size * our_icon_size) + (our_icon_size * i) + pixel_x_correction
|
||||
var/y_displacement = (box_size * our_icon_size) + pixel_y_correction
|
||||
our_box += draw_line(A, NORTH, x_displacement, y_displacement, C)
|
||||
|
||||
// Top-left corner.
|
||||
our_box += draw_line(A, NORTHWEST, (-box_size * our_icon_size) + pixel_x_correction, (box_size * our_icon_size) + pixel_y_correction, C)
|
||||
|
||||
// Fourth and last line, for the left side going up.
|
||||
for(var/i = 1 to rendered_size)
|
||||
var/x_displacement = (-box_size * our_icon_size) + pixel_x_correction
|
||||
var/y_displacement = (-box_size * our_icon_size) + (our_icon_size * i) + pixel_y_correction
|
||||
our_box += draw_line(A, WEST, x_displacement, y_displacement, C)
|
||||
return our_box
|
||||
|
||||
// Draws an individual segment of the box.
|
||||
/obj/item/bork_medigun/proc/draw_line(atom/A, line_dir, line_pixel_x, line_pixel_y, client/C)
|
||||
var/image/line = image(icon = 'icons/effects/effects.dmi', loc = A, icon_state = "stripes", dir = line_dir)
|
||||
line.pixel_x = line_pixel_x
|
||||
line.pixel_y = line_pixel_y
|
||||
line.plane = PLANE_FULLSCREEN // It's technically a HUD element but it doesn't need to show above item slots.
|
||||
line.appearance_flags = RESET_TRANSFORM|RESET_COLOR|RESET_ALPHA|NO_CLIENT_COLOR|TILE_BOUND
|
||||
line.alpha = 125
|
||||
C.images += line
|
||||
return line
|
||||
|
||||
// Removes the box that was generated before from the client.
|
||||
/obj/item/bork_medigun/proc/delete_box(list/box_segments, client/C)
|
||||
for(var/i in box_segments)
|
||||
C.images -= i
|
||||
qdel(i)
|
||||
|
||||
/obj/item/bork_medigun/proc/color_box(list/box_segments, new_color, new_time)
|
||||
for(var/i in box_segments)
|
||||
animate(i, color = new_color, time = new_time)
|
||||
|
||||
/obj/item/bork_medigun/attack_hand(mob/user)
|
||||
if(user.get_inactive_hand() == src)// && loc != get_turf)
|
||||
return
|
||||
return ..()
|
||||
280
code/game/objects/items/weapons/medigun/linked_medigun.dm
Normal file
@@ -0,0 +1,280 @@
|
||||
/obj/item/bork_medigun/linked
|
||||
var/obj/item/medigun_backpack/medigun_base_unit
|
||||
|
||||
/obj/item/bork_medigun/linked/Destroy()
|
||||
if(medigun_base_unit)
|
||||
var/obj/item/bork_medigun/medigun = medigun_base_unit.get_medigun()
|
||||
//ensure the base unit's icon updates
|
||||
if(medigun == src)
|
||||
medigun = null
|
||||
medigun_base_unit.replace_icon()
|
||||
if(ismob(loc))
|
||||
var/mob/user = loc
|
||||
user.update_inv_back()
|
||||
medigun_base_unit = null
|
||||
return ..()
|
||||
|
||||
/obj/item/bork_medigun/linked/forceMove(atom/destination) //Forcemove override, ugh
|
||||
if(destination == medigun_base_unit || destination == medigun_base_unit.loc || isturf(destination))
|
||||
. = doMove(destination, 0, 0)
|
||||
if(isturf(destination))
|
||||
for(var/atom/A as anything in destination) // If we can't scan the turf, see if we can scan anything on it, to help with aiming.
|
||||
if(istype(A,/obj/structure/closet ))
|
||||
break
|
||||
|
||||
/obj/item/bork_medigun/linked/proc/check_charge(var/charge_amt)
|
||||
return (medigun_base_unit.bcell && medigun_base_unit.bcell.check_charge(charge_amt))
|
||||
|
||||
/obj/item/bork_medigun/linked/proc/checked_use(var/charge_amt)
|
||||
return (medigun_base_unit.bcell && medigun_base_unit.bcell.checked_use(charge_amt))
|
||||
|
||||
/obj/item/bork_medigun/linked/attack_self(mob/living/user)
|
||||
if(medigun_base_unit.is_twohanded())
|
||||
update_twohanding()
|
||||
if(busy)
|
||||
busy = MEDIGUN_CANCELLED
|
||||
|
||||
/obj/item/bork_medigun/linked/proc/should_stop(var/mob/living/target, var/mob/living/user, var/active_hand)
|
||||
if(!target || !user || (!active_hand && medigun_base_unit.is_twohanded()) || !istype(target) || !istype(user) || busy < MEDIGUN_BUSY)
|
||||
return TRUE
|
||||
|
||||
if((user.get_active_hand() != active_hand || wielded == 0) && medigun_base_unit.is_twohanded())
|
||||
to_chat(user, span_warning("Please keep your hands free!"))
|
||||
return TRUE
|
||||
|
||||
if(user.is_incorporeal())
|
||||
return TRUE
|
||||
|
||||
if(user.incapacitated(INCAPACITATION_DEFAULT | INCAPACITATION_KNOCKDOWN | INCAPACITATION_DISABLED | INCAPACITATION_KNOCKOUT | INCAPACITATION_STUNNED | INCAPACITATION_RESTRAINED))
|
||||
return TRUE
|
||||
|
||||
if(user.stat)
|
||||
return TRUE
|
||||
|
||||
if(target.isSynthetic())
|
||||
to_chat(user, span_warning("Target is not organic."))
|
||||
return TRUE
|
||||
|
||||
//if(get_dist(user, target) > beam_range)
|
||||
if(!(target in range(beam_range, user)) || (!(target in view(10, user)) && !(medigun_base_unit.smodule.get_rating() >= 5)))
|
||||
to_chat(user, span_warning("You are too far away from \the [target] to heal them, Or they are not in view. Get closer."))
|
||||
return TRUE
|
||||
|
||||
if(!isliving(target))
|
||||
//to_chat(user, span_warning("\the [target] is not a valid target."))
|
||||
return TRUE
|
||||
|
||||
if(!ishuman(target))
|
||||
return TRUE
|
||||
|
||||
/*if(!H.getBruteLoss() && !H.getFireLoss() && !H.getToxLoss())// && !H.getOxyLoss()) // No point Wasting fuel/power if target healed
|
||||
playsound(src, 'sound/machines/ping.ogg', 50)
|
||||
to_chat(user, span_warning("\the [target] is fully healed."))
|
||||
return TRUE
|
||||
*/
|
||||
return FALSE
|
||||
|
||||
/obj/item/bork_medigun/linked/afterattack(atom/target, mob/user, proximity_flag)
|
||||
// Things that invalidate the scan immediately.
|
||||
if(isturf(target))
|
||||
for(var/atom/A as anything in target) // If we can't scan the turf, see if we can scan anything on it, to help with aiming.
|
||||
if(isliving(A))
|
||||
target = A
|
||||
break
|
||||
if(!istype(medigun_base_unit, /obj/item/medigun_backpack/cmo))
|
||||
update_twohanding()
|
||||
if(busy && !(target == current_target) && isliving(target))
|
||||
to_chat(user, span_warning("\The [src] is already targeting something."))
|
||||
return
|
||||
|
||||
if(!ishuman(target))
|
||||
return
|
||||
|
||||
if(!medigun_base_unit.smanipulator)
|
||||
to_chat(user, span_warning("\The [src] Blinks a red error light, Manipulator missing."))
|
||||
return
|
||||
if(!medigun_base_unit.scapacitor)
|
||||
to_chat(user, span_warning("\The [src] Blinks a blue error light, capacitor missing."))
|
||||
return
|
||||
if(!medigun_base_unit.slaser)
|
||||
to_chat(user, span_warning("\The [src] Blinks an orange error light, laser missing."))
|
||||
return
|
||||
if(!medigun_base_unit.smodule)
|
||||
to_chat(user, span_warning("\The [src] Blinks a pink error light, scanning module missing."))
|
||||
return
|
||||
if(!check_charge(5))
|
||||
to_chat(user, span_warning("\The [src] doesn't have enough charge left to do that."))
|
||||
return
|
||||
if(get_dist(target, user) > beam_range)
|
||||
to_chat(user, span_warning("You are too far away from \the [target] to affect it. Get closer."))
|
||||
return
|
||||
|
||||
if(target == current_target && busy)
|
||||
busy = MEDIGUN_CANCELLED
|
||||
return
|
||||
if(target == user)
|
||||
to_chat(user, span_warning("Cant heal yourself."))
|
||||
return
|
||||
if(!(target in range(beam_range, user)) || (!(target in view(10, user)) && !medigun_base_unit.smodule))
|
||||
to_chat(user, span_warning("You are too far away from \the [target] to heal them, Or they are not in view. Get closer."))
|
||||
return
|
||||
|
||||
current_target = target
|
||||
busy = MEDIGUN_BUSY
|
||||
update_icon()
|
||||
var/myicon = "medbeam_basic"
|
||||
var/mycolor = "#037ffc"
|
||||
var/datum/beam/scan_beam = user.Beam(target, icon = 'icons/obj/borkmedigun.dmi', icon_state = myicon, time = 6000)
|
||||
var/filter = filter(type = "outline", size = 1, color = mycolor)
|
||||
var/list/box_segments = list()
|
||||
playsound(src, 'sound/weapons/wave.ogg', 50)
|
||||
var/mob/living/carbon/human/H = target
|
||||
to_chat(user, span_notice("Locking on to [H]"))
|
||||
to_chat(H, span_warning("[user] is targetting you with their medigun"))
|
||||
if(user.client)
|
||||
box_segments = draw_box(target, beam_range, user.client)
|
||||
color_box(box_segments, mycolor, 5)
|
||||
process_medigun(H, user, filter)
|
||||
|
||||
action_cancelled = FALSE
|
||||
busy = MEDIGUN_IDLE
|
||||
current_target = null
|
||||
|
||||
// Now clean up the effects.
|
||||
update_icon()
|
||||
QDEL_NULL(scan_beam)
|
||||
target.filters -= filter
|
||||
if(user.client) // If for some reason they logged out mid-scan the box will be gone anyways.
|
||||
delete_box(box_segments, user.client)
|
||||
|
||||
/obj/item/bork_medigun/linked/proc/process_medigun(mob/living/carbon/human/H, mob/user, filter, ishealing = FALSE)
|
||||
if(should_stop(H, user, user.get_active_hand()))
|
||||
return
|
||||
|
||||
if(do_after(user, 1 SECOND, user, timed_action_flags = IGNORE_USER_LOC_CHANGE, hidden = TRUE))
|
||||
var/washealing = ishealing // Did we heal last cycle
|
||||
ishealing = FALSE // The default is 'we didn't heal this cycle'
|
||||
if(!checked_use(5))
|
||||
to_chat(user, span_warning("\The [src] doesn't have enough charge left to do that."))
|
||||
return
|
||||
if(H.stat == DEAD)
|
||||
process_medigun(H, user, filter)
|
||||
return
|
||||
var/lastier = medigun_base_unit.slaser.get_rating()
|
||||
if(lastier >= 2)
|
||||
if(checked_use(5))
|
||||
H.add_modifier(/datum/modifier/medbeameffect, 2 SECONDS)
|
||||
if(H.getHalLoss() && checked_use(5))
|
||||
H.adjustHalLoss(-20)
|
||||
if(H.weakened && checked_use(5))
|
||||
H.AdjustWeakened(-1)
|
||||
if(lastier >= 3)
|
||||
if(H.paralysis && (checked_use(15)))
|
||||
H.AdjustParalysis(-1)
|
||||
|
||||
var/healmod = lastier
|
||||
/*if(H.getBruteLoss())
|
||||
healmod = min(lastier,medigun_base_unit.brutecharge,H.getBruteLoss())
|
||||
if(medigun_base_unit.brutecharge >= healmod)
|
||||
if(!checked_use(healmod))
|
||||
to_chat(user, span_warning("\The [src] doesn't have enough charge left to do that."))
|
||||
break
|
||||
if(healmod < 0)
|
||||
healmod = 0
|
||||
else
|
||||
H.adjustBruteLoss(-healmod)
|
||||
medigun_base_unit.brutecharge -= healmod
|
||||
ishealing = 1
|
||||
if(H.getFireLoss())
|
||||
healmod = min(lastier,medigun_base_unit.burncharge,H.getFireLoss())
|
||||
if(medigun_base_unit.burncharge >= healmod)
|
||||
if(!checked_use(healmod))
|
||||
to_chat(user, span_warning("\The [src] doesn't have enough charge left to do that."))
|
||||
break
|
||||
if(healmod < 0)
|
||||
healmod = 0
|
||||
else
|
||||
H.adjustFireLoss(-healmod)
|
||||
medigun_base_unit.burncharge -= healmod
|
||||
ishealing = 1*/
|
||||
if(H.getToxLoss())
|
||||
healmod = min(lastier,medigun_base_unit.toxcharge,H.getToxLoss())
|
||||
if(medigun_base_unit.toxcharge >= healmod)
|
||||
if(!checked_use(healmod))
|
||||
to_chat(user, span_warning("\The [src] doesn't have enough charge left to do that."))
|
||||
return
|
||||
if(healmod < 0)
|
||||
healmod = 0
|
||||
else
|
||||
H.adjustToxLoss(-healmod)
|
||||
medigun_base_unit.toxcharge -= healmod
|
||||
ishealing = TRUE
|
||||
if(H.getOxyLoss())
|
||||
healmod = min(10*lastier,H.getOxyLoss())
|
||||
if(!checked_use(min(10,healmod)))
|
||||
to_chat(user, span_warning("\The [src] doesn't have enough charge left to do that."))
|
||||
return
|
||||
H.adjustOxyLoss(-healmod)
|
||||
ishealing = TRUE
|
||||
|
||||
ishealing = process_wounds(H, lastier, lastier, ishealing)
|
||||
//if(medigun_base_unit.brutecharge <= 0 || medigun_base_unit.burncharge <= 0 || medigun_base_unit.toxcharge <= 0)
|
||||
medigun_base_unit.update_icon()
|
||||
//if(medigun_base_unit.slaser.get_rating() >= 5)
|
||||
|
||||
//Blood regeneration if there is some space
|
||||
if(lastier >= 5)
|
||||
if(H.vessel.get_reagent_amount("blood") < H.species.blood_volume)
|
||||
var/datum/reagent/blood/B = locate() in H.vessel.reagent_list //Grab some blood
|
||||
B.volume += min(5, (H.species.blood_volume - H.vessel.get_reagent_amount("blood")))// regenerate blood
|
||||
|
||||
if(ishealing != washealing) // Either we stopped or started healing this cycle
|
||||
if(ishealing)
|
||||
H.filters += filter
|
||||
else
|
||||
H.filters -= filter
|
||||
|
||||
process_medigun(H, user, filter, ishealing)
|
||||
|
||||
/obj/item/bork_medigun/linked/proc/process_wounds(mob/living/carbon/human/H, heal_ticks, remaining_strength, ishealing)
|
||||
while(heal_ticks > 0)
|
||||
if(remaining_strength <= 0)
|
||||
return ishealing
|
||||
if((!H.getFireLoss() || medigun_base_unit.burncharge <= 0) && (!H.getBruteLoss() || medigun_base_unit.burncharge <= 0))
|
||||
return ishealing
|
||||
|
||||
for(var/name in BP_ALL)
|
||||
var/obj/item/organ/external/O = H.organs_by_name[name]
|
||||
for(var/datum/wound/W in O.wounds)
|
||||
if (W.internal)
|
||||
continue
|
||||
//if (W.bandaged && W.disinfected)
|
||||
// continue
|
||||
if (W.damage_type == BRUISE || W.damage_type == CUT || W.damage_type == PIERCE)
|
||||
if(medigun_base_unit.brutecharge >= 1)
|
||||
if(W.damage <= 1)
|
||||
O.wounds -= W
|
||||
medigun_base_unit.brutecharge -= 1
|
||||
ishealing = TRUE
|
||||
else if(medigun_base_unit.brutecharge >= 1)
|
||||
W.damage -= 1
|
||||
medigun_base_unit.brutecharge -= 1
|
||||
remaining_strength -= 1
|
||||
ishealing = TRUE
|
||||
if (W.damage_type == BURN)
|
||||
if(medigun_base_unit.burncharge >= 1)
|
||||
if(W.damage <= 1)
|
||||
O.wounds -= W
|
||||
medigun_base_unit.burncharge -= 1
|
||||
ishealing = TRUE
|
||||
else if(medigun_base_unit.burncharge >= 1)
|
||||
W.damage -= 1
|
||||
medigun_base_unit.burncharge -= 1
|
||||
remaining_strength -= 1
|
||||
ishealing = TRUE
|
||||
if(remaining_strength <= 0)
|
||||
return ishealing
|
||||
if(remaining_strength <= 0)
|
||||
return ishealing
|
||||
heal_ticks--
|
||||
return ishealing
|
||||
569
code/game/objects/items/weapons/medigun/medigun_backpack.dm
Normal file
@@ -0,0 +1,569 @@
|
||||
//backpack item
|
||||
/obj/item/medigun_backpack
|
||||
name = "protoype bluespace medigun backpack"
|
||||
desc = "Contains a bluespace medigun, this portable unit digitizes and stores chems and battery power used by the attached gun."
|
||||
icon = 'icons/obj/borkmedigun.dmi'
|
||||
icon_override = 'icons/obj/borkmedigun.dmi'
|
||||
icon_state = "mg-backpack"
|
||||
item_state = "mg-backpack-onmob"
|
||||
slot_flags = SLOT_BACK
|
||||
force = 5
|
||||
throwforce = 6
|
||||
preserve_item = TRUE
|
||||
w_class = ITEMSIZE_HUGE
|
||||
unacidable = TRUE
|
||||
origin_tech = list(TECH_BIO = 4, TECH_POWER = 2, TECH_BLUESPACE = 4)
|
||||
|
||||
var/obj/item/bork_medigun/linked/medigun_path = /obj/item/bork_medigun/linked
|
||||
var/obj/item/cell/bcell = /obj/item/cell
|
||||
var/obj/item/cell/ccell = null
|
||||
var/obj/item/stock_parts/matter_bin/sbin = /obj/item/stock_parts/matter_bin
|
||||
var/obj/item/stock_parts/scanning_module/smodule = /obj/item/stock_parts/scanning_module
|
||||
var/obj/item/stock_parts/manipulator/smanipulator = /obj/item/stock_parts/manipulator
|
||||
var/obj/item/stock_parts/capacitor/scapacitor = /obj/item/stock_parts/capacitor
|
||||
var/obj/item/stock_parts/micro_laser/slaser = /obj/item/stock_parts/micro_laser
|
||||
var/charging = FALSE
|
||||
var/brutecharge = 0
|
||||
var/toxcharge = 0
|
||||
var/burncharge = 0
|
||||
var/brutevol = 0
|
||||
var/toxvol = 0
|
||||
var/burnvol = 0
|
||||
var/chemcap = 60
|
||||
var/tankmax = 30
|
||||
var/containsgun = TRUE
|
||||
var/maintenance = FALSE
|
||||
var/smaniptier = 1
|
||||
var/sbintier = 1
|
||||
var/gridstatus = 0
|
||||
var/chargecap = 1000
|
||||
|
||||
//backpack item
|
||||
/obj/item/medigun_backpack/cmo
|
||||
name = "prototype bluespace medigun backpack - CMO"
|
||||
desc = "Contains a compact version of the bluespace medigun able to be used one handed, this portable unit digitizes and stores chems and battery power used by the attached gun."
|
||||
icon_state = "mg-backpack_cmo"
|
||||
item_state = "mg-backpack_cmo-onmob"
|
||||
scapacitor = /obj/item/stock_parts/capacitor/adv
|
||||
smanipulator = /obj/item/stock_parts/manipulator/nano
|
||||
smodule = /obj/item/stock_parts/scanning_module/adv
|
||||
slaser = /obj/item/stock_parts/micro_laser/high
|
||||
bcell = /obj/item/cell/apc
|
||||
tankmax = 60
|
||||
brutecharge = 60
|
||||
toxcharge = 60
|
||||
burncharge = 60
|
||||
chemcap = 120
|
||||
brutevol = 120
|
||||
toxvol = 120
|
||||
burnvol = 120
|
||||
chargecap = 5000
|
||||
|
||||
/obj/item/medigun_backpack/proc/is_twohanded()
|
||||
return TRUE
|
||||
|
||||
/obj/item/medigun_backpack/cmo/is_twohanded()
|
||||
return FALSE
|
||||
|
||||
/obj/item/medigun_backpack/proc/apc_charge()
|
||||
gridstatus = 0
|
||||
var/area/A = get_area(src)
|
||||
if(!istype(A) || !A.powered(EQUIP))
|
||||
return FALSE
|
||||
gridstatus = 1
|
||||
if(bcell && (bcell.charge < bcell.maxcharge))
|
||||
var/cur_charge = bcell.charge
|
||||
var/delta = min(50, bcell.maxcharge-cur_charge)
|
||||
bcell.give(delta)
|
||||
A.use_power_oneoff(delta*100, EQUIP)
|
||||
gridstatus = 2
|
||||
return TRUE
|
||||
|
||||
/obj/item/medigun_backpack/proc/adjust_brutevol(modifier)
|
||||
if(modifier > brutevol)
|
||||
modifier = brutevol
|
||||
if(modifier > (tankmax - brutecharge))
|
||||
modifier = tankmax - brutecharge
|
||||
brutevol -= modifier
|
||||
brutecharge += modifier
|
||||
|
||||
/obj/item/medigun_backpack/proc/adjust_burnvol(modifier)
|
||||
if(modifier > burnvol)
|
||||
modifier = burnvol
|
||||
if(modifier > (tankmax - burncharge))
|
||||
modifier = tankmax - burncharge
|
||||
burnvol -= modifier
|
||||
burncharge += modifier
|
||||
|
||||
/obj/item/medigun_backpack/proc/adjust_toxvol(modifier)
|
||||
if(modifier > toxvol)
|
||||
modifier = toxvol
|
||||
if(modifier > (tankmax - toxcharge))
|
||||
modifier = tankmax - toxcharge
|
||||
toxvol -= modifier
|
||||
toxcharge += modifier
|
||||
|
||||
/obj/item/medigun_backpack/process()
|
||||
if(!bcell)
|
||||
return
|
||||
|
||||
var/obj/item/bork_medigun/medigun = get_medigun()
|
||||
|
||||
if(bcell.charge >= 10)
|
||||
var/icon_needs_update = FALSE
|
||||
if(brutecharge < tankmax && brutevol > 0 && (bcell.checked_use(smaniptier * 2)))
|
||||
adjust_brutevol(smaniptier * 2)
|
||||
icon_needs_update = TRUE
|
||||
if(burncharge < tankmax && burnvol > 0 && (bcell.checked_use(smaniptier * 2)))
|
||||
adjust_burnvol(smaniptier * 2)
|
||||
icon_needs_update = TRUE
|
||||
if(toxcharge < tankmax && toxvol > 0 && (bcell.checked_use(smaniptier * 2)))
|
||||
adjust_toxvol(smaniptier * 2)
|
||||
icon_needs_update = TRUE
|
||||
//Alien tier
|
||||
if(sbintier >= 5 && medigun.busy == MEDIGUN_IDLE && (bcell.charge >= 10))
|
||||
if(brutevol < chemcap && (bcell.checked_use(10)))
|
||||
icon_needs_update = TRUE
|
||||
brutevol ++
|
||||
if(burnvol < chemcap && (bcell.checked_use(10)))
|
||||
icon_needs_update = TRUE
|
||||
burnvol ++
|
||||
if(toxvol < chemcap && (bcell.checked_use(10)))
|
||||
icon_needs_update = TRUE
|
||||
toxvol ++
|
||||
|
||||
if(icon_needs_update)
|
||||
update_icon()
|
||||
|
||||
if(scapacitor.get_rating() >= 5)
|
||||
if(apc_charge())
|
||||
charging = FALSE
|
||||
return
|
||||
charging = TRUE
|
||||
|
||||
if(!charging || !ccell)
|
||||
return
|
||||
|
||||
var/scaptier = scapacitor.get_rating()
|
||||
var/missing = min(scaptier*25, bcell.amount_missing())
|
||||
|
||||
if(missing > 0)
|
||||
if(ccell && ccell.checked_use(missing))
|
||||
bcell.give(missing)
|
||||
update_icon()
|
||||
return
|
||||
|
||||
if(ismob(loc))
|
||||
to_chat(loc, span_notice("The [ccell] runs out of power.."))
|
||||
charging = FALSE
|
||||
|
||||
/obj/item/medigun_backpack/get_cell()
|
||||
return bcell
|
||||
|
||||
/obj/item/medigun_backpack/update_icon()
|
||||
. = ..()
|
||||
cut_overlays()
|
||||
if((bcell.percent() <= 5 ))
|
||||
add_overlay(image('icons/obj/borkmedigun.dmi', "no_battery"))
|
||||
else if((bcell.percent() <= 25 && bcell.percent() > 5))
|
||||
add_overlay(image('icons/obj/borkmedigun.dmi', "low_battery"))
|
||||
|
||||
if(brutevol <= 0 && brutecharge > 0)
|
||||
add_overlay(image('icons/obj/borkmedigun.dmi', "red"))
|
||||
else if(brutecharge <= 0 && brutevol <= 0)
|
||||
add_overlay(image('icons/obj/borkmedigun.dmi', "redstrike-blink"))
|
||||
|
||||
if(toxvol <= 0 && toxcharge > 0)
|
||||
add_overlay(image('icons/obj/borkmedigun.dmi', "green"))
|
||||
else if(toxcharge <= 0 && toxvol <= 0)
|
||||
add_overlay(image('icons/obj/borkmedigun.dmi', "greenstrike-blink"))
|
||||
|
||||
if(burnvol <= 0 && burncharge > 0)
|
||||
add_overlay(image('icons/obj/borkmedigun.dmi', "orange"))
|
||||
else if(burncharge <= 0 && burnvol <= 0)
|
||||
add_overlay(image('icons/obj/borkmedigun.dmi', "orangestrike-blink"))
|
||||
|
||||
/obj/item/medigun_backpack/proc/replace_icon(inhand)
|
||||
var/obj/item/bork_medigun/medigun = get_medigun()
|
||||
if(inhand)
|
||||
icon_state = "mg-backpack-deployed"
|
||||
item_state = "mg-backpack-deployed-onmob"
|
||||
if(is_twohanded())
|
||||
medigun.icon_state = "medblaster"
|
||||
medigun.base_icon_state = "medblaster"
|
||||
medigun.wielded_item_state = "medblaster-wielded"
|
||||
medigun.update_icon()
|
||||
else
|
||||
medigun.icon_state = "medblaster_cmo"
|
||||
medigun.base_icon_state = "medblaster_cmo"
|
||||
medigun.wielded_item_state = ""
|
||||
medigun.update_icon()
|
||||
else if(is_twohanded())
|
||||
icon_state = "mg-backpack"
|
||||
item_state = "mg-backpack-onmob"
|
||||
medigun.icon_state = "medblaster"
|
||||
medigun.base_icon_state = "medblaster"
|
||||
else
|
||||
icon_state = "mg-backpack_cmo"
|
||||
item_state = "mg-backpack_cmo-onmob"
|
||||
medigun.icon_state = "medblaster_cmo"
|
||||
medigun.base_icon_state = "medblaster_cmo"
|
||||
|
||||
update_icon()
|
||||
|
||||
/obj/item/medigun_backpack/Initialize(mapload)
|
||||
AddComponent(/datum/component/tethered_item, medigun_path)
|
||||
. = ..()
|
||||
|
||||
var/obj/item/bork_medigun/linked/medigun = get_medigun()
|
||||
medigun.medigun_base_unit = src
|
||||
|
||||
if(!is_twohanded())
|
||||
medigun.beam_range = 4
|
||||
medigun.icon_state = "medblaster_cmo"
|
||||
medigun.base_icon_state = "medblaster_cmo"
|
||||
medigun.wielded_item_state = ""
|
||||
medigun.update_icon()
|
||||
if(ispath(bcell))
|
||||
bcell = new bcell(src)
|
||||
bcell.charge = 0
|
||||
if(ispath(sbin))
|
||||
sbin = new sbin(src)
|
||||
if(ispath(smodule))
|
||||
smodule = new smodule(src)
|
||||
START_PROCESSING(SSobj, src)
|
||||
if(ispath(smanipulator))
|
||||
smanipulator = new smanipulator(src)
|
||||
if(ispath(scapacitor))
|
||||
scapacitor = new scapacitor(src)
|
||||
if(ispath(slaser))
|
||||
slaser = new slaser(src)
|
||||
update_icon()
|
||||
|
||||
|
||||
/obj/item/medigun_backpack/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
QDEL_NULL(bcell)
|
||||
QDEL_NULL(smodule)
|
||||
QDEL_NULL(smanipulator)
|
||||
QDEL_NULL(scapacitor)
|
||||
QDEL_NULL(slaser)
|
||||
. = ..()
|
||||
|
||||
/obj/item/medigun_backpack/proc/get_medigun()
|
||||
var/datum/component/tethered_item/TI = GetComponent(/datum/component/tethered_item)
|
||||
return TI.get_handheld()
|
||||
|
||||
/obj/item/medigun_backpack/emp_act(severity)
|
||||
. = ..()
|
||||
if(bcell)
|
||||
bcell.emp_act(severity)
|
||||
|
||||
/obj/item/medigun_backpack/attack_hand(mob/living/user)
|
||||
// See important note in tethered_item.dm
|
||||
if(SEND_SIGNAL(src,COMSIG_ITEM_ATTACK_SELF,user) & COMPONENT_CANCEL_ATTACK_CHAIN)
|
||||
return TRUE
|
||||
. = ..()
|
||||
|
||||
/obj/item/medigun_backpack/MouseDrop()
|
||||
if(ismob(src.loc))
|
||||
if(!CanMouseDrop(src))
|
||||
return
|
||||
var/mob/M = src.loc
|
||||
if(!M.unEquip(src))
|
||||
return
|
||||
src.add_fingerprint(usr)
|
||||
M.put_in_any_hand_if_possible(src)
|
||||
/*icon_state = "mg-backpack"
|
||||
item_state = "mg-backpack-onmob"
|
||||
update_icon() //success
|
||||
usr.update_inv_back()*/
|
||||
|
||||
|
||||
/obj/item/medigun_backpack/attackby(obj/item/W, mob/user, params)
|
||||
if(refill_reagent(W, user))
|
||||
return
|
||||
|
||||
var/obj/item/bork_medigun/medigun = get_medigun()
|
||||
|
||||
if(W.is_crowbar() && maintenance)
|
||||
if(smodule )
|
||||
smodule.forceMove(get_turf(loc))
|
||||
smodule = null
|
||||
|
||||
if(smanipulator)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
smanipulator.forceMove(get_turf(loc))
|
||||
smanipulator = null
|
||||
smaniptier = 0
|
||||
|
||||
if(slaser)
|
||||
slaser.forceMove(get_turf(loc))
|
||||
slaser = null
|
||||
|
||||
if(scapacitor)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
scapacitor.forceMove(get_turf(loc))
|
||||
scapacitor = null
|
||||
|
||||
if(sbin)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
sbin.forceMove(get_turf(loc))
|
||||
sbin = null
|
||||
sbintier = 0
|
||||
|
||||
to_chat(user, span_notice("You remove the Components from \the [src]."))
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
if(W.is_screwdriver())
|
||||
if(!maintenance)
|
||||
maintenance = TRUE
|
||||
to_chat(user, span_notice("You open the maintenance hatch on \the [src]."))
|
||||
return
|
||||
|
||||
maintenance = FALSE
|
||||
to_chat(user, span_notice("You close the maintenance hatch on \the [src]."))
|
||||
return
|
||||
|
||||
if(istype(W, /obj/item/cell))
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
W.forceMove(src)
|
||||
if(ccell)
|
||||
to_chat(user, span_notice("You swap the [W] for \the [ccell]."))
|
||||
ccell = W
|
||||
to_chat(user, span_notice("You install the [W] into \the [src]."))
|
||||
charging = TRUE
|
||||
return
|
||||
|
||||
if(maintenance)
|
||||
if(istype(W, /obj/item/stock_parts/scanning_module))
|
||||
if(smodule)
|
||||
to_chat(user, span_notice("\The [src] already has a scanning module."))
|
||||
else
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
W.forceMove(src)
|
||||
smodule = W
|
||||
to_chat(user, span_notice("You install the [W] into \the [src]."))
|
||||
medigun.beam_range = 3+smodule.get_rating()
|
||||
update_icon()
|
||||
return
|
||||
|
||||
if(istype(W, /obj/item/stock_parts/manipulator))
|
||||
if(smanipulator)
|
||||
to_chat(user, span_notice("\The [src] already has a manipulator."))
|
||||
return
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
W.forceMove(src)
|
||||
smanipulator = W
|
||||
smaniptier = smanipulator.get_rating()
|
||||
if(sbin && scapacitor)START_PROCESSING(SSobj, src)
|
||||
to_chat(user, span_notice("You install the [W] into \the [src]."))
|
||||
update_icon()
|
||||
return
|
||||
|
||||
if(istype(W, /obj/item/stock_parts/micro_laser))
|
||||
if(slaser)
|
||||
to_chat(user, span_notice("\The [src] already has a micro laser."))
|
||||
return
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
W.forceMove(src)
|
||||
slaser = W
|
||||
to_chat(user, span_notice("You install the [W] into \the [src]."))
|
||||
update_icon()
|
||||
return
|
||||
|
||||
if(istype(W, /obj/item/stock_parts/capacitor))
|
||||
if(scapacitor)
|
||||
to_chat(user, span_notice("\The [src] already has a capacitor."))
|
||||
return
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
W.forceMove(src)
|
||||
scapacitor = W
|
||||
var/scaptier = scapacitor.get_rating()
|
||||
if(scaptier == 1)
|
||||
chargecap = 1000
|
||||
bcell.maxcharge = 1000
|
||||
if(bcell.charge > chargecap)
|
||||
bcell.charge = chargecap
|
||||
else if(scaptier == 2)
|
||||
chargecap = 2000
|
||||
bcell.maxcharge = 2000
|
||||
if(bcell.charge > chargecap)
|
||||
bcell.charge = chargecap
|
||||
else if(scaptier == 3)
|
||||
chargecap = 3000
|
||||
bcell.maxcharge = 3000
|
||||
if(bcell.charge > chargecap)
|
||||
bcell.charge = chargecap
|
||||
else if(scaptier == 4)
|
||||
chargecap = 4000
|
||||
bcell.maxcharge = 4000
|
||||
if(bcell.charge > chargecap)
|
||||
bcell.charge = chargecap
|
||||
else if(scaptier == 5)
|
||||
chargecap = 5000
|
||||
bcell.maxcharge = 5000
|
||||
if(bcell.charge > chargecap)
|
||||
bcell.charge = chargecap
|
||||
|
||||
if(sbin && smanipulator)START_PROCESSING(SSobj, src)
|
||||
to_chat(user, span_notice("You install the [W] into \the [src]."))
|
||||
update_icon()
|
||||
return
|
||||
|
||||
if(istype(W, /obj/item/stock_parts/matter_bin))
|
||||
if(sbin)
|
||||
to_chat(user, span_notice("\The [src] already has a matter bin."))
|
||||
return
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
W.forceMove(src)
|
||||
sbin = W
|
||||
sbintier = sbin.get_rating()
|
||||
if(sbintier >= 5)
|
||||
chemcap = 300
|
||||
tankmax = 150
|
||||
else
|
||||
chemcap = 60*(sbintier)
|
||||
tankmax = 30*sbintier
|
||||
if(brutecharge > chemcap)
|
||||
brutecharge = chemcap
|
||||
if(burncharge > chemcap)
|
||||
burncharge = chemcap
|
||||
if(toxcharge > chemcap)
|
||||
toxcharge = chemcap
|
||||
if(brutecharge > tankmax)
|
||||
brutecharge = tankmax
|
||||
if(burncharge > tankmax)
|
||||
burncharge = tankmax
|
||||
if(toxcharge > tankmax)
|
||||
toxcharge = tankmax
|
||||
if(scapacitor && smanipulator)START_PROCESSING(SSobj, src)
|
||||
to_chat(user, span_notice("You install the [W] into \the [src]."))
|
||||
update_icon()
|
||||
return
|
||||
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/item/medigun_backpack/proc/refill_reagent(var/obj/item/container, mob/user)
|
||||
. = FALSE
|
||||
if(!maintenance && (istype(container, /obj/item/reagent_containers/glass/beaker) || istype(container, /obj/item/reagent_containers/glass/bottle)))
|
||||
|
||||
if(!(container.flags & OPENCONTAINER))
|
||||
to_chat(user, span_warning("You need to open the [container] first!"))
|
||||
return
|
||||
|
||||
var/reagentwhitelist = list(REAGENT_ID_BICARIDINE, REAGENT_ID_ANTITOXIN, REAGENT_ID_KELOTANE, REAGENT_ID_DERMALINE)//, "tricordrazine")
|
||||
|
||||
for(var/G in container.reagents.reagent_list)
|
||||
var/datum/reagent/R = G
|
||||
var/modifier = 1
|
||||
var/totransfer = 0
|
||||
var/name = ""
|
||||
|
||||
if(R.id in reagentwhitelist)
|
||||
switch(R.id)
|
||||
if(REAGENT_ID_BICARIDINE)
|
||||
name = "bruteheal"
|
||||
modifier = 4
|
||||
totransfer = chemcap - brutevol
|
||||
if(REAGENT_ID_ANTITOXIN)
|
||||
name = "toxheal"
|
||||
modifier = 4
|
||||
totransfer = chemcap - toxvol
|
||||
if(REAGENT_ID_KELOTANE)
|
||||
name = "burnheal"
|
||||
modifier = 4
|
||||
totransfer = chemcap - burnvol
|
||||
if(REAGENT_ID_DERMALINE)
|
||||
name = "burnheal"
|
||||
modifier = 8
|
||||
totransfer = chemcap - burnvol
|
||||
/*if("tricordrazine")
|
||||
name = "tricordrazine"
|
||||
modifier = 1
|
||||
if((brutevol != chemcap) && (burnvol != chemcap) && (toxvol != chemcap))
|
||||
totransfer = 1 //tempcheck to get past the totransfer check
|
||||
else
|
||||
totransfer = 0*/
|
||||
if(totransfer <= 0)
|
||||
to_chat(user, span_notice("The [src] cannot accept anymore [name]!"))
|
||||
totransfer = min(totransfer, container.reagents.get_reagent_amount(R.id) * modifier)
|
||||
|
||||
switch(R.id)
|
||||
if(REAGENT_ID_BICARIDINE)
|
||||
brutevol += totransfer
|
||||
if(REAGENT_ID_ANTITOXIN)
|
||||
toxvol += totransfer
|
||||
if(REAGENT_ID_KELOTANE)
|
||||
burnvol += totransfer
|
||||
if(REAGENT_ID_DERMALINE)
|
||||
burnvol += totransfer
|
||||
/*if("tricordrazine") //Tricord too problematic
|
||||
var/maxamount = container.reagents.get_reagent_amount(R.id)
|
||||
var/amountused
|
||||
var/oldbrute = brutevol
|
||||
var/oldburn = burnvol
|
||||
var/oldtox = toxvol
|
||||
|
||||
while(maxamount > 0)
|
||||
if(brutevol >= chemcap && burnvol >= chemcap && toxvol >= chemcap)
|
||||
break
|
||||
maxamount --
|
||||
amountused++
|
||||
totransfer ++
|
||||
if(brutevol < chemcap)
|
||||
brutevol ++
|
||||
if(burnvol < chemcap)
|
||||
burnvol ++
|
||||
if(toxvol < chemcap)
|
||||
toxvol ++
|
||||
var/readout = "You add [amountused] units of [R.name] to the [src]. \n The [src] Stores "
|
||||
var/readoutadditions = FALSE
|
||||
if(oldbrute != brutevol)
|
||||
readout += "[round(brutevol - oldbrute)] U of bruteheal vol"
|
||||
readoutadditions = TRUE
|
||||
if(oldburn != burnvol)
|
||||
if(readoutadditions)
|
||||
readout += ", "
|
||||
readout += "[round(burnvol - oldburn)] U of burnheal vol"
|
||||
readoutadditions = TRUE
|
||||
if(oldtox != toxvol)
|
||||
if(readoutadditions)
|
||||
readout += ", "
|
||||
readout += "[round(toxvol - oldtox)] U of toxheal vol"
|
||||
if(oldbrute != brutevol || oldburn != burnvol || oldtox != toxvol)to_chat(user, span_notice("[readout]."))*/
|
||||
if(totransfer > 0)
|
||||
if(R.id != "tricordrazine")
|
||||
to_chat(user, span_notice("You add [totransfer / modifier] units of [R.name] to the [src]. \n The [src] stores [round(totransfer)] U of [name]."))
|
||||
container.reagents.remove_reagent(R.id, totransfer / modifier)
|
||||
playsound(src, 'sound/weapons/empty.ogg', 50, 1)
|
||||
update_icon()
|
||||
. = TRUE
|
||||
return
|
||||
|
||||
//checks that the base unit is in the correct slot to be used
|
||||
/obj/item/medigun_backpack/proc/slot_check()
|
||||
var/mob/M = loc
|
||||
if(!istype(M))
|
||||
return FALSE //not equipped
|
||||
|
||||
if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_back) == src)
|
||||
return TRUE
|
||||
if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_s_store) == src)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/item/medigun_backpack/dropped(mob/user)
|
||||
..()
|
||||
replace_icon()
|
||||
|
||||
/obj/item/medigun_backpack/proc/checked_use(var/charge_amt)
|
||||
return (bcell && bcell.checked_use(charge_amt))
|
||||
167
code/game/objects/items/weapons/medigun/medigun_backpack_ui.dm
Normal file
@@ -0,0 +1,167 @@
|
||||
/obj/item/medigun_backpack/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Medigun", name)
|
||||
ui.open()
|
||||
|
||||
/obj/item/medigun_backpack/tgui_data(mob/user)
|
||||
var/obj/item/bork_medigun/medigun = get_medigun()
|
||||
var/mob/living/carbon/human/H = medigun.current_target
|
||||
var/patientname
|
||||
var/patienthealth = 0
|
||||
var/patientbruteloss = 0
|
||||
var/patientfireloss = 0
|
||||
var/patienttoxloss = 0
|
||||
var/patientoxyloss = 0
|
||||
var/patientstatus = 0
|
||||
var/list/bloodData = list()
|
||||
var/inner_bleeding = FALSE
|
||||
var/organ_damage = FALSE
|
||||
|
||||
//var/minhealth = 0
|
||||
if(scapacitor?.get_rating() < 5)
|
||||
gridstatus = 3
|
||||
if(H)
|
||||
for(var/obj/item/organ/org in H.internal_organs)
|
||||
if(org.robotic >= ORGAN_ROBOT)
|
||||
continue
|
||||
if(org.status & ORGAN_BLEEDING)
|
||||
inner_bleeding = TRUE
|
||||
if(org.damage >= 1 && !istype(org, /obj/item/organ/internal/brain))
|
||||
organ_damage = TRUE
|
||||
patientname = H
|
||||
patienthealth = max(0, (H.health + abs(-H.getMaxHealth())) / (H.getMaxHealth() + abs(-H.getMaxHealth())))
|
||||
patientbruteloss = H.getBruteLoss()
|
||||
patientfireloss = H.getFireLoss()
|
||||
patienttoxloss = H.getToxLoss()
|
||||
patientoxyloss = H.getOxyLoss()
|
||||
patientstatus = H.stat
|
||||
if(H.vessel)
|
||||
bloodData["volume"] = round(H.vessel.get_reagent_amount("blood"))
|
||||
bloodData["max_volume"] = H.species.blood_volume
|
||||
var/list/data = list(
|
||||
"maintenance" = maintenance,
|
||||
"generator" = charging,
|
||||
"gridstatus" = gridstatus,
|
||||
"tankmax" = tankmax,
|
||||
"power_cell_status" = bcell ? bcell.percent() : null,
|
||||
"cell_status" = ccell ? (ccell.percent()/100) : null,
|
||||
"bruteheal_charge" = scapacitor ? brutecharge : null,
|
||||
"burnheal_charge" = scapacitor ? burncharge : null,
|
||||
"toxheal_charge" = scapacitor ? toxcharge : null,
|
||||
"bruteheal_vol" = sbin ? brutevol : null,
|
||||
"burnheal_vol" = sbin ? burnvol : null,
|
||||
"toxheal_vol" = sbin ? toxvol : null,
|
||||
"patient_name" = smodule ? patientname : null,
|
||||
"patient_health" = smodule ? patienthealth : null,
|
||||
"patient_brute" = smodule ? patientbruteloss : null,
|
||||
"patient_burn" = smodule ? patientfireloss : null,
|
||||
"patient_tox" = smodule ? patienttoxloss : null,
|
||||
"patient_oxy" = smodule ? patientoxyloss : null,
|
||||
"blood_status" = smodule ? bloodData : null,
|
||||
"patient_status" = smodule ? patientstatus : null,
|
||||
"organ_damage" = smodule ? organ_damage : null,
|
||||
"inner_bleeding" = smodule ? inner_bleeding : null,
|
||||
"examine_data" = get_examine_data()
|
||||
)
|
||||
return data
|
||||
|
||||
/obj/item/medigun_backpack/proc/get_examine_data()
|
||||
var/obj/item/bork_medigun/medigun = get_medigun()
|
||||
|
||||
return list(
|
||||
"smodule" = smodule ? list("name" = smodule.name, "range" = medigun.beam_range, "rating" = smodule.get_rating()) : null,
|
||||
"smanipulator" = smanipulator ? list("name" = smanipulator.name, "rating" = smaniptier) : null,
|
||||
"slaser" = slaser ? list("name" = slaser.name, "rating" = slaser.get_rating()) : null,
|
||||
"scapacitor" = scapacitor ? list("name" = scapacitor.name, "chargecap" = chargecap, "rating" = scapacitor.get_rating()) : null,
|
||||
"sbin" = sbin ? list("name" = sbin.name, "chemcap" = chemcap, "tankmax" = tankmax, "rating" = sbin.get_rating()) : null
|
||||
)
|
||||
|
||||
/obj/item/medigun_backpack/tgui_act(action, params, datum/tgui/ui)
|
||||
if(..())
|
||||
return TRUE
|
||||
|
||||
. = TRUE
|
||||
|
||||
var/obj/item/bork_medigun/medigun = get_medigun()
|
||||
|
||||
switch(action)
|
||||
if("celleject")
|
||||
cell_eject()
|
||||
return TRUE
|
||||
|
||||
if("cancel_healing")
|
||||
if(medigun?.busy)
|
||||
medigun.busy = MEDIGUN_CANCELLED
|
||||
return TRUE
|
||||
|
||||
if("toggle_maintenance")
|
||||
maintenance = !maintenance
|
||||
return TRUE
|
||||
|
||||
if("rem_smodule")
|
||||
if(!smodule || !maintenance)
|
||||
return FALSE
|
||||
smodule.forceMove(get_turf(loc))
|
||||
to_chat(ui.user, span_notice("You remove the [smodule] from \the [src]."))
|
||||
smodule = null
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
if("rem_mani")
|
||||
if(!smanipulator || !maintenance)
|
||||
return FALSE
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
smanipulator.forceMove(get_turf(loc))
|
||||
to_chat(ui.user, span_notice("You remove the [smanipulator] from \the [src]."))
|
||||
smanipulator = null
|
||||
smaniptier = 0
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
if("rem_laser")
|
||||
if(!slaser || !maintenance)
|
||||
return FALSE
|
||||
slaser.forceMove(get_turf(loc))
|
||||
to_chat(ui.user, span_notice("You remove the [slaser] from \the [src]."))
|
||||
slaser = null
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
if("rem_cap")
|
||||
if(!scapacitor || !maintenance)
|
||||
return FALSE
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
scapacitor.forceMove(get_turf(loc))
|
||||
to_chat(ui.user, span_notice("You remove the [scapacitor] from \the [src]."))
|
||||
scapacitor = null
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
if("rem_bin")
|
||||
if(!sbin || !maintenance)
|
||||
return FALSE
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
sbin.forceMove(get_turf(loc))
|
||||
to_chat(ui.user, span_notice("You remove the [sbin] from \the [src]."))
|
||||
sbin = null
|
||||
sbintier = 0
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
/obj/item/medigun_backpack/ShiftClick(mob/user)
|
||||
. = ..()
|
||||
var/obj/item/bork_medigun/medigun = get_medigun()
|
||||
if(!medigun)
|
||||
return
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/item/medigun_backpack/proc/cell_eject()
|
||||
if(!ccell)
|
||||
return FALSE
|
||||
charging = FALSE
|
||||
ccell.forceMove(get_turf(loc))
|
||||
to_chat(usr, span_notice("You remove the [ccell] from \the [src]."))
|
||||
ccell = null
|
||||
update_icon()
|
||||
return TRUE
|
||||
@@ -0,0 +1,8 @@
|
||||
/datum/modifier/medbeameffect
|
||||
name = "medgunffect"
|
||||
desc = "You're being stabilized"
|
||||
mob_overlay_state = "medigun_effect"
|
||||
stacks = MODIFIER_STACK_EXTEND
|
||||
//pain_immunity = TRUE
|
||||
bleeding_rate_percent = 0.1 //only a little
|
||||
incoming_oxy_damage_percent = 0
|
||||
28
code/game/objects/items/weapons/medigun/medigun_verbs.dm
Normal file
@@ -0,0 +1,28 @@
|
||||
/obj/item/medigun_backpack/verb/toggle_medigun()
|
||||
set name = "Toggle medigun"
|
||||
set category = "Object"
|
||||
|
||||
var/mob/living/carbon/human/user = usr
|
||||
if(maintenance)
|
||||
to_chat(user, span_warning("Please close the maintenance hatch with a screwdriver first, or to remove components, use a crowbar."))
|
||||
return
|
||||
|
||||
if(!medigun)
|
||||
to_chat(user, span_warning("The medigun is missing!"))
|
||||
return
|
||||
|
||||
if(medigun.loc != src)
|
||||
reattach_medigun(user) //Remove from their hands and back onto the medigun unit
|
||||
return
|
||||
|
||||
if(!slot_check())
|
||||
to_chat(user, span_warning("You need to equip [src] before taking out [medigun]."))
|
||||
else
|
||||
if(!user.put_in_hands(medigun)) //Detach the medigun into the user's hands
|
||||
to_chat(user, span_warning("You need a free hand to hold the medigun!"))
|
||||
else
|
||||
containsgun = 0
|
||||
replace_icon(TRUE)
|
||||
if(is_twohanded())
|
||||
medigun.update_twohanding()
|
||||
user.update_inv_back()
|
||||
@@ -406,6 +406,7 @@
|
||||
active_armourpen = 25
|
||||
projectile_parry_chance = 40
|
||||
colorable = TRUE
|
||||
item_flags = DROPDEL | NOSTRIP
|
||||
|
||||
hitcost = 75
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
356
code/modules/clothing/suits/reactive_armour.dm
Normal file
@@ -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)
|
||||
|
||||
|
||||
@@ -672,6 +672,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)
|
||||
|
||||
@@ -1266,7 +1266,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))
|
||||
|
||||
@@ -510,6 +510,7 @@
|
||||
|
||||
if(blob.mob_radio)
|
||||
blob.mob_radio.forceMove(src)
|
||||
equip_to_appropriate_slot(blob.mob_radio) // Actually put it back on the mob in a slot
|
||||
blob.mob_radio = null
|
||||
if(blob.myid)
|
||||
blob.myid = null
|
||||
|
||||
@@ -574,6 +574,7 @@
|
||||
|
||||
if(blob.mob_radio)
|
||||
blob.mob_radio.forceMove(src)
|
||||
equip_to_appropriate_slot(blob.mob_radio) // Actually put it back on the mob in a slot
|
||||
blob.mob_radio = null
|
||||
if(blob.myid)
|
||||
blob.myid = null
|
||||
|
||||
@@ -39,9 +39,6 @@ var/list/mob_hat_cache = list()
|
||||
|
||||
can_pull_size = ITEMSIZE_NO_CONTAINER
|
||||
can_pull_mobs = MOB_PULL_SMALLER
|
||||
can_enter_vent_with = list(
|
||||
/obj,
|
||||
/atom/movable/emissive_blocker)
|
||||
|
||||
mob_bump_flag = SIMPLE_ANIMAL
|
||||
mob_swap_flags = SIMPLE_ANIMAL
|
||||
@@ -80,6 +77,14 @@ var/list/mob_hat_cache = list()
|
||||
/mob/living/silicon/robot/drone/is_sentient()
|
||||
return FALSE
|
||||
|
||||
/mob/living/silicon/robot/drone/ventcrawl_get_item_whitelist()
|
||||
// Yes this allows any object, yes it's silly. I don't know if it's ever been abused by drones though.
|
||||
return list(
|
||||
/atom/movable/emissive_blocker,
|
||||
/atom/movable/screen,
|
||||
/obj
|
||||
)
|
||||
|
||||
/mob/living/silicon/robot/drone/construction
|
||||
name = "construction drone"
|
||||
icon_state = "constructiondrone"
|
||||
|
||||
@@ -22,9 +22,6 @@
|
||||
|
||||
can_pull_size = ITEMSIZE_NO_CONTAINER
|
||||
can_pull_mobs = MOB_PULL_SMALLER
|
||||
can_enter_vent_with = list(
|
||||
/obj,
|
||||
/atom/movable/emissive_blocker)
|
||||
|
||||
mob_always_swap = 1
|
||||
|
||||
|
||||
@@ -60,42 +60,6 @@
|
||||
|
||||
allow_mind_transfer = TRUE
|
||||
|
||||
|
||||
can_enter_vent_with = list(
|
||||
/obj/item/implant,
|
||||
/obj/item/radio/borg,
|
||||
/obj/item/holder,
|
||||
/obj/machinery/camera,
|
||||
/obj/belly,
|
||||
/obj/soulgem,
|
||||
/atom/movable/screen,
|
||||
/atom/movable/emissive_blocker,
|
||||
/obj/item/material,
|
||||
/obj/item/melee,
|
||||
/obj/item/stack/,
|
||||
/obj/item/tool,
|
||||
/obj/item/reagent_containers/food,
|
||||
/obj/item/coin,
|
||||
/obj/item/aliencoin,
|
||||
/obj/item/ore,
|
||||
/obj/item/disk/nuclear,
|
||||
/obj/item/toy,
|
||||
/obj/item/card,
|
||||
/obj/item/radio,
|
||||
/obj/item/perfect_tele_beacon,
|
||||
/obj/item/clipboard,
|
||||
/obj/item/paper,
|
||||
/obj/item/pen,
|
||||
/obj/item/canvas,
|
||||
/obj/item/paint_palette,
|
||||
/obj/item/paint_brush,
|
||||
/obj/item/camera,
|
||||
/obj/item/photo,
|
||||
/obj/item/camera_film,
|
||||
/obj/item/taperecorder,
|
||||
/obj/item/rectape
|
||||
)
|
||||
|
||||
vore_active = 1
|
||||
vore_capacity = 1
|
||||
vore_bump_chance = 1
|
||||
@@ -126,6 +90,24 @@
|
||||
B.absorbchance = 1
|
||||
B.escapechance = 15
|
||||
|
||||
/mob/living/simple_mob/vore/alienanimals/catslug/ventcrawl_get_item_whitelist()
|
||||
return list(
|
||||
VENTCRAWL_BASE_WHITELIST,
|
||||
VENTCRAWL_VORE_WHITELIST,
|
||||
VENTCRAWL_SMALLITEM_WHITELIST,
|
||||
// Catslug unique items.
|
||||
/obj/item/material,
|
||||
/obj/item/melee,
|
||||
/obj/item/stack/,
|
||||
/obj/item/tool,
|
||||
/obj/item/reagent_containers/food,
|
||||
/obj/item/ore,
|
||||
/obj/item/disk/nuclear,
|
||||
/obj/item/card,
|
||||
/obj/item/radio,
|
||||
/obj/item/perfect_tele_beacon,
|
||||
)
|
||||
|
||||
/datum/ai_holder/simple_mob/melee/evasive/catslug
|
||||
hostile = FALSE
|
||||
cooperative = FALSE
|
||||
|
||||
@@ -80,8 +80,6 @@ var/list/_slime_default_emotes = list(
|
||||
var/injection_amount = 5 // This determines how much.
|
||||
var/mood = ":3" // Icon to use to display 'mood', as an overlay.
|
||||
|
||||
can_enter_vent_with = list(/obj/item/clothing/head, /obj/soulgem)
|
||||
|
||||
can_be_drop_prey = FALSE
|
||||
|
||||
species_sounds = "Slime"
|
||||
@@ -109,6 +107,14 @@ var/list/_slime_default_emotes = list(
|
||||
drop_hat()
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/robot/drone/ventcrawl_get_item_whitelist()
|
||||
return list(
|
||||
VENTCRAWL_BASE_WHITELIST,
|
||||
VENTCRAWL_VORE_WHITELIST,
|
||||
// Slime unique items
|
||||
/obj/item/clothing/head,
|
||||
)
|
||||
|
||||
/mob/living/simple_mob/slime/death()
|
||||
// Make dead slimes stop glowing.
|
||||
glow_toggle = FALSE
|
||||
|
||||
@@ -182,6 +182,8 @@
|
||||
|
||||
var/voice_name = "unidentifiable voice"
|
||||
|
||||
var/list/ventcraw_item_admin_allow = null // If this is a list, it will be appended to the default list of items the mob is allowed to ventcrawl with
|
||||
|
||||
var/faction = FACTION_NEUTRAL //Used for checking whether hostile simple animals will attack you, possibly more stuff later
|
||||
|
||||
var/can_be_antagged = FALSE // To prevent pAIs/mice/etc from getting antag in autotraitor and future auto- modes. Uses inheritance instead of a bunch of typechecks.
|
||||
|
||||
@@ -348,6 +348,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.
|
||||
|
||||
@@ -89,6 +89,8 @@
|
||||
// this will be revealed if a T-scanner is used
|
||||
// if visible, use regular icon_state
|
||||
/obj/structure/disposalpipe/update_icon()
|
||||
if(!(flags & ATOM_INITIALIZED)) // Do not call update_icon before init. E.g. hide might be called before
|
||||
return
|
||||
/* if(invisibility) //we hide things with alpha now, no need for transparent icons
|
||||
icon_state = "[base_icon_state]f"
|
||||
else
|
||||
|
||||
92
code/modules/research/anomaly/anomaly_core.dm
Normal file
@@ -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
|
||||
)
|
||||
|
||||
@@ -870,6 +870,17 @@
|
||||
)
|
||||
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE
|
||||
|
||||
/datum/design_techweb/medigun_constant
|
||||
name = "Prototype Bluespace Medigun Backpack"
|
||||
id = "medigun_constant"
|
||||
build_type = PROTOLATHE
|
||||
materials = list(MAT_STEEL = 8000, MAT_PLASTIC = 8000, MAT_GLASS = 5000, MAT_SILVER = 1000, MAT_GOLD = 1000, MAT_URANIUM = 1000)
|
||||
build_path = /obj/item/medigun_backpack
|
||||
category = list(
|
||||
RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_RANGED
|
||||
)
|
||||
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE
|
||||
|
||||
/datum/design_techweb/upgradeAOE
|
||||
name = "Mining Explosion Upgrade"
|
||||
desc = "An area of effect upgrade for the Proto-Kinetic Accelerator."
|
||||
|
||||
@@ -9,3 +9,16 @@
|
||||
if(check.size_multiplier < 1.25 && check.size_multiplier > 0.75)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/experiment/scanning/people/hurt_medigun
|
||||
required_count = 3
|
||||
required_traits_desc = "Our newly and improvised Medi-Gun needs field testing! Surely, there has to be someone who's gotten a few bruises or scratches here or there."
|
||||
|
||||
/datum/experiment/scanning/people/hurt_medigun/is_valid_scan_target(mob/living/carbon/human/check, datum/component/experiment_handler/experiment_handler)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/HP_percent = check.health/check.getMaxHealth()
|
||||
if(HP_percent < 1)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
@@ -173,6 +173,18 @@
|
||||
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
|
||||
announce_channels = list(CHANNEL_MEDICAL)
|
||||
|
||||
/datum/techweb_node/medigun_constant
|
||||
id = TECHWEB_NODE_MEDIGUN_CONSTANT
|
||||
display_name = "Medigun Backpack"
|
||||
description = "A revised version of the ML3M series. This one features a cell-powered constant beam, and ability to charge it with chemicals."
|
||||
prereq_ids = list(TECHWEB_NODE_MEDIGUN)
|
||||
design_ids = list(
|
||||
"medigun_constant"
|
||||
)
|
||||
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
|
||||
announce_channels = list(CHANNEL_MEDICAL)
|
||||
discount_experiments = list(/datum/experiment/scanning/people/hurt_medigun = TECHWEB_TIER_3_POINTS)
|
||||
|
||||
/datum/techweb_node/nif
|
||||
id = TECHWEB_NODE_NIF
|
||||
display_name = "Nanite-Implant Frameworks"
|
||||
|
||||
@@ -109,8 +109,8 @@
|
||||
"excavationdrill",
|
||||
"ano_scanner",
|
||||
//"anomaly_refinery",
|
||||
// "anomaly_neutralizer",
|
||||
// "reactive_armour",
|
||||
"anomaly_neutralizer",
|
||||
"reactive_armour",
|
||||
)
|
||||
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
|
||||
announce_channels = list(CHANNEL_SCIENCE)
|
||||
|
||||
@@ -3,22 +3,6 @@ var/list/ventcrawl_machinery = list(
|
||||
/obj/machinery/atmospherics/unary/vent_scrubber
|
||||
)
|
||||
|
||||
// Vent crawling whitelisted items, whoo
|
||||
/mob/living/var/list/can_enter_vent_with = list(
|
||||
/obj/item/implant,
|
||||
/obj/item/radio/borg,
|
||||
/obj/item/radio/headset/mob_headset,
|
||||
/obj/item/holder,
|
||||
/obj/machinery/camera,
|
||||
/obj/belly,
|
||||
/obj/soulgem,
|
||||
/atom/movable/screen,
|
||||
/atom/movable/emissive_blocker,
|
||||
/obj/item/rig/protean
|
||||
)
|
||||
//VOREStation Edit : added /obj/belly, to this list, CI is complaining about this in his indentation check. Added mob_headset for those with radios so there's no weirdness.
|
||||
//mob/living/simple_mob/borer, //VORESTATION AI TEMPORARY REMOVAL REPLACE BACK IN LIST WHEN RESOLVED //VOREStation Edit
|
||||
|
||||
/mob/living/var/list/icon/pipes_shown = list()
|
||||
/mob/living/var/last_played_vent
|
||||
/mob/living/var/is_ventcrawling = FALSE
|
||||
@@ -75,7 +59,10 @@ var/list/ventcrawl_machinery = list(
|
||||
return TRUE
|
||||
//Try to find it in our allowed list (istype includes subtypes)
|
||||
var/listed = FALSE
|
||||
for(var/test_type in can_enter_vent_with)
|
||||
var/list/vent_allow = ventcrawl_get_item_whitelist()
|
||||
if(islist(ventcraw_item_admin_allow)) // If mob has a list varedited onto it, we allow anything in this list as well
|
||||
vent_allow += ventcraw_item_admin_allow
|
||||
for(var/test_type in vent_allow)
|
||||
if(istype(carried_item,test_type))
|
||||
listed = TRUE
|
||||
break
|
||||
@@ -108,6 +95,13 @@ var/list/ventcrawl_machinery = list(
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/mob/living/proc/ventcrawl_get_item_whitelist()
|
||||
return list(
|
||||
VENTCRAWL_BASE_WHITELIST,
|
||||
VENTCRAWL_VORE_WHITELIST,
|
||||
VENTCRAWL_SMALLITEM_WHITELIST
|
||||
)
|
||||
|
||||
/mob/living/simple_mob/protean_blob/ventcrawl_carry()
|
||||
for(var/atom/A in contents)
|
||||
if(!is_allowed_vent_crawl_item(A))
|
||||
|
||||
@@ -17,7 +17,7 @@ export RUST_G_VERSION=4.2.0
|
||||
export NODE_VERSION_LTS=22.14.0
|
||||
|
||||
# Bun version
|
||||
export BUN_VERSION=1.3.3
|
||||
export BUN_VERSION=1.3.4
|
||||
|
||||
# SpacemanDMM git tag
|
||||
export SPACEMAN_DMM_VERSION=suite-1.11
|
||||
|
||||
4
html/changelogs_ch/AutoChangeLog-pr-12098.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "CHOMPStation2StaffMirrorBot"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "continuous medigun"
|
||||
4
html/changelogs_ch/AutoChangeLog-pr-12099.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "CHOMPStation2StaffMirrorBot"
|
||||
delete-after: True
|
||||
changes:
|
||||
- code_imp: "tgui-core 5.5.10"
|
||||
5
html/changelogs_ch/AutoChangeLog-pr-12104.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
author: "CHOMPStation2StaffMirrorBot"
|
||||
delete-after: True
|
||||
changes:
|
||||
- bugfix: "some bad flag compares"
|
||||
- bugfix: "disposal template loading being broken"
|
||||
5
html/changelogs_ch/AutoChangeLog-pr-12105.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
author: "Guti"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "Added TG-like anomalies"
|
||||
- rscadd: "Added anomaly cores"
|
||||
5
html/changelogs_ch/AutoChangeLog-pr-12106.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
author: "Will"
|
||||
delete-after: True
|
||||
changes:
|
||||
- refactor: "Ventcrawl item whitelist is now a proc instead of a list instantiated on every single mob in the world"
|
||||
- bugfix: "promies and proteans properly reequip their radio when they return to human form from blob"
|
||||
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 2.8 MiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 2.6 MiB |
|
Before Width: | Height: | Size: 700 KiB After Width: | Height: | Size: 700 KiB |
|
Before Width: | Height: | Size: 3.8 MiB After Width: | Height: | Size: 3.8 MiB |
|
Before Width: | Height: | Size: 4.5 MiB After Width: | Height: | Size: 4.5 MiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 295 KiB After Width: | Height: | Size: 295 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 2.4 MiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 2.6 MiB |
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 2.6 MiB |
|
Before Width: | Height: | Size: 2.3 MiB After Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 188 KiB After Width: | Height: | Size: 188 KiB |
|
Before Width: | Height: | Size: 740 KiB After Width: | Height: | Size: 741 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 781 B After Width: | Height: | Size: 781 B |
|
Before Width: | Height: | Size: 959 KiB After Width: | Height: | Size: 959 KiB |
|
Before Width: | Height: | Size: 991 KiB After Width: | Height: | Size: 991 KiB |
|
Before Width: | Height: | Size: 924 KiB After Width: | Height: | Size: 925 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 781 B After Width: | Height: | Size: 781 B |
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 980 KiB After Width: | Height: | Size: 980 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 191 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 44 KiB |