11 Commits

Author SHA1 Message Date
chompstation-ci[bot]
e6dc148bf2 Automatic changelog for PR #12106 [ci skip] 2025-12-11 12:46:49 +00:00
CHOMPStation2StaffMirrorBot
93d51618fb [MIRROR] Ventcrawling Item Whitelist Memory Savings (#12106)
Co-authored-by: Will <7099514+Willburd@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
2025-12-11 07:46:11 -05:00
chompstation-ci[bot]
ae684bd969 Automatic changelog for PR #12104 [ci skip] 2025-12-11 12:36:00 +00:00
CHOMPStation2StaffMirrorBot
574d960872 [MIRROR] fix disposal template loading (#12104)
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
2025-12-11 07:35:20 -05:00
chompstation-ci[bot]
ab8e6d3921 Automatic changelog for PR #12105 [ci skip] 2025-12-11 12:27:53 +00:00
CHOMPStation2StaffMirrorBot
7971b00c5d [MIRROR] Ports TG Anomalies (#12105)
Co-authored-by: Guti <32563288+TheCaramelion@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
2025-12-11 07:27:13 -05:00
chompstation-ci[bot]
e7feab24f7 Automatic changelog for PR #12098 [ci skip] 2025-12-11 02:01:25 +00:00
chompstation-ci[bot]
d92c790876 Automatic changelog for PR #12099 [ci skip] 2025-12-11 02:01:13 +00:00
github-actions[bot]
989cae1d22 Automatic NanoMap Update (#12025)
Co-authored-by: NanoMap Generation <action@github.com>
2025-12-11 03:01:02 +01:00
CHOMPStation2StaffMirrorBot
f4fe45f8d3 [MIRROR] Medigun Port (#12098)
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
Co-authored-by: Guti <32563288+TheCaramelion@users.noreply.github.com>
Co-authored-by: vorestation-ci[bot] <199609141+vorestation-ci[bot]@users.noreply.github.com>
Co-authored-by: Killian <49700375+KillianKirilenko@users.noreply.github.com>
Co-authored-by: Jenny <ghosttehspychecka@gmail.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Will <7099514+Willburd@users.noreply.github.com>
Co-authored-by: TheToaster98 <51209769+TheToaster98@users.noreply.github.com>
Co-authored-by: Selis <12716288+ItsSelis@users.noreply.github.com>
Co-authored-by: SatinIsle <98125273+SatinIsle@users.noreply.github.com>
Co-authored-by: Aura Dusklight <46622484+NovaDusklight@users.noreply.github.com>
Co-authored-by: Aroliacue <96730930+Aroliacue@users.noreply.github.com>
Co-authored-by: Aroliacue <avaylaiss34@gmail.com>
Co-authored-by: nesquik <24830358+lbnesquik@users.noreply.github.com>
Co-authored-by: ShadowLarkens <shadowlarkens@gmail.com>
Co-authored-by: MeepleMuncher <76881946+MeepleMuncher@users.noreply.github.com>
Co-authored-by: Olive <49600480+zeskorion@users.noreply.github.com>
Co-authored-by: shybandit5213 <shybandit5213@gmail.com>
Co-authored-by: Bandit <queenjess521@gmail.com>
2025-12-11 03:00:49 +01:00
CHOMPStation2StaffMirrorBot
a8ad45d7b8 [MIRROR] tgui core 5.5.10 (#12099)
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
2025-12-11 03:00:37 +01:00
136 changed files with 3859 additions and 643 deletions

View File

@@ -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
View File

@@ -0,0 +1,47 @@
/**
* # Anomaly Defines
* This file contains defines for the random event anomaly subtypes.
*/
///Time in ticks before the anomaly goes poof/explodes depending on type.
#define ANOMALY_COUNTDOWN_TIMER (120 SECONDS)
/**
* Nuisance/funny anomalies
*/
///Time in seconds before anomaly spawns
#define ANOMALY_START_MEDIUM_TIME (6 EVENT_SECONDS)
///Time in seconds before anomaly is announced
#define ANOMALY_ANNOUNCE_MEDIUM_TIME (2 EVENT_SECONDS)
///Let them know how far away the anomaly is
#define ANOMALY_ANNOUNCE_MEDIUM_TEXT "long range scanners. Expected location:"
/**
* Chaotic but not harmful anomalies. Give the station a chance to find it on their own.
*/
///Time in seconds before anomaly spawns
#define ANOMALY_START_HARMFUL_TIME (2 EVENT_SECONDS)
///Time in seconds before anomaly is announced
#define ANOMALY_ANNOUNCE_HARMFUL_TIME (30 EVENT_SECONDS)
///Let them know how far away the anomaly is
#define ANOMALY_ANNOUNCE_HARMFUL_TEXT "localized scanners. Detected location:"
/**
* Anomalies that can fuck you up. Give them a bit of warning.
*/
///Time in seconds before anomaly spawns
#define ANOMALY_START_DANGEROUS_TIME (2 EVENT_SECONDS)
///Time in seconds before anomaly is announced
#define ANOMALY_ANNOUNCE_DANGEROUS_TIME (30 EVENT_SECONDS)
///Let them know how far away the anomaly is
#define ANOMALY_ANNOUNCE_DANGEROUS_TEXT "localized scanners. Detected location:"
#define GRAVITATIONAL_ANOMALY "gravitational_anomaly"
#define FLUX_ANOMALY "flux_anomaly"
#define PYRO_ANOMALY "pyro_anomaly"
#define BIOSCRAMBLER_ANOMALY "bioscrambler_anomaly"
#define HALLUCINATION_ANOMALY "hallucination_anomaly"
#define DIMENSIONAL_ANOMALY "dimensional_anomaly"

View File

@@ -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)]")

View File

@@ -96,6 +96,8 @@
#define COLOR_NUKIES_YELLOW "#ffe900"
#define COLOR_DESATTI_PRPLOW "#3300cc"
#define COLOR_DESATTI_PRPHI "#6600cc"
#define COLOR_FULL_TONER_BLACK "#101010"
#define COLOR_VOID_PURPLE "#53277E"
#define PIPE_COLOR_GREY "#808080"
#define PIPE_COLOR_RED "#ff0000"

View File

@@ -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))

View File

@@ -237,3 +237,10 @@
return 1
else
return 0
/// Converts a probability/second chance to probability/seconds_per_tick chance
/// For example, if you want an event to happen with a 10% per second chance, but your proc only runs every 5 seconds, do `if(prob(100*SPT_PROB_RATE(0.1, 5)))`
#define SPT_PROB_RATE(prob_per_second, seconds_per_tick) (1 - (1 - (prob_per_second)) ** (seconds_per_tick))
/// Like SPT_PROB_RATE but easier to use, simply put `if(SPT_PROB(10, 5))`
#define SPT_PROB(prob_per_second_percent, seconds_per_tick) (prob(100*SPT_PROB_RATE((prob_per_second_percent)/100, (seconds_per_tick))))

View File

@@ -0,0 +1,3 @@
#define MEDIGUN_IDLE 0
#define MEDIGUN_CANCELLED 1
#define MEDIGUN_BUSY 2

View File

@@ -0,0 +1,7 @@
///Defines for the different types of explosion a flux anomaly can have
#define FLUX_NO_EMP 0
#define FLUX_EMP 1
#define FLUX_LIGHT_EMP 2
/// Chance of anomalies moving every process tick
#define ANOMALY_MOVECHANCE 45

View File

@@ -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"

View File

@@ -45,3 +45,5 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_TESLA_SHOCKIMMUNE "tesla_shock_immunity"
/// Do we show up as a changeling / the wrong body-mind pair to sleevmates
#define UNIQUE_MINDSTRUCTURE "unique_mindstructure"
// Owner is immune to hallucinations
#define TRAIT_MADNESS_IMMUNE "supermatter_madness_immune"

View 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

View File

@@ -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,

View File

@@ -151,9 +151,17 @@
return 0
//Checks for specific paths in a list
/proc/is_path_in_list(var/atom/A, var/list/L)
/**
* Arguments:
* A : Typepath to check
* L : A list of typepath to check A against
* zebra: Wether to use the value of the path in the list instead of just returning TRUE when a match is found
*/
/proc/is_path_in_list(var/atom/A, var/list/L, zebra = FALSE)
for(var/path in L)
if(ispath(A, path))
if(ispath(A, path))
return !zebra || L[path]
return 1
return 0
@@ -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

View File

@@ -189,3 +189,57 @@
var/testdir = get_dir(target, origin)
return (dir & testdir)
return TRUE
///similar function to RANGE_TURFS(), but will search spiralling outwards from the center (like the above, but only turfs)
/proc/spiral_range_turfs(dist = 0, center = usr, orange = FALSE, list/outlist = list(), tick_checked)
outlist.Cut()
if(!dist)
outlist += center
return outlist
var/turf/t_center = get_turf(center)
if(!t_center)
return outlist
var/list/turf_list = outlist
var/turf/checked_turf
var/y
var/x
var/c_dist = 1
if(!orange)
turf_list += t_center
while( c_dist <= dist )
y = t_center.y + c_dist
x = t_center.x - c_dist + 1
for(x in x to t_center.x + c_dist)
checked_turf = locate(x, y, t_center.z)
if(checked_turf)
turf_list += checked_turf
y = t_center.y + c_dist - 1
x = t_center.x + c_dist
for(y in t_center.y - c_dist to y)
checked_turf = locate(x, y, t_center.z)
if(checked_turf)
turf_list += checked_turf
y = t_center.y - c_dist
x = t_center.x + c_dist - 1
for(x in t_center.x - c_dist to x)
checked_turf = locate(x, y, t_center.z)
if(checked_turf)
turf_list += checked_turf
y = t_center.y - c_dist + 1
x = t_center.x - c_dist
for(y in y to t_center.y + c_dist)
checked_turf = locate(x, y, t_center.z)
if(checked_turf)
turf_list += checked_turf
c_dist++
if(tick_checked)
CHECK_TICK
return turf_list

View File

@@ -44,7 +44,14 @@ SUBSYSTEM_DEF(research)
/obj/item/research_sample/common = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS*0.5),
/obj/item/research_sample/uncommon = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS*0.5),
/obj/item/research_sample/rare = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS*0.5),
/obj/item/research_sample/bluespace = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS*0.5)
/obj/item/research_sample/bluespace = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS*0.5),
/obj/item/assembly/signaler/anomaly/bioscrambler = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS),
/obj/item/assembly/signaler/anomaly/bluespace = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS),
/obj/item/assembly/signaler/anomaly/dimensional = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS),
/obj/item/assembly/signaler/anomaly/flux = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS),
/obj/item/assembly/signaler/anomaly/grav = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS),
/obj/item/assembly/signaler/anomaly/hallucination = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS),
/obj/item/assembly/signaler/anomaly/pyro = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
)
///Allows repeated deconstruction of these items for points. These items MUST be in techweb_point_items as well.
var/list/techweb_repeatable_items = list(

View File

@@ -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

View File

@@ -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)

View File

@@ -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(

View 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)

View File

@@ -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()

View 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

View 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

View File

@@ -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/)
)

View 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 ..()

View 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

View 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

View 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()

View 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

View 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)

View File

@@ -9,7 +9,7 @@
anchored = TRUE
plane = PLANE_GHOSTS
color = "#ff0000"
var/text_size = 3
var/text_size = 3 // Larger values clip when the displayed text is larger than 2 digits
var/started = FALSE
var/displayed_text
var/atom/attached_to
@@ -78,7 +78,7 @@
/obj/effect/countdown/singularity_act()
return
/*
/obj/effect/countdown/anomaly
name = "anomaly countdown"
@@ -91,4 +91,3 @@
else
var/time_left = max(0, (A.death_time - world.time)/10)
return round(time_left)
*/

View File

@@ -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

View 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

View 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 ..()

View 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

View 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))

View 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

View File

@@ -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

View 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()

View File

@@ -406,6 +406,7 @@
active_armourpen = 25
projectile_parry_chance = 40
colorable = TRUE
item_flags = DROPDEL | NOSTRIP
hitcost = 75

View File

@@ -160,7 +160,8 @@
body_parts_covered = CHEST
armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0)
// Ooold, old reactive armor.
/*
//Reactive armor
//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!)
/obj/item/clothing/suit/armor/reactive
@@ -211,7 +212,7 @@
active = FALSE
icon_state = "reactiveoff"
..()
*/
// Alien armor has a chance to completely block attacks.
/obj/item/clothing/suit/armor/alien
name = "alien enhancement vest"

View 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

View File

@@ -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

View File

@@ -49,7 +49,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
// Traditional hallucinations
/////////////////////////////////////////////////////////////////////////////////////////////////////
/mob/living/carbon/proc/handle_hallucinations()
if(get_hallucination_component() || !client)
if(get_hallucination_component() || !client || HAS_TRAIT(src, TRAIT_MADNESS_IMMUNE))
return
LoadComponent(/datum/component/hallucinations)

View File

@@ -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)

View File

@@ -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))

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View 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

View File

@@ -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
)

View File

@@ -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."

View File

@@ -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

View File

@@ -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"

View File

@@ -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)

View File

@@ -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))

View File

@@ -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

View File

@@ -0,0 +1,4 @@
author: "CHOMPStation2StaffMirrorBot"
delete-after: True
changes:
- rscadd: "continuous medigun"

View File

@@ -0,0 +1,4 @@
author: "CHOMPStation2StaffMirrorBot"
delete-after: True
changes:
- code_imp: "tgui-core 5.5.10"

View File

@@ -0,0 +1,5 @@
author: "CHOMPStation2StaffMirrorBot"
delete-after: True
changes:
- bugfix: "some bad flag compares"
- bugfix: "disposal template loading being broken"

View File

@@ -0,0 +1,5 @@
author: "Guti"
delete-after: True
changes:
- rscadd: "Added TG-like anomalies"
- rscadd: "Added anomaly cores"

View 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"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 700 KiB

After

Width:  |  Height:  |  Size: 700 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 MiB

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 MiB

After

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 KiB

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 740 KiB

After

Width:  |  Height:  |  Size: 741 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 781 B

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 959 KiB

After

Width:  |  Height:  |  Size: 959 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 991 KiB

After

Width:  |  Height:  |  Size: 991 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 924 KiB

After

Width:  |  Height:  |  Size: 925 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 781 B

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 980 KiB

After

Width:  |  Height:  |  Size: 980 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Some files were not shown because too many files have changed in this diff Show More