diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000000..7d4716f9122
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+patreon: ParadiseStation
diff --git a/_build_dependencies.sh b/_build_dependencies.sh
index 8cef5ce91b9..3aabf9e3758 100644
--- a/_build_dependencies.sh
+++ b/_build_dependencies.sh
@@ -1,9 +1,9 @@
# This file has all the information on what versions of libraries are thrown into the code
# For dreamchecker
-export SPACEMANDMM_TAG=suite-1.6
+export SPACEMANDMM_TAG=suite-1.7
# For TGUI
export NODE_VERSION=12
# Byond Major
-export BYOND_MAJOR=513
+export BYOND_MAJOR=514
# Byond Minor
-export BYOND_MINOR=1528
+export BYOND_MINOR=1566
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index e87108a1f5c..47f06559714 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -10168,10 +10168,6 @@
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
},
-/obj/structure/disposalpipe/segment{
- dir = 1;
- icon_state = "pipe-c"
- },
/obj/structure/disposalpipe/sortjunction{
dir = 4;
icon_state = "pipe-j2s";
@@ -24092,7 +24088,7 @@
/obj/machinery/door/firedoor,
/obj/machinery/door/airlock/engineering/glass{
name = "Engineering";
- req_access_txt = "32"
+ req_one_access_txt = "32;70"
},
/obj/structure/cable/yellow{
d1 = 4;
@@ -27172,13 +27168,14 @@
},
/area/storage/tech)
"bhK" = (
-/obj/structure/reagent_dispensers/spacecleanertank{
- pixel_y = 30
- },
/obj/structure/disposalpipe/segment{
dir = 2;
icon_state = "pipe-c"
},
+/obj/structure/closet/jcloset,
+/obj/machinery/light_switch{
+ pixel_x = 23
+ },
/turf/simulated/floor/plasteel,
/area/janitor)
"bhL" = (
@@ -27726,8 +27723,7 @@
/area/hallway/primary/central)
"biY" = (
/obj/structure/reagent_dispensers/watertank,
-/obj/machinery/light_switch{
- pixel_x = 8;
+/obj/structure/reagent_dispensers/spacecleanertank{
pixel_y = 30
},
/turf/simulated/floor/plasteel{
@@ -28629,7 +28625,7 @@
"bkL" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
- dir = 9
+ dir = 5
},
/turf/simulated/floor/plating,
/area/janitor)
@@ -28639,6 +28635,9 @@
d2 = 4;
icon_state = "2-4"
},
+/obj/machinery/atmospherics/unary/vent_scrubber/on{
+ dir = 8
+ },
/turf/simulated/floor/plasteel{
icon_state = "floorgrime"
},
@@ -29570,13 +29569,13 @@
d2 = 8;
icon_state = "4-8"
},
-/obj/machinery/atmospherics/pipe/simple/hidden/supply{
- dir = 5
- },
/mob/living/simple_animal/lizard{
name = "Wags-His-Tail";
real_name = "Wags-His-Tail"
},
+/obj/machinery/atmospherics/unary/vent_pump/on{
+ dir = 1
+ },
/turf/simulated/floor/plasteel{
icon_state = "floorgrime"
},
@@ -29590,9 +29589,6 @@
/obj/effect/landmark/start{
name = "Janitor"
},
-/obj/machinery/atmospherics/unary/vent_pump/on{
- dir = 8
- },
/turf/simulated/floor/plasteel{
icon_state = "floorgrime"
},
@@ -29743,14 +29739,12 @@
pixel_x = -24
},
/obj/structure/table,
-/obj/item/clothing/gloves/color/orange,
-/obj/item/storage/box/mousetraps,
-/obj/item/storage/box/mousetraps,
/obj/structure/cable/yellow{
d2 = 4;
icon_state = "0-4"
},
/obj/item/key/janitor,
+/obj/item/reagent_containers/spray/cleaner,
/turf/simulated/floor/plasteel{
icon_state = "floorgrime"
},
@@ -30503,6 +30497,8 @@
/obj/item/grenade/chem_grenade/cleaner,
/obj/item/grenade/chem_grenade/cleaner,
/obj/item/grenade/chem_grenade/cleaner,
+/obj/item/storage/box/mousetraps,
+/obj/item/storage/box/mousetraps,
/turf/simulated/floor/plasteel{
icon_state = "floorgrime"
},
@@ -30670,13 +30666,11 @@
"boN" = (
/obj/item/restraints/legcuffs/beartrap,
/obj/item/restraints/legcuffs/beartrap,
-/obj/structure/table,
/obj/machinery/requests_console{
department = "Janitorial";
departmentType = 1;
pixel_x = -29
},
-/obj/item/reagent_containers/spray/cleaner,
/obj/machinery/camera{
c_tag = "Custodial Closet";
dir = 4
@@ -30684,9 +30678,12 @@
/obj/machinery/light/small{
dir = 8
},
-/obj/machinery/atmospherics/unary/vent_scrubber/on{
- dir = 4
+/obj/item/mop,
+/obj/item/reagent_containers/glass/bucket,
+/obj/structure/rack{
+ dir = 1
},
+/obj/item/clothing/gloves/color/orange,
/turf/simulated/floor/plasteel{
icon_state = "floorgrime"
},
@@ -31247,8 +31244,6 @@
dir = 4;
pixel_x = 11
},
-/obj/item/reagent_containers/glass/bucket,
-/obj/item/mop,
/obj/machinery/firealarm{
dir = 4;
pixel_x = 24
@@ -35714,7 +35709,6 @@
},
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
-/obj/structure/disposalpipe/segment,
/obj/structure/disposalpipe/sortjunction{
dir = 1;
name = "Custodial Junction";
@@ -36158,7 +36152,7 @@
"byV" = (
/obj/machinery/door/airlock/maintenance{
name = "Engineering Foyer Maintenance";
- req_one_access_txt = "32;19"
+ req_one_access_txt = "32;19;70"
},
/turf/simulated/floor/plating,
/area/engine/break_room)
@@ -49728,7 +49722,6 @@
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 5
},
-/obj/structure/disposalpipe/segment,
/obj/structure/disposalpipe/sortjunction{
dir = 1;
icon_state = "pipe-j2s";
@@ -82360,14 +82353,6 @@
icon_state = "redfull"
},
/area/security/main)
-"vgQ" = (
-/obj/docking_port/stationary/whiteship{
- dir = 8;
- id = "whiteship_cyberiad";
- name = "North of Cerebron"
- },
-/turf/space,
-/area/space)
"vjp" = (
/obj/effect/decal/warning_stripes/west,
/obj/machinery/atmospherics/unary/vent_pump/on{
@@ -136762,7 +136747,7 @@ aaa
aaa
aaa
aaa
-vgQ
+aaa
aaa
aaa
aaa
diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm
index 6506ef1889a..ea8e5875eed 100644
--- a/_maps/map_files/cyberiad/cyberiad.dmm
+++ b/_maps/map_files/cyberiad/cyberiad.dmm
@@ -6,6 +6,19 @@
/obj/structure/lattice,
/turf/space,
/area/space/nearstation)
+"aac" = (
+/obj/machinery/alarm{
+ pixel_y = 22
+ },
+/obj/structure/table,
+/obj/item/radio/electropack,
+/obj/item/assembly/signaler,
+/obj/item/healthanalyzer,
+/obj/item/hand_labeler,
+/turf/simulated/floor/plasteel{
+ icon_state = "white"
+ },
+/area/toxins/misc_lab)
"aaQ" = (
/obj/docking_port/stationary/whiteship{
dir = 8;
@@ -64134,18 +64147,6 @@
},
/turf/simulated/floor/plasteel,
/area/engine/controlroom)
-"cBZ" = (
-/obj/machinery/alarm{
- pixel_y = 22
- },
-/obj/structure/table,
-/obj/item/radio/electropack,
-/obj/item/assembly/signaler,
-/obj/item/healthanalyzer,
-/turf/simulated/floor/plasteel{
- icon_state = "white"
- },
-/area/toxins/misc_lab)
"cCa" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
@@ -133899,7 +133900,7 @@ cuQ
bGG
cyR
cAP
-cBZ
+aac
cDw
cDb
cEt
diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm
index fc67f448526..f399915e3e7 100644
--- a/code/__DEFINES/colors.dm
+++ b/code/__DEFINES/colors.dm
@@ -114,3 +114,12 @@
#define PIPE_COLOR_GREEN "#00ff00"
#define PIPE_COLOR_YELLOW "#ffcc00"
#define PIPE_COLOR_PURPLE "#5c1ec0"
+
+///Main colors for UI themes
+#define COLOR_THEME_MIDNIGHT "#6086A0"
+#define COLOR_THEME_PLASMAFIRE "#FFB200"
+#define COLOR_THEME_RETRO "#24CA00"
+#define COLOR_THEME_SLIMECORE "#4FB259"
+#define COLOR_THEME_OPERATIVE "#B8221F"
+#define COLOR_THEME_GLASS "#75A4C4"
+#define COLOR_THEME_CLOCKWORK "#CFBA47"
diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm
index a68646224cf..7e0941d3e65 100644
--- a/code/__DEFINES/flags.dm
+++ b/code/__DEFINES/flags.dm
@@ -103,6 +103,7 @@
#define PASSBLOB 8
#define PASSMOB 16
#define LETPASSTHROW 32
+#define PASSFENCE 64
//turf-only flags
#define NOJAUNT 1
diff --git a/code/__DEFINES/footstep.dm b/code/__DEFINES/footstep.dm
index 469549b8827..bda71aa4443 100644
--- a/code/__DEFINES/footstep.dm
+++ b/code/__DEFINES/footstep.dm
@@ -7,12 +7,14 @@
#define FOOTSTEP_WATER "water"
#define FOOTSTEP_LAVA "lava"
#define FOOTSTEP_MEAT "meat"
+#define FOOTSTEP_GLASS "glass"
//barefoot sounds
#define FOOTSTEP_WOOD_BAREFOOT "woodbarefoot"
#define FOOTSTEP_WOOD_CLAW "woodclaw"
#define FOOTSTEP_HARD_BAREFOOT "hardbarefoot"
#define FOOTSTEP_HARD_CLAW "hardclaw"
#define FOOTSTEP_CARPET_BAREFOOT "carpetbarefoot"
+#define FOOTSTEP_GLASS_BAREFOOT "glassbarefoot"
//misc footstep sounds
#define FOOTSTEP_GENERIC_HEAVY "heavy"
@@ -83,7 +85,11 @@ GLOBAL_LIST_INIT(footstep, list(
'sound/effects/footstep/lava2.ogg',
'sound/effects/footstep/lava3.ogg'), 100, 0),
FOOTSTEP_MEAT = list(list(
- 'sound/effects/meatslap.ogg'), 100, 0)
+ 'sound/effects/meatslap.ogg'), 100, 0),
+ FOOTSTEP_GLASS = list(list(
+ 'sound/effects/footstep/glass1.ogg',
+ 'sound/effects/footstep/glass2.ogg',
+ 'sound/effects/footstep/glass3.ogg'), 100, 1),
))
//bare footsteps lists
GLOBAL_LIST_INIT(barefootstep, list(
@@ -127,6 +133,10 @@ GLOBAL_LIST_INIT(barefootstep, list(
'sound/effects/footstep/lava3.ogg'), 100, 0),
FOOTSTEP_MEAT = list(list(
'sound/effects/meatslap.ogg'), 100, 0),
+ FOOTSTEP_GLASS_BAREFOOT = list(list(
+ 'sound/effects/footstep/glassbarefoot1.ogg',
+ 'sound/effects/footstep/glassbarefoot2.ogg',
+ 'sound/effects/footstep/glassbarefoot3.ogg'), 100, 1),
))
//claw footsteps lists
@@ -171,6 +181,10 @@ GLOBAL_LIST_INIT(clawfootstep, list(
'sound/effects/footstep/lava3.ogg'), 100, 0),
FOOTSTEP_MEAT = list(list(
'sound/effects/meatslap.ogg'), 100, 0),
+ FOOTSTEP_GLASS = list(list(
+ 'sound/effects/footstep/glassbarefoot1.ogg',
+ 'sound/effects/footstep/glassbarefoot2.ogg',
+ 'sound/effects/footstep/glassbarefoot3.ogg'), 100, 1),
))
//heavy footsteps list
diff --git a/code/__DEFINES/genetics.dm b/code/__DEFINES/genetics.dm
index f64022136ef..55701ed9166 100644
--- a/code/__DEFINES/genetics.dm
+++ b/code/__DEFINES/genetics.dm
@@ -74,7 +74,6 @@
//Species traits.
#define NO_BLOOD "no_blood"
-#define NOTRANSSTING "no_trans_sting"
#define IS_WHITELISTED "whitelisted"
#define LIPS "lips"
#define EXOTIC_COLOR "exotic_blood_color"
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index 4950beb7e74..bff5100982a 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -26,11 +26,13 @@
#define DISPOSAL_PIPE_LAYER 2.3
#define GAS_PIPE_HIDDEN_LAYER 2.35
#define WIRE_LAYER 2.4
+#define TRANSPARENT_TURF_LAYER 2.41
#define WIRE_TERMINAL_LAYER 2.45
#define GAS_SCRUBBER_LAYER 2.46
#define GAS_PIPE_VISIBLE_LAYER 2.47
#define GAS_FILTER_LAYER 2.48
#define GAS_PUMP_LAYER 2.49
+#define HOLOPAD_LAYER 2.491
#define CONVEYOR_LAYER 2.495
#define LOW_OBJ_LAYER 2.5
#define LOW_SIGIL_LAYER 2.52
diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index ba292ab65f6..a198421a2cb 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -56,10 +56,11 @@
#define PREFTOGGLE_2_REVERB_DISABLE 512
#define PREFTOGGLE_2_FORCE_WHITE_RUNECHAT 1024
#define PREFTOGGLE_2_SIMPLE_STAT_PANEL 2048
+#define PREFTOGGLE_2_SEE_ITEM_OUTLINES 4096
-#define TOGGLES_2_TOTAL 4095 // If you add or remove a preference toggle above, make sure you update this define with the total value of the toggles combined.
+#define TOGGLES_2_TOTAL 8191 // If you add or remove a preference toggle above, make sure you update this define with the total value of the toggles combined.
-#define TOGGLES_2_DEFAULT (PREFTOGGLE_2_FANCYUI|PREFTOGGLE_2_ITEMATTACK|PREFTOGGLE_2_WINDOWFLASHING|PREFTOGGLE_2_RUNECHAT|PREFTOGGLE_2_DEATHMESSAGE|PREFTOGGLE_2_EMOTE_BUBBLE)
+#define TOGGLES_2_DEFAULT (PREFTOGGLE_2_FANCYUI|PREFTOGGLE_2_ITEMATTACK|PREFTOGGLE_2_WINDOWFLASHING|PREFTOGGLE_2_RUNECHAT|PREFTOGGLE_2_DEATHMESSAGE|PREFTOGGLE_2_EMOTE_BUBBLE|PREFTOGGLE_2_SEE_ITEM_OUTLINES)
// Sanity checks
#if TOGGLES_TOTAL > 16777215
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index e836824ad3a..0aa01605808 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -272,15 +272,19 @@
update_all_mob_security_hud()
return 1
-/*
-Proc for attack log creation, because really why not
-1 argument is the actor
-2 argument is the target of action
-3 is the full description of the action
-4 is whether or not to message admins
-This is always put in the attack log.
-*/
-
+/**
+ * Creates attack (old and new) logs for the user and defense logs for the target.
+ * Will message admins depending on the custom_level, user and target.
+ *
+ * custom_level will determine the log level set. Unless the target is SSD and there is a user doing it
+ * If custom_level is not set then the log level will be determined using the user and the target.
+ *
+ * * Arguments:
+ * * user - The thing doing it. Can be null
+ * * target - The target of the attack
+ * * what_done - What has happened
+ * * custom_level - The log level override
+ */
/proc/add_attack_logs(atom/user, target, what_done, custom_level)
if(islist(target)) // Multi-victim adding
var/list/targets = target
@@ -323,7 +327,7 @@ This is always put in the attack log.
loglevel = ATKLOG_ALMOSTALL
else
loglevel = ATKLOG_ALL // Hitting an object. Not a mob
- if(isLivingSSD(target)) // Attacks on SSDs are shown to admins with any log level except ATKLOG_NONE. Overrides custom level
+ if(user && isLivingSSD(target)) // Attacks on SSDs are shown to admins with any log level except ATKLOG_NONE. Overrides custom level
loglevel = ATKLOG_FEW
diff --git a/code/__HELPERS/traits.dm b/code/__HELPERS/traits.dm
index 5534c19cdd1..b92fb82f9af 100644
--- a/code/__HELPERS/traits.dm
+++ b/code/__HELPERS/traits.dm
@@ -164,6 +164,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_BLOODCRAWL_EAT "bloodcrawl_eat"
#define TRAIT_DWARF "dwarf"
#define TRAIT_SILENT_FOOTSTEPS "silent_footsteps" //makes your footsteps completely silent
+#define TRAIT_MESON_VISION "meson_vision"
+#define TRAIT_FLASH_PROTECTION "flash_protection"
+#define TRAIT_NIGHT_VISION "night_vision"
#define TRAIT_COMIC_SANS "comic_sans"
#define TRAIT_NOFINGERPRINTS "no_fingerprints"
diff --git a/code/_compile_options.dm b/code/_compile_options.dm
index 0e73437dd93..d418f30c78d 100644
--- a/code/_compile_options.dm
+++ b/code/_compile_options.dm
@@ -22,12 +22,12 @@
#define IS_MODE_COMPILED(MODE) (ispath(text2path("/datum/game_mode/"+(MODE))))
//Update this whenever you need to take advantage of more recent byond features
-#define MIN_COMPILER_VERSION 513
-#define MIN_COMPILER_BUILD 1514
+#define MIN_COMPILER_VERSION 514
+#define MIN_COMPILER_BUILD 1554
#if DM_VERSION < MIN_COMPILER_VERSION || DM_BUILD < MIN_COMPILER_BUILD
//Don't forget to update this part
#error Your version of BYOND is too out-of-date to compile this project. Go to https://secure.byond.com/download and update.
-#error You need version 513.1514 or higher
+#error You need version 514.1554 or higher
#endif
// Macros that must exist before world.dm
diff --git a/code/_globalvars/genetics.dm b/code/_globalvars/genetics.dm
index 1119ac364cc..2c49057e636 100644
--- a/code/_globalvars/genetics.dm
+++ b/code/_globalvars/genetics.dm
@@ -5,7 +5,6 @@ GLOBAL_VAR_INIT(deafblock, 0)
GLOBAL_VAR_INIT(hulkblock, 0)
GLOBAL_VAR_INIT(teleblock, 0)
GLOBAL_VAR_INIT(fireblock, 0)
-GLOBAL_VAR_INIT(xrayblock, 0)
GLOBAL_VAR_INIT(clumsyblock, 0)
GLOBAL_VAR_INIT(fakeblock, 0)
GLOBAL_VAR_INIT(coughblock, 0)
@@ -30,6 +29,9 @@ GLOBAL_VAR_INIT(hallucinationblock, 0)
GLOBAL_VAR_INIT(noprintsblock, 0)
GLOBAL_VAR_INIT(shockimmunityblock, 0)
GLOBAL_VAR_INIT(smallsizeblock, 0)
+GLOBAL_VAR_INIT(mesonblock, 0)
+GLOBAL_VAR_INIT(noflashblock, 0)
+GLOBAL_VAR_INIT(nightvisionblock, 0)
///////////////////////////////
// Goon Stuff
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index 39551da14b6..90840095938 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -46,6 +46,9 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_DWARF" = TRAIT_DWARF,
"TRAIT_SILENT_FOOTSTEPS" = TRAIT_SILENT_FOOTSTEPS,
"TRAIT_ALCOHOL_TOLERANCE" = TRAIT_ALCOHOL_TOLERANCE,
+ "TRAIT_MESON_VISION" = TRAIT_MESON_VISION,
+ "TRAIT_FLASH_PROTECTION" = TRAIT_FLASH_PROTECTION,
+ "TRAIT_NIGHT_VISION" = TRAIT_NIGHT_VISION,
"TRAIT_COMIC_SANS" = TRAIT_COMIC_SANS,
"TRAIT_NOFINGERPRINTS" = TRAIT_NOFINGERPRINTS,
diff --git a/code/controllers/subsystem/metrics.dm b/code/controllers/subsystem/metrics.dm
index 7ebaadfae77..95055f99216 100644
--- a/code/controllers/subsystem/metrics.dm
+++ b/code/controllers/subsystem/metrics.dm
@@ -23,7 +23,7 @@ SUBSYSTEM_DEF(metrics)
var/list/out = list()
out["@timestamp"] = time_stamp() // This is required by ElasticSearch, complete with this name. DO NOT REMOVE THIS.
out["cpu"] = world.cpu
- // out["maptick"] = world.map_cpu // TODO: 514
+ out["maptick"] = world.map_cpu
out["elapsed_processed"] = world.time
out["elapsed_real"] = (REALTIMEOFDAY - world_init_time)
out["client_count"] = length(GLOB.clients)
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 14a952cb36f..28f69ba1795 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -287,7 +287,7 @@ SUBSYSTEM_DEF(ticker)
else
log_debug("Playercount: [playercount] versus trigger: [highpop_trigger] - keeping standard job config")
- SSnightshift.check_nightshift()
+ SSnightshift.check_nightshift(TRUE)
#ifdef UNIT_TESTS
RunUnitTests()
diff --git a/code/datums/components/material_container.dm b/code/datums/components/material_container.dm
index f4b8e84204f..2ea582aab62 100644
--- a/code/datums/components/material_container.dm
+++ b/code/datums/components/material_container.dm
@@ -49,6 +49,10 @@
var/mat_path = possible_mats[id]
materials[id] = new mat_path()
+/datum/component/material_container/Destroy(force, silent)
+ QDEL_LIST_ASSOC_VAL(materials)
+ return ..()
+
/datum/component/material_container/proc/OnExamine(datum/source, mob/user, list/examine_list)
if(show_on_examine)
for(var/I in materials)
diff --git a/code/defines/procs/announce.dm b/code/defines/procs/announce.dm
index e8d3abb7335..301ed685d46 100644
--- a/code/defines/procs/announce.dm
+++ b/code/defines/procs/announce.dm
@@ -44,12 +44,13 @@ GLOBAL_DATUM_INIT(event_announcement, /datum/announcement/priority/command/event
title = "Security Announcement"
announcement_type = "Security Announcement"
-/datum/announcement/proc/Announce(message as text, new_title = "", new_sound = null, do_newscast = newscast, msg_sanitized = 0, from, msg_language)
+/datum/announcement/proc/Announce(message as text, new_title = "", new_sound = null, do_newscast = newscast, msg_sanitized = 0, from, msg_language, new_sound2 = null)
if(!message)
return
var/message_title = new_title ? new_title : title
var/message_sound = new_sound ? sound(new_sound) : sound
+ var/message_sound2 = new_sound2 ? sound(new_sound2) : sound
if(!msg_sanitized)
message = trim_strip_html_properly(message, allow_lines = 1)
@@ -74,6 +75,7 @@ GLOBAL_DATUM_INIT(event_announcement, /datum/announcement/priority/command/event
NewsCast(message, message_title)
Sound(message_sound, combined_receivers[1] + combined_receivers[2])
+ Sound(message_sound2, combined_receivers[1] + combined_receivers[2])
Log(message, message_title)
/datum/announcement/proc/Get_Receivers(datum/language/message_language)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index d8dd3bb44c8..e46086d9b2b 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1153,3 +1153,15 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
if(degrees)
appearance_flags |= PIXEL_SCALE
transform = M
+
+/*
+ Setter for the `density` variable.
+ Arguments:
+ * new_value - the new density you would want it to set.
+ Returns: Either null if identical to existing density, or the new density if different.
+*/
+/atom/proc/set_density(new_value)
+ if(density == new_value)
+ return
+ . = density
+ density = new_value
diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm
index 7e9b6c34634..5d1bb0c98b0 100644
--- a/code/game/dna/dna_modifier.dm
+++ b/code/game/dna/dna_modifier.dm
@@ -156,7 +156,7 @@
to_chat(usr, "[src] is already occupied!")
return
if(usr.abiotic())
- to_chat(usr, "Subject cannot have abiotic items on.")
+ to_chat(usr, "Subject may not hold anything in their hands.")
return
if(usr.has_buckled_mobs()) //mob attached to us
to_chat(usr, "[usr] will not fit into [src] because [usr.p_they()] [usr.p_have()] a slime latched onto [usr.p_their()] head.")
@@ -194,7 +194,7 @@
if(!istype(L) || L.buckled)
return
if(L.abiotic())
- to_chat(user, "Subject cannot have abiotic items on.")
+ to_chat(user, "Subject may not hold anything in their hands.")
return
if(L.has_buckled_mobs()) //mob attached to us
to_chat(user, "[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.")
@@ -232,7 +232,7 @@
to_chat(user, "The scanner is already occupied!")
return
if(G.affecting.abiotic())
- to_chat(user, "Subject cannot have abiotic items on.")
+ to_chat(user, "Subject may not hold anything in their hands.")
return
if(G.affecting.has_buckled_mobs()) //mob attached to us
to_chat(user, "[G] will not fit into [src] because [G.affecting.p_they()] [G.affecting.p_have()] a slime latched onto [G.affecting.p_their()] head.")
diff --git a/code/game/dna/mutations/disabilities.dm b/code/game/dna/mutations/disabilities.dm
index bcb11eb1631..a03c686140a 100644
--- a/code/game/dna/mutations/disabilities.dm
+++ b/code/game/dna/mutations/disabilities.dm
@@ -140,12 +140,16 @@
/datum/mutation/disability/colourblindness/activate(mob/M)
..()
M.update_client_colour() //Handle the activation of the colourblindness on the mob.
- M.update_icons() //Apply eyeshine as needed.
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ H.update_misc_effects()
/datum/mutation/disability/colourblindness/deactivate(mob/M)
..()
M.update_client_colour() //Handle the deactivation of the colourblindness on the mob.
- M.update_icons() //Remove eyeshine as needed.
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ H.update_misc_effects()
/datum/mutation/disability/deaf
name = "Deafness"
diff --git a/code/game/dna/mutations/powers.dm b/code/game/dna/mutations/powers.dm
index 3d2f991acb6..20c37357c50 100644
--- a/code/game/dna/mutations/powers.dm
+++ b/code/game/dna/mutations/powers.dm
@@ -158,28 +158,6 @@
M.status_flags |= CANSTUN | CANWEAKEN | CANPARALYSE | CANPUSH //temporary fix until the problem can be solved.
to_chat(M, "You suddenly feel very weak.")
-/datum/mutation/xray
- name = "X-Ray Vision"
- activation_messages = list("The walls suddenly disappear.")
- deactivation_messages = list("The walls around you re-appear.")
- instability = GENE_INSTABILITY_MAJOR
- traits_to_add = list(TRAIT_XRAY_VISION)
- activation_prob = 15
-
-/datum/mutation/xray/New()
- ..()
- block = GLOB.xrayblock
-
-/datum/mutation/xray/activate(mob/living/M)
- ..()
- M.update_sight()
- M.update_icons() //Apply eyeshine as needed.
-
-/datum/mutation/xray/deactivate(mob/living/M)
- ..()
- M.update_sight()
- M.update_icons() //Remove eyeshine as needed.
-
/datum/mutation/tk
name = "Telekenesis"
activation_messages = list("You feel smarter.")
@@ -1181,3 +1159,58 @@
else
H.remoteview_target = null
H.reset_perspective()
+
+/datum/mutation/meson_vision
+ name = "Meson Vision"
+ activation_messages = list("More information seems to reach your eyes...")
+ deactivation_messages = list("The amount of information reaching your eyes fades...")
+ instability = GENE_INSTABILITY_MINOR
+ traits_to_add = list(TRAIT_MESON_VISION)
+
+/datum/mutation/meson_vision/New()
+ ..()
+ block = GLOB.mesonblock
+
+/datum/mutation/meson_vision/activate(mob/living/M)
+ ..()
+ M.update_sight()
+
+/datum/mutation/meson_vision/deactivate(mob/living/M)
+ ..()
+ M.update_sight()
+
+/datum/mutation/night_vision
+ name = "Night Vision"
+ activation_messages = list("Were the lights always that bright?")
+ deactivation_messages = list("The ambient light level returns to normal...")
+ instability = GENE_INSTABILITY_MODERATE
+ traits_to_add = list(TRAIT_NIGHT_VISION)
+
+/datum/mutation/night_vision/New()
+ ..()
+ block = GLOB.nightvisionblock
+
+/datum/mutation/night_vision/activate(mob/living/M)
+ ..()
+ M.update_sight()
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ H.update_misc_effects()
+
+/datum/mutation/night_vision/deactivate(mob/living/M)
+ ..()
+ M.update_sight()
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ H.update_misc_effects()
+
+/datum/mutation/flash_protection
+ name = "Flash Protection"
+ activation_messages = list("You stop noticing the glare from lights...")
+ deactivation_messages = list("Lights begin glaring again...")
+ instability = GENE_INSTABILITY_MINOR
+ traits_to_add = list(TRAIT_FLASH_PROTECTION)
+
+/datum/mutation/flash_protection/New()
+ ..()
+ block = GLOB.noflashblock
diff --git a/code/game/gamemodes/blob/overmind.dm b/code/game/gamemodes/blob/overmind.dm
index e5da77c8196..02022121209 100644
--- a/code/game/gamemodes/blob/overmind.dm
+++ b/code/game/gamemodes/blob/overmind.dm
@@ -66,10 +66,10 @@
return
if(src.client)
- if(client.prefs.muted & MUTE_IC)
+ if(check_mute(client.ckey, MUTE_IC))
to_chat(src, "You cannot send IC messages (muted).")
return
- if(src.client.handle_spam_prevention(message,MUTE_IC))
+ if(src.client.handle_spam_prevention(message, MUTE_IC))
return
if(stat)
diff --git a/code/game/gamemodes/changeling/powers/epinephrine.dm b/code/game/gamemodes/changeling/powers/epinephrine.dm
index ef22de10ce4..5d442caf60c 100644
--- a/code/game/gamemodes/changeling/powers/epinephrine.dm
+++ b/code/game/gamemodes/changeling/powers/epinephrine.dm
@@ -10,20 +10,19 @@
//Recover from stuns.
/datum/action/changeling/epinephrine/sting_action(mob/living/user)
-
if(user.lying)
to_chat(user, "We arise.")
else
to_chat(user, "Adrenaline rushes through us.")
user.SetSleeping(0)
- user.stat = 0
+ user.WakeUp()
user.SetParalysis(0)
user.SetStunned(0)
user.SetWeakened(0)
- user.lying = 0
+ user.lying = FALSE
user.update_canmove()
user.reagents.add_reagent("synaptizine", 20)
user.adjustStaminaLoss(-75)
SSblackbox.record_feedback("nested tally", "changeling_powers", 1, list("[name]"))
- return 1
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/tiny_prick.dm b/code/game/gamemodes/changeling/powers/tiny_prick.dm
index 14d93439510..09702c97d3c 100644
--- a/code/game/gamemodes/changeling/powers/tiny_prick.dm
+++ b/code/game/gamemodes/changeling/powers/tiny_prick.dm
@@ -66,60 +66,6 @@
add_attack_logs(user, target, "Unsuccessful sting (changeling)")
return 1
-/datum/action/changeling/sting/transformation
- name = "Transformation Sting"
- desc = "We silently sting a human, injecting a retrovirus that forces them to transform. Costs 50 chemicals."
- helptext = "The victim will transform much like a changeling would. The effects will be obvious to the victim, and the process will damage our genomes."
- button_icon_state = "sting_transform"
- sting_icon = "sting_transform"
- chemical_cost = 50
- dna_cost = 3
- genetic_damage = 100
- var/datum/dna/selected_dna = null
-
-/datum/action/changeling/sting/transformation/Trigger()
- var/mob/user = usr
- var/datum/changeling/changeling = user.mind.changeling
- if(changeling.chosen_sting)
- unset_sting(user)
- return
- selected_dna = changeling.select_dna("Select the target DNA: ", "Target DNA")
- if(!selected_dna)
- return
- if((NOTRANSSTING in selected_dna.species.species_traits) || selected_dna.species.is_small)
- to_chat(user, "The selected DNA is incompatible with our sting.")
- return
- ..()
-
-/datum/action/changeling/sting/transformation/can_sting(mob/user, mob/target)
- if(!..())
- return
- if(HAS_TRAIT(target, TRAIT_HUSK) || !ishuman(target) || (NOTRANSSTING in target.dna.species.species_traits))
- to_chat(user, "Our sting appears ineffective against its DNA.")
- return FALSE
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- if(HAS_TRAIT(H, TRAIT_GENELESS))
- to_chat(user, "This won't work on a creature without DNA.")
- return FALSE
- return TRUE
-
-/datum/action/changeling/sting/transformation/sting_action(mob/user, mob/target)
- add_attack_logs(user, target, "Transformation sting (changeling) (new identity is [selected_dna.real_name])")
- if(issmall(target))
- to_chat(user, "Our genes cry out as we sting [target.name]!")
-
- if(iscarbon(target) && (target.status_flags & CANWEAKEN))
- var/mob/living/carbon/C = target
- C.do_jitter_animation(500)
-
- target.visible_message("[target] begins to violenty convulse!","You feel a tiny prick and a begin to uncontrollably convulse!")
-
- spawn(10)
- transform_dna(target,selected_dna)//target is always human so no problem here
- SSblackbox.record_feedback("nested tally", "changeling_powers", 1, list("[name]"))
- return TRUE
-
/datum/action/changeling/sting/extract_dna
name = "Extract DNA Sting"
desc = "We stealthily sting a target and extract their DNA. Costs 25 chemicals."
diff --git a/code/game/gamemodes/cult/cult_datums.dm b/code/game/gamemodes/cult/cult_datums.dm
index 67e9a78d879..fbbee18a94b 100644
--- a/code/game/gamemodes/cult/cult_datums.dm
+++ b/code/game/gamemodes/cult/cult_datums.dm
@@ -73,6 +73,9 @@
var/airlock_unruned_icon_file = 'icons/obj/doors/airlocks/cult/unruned/cult.dmi'
var/airlock_unruned_overlays_file = 'icons/obj/doors/airlocks/cult/unruned/cult-overlays.dmi'
+ /// Are cultist mirror shields active yet?
+ var/mirror_shields_active = FALSE
+
/datum/cult_info/fire
name = "Cult of Kha'Rin"
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index 9b890787d7f..4f6771739f2 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -444,6 +444,11 @@
* The illusion has a 60% chance to be hostile and attack non-cultists, and a 40% chance to just run away from the user.
*/
/obj/item/shield/mirror/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ // Incase they get one by some magic
+ if(!SSticker.cultdat.mirror_shields_active)
+ to_chat(owner, "This shield is powerless! You must perform the required sacrifice to empower it!")
+ return
+
if(iscultist(owner) && !owner.holy_check()) // Cultist holding the shield
// Hit by a projectile
diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm
index 8eae3876082..21c894ea4f6 100644
--- a/code/game/gamemodes/cult/cult_structures.dm
+++ b/code/game/gamemodes/cult/cult_structures.dm
@@ -86,8 +86,10 @@
to_chat(user, "The magic in [src] is weak, it will be ready to use again in [get_ETA()].")
return
- var/choice = show_radial_menu(user, src, choosable_items, require_near = TRUE)
- var/picked_type = choosable_items[choice]
+
+ var/list/pickable_items = get_choosable_items()
+ var/choice = show_radial_menu(user, src, pickable_items, require_near = TRUE)
+ var/picked_type = pickable_items[choice]
if(!QDELETED(src) && picked_type && Adjacent(user) && !user.incapacitated() && cooldowntime <= world.time)
cooldowntime = world.time + creation_delay
var/obj/O = new picked_type
@@ -95,6 +97,14 @@
O.forceMove(get_turf(src))
to_chat(user, replacetext("[creation_message]", "%ITEM%", "[O.name]"))
+/**
+ * Returns the items the cult can craft from this forge.
+ *
+ * Override on children for logic regarding game state.
+ */
+/obj/structure/cult/functional/proc/get_choosable_items()
+ return choosable_items.Copy() // Copied incase its modified on children
+
/**
* Returns the cooldown time in minutes and seconds
*/
@@ -159,8 +169,15 @@
selection_prompt = "You study the schematics etched on the forge..."
selection_title = "Forge"
creation_message = "You work the forge as dark knowledge guides your hands, creating a %ITEM%!"
- choosable_items = list("Shielded Robe" = /obj/item/clothing/suit/hooded/cultrobes/cult_shield, "Flagellant's Robe" = /obj/item/clothing/suit/hooded/cultrobes/flagellant_robe,
- "Mirror Shield" = /obj/item/shield/mirror)
+ choosable_items = list("Shielded Robe" = /obj/item/clothing/suit/hooded/cultrobes/cult_shield, "Flagellant's Robe" = /obj/item/clothing/suit/hooded/cultrobes/flagellant_robe)
+
+/obj/structure/cult/functional/forge/get_choosable_items()
+ . = ..()
+ if(SSticker.cultdat.mirror_shields_active)
+ // Both lines here are needed. If you do it without, youll get issues.
+ . += "Mirror Shield"
+ .["Mirror Shield"] = /obj/item/shield/mirror
+
/obj/structure/cult/functional/forge/Initialize(mapload)
. = ..()
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index 1f03e634a8c..4a9234ac49f 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -384,6 +384,9 @@ structure_check() searches for nearby cultist structures required for the invoca
for(var/M in invokers)
if(sacrifice_fulfilled)
to_chat(M, "\"Yes! This is the one I desire! You have done well.\"")
+ if(!SSticker.cultdat.mirror_shields_active) // Only show once
+ to_chat(M, "You are now able to construct mirror shields inside the daemon forge.")
+ SSticker.cultdat.mirror_shields_active = TRUE
else
if(ishuman(offering) && offering.mind.offstation_role && offering.mind.special_role != SPECIAL_ROLE_ERT) //If you try it on a ghost role, you get nothing
to_chat(M, "\"This soul is of no use to either of us.\"")
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 4e5372ffc2d..7411622d358 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -1,3 +1,6 @@
+#define NUKE_INTACT 0
+#define NUKE_CORE_MISSING 1
+#define NUKE_MISSING 2
/*
* GAMEMODES (by Rastaf0)
*
@@ -434,6 +437,15 @@
nukecode = bomb.r_code
return nukecode
+/proc/get_nuke_status()
+ var/nuke_status = NUKE_MISSING
+ for(var/obj/machinery/nuclearbomb/bomb in GLOB.machines)
+ if(is_station_level(bomb.z))
+ nuke_status = NUKE_CORE_MISSING
+ if(bomb.core)
+ nuke_status = NUKE_INTACT
+ return nuke_status
+
/datum/game_mode/proc/replace_jobbanned_player(mob/living/M, role_type)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [role_type]?", role_type, FALSE, 10 SECONDS)
var/mob/dead/observer/theghost = null
@@ -534,3 +546,7 @@
var/datum/atom_hud/antag/antaghud = GLOB.huds[ANTAG_HUD_EVENTMISC]
antaghud.leave_hud(mob_mind.current)
set_antag_hud(mob_mind.current, null)
+
+#undef NUKE_INTACT
+#undef NUKE_CORE_MISSING
+#undef NUKE_MISSING
diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm
index e299f0c97c2..4f0552cbefa 100644
--- a/code/game/gamemodes/malfunction/Malf_Modules.dm
+++ b/code/game/gamemodes/malfunction/Malf_Modules.dm
@@ -248,7 +248,7 @@
/datum/action/innate/ai/nuke_station/proc/set_us_up_the_bomb()
to_chat(owner_AI, "Nuclear device armed.")
- GLOB.event_announcement.Announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", new_sound = 'sound/AI/aimalf.ogg')
+ GLOB.event_announcement.Announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", 'sound/AI/aimalf.ogg')
set_security_level("delta")
owner_AI.nuking = TRUE
var/obj/machinery/doomsday_device/DOOM = new /obj/machinery/doomsday_device(owner_AI)
@@ -280,7 +280,7 @@
if(SSshuttle.emergency.mode == SHUTTLE_STRANDED)
SSshuttle.emergency.mode = SHUTTLE_DOCKED
SSshuttle.emergency.timer = world.time
- GLOB.priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg')
+ GLOB.priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/eshuttle_dock.ogg')
return ..()
/obj/machinery/doomsday_device/proc/start()
@@ -300,7 +300,7 @@
if(SSshuttle.emergency.mode == SHUTTLE_STRANDED)
SSshuttle.emergency.mode = SHUTTLE_DOCKED
SSshuttle.emergency.timer = world.time
- GLOB.priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg')
+ GLOB.priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/eshuttle_dock.ogg')
qdel(src)
if(!timing)
STOP_PROCESSING(SSfastprocess, src)
diff --git a/code/game/gamemodes/miniantags/borer/borer.dm b/code/game/gamemodes/miniantags/borer/borer.dm
index b2f07c7e4e6..c39babad9f6 100644
--- a/code/game/gamemodes/miniantags/borer/borer.dm
+++ b/code/game/gamemodes/miniantags/borer/borer.dm
@@ -4,10 +4,10 @@
/mob/living/captive_brain/say(message)
if(client)
- if(client.prefs.muted & MUTE_IC)
+ if(check_mute(client.ckey, MUTE_IC))
to_chat(src, "You cannot speak in IC (muted).")
return
- if(client.handle_spam_prevention(message,MUTE_IC))
+ if(client.handle_spam_prevention(message, MUTE_IC))
return
if(istype(loc,/mob/living/simple_animal/borer))
diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm
index 93e385df6a9..39b883fed33 100644
--- a/code/game/gamemodes/revolution/revolution.dm
+++ b/code/game/gamemodes/revolution/revolution.dm
@@ -220,7 +220,7 @@
if(SSshuttle.emergency.mode == SHUTTLE_STRANDED)
SSshuttle.emergency.mode = SHUTTLE_DOCKED
SSshuttle.emergency.timer = world.time
- GLOB.command_announcement.Announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg')
+ GLOB.command_announcement.Announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/eshuttle_dock.ogg')
return ..()
if(finished != 0)
return TRUE
diff --git a/code/game/gamemodes/setupgame.dm b/code/game/gamemodes/setupgame.dm
index d749f8ef553..7315b52b062 100644
--- a/code/game/gamemodes/setupgame.dm
+++ b/code/game/gamemodes/setupgame.dm
@@ -40,7 +40,6 @@
GLOB.hulkblock = getAssignedBlock("HULK", numsToAssign, DNA_HARD_BOUNDS, good=1)
GLOB.teleblock = getAssignedBlock("TELE", numsToAssign, DNA_HARD_BOUNDS, good=1)
GLOB.fireblock = getAssignedBlock("FIRE", numsToAssign, DNA_HARDER_BOUNDS, good=1)
- GLOB.xrayblock = getAssignedBlock("XRAY", numsToAssign, DNA_HARDER_BOUNDS, good=1)
GLOB.clumsyblock = getAssignedBlock("CLUMSY", numsToAssign)
GLOB.fakeblock = getAssignedBlock("FAKE", numsToAssign)
GLOB.coughblock = getAssignedBlock("COUGH", numsToAssign)
@@ -49,6 +48,10 @@
GLOB.twitchblock = getAssignedBlock("TWITCH", numsToAssign)
GLOB.nervousblock = getAssignedBlock("NERVOUS", numsToAssign)
GLOB.wingdingsblock = getAssignedBlock("WINGDINGS", numsToAssign)
+ GLOB.mesonblock = getAssignedBlock("MESONS", numsToAssign, good=1)
+ GLOB.noflashblock = getAssignedBlock("NOFLASH", numsToAssign, DNA_HARDER_BOUNDS, good=1)
+ GLOB.nightvisionblock = getAssignedBlock("NIGHTVISION", numsToAssign, DNA_HARDER_BOUNDS, good=1)
+
// Bay muts
GLOB.breathlessblock = getAssignedBlock("BREATHLESS", numsToAssign, DNA_HARD_BOUNDS, good=1)
@@ -62,7 +65,6 @@
GLOB.noprintsblock = getAssignedBlock("NOPRINTS", numsToAssign, DNA_HARD_BOUNDS, good=1)
GLOB.shockimmunityblock = getAssignedBlock("SHOCKIMMUNITY", numsToAssign, good=1)
GLOB.smallsizeblock = getAssignedBlock("SMALLSIZE", numsToAssign, DNA_HARD_BOUNDS, good=1)
-
//
// Goon muts
/////////////////////////////////////////////
diff --git a/code/game/gamemodes/steal_items.dm b/code/game/gamemodes/steal_items.dm
index 77e775939fc..9814eb3ff2e 100644
--- a/code/game/gamemodes/steal_items.dm
+++ b/code/game/gamemodes/steal_items.dm
@@ -74,8 +74,8 @@
return 0
/datum/theft_objective/defib
- name = "a compact defibrillator"
- typepath = /obj/item/defibrillator/compact
+ name = "the chief medical officer's advanced compact defibrillator"
+ typepath = /obj/item/defibrillator/compact/advanced
protected_jobs = list("Chief Medical Officer", "Paramedic")
location_override = "the Chief Medical Officer's Office"
diff --git a/code/game/machinery/Beacon.dm b/code/game/machinery/Beacon.dm
index 41612fb870b..4705c041775 100644
--- a/code/game/machinery/Beacon.dm
+++ b/code/game/machinery/Beacon.dm
@@ -4,7 +4,8 @@
name = "Bluespace Gigabeacon"
desc = "A device that draws power from bluespace and creates a permanent tracking beacon."
level = 1 // underfloor
- layer = 2.5
+ layer = WIRE_LAYER
+ plane = FLOOR_PLANE
anchored = 1
use_power = IDLE_POWER_USE
idle_power_usage = 0
@@ -26,7 +27,8 @@
Beacon.syndicate = syndicate
Beacon.area_bypass = area_bypass
Beacon.cc_beacon = cc_beacon
- hide(T.intact)
+ if(!T.transparent_floor)
+ hide(T.intact)
/obj/machinery/bluespace_beacon/proc/destroy_beacon()
QDEL_NULL(Beacon)
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index 57062a6e806..1272a667baa 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -512,7 +512,7 @@
if(!istype(L) || L.buckled)
return
if(L.abiotic())
- to_chat(user, "Subject cannot have abiotic items on.")
+ to_chat(user, "Subject may not hold anything in their hands.")
return
if(L.has_buckled_mobs()) //mob attached to us
to_chat(user, "[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.")
diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm
index 9b16d52383c..52857a071dc 100644
--- a/code/game/machinery/adv_med.dm
+++ b/code/game/machinery/adv_med.dm
@@ -67,7 +67,7 @@
return
var/mob/living/carbon/human/M = TYPECAST_YOUR_SHIT.affecting
if(M.abiotic())
- to_chat(user, "Subject cannot have abiotic items on.")
+ to_chat(user, "Subject may not hold anything in their hands.")
return
M.forceMove(src)
occupant = M
@@ -122,7 +122,7 @@
if(H.buckled)
return FALSE
if(H.abiotic())
- to_chat(user, "Subject cannot have abiotic items on.")
+ to_chat(user, "Subject may not hold anything in their hands.")
return FALSE
if(H.has_buckled_mobs()) //mob attached to us
to_chat(user, "[H] will not fit into [src] because [H.p_they()] [H.p_have()] a slime latched onto [H.p_their()] head.")
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index e8d5cfcf437..79785006452 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -100,6 +100,7 @@ GLOBAL_LIST_INIT(cloner_biomass_items, list(\
/obj/machinery/clonepod/Destroy()
if(connected)
connected.pods -= src
+ connected = null
if(clonemind)
UnregisterSignal(clonemind.current, COMSIG_LIVING_REVIVE)
UnregisterSignal(clonemind, COMSIG_MIND_TRANSER_TO)
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 46da580a0d1..71e6155056f 100644
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -456,11 +456,11 @@
return
if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes.
+ SSshuttle.emergency.canRecall = FALSE
SSshuttle.emergency.request(null, 0.5, null, " Automatic Crew Transfer", 1)
- SSshuttle.emergency.canRecall = FALSE
else
- SSshuttle.emergency.request(null, 1, null, " Automatic Crew Transfer", 0)
SSshuttle.emergency.canRecall = FALSE
+ SSshuttle.emergency.request(null, 1, null, " Automatic Crew Transfer", 0)
if(user)
log_game("[key_name(user)] has called the shuttle.")
message_admins("[key_name_admin(user)] has called the shuttle - [formatJumpTo(user)].", 1)
diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm
index a7345522b25..d5d469ccdd1 100644
--- a/code/game/machinery/computer/message.dm
+++ b/code/game/machinery/computer/message.dm
@@ -34,6 +34,24 @@
light_color = LIGHT_COLOR_DARKGREEN
+/obj/machinery/computer/message_monitor/Initialize()
+ ..()
+ return INITIALIZE_HINT_LATELOAD // Give the message server time to initialize
+
+/obj/machinery/computer/message_monitor/LateInitialize()
+ //If the monitor isn't linked to a server, and there's a server available, default it to the first one in the list.
+ if(!linkedServer && length(GLOB.message_servers))
+ linkedServer = GLOB.message_servers[1]
+ RegisterSignal(linkedServer, COMSIG_PARENT_QDELETING, .proc/unlink_server)
+
+/obj/machinery/computer/message_monitor/proc/unlink_server()
+ SIGNAL_HANDLER
+ linkedServer = null
+
+/obj/machinery/computer/message_monitor/Destroy()
+ customrecepient = null
+ linkedServer = null
+ return ..()
/obj/machinery/computer/message_monitor/screwdriver_act(mob/user, obj/item/I)
if(emag) //Stops people from just unscrewing the monitor and putting it back to get the console working again.
@@ -71,14 +89,6 @@
..()
-/obj/machinery/computer/message_monitor/Initialize()
- ..()
- //Is the server isn't linked to a server, and there's a server available, default it to the first one in the list.
- if(!linkedServer)
- if(GLOB.message_servers && GLOB.message_servers.len > 0)
- linkedServer = GLOB.message_servers[1]
- return
-
/obj/machinery/computer/message_monitor/attack_hand(mob/user as mob)
if(..())
return
@@ -490,18 +500,18 @@
/obj/item/paper/monitorkey
- //..()
name = "Monitor Decryption Key"
var/obj/machinery/message_server/server = null
-/obj/item/paper/monitorkey/New()
+/obj/item/paper/monitorkey/Initialize(mapload)
..()
- spawn(10)
- if(GLOB.message_servers)
- for(var/obj/machinery/message_server/server in GLOB.message_servers)
- if(!isnull(server))
- if(!isnull(server.decryptkey))
- info = "
Daily Key Reset
\n\t
The new message monitor key is '[server.decryptkey]'.
Please keep this a secret and away from the clown.
If necessary, change the password to a more secure one."
- info_links = info
- overlays += "paper_words"
- break
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/item/paper/monitorkey/LateInitialize()
+ for(var/obj/machinery/message_server/server in GLOB.message_servers)
+ if(!isnull(server))
+ if(!isnull(server.decryptkey))
+ info = "Daily Key Reset
\n\t
The new message monitor key is '[server.decryptkey]'.
Please keep this a secret and away from the clown.
If necessary, change the password to a more secure one."
+ info_links = info
+ overlays += "paper_words"
+ break
diff --git a/code/game/machinery/defib_mount.dm b/code/game/machinery/defib_mount.dm
index 5aac39d12dd..fe333ebf4ef 100644
--- a/code/game/machinery/defib_mount.dm
+++ b/code/game/machinery/defib_mount.dm
@@ -34,8 +34,8 @@
if(building)
set_pixel_offsets_from_dir(30, -30, 30, -30)
-/obj/machinery/defibrillator_mount/loaded/New() //loaded subtype for mapping use
- ..()
+/obj/machinery/defibrillator_mount/loaded/Initialize(mapload) //loaded subtype for mapping use
+ . = ..()
defib = new/obj/item/defibrillator/loaded(src)
update_icon()
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index c4d5c4984b9..da287020804 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -43,7 +43,7 @@ GLOBAL_LIST_EMPTY(holopads)
use_power = IDLE_POWER_USE
idle_power_usage = 5
active_power_usage = 100
- layer = TURF_LAYER+0.1 //Preventing mice and drones from sneaking under them.
+ layer = HOLOPAD_LAYER //Preventing mice and drones from sneaking under them.
plane = FLOOR_PLANE
max_integrity = 300
armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 0)
diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm
index 7bd89ac6dae..1a5a5ced2ec 100644
--- a/code/game/machinery/magnet.dm
+++ b/code/game/machinery/magnet.dm
@@ -11,7 +11,7 @@
name = "Electromagnetic Generator"
desc = "A device that uses station power to create points of magnetic energy."
level = 1 // underfloor
- layer = 2.5
+ layer = WIRE_LAYER+0.001
anchored = 1
use_power = IDLE_POWER_USE
idle_power_usage = 50
@@ -32,7 +32,8 @@
/obj/machinery/magnetic_module/New()
..()
var/turf/T = loc
- hide(T.intact)
+ if(!T.transparent_floor)
+ hide(T.intact)
center = T
spawn(10) // must wait for map loading to finish
diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm
index 48d87d0f330..f43e0033e23 100644
--- a/code/game/machinery/navbeacon.dm
+++ b/code/game/machinery/navbeacon.dm
@@ -8,7 +8,8 @@
name = "navigation beacon"
desc = "A radio beacon used for bot navigation."
level = 1 // underfloor
- layer = 2.5
+ layer = WIRE_LAYER
+ plane = FLOOR_PLANE
anchored = 1
max_integrity = 500
armor = list(melee = 70, bullet = 70, laser = 70, energy = 70, bomb = 0, bio = 0, rad = 0, fire = 80, acid = 80)
@@ -26,7 +27,8 @@
set_codes()
var/turf/T = loc
- hide(T.intact)
+ if(!T.transparent_floor)
+ hide(T.intact)
if(!codes || !codes.len)
log_runtime(EXCEPTION("Empty codes datum at ([x],[y],[z])"), src, list("codes_txt: '[codes_txt]'"))
if("patrol" in codes)
diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm
index ac61c2cccc9..98eaf1ceb2e 100644
--- a/code/game/machinery/pipe/construction.dm
+++ b/code/game/machinery/pipe/construction.dm
@@ -337,12 +337,18 @@
fixdir()
var/pipe_dir = get_pipe_dir()
+ var/turf/T = get_turf(loc)
for(var/obj/machinery/atmospherics/M in src.loc)
if((M.initialize_directions & pipe_dir) && M.check_connect_types_construction(M,src)) // matches at least one direction on either type of pipe
to_chat(user, "There is already a pipe of the same type at this location.")
return 1
+ if(pipe_type in list(PIPE_SUPPLY_STRAIGHT, PIPE_SUPPLY_BENT, PIPE_SCRUBBERS_STRAIGHT, PIPE_SCRUBBERS_BENT, PIPE_HE_STRAIGHT, PIPE_HE_BENT, PIPE_SUPPLY_MANIFOLD, PIPE_SCRUBBERS_MANIFOLD, PIPE_SUPPLY_MANIFOLD4W, PIPE_SCRUBBERS_MANIFOLD4W, PIPE_UVENT, PIPE_SUPPLY_CAP, PIPE_SCRUBBERS_CAP, PIPE_PASV_VENT, PIPE_DP_VENT, PIPE_PASSIVE_GATE))
+ if(T.transparent_floor) //stops jank with transparent floors and pipes
+ to_chat(user, "You can only fix simple pipes and devices over glass floors!")
+ return 1
+
switch(pipe_type) //What kind of heartless person thought of doing this?
if(PIPE_SIMPLE_STRAIGHT, PIPE_SIMPLE_BENT)
var/obj/machinery/atmospherics/pipe/simple/P = new( src.loc )
diff --git a/code/game/machinery/wishgranter.dm b/code/game/machinery/wishgranter.dm
index d762543f807..23f251efaff 100644
--- a/code/game/machinery/wishgranter.dm
+++ b/code/game/machinery/wishgranter.dm
@@ -42,11 +42,11 @@
user.mind.add_antag_datum(/datum/antagonist/wishgranter)
to_chat(user, "You have a very bad feeling about this.")
-
+
/obj/machinery/wish_granter/super
name = "super wish granter"
var/list/types = list()
-
+
/obj/machinery/wish_granter/super/attack_hand(mob/living/carbon/user)
. = ..()
@@ -60,9 +60,9 @@
if(is_special_character(user) || jobban_isbanned(user, ROLE_TRAITOR) || jobban_isbanned(user, ROLE_SYNDICATE))
to_chat(user, "Something instinctual makes you pull away.")
return TRUE
-
+
to_chat(user, "Your touch makes the Wish Granter stir. Are you really sure you want to do this?")
-
+
for(var/supname in GLOB.all_superheroes)
types += supname
@@ -85,6 +85,6 @@
playsound(src.loc, 'sound/effects/bamf.ogg', 50, 1)
visible_message("The wishgranter fades into mist..")
- add_attack_logs(null, user, "Became [GLOB.all_superheroes[wish]]")
+ add_attack_logs(user, user, "Became [GLOB.all_superheroes[wish]]")
notify_ghosts("[GLOB.all_superheroes[wish]] has appeared in [get_area(user)].", source = user)
qdel(src)
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index 351aba3905b..2b04b105535 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -165,7 +165,7 @@
if(istype(firer, /mob))
add_attack_logs(firer, M, "Mecha-shot with [src]")
else
- add_attack_logs(null, M, "Mecha-shot with [src]")
+ add_attack_logs(src, M, "Mecha-shot with [src] (no firer)")
if(life <= 0)
qdel(src)
return
diff --git a/code/game/objects/effects/spawners/windowspawner.dm b/code/game/objects/effects/spawners/windowspawner.dm
index 1d498888588..20f43f6eea2 100644
--- a/code/game/objects/effects/spawners/windowspawner.dm
+++ b/code/game/objects/effects/spawners/windowspawner.dm
@@ -59,6 +59,12 @@
window_to_spawn_regular = /obj/structure/window/reinforced/tinted
window_to_spawn_full = /obj/structure/window/full/reinforced/tinted
+/obj/effect/spawner/window/reinforced/polarized
+ name = "electrochromic reinforced window spawner"
+ icon_state = "pwindow_spawner"
+ window_to_spawn_regular = /obj/structure/window/reinforced/polarized
+ window_to_spawn_full = /obj/structure/window/full/reinforced/polarized
+
/obj/effect/spawner/window/shuttle
name = "shuttle window spawner"
icon_state = "swindow_spawner"
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 2d014e9e6c2..1a6edd1dd45 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -117,6 +117,12 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
var/in_inventory = FALSE //is this item equipped into an inventory slot or hand of a mob?
var/tip_timer = 0
+ // item hover FX
+ /// Is this item inside a storage object?
+ var/in_storage = FALSE
+ /// Holder var for the item outline filter, null when no outline filter on the item.
+ var/outline_filter
+
/obj/item/New()
..()
for(var/path in actions_types)
@@ -131,6 +137,11 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
if(!move_resist)
determine_move_resist()
+/obj/item/Initialize(mapload)
+ . = ..()
+ if(istype(loc, /obj/item/storage)) //marks all items in storage as being such
+ in_storage = TRUE
+
/obj/item/proc/determine_move_resist()
switch(w_class)
if(WEIGHT_CLASS_TINY)
@@ -406,6 +417,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
if((flags & NODROP) && !(initial(flags) & NODROP)) //Remove NODROP is dropped
flags &= ~NODROP
in_inventory = FALSE
+ remove_outline()
SEND_SIGNAL(src, COMSIG_ITEM_DROPPED,user)
if(!silent)
playsound(src, drop_sound, DROP_SOUND_VOLUME, ignore_walls = FALSE)
@@ -418,10 +430,12 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
// called when this item is removed from a storage item, which is passed on as S. The loc variable is already set to the new destination before this is called.
/obj/item/proc/on_exit_storage(obj/item/storage/S as obj)
+ in_storage = FALSE
return
// called when this item is added into a storage item, which is passed on as S. The loc variable is already set to the storage item.
/obj/item/proc/on_enter_storage(obj/item/storage/S as obj)
+ in_storage = TRUE
return
/**
@@ -691,14 +705,24 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
openToolTip(user, src, params, title = name, content = "[desc]", theme = "")
/obj/item/MouseEntered(location, control, params)
- if(in_inventory)
+ if(in_inventory || in_storage)
var/timedelay = 8
- var/user = usr
+ var/mob/user = usr
tip_timer = addtimer(CALLBACK(src, .proc/openTip, location, control, params, user), timedelay, TIMER_STOPPABLE)
+ if(QDELETED(src))
+ return
+ var/mob/living/L = user
+ if(!(user.client.prefs.toggles2 & PREFTOGGLE_2_SEE_ITEM_OUTLINES))
+ return
+ if(istype(L) && L.incapacitated(ignore_lying = TRUE))
+ apply_outline(L, COLOR_RED_GRAY) //if they're dead or handcuffed, let's show the outline as red to indicate that they can't interact with that right now
+ else
+ apply_outline(L) //if the player's alive and well we send the command with no color set, so it uses the theme's color
/obj/item/MouseExited()
deltimer(tip_timer) //delete any in-progress timer if the mouse is moved off the item before it finishes
closeToolTip(usr)
+ remove_outline()
/obj/item/MouseDrop_T(obj/item/I, mob/user)
if(!user || user.incapacitated(ignore_lying = TRUE) || src == I)
@@ -707,6 +731,41 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
if(loc && I.loc == loc && istype(loc, /obj/item/storage) && loc.Adjacent(user)) // Are we trying to swap two items in the storage?
var/obj/item/storage/S = loc
S.swap_items(src, I, user)
+ remove_outline() //get rid of the hover effect in case the mouse exit isn't called if someone drags and drops an item and somthing goes wrong
+
+/obj/item/proc/apply_outline(mob/user, outline_color = null)
+ if(!(in_inventory || in_storage) || QDELETED(src) || isobserver(user)) //cancel if the item isn't in an inventory, is being deleted, or if the person hovering is a ghost (so that people spectating you don't randomly make your items glow)
+ return
+ var/theme = lowertext(user.client.prefs.UI_style)
+ if(!outline_color) //if we weren't provided with a color, take the theme's color
+ switch(theme) //yeah it kinda has to be this way
+ if("midnight")
+ outline_color = COLOR_THEME_MIDNIGHT
+ if("plasmafire")
+ outline_color = COLOR_THEME_PLASMAFIRE
+ if("retro")
+ outline_color = COLOR_THEME_RETRO //just as garish as the rest of this theme
+ if("slimecore")
+ outline_color = COLOR_THEME_SLIMECORE
+ if("operative")
+ outline_color = COLOR_THEME_OPERATIVE
+ if("clockwork")
+ outline_color = COLOR_THEME_CLOCKWORK //if you want free gbp go fix the fact that clockwork's tooltip css is glass'
+ if("glass")
+ outline_color = COLOR_THEME_GLASS
+ else //this should never happen, hopefully
+ outline_color = COLOR_WHITE
+ if(color)
+ outline_color = COLOR_WHITE //if the item is recolored then the outline will be too, let's make the outline white so it becomes the same color instead of some ugly mix of the theme and the tint
+ if(outline_filter)
+ filters -= outline_filter
+ outline_filter = filter(type = "outline", size = 1, color = outline_color)
+ filters += outline_filter
+
+/obj/item/proc/remove_outline()
+ if(outline_filter)
+ filters -= outline_filter
+ outline_filter = null
// Returns a numeric value for sorting items used as parts in machines, so they can be replaced by the rped
/obj/item/proc/get_part_rating()
diff --git a/code/game/objects/items/devices/megaphone.dm b/code/game/objects/items/devices/megaphone.dm
index ce8c6ca7191..ced5a810bf4 100644
--- a/code/game/objects/items/devices/megaphone.dm
+++ b/code/game/objects/items/devices/megaphone.dm
@@ -13,7 +13,7 @@
var/list/insultmsg = list("FUCK EVERYONE!", "I'M A TATER!", "ALL SECURITY TO SHOOT ME ON SIGHT!", "I HAVE A BOMB!", "CAPTAIN IS A COMDOM!", "FOR THE SYNDICATE!")
/obj/item/megaphone/attack_self(mob/living/user as mob)
- if(user.client && (user.client.prefs.muted & MUTE_IC))
+ if(check_mute(user.ckey, MUTE_IC))
to_chat(src, "You cannot speak in IC (muted).")
return
if(!ishuman(user))
diff --git a/code/game/objects/items/devices/painter/pipe_painter.dm b/code/game/objects/items/devices/painter/pipe_painter.dm
index 4ad7b16b293..17c383809aa 100644
--- a/code/game/objects/items/devices/painter/pipe_painter.dm
+++ b/code/game/objects/items/devices/painter/pipe_painter.dm
@@ -27,8 +27,8 @@
return
var/turf/T = get_turf(P)
- if(P.level < 2 && T.level == 1 && T.intact)
- to_chat(user, "You must remove the plating first.")
+ if(P.level < 2 && T.level == 1 && T.intact && !T.transparent_floor)
+ to_chat(user, "You must remove the flooring first.")
return
P.change_color(GLOB.pipe_colors[paint_setting])
diff --git a/code/game/objects/items/mountable_frames/buttons_switches.dm b/code/game/objects/items/mountable_frames/buttons_switches.dm
index 76e4f0ecfd4..30b77ea2f33 100644
--- a/code/game/objects/items/mountable_frames/buttons_switches.dm
+++ b/code/game/objects/items/mountable_frames/buttons_switches.dm
@@ -21,3 +21,11 @@
/obj/item/mounted/frame/light_switch/do_build(turf/on_wall, mob/user)
new /obj/machinery/light_switch(get_turf(user), get_dir(user, on_wall))
qdel(src)
+
+/obj/item/mounted/frame/light_switch/windowtint
+ name = "window tint control button frame"
+ desc = "Used for repairing or building window tint control buttons"
+
+/obj/item/mounted/frame/light_switch/windowtint/do_build(turf/on_wall, mob/user)
+ new /obj/machinery/button/windowtint(get_turf(user), get_dir(user, on_wall))
+ qdel(src)
diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm
index c736026bca8..adf6fa59869 100644
--- a/code/game/objects/items/stacks/rods.dm
+++ b/code/game/objects/items/stacks/rods.dm
@@ -1,6 +1,17 @@
GLOBAL_LIST_INIT(rod_recipes, list ( \
- new/datum/stack_recipe("grille", /obj/structure/grille, 2, time = 10, one_per_turf = 1, on_floor = 1), \
- new/datum/stack_recipe("table frame", /obj/structure/table_frame, 2, time = 10, one_per_turf = 1, on_floor = 1), \
+ new /datum/stack_recipe("grille", /obj/structure/grille, 2, time = 10, one_per_turf = 1, on_floor = 1), \
+ new /datum/stack_recipe("table frame", /obj/structure/table_frame, 2, time = 10, one_per_turf = 1, on_floor = 1), \
+ null,
+ new /datum/stack_recipe("railing", /obj/structure/railing, 3, time = 10, one_per_turf = 1, on_floor = 1), \
+ new /datum/stack_recipe("railing corner", /obj/structure/railing/corner, 3, time = 10, one_per_turf = 1, on_floor = 1), \
+ null,
+ new /datum/stack_recipe_list("chainlink fence", list( \
+ new /datum/stack_recipe("chainlink fence", /obj/structure/fence, 5, time = 10, one_per_turf = 1, on_floor = 1), \
+ new /datum/stack_recipe("chainlink fence post", /obj/structure/fence/post, 5, time = 10, one_per_turf = 1, on_floor = 1), \
+ new /datum/stack_recipe("chainlink fence corner", /obj/structure/fence/corner, 5, time = 10, one_per_turf = 1, on_floor = 1), \
+ new /datum/stack_recipe("chainlink fence door", /obj/structure/fence/door, 10, time = 10, one_per_turf = 1, on_floor = 1), \
+ new /datum/stack_recipe("chainlink fence end", /obj/structure/fence/end, 3, time = 10, one_per_turf = 1, on_floor = 1), \
+ )), \
))
/obj/item/stack/rods
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index c9901c8b0fe..1f8ae46083e 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -89,7 +89,10 @@ GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \
new/datum/stack_recipe/window("windoor frame", /obj/structure/windoor_assembly, 5, time = 0, on_floor = TRUE, window_checks = TRUE), \
null, \
new/datum/stack_recipe/window("directional reinforced window", /obj/structure/window/reinforced, time = 0, on_floor = TRUE, window_checks = TRUE), \
- new/datum/stack_recipe/window("fulltile reinforced window", /obj/structure/window/full/reinforced, 2, time = 0, on_floor = TRUE, window_checks = TRUE) \
+ new/datum/stack_recipe/window("fulltile reinforced window", /obj/structure/window/full/reinforced, 2, time = 0, on_floor = TRUE, window_checks = TRUE), \
+ null, \
+ new/datum/stack_recipe/window("directional electrochromic window", /obj/structure/window/reinforced/polarized, 2, time = 0, on_floor = TRUE, window_checks = TRUE), \
+ new/datum/stack_recipe/window("fulltile electrochromic window", /obj/structure/window/full/reinforced/polarized, 4, time = 0, on_floor = TRUE, window_checks = TRUE) \
))
/obj/item/stack/sheet/rglass
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index c617860643e..2e5b492684b 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -92,6 +92,7 @@ GLOBAL_LIST_INIT(metal_recipes, list(
null,
new /datum/stack_recipe("mass driver button frame", /obj/item/mounted/frame/driver_button, 1, time = 50, one_per_turf = 0, on_floor = 1),
new /datum/stack_recipe("light switch frame", /obj/item/mounted/frame/light_switch, 1, time = 50, one_per_turf = 0, on_floor = 1),
+ new /datum/stack_recipe("window tint control button frame", /obj/item/mounted/frame/light_switch/windowtint, 1, time = 50, one_per_turf = 0, on_floor = 1),
null,
new /datum/stack_recipe("grenade casing", /obj/item/grenade/chem_grenade),
new /datum/stack_recipe("light fixture frame", /obj/item/mounted/frame/light_fixture, 2),
diff --git a/code/game/objects/items/weapons/defib.dm b/code/game/objects/items/weapons/defib.dm
index e76207ae85c..4cf977dc91c 100644
--- a/code/game/objects/items/weapons/defib.dm
+++ b/code/game/objects/items/weapons/defib.dm
@@ -3,6 +3,7 @@
/obj/item/defibrillator
name = "defibrillator"
desc = "A device that delivers powerful shocks to detachable paddles that resuscitate incapacitated patients."
+ icon = 'icons/obj/defib.dmi'
icon_state = "defibunit"
item_state = "defibunit"
slot_flags = SLOT_BACK
@@ -17,27 +18,27 @@
)
var/paddles_on_defib = TRUE //if the paddles are on the defib (TRUE)
- var/safety = TRUE //if you can zap people with the defibs on harm mode
var/powered = FALSE //if there's a cell in the defib with enough power for a revive, blocks paddles from reviving otherwise
var/obj/item/twohanded/shockpaddles/paddles
var/obj/item/stock_parts/cell/high/cell = null
+ var/safety = TRUE //if you can zap people with the defibs on harm mode
var/combat = FALSE //can we revive through space suits?
+ var/heart_attack = FALSE //can it give instant heart attacks when zapped on harm intent with combat?
+ var/base_icon_state = "defibpaddles"
+ var/obj/item/twohanded/shockpaddles/paddle_type = /obj/item/twohanded/shockpaddles
/obj/item/defibrillator/get_cell()
return cell
-/obj/item/defibrillator/New() //starts without a cell for rnd
- ..()
- paddles = make_paddles()
+/obj/item/defibrillator/Initialize(mapload) //starts without a cell for rnd
+ . = ..()
+ paddles = new paddle_type(src)
update_icon()
- return
-/obj/item/defibrillator/loaded/New() //starts with hicap
- ..()
- paddles = make_paddles()
+/obj/item/defibrillator/loaded/Initialize(mapload) //starts with hicap
+ . = ..()
cell = new(src)
update_icon()
- return
/obj/item/defibrillator/update_icon()
update_power()
@@ -158,9 +159,6 @@
var/datum/action/A = X
A.UpdateButtonIcon()
-/obj/item/defibrillator/proc/make_paddles()
- return new /obj/item/twohanded/shockpaddles(src)
-
/obj/item/defibrillator/equipped(mob/user, slot)
..()
if(slot != slot_back)
@@ -214,38 +212,68 @@
/obj/item/defibrillator/compact
name = "compact defibrillator"
- desc = "A belt-equipped defibrillator that can be rapidly deployed."
+ desc = "A belt-mounted defibrillator that can be rapidly deployed."
icon_state = "defibcompact"
item_state = "defibcompact"
+ sprite_sheets = null //Because Vox had the belt defibrillator sprites in back.dm
w_class = WEIGHT_CLASS_NORMAL
slot_flags = SLOT_BELT
origin_tech = "biotech=5"
+/obj/item/defibrillator/compact/loaded/Initialize(mapload)
+ . = ..()
+ cell = new(src)
+ update_icon()
+
/obj/item/defibrillator/compact/item_action_slot_check(slot, mob/user)
if(slot == slot_belt)
return TRUE
-/obj/item/defibrillator/compact/loaded/New()
- ..()
- paddles = make_paddles()
- cell = new(src)
- update_icon()
- return
-
/obj/item/defibrillator/compact/combat
name = "combat defibrillator"
- desc = "A belt-equipped blood-red defibrillator that can be rapidly deployed. Does not have the restrictions or safeties of conventional defibrillators and can revive through space suits."
+ desc = "A belt-mounted blood-red defibrillator that can be rapidly deployed. Does not have the restrictions or safeties of conventional defibrillators and can revive through space suits."
+ icon_state = "defibcombat"
+ item_state = "defibcombat"
+ paddle_type = /obj/item/twohanded/shockpaddles/syndicate
combat = TRUE
safety = FALSE
-/obj/item/defibrillator/compact/combat/loaded/New()
- ..()
- paddles = make_paddles()
+/obj/item/defibrillator/compact/combat/loaded/Initialize(mapload)
+ . = ..()
cell = new(src)
update_icon()
- return
-/obj/item/defibrillator/compact/combat/attackby(obj/item/W, mob/user, params)
+/obj/item/defibrillator/compact/advanced
+ name = "advanced compact defibrillator"
+ desc = "A belt-mounted state-of-the-art defibrillator that can be rapidly deployed in all environments. Uses an experimental self-charging cell, meaning that it will (probably) never stop working. Can be used to defibrillate through space suits. It is impossible to damage."
+ icon_state = "defibnt"
+ item_state = "defibnt"
+ paddle_type = /obj/item/twohanded/shockpaddles/advanced
+ combat = TRUE
+ safety = TRUE
+ heart_attack = TRUE
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF //Objective item, better not have it destroyed.
+
+ var/next_emp_message //to prevent spam from the emagging message on the advanced defibrillator
+
+/obj/item/defibrillator/compact/advanced/attackby(obj/item/W, mob/user, params)
+ if(W == paddles)
+ paddles.unwield()
+ toggle_paddles()
+ update_icon()
+
+/obj/item/defibrillator/compact/advanced/loaded/Initialize(mapload)
+ . = ..()
+ cell = new /obj/item/stock_parts/cell/bluespace/charging(src)
+ update_icon()
+
+/obj/item/defibrillator/compact/advanced/emp_act(severity)
+ if(world.time > next_emp_message)
+ atom_say("Warning: Electromagnetic pulse detected. Integrated shielding prevented all potential hardware damage.")
+ playsound(src, 'sound/machines/defib_saftyon.ogg', 50)
+ next_emp_message = world.time + 5 SECONDS
+
+/obj/item/defibrillator/compact/advanced/attackby(obj/item/W, mob/user, params)
if(W == paddles)
paddles.unwield()
toggle_paddles()
@@ -257,6 +285,7 @@
/obj/item/twohanded/shockpaddles
name = "defibrillator paddles"
desc = "A pair of plastic-gripped paddles with flat metal surfaces that are used to deliver powerful electric shocks."
+ icon = 'icons/obj/defib.dmi'
icon_state = "defibpaddles"
item_state = "defibpaddles"
force = 0
@@ -268,6 +297,7 @@
var/revivecost = 1000
var/cooldown = FALSE
var/busy = FALSE
+ var/base_icon_state = "defibpaddles"
var/obj/item/defibrillator/defib
/obj/item/twohanded/shockpaddles/New(mainunit)
@@ -280,10 +310,10 @@
return
/obj/item/twohanded/shockpaddles/update_icon()
- icon_state = "defibpaddles[wielded]"
- item_state = "defibpaddles[wielded]"
+ icon_state = "[base_icon_state][wielded]"
+ item_state = "[base_icon_state][wielded]"
if(cooldown)
- icon_state = "defibpaddles[wielded]_cooldown"
+ icon_state = "[base_icon_state][wielded]_cooldown"
/obj/item/twohanded/shockpaddles/suicide_act(mob/user)
user.visible_message("[user] is putting the live paddles on [user.p_their()] chest! It looks like [user.p_theyre()] trying to commit suicide.")
@@ -336,7 +366,7 @@
to_chat(user, "[defib] is recharging.")
return
if(!ishuman(M))
- to_chat(user, "The instructions on [defib] don't mention how to revive that...")
+ to_chat(user, "The instructions on [defib] don't mention how to defibrillate that...")
return
else
if(user.a_intent == INTENT_HARM && !defib.safety)
@@ -347,7 +377,7 @@
H.Weaken(5)
playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 50, 1, -1)
H.emote("gasp")
- if(!H.undergoing_cardiac_arrest() && (prob(10) || defib.combat)) // Your heart explodes.
+ if(!H.undergoing_cardiac_arrest() && (prob(10) || (defib.combat && !defib.heart_attack) || prob(10) && (defib.combat && defib.heart_attack))) // Your heart explodes.
H.set_heartattack(TRUE)
SEND_SIGNAL(H, COMSIG_LIVING_MINOR_SHOCK, 100)
add_attack_logs(user, M, "Stunned with [src]")
@@ -470,7 +500,8 @@
/obj/item/borg_defib
name = "defibrillator paddles"
- desc = "A pair of mounted paddles with flat metal surfaces that are used to deliver powerful electric shocks."
+ desc = "A pair of paddles with flat metal surfaces that are used to deliver powerful electric shocks."
+ icon = 'icons/obj/defib.dmi'
icon_state = "defibpaddles0"
item_state = "defibpaddles0"
force = 0
@@ -590,3 +621,17 @@
playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0)
busy = FALSE
update_icon()
+
+/obj/item/twohanded/shockpaddles/syndicate
+ name = "combat defibrillator paddles"
+ desc = "A pair of high-tech paddles with flat plasteel surfaces to revive deceased operatives (unless they exploded). They possess both the ability to penetrate armor and to deliver powerful or disabling shocks offensively."
+ icon_state = "syndiepaddles0"
+ item_state = "syndiepaddles0"
+ base_icon_state = "syndiepaddles"
+
+/obj/item/twohanded/shockpaddles/advanced
+ name = "advanced defibrillator paddles"
+ desc = "A pair of high-tech paddles with flat plasteel surfaces that are used to deliver powerful electric shocks. They possess the ability to penetrate armor to deliver shock."
+ icon_state = "ntpaddles0"
+ item_state = "ntpaddles0"
+ base_icon_state = "ntpaddles"
diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm
index 46d347e0db7..02b53d2ff56 100644
--- a/code/game/objects/items/weapons/dna_injector.dm
+++ b/code/game/objects/items/weapons/dna_injector.dm
@@ -170,28 +170,6 @@
block = GLOB.hulkblock
..()
-/obj/item/dnainjector/xraymut
- name = "DNA-Injector (Xray)"
- desc = "Finally you can see what the Captain does."
- datatype = DNA2_BUF_SE
- value = 0xFFF
- forcedmutation = TRUE
-
-/obj/item/dnainjector/xraymut/Initialize()
- block = GLOB.xrayblock
- ..()
-
-/obj/item/dnainjector/antixray
- name = "DNA-Injector (Anti-Xray)"
- desc = "It will make you see harder."
- datatype = DNA2_BUF_SE
- value = 0x001
- forcedmutation = TRUE
-
-/obj/item/dnainjector/antixray/Initialize()
- block = GLOB.xrayblock
- ..()
-
/obj/item/dnainjector/firemut
name = "DNA-Injector (Fire)"
desc = "Gives you fire."
diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm
index 9fb4684f3ff..972ab8ed7bd 100644
--- a/code/game/objects/items/weapons/storage/belt.dm
+++ b/code/game/objects/items/weapons/storage/belt.dm
@@ -716,7 +716,6 @@
new /obj/item/stack/cable_coil(src)
new /obj/item/restraints/handcuffs(src)
- new /obj/item/dnainjector/xraymut(src)
new /obj/item/dnainjector/firemut(src)
new /obj/item/dnainjector/telemut(src)
new /obj/item/dnainjector/hulkmut(src)
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index b796740a60b..7330819e7d0 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -90,7 +90,7 @@
/obj/blob_act(obj/structure/blob/B)
if(isturf(loc))
var/turf/T = loc
- if(T.intact && level == 1) //the blob doesn't destroy thing below the floor
+ if((T.intact && level == 1) || T.transparent_floor) //the blob doesn't destroy thing below the floor
return
take_damage(400, BRUTE, "melee", 0, get_dir(src, B))
@@ -199,7 +199,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
/obj/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE)
if(isturf(loc))
var/turf/T = loc
- if(T.intact && level == 1) //fire can't damage things hidden below the floor.
+ if((T.intact && level == 1) || T.transparent_floor) //fire can't damage things hidden below the floor.
return
..()
if(exposed_temperature && !(resistance_flags & FIRE_PROOF))
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index 4d58c90a095..6d1b884177d 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -59,30 +59,31 @@
/obj/structure/proc/do_climb(mob/living/user)
if(!can_touch(user) || !climbable)
- return
+ return FALSE
var/blocking_object = density_check()
if(blocking_object)
to_chat(user, "You cannot climb [src], as it is blocked by \a [blocking_object]!")
- return
+ return FALSE
var/turf/T = src.loc
- if(!T || !istype(T)) return
+ if(!T || !istype(T)) return FALSE
usr.visible_message("[user] starts climbing onto \the [src]!")
climber = user
if(!do_after(user, 50, target = src))
climber = null
- return
+ return FALSE
if(!can_touch(user) || !climbable)
climber = null
- return
+ return FALSE
usr.loc = get_turf(src)
if(get_turf(user) == get_turf(src))
usr.visible_message("[user] climbs onto \the [src]!")
climber = null
+ return TRUE
/obj/structure/proc/structure_shaken()
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
index a56c63937e3..555124cbb47 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
@@ -182,7 +182,7 @@
new /obj/item/clothing/shoes/brown (src)
new /obj/item/radio/headset/heads/cmo(src)
new /obj/item/clothing/gloves/color/latex/nitrile(src)
- new /obj/item/defibrillator/compact/loaded(src)
+ new /obj/item/defibrillator/compact/advanced/loaded(src)
new /obj/item/handheld_defibrillator(src)
new /obj/item/storage/belt/medical(src)
new /obj/item/flash(src)
diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm
new file mode 100644
index 00000000000..21ad71f329f
--- /dev/null
+++ b/code/game/objects/structures/fence.dm
@@ -0,0 +1,216 @@
+//Chain link fences
+//Sprites ported from /VG/
+
+#define CUT_TIME 100
+#define CLIMB_TIME 150
+
+#define NO_HOLE 0 //section is intact
+#define MEDIUM_HOLE 1 //medium hole in the section - can climb through
+#define LARGE_HOLE 2 //large hole in the section - can walk through
+#define MAX_HOLE_SIZE LARGE_HOLE
+#define HOLE_REPAIR (hole_size * 2) //How many rods to fix these sections
+
+/obj/structure/fence
+ name = "fence"
+ desc = "A chain link fence. Not as effective as a wall, but generally it keeps people out."
+ density = TRUE
+ anchored = TRUE
+
+ icon = 'icons/obj/fence.dmi'
+ icon_state = "straight"
+
+ var/cuttable = TRUE
+ var/hole_size = NO_HOLE
+ var/invulnerable = FALSE
+ var/shock_cooldown = FALSE
+
+/obj/structure/fence/Initialize()
+ . = ..()
+ update_cut_status()
+
+/obj/structure/fence/examine(mob/user)
+ . = ..()
+ switch(hole_size)
+ if(MEDIUM_HOLE)
+ . += "There is a large hole in \the [src]."
+ if(LARGE_HOLE)
+ . += "\The [src] has been completely cut through."
+
+/obj/structure/fence/end
+ icon_state = "end"
+ cuttable = FALSE
+
+/obj/structure/fence/corner
+ icon_state = "corner"
+ cuttable = FALSE
+
+/obj/structure/fence/post
+ icon_state = "post"
+ cuttable = FALSE
+
+/obj/structure/fence/cut/medium
+ icon_state = "straight_cut2"
+ hole_size = MEDIUM_HOLE
+ climbable = TRUE
+
+/obj/structure/fence/cut/large
+ icon_state = "straight_cut3"
+ hole_size = LARGE_HOLE
+
+/obj/structure/fence/CanPass(atom/movable/mover, turf/target)
+ if(istype(mover) && mover.checkpass(PASSFENCE))
+ return TRUE
+ if(istype(mover, /obj/item/projectile))
+ return TRUE
+ if(!density)
+ return TRUE
+ return FALSE
+
+/*
+ Shock user with probability prb (if all connections & power are working)
+ Returns TRUE if shocked, FALSE otherwise
+ Totally not stolen from code\game\objects\structures\grille.dm
+*/
+/obj/structure/fence/proc/shock(mob/user, prb)
+ if(!prob(prb))
+ return FALSE
+ if(!in_range(src, user)) //To prevent TK and mech users from getting shocked
+ return FALSE
+ var/turf/T = get_turf(src)
+ var/obj/structure/cable/C = T.get_cable_node()
+ if(C)
+ if(electrocute_mob(user, C, src, 1, TRUE))
+ do_sparks(3, 1, src)
+ return TRUE
+ return FALSE
+
+/obj/structure/fence/wirecutter_act(mob/living/user, obj/item/W)
+ . = TRUE
+ if(shock(user, 100))
+ return
+ if(!cuttable)
+ to_chat(user, "This section of the fence can't be cut!")
+ return
+ if(invulnerable)
+ to_chat(user, "This fence is too strong to cut through!")
+ return
+ var/current_stage = hole_size
+ user.visible_message("\The [user] starts cutting through \the [src] with \the [W].",\
+ "You start cutting through \the [src] with \the [W].")
+ if(W.use_tool(src, user, CUT_TIME * W.toolspeed, volume = W.tool_volume))
+ if(current_stage == hole_size)
+ switch(hole_size)
+ if(NO_HOLE)
+ visible_message("\The [user] cuts into \the [src] some more.")
+ to_chat(user, "You could probably fit yourself through that hole now. Although climbing through would be much faster if you made it even bigger.")
+ hole_size = MEDIUM_HOLE
+ if(MEDIUM_HOLE)
+ visible_message("\The [user] completely cuts through \the [src].")
+ to_chat(user, "The hole in \the [src] is now big enough to walk through.")
+ hole_size = LARGE_HOLE
+ if(LARGE_HOLE)
+ visible_message("\The [user] completely dismantles \the [src].")
+ to_chat(user, "You completely take apart \the [src].")
+ qdel(src)
+ return
+ update_cut_status()
+
+/obj/structure/fence/attackby(obj/item/C, mob/user)
+ if(shock(user, 90))
+ return
+ if(istype(C, /obj/item/stack/rods))
+ if(hole_size == NO_HOLE)
+ return
+ var/obj/item/stack/rods/R = C
+ if(R.get_amount() < HOLE_REPAIR)
+ to_chat(user, "You need [HOLE_REPAIR] rods to fix this fence!")
+ return
+ to_chat(user, "You begin repairing the fence...")
+ if(do_after(user, 3 SECONDS * C.toolspeed, target = src) && hole_size != NO_HOLE && R.use(HOLE_REPAIR))
+ playsound(src, C.usesound, 80, 1)
+ hole_size = NO_HOLE
+ obj_integrity = max_integrity
+ to_chat(user, "You repair the fence.")
+ update_cut_status()
+ return
+ . = ..()
+
+/obj/structure/fence/Bumped(atom/user)
+ if(!ismob(user))
+ return
+ if(shock_cooldown)
+ return
+ shock(user, 70)
+ shock_cooldown = TRUE // We do not want bump shock spam!
+ addtimer(CALLBACK(src, .proc/shock_cooldown), 1 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
+
+/obj/structure/fence/proc/shock_cooldown()
+ shock_cooldown = FALSE
+
+/obj/structure/fence/attack_animal(mob/user)
+ . = ..()
+ if(. && !QDELETED(src) && !shock(user, 70))
+ take_damage(rand(5,10), BRUTE, "melee", 1)
+
+/obj/structure/fence/proc/update_cut_status()
+ if(!cuttable)
+ return
+ var/new_density = TRUE
+ switch(hole_size)
+ if(NO_HOLE)
+ icon_state = initial(icon_state)
+ climbable = FALSE
+ if(MEDIUM_HOLE)
+ icon_state = "straight_cut2"
+ climbable = TRUE
+ if(LARGE_HOLE)
+ icon_state = "straight_cut3"
+ new_density = FALSE
+ climbable = FALSE
+ set_density(new_density)
+
+//FENCE DOORS
+
+/obj/structure/fence/door
+ name = "fence door"
+ desc = "Not very useful without a real lock."
+ icon_state = "door_closed"
+ cuttable = FALSE
+ var/open = FALSE
+
+/obj/structure/fence/door/Initialize()
+ . = ..()
+ update_door_status()
+
+/obj/structure/fence/door/opened
+ icon_state = "door_opened"
+ open = TRUE
+ density = TRUE
+
+/obj/structure/fence/door/attack_hand(mob/user, list/modifiers)
+ shock(user, 70)
+ if(can_open(user))
+ toggle(user)
+ return TRUE
+
+/obj/structure/fence/door/proc/toggle(mob/user)
+ open = !open
+ visible_message("\The [user] [open ? "opens" : "closes"] \the [src].")
+ update_door_status()
+ playsound(src, 'sound/machines/door_open.ogg', 100, TRUE)
+
+/obj/structure/fence/door/proc/update_door_status()
+ set_density(!open)
+ icon_state = open ? "door_opened" : "door_closed"
+
+/obj/structure/fence/door/proc/can_open(mob/user)
+ return TRUE
+
+#undef CUT_TIME
+#undef CLIMB_TIME
+
+#undef NO_HOLE
+#undef MEDIUM_HOLE
+#undef LARGE_HOLE
+#undef MAX_HOLE_SIZE
+#undef HOLE_REPAIR
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
new file mode 100644
index 00000000000..2dc7a509206
--- /dev/null
+++ b/code/game/objects/structures/railings.dm
@@ -0,0 +1,136 @@
+/obj/structure/railing
+ name = "railing"
+ desc = "Basic railing meant to protect idiots like you from falling."
+ icon = 'icons/obj/fence.dmi'
+ icon_state = "railing"
+ density = TRUE
+ anchored = TRUE
+ pass_flags = LETPASSTHROW
+ climbable = TRUE
+ layer = ABOVE_MOB_LAYER
+ var/currently_climbed = FALSE
+
+/obj/structure/railing/corner //aesthetic corner sharp edges hurt oof ouch
+ icon_state = "railing_corner"
+ density = FALSE
+ climbable = FALSE
+
+/obj/structure/railing/attackby(obj/item/I, mob/living/user, params)
+ ..()
+ add_fingerprint(user)
+
+/obj/structure/railing/welder_act(mob/living/user, obj/item/I)
+ if(user.intent != INTENT_HELP)
+ return
+ if(obj_integrity >= max_integrity)
+ to_chat(user, "[src] is already in good condition!")
+ return
+ if(!I.tool_start_check(user, amount = 0))
+ return
+ to_chat(user, "You begin repairing [src]...")
+ if(I.use_tool(src, user, 40, volume = 50))
+ obj_integrity = max_integrity
+ to_chat(user, "You repair [src].")
+
+/obj/structure/railing/wirecutter_act(mob/living/user, obj/item/I)
+ if(anchored)
+ return
+ to_chat(user, "You cut apart the railing.")
+ I.play_tool_sound(src, 100)
+ deconstruct()
+ return TRUE
+
+/obj/structure/railing/deconstruct(disassembled)
+ if(!(flags & NODECONSTRUCT))
+ var/obj/item/stack/rods/rod = new /obj/item/stack/rods(drop_location(), 3)
+ transfer_fingerprints_to(rod)
+ return ..()
+
+///Implements behaviour that makes it possible to unanchor the railing.
+/obj/structure/railing/wrench_act(mob/living/user, obj/item/I)
+ if(flags & NODECONSTRUCT)
+ return
+ to_chat(user, "You begin to [anchored ? "unfasten the railing from":"fasten the railing to"] the floor...")
+ if(I.use_tool(src, user, volume = 75, extra_checks = CALLBACK(src, .proc/check_anchored, anchored)))
+ anchored = !anchored
+ to_chat(user, "You [anchored ? "fasten the railing to":"unfasten the railing from"] the floor.")
+ return TRUE
+
+/obj/structure/railing/corner/CanPass()
+ return TRUE
+
+/obj/structure/railing/corner/CheckExit()
+ return TRUE
+
+/obj/structure/railing/CanPass(atom/movable/mover, turf/target)
+ if(istype(mover) && mover.checkpass(PASSFENCE))
+ return TRUE
+ if(istype(mover, /obj/item/projectile))
+ return TRUE
+ if(ismob(mover))
+ var/mob/M = mover
+ if(M.flying)
+ return TRUE
+ if(mover.throwing)
+ return TRUE
+ if(get_dir(loc, target) != dir)
+ return density
+ return FALSE
+
+/obj/structure/railing/CheckExit(atom/movable/O, target)
+ var/mob/living/M = O
+ if(istype(O) && O.checkpass(PASSFENCE))
+ return TRUE
+ if(istype(O, /obj/item/projectile))
+ return TRUE
+ if(ismob(O))
+ if(M.flying || M.floating)
+ return TRUE
+ if(O.throwing)
+ return TRUE
+ if(O.move_force >= MOVE_FORCE_EXTREMELY_STRONG)
+ return TRUE
+ if(currently_climbed)
+ return TRUE
+ if(get_dir(O.loc, target) == dir)
+ return FALSE
+ return TRUE
+
+/obj/structure/railing/do_climb(mob/living/user)
+ var/initial_mob_loc = get_turf(user)
+ . = ..()
+ if(.)
+ currently_climbed = TRUE
+ if(initial_mob_loc != get_turf(src)) // If we are on the railing, we want to move in the same dir as the railing. Otherwise we get put on the railing
+ currently_climbed = FALSE
+ return
+ user.Move(get_step(user, dir), TRUE)
+ currently_climbed = FALSE
+
+/obj/structure/railing/proc/can_be_rotated(mob/user)
+ if(anchored)
+ to_chat(user, "[src] cannot be rotated while it is fastened to the floor!")
+ return FALSE
+
+ var/target_dir = turn(dir, -90)
+
+ if(!valid_window_location(loc, target_dir)) //Expanded to include rails, as well!
+ to_chat(user, "[src] cannot be rotated in that direction!")
+ return FALSE
+ return TRUE
+
+/obj/structure/railing/proc/check_anchored(checked_anchored)
+ if(anchored == checked_anchored)
+ return TRUE
+
+/obj/structure/railing/proc/after_rotation(mob/user)
+ add_fingerprint(user)
+
+/obj/structure/railing/AltClick(mob/user)
+ if(user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!Adjacent(user))
+ return
+ if(can_be_rotated(user))
+ setDir(turn(dir, 90))
diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm
index 4266d6ef254..c373a28936c 100644
--- a/code/game/objects/structures/safe.dm
+++ b/code/game/objects/structures/safe.dm
@@ -368,7 +368,8 @@ GLOBAL_LIST_EMPTY(safes)
/obj/structure/safe/floor/Initialize()
. = ..()
var/turf/T = loc
- hide(T.intact)
+ if(!T.transparent_floor)
+ hide(T.intact)
/obj/structure/safe/floor/hide(intact)
invisibility = intact ? INVISIBILITY_MAXIMUM : 0
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index a2d13ad8afb..7872c395d1e 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -27,6 +27,8 @@
var/real_explosion_block //ignore this, just use explosion_block
var/breaksound = "shatter"
var/hitsound = 'sound/effects/Glasshit.ogg'
+ /// Used to restore colours from polarised glass
+ var/old_color
/obj/structure/window/examine(mob/user)
. = ..()
@@ -69,6 +71,17 @@
air_update_turf(TRUE)
+/obj/structure/window/proc/toggle_polarization()
+ if(opacity)
+ if(!old_color)
+ old_color = "#FFFFFF"
+ animate(src, color = old_color, time = 0.5 SECONDS)
+ set_opacity(FALSE)
+ else
+ old_color = color
+ animate(src, color = "#222222", time = 0.5 SECONDS)
+ set_opacity(TRUE)
+
/obj/structure/window/narsie_act()
color = NARSIE_WINDOW_COLOUR
@@ -497,14 +510,6 @@
desc = "Adjusts its tint with voltage. Might take a few good hits to shatter it."
var/id
-/obj/structure/window/reinforced/polarized/proc/toggle()
- if(opacity)
- animate(src, color="#FFFFFF", time=5)
- set_opacity(0)
- else
- animate(src, color="#222222", time=5)
- set_opacity(1)
-
/obj/machinery/button/windowtint
name = "window tint control"
icon = 'icons/obj/power.dmi'
@@ -514,12 +519,35 @@
var/id = 0
var/active = 0
+/obj/machinery/button/windowtint/Initialize(mapload, w_dir = null)
+ . = ..()
+ switch(w_dir)
+ if(NORTH)
+ pixel_y = 25
+ if(SOUTH)
+ pixel_y = -25
+ if(EAST)
+ pixel_x = 25
+ if(WEST)
+ pixel_x = -25
+
/obj/machinery/button/windowtint/attack_hand(mob/user)
if(..())
- return 1
+ return TRUE
toggle_tint()
+/obj/machinery/button/windowtint/wrench_act(mob/user, obj/item/I)
+ . = TRUE
+ if(!I.tool_use_check(user, 0))
+ return
+ user.visible_message("[user] starts unwrenching [src] from the wall...", "You are unwrenching [src] from the wall...", "You hear ratcheting.")
+ if(!I.use_tool(src, user, 50, volume = I.tool_volume))
+ return
+ WRENCH_UNANCHOR_WALL_MESSAGE
+ new /obj/item/mounted/frame/light_switch/windowtint(get_turf(src))
+ qdel(src)
+
/obj/machinery/button/windowtint/proc/toggle_tint()
use_power(5)
@@ -528,9 +556,11 @@
for(var/obj/structure/window/reinforced/polarized/W in range(src,range))
if(W.id == src.id || !W.id)
- spawn(0)
- W.toggle()
- return
+ W.toggle_polarization()
+
+ for(var/obj/structure/window/full/reinforced/polarized/W in range(src, range))
+ if(W.id == id || !W.id)
+ W.toggle_polarization()
/obj/machinery/button/windowtint/power_change()
..()
@@ -589,7 +619,7 @@
icon_state = "window"
max_integrity = 50
smooth = SMOOTH_TRUE
- canSmoothWith = list(/obj/structure/window/full/basic, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/plasmabasic, /obj/structure/window/full/plasmareinforced, /turf/simulated/wall/indestructible/fakeglass)
+ canSmoothWith = list(/obj/structure/window/full/basic, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/polarized, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/plasmabasic, /obj/structure/window/full/plasmareinforced, /turf/simulated/wall/indestructible/fakeglass)
/obj/structure/window/full/plasmabasic
name = "plasma window"
@@ -602,7 +632,7 @@
heat_resistance = 32000
max_integrity = 300
smooth = SMOOTH_TRUE
- canSmoothWith = list(/obj/structure/window/full/basic, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/plasmabasic, /obj/structure/window/full/plasmareinforced, /turf/simulated/wall/indestructible/fakeglass)
+ canSmoothWith = list(/obj/structure/window/full/basic, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/polarized, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/plasmabasic, /obj/structure/window/full/plasmareinforced, /turf/simulated/wall/indestructible/fakeglass)
explosion_block = 1
armor = list("melee" = 75, "bullet" = 5, "laser" = 0, "energy" = 0, "bomb" = 45, "bio" = 100, "rad" = 100, "fire" = 99, "acid" = 100)
rad_insulation = RAD_NO_INSULATION
@@ -616,7 +646,7 @@
shardtype = /obj/item/shard/plasma
glass_type = /obj/item/stack/sheet/plasmarglass
smooth = SMOOTH_TRUE
- canSmoothWith = list(/obj/structure/window/full/basic, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/plasmabasic, /obj/structure/window/full/plasmareinforced, /turf/simulated/wall/indestructible/fakeglass)
+ canSmoothWith = list(/obj/structure/window/full/basic, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/polarized, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/plasmabasic, /obj/structure/window/full/plasmareinforced, /turf/simulated/wall/indestructible/fakeglass)
reinf = TRUE
max_integrity = 1000
explosion_block = 2
@@ -632,7 +662,7 @@
icon = 'icons/obj/smooth_structures/reinforced_window.dmi'
icon_state = "r_window"
smooth = SMOOTH_TRUE
- canSmoothWith = list(/obj/structure/window/full/basic, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/plasmabasic, /obj/structure/window/full/plasmareinforced, /turf/simulated/wall/indestructible/fakeglass)
+ canSmoothWith = list(/obj/structure/window/full/basic, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/polarized, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/plasmabasic, /obj/structure/window/full/plasmareinforced, /turf/simulated/wall/indestructible/fakeglass)
max_integrity = 100
reinf = TRUE
heat_resistance = 1600
@@ -641,6 +671,11 @@
explosion_block = 1
glass_type = /obj/item/stack/sheet/rglass
+/obj/structure/window/full/reinforced/polarized
+ name = "electrochromic window"
+ desc = "Adjusts its tint with voltage. Might take a few good hits to shatter it."
+ var/id
+
/obj/structure/window/full/reinforced/tinted
name = "tinted window"
desc = "It looks rather strong and opaque. Might take a few good hits to shatter it."
diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm
index 24fcba648f2..8f8e94362b7 100644
--- a/code/game/turfs/simulated/floor.dm
+++ b/code/game/turfs/simulated/floor.dm
@@ -27,6 +27,7 @@ GLOBAL_LIST_INIT(icons_to_ignore_at_floor_init, list("damaged1","damaged2","dama
var/list/broken_states = list("damaged1", "damaged2", "damaged3", "damaged4", "damaged5")
var/list/burnt_states = list("floorscorched1", "floorscorched2")
var/list/prying_tool_list = list(TOOL_CROWBAR) //What tool/s can we use to pry up the tile?
+ var/keep_dir = TRUE //When false, resets dir to default on changeturf()
var/footstep = FOOTSTEP_FLOOR
var/barefootstep = FOOTSTEP_HARD_BAREFOOT
@@ -124,11 +125,18 @@ GLOBAL_LIST_INIT(icons_to_ignore_at_floor_init, list("damaged1","damaged2","dama
var/turf/simulated/floor/W = ..()
+ var/obj/machinery/atmospherics/R
+
if(keep_icon)
W.icon_regular_floor = old_icon
W.icon_plating = old_plating
+ if(W.keep_dir)
W.dir = old_dir
-
+ if(W.transparent_floor)
+ for(R in W)
+ R.update_icon()
+ for(R in W)
+ R.update_underlays()
W.update_icon()
return W
diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm
index d0805cf5099..ad26fa8919e 100644
--- a/code/game/turfs/simulated/floor/plating.dm
+++ b/code/game/turfs/simulated/floor/plating.dm
@@ -70,6 +70,35 @@
to_chat(user, "This section is too damaged to support a tile! Use a welder to fix the damage.")
return TRUE
+ else if(is_glass_sheet(C))
+ if(broken || burnt)
+ to_chat(user, "Repair the plating first!")
+ return TRUE
+ var/obj/item/stack/sheet/R = C
+ if(R.get_amount() < 2)
+ to_chat(user, "You need two sheets to build a [C.name] floor!")
+ return TRUE
+ to_chat(user, "You begin swapping the plating for [C]...")
+ if(do_after(user, 3 SECONDS * C.toolspeed, target = src))
+ if(R.get_amount() >= 2 && !transparent_floor)
+ if(istype(C, /obj/item/stack/sheet/plasmaglass)) //So, what type of glass floor do we want today?
+ ChangeTurf(/turf/simulated/floor/transparent/glass/plasma)
+ else if(istype(C, /obj/item/stack/sheet/plasmarglass))
+ ChangeTurf(/turf/simulated/floor/transparent/glass/reinforced/plasma)
+ else if(istype(C, /obj/item/stack/sheet/glass))
+ ChangeTurf(/turf/simulated/floor/transparent/glass)
+ else if(istype(C, /obj/item/stack/sheet/rglass))
+ ChangeTurf(/turf/simulated/floor/transparent/glass/reinforced)
+ else if(istype(C, /obj/item/stack/sheet/titaniumglass))
+ ChangeTurf(/turf/simulated/floor/transparent/glass/titanium)
+ else if(istype(C, /obj/item/stack/sheet/plastitaniumglass))
+ ChangeTurf(/turf/simulated/floor/transparent/glass/titanium/plasma)
+ playsound(src, C.usesound, 80, TRUE)
+ R.use(2)
+ to_chat(user, "You swap the plating for [C].")
+ new /obj/item/stack/sheet/metal(src, 2)
+ return TRUE
+
/turf/simulated/floor/plating/screwdriver_act(mob/user, obj/item/I)
. = TRUE
if(!I.tool_use_check(user, 0))
diff --git a/code/game/turfs/simulated/floor/transparent.dm b/code/game/turfs/simulated/floor/transparent.dm
new file mode 100644
index 00000000000..81030c4d294
--- /dev/null
+++ b/code/game/turfs/simulated/floor/transparent.dm
@@ -0,0 +1,112 @@
+/turf/simulated/floor/transparent/glass
+ name = "glass floor"
+ desc = "Don't jump on it... Or do, I'm not your mom."
+ icon = 'icons/turf/floors/glass.dmi'
+ icon_state = "unsmooth"
+ baseturf = /turf/space
+ smooth = SMOOTH_TRUE
+ canSmoothWith = list(/turf/simulated/floor/transparent/glass, /turf/simulated/floor/transparent/glass/reinforced, /turf/simulated/floor/transparent/glass/plasma, /turf/simulated/floor/transparent/glass/reinforced/plasma)
+ footstep = FOOTSTEP_GLASS
+ barefootstep = FOOTSTEP_GLASS_BAREFOOT
+ clawfootstep = FOOTSTEP_GLASS_BAREFOOT
+ heavyfootstep = FOOTSTEP_GLASS_BAREFOOT
+ light_power = 0.25
+ light_range = 2
+ layer = TRANSPARENT_TURF_LAYER
+ keep_dir = FALSE
+ intact = FALSE
+ transparent_floor = TRUE
+ heat_capacity = 800
+
+/turf/simulated/floor/transparent/glass/Initialize(mapload)
+ . = ..()
+ var/image/I = image('icons/turf/space.dmi', src, SPACE_ICON_STATE)
+ I.plane = PLANE_SPACE
+ underlays += I
+ dir = SOUTH //dirs that are not 2/south cause smoothing jank
+ icon_state = "" //Prevents default icon appearing behind the glass
+
+/turf/simulated/floor/transparent/glass/welder_act(mob/user, obj/item/I)
+ if(!broken && !burnt)
+ return
+ if(!I.tool_use_check(user, 0))
+ return
+ if(I.use_tool(src, user, volume = I.tool_volume))
+ to_chat(user, "You fix some cracks in the glass.")
+ overlays -= current_overlay
+ current_overlay = null
+ burnt = FALSE
+ broken = FALSE
+ update_icon()
+
+/turf/simulated/floor/transparent/glass/crowbar_act(mob/user, obj/item/I)
+ if(!I || !user)
+ return
+ var/obj/item/stack/R = user.get_inactive_hand()
+ if(istype(R, /obj/item/stack/sheet/metal))
+ if(R.get_amount() < 2) //not enough metal in the stack
+ to_chat(user, "You also need to hold two sheets of metal to dismantle [src]!")
+ return
+ else
+ to_chat(user, "You begin replacing [src]...")
+ playsound(src, I.usesound, 80, TRUE)
+ if(do_after(user, 3 SECONDS * I.toolspeed, target = src))
+ if(R.get_amount() < 2 || !transparent_floor)
+ return
+ else //not holding metal at all
+ to_chat(user, "You also need to hold two sheets of metal to dismantle \the [src]!")
+ return
+ switch(type) //What material is returned? Depends on the turf
+ if(/turf/simulated/floor/transparent/glass/reinforced)
+ new /obj/item/stack/sheet/rglass(src, 2)
+ if(/turf/simulated/floor/transparent/glass)
+ new /obj/item/stack/sheet/glass(src, 2)
+ if(/turf/simulated/floor/transparent/glass/plasma)
+ new /obj/item/stack/sheet/plasmaglass(src, 2)
+ if(/turf/simulated/floor/transparent/glass/reinforced/plasma)
+ new /obj/item/stack/sheet/plasmarglass(src, 2)
+ if(/turf/simulated/floor/transparent/glass/titanium)
+ new /obj/item/stack/sheet/titaniumglass(src, 2)
+ if(/turf/simulated/floor/transparent/glass/titanium/plasma)
+ new /obj/item/stack/sheet/plastitaniumglass(src, 2)
+ R.use(2)
+ playsound(src, 'sound/items/deconstruct.ogg', 80, TRUE)
+ ChangeTurf(/turf/simulated/floor/plating)
+
+
+/turf/simulated/floor/transparent/glass/reinforced
+ name = "reinforced glass floor"
+ desc = "Jump on it, it can cope. Promise..."
+ icon = 'icons/turf/floors/reinf_glass.dmi'
+ thermal_conductivity = 0.035
+ heat_capacity = 1600
+
+/turf/simulated/floor/transparent/glass/reinforced/acid_act(acidpwr, acid_volume)
+ acidpwr = min(acidpwr, 50)
+ . = ..()
+
+/turf/simulated/floor/transparent/glass/plasma
+ name = "plasma glass floor"
+ desc = "Wait, was space always that color?"
+ icon = 'icons/turf/floors/plasmaglass.dmi'
+ thermal_conductivity = 0.030
+ heat_capacity = 32000
+
+/turf/simulated/floor/transparent/glass/reinforced/plasma
+ name = "reinforced plasma glass floor"
+ desc = "For when you REALLY don't want your floor choice to suffocate everyone."
+ icon = 'icons/turf/floors/reinf_plasmaglass.dmi'
+ thermal_conductivity = 0.025
+ heat_capacity = 325000
+
+/turf/simulated/floor/transparent/glass/titanium
+ name = "titanium glass floor"
+ desc = "Stylish AND strong!"
+ icon = 'icons/turf/floors/titaniumglass.dmi'
+ canSmoothWith = list(/turf/simulated/floor/transparent/glass/titanium, /turf/simulated/floor/transparent/glass/titanium/plasma)
+ thermal_conductivity = 0.025
+ heat_capacity = 1600
+
+/turf/simulated/floor/transparent/glass/titanium/plasma
+ name = "plastitanium glass floor"
+ icon = 'icons/turf/floors/plastitaniumglass.dmi'
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 938e2f8a851..8c64a8456bf 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -6,6 +6,7 @@
var/intact = TRUE
var/turf/baseturf = /turf/space
var/slowdown = 0 //negative for faster, positive for slower
+ var/transparent_floor = FALSE //used to check if pipes should be visible under the turf or not
///Properties for open tiles (/floor)
/// All the gas vars, on the turf, are meant to be utilized for initializing a gas datum and setting its first gas values; the turf vars are never further modified at runtime; it is never directly used for calculations by the atmospherics system.
diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm
index 458f06e9c19..162e1ebd26e 100644
--- a/code/game/verbs/ooc.dm
+++ b/code/game/verbs/ooc.dm
@@ -25,7 +25,7 @@ GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00")
if(!GLOB.dooc_enabled && (mob.stat == DEAD))
to_chat(usr, "OOC for dead mobs has been turned off.")
return
- if(prefs.muted & MUTE_OOC)
+ if(check_mute(ckey, MUTE_OOC))
to_chat(src, "You cannot use OOC (muted).")
return
@@ -181,7 +181,7 @@ GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00")
if(!GLOB.dooc_enabled && (mob.stat == DEAD))
to_chat(usr, "LOOC for dead mobs has been turned off.")
return
- if(prefs.muted & MUTE_OOC)
+ if(check_mute(ckey, MUTE_OOC))
to_chat(src, "You cannot use LOOC (muted).")
return
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 85d4be92516..5e1d4036171 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -122,7 +122,6 @@ GLOBAL_VAR_INIT(nologevent, 0)
if(M.ckey)
body += "Kick | "
- body += "Warn | "
body += "Ban | "
body += "Jobban | "
body += "Appearance Ban | "
@@ -141,14 +140,13 @@ GLOBAL_VAR_INIT(nologevent, 0)
body += "\ Send back to Lobby | "
body += "\ Erase Flavor Text | "
body += "\ Use Random Name | "
- var/muted = M.client.prefs.muted
body += {"
Mute:
- \[IC |
- OOC |
- PRAY |
- ADMINHELP |
- DEADCHAT\]
- (toggle all)
+ \[IC |
+ OOC |
+ PRAY |
+ ADMINHELP |
+ DEADCHAT]
+ (toggle all)
"}
var/jumptoeye = ""
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index de44ccd4788..5ddf090f101 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -492,51 +492,6 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
log_admin("[key_name(usr)] has turned BB mode [holder.fakekey ? "ON" : "OFF"]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Big Brother Mode")
-#define MAX_WARNS 3
-#define AUTOBANTIME 10
-
-/client/proc/warn(warned_ckey)
- if(!check_rights(R_ADMIN))
- return
-
- if(!warned_ckey || !istext(warned_ckey)) return
- if(warned_ckey in GLOB.admin_datums)
- to_chat(usr, "Error: warn(): You can't warn admins.")
- return
-
- var/datum/preferences/D
- var/client/C = GLOB.directory[warned_ckey]
- if(C) D = C.prefs
- else D = GLOB.preferences_datums[warned_ckey]
-
- if(!D)
- to_chat(src, "Error: warn(): No such ckey found.")
- return
-
- if(++D.warns >= MAX_WARNS) //uh ohhhh...you'reee iiiiin trouuuubble O:)
- if(C)
- message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] resulting in a [AUTOBANTIME] minute ban")
- log_admin("[key_name(src)] has warned [key_name(C)] resulting in a [AUTOBANTIME] minute ban")
- to_chat(C, "You have been autobanned due to a warning by [ckey].
This is a temporary ban, it will be removed in [AUTOBANTIME] minutes.")
- qdel(C)
- else
- message_admins("[key_name_admin(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban")
- log_admin("[key_name(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban")
- AddBan(warned_ckey, D.last_id, "Autobanning due to too many formal warnings", ckey, 1, AUTOBANTIME)
- else
- if(C)
- to_chat(C, "You have been formally warned by an administrator.
Further warnings will result in an autoban.")
- message_admins("[key_name_admin(src)] has warned [key_name_admin(C)]. They have [MAX_WARNS-D.warns] strikes remaining.")
- log_admin("[key_name(src)] has warned [key_name(C)]. They have [MAX_WARNS-D.warns] strikes remaining.")
- else
- message_admins("[key_name_admin(src)] has warned [warned_ckey] (DC). They have [MAX_WARNS-D.warns] strikes remaining.")
- log_admin("[key_name(src)] has warned [warned_ckey] (DC). They have [MAX_WARNS-D.warns] strikes remaining.")
-
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Warning") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-#undef MAX_WARNS
-#undef AUTOBANTIME
-
/client/proc/drop_bomb() // Some admin dickery that can probably be done better -- TLE
set category = "Event"
set name = "Drop Bomb"
diff --git a/code/modules/admin/mute.dm b/code/modules/admin/mute.dm
new file mode 100644
index 00000000000..cab33a4edda
--- /dev/null
+++ b/code/modules/admin/mute.dm
@@ -0,0 +1,32 @@
+/// Associative list of people who are muted via admin mutes
+GLOBAL_LIST_EMPTY(admin_mutes_assoc)
+
+/proc/check_mute(ckey, muteflag)
+ if(isnull(GLOB.admin_mutes_assoc[ckey]))
+ return FALSE
+
+ if(GLOB.admin_mutes_assoc[ckey] & muteflag)
+ return TRUE
+ return FALSE
+
+/proc/toggle_mute(ckey, muteflag)
+ if(isnull(GLOB.admin_mutes_assoc[ckey]))
+ GLOB.admin_mutes_assoc[ckey] = 0
+
+ if(GLOB.admin_mutes_assoc[ckey] & muteflag)
+ GLOB.admin_mutes_assoc[ckey] &= ~muteflag
+ else
+ GLOB.admin_mutes_assoc[ckey] |= muteflag
+
+/proc/force_add_mute(ckey, muteflag)
+ if(isnull(GLOB.admin_mutes_assoc[ckey]))
+ GLOB.admin_mutes_assoc[ckey] = 0
+
+ GLOB.admin_mutes_assoc[ckey] |= muteflag
+
+/proc/force_remove_mute(ckey, muteflag)
+ if(isnull(GLOB.admin_mutes_assoc[ckey]))
+ GLOB.admin_mutes_assoc[ckey] = 0
+
+ GLOB.admin_mutes_assoc[ckey] &= ~muteflag
+
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index e1e31544d3e..d9510a4defb 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -446,9 +446,6 @@
alert(usr, "This ban has already been lifted / does not exist.", "Error", "Ok")
unbanpanel()
- else if(href_list["warn"])
- usr.client.warn(href_list["warn"])
-
else if(href_list["unbane"])
if(!check_rights(R_BAN)) return
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index e206f345c0c..3db301985ed 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -6,7 +6,7 @@ GLOBAL_LIST_INIT(adminhelp_ignored_words, list("unknown", "the", "a", "an", "of"
set name = "Adminhelp"
//handle muting and automuting
- if(prefs.muted & MUTE_ADMINHELP)
+ if(check_mute(ckey, MUTE_ADMINHELP))
to_chat(src, "Error: Admin-PM: You cannot send adminhelps (Muted).")
return
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index 7186406e7fa..348d5690ecd 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -58,7 +58,7 @@
//takes input from cmd_admin_pm_context, cmd_admin_pm_panel or /client/Topic and sends them a PM.
//Fetching a message if needed. src is the sender and C is the target client
/client/proc/cmd_admin_pm(whom, msg, type = "PM")
- if(prefs.muted & MUTE_ADMINHELP)
+ if(check_mute(ckey, MUTE_ADMINHELP))
to_chat(src, "Error: Private-Message: You are unable to use PM-s (muted).")
return
@@ -212,7 +212,7 @@
return
/client/proc/cmd_admin_discord_pm()
- if(prefs.muted & MUTE_ADMINHELP)
+ if(check_mute(ckey, MUTE_ADMINHELP))
to_chat(src, "Error: Private-Message: You are unable to use PMs (muted).")
return
diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm
index be4a4763bff..4005d9b7435 100644
--- a/code/modules/admin/verbs/adminsay.dm
+++ b/code/modules/admin/verbs/adminsay.dm
@@ -43,6 +43,7 @@
msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN))
log_mentorsay(msg, src)
+ mob.create_log(OOC_LOG, "MSAY: [msg]")
if(!msg)
return
diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm
index c3ab3bfe47f..410956c9d2a 100644
--- a/code/modules/admin/verbs/deadsay.dm
+++ b/code/modules/admin/verbs/deadsay.dm
@@ -9,7 +9,7 @@
if(!src.mob)
return
- if(prefs.muted & MUTE_DEADCHAT)
+ if(check_mute(ckey, MUTE_DEADCHAT))
to_chat(src, "You cannot send DSAY messages (muted).")
return
diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm
index 8055c5e7a03..93aaf292332 100644
--- a/code/modules/admin/verbs/pray.dm
+++ b/code/modules/admin/verbs/pray.dm
@@ -1,3 +1,7 @@
+#define NUKE_INTACT 0
+#define NUKE_CORE_MISSING 1
+#define NUKE_MISSING 2
+
/mob/living/verb/pray(msg as text)
set category = "IC"
set name = "Pray"
@@ -7,7 +11,7 @@
return
if(usr.client)
- if(usr.client.prefs.muted & MUTE_PRAY)
+ if(check_mute(client.ckey, MUTE_PRAY))
to_chat(usr, "You cannot pray (muted).")
return
if(client.handle_spam_prevention(msg, MUTE_PRAY, OOC_COOLDOWN))
@@ -72,11 +76,21 @@
/proc/Nuke_request(text , mob/Sender)
var/nuke_code = get_nuke_code()
+ var/nuke_status = get_nuke_status()
var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN))
msg = "NUKE CODE REQUEST: [key_name(Sender)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) ([ADMIN_CENTCOM_REPLY(Sender,"RPLY")]): [msg]"
for(var/client/X in GLOB.admins)
if(check_rights(R_EVENT,0,X.mob))
to_chat(X, msg)
- to_chat(X, "The nuke code is [nuke_code].")
+ if(nuke_status == NUKE_MISSING)
+ to_chat(X, "The nuclear device is not on station!")
+ else
+ to_chat(X, "The nuke code is [nuke_code].")
+ if(nuke_status == NUKE_CORE_MISSING)
+ to_chat(X, "The nuclear device does not have a core, and will not arm!")
if(X.prefs.sound & SOUND_ADMINHELP)
SEND_SOUND(X, sound('sound/effects/adminhelp.ogg'))
+
+#undef NUKE_INTACT
+#undef NUKE_CORE_MISSING
+#undef NUKE_MISSING
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 81be8843152..b12d3de9963 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -230,19 +230,19 @@
if(automute)
muteunmute = "auto-muted"
- M.client.prefs.muted |= mute_type
+ force_add_mute(M.client.ckey, mute_type)
log_admin("SPAM AUTOMUTE: [muteunmute] [key_name(M)] from [mute_string]")
message_admins("SPAM AUTOMUTE: [muteunmute] [key_name_admin(M)] from [mute_string].", 1)
to_chat(M, "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Automute") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
- if(M.client.prefs.muted & mute_type)
- muteunmute = "unmuted"
- M.client.prefs.muted &= ~mute_type
- else
+ toggle_mute(M.client.ckey, mute_type)
+
+ if(check_mute(M.client.ckey, mute_type))
muteunmute = "muted"
- M.client.prefs.muted |= mute_type
+ else
+ muteunmute = "unmuted"
log_admin("[key_name(usr)] has [muteunmute] [key_name(M)] from [mute_string]")
message_admins("[key_name_admin(usr)] has [muteunmute] [key_name_admin(M)] from [mute_string].", 1)
@@ -606,7 +606,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
"Custom" = "Cryptic Message")
var/list/MsgSound = list("Beep" = 'sound/misc/notice2.ogg',
- "Enemy Communications Intercepted" = 'sound/AI/intercept2.ogg',
+ "Enemy Communications Intercepted" = 'sound/AI/intercept.ogg',
"New Command Report Created" = 'sound/AI/commandreport.ogg')
var/type = input(usr, "Pick a type of report to send", "Report Type", "") as anything in MsgType
diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm
index 92e6336d693..99efbc811f4 100644
--- a/code/modules/antagonists/wishgranter/wishgranter.dm
+++ b/code/modules/antagonists/wishgranter/wishgranter.dm
@@ -25,9 +25,6 @@
H.dna.SetSEState(GLOB.hulkblock, TRUE)
singlemutcheck(H, GLOB.hulkblock, MUTCHK_FORCED)
- H.dna.SetSEState(GLOB.xrayblock, TRUE)
- singlemutcheck(H, GLOB.xrayblock, MUTCHK_FORCED)
-
H.dna.SetSEState(GLOB.fireblock, TRUE)
singlemutcheck(H, GLOB.fireblock, MUTCHK_FORCED)
diff --git a/code/modules/arcade/mob_hunt/mob_avatar.dm b/code/modules/arcade/mob_hunt/mob_avatar.dm
index 7f7f53d3199..19f792af763 100644
--- a/code/modules/arcade/mob_hunt/mob_avatar.dm
+++ b/code/modules/arcade/mob_hunt/mob_avatar.dm
@@ -1,4 +1,3 @@
-
/obj/effect/nanomob
name = "Nano-Mob Avatar" //will be overridden by the mob datum name value when created
desc = "A wild Nano-Mob appeared! Hit it with your PDA with the game open to attempt to capture it!"
@@ -12,17 +11,29 @@
var/list/clients_encountered = list() //tracks who has already interacted with us, so they can't attempt a second capture
var/image/avatar
-/obj/effect/nanomob/New(loc, datum/mob_hunt/new_info)
- ..()
+/obj/effect/nanomob/Initialize(mapload, datum/mob_hunt/new_info)
+ . = ..()
if(!new_info)
- qdel(src)
- return
+ return INITIALIZE_HINT_QDEL
mob_info = new_info
+ RegisterSignal(mob_info, COMSIG_PARENT_QDELETING, .proc/delete_wrapper)
update_self()
forceMove(mob_info.spawn_point)
if(!mob_info.is_trap)
addtimer(CALLBACK(src, .proc/despawn), mob_info.lifetime)
+/obj/effect/nanomob/Destroy()
+ SSmob_hunt.trap_spawns -= src
+ SSmob_hunt.normal_spawns -= src
+ mob_info = null // Can't delete this since multiple players can get the exact same /datum/mob_hunt. (This should be refactored)
+ clients_encountered.Cut()
+ QDEL_NULL(avatar)
+ return ..()
+
+/obj/effect/nanomob/proc/delete_wrapper()
+ SIGNAL_HANDLER
+ qdel(src)
+
/obj/effect/nanomob/proc/update_self()
if(!mob_info)
return
@@ -132,12 +143,6 @@
icon_state = "placeholder"
var/obj/machinery/computer/mob_battle_terminal/my_terminal
-/obj/effect/nanomob/battle/New(loc, datum/mob_hunt/new_info)
- . = ..()
- if(new_info)
- mob_info = new_info
- update_self()
-
/obj/effect/nanomob/battle/update_self()
if(!mob_info)
name = "Nano-Mob Battle Avatar"
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index d1efa862a63..cd83952fcae 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -215,7 +215,7 @@
var/life_cycles = 0
var/life_cap = 20
anchored = TRUE
- pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
+ pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE | PASSFENCE
/obj/effect/beam/i_beam/proc/hit()
diff --git a/code/modules/atmospherics/machinery/atmospherics.dm b/code/modules/atmospherics/machinery/atmospherics.dm
index bb255692633..8cf6e965e62 100644
--- a/code/modules/atmospherics/machinery/atmospherics.dm
+++ b/code/modules/atmospherics/machinery/atmospherics.dm
@@ -71,10 +71,13 @@ Pipelines + Other Objects -> Pipe network
// Icons/overlays/underlays
/obj/machinery/atmospherics/update_icon()
var/turf/T = get_turf(loc)
- if(!T || level == 2 || !T.intact)
- plane = GAME_PLANE
- else
+ if(T && T.transparent_floor)
plane = FLOOR_PLANE
+ else
+ if(!T || level == 2 || !T.intact)
+ plane = GAME_PLANE
+ else
+ plane = FLOOR_PLANE
/obj/machinery/atmospherics/proc/update_pipe_image()
pipe_image = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir) //the 20 puts it above Byond's darkness (not its opacity view)
@@ -95,15 +98,18 @@ Pipelines + Other Objects -> Pipe network
/obj/machinery/atmospherics/proc/add_underlay(turf/T, obj/machinery/atmospherics/node, direction, icon_connect_type)
if(node)
- if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe))
+ if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe) && !T.transparent_floor)
//underlays += SSair.icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node))
underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type)
else
//underlays += SSair.icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node))
underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type)
else
- //underlays += SSair.icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color)
- underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type)
+ if(T.transparent_floor) //we want to keep pipes under transparent floors connected normally
+ underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type)
+ else
+ //underlays += SSair.icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color)
+ underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type)
/obj/machinery/atmospherics/proc/update_underlays()
if(check_icon_cache())
@@ -166,11 +172,14 @@ Pipelines + Other Objects -> Pipe network
//(De)construction
/obj/machinery/atmospherics/attackby(obj/item/W, mob/user)
+ var/turf/T = get_turf(src)
if(can_unwrench && istype(W, /obj/item/wrench))
- var/turf/T = get_turf(src)
+ if(T.transparent_floor && istype(src, /obj/machinery/atmospherics/pipe) && layer != GAS_PIPE_VISIBLE_LAYER) //pipes on GAS_PIPE_VISIBLE_LAYER are above the transparent floor and should be interactable
+ to_chat(user, "You can't interact with something that's under the floor!")
+ return
if(level == 1 && isturf(T) && T.intact)
to_chat(user, "You must remove the plating first.")
- return 1
+ return
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
add_fingerprint(user)
@@ -184,14 +193,14 @@ Pipelines + Other Objects -> Pipe network
playsound(src.loc, W.usesound, 50, 1)
to_chat(user, "You begin to unfasten \the [src]...")
if(internal_pressure > 2*ONE_ATMOSPHERE)
- to_chat(user, "As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?")
+ to_chat(user, "As you begin unwrenching \the [src] a gust of air blows in your face... maybe you should reconsider?")
unsafe_wrenching = TRUE //Oh dear oh dear
if(do_after(user, 40 * W.toolspeed, target = src) && !QDELETED(src))
user.visible_message( \
"[user] unfastens \the [src].", \
"You have unfastened \the [src].", \
- "You hear ratchet.")
+ "You hear ratcheting.")
investigate_log("was REMOVED by [key_name(usr)]", "atmos")
for(var/obj/item/clothing/shoes/magboots/usermagboots in user.get_equipped_items())
@@ -206,6 +215,9 @@ Pipelines + Other Objects -> Pipe network
unsafe_pressure_release(user,internal_pressure)
deconstruct(TRUE)
else
+ if(T.transparent_floor)
+ to_chat(user, "You can't interact with something that's under the floor!")
+ return TRUE
return ..()
//Called when an atmospherics object is unwrenched while having a large pressure difference
@@ -240,7 +252,12 @@ Pipelines + Other Objects -> Pipe network
dir = D
initialize_directions = P
var/turf/T = loc
- level = T.intact ? 2 : 1
+ if(!T.transparent_floor)
+ level = T.intact ? 2 : 1
+ else
+ level = 2
+ plane = GAME_PLANE
+ layer = GAS_PIPE_VISIBLE_LAYER
add_fingerprint(usr)
if(!SSair.initialized) //If there's no atmos subsystem, we can't really initialize pipenets
SSair.machinery_to_construct.Add(src)
@@ -337,12 +354,15 @@ Pipelines + Other Objects -> Pipe network
/obj/machinery/atmospherics/proc/add_underlay_adapter(turf/T, obj/machinery/atmospherics/node, direction, icon_connect_type) //modified from add_underlay, does not make exposed underlays
if(node)
- if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe))
+ if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe) && !T.transparent_floor)
underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type)
else
underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type)
else
- underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "retracted" + icon_connect_type)
+ if(T.transparent_floor) //we want to keep pipes under transparent floors connected normally
+ underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type)
+ else
+ underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "retracted" + icon_connect_type)
/obj/machinery/atmospherics/singularity_pull(S, current_size)
if(current_size >= STAGE_FIVE)
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
index 3943bde9a1d..8a5743da3d3 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
@@ -144,7 +144,7 @@
if(!istype(L) || L.buckled)
return
if(L.abiotic())
- to_chat(user, "Subject cannot have abiotic items on.")
+ to_chat(user, "Subject may not hold anything in their hands.")
return
if(L.has_buckled_mobs()) //mob attached to us
to_chat(user, "[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.")
@@ -462,7 +462,7 @@
to_chat(usr, "The cryo cell is already occupied!")
return
if(M.abiotic())
- to_chat(usr, "Subject may not have abiotic items on.")
+ to_chat(usr, "Subject may not hold anything in their hands.")
return
if(!node)
to_chat(usr, "The cell is not correctly connected to its pipe network!")
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm b/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm
index 9859058d731..94f9f17e4b5 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm
@@ -2,6 +2,7 @@
icon = 'icons/atmos/vent_pump.dmi'
icon_state = "map_vent"
plane = FLOOR_PLANE
+ layer = GAS_SCRUBBER_LAYER
name = "passive vent"
desc = "A large air vent"
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/tank.dm b/code/modules/atmospherics/machinery/components/unary_devices/tank.dm
index 1cfb4d18eb1..ed0c438008a 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/tank.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/tank.dm
@@ -1,7 +1,7 @@
/obj/machinery/atmospherics/unary/tank
icon = 'icons/atmos/tank.dmi'
icon_state = "air_map"
-
+ layer = GAS_PIPE_VISIBLE_LAYER
name = "pressure tank"
desc = "A large vessel containing pressurized gas."
diff --git a/code/modules/atmospherics/machinery/datum_icon_manager.dm b/code/modules/atmospherics/machinery/datum_icon_manager.dm
index 2e0ef593bae..f7a345a7c9a 100644
--- a/code/modules/atmospherics/machinery/datum_icon_manager.dm
+++ b/code/modules/atmospherics/machinery/datum_icon_manager.dm
@@ -159,9 +159,9 @@
var/cache_name = state
for(var/D in GLOB.cardinal)
- var/image/I = image(icon('icons/atmos/pipe_underlays.dmi', icon_state = state, dir = D))
+ var/image/I = image(icon('icons/atmos/pipe_underlays.dmi', icon_state = state, dir = D), layer = GAS_PIPE_HIDDEN_LAYER)
underlays[cache_name + "[D]"] = I
for(var/pipe_color in GLOB.pipe_colors)
- I = image(icon('icons/atmos/pipe_underlays.dmi', icon_state = state, dir = D))
+ I = image(icon('icons/atmos/pipe_underlays.dmi', icon_state = state, dir = D), layer = GAS_PIPE_HIDDEN_LAYER)
I.color = GLOB.pipe_colors[pipe_color]
underlays[state + "[D]" + "[GLOB.pipe_colors[pipe_color]]"] = I
diff --git a/code/modules/atmospherics/machinery/pipes/cap.dm b/code/modules/atmospherics/machinery/pipes/cap.dm
index 615075cf3c0..4cfa2efb450 100644
--- a/code/modules/atmospherics/machinery/pipes/cap.dm
+++ b/code/modules/atmospherics/machinery/pipes/cap.dm
@@ -79,7 +79,8 @@
break
var/turf/T = get_turf(src) // hide if turf is not intact
- if(!istype(T)) return
+ if(!istype(T) || T.transparent_floor)
+ return
hide(T.intact)
update_icon()
diff --git a/code/modules/atmospherics/machinery/pipes/manifold.dm b/code/modules/atmospherics/machinery/pipes/manifold.dm
index ffb66a01da3..20dd2990888 100644
--- a/code/modules/atmospherics/machinery/pipes/manifold.dm
+++ b/code/modules/atmospherics/machinery/pipes/manifold.dm
@@ -57,6 +57,8 @@
node3 = target
break
var/turf/T = src.loc // hide if turf is not intact
+ if(T.transparent_floor)
+ return
hide(T.intact)
update_icon()
diff --git a/code/modules/atmospherics/machinery/pipes/manifold4w.dm b/code/modules/atmospherics/machinery/pipes/manifold4w.dm
index 80d18bcee0f..5cefc6f723c 100644
--- a/code/modules/atmospherics/machinery/pipes/manifold4w.dm
+++ b/code/modules/atmospherics/machinery/pipes/manifold4w.dm
@@ -165,7 +165,8 @@
break
var/turf/T = src.loc // hide if turf is not intact
- hide(T.intact)
+ if(!T.transparent_floor)
+ hide(T.intact)
update_icon()
/obj/machinery/atmospherics/pipe/manifold4w/visible
diff --git a/code/modules/atmospherics/machinery/pipes/pipe.dm b/code/modules/atmospherics/machinery/pipes/pipe.dm
index afda1c30db7..925bf48715d 100644
--- a/code/modules/atmospherics/machinery/pipes/pipe.dm
+++ b/code/modules/atmospherics/machinery/pipes/pipe.dm
@@ -46,10 +46,6 @@
if(istype(W, /obj/item/analyzer))
atmosanalyzer_scan(parent.air, user)
return
-
- if(istype(W, /obj/item/painter))
- return
-
return ..()
/obj/machinery/atmospherics/proc/pipeline_expansion()
diff --git a/code/modules/atmospherics/machinery/pipes/simple/pipe_simple.dm b/code/modules/atmospherics/machinery/pipes/simple/pipe_simple.dm
index 92c421fbff9..7e14946e61e 100644
--- a/code/modules/atmospherics/machinery/pipes/simple/pipe_simple.dm
+++ b/code/modules/atmospherics/machinery/pipes/simple/pipe_simple.dm
@@ -68,7 +68,8 @@
break
var/turf/T = loc // hide if turf is not intact
- hide(T.intact)
+ if(!T.transparent_floor)
+ hide(T.intact)
update_icon()
/obj/machinery/atmospherics/pipe/simple/check_pressure(pressure)
diff --git a/code/modules/awaymissions/mission_code/wildwest.dm b/code/modules/awaymissions/mission_code/wildwest.dm
index 2377deca844..511fbb92294 100644
--- a/code/modules/awaymissions/mission_code/wildwest.dm
+++ b/code/modules/awaymissions/mission_code/wildwest.dm
@@ -79,8 +79,6 @@
ADD_TRAIT(user, TRAIT_LASEREYES, "ww_wishgranter")
user.dna.SetSEState(GLOB.fireblock, TRUE)
singlemutcheck(user, GLOB.fireblock, MUTCHK_FORCED)
- user.dna.SetSEState(GLOB.xrayblock, TRUE)
- singlemutcheck(user, GLOB.xrayblock, MUTCHK_FORCED)
if(ishuman(user))
var/mob/living/carbon/human/human = user
if(!isshadowperson(human))
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 8c7dc4963f6..8a07b9fe4d0 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -8,8 +8,8 @@
#define UPLOAD_LIMIT 10485760 //Restricts client uploads to the server to 10MB //Boosted this thing. What's the worst that can happen?
#define MIN_CLIENT_VERSION 513 // Minimum byond major version required to play.
//I would just like the code ready should it ever need to be used.
-#define SUGGESTED_CLIENT_VERSION 513 // only integers (e.g: 513, 514) are useful here. This is the part BEFORE the ".", IE 513 out of 513.1542
-#define SUGGESTED_CLIENT_BUILD 1542 // only integers (e.g: 1542, 1543) are useful here. This is the part AFTER the ".", IE 1542 out of 513.1542
+#define SUGGESTED_CLIENT_VERSION 514 // only integers (e.g: 513, 514) are useful here. This is the part BEFORE the ".", IE 513 out of 513.1542
+#define SUGGESTED_CLIENT_BUILD 1566 // only integers (e.g: 1542, 1543) are useful here. This is the part AFTER the ".", IE 1542 out of 513.1542
#define SSD_WARNING_TIMER 30 // cycles, not seconds, so 30=60s
@@ -113,7 +113,7 @@
if(!holder && received_discord_pm < world.time - 6000) // Worse they can do is spam discord for 10 minutes
to_chat(usr, "You are no longer able to use this, it's been more then 10 minutes since an admin on Discord has responded to you")
return
- if(prefs.muted & MUTE_ADMINHELP)
+ if(check_mute(ckey, MUTE_ADMINHELP))
to_chat(usr, "You cannot use this as your client has been muted from sending messages to the admins on Discord")
return
cmd_admin_discord_pm()
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index 1979be243fa..b4688cb73b4 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -71,8 +71,6 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
var/max_gear_slots = 0
//non-preference stuff
- var/warns = 0
- var/muted = 0
var/last_ip
var/last_id
diff --git a/code/modules/client/preference/preferences_toggles.dm b/code/modules/client/preference/preferences_toggles.dm
index 2bb7b7529e2..21c81287a2b 100644
--- a/code/modules/client/preference/preferences_toggles.dm
+++ b/code/modules/client/preference/preferences_toggles.dm
@@ -356,3 +356,11 @@
prefs.toggles2 ^= PREFTOGGLE_2_SIMPLE_STAT_PANEL
prefs.save_preferences(src)
to_chat(src, "You will [(prefs.toggles2 & PREFTOGGLE_2_SIMPLE_STAT_PANEL) ? "no longer" : "now"] get detailed information on the status panel.")
+
+/client/verb/toggle_item_outlines()
+ set name = "Toggle Item Outlines"
+ set category = "Preferences"
+ set desc = "Toggles seeing item outlines on hover."
+ prefs.toggles2 ^= PREFTOGGLE_2_SEE_ITEM_OUTLINES
+ prefs.save_preferences(src)
+ to_chat(usr, "You will [(prefs.toggles2 & PREFTOGGLE_2_SEE_ITEM_OUTLINES) ? "now" : "no longer"] see item outlines on hover.")
diff --git a/code/modules/events/anomaly.dm b/code/modules/events/anomaly.dm
index c806fac3ce0..a97abdd8e5a 100644
--- a/code/modules/events/anomaly.dm
+++ b/code/modules/events/anomaly.dm
@@ -5,6 +5,13 @@
/datum/event/anomaly/proc/findEventArea()
var/static/list/allowed_areas
+ var/static/list/existing_areas
+ if(!existing_areas)
+ existing_areas = list()
+ for(var/area/AR in world)
+ var/turf/picked = safepick(get_area_turfs(AR.type))
+ if(picked && is_station_level(picked.z))
+ existing_areas += AR
if(!allowed_areas)
//Places that shouldn't explode
var/list/safe_area_types = typecacheof(list(
@@ -20,10 +27,16 @@
)
//Subtypes from the above that actually should explode.
- var/list/unsafe_area_subtypes = typecacheof(list(/area/engine/break_room))
+ var/list/unsafe_area_subtypes = typecacheof(list(
+ /area/engine/break_room,
+ /area/engine/equipmentstorage,
+ /area/engine/chiefs_office,
+ /area/engine/controlroom,
+ /area/engine/mechanic_workshop
+ ))
- allowed_areas = make_associative(GLOB.the_station_areas) - safe_area_types + unsafe_area_subtypes
- var/list/possible_areas = typecache_filter_list(GLOB.all_areas, allowed_areas)
+ allowed_areas = typecacheof(GLOB.the_station_areas) - safe_area_types + unsafe_area_subtypes
+ var/list/possible_areas = typecache_filter_list(existing_areas, allowed_areas)
if(length(possible_areas))
return pick(possible_areas)
@@ -36,7 +49,7 @@
CRASH("Anomaly: No valid turfs found for [impact_area] - [impact_area.type]")
/datum/event/anomaly/announce()
- GLOB.event_announcement.Announce("Localized hyper-energetic flux wave detected on long range scanners. Expected location of impact: [impact_area.name].", "Anomaly Alert")
+ GLOB.event_announcement.Announce("Localized hyper-energetic flux wave detected on long range scanners. Expected location of impact: [impact_area.name].", "Anomaly Alert", 'sound/AI/anomaly_flux.ogg')
/datum/event/anomaly/start()
var/turf/T = pick(get_area_turfs(impact_area))
diff --git a/code/modules/events/anomaly_bluespace.dm b/code/modules/events/anomaly_bluespace.dm
index b90e8b86b01..67a559f97bd 100644
--- a/code/modules/events/anomaly_bluespace.dm
+++ b/code/modules/events/anomaly_bluespace.dm
@@ -5,4 +5,4 @@
anomaly_path = /obj/effect/anomaly/bluespace
/datum/event/anomaly/anomaly_bluespace/announce()
- GLOB.event_announcement.Announce("Unstable bluespace anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")
+ GLOB.event_announcement.Announce("Unstable bluespace anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert", 'sound/AI/anomaly_bluespace.ogg')
diff --git a/code/modules/events/anomaly_flux.dm b/code/modules/events/anomaly_flux.dm
index 350e21cb496..2635185faa1 100644
--- a/code/modules/events/anomaly_flux.dm
+++ b/code/modules/events/anomaly_flux.dm
@@ -5,4 +5,4 @@
anomaly_path = /obj/effect/anomaly/flux
/datum/event/anomaly/anomaly_flux/announce()
- GLOB.event_announcement.Announce("Localized hyper-energetic flux wave detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")
+ GLOB.event_announcement.Announce("Localized hyper-energetic flux wave detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert", 'sound/AI/anomaly_flux.ogg')
diff --git a/code/modules/events/anomaly_grav.dm b/code/modules/events/anomaly_grav.dm
index 4acf530c081..1009e12fed2 100644
--- a/code/modules/events/anomaly_grav.dm
+++ b/code/modules/events/anomaly_grav.dm
@@ -5,4 +5,4 @@
anomaly_path = /obj/effect/anomaly/grav
/datum/event/anomaly/anomaly_grav/announce()
- GLOB.event_announcement.Announce("Gravitational anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")
+ GLOB.event_announcement.Announce("Gravitational anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert", 'sound/AI/anomaly_gravity.ogg')
diff --git a/code/modules/events/anomaly_pyro.dm b/code/modules/events/anomaly_pyro.dm
index 2caa8dc4a0c..5c29ba1c4b7 100644
--- a/code/modules/events/anomaly_pyro.dm
+++ b/code/modules/events/anomaly_pyro.dm
@@ -5,4 +5,4 @@
anomaly_path = /obj/effect/anomaly/pyro
/datum/event/anomaly/anomaly_pyro/announce()
- GLOB.event_announcement.Announce("Atmospheric anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")
+ GLOB.event_announcement.Announce("Atmospheric anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert", 'sound/AI/anomaly_pyro.ogg')
diff --git a/code/modules/events/anomaly_vortex.dm b/code/modules/events/anomaly_vortex.dm
index 76058cfa574..a3bf8938d57 100644
--- a/code/modules/events/anomaly_vortex.dm
+++ b/code/modules/events/anomaly_vortex.dm
@@ -5,4 +5,4 @@
anomaly_path = /obj/effect/anomaly/bhole
/datum/event/anomaly/anomaly_vortex/announce()
- GLOB.event_announcement.Announce("Localized high-intensity vortex anomaly detected on long range scanners. Expected location: [impact_area.name]", "Anomaly Alert")
+ GLOB.event_announcement.Announce("Localized high-intensity vortex anomaly detected on long range scanners. Expected location: [impact_area.name]", "Anomaly Alert", 'sound/AI/anomaly_vortex.ogg')
diff --git a/code/modules/events/apc_overload.dm b/code/modules/events/apc_overload.dm
index 4e2a296dda5..e1784ae9726 100644
--- a/code/modules/events/apc_overload.dm
+++ b/code/modules/events/apc_overload.dm
@@ -21,7 +21,7 @@
SEND_SOUND(M, S)
/datum/event/apc_overload/announce()
- GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please check all underfloor APC terminals.", "Critical Power Failure", new_sound = 'sound/AI/attention.ogg')
+ GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please check all underfloor APC terminals.", "Critical Power Failure", 'sound/AI/power_overload.ogg')
/datum/event/apc_overload/end()
return TRUE
@@ -33,7 +33,7 @@
/area/turret_protected/ai)
if(announce)
- GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please check all underfloor APC terminals.", "Critical Power Failure", new_sound = 'sound/AI/attention.ogg')
+ GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please check all underfloor APC terminals.", "Critical Power Failure", 'sound/AI/power_overload.ogg')
// break APC_BREAK_PROBABILITY% of all of the APCs on the station
var/affected_apc_count = 0
diff --git a/code/modules/events/apc_short.dm b/code/modules/events/apc_short.dm
index 759b443e2bf..9c29b96a0f1 100644
--- a/code/modules/events/apc_short.dm
+++ b/code/modules/events/apc_short.dm
@@ -20,7 +20,7 @@
SEND_SOUND(M, S)
/datum/event/apc_short/announce()
- GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please repair shorted APCs.", "Systems Power Failure", new_sound = 'sound/AI/attention.ogg')
+ GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please repair shorted APCs.", "Systems Power Failure", 'sound/AI/power_overload.ogg')
/datum/event/apc_short/end()
return TRUE
@@ -30,7 +30,7 @@
/area/engine/engineering,
/area/engine/supermatter,
/area/turret_protected/ai)
- GLOB.event_announcement.Announce("Power failure detected in [station_name()]'s powernet. All APCs have lost power. Gravity systems likely to fail.", "Systems Power Failure", new_sound = 'sound/AI/attention.ogg')
+ GLOB.event_announcement.Announce("Power failure detected in [station_name()]'s powernet. All APCs have lost power. Gravity systems likely to fail.", "Systems Power Failure", 'sound/AI/attention.ogg')
for(var/thing in GLOB.apcs)
var/obj/machinery/power/apc/A = thing
var/area/current_area = get_area(A)
@@ -49,7 +49,7 @@
/area/engine/supermatter,
/area/turret_protected/ai)
if(announce)
- GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please repair shorted APCs.", "Systems Power Failure", new_sound = 'sound/AI/attention.ogg')
+ GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please repair shorted APCs.", "Systems Power Failure", 'sound/AI/power_overload.ogg')
// break APC_BREAK_PROBABILITY% of all of the APCs on the station
var/affected_apc_count = 0
for(var/thing in GLOB.apcs)
@@ -76,7 +76,7 @@
/proc/power_restore(announce = TRUE, power_type)
if(power_type == 0) //Power without Repairing
if(announce)
- GLOB.event_announcement.Announce("All operational APCs on \the [station_name()] have been fully charged.", "Power Systems Nominal", new_sound = 'sound/AI/poweron.ogg')
+ GLOB.event_announcement.Announce("All operational APCs on \the [station_name()] have been fully charged.", "Power Systems Nominal", 'sound/AI/power_restore.ogg')
var/affected_apc_count = 0
for(var/thing in GLOB.apcs)
var/obj/machinery/power/apc/A = thing
@@ -90,7 +90,7 @@
log_and_message_admins("Power has been restored to [affected_apc_count] APCs.")
if(power_type == 1) //Repair without charging
if(announce)
- GLOB.event_announcement.Announce("All APCs on \the [station_name()] have been repaired.", "Power Systems Nominal", new_sound = 'sound/AI/poweron.ogg')
+ GLOB.event_announcement.Announce("All APCs on \the [station_name()] have been repaired.", "Power Systems Nominal", 'sound/AI/power_restore.ogg')
for(var/thing in GLOB.apcs)
var/obj/machinery/power/apc/A = thing
var/area/current_area = get_area(A)
@@ -101,7 +101,7 @@
log_and_message_admins("Power has been restored to all APCs.")
if(power_type == 2) //Repair and Power APCs
if(announce)
- GLOB.event_announcement.Announce("All APCs on \the [station_name()] have been repaired and recharged. We apologize for the inconvenience.", "Power Systems Optimal", new_sound = 'sound/AI/poweron.ogg')
+ GLOB.event_announcement.Announce("All APCs on \the [station_name()] have been repaired and recharged. We apologize for the inconvenience.", "Power Systems Optimal", 'sound/AI/power_restore.ogg')
// repair the APCs and recharge them
for(var/thing in GLOB.apcs)
var/obj/machinery/power/apc/A = thing
@@ -115,7 +115,7 @@
/proc/power_restore_quick(announce = TRUE)
if(announce)
- GLOB.event_announcement.Announce("All SMESs on \the [station_name()] have been recharged. We apologize for the inconvenience.", "Power Systems Nominal", new_sound = 'sound/AI/poweron.ogg')
+ GLOB.event_announcement.Announce("All SMESs on \the [station_name()] have been recharged. We apologize for the inconvenience.", "Power Systems Nominal", 'sound/AI/power_restore.ogg')
// fix all of the SMESs
for(var/obj/machinery/power/smes/S in GLOB.machines)
if(!is_station_level(S.z))
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index 2ffcdb2acb9..e8a557ed069 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -14,7 +14,7 @@
"You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")
/datum/event/brand_intelligence/announce()
- GLOB.event_announcement.Announce("Rampant brand intelligence has been detected aboard [station_name()], please stand-by. The origin is believed to be \a [originMachine.name].", "Machine Learning Alert")
+ GLOB.event_announcement.Announce("Rampant brand intelligence has been detected aboard [station_name()], please stand-by. The origin is believed to be \a [originMachine.name].", "Machine Learning Alert", 'sound/AI/brand_intelligence.ogg')
/datum/event/brand_intelligence/start()
for(var/obj/machinery/vending/V in GLOB.machines)
diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm
index 9a6ab6ca614..feba4fc1626 100644
--- a/code/modules/events/disease_outbreak.dm
+++ b/code/modules/events/disease_outbreak.dm
@@ -20,7 +20,7 @@
D.carrier = TRUE
/datum/event/disease_outbreak/announce()
- GLOB.event_announcement.Announce("Confirmed outbreak of level 7 major viral biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg')
+ GLOB.event_announcement.Announce("Confirmed outbreak of level 7 major viral biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak7.ogg')
for(var/p in GLOB.dead_mob_list)
var/mob/M = p
to_chat(M, "[patient_zero] has been infected with [D.name] ([ghost_follow_link(patient_zero, M)])")
diff --git a/code/modules/events/door_runtime.dm b/code/modules/events/door_runtime.dm
index 39e02e694db..67110810a82 100644
--- a/code/modules/events/door_runtime.dm
+++ b/code/modules/events/door_runtime.dm
@@ -1,7 +1,7 @@
/datum/event/door_runtime
/datum/event/door_runtime/announce()
- GLOB.minor_announcement.Announce("Hostile runtime detected in door controllers. Isolation lockdown protocols are now in effect. Please remain calm.", "Network Alert")
+ GLOB.minor_announcement.Announce("Hostile runtime detected in door controllers. Isolation lockdown protocols are now in effect. Please remain calm.", "Network Alert", 'sound/AI/door_runtimes.ogg')
/datum/event/door_runtime/start()
for(var/obj/machinery/door/D in GLOB.airlocks)
@@ -13,4 +13,4 @@
post_status("alert", "lockdown")
/datum/event/door_runtime/proc/reboot()
- GLOB.minor_announcement.Announce("Automatic system reboot complete. Have a secure day.","Network reset:")
+ GLOB.minor_announcement.Announce("Automatic system reboot complete. Have a secure day.","Network reset:", 'sound/AI/door_runtimes_fix.ogg')
diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm
index 17c697faf11..555d26ee353 100644
--- a/code/modules/events/electrical_storm.dm
+++ b/code/modules/events/electrical_storm.dm
@@ -3,7 +3,7 @@
var/lightsoutRange = 25
/datum/event/electrical_storm/announce()
- GLOB.event_announcement.Announce("An electrical storm has been detected in your area, please repair potential electronic overloads.", "Electrical Storm Alert")
+ GLOB.event_announcement.Announce("An electrical storm has been detected in your area, please repair potential electronic overloads.", "Electrical Storm Alert", 'sound/AI/elec_storm.ogg')
/datum/event/electrical_storm/start()
var/list/epicentreList = list()
@@ -26,4 +26,3 @@
var/obj/effect/landmark/epicentre = thing
for(var/obj/machinery/power/apc/apc in range(epicentre, lightsoutRange))
INVOKE_ASYNC(apc, /obj/machinery/power/apc.proc/overload_lighting)
-
diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm
index 60fc5e4d39a..310bc8a1767 100644
--- a/code/modules/events/ion_storm.dm
+++ b/code/modules/events/ion_storm.dm
@@ -18,7 +18,7 @@
/datum/event/ion_storm/announce()
if(announceEvent == ION_ANNOUNCE || (announceEvent == ION_RANDOM && prob(ionAnnounceChance)))
- GLOB.event_announcement.Announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", 'sound/AI/ionstorm.ogg')
+ GLOB.event_announcement.Announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", 'sound/AI/ions.ogg')
/datum/event/ion_storm/start()
//AI laws
diff --git a/code/modules/events/koi_mirgration.dm b/code/modules/events/koi_mirgration.dm
index 45f1ccf6d85..33510149c1b 100644
--- a/code/modules/events/koi_mirgration.dm
+++ b/code/modules/events/koi_mirgration.dm
@@ -5,4 +5,4 @@
/datum/event/carp_migration/koi/start()
- spawn_fish(GLOB.landmarks_list.len)
+ spawn_fish(rand(4, 6)) //12 to 30 koi, in small groups
diff --git a/code/modules/events/solarflare.dm b/code/modules/events/solarflare.dm
index edd1726cbb2..fa72b6182cb 100644
--- a/code/modules/events/solarflare.dm
+++ b/code/modules/events/solarflare.dm
@@ -4,7 +4,7 @@
announceWhen = 1
/datum/event/solar_flare/announce()
- GLOB.event_announcement.Announce("A solar flare has been detected on collision course with the station.", "Incoming Solar Flare", 'sound/AI/attention.ogg')
+ GLOB.event_announcement.Announce("A solar flare has been detected on collision course with the station.", "Incoming Solar Flare", 'sound/AI/flare.ogg')
/datum/event/solar_flare/start()
SSweather.run_weather(/datum/weather/solar_flare)
diff --git a/code/modules/events/spider_terror.dm b/code/modules/events/spider_terror.dm
index 5f9a06a501b..6bcd369884e 100644
--- a/code/modules/events/spider_terror.dm
+++ b/code/modules/events/spider_terror.dm
@@ -11,7 +11,7 @@
/datum/event/spider_terror/announce()
if(successSpawn)
- GLOB.command_announcement.Announce("Confirmed outbreak of level 3 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/effects/siren-spooky.ogg')
+ GLOB.command_announcement.Announce("Confirmed outbreak of level 3 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/effects/siren-spooky.ogg', new_sound2 = 'sound/AI/outbreak3.ogg')
else
log_and_message_admins("Warning: Could not spawn any mobs for event Terror Spiders")
@@ -61,4 +61,3 @@
successSpawn = TRUE
#undef TS_HIGHPOP_TRIGGER
-
diff --git a/code/modules/events/tear.dm b/code/modules/events/tear.dm
index 85976290f9c..ae0597e501f 100644
--- a/code/modules/events/tear.dm
+++ b/code/modules/events/tear.dm
@@ -5,7 +5,7 @@
var/obj/effect/tear/TE
/datum/event/tear/announce()
- GLOB.event_announcement.Announce("A tear in the fabric of space and time has opened. Expected location: [impact_area.name].", "Anomaly Alert")
+ GLOB.event_announcement.Announce("A tear in the fabric of space and time has opened. Expected location: [impact_area.name].", "Anomaly Alert", 'sound/AI/anomaly.ogg')
/datum/event/tear/start()
var/turf/T = pick(get_area_turfs(impact_area))
diff --git a/code/modules/events/vent_clog.dm b/code/modules/events/vent_clog.dm
index daa2013a126..484b3eb2e73 100644
--- a/code/modules/events/vent_clog.dm
+++ b/code/modules/events/vent_clog.dm
@@ -6,7 +6,7 @@
var/list/vents = list()
/datum/event/vent_clog/announce()
- GLOB.event_announcement.Announce("The scrubbers network is experiencing a backpressure surge. Some ejection of contents may occur.", "Atmospherics alert")
+ GLOB.event_announcement.Announce("The scrubbers network is experiencing a backpressure surge. Some ejection of contents may occur.", "Atmospherics alert", 'sound/AI/scrubbers.ogg')
/datum/event/vent_clog/setup()
endWhen = rand(25, 100)
diff --git a/code/modules/food_and_drinks/food/foods/sandwiches.dm b/code/modules/food_and_drinks/food/foods/sandwiches.dm
index f0e49305175..0649a5beed5 100644
--- a/code/modules/food_and_drinks/food/foods/sandwiches.dm
+++ b/code/modules/food_and_drinks/food/foods/sandwiches.dm
@@ -180,7 +180,7 @@
/obj/item/reagent_containers/food/snacks/grilledcheese
name = "grilled cheese sandwich"
desc = "Goes great with tomato soup!"
- icon_state = "toastedsandwich"
+ icon_state = "grilledcheese"
filling_color = "#D9BE29"
list_reagents = list("nutriment" = 7, "vitamin" = 1) //why make a regualr sandwhich when you can make grilled cheese, with this nutriment value?
tastes = list("toast" = 1, "grilled cheese" = 1)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
index 4ec135e9e01..b2443b4d8a7 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
@@ -139,8 +139,8 @@
to_chat(user, "This is not suitable for [src]!")
return
- if(victim.abiotic(1))
- to_chat(user, "Subject may not have abiotic items on.")
+ if(victim.abiotic(TRUE))
+ to_chat(user, "Subject may not have anything on their body.")
return
user.visible_message("[user] starts to put [victim] into [src]!")
diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm
index b42045f86d9..13cce41ed77 100644
--- a/code/modules/hydroponics/gene_modder.dm
+++ b/code/modules/hydroponics/gene_modder.dm
@@ -277,6 +277,13 @@
if(can_insert && istype(disk.gene, /datum/plant_gene/trait))
dat += "Insert: [disk.gene.get_name()]"
dat += ""
+
+ dat += "Variant
"
+ dat += "| [seed.variant ? seed.variant : "None"] | "
+ dat += "Edit | "
+ if(seed.variant)
+ dat += "Remove | "
+ dat += "
"
else
dat += "
No sample found.
Please, insert a plant sample to use this device."
popup.set_content(dat)
@@ -391,6 +398,16 @@
else if(href_list["abort"])
operation = ""
target = null
+ else if(href_list["set_v"])
+ if(!seed)
+ return
+ seed.variant_prompt(usr, src)
+ else if(href_list["del_v"])
+ if(!seed)
+ return
+ seed.variant = null
+ seed.apply_variant_name()
+ to_chat(usr, "You remove the [seed.plantname]'s variant designation.")
interact(usr)
diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm
index a69e2da495e..e5d4f501c5d 100644
--- a/code/modules/hydroponics/grown.dm
+++ b/code/modules/hydroponics/grown.dm
@@ -42,6 +42,8 @@
seed.prepare_result(src)
transform *= TRANSFORM_USING_VARIABLE(seed.potency, 100) + 0.5 //Makes the resulting produce's sprite larger or smaller based on potency!
add_juice()
+ if(seed.variant)
+ name += " \[[seed.variant]]"
/obj/item/reagent_containers/food/snacks/grown/Destroy()
QDEL_NULL(seed)
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index 134e337fa4e..81a4f83e6a6 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -344,7 +344,10 @@
/obj/machinery/hydroponics/examine(user)
. = ..()
if(myseed)
- . += "It has [myseed.plantname] planted."
+ if(myseed.variant)
+ . += "It has the [myseed.variant] variant of [myseed.plantname] planted."
+ else
+ . += "It has [myseed.plantname] planted."
if (dead)
. += "It's dead!"
else if (harvest)
@@ -875,7 +878,8 @@
plant_hud_set_status()
adjustWeeds(-10) //Has a side effect of cleaning up those nasty weeds
update_icon()
-
+ else if(istype(O, /obj/item/pen) && myseed)
+ myseed.variant_prompt(user, src)
else
return ..()
diff --git a/code/modules/hydroponics/seed_extractor.dm b/code/modules/hydroponics/seed_extractor.dm
index 34ec63ebcb7..f35d7751c7a 100644
--- a/code/modules/hydroponics/seed_extractor.dm
+++ b/code/modules/hydroponics/seed_extractor.dm
@@ -109,6 +109,7 @@
/datum/seed_pile
var/name = ""
+ var/variant = ""
var/lifespan = 0 //Saved stats
var/endurance = 0
var/maturation = 0
@@ -117,8 +118,9 @@
var/potency = 0
var/amount = 0
-/datum/seed_pile/New(name, life, endur, matur, prod, yie, poten, am = 1)
+/datum/seed_pile/New(name, variant, life, endur, matur, prod, yie, poten, am = 1)
src.name = name
+ src.variant = variant
src.lifespan = life
src.endurance = endur
src.maturation = matur
@@ -145,9 +147,9 @@
if (contents.len == 0)
dat += "No seeds"
else
- dat += "| Name | Lifespan | Endurance | Maturation | Production | Yield | Potency | Stock |
"
+ dat += "| Name | Variant | Lifespan | Endurance | Maturation | Production | Yield | Potency | Stock |
"
for (var/datum/seed_pile/O in piles)
- dat += "| [O.name] | [O.lifespan] | [O.endurance] | [O.maturation] | "
+ dat += "
| [O.name] | [O.variant] | [O.lifespan] | [O.endurance] | [O.maturation] | "
dat += "[O.production] | [O.yield] | [O.potency] | "
dat += "Vend ([O.amount] left) |
"
dat += "
"
@@ -203,9 +205,9 @@
O.forceMove(src)
. = 1
for (var/datum/seed_pile/N in piles)
- if (O.plantname == N.name && O.lifespan == N.lifespan && O.endurance == N.endurance && O.maturation == N.maturation && O.production == N.production && O.yield == N.yield && O.potency == N.potency)
+ if (O.plantname == N.name && O.variant == N.variant && O.lifespan == N.lifespan && O.endurance == N.endurance && O.maturation == N.maturation && O.production == N.production && O.yield == N.yield && O.potency == N.potency)
++N.amount
return
- piles += new /datum/seed_pile(O.plantname, O.lifespan, O.endurance, O.maturation, O.production, O.yield, O.potency)
+ piles += new /datum/seed_pile(O.plantname, O.variant, O.lifespan, O.endurance, O.maturation, O.production, O.yield, O.potency)
return
diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm
index cd9271379ed..539ab5e7573 100644
--- a/code/modules/hydroponics/seeds.dm
+++ b/code/modules/hydroponics/seeds.dm
@@ -10,6 +10,7 @@
var/plantname = "Plants" // Name of plant when planted.
var/product // A type path. The thing that is created when the plant is harvested.
var/species = "" // Used to update icons. Should match the name in the sprites unless all icon_* are overriden.
+ var/variant = null // Optional custom name to track modified plants. Can be set with pen or from gene modder.
var/growing_icon = 'icons/obj/hydroponics/growing.dmi' //the file that stores the sprites of the growing plant from this seed.
var/icon_grow // Used to override grow icon (default is "[species]-grow"). You can use one grow icon for multiple closely related plants with it.
@@ -83,6 +84,8 @@
S.potency = potency
S.weed_rate = weed_rate
S.weed_chance = weed_chance
+ S.variant = variant
+ S.apply_variant_name()
S.genes = list()
for(var/g in genes)
var/datum/plant_gene/G = g
@@ -317,16 +320,43 @@
return
/obj/item/seeds/attackby(obj/item/O, mob/user, params)
- if (istype(O, /obj/item/plant_analyzer))
+ if(istype(O, /obj/item/plant_analyzer))
to_chat(user, "*---------*\n This is \a [src].")
var/text = get_analyzer_text()
if(text)
to_chat(user, "[text]")
return
+ if(istype(O, /obj/item/pen))
+ variant_prompt(user)
+ return
..() // Fallthrough to item/attackby() so that bags can pick seeds up
+/obj/item/seeds/proc/variant_prompt(mob/user, obj/item/container = null)
+ var/prev = variant
+ var/V = input(user, "Choose variant name:", "Plant Variant Naming", variant) as text|null
+ if(isnull(V)) // Did the user cancel?
+ return
+ if(container && (loc != container)) // Was the seed removed from the container, if there is a container?
+ return
+ if(!(container ? container : src).Adjacent(user)) // Is the user next to the seed/container?
+ return
+ variant = copytext(sanitize(html_encode(trim(V))), 1, 64) // Sanitization must happen after null check because it converts nulls to empty strings
+ if(variant == "")
+ variant = null
+ if(prev != variant)
+ to_chat(user, "You [variant ? "change" : "remove"] the [plantname]'s variant designation.")
+ apply_variant_name()
+
+/obj/item/seeds/proc/apply_variant_name()
+ var/V = variant ? " \[[variant]]" : "" // If we have a non-empty variant add it to the name
+ var/N = initial(name)
+ if(copytext(name, 1, 13) == "experimental") // Don't delete 'experimental'
+ N = "experimental " + N
+ name = N + V
+ if(GetComponent(/datum/component/label))
+ GetComponent(/datum/component/label).apply_label() // Don't delete labels
diff --git a/code/modules/maze_generation/maze_generator.dm b/code/modules/maze_generation/maze_generator.dm
index 96f6392a3f1..339b595f084 100644
--- a/code/modules/maze_generation/maze_generator.dm
+++ b/code/modules/maze_generation/maze_generator.dm
@@ -14,7 +14,7 @@
do { \
var/timer = start_watch(); \
proc2run ;\
- log_debug("\[MAZE] Operation '[opname]' on maze at [##x],[##y],[##z] took [stop_watch(timer)]s"); \
+ log_debug("\[MAZE] Operation '[opname]' on maze at [x],[y],[z] took [stop_watch(timer)]s"); \
} while (FALSE)
diff --git a/code/modules/mining/abandonedcrates.dm b/code/modules/mining/abandonedcrates.dm
index 56c5c820559..f289a2dc8e1 100644
--- a/code/modules/mining/abandonedcrates.dm
+++ b/code/modules/mining/abandonedcrates.dm
@@ -104,21 +104,7 @@
new /obj/item/toy/katana(src)
if(85 to 86)
new /obj/item/defibrillator/compact(src)
- if(87) //1% chance
- new /obj/item/weed_extract(src)
- if(88)
- new /obj/item/organ/internal/brain(src)
- if(89)
- new /obj/item/organ/internal/brain/xeno(src)
- if(90)
- new /obj/item/organ/internal/heart(src)
- if(91)
- new /obj/item/soulstone/anybody(src)
- if(92)
- new /obj/item/katana(src)
- if(93)
- new /obj/item/dnainjector/xraymut(src)
- if(94)
+ if(87 to 88)
new /obj/item/storage/backpack/clown(src)
new /obj/item/clothing/under/rank/clown(src)
new /obj/item/clothing/shoes/clown_shoes(src)
@@ -128,6 +114,18 @@
new /obj/item/toy/crayon/rainbow(src)
new /obj/item/reagent_containers/spray/waterflower(src)
new /obj/item/reagent_containers/food/drinks/bottle/bottleofbanana(src)
+ if(89) //1% chance
+ new /obj/item/weed_extract(src)
+ if(90)
+ new /obj/item/organ/internal/brain(src)
+ if(91)
+ new /obj/item/organ/internal/brain/xeno(src)
+ if(92)
+ new /obj/item/organ/internal/heart(src)
+ if(93)
+ new /obj/item/soulstone/anybody(src)
+ if(94)
+ new /obj/item/katana(src)
if(95)
new /obj/item/clothing/under/mime(src)
new /obj/item/clothing/shoes/black(src)
diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm
index 9e9245d274a..a7dc2c86a9f 100644
--- a/code/modules/mob/dead/observer/say.dm
+++ b/code/modules/mob/dead/observer/say.dm
@@ -19,7 +19,7 @@
log_ghostemote(message, src)
if(src.client)
- if(src.client.prefs.muted & MUTE_DEADCHAT)
+ if(check_mute(client.ckey, MUTE_DEADCHAT))
to_chat(src, "You cannot emote in deadchat (muted).")
return
diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm
index 12312a4fdff..ae6cd715cec 100644
--- a/code/modules/mob/emote.dm
+++ b/code/modules/mob/emote.dm
@@ -112,7 +112,7 @@
O.show_message(message, m_type)
/mob/proc/emote_dead(message)
- if(client.prefs.muted & MUTE_DEADCHAT)
+ if(check_mute(client.ckey, MUTE_DEADCHAT))
to_chat(src, "You cannot send deadchat emotes (muted).")
return
diff --git a/code/modules/mob/living/carbon/alien/larva/emote.dm b/code/modules/mob/living/carbon/alien/larva/emote.dm
index 9871c735725..e2c2a224831 100644
--- a/code/modules/mob/living/carbon/alien/larva/emote.dm
+++ b/code/modules/mob/living/carbon/alien/larva/emote.dm
@@ -14,10 +14,10 @@
if(silent)
return
if(src.client)
- if(client.prefs.muted & MUTE_IC)
+ if(check_mute(client.ckey, MUTE_IC))
to_chat(src, "You cannot send IC messages (muted).")
return
- if(src.client.handle_spam_prevention(message,MUTE_IC))
+ if(src.client.handle_spam_prevention(message, MUTE_IC))
return
if(stat)
return
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 160a9d55288..46a71531dcd 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -1203,6 +1203,14 @@ so that different stomachs can handle things in different ways VB*/
sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
see_in_dark = max(see_in_dark, 8)
+ if(HAS_TRAIT(src, TRAIT_MESON_VISION))
+ sight |= SEE_TURFS
+ lighting_alpha = min(lighting_alpha, LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
+
+ if(HAS_TRAIT(src, TRAIT_NIGHT_VISION))
+ see_in_dark = max(see_in_dark, 8)
+ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
+
SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT)
sync_lighting_plane_alpha()
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index a8a4133310d..d98e00b1e3f 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -446,15 +446,14 @@
SpinAnimation(5,1)
if("spin", "spins")
- if(!restrained() && !lying)
+ if(!incapacitated(ignore_lying = TRUE))
if(prob(5))
- spin(30, 1)
- message = "[src] spins too much!"
+ spin(32, 1)
+ to_chat(src, "You spin too much!")
Dizzy(12)
Confused(12)
else
spin(20, 1)
- message = "[src] spins!"
if("aflap", "aflaps")
if(!restrained())
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 139a3868f1d..0f8d3e2bb63 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -39,7 +39,7 @@ emp_act
if(mind?.martial_art?.deflection_chance) //Some martial arts users can deflect projectiles!
if(!lying && !HAS_TRAIT(src, TRAIT_HULK) && mind.martial_art.try_deflect(src)) //But only if they're not lying down, and hulks can't do it
add_attack_logs(P.firer, src, "hit by [P.type] but got deflected by martial arts '[mind.martial_art]'")
- visible_message("[src] deflects the projectile; [p_they()] can't be hit with ranged weapons!", "You deflect the projectile!")
+ visible_message("[src] deflects the projectile!", "You deflect the projectile!")
playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE)
if(mind.martial_art.reroute_deflection)
P.firer = src
diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm
index 7d1ae80a955..f7cc37fbd8b 100644
--- a/code/modules/mob/living/carbon/human/species/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/_species.dm
@@ -894,13 +894,21 @@ It'll return null if the organ doesn't correspond, so include null checks when u
if(!isnull(H.vision_type.lighting_alpha))
H.lighting_alpha = min(H.vision_type.lighting_alpha, H.lighting_alpha)
- if(HAS_TRAIT(src, TRAIT_THERMAL_VISION))
+ if(HAS_TRAIT(H, TRAIT_MESON_VISION))
+ H.sight |= SEE_TURFS
+ H.lighting_alpha = min(H.lighting_alpha, LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
+
+ if(HAS_TRAIT(H, TRAIT_THERMAL_VISION))
H.sight |= (SEE_MOBS)
H.lighting_alpha = min(H.lighting_alpha, LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
if(HAS_TRAIT(H, TRAIT_XRAY_VISION))
H.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
+ if(HAS_TRAIT(H, TRAIT_NIGHT_VISION))
+ H.see_in_dark = max(H.see_in_dark, 8)
+ H.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
+
if(H.has_status_effect(STATUS_EFFECT_SUMMONEDGHOST))
H.see_invisible = SEE_INVISIBLE_OBSERVER
diff --git a/code/modules/mob/living/carbon/human/species/machine.dm b/code/modules/mob/living/carbon/human/species/machine.dm
index 963b878a758..43c0931f96b 100644
--- a/code/modules/mob/living/carbon/human/species/machine.dm
+++ b/code/modules/mob/living/carbon/human/species/machine.dm
@@ -21,7 +21,7 @@
death_message = "gives a short series of shrill beeps, their chassis shuddering before falling limp, nonfunctional."
death_sounds = list('sound/voice/borg_deathsound.ogg') //I've made this a list in the event we add more sounds for dead robots.
- species_traits = list(IS_WHITELISTED, NO_BLOOD, NO_CLONESCAN, NO_INTORGANS, NOTRANSSTING)
+ species_traits = list(IS_WHITELISTED, NO_BLOOD, NO_CLONESCAN, NO_INTORGANS)
inherent_traits = list(TRAIT_VIRUSIMMUNE, TRAIT_NOBREATH, TRAIT_RADIMMUNE, TRAIT_NOGERMS, TRAIT_NODECAY, TRAIT_NOPAIN, TRAIT_GENELESS) //Computers that don't decay? What a lie!
inherent_biotypes = MOB_ROBOTIC | MOB_HUMANOID
clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
diff --git a/code/modules/mob/living/carbon/human/species/plasmaman.dm b/code/modules/mob/living/carbon/human/species/plasmaman.dm
index 926ed17348e..4a49b965fb8 100644
--- a/code/modules/mob/living/carbon/human/species/plasmaman.dm
+++ b/code/modules/mob/living/carbon/human/species/plasmaman.dm
@@ -5,7 +5,7 @@
dangerous_existence = TRUE //So so much
//language = "Clatter"
- species_traits = list(IS_WHITELISTED, NO_BLOOD, NOTRANSSTING, NO_HAIR)
+ species_traits = list(IS_WHITELISTED, NO_BLOOD, NO_HAIR)
inherent_traits = list(TRAIT_RADIMMUNE, TRAIT_NOHUNGER)
inherent_biotypes = MOB_HUMANOID | MOB_MINERAL
forced_heartattack = TRUE // Plasmamen have no blood, but they should still get heart-attacks
diff --git a/code/modules/mob/living/carbon/human/species/vox.dm b/code/modules/mob/living/carbon/human/species/vox.dm
index e7736da2291..377da561e5a 100644
--- a/code/modules/mob/living/carbon/human/species/vox.dm
+++ b/code/modules/mob/living/carbon/human/species/vox.dm
@@ -22,7 +22,7 @@
eyes = "vox_eyes_s"
- species_traits = list(NO_CLONESCAN, IS_WHITELISTED, NOTRANSSTING)
+ species_traits = list(NO_CLONESCAN, IS_WHITELISTED)
inherent_traits = list(TRAIT_NOGERMS, TRAIT_NODECAY)
clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS //Species-fitted 'em all.
dietflags = DIET_OMNI
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index bd0aad32dac..148e73e873f 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -760,7 +760,7 @@
//called when the mob receives a bright flash
/mob/living/proc/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash)
- if(check_eye_prot() < intensity && (override_blindness_check || !HAS_TRAIT(src, TRAIT_BLIND)))
+ if(check_eye_prot() < intensity && (override_blindness_check || !HAS_TRAIT(src, TRAIT_BLIND)) && !HAS_TRAIT(src, TRAIT_FLASH_PROTECTION))
overlay_fullscreen("flash", type)
addtimer(CALLBACK(src, .proc/clear_fullscreen, "flash", 25), 25)
return 1
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index 43d1c402fd0..0ac3b497530 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -110,7 +110,7 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key)
/mob/living/say(message, verb = "says", sanitize = TRUE, ignore_speech_problems = FALSE, ignore_atmospherics = FALSE, ignore_languages = FALSE)
if(client)
- if(client.prefs.muted & MUTE_IC)
+ if(check_mute(client.ckey, MUTE_IC))
to_chat(src, "You cannot speak in IC (Muted).")
return
@@ -297,7 +297,7 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key)
/mob/living/emote(act, type, message, force) //emote code is terrible, this is so that anything that isn't already snowflaked to shit can call the parent and handle emoting sanely
if(client)
- if(client.prefs.muted & MUTE_IC)
+ if(check_mute(client.ckey, MUTE_IC))
to_chat(src, "You cannot speak in IC (Muted).")
return
@@ -352,7 +352,7 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key)
/mob/living/proc/whisper_say(list/message_pieces, verb = "whispers")
if(client)
- if(client.prefs.muted & MUTE_IC)
+ if(check_mute(client.ckey, MUTE_IC))
to_chat(src, "You cannot speak in IC (Muted).")
return
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 6c646d2509e..5d20cb66209 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -212,6 +212,20 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
GLOB.shuttle_caller_list += src
..()
+/mob/living/silicon/ai/Destroy()
+ GLOB.ai_list -= src
+ GLOB.shuttle_caller_list -= src
+ SSshuttle.autoEvac()
+ if(malfhacking)
+ deltimer(malfhacking)
+ malfhacking = null
+ QDEL_NULL(eyeobj) // No AI, no Eye
+ QDEL_NULL(aiPDA)
+ QDEL_NULL(aiMulti)
+ QDEL_NULL(aiRadio)
+ QDEL_NULL(builtInCamera)
+ return ..()
+
/mob/living/silicon/ai/proc/on_mob_init()
to_chat(src, "You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras).")
to_chat(src, "To look at other parts of the station, click on yourself to get a camera menu.")
@@ -314,17 +328,6 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
return TRUE
-/mob/living/silicon/ai/Destroy()
- GLOB.ai_list -= src
- GLOB.shuttle_caller_list -= src
- SSshuttle.autoEvac()
- QDEL_NULL(eyeobj) // No AI, no Eye
- if(malfhacking)
- deltimer(malfhacking)
- malfhacking = null
- malfhack = null
- return ..()
-
/*
The AI Power supply is a dummy object used for powering the AI since only machinery should be using power.
diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm
index 717f9700316..30bd84c48fc 100644
--- a/code/modules/mob/living/silicon/ai/death.dm
+++ b/code/modules/mob/living/silicon/ai/death.dm
@@ -27,7 +27,7 @@
if(SSshuttle.emergency.mode == SHUTTLE_STRANDED)
SSshuttle.emergency.mode = SHUTTLE_DOCKED
SSshuttle.emergency.timer = world.time
- GLOB.priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg')
+ GLOB.priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/eshuttle_dock.ogg')
qdel(doomsday_device)
if(explosive)
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 21d9e75db90..5748775558a 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -1147,55 +1147,18 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
cell = null
qdel(src)
-#define BORG_CAMERA_BUFFER 30
-/mob/living/silicon/robot/Move(a, b, flag)
- var/oldLoc = src.loc
+#define BORG_CAMERA_BUFFER 3 SECONDS
+
+/mob/living/silicon/robot/Move(atom/newloc, direct, movetime)
+ var/oldLoc = loc
. = ..()
- if(.)
- if(src.camera)
- if(!updating)
- updating = 1
- spawn(BORG_CAMERA_BUFFER)
- if(camera && oldLoc != src.loc)
- GLOB.cameranet.updatePortableCamera(src.camera)
- updating = 0
- if(module)
- if(module.type == /obj/item/robot_module/janitor)
- var/turf/tile = loc
- if(stat != DEAD && isturf(tile))
- var/floor_only = TRUE
- for(var/A in tile)
- if(istype(A, /obj/effect))
- var/obj/effect/E = A
- if(E.is_cleanable())
- var/obj/effect/decal/cleanable/blood/B = E
- if(istype(B) && B.off_floor)
- floor_only = FALSE
- else
- qdel(E)
- else if(istype(A, /obj/item))
- var/obj/item/cleaned_item = A
- cleaned_item.clean_blood()
- else if(istype(A, /mob/living/carbon/human))
- var/mob/living/carbon/human/cleaned_human = A
- if(cleaned_human.lying)
- if(cleaned_human.head)
- cleaned_human.head.clean_blood()
- cleaned_human.update_inv_head()
- if(cleaned_human.wear_suit)
- cleaned_human.wear_suit.clean_blood()
- cleaned_human.update_inv_wear_suit()
- else if(cleaned_human.w_uniform)
- cleaned_human.w_uniform.clean_blood()
- cleaned_human.update_inv_w_uniform()
- if(cleaned_human.shoes)
- cleaned_human.shoes.clean_blood()
- cleaned_human.update_inv_shoes()
- cleaned_human.clean_blood()
- to_chat(cleaned_human, "[src] cleans your face!")
- if(floor_only)
- tile.clean_blood()
- return
+ if(. && !updating && camera)
+ updating = TRUE
+ spawn(BORG_CAMERA_BUFFER)
+ if(camera && oldLoc != loc)
+ GLOB.cameranet.updatePortableCamera(camera)
+ updating = FALSE
+
#undef BORG_CAMERA_BUFFER
/mob/living/silicon/robot/proc/self_destruct()
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 8554068dce8..8a43a2a011e 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -447,6 +447,43 @@
/obj/item/extinguisher/mini
)
+/obj/item/robot_module/janitor/Initialize(mapload)
+ . = ..()
+ var/mob/living/silicon/robot/R = loc
+ RegisterSignal(R, COMSIG_MOVABLE_MOVED, .proc/on_cyborg_move)
+
+/**
+ * Proc called after the janitor cyborg has moved, in order to clean atoms at it's new location.
+ *
+ * Arguments:
+ * * mob/living/silicon/robot/R - The cyborg who moved.
+ */
+/obj/item/robot_module/janitor/proc/on_cyborg_move(mob/living/silicon/robot/R)
+ SIGNAL_HANDLER
+
+ if(R.stat == DEAD || !isturf(R.loc))
+ return
+ var/turf/tile = R.loc
+ for(var/A in tile)
+ if(iseffect(A))
+ var/obj/effect/E = A
+ if(!E.is_cleanable())
+ continue
+ var/obj/effect/decal/cleanable/blood/B = E
+ if(istype(B) && B.off_floor)
+ tile.clean_blood()
+ else
+ qdel(E)
+ else if(isitem(A))
+ var/obj/item/I = A
+ I.clean_blood()
+ else if(ishuman(A))
+ var/mob/living/carbon/human/cleaned_human = A
+ if(!cleaned_human.lying)
+ continue
+ cleaned_human.clean_blood()
+ to_chat(cleaned_human, "[src] cleans your face!")
+
/obj/item/reagent_containers/spray/cyborg_lube
name = "Lube spray"
list_reagents = list("lube" = 250)
diff --git a/code/modules/mob/living/simple_animal/hostile/bat.dm b/code/modules/mob/living/simple_animal/hostile/bat.dm
index 1cc904f1017..9e5ae1e2ec1 100644
--- a/code/modules/mob/living/simple_animal/hostile/bat.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bat.dm
@@ -33,27 +33,16 @@
// break_stuff_probability = 2
faction = list("scarybat")
- var/mob/living/owner
gold_core_spawnable = HOSTILE_SPAWN
-/mob/living/simple_animal/hostile/scarybat/New(loc, mob/living/L as mob)
- ..()
+/mob/living/simple_animal/hostile/scarybat/Initialize(mapload, mob/living/L)
+ . = ..()
if(istype(L))
- owner = L
+ faction += "\ref[L]"
/mob/living/simple_animal/hostile/scarybat/Process_Spacemove(check_drift = 0)
return ..() //No drifting in space for space carp! //original comments do not steal
-/mob/living/simple_animal/hostile/scarybat/Found(atom/A)//This is here as a potential override to pick a specific target if available
- if(istype(A) && A == owner)
- return 0
- return ..()
-
-/mob/living/simple_animal/hostile/scarybat/CanAttack(atom/the_target)//This is here as a potential override to pick a specific target if available
- if(istype(the_target) && the_target == owner)
- return 0
- return ..()
-
/mob/living/simple_animal/hostile/scarybat/AttackingTarget()
. =..()
var/mob/living/L = .
diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm
index 0370a84baba..63a883b4d00 100644
--- a/code/modules/mob/living/simple_animal/hostile/bees.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bees.dm
@@ -31,7 +31,6 @@
move_to_delay = 0
obj_damage = 0
environment_smash = 0
- mouse_opacity = MOUSE_OPACITY_OPAQUE
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
density = FALSE
mob_size = MOB_SIZE_TINY
@@ -51,6 +50,8 @@
var/isqueen = FALSE
var/bee_syndicate = FALSE
var/icon_base = "bee"
+ var/last_attack = 0
+ var/opacity_time = 1 MINUTES
var/static/list/bee_icons = list()
var/static/beehometypecache = typecacheof(/obj/structure/beebox)
var/static/hydroponicstypecache = typecacheof(/obj/machinery/hydroponics)
@@ -162,6 +163,7 @@
else
. = ..()
if(. && isliving(target) && (!client || a_intent == INTENT_HARM))
+ make_opaque()
var/mob/living/L = target
if(L.reagents)
if(beegent)
@@ -170,6 +172,23 @@
else
L.reagents.add_reagent("spidertoxin", 5)
+/mob/living/simple_animal/hostile/poison/bees/proc/make_opaque()
+ // If a bee attacks someone, make it very easy to hit for a while
+ last_attack = world.time
+ mouse_opacity = MOUSE_OPACITY_OPAQUE
+
+/mob/living/simple_animal/hostile/poison/bees/Life(seconds, times_fired)
+ . = ..()
+ if(mind || (mouse_opacity == initial(mouse_opacity)))
+ return
+ var/diff = world.time - last_attack
+ if(diff >= opacity_time)
+ mouse_opacity = initial(mouse_opacity)
+
+/mob/living/simple_animal/hostile/poison/bees/sentience_act()
+ . = ..()
+ make_opaque()
+
/mob/living/simple_animal/hostile/poison/bees/proc/assign_reagent(datum/reagent/R)
if(istype(R))
beegent = R
@@ -233,6 +252,7 @@
desc = "She's the queen of bees, BZZ BZZ"
icon_base = "queen"
isqueen = TRUE
+ mouse_opacity = MOUSE_OPACITY_OPAQUE
//the Queen doesn't leave the box on her own, and she CERTAINLY doesn't pollinate by herself
@@ -324,6 +344,7 @@
search_objects = FALSE //these bees don't care about trivial things like plants, especially when there is havoc to sow
bee_syndicate = TRUE
var/list/master_and_friends = list()
+ mouse_opacity = MOUSE_OPACITY_OPAQUE
/mob/living/simple_animal/hostile/poison/bees/syndi/New()
beegent = GLOB.chemical_reagents_list["facid"] //Prepare to die
diff --git a/code/modules/mob/living/simple_animal/hostile/netherworld.dm b/code/modules/mob/living/simple_animal/hostile/netherworld.dm
index 4c3635e406a..9d2ddae7a65 100644
--- a/code/modules/mob/living/simple_animal/hostile/netherworld.dm
+++ b/code/modules/mob/living/simple_animal/hostile/netherworld.dm
@@ -33,7 +33,7 @@
/mob/living/simple_animal/hostile/netherworld/migo/Initialize(mapload)
. = ..()
- migo_sounds = list('sound/items/bubblewrap.ogg', 'sound/items/change_jaws.ogg', 'sound/items/crowbar.ogg', 'sound/items/drink.ogg', 'sound/items/deconstruct.ogg', 'sound/items/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/screwdriver.ogg', 'sound/items/weeoo1.ogg', 'sound/items/wirecutter.ogg', 'sound/items/welder.ogg', 'sound/items/zip.ogg', 'sound/items/rped.ogg', 'sound/items/ratchet.ogg', 'sound/items/polaroid1.ogg', 'sound/items/pshoom.ogg', 'sound/items/airhorn.ogg', 'sound/voice/bcreep.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/ed209_20sec.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss6.ogg', 'sound/voice/mpatchedup.ogg', 'sound/voice/mfeelbetter.ogg', 'sound/weapons/sear.ogg', 'sound/ambience/antag/tatoralert.ogg', 'sound/mecha/nominal.ogg', 'sound/mecha/weapdestr.ogg', 'sound/mecha/critdestr.ogg', 'sound/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', 'sound/effects/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles.ogg', 'sound/effects/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clownstep2.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', 'sound/effects/empulse.ogg', 'sound/effects/explosionfar.ogg', 'sound/effects/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', 'sound/effects/heartbeat.ogg', 'sound/effects/hyperspace_begin.ogg', 'sound/effects/hyperspace_end.ogg', 'sound/goonstation/effects/screech.ogg', 'sound/effects/phasein.ogg', 'sound/effects/picaxe1.ogg', 'sound/effects/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', 'sound/effects/supermatter.ogg', 'sound/misc/desceration-01.ogg', 'sound/misc/desceration-02.ogg', 'sound/misc/desceration-03.ogg', 'sound/misc/bloblarm.ogg', 'sound/goonstation/misc/airraid_loop.ogg', 'sound/misc/interference.ogg', 'sound/misc/notice1.ogg', 'sound/misc/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', 'sound/weapons/armbomb.ogg', 'sound/weapons/chainsaw.ogg', 'sound/weapons/emitter.ogg', 'sound/weapons/emitter2.ogg', 'sound/weapons/blade1.ogg', 'sound/weapons/bladeslice.ogg', 'sound/weapons/blastcannon.ogg', 'sound/weapons/blaster.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/weapons/circsawhit.ogg', 'sound/weapons/cqchit2.ogg', 'sound/weapons/drill.ogg', 'sound/weapons/genhit1.ogg', 'sound/weapons/gunshots/gunshot_silenced.ogg', 'sound/weapons/gunshots/gunshot.ogg', 'sound/weapons/handcuffs.ogg', 'sound/weapons/homerun.ogg', 'sound/weapons/kenetic_accel.ogg', 'sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock_alien_prying.ogg', 'sound/machines/airlock_close.ogg', 'sound/machines/airlockforced.ogg', 'sound/machines/airlock_open.ogg', 'sound/machines/alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/boltsdown.ogg', 'sound/machines/boltsup.ogg', 'sound/machines/buzz-sigh.ogg', 'sound/machines/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/defib_charge.ogg', 'sound/machines/defib_failed.ogg', 'sound/machines/defib_ready.ogg', 'sound/machines/defib_zap.ogg', 'sound/machines/deniedbeep.ogg', 'sound/machines/ding.ogg', 'sound/machines/disposalflush.ogg', 'sound/machines/door_close.ogg', 'sound/machines/door_open.ogg', 'sound/machines/engine_alert1.ogg', 'sound/machines/engine_alert2.ogg', 'sound/machines/hiss.ogg', 'sound/machines/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/ambience/signal.ogg', 'sound/machines/synth_no.ogg', 'sound/machines/synth_yes.ogg', 'sound/machines/terminal_alert.ogg', 'sound/machines/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', 'sound/ai/outbreak5.ogg', 'sound/ai/outbreak7.ogg', 'sound/ai/poweroff.ogg', 'sound/ai/radiation.ogg', 'sound/ai/shuttlecalled.ogg', 'sound/ai/shuttledock.ogg', 'sound/ai/shuttlerecalled.ogg', 'sound/ai/aimalf.ogg', 'sound/ambience/ambigen1.ogg', 'sound/ambience/ambigen3.ogg', 'sound/ambience/ambigen4.ogg', 'sound/ambience/ambigen5.ogg', 'sound/ambience/ambigen6.ogg', 'sound/ambience/ambigen10.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg') //hahahaha fuck you code divers
+ migo_sounds = list('sound/items/bubblewrap.ogg', 'sound/items/change_jaws.ogg', 'sound/items/crowbar.ogg', 'sound/items/drink.ogg', 'sound/items/deconstruct.ogg', 'sound/items/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/screwdriver.ogg', 'sound/items/weeoo1.ogg', 'sound/items/wirecutter.ogg', 'sound/items/welder.ogg', 'sound/items/zip.ogg', 'sound/items/rped.ogg', 'sound/items/ratchet.ogg', 'sound/items/polaroid1.ogg', 'sound/items/pshoom.ogg', 'sound/items/airhorn.ogg', 'sound/voice/bcreep.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/ed209_20sec.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss6.ogg', 'sound/voice/mpatchedup.ogg', 'sound/voice/mfeelbetter.ogg', 'sound/weapons/sear.ogg', 'sound/ambience/antag/tatoralert.ogg', 'sound/mecha/nominal.ogg', 'sound/mecha/weapdestr.ogg', 'sound/mecha/critdestr.ogg', 'sound/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', 'sound/effects/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles.ogg', 'sound/effects/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clownstep2.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', 'sound/effects/empulse.ogg', 'sound/effects/explosionfar.ogg', 'sound/effects/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', 'sound/effects/heartbeat.ogg', 'sound/effects/hyperspace_begin.ogg', 'sound/effects/hyperspace_end.ogg', 'sound/goonstation/effects/screech.ogg', 'sound/effects/phasein.ogg', 'sound/effects/picaxe1.ogg', 'sound/effects/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', 'sound/effects/supermatter.ogg', 'sound/misc/desceration-01.ogg', 'sound/misc/desceration-02.ogg', 'sound/misc/desceration-03.ogg', 'sound/misc/bloblarm.ogg', 'sound/goonstation/misc/airraid_loop.ogg', 'sound/misc/interference.ogg', 'sound/misc/notice1.ogg', 'sound/misc/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', 'sound/weapons/armbomb.ogg', 'sound/weapons/chainsaw.ogg', 'sound/weapons/emitter.ogg', 'sound/weapons/emitter2.ogg', 'sound/weapons/blade1.ogg', 'sound/weapons/bladeslice.ogg', 'sound/weapons/blastcannon.ogg', 'sound/weapons/blaster.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/weapons/circsawhit.ogg', 'sound/weapons/cqchit2.ogg', 'sound/weapons/drill.ogg', 'sound/weapons/genhit1.ogg', 'sound/weapons/gunshots/gunshot_silenced.ogg', 'sound/weapons/gunshots/gunshot.ogg', 'sound/weapons/handcuffs.ogg', 'sound/weapons/homerun.ogg', 'sound/weapons/kenetic_accel.ogg', 'sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock_alien_prying.ogg', 'sound/machines/airlock_close.ogg', 'sound/machines/airlockforced.ogg', 'sound/machines/airlock_open.ogg', 'sound/machines/alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/boltsdown.ogg', 'sound/machines/boltsup.ogg', 'sound/machines/buzz-sigh.ogg', 'sound/machines/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/defib_charge.ogg', 'sound/machines/defib_failed.ogg', 'sound/machines/defib_ready.ogg', 'sound/machines/defib_zap.ogg', 'sound/machines/deniedbeep.ogg', 'sound/machines/ding.ogg', 'sound/machines/disposalflush.ogg', 'sound/machines/door_close.ogg', 'sound/machines/door_open.ogg', 'sound/machines/engine_alert1.ogg', 'sound/machines/engine_alert2.ogg', 'sound/machines/hiss.ogg', 'sound/machines/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/ambience/signal.ogg', 'sound/machines/synth_no.ogg', 'sound/machines/synth_yes.ogg', 'sound/machines/terminal_alert.ogg', 'sound/machines/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', 'sound/ai/outbreak5.ogg', 'sound/ai/outbreak7.ogg', 'sound/ai/alert.ogg', 'sound/ai/radiation.ogg', 'sound/ai/eshuttle_call.ogg', 'sound/ai/eshuttle_dock.ogg', 'sound/ai/eshuttle_recall.ogg', 'sound/ai/aimalf.ogg', 'sound/ambience/ambigen1.ogg', 'sound/ambience/ambigen3.ogg', 'sound/ambience/ambigen4.ogg', 'sound/ambience/ambigen5.ogg', 'sound/ambience/ambigen6.ogg', 'sound/ambience/ambigen10.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg') //hahahaha fuck you code divers
/mob/living/simple_animal/hostile/netherworld/migo/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
..()
diff --git a/code/modules/mob/living/simple_animal/slime/emote.dm b/code/modules/mob/living/simple_animal/slime/emote.dm
index 0eceec7a124..14ce0de54c8 100644
--- a/code/modules/mob/living/simple_animal/slime/emote.dm
+++ b/code/modules/mob/living/simple_animal/slime/emote.dm
@@ -19,10 +19,10 @@
if(silent)
return
if(src.client)
- if(client.prefs.muted & MUTE_IC)
+ if(check_mute(client.ckey, MUTE_IC))
to_chat(src, "You cannot send IC messages (muted).")
return
- if(src.client.handle_spam_prevention(message,MUTE_IC))
+ if(src.client.handle_spam_prevention(message, MUTE_IC))
return
if(stat)
return
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index f801c0f072f..c6cceea3842 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1351,17 +1351,21 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
return FALSE //overridden in living.dm
/mob/proc/spin(spintime, speed)
- set waitfor = 0
+ set waitfor = FALSE
if(!spintime || !speed || spintime > 100)
CRASH("Aborted attempted call of /mob/proc/spin with invalid args ([spintime],[speed]) which could have frozen the server.")
- var/end_time = world.time + spintime
- var/spin_dir = prob(50)
- while(world.time <= end_time)
+ while(spintime >= speed)
sleep(speed)
- if(spin_dir)
- dir = turn(dir, 90)
- else
- dir = turn(dir, -90)
+ switch(dir)
+ if(NORTH)
+ setDir(EAST)
+ if(SOUTH)
+ setDir(WEST)
+ if(EAST)
+ setDir(SOUTH)
+ if(WEST)
+ setDir(NORTH)
+ spintime -= speed
/mob/proc/is_literate()
return FALSE
@@ -1479,7 +1483,7 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
MA.plane = GAME_PLANE
pic.appearance = MA
flick_overlay(pic, list(client), 10)
-
+
GLOBAL_LIST_INIT(holy_areas, typecacheof(list(
/area/chapel
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index e199e81eb69..10d56a99dc0 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -51,7 +51,7 @@
to_chat(src, "Deadchat is globally muted.")
return
- if(client.prefs.muted & MUTE_DEADCHAT)
+ if(check_mute(client.ckey, MUTE_DEADCHAT))
to_chat(src, "You cannot talk in deadchat (muted).")
return
diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm
index 76a5dd4dc03..a3a4d0c58c4 100644
--- a/code/modules/paperwork/faxmachine.dm
+++ b/code/modules/paperwork/faxmachine.dm
@@ -40,11 +40,15 @@ GLOBAL_LIST_EMPTY(fax_blacklist)
/// Target department to send outgoing faxes to
var/destination
-/obj/machinery/photocopier/faxmachine/New()
- ..()
+/obj/machinery/photocopier/faxmachine/Initialize(mapload)
+ . = ..()
GLOB.allfaxes += src
update_network()
+/obj/machinery/photocopier/faxmachine/Destroy()
+ GLOB.allfaxes -= src
+ return ..()
+
/obj/machinery/photocopier/faxmachine/proc/update_network()
if(department != "Unknown")
if(!(("[department]" in GLOB.alldepartments) || ("[department]" in GLOB.hidden_departments) || ("[department]" in GLOB.admin_departments) || ("[department]" in GLOB.hidden_admin_departments)))
diff --git a/code/modules/pda/mob_hunt_game_app.dm b/code/modules/pda/mob_hunt_game_app.dm
index 5db744c37ef..fe4ef7962f3 100644
--- a/code/modules/pda/mob_hunt_game_app.dm
+++ b/code/modules/pda/mob_hunt_game_app.dm
@@ -28,6 +28,11 @@
disconnect("Program Terminated")
STOP_PROCESSING(SSobj, pda)
+/datum/data/pda/app/mob_hunter_game/Destroy()
+ STOP_PROCESSING(SSobj, pda)
+ SSmob_hunt.connected_clients -= src
+ return ..()
+
/datum/data/pda/app/mob_hunter_game/proc/scan_nearby()
if(!SSmob_hunt || !connected)
return
@@ -74,13 +79,14 @@
return
scan_nearby()
-/datum/data/pda/app/mob_hunter_game/proc/register_capture(datum/mob_hunt/captured, wild = 0)
+/datum/data/pda/app/mob_hunter_game/proc/register_capture(datum/mob_hunt/captured, wild = FALSE)
if(!captured)
- return 0
- my_collection.Add(captured)
+ return FALSE
+ my_collection += captured
+ RegisterSignal(captured, COMSIG_PARENT_QDELETING, .proc/remove_mob)
if(wild)
wild_captures++
- return 1
+ return TRUE
/datum/data/pda/app/mob_hunter_game/update_ui(mob/user, list/data)
if(!SSmob_hunt || !(src in SSmob_hunt.connected_clients))
@@ -138,12 +144,27 @@
card.forceMove(get_turf(pda))
remove_mob()
-/datum/data/pda/app/mob_hunter_game/proc/remove_mob()
- if(!my_collection.len)
+/**
+ * Removes a Nanomob from the [my_collection] list.
+ *
+ * The Nanomob that is currently selected in the app ([current_index]) will be removed from the list unless a `mob_override` argument is given, in which case that will be removed instead.
+ *
+ * Arguments:
+ * * mob_override - A specific Nanomob to remove from the list. (Optional)
+ */
+/datum/data/pda/app/mob_hunter_game/proc/remove_mob(datum/mob_hunt/mob_override = null)
+ SIGNAL_HANDLER
+ if(!length(my_collection))
return
- my_collection.Remove(my_collection[current_index])
- if(current_index > my_collection.len)
- current_index = my_collection.len
+
+ if(mob_override)
+ my_collection -= mob_override
+ else
+ my_collection -= my_collection[current_index]
+
+ var/collection_length = length(my_collection)
+ if(current_index > collection_length)
+ current_index = collection_length
/datum/data/pda/app/mob_hunter_game/proc/set_trap()
if(!my_collection.len || !pda || !hacked)
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index fcccefbf513..db7152b3505 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -35,6 +35,7 @@ By design, d1 is the smallest direction and d2 is the highest
icon_state = "0-1"
var/d1 = 0
var/d2 = 1
+ plane = FLOOR_PLANE
layer = WIRE_LAYER //Just below unary stuff, which is at 2.45 and above pipes, which are at 2.4
color = COLOR_RED
@@ -68,9 +69,11 @@ By design, d1 is the smallest direction and d2 is the highest
d2 = text2num(copytext( icon_state, dash+1 ))
var/turf/T = get_turf(src) // hide if turf is not intact
+ LAZYADD(GLOB.cable_list, src) //add it to the global cable list
+ if(T.transparent_floor)
+ return
if(level == 1)
hide(T.intact)
- LAZYADD(GLOB.cable_list, src) //add it to the global cable list
/obj/structure/cable/Destroy() // called when a cable is deleted
if(powernet)
@@ -162,7 +165,8 @@ By design, d1 is the smallest direction and d2 is the highest
//
/obj/structure/cable/attackby(obj/item/W, mob/user)
var/turf/T = get_turf(src)
- if(T.intact)
+ if(T.transparent_floor || T.intact)
+ to_chat(user, "You can't interact with something that's under the floor!")
return
else if(istype(W, /obj/item/stack/cable_coil))
@@ -204,7 +208,8 @@ By design, d1 is the smallest direction and d2 is the highest
/obj/structure/cable/wirecutter_act(mob/user, obj/item/I)
. = TRUE
var/turf/T = get_turf(src)
- if(T.intact)
+ if(T.transparent_floor || T.intact)
+ to_chat(user, "You can't interact with something that's under the floor!")
return
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
return
@@ -703,7 +708,7 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe/cable_restrain
var/turf/T = get_turf(C)
- if(!isturf(T) || T.intact) // sanity checks, also stop use interacting with T-scanner revealed cable
+ if(!isturf(T) || T.intact || T.transparent_floor) // sanity checks, also stop use interacting with T-scanner revealed cable
return
if(get_dist(C, user) > 1) // make sure it's close enough
@@ -719,7 +724,7 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe/cable_restrain
// one end of the clicked cable is pointing towards us
if(C.d1 == dirn || C.d2 == dirn)
- if(U.intact) // can't place a cable if the floor is complete
+ if(U.intact || U.transparent_floor) // can't place a cable if the floor is complete
to_chat(user, "You can't lay cable there unless the floor tiles are removed!")
return
else
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index 38575891d07..eae1047d0e0 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -284,6 +284,12 @@
charge = 0
update_icon()
+/obj/item/stock_parts/cell/bluespace/charging
+ name = "self-charging bluespace power cell"
+ desc = "An experimental, self-charging, transdimensional power cell."
+ origin_tech = "powerstorage=10;bluespace=10"
+ self_recharge = TRUE
+
/obj/item/stock_parts/cell/infinite
name = "infinite-capacity power cell!"
icon_state = "icell"
@@ -294,7 +300,7 @@
chargerate = 30000
/obj/item/stock_parts/cell/infinite/use()
- return 1
+ return TRUE
/obj/item/stock_parts/cell/infinite/abductor
name = "void core"
diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm
index 6c3ac951b12..172836f6cdd 100644
--- a/code/modules/power/solar.dm
+++ b/code/modules/power/solar.dm
@@ -352,8 +352,6 @@
overlays += "[icon_state]_broken"
else
overlays += icon_screen
- if(cdir > -1)
- overlays += image('icons/obj/computer.dmi', "solcon-o", FLY_LAYER, angle2dir(cdir))
/obj/machinery/power/solar_control/attack_ai(mob/user as mob)
add_hiddenprint(user)
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index 996817666ca..e2ac3506855 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -221,7 +221,7 @@
return
var/mob/living/carbon/human/H = user
var/immune = istype(H.glasses, /obj/item/clothing/glasses/meson)
- if(!immune && (get_dist(user, src) < HALLUCINATION_RANGE(power)))
+ if(!immune && !HAS_TRAIT(H, TRAIT_MESON_VISION) && (get_dist(user, src) < HALLUCINATION_RANGE(power)))
. += "You get headaches just from looking at it."
/obj/machinery/power/supermatter_crystal/detailed_examine()
@@ -537,7 +537,7 @@
//Makes em go mad and accumulate rads.
for(var/mob/living/carbon/human/l in view(src, HALLUCINATION_RANGE(power))) // If they can see it without mesons on. Bad on them.
- if(!istype(l.glasses, /obj/item/clothing/glasses/meson))
+ if(!istype(l.glasses, /obj/item/clothing/glasses/meson) && !HAS_TRAIT(l, TRAIT_MESON_VISION))
var/D = sqrt(1 / max(1, get_dist(l, src)))
l.hallucination += power * hallucination_power * D
l.hallucination = clamp(l.hallucination, 0, 200)
diff --git a/code/modules/power/terminal.dm b/code/modules/power/terminal.dm
index 584079b3c45..d8fc709a656 100644
--- a/code/modules/power/terminal.dm
+++ b/code/modules/power/terminal.dm
@@ -15,6 +15,8 @@
/obj/machinery/power/terminal/Initialize(mapload)
. = ..()
var/turf/T = get_turf(src)
+ if(T.transparent_floor)
+ return
if(level == 1)
hide(T.intact)
diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm
index 06e522733e2..ec854602ab9 100644
--- a/code/modules/projectiles/guns/magic/staff.dm
+++ b/code/modules/projectiles/guns/magic/staff.dm
@@ -53,7 +53,7 @@
fire_sound = 'sound/magic/staff_door.ogg'
/obj/item/gun/magic/staff/slipping
- name = "wand of slipping"
+ name = "staff of slipping"
desc = "An artefact that spits... bananas?"
ammo_type = /obj/item/ammo_casing/magic/slipping
icon_state = "staffofslipping"
diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm
index 08e7b79ac33..5e0f6ffcf09 100644
--- a/code/modules/projectiles/guns/magic/wand.dm
+++ b/code/modules/projectiles/guns/magic/wand.dm
@@ -51,7 +51,7 @@
user.visible_message("[user] zaps [user.p_them()]self with [src].")
playsound(user, fire_sound, 50, 1)
user.create_attack_log("[key_name(user)] zapped [user.p_them()]self with a [src]")
- add_attack_logs(null, user, "zapped [user.p_them()]self with a [src]", ATKLOG_ALL)
+ add_attack_logs(user, user, "zapped [user.p_them()]self with a [src]", ATKLOG_ALL)
/////////////////////////////////////
//WAND OF DEATH
@@ -182,7 +182,7 @@
desc = "This wand shoots... banana peels?"
fire_sound = 'sound/items/bikehorn.ogg'
ammo_type = /obj/item/ammo_casing/magic/slipping
- icon_state = "staffofslipping"
+ icon_state = "wandofslipping"
max_charges = 5 //5, 4, 3, 2
/obj/item/gun/magic/wand/slipping/zap_self(mob/living/user)
diff --git a/code/modules/projectiles/guns/medbeam.dm b/code/modules/projectiles/guns/medbeam.dm
index 452023946ae..459c6681aa2 100644
--- a/code/modules/projectiles/guns/medbeam.dm
+++ b/code/modules/projectiles/guns/medbeam.dm
@@ -81,7 +81,7 @@
if(!istype(user_turf))
return 0
var/obj/dummy = new(user_turf)
- dummy.pass_flags |= PASSTABLE & PASSGLASS & PASSGRILLE //Grille/Glass so it can be used through common windows
+ dummy.pass_flags |= PASSTABLE & PASSGLASS & PASSGRILLE & PASSFENCE //Grille/Glass so it can be used through common windows
for(var/turf/turf in getline(user_turf,target))
if(turf.density)
qdel(dummy)
diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm
index b8e1348fb43..1be7bc9d95c 100644
--- a/code/modules/projectiles/projectile/beams.dm
+++ b/code/modules/projectiles/projectile/beams.dm
@@ -142,8 +142,8 @@
/obj/item/projectile/beam/immolator/on_hit(atom/target, blocked = 0)
. = ..()
- if(istype(target, /mob/living/carbon))
- var/mob/living/carbon/M = target
+ if(isliving(target))
+ var/mob/living/M = target
M.adjust_fire_stacks(1)
M.IgniteMob()
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index 512f9188606..19efbbc35d5 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -275,7 +275,7 @@
return
M.create_attack_log("[key_name(M)] became [new_mob.real_name].")
- add_attack_logs(null, M, "became [new_mob.real_name]", ATKLOG_ALL)
+ add_attack_logs(M, M, "became [new_mob.real_name]", ATKLOG_ALL)
new_mob.a_intent = INTENT_HARM
if(M.mind)
diff --git a/code/modules/reagents/chemistry/recipes/drinks.dm b/code/modules/reagents/chemistry/recipes/drinks.dm
index f03ae9b316b..c5f4a2b63bf 100644
--- a/code/modules/reagents/chemistry/recipes/drinks.dm
+++ b/code/modules/reagents/chemistry/recipes/drinks.dm
@@ -915,8 +915,8 @@
name = "Gimlet"
id = "gimlet"
result = "gimlet"
- required_reagents = list("gin" = 1, "limejuice" = 1)
- result_amount = 2
+ required_reagents = list("gin" = 1, "limejuice" = 1, "sugar" = 1)
+ result_amount = 3
mix_sound = 'sound/goonstation/misc/drinkfizz.ogg'
/datum/chemical_reaction/sidecar
diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm
index b2afa9c06e3..cb875dce0cf 100644
--- a/code/modules/recycling/disposal.dm
+++ b/code/modules/recycling/disposal.dm
@@ -691,6 +691,7 @@
armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30)
damage_deflection = 10
flags_2 = RAD_PROTECT_CONTENTS_2 | RAD_NO_CONTAMINATE_2
+ plane = FLOOR_PLANE
layer = DISPOSAL_PIPE_LAYER // slightly lower than wires and other pipes
var/base_icon_state // initial icon state on map
@@ -757,6 +758,9 @@
// update the icon_state to reflect hidden status
/obj/structure/disposalpipe/proc/update()
var/turf/T = get_turf(src)
+ if(T.transparent_floor)
+ update_icon()
+ return
hide(T.intact && !istype(T, /turf/space)) // space never hides pipes
update_icon()
@@ -889,15 +893,20 @@
/obj/structure/disposalpipe/attackby(obj/item/I, mob/user, params)
var/turf/T = get_turf(src)
- if(T.intact)
- return // prevent interaction with T-scanner revealed pipes
+ if(T.intact || T.transparent_floor)
+ to_chat(user, "You can't interact with something that's under the floor!")
+ return // prevent interaction with T-scanner revealed pipes and pipes under glass
add_fingerprint(user)
/obj/structure/disposalpipe/welder_act(mob/user, obj/item/I)
. = TRUE
+ var/turf/T = get_turf(src)
if(!I.tool_use_check(user, 0))
return
+ if(T.transparent_floor)
+ to_chat(user, "You can't interact with something that's under the floor!")
+ return
WELDER_ATTEMPT_SLICING_MESSAGE
if(!I.use_tool(src, user, 30, volume = I.tool_volume))
return
@@ -1201,7 +1210,7 @@
return
var/turf/T = src.loc
- if(T.intact)
+ if(T.intact || T.transparent_floor)
return // prevent interaction with T-scanner revealed pipes
src.add_fingerprint(user)
diff --git a/code/modules/research/circuitprinter.dm b/code/modules/research/circuitprinter.dm
index 83b09e5c228..4c7f1e94ca1 100644
--- a/code/modules/research/circuitprinter.dm
+++ b/code/modules/research/circuitprinter.dm
@@ -11,19 +11,17 @@ using metal and glass, it uses glass and reagents (usually sulfuric acis).
container_type = OPENCONTAINER
categories = list(
- "AI Modules",
- "Computer Boards",
- "Engineering Machinery",
- "Exosuit Modules",
- "Hydroponics Machinery",
- "Medical Machinery",
- "Misc. Machinery",
- "Research Machinery",
- "Subspace Telecomms",
- "Teleportation Machinery"
- )
-
- reagents = new()
+ "AI Modules",
+ "Computer Boards",
+ "Engineering Machinery",
+ "Exosuit Modules",
+ "Hydroponics Machinery",
+ "Medical Machinery",
+ "Misc. Machinery",
+ "Research Machinery",
+ "Subspace Telecomms",
+ "Teleportation Machinery"
+ )
/obj/machinery/r_n_d/circuit_imprinter/New()
..()
@@ -33,8 +31,8 @@ using metal and glass, it uses glass and reagents (usually sulfuric acis).
component_parts += new /obj/item/stock_parts/manipulator(null)
component_parts += new /obj/item/reagent_containers/glass/beaker(null)
component_parts += new /obj/item/reagent_containers/glass/beaker(null)
+ create_reagents()
RefreshParts()
- reagents.my_atom = src
/obj/machinery/r_n_d/circuit_imprinter/upgraded/New()
..()
@@ -45,7 +43,11 @@ using metal and glass, it uses glass and reagents (usually sulfuric acis).
component_parts += new /obj/item/reagent_containers/glass/beaker/large(null)
component_parts += new /obj/item/reagent_containers/glass/beaker/large(null)
RefreshParts()
- reagents.my_atom = src
+
+/obj/machinery/r_n_d/circuit_imprinter/Destroy()
+ if(linked_console)
+ linked_console.linked_imprinter = null
+ return ..()
/obj/machinery/r_n_d/circuit_imprinter/RefreshParts()
reagents.maximum_volume = 0
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index f91b8f2012b..7ce266b522a 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -72,6 +72,16 @@
build_path = /obj/item/defibrillator
category = list("Medical")
+/datum/design/compact_defib
+ name = "Compact Defibrillator"
+ desc = "A belt-mounted defibrillator for rapid deployment."
+ id = "compact_defib"
+ req_tech = list("materials" = 7, "biotech" = 7, "powerstorage" = 6)
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 10000, MAT_GLASS = 4000, MAT_SILVER = 2000)
+ build_path = /obj/item/defibrillator/compact
+ category = list("Medical")
+
/datum/design/defib_mount
name = "Defibrillator Wall Mount"
desc = "A wall mount for defibrillator units."
diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm
index 41b7fedd6db..5423f41a236 100644
--- a/code/modules/research/destructive_analyzer.dm
+++ b/code/modules/research/destructive_analyzer.dm
@@ -29,6 +29,11 @@ Note: Must be placed within 3 tiles of the R&D Console
component_parts += new /obj/item/stock_parts/micro_laser/ultra(null)
RefreshParts()
+/obj/machinery/r_n_d/destructive_analyzer/Destroy()
+ if(linked_console)
+ linked_console.linked_destroy = null
+ return ..()
+
/obj/machinery/r_n_d/destructive_analyzer/RefreshParts()
var/T = 0
for(var/obj/item/stock_parts/S in component_parts)
diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm
index 3c2e2cfda0c..4ceacf94301 100644
--- a/code/modules/research/message_server.dm
+++ b/code/modules/research/message_server.dm
@@ -60,14 +60,16 @@ GLOBAL_LIST_EMPTY(message_servers)
var/active = TRUE
var/decryptkey = "password"
-/obj/machinery/message_server/New()
+/obj/machinery/message_server/Initialize(mapload)
+ . = ..()
GLOB.message_servers += src
decryptkey = GenerateKey()
send_pda_message("System Administrator", "system", "This is an automated message. The messaging system is functioning correctly.")
- ..()
/obj/machinery/message_server/Destroy()
GLOB.message_servers -= src
+ QDEL_LIST(pda_msgs)
+ QDEL_LIST(rc_msgs)
return ..()
/obj/machinery/message_server/process()
diff --git a/code/modules/research/protolathe.dm b/code/modules/research/protolathe.dm
index 263e67dd795..0f69a938734 100644
--- a/code/modules/research/protolathe.dm
+++ b/code/modules/research/protolathe.dm
@@ -14,19 +14,16 @@ Note: Must be placed west/left of and R&D console to function.
container_type = OPENCONTAINER
categories = list(
- "Bluespace",
- "Equipment",
- "Janitorial",
- "Medical",
- "Mining",
- "Miscellaneous",
- "Power",
- "Stock Parts",
- "Weapons"
- )
-
- reagents = new()
-
+ "Bluespace",
+ "Equipment",
+ "Janitorial",
+ "Medical",
+ "Mining",
+ "Miscellaneous",
+ "Power",
+ "Stock Parts",
+ "Weapons"
+ )
/obj/machinery/r_n_d/protolathe/New()
..()
@@ -38,10 +35,9 @@ Note: Must be placed west/left of and R&D console to function.
component_parts += new /obj/item/stock_parts/manipulator(null)
component_parts += new /obj/item/reagent_containers/glass/beaker/large(null)
component_parts += new /obj/item/reagent_containers/glass/beaker/large(null)
+ create_reagents()
RefreshParts()
- reagents.my_atom = src
-
/obj/machinery/r_n_d/protolathe/upgraded/New()
..()
component_parts = list()
@@ -54,7 +50,10 @@ Note: Must be placed west/left of and R&D console to function.
component_parts += new /obj/item/reagent_containers/glass/beaker/large(null)
RefreshParts()
- reagents.my_atom = src
+/obj/machinery/r_n_d/protolathe/Destroy()
+ if(linked_console)
+ linked_console.linked_lathe = null
+ return ..()
/obj/machinery/r_n_d/protolathe/RefreshParts()
var/T = 0
diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm
index ffa8ac763de..03c916e8b69 100644
--- a/code/modules/research/rdconsole.dm
+++ b/code/modules/research/rdconsole.dm
@@ -176,6 +176,20 @@ won't update every console in existence) but it's more of a hassle to do. Also,
SyncRDevices()
/obj/machinery/computer/rdconsole/Destroy()
+ QDEL_NULL(files)
+ QDEL_NULL(t_disk)
+ QDEL_NULL(d_disk)
+ QDEL_LIST(matching_designs)
+ if(linked_destroy)
+ linked_destroy.linked_console = null
+ linked_destroy = null
+ if(linked_lathe)
+ linked_lathe.linked_console = null
+ linked_lathe = null
+ if(linked_imprinter)
+ linked_imprinter.linked_console = null
+ linked_imprinter = null
+
if(wait_message_timer)
deltimer(wait_message_timer)
wait_message_timer = 0
diff --git a/code/modules/research/rdmachines.dm b/code/modules/research/rdmachines.dm
index ac78e892c7e..3fba5e8093a 100644
--- a/code/modules/research/rdmachines.dm
+++ b/code/modules/research/rdmachines.dm
@@ -32,12 +32,17 @@
wires["Black"] = 0
wires["White"] = 0
var/list/w = list("Red","Blue","Green","Yellow","Black","White")
- src.hack_wire = pick(w)
- w -= src.hack_wire
- src.shock_wire = pick(w)
- w -= src.shock_wire
- src.disable_wire = pick(w)
- w -= src.disable_wire
+ hack_wire = pick_n_take(w)
+ shock_wire = pick_n_take(w)
+ disable_wire = pick_n_take(w)
+
+/obj/machinery/r_n_d/Destroy()
+ if(loaded_item)
+ loaded_item.forceMove(get_turf(src))
+ loaded_item = null
+ linked_console = null
+ materials = null
+ return ..()
/obj/machinery/r_n_d/attack_hand(mob/user as mob)
if(shocked)
diff --git a/code/modules/response_team/ert_outfits.dm b/code/modules/response_team/ert_outfits.dm
index b597af644f9..d4b4f5fc929 100644
--- a/code/modules/response_team/ert_outfits.dm
+++ b/code/modules/response_team/ert_outfits.dm
@@ -340,7 +340,7 @@
/obj/item/organ/internal/cyberimp/arm/surgery,
/obj/item/organ/internal/cyberimp/chest/nutriment
)
- belt = /obj/item/defibrillator/compact/loaded
+ belt = /obj/item/defibrillator/compact/advanced/loaded
l_pocket = /obj/item/reagent_containers/hypospray/safety/ert
r_pocket = /obj/item/melee/classic_baton/telescopic
@@ -369,7 +369,7 @@
mask = /obj/item/clothing/mask/gas/sechailer/swat
suit_store = /obj/item/gun/energy/gun/blueshield/pdw9
- belt = /obj/item/defibrillator/compact/loaded
+ belt = /obj/item/defibrillator/compact/advanced/loaded
l_pocket = /obj/item/reagent_containers/hypospray/combat/nanites
r_pocket = /obj/item/reagent_containers/hypospray/autoinjector
diff --git a/code/modules/security_levels/security_levels.dm b/code/modules/security_levels/security_levels.dm
index d3d4be7dff9..c9bf85eaac4 100644
--- a/code/modules/security_levels/security_levels.dm
+++ b/code/modules/security_levels/security_levels.dm
@@ -33,7 +33,7 @@ GLOBAL_DATUM_INIT(security_announcement_down, /datum/announcement/priority/secur
switch(level)
if(SEC_LEVEL_GREEN)
- GLOB.security_announcement_down.Announce("All threats to the station have passed. All weapons need to be holstered and privacy laws are once again fully enforced.","Attention! Security level lowered to green.")
+ GLOB.security_announcement_down.Announce("All threats to the station have passed. All weapons need to be holstered and privacy laws are once again fully enforced.","Attention! Security level lowered to green.", 'sound/AI/green.ogg')
GLOB.security_level = SEC_LEVEL_GREEN
post_status("alert", "outline")
@@ -45,9 +45,9 @@ GLOBAL_DATUM_INIT(security_announcement_down, /datum/announcement/priority/secur
if(SEC_LEVEL_BLUE)
if(GLOB.security_level < SEC_LEVEL_BLUE)
- GLOB.security_announcement_up.Announce("The station has received reliable information about possible hostile activity on the station. Security staff may have weapons visible and random searches are permitted.","Attention! Security level elevated to blue.")
+ GLOB.security_announcement_up.Announce("The station has received reliable information about possible hostile activity on the station. Security staff may have weapons visible and random searches are permitted.","Attention! Security level elevated to blue.", 'sound/AI/blue.ogg')
else
- GLOB.security_announcement_down.Announce("The immediate threat has passed. Security may no longer have weapons drawn at all times, but may continue to have them visible. Random searches are still allowed.","Attention! Security level lowered to blue.")
+ GLOB.security_announcement_down.Announce("The immediate threat has passed. Security may no longer have weapons drawn at all times, but may continue to have them visible. Random searches are still allowed.","Attention! Security level lowered to blue.", 'sound/AI/blue.ogg')
GLOB.security_level = SEC_LEVEL_BLUE
post_status("alert", "outline")
@@ -59,9 +59,9 @@ GLOBAL_DATUM_INIT(security_announcement_down, /datum/announcement/priority/secur
if(SEC_LEVEL_RED)
if(GLOB.security_level < SEC_LEVEL_RED)
- GLOB.security_announcement_up.Announce("There is an immediate and serious threat to the station. Security may have weapons unholstered at all times. Random searches are allowed and advised.","Attention! Code Red!")
+ GLOB.security_announcement_up.Announce("There is an immediate and serious threat to the station. Security may have weapons unholstered at all times. Random searches are allowed and advised.","Attention! Code Red!", 'sound/AI/red.ogg')
else
- GLOB.security_announcement_down.Announce("The station's self-destruct mechanism has been deactivated, but there is still an immediate and serious threat to the station. Security may have weapons unholstered at all times. Random searches are allowed and advised.","Attention! Code Red!")
+ GLOB.security_announcement_down.Announce("The station's self-destruct mechanism has been deactivated, but there is still an immediate and serious threat to the station. Security may have weapons unholstered at all times. Random searches are allowed and advised.","Attention! Code Red!", 'sound/AI/red.ogg')
GLOB.security_level = SEC_LEVEL_RED
var/obj/machinery/door/airlock/highsecurity/red/R = locate(/obj/machinery/door/airlock/highsecurity/red) in GLOB.airlocks
@@ -77,7 +77,7 @@ GLOBAL_DATUM_INIT(security_announcement_down, /datum/announcement/priority/secur
FA.overlays += image('icons/obj/monitors.dmi', "overlay_red")
if(SEC_LEVEL_GAMMA)
- GLOB.security_announcement_up.Announce("Central Command has ordered the Gamma security level on the station. Security is to have weapons equipped at all times, and all civilians are to immediately seek their nearest head for transportation to a secure location.", "Attention! Gamma security level activated!", sound('sound/effects/new_siren.ogg'))
+ GLOB.security_announcement_up.Announce("Central Command has ordered the Gamma security level on the station. Security is to have weapons equipped at all times, and all civilians are to immediately seek their nearest head for transportation to a secure location.", "Attention! Gamma security level activated!", 'sound/effects/new_siren.ogg', new_sound2 = 'sound/AI/gamma.ogg')
GLOB.security_level = SEC_LEVEL_GAMMA
if(GLOB.security_level < SEC_LEVEL_RED)
@@ -95,7 +95,7 @@ GLOBAL_DATUM_INIT(security_announcement_down, /datum/announcement/priority/secur
FA.update_icon()
if(SEC_LEVEL_EPSILON)
- GLOB.security_announcement_up.Announce("Central Command has ordered the Epsilon security level on the station. Consider all contracts terminated.","Attention! Epsilon security level activated!", new_sound = sound('sound/effects/purge_siren.ogg'))
+ GLOB.security_announcement_up.Announce("Central Command has ordered the Epsilon security level on the station. Consider all contracts terminated.","Attention! Epsilon security level activated!", 'sound/effects/purge_siren.ogg', new_sound2 = 'sound/AI/epsilon.ogg')
GLOB.security_level = SEC_LEVEL_EPSILON
post_status("alert", "epsilonalert")
@@ -106,7 +106,7 @@ GLOBAL_DATUM_INIT(security_announcement_down, /datum/announcement/priority/secur
FA.overlays += image('icons/obj/monitors.dmi', "overlay_epsilon")
if(SEC_LEVEL_DELTA)
- GLOB.security_announcement_up.Announce("The station's self-destruct mechanism has been engaged. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill.","Attention! Delta security level reached!", new_sound = sound('sound/effects/deltaalarm.ogg'))
+ GLOB.security_announcement_up.Announce("The station's self-destruct mechanism has been engaged. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill.","Attention! Delta security level reached!", 'sound/effects/deltaalarm.ogg', new_sound2 = 'sound/AI/delta.ogg')
GLOB.security_level = SEC_LEVEL_DELTA
post_status("alert", "deltaalert")
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index 8a5c2118794..f9c3edf437c 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -86,12 +86,15 @@
travelDir = 0
var/sound_played = 0 //If the launch sound has been sent to all players on the shuttle itself
- var/datum/announcement/priority/emergency_shuttle_docked = new(0, new_sound = sound('sound/AI/shuttledock.ogg'))
- var/datum/announcement/priority/emergency_shuttle_called = new(0, new_sound = sound('sound/AI/shuttlecalled.ogg'))
- var/datum/announcement/priority/emergency_shuttle_recalled = new(0, new_sound = sound('sound/AI/shuttlerecalled.ogg'))
+ var/datum/announcement/priority/emergency_shuttle_docked = new(0, new_sound = sound('sound/AI/eshuttle_dock.ogg'))
+ var/datum/announcement/priority/emergency_shuttle_called = new(0, new_sound = sound('sound/AI/eshuttle_call.ogg'))
+ var/datum/announcement/priority/emergency_shuttle_recalled = new(0, new_sound = sound('sound/AI/eshuttle_recall.ogg'))
var/canRecall = TRUE //no bad condom, do not recall the crew transfer shuttle!
+ var/datum/announcement/priority/crew_shuttle_called = new(0, new_sound = sound('sound/AI/cshuttle.ogg'))
+ var/datum/announcement/priority/crew_shuttle_docked = new(0, new_sound = sound('sound/AI/cshuttle_dock.ogg'))
+
/obj/docking_port/mobile/emergency/register()
if(!..())
@@ -146,9 +149,10 @@
SSshuttle.emergencyLastCallLoc = signalOrigin
else
SSshuttle.emergencyLastCallLoc = null
-
- emergency_shuttle_called.Announce("The emergency shuttle has been called. [redAlert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [timeLeft(600)] minutes.[reason][SSshuttle.emergencyLastCallLoc ? "\n\nCall signal traced. Results can be viewed on any communications console." : "" ]")
-
+ if(canRecall)
+ emergency_shuttle_called.Announce("The emergency shuttle has been called. [redAlert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [timeLeft(600)] minutes.[reason][SSshuttle.emergencyLastCallLoc ? "\n\nCall signal traced. Results can be viewed on any communications console." : "" ]")
+ else
+ crew_shuttle_called.Announce("The crew transfer shuttle has been called. [redAlert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [timeLeft(600)] minutes.[reason]")
/obj/docking_port/mobile/emergency/cancel(area/signalOrigin)
if(!canRecall)
@@ -230,7 +234,10 @@
return
mode = SHUTTLE_DOCKED
timer = world.time
- emergency_shuttle_docked.Announce("The Emergency Shuttle has docked with the station. You have [timeLeft(600)] minutes to board the Emergency Shuttle.")
+ if(canRecall)
+ emergency_shuttle_docked.Announce("The emergency shuttle has docked with the station. You have [timeLeft(600)] minutes to board the emergency shuttle.")
+ else
+ crew_shuttle_docked.Announce("The crew transfer shuttle has docked with the station. You have [timeLeft(600)] minutes to board the crew transfer shuttle.")
/*
//Gangs only have one attempt left if the shuttle has docked with the station to prevent suffering from dominator delays
diff --git a/code/modules/station_goals/bluespace_tap.dm b/code/modules/station_goals/bluespace_tap.dm
index 6e6898bde1f..9bdf10d9407 100644
--- a/code/modules/station_goals/bluespace_tap.dm
+++ b/code/modules/station_goals/bluespace_tap.dm
@@ -326,7 +326,7 @@
else if(input_level > desired_level)
input_level--
if(prob(input_level - safe_levels + (emagged * 5))) //at dangerous levels, start doing freaky shit. prob with values less than 0 treat it as 0
- GLOB.event_announcement.Announce("Unexpected power spike during Bluespace Harvester Operation. Extra-dimensional intruder alert. Expected location: [get_area(src).name]. [emagged ? "DANGER: Emergency shutdown failed! Please proceed with manual shutdown." : "Emergency shutdown initiated."]", "Bluespace Harvester Malfunction")
+ GLOB.event_announcement.Announce("Unexpected power spike during Bluespace Harvester Operation. Extra-dimensional intruder alert. Expected location: [get_area(src).name]. [emagged ? "DANGER: Emergency shutdown failed! Please proceed with manual shutdown." : "Emergency shutdown initiated."]", "Bluespace Harvester Malfunction", 'sound/AI/harvester.ogg')
if(!emagged)
input_level = 0 //emergency shutdown unless we're sabotaged
desired_level = 0
diff --git a/code/modules/vehicle/ambulance.dm b/code/modules/vehicle/ambulance.dm
index 87323fc2042..e5d089c7fe9 100644
--- a/code/modules/vehicle/ambulance.dm
+++ b/code/modules/vehicle/ambulance.dm
@@ -12,6 +12,11 @@
AA = new(src)
soundloop = new(list(src), FALSE)
+/obj/vehicle/ambulance/Destroy()
+ QDEL_NULL(AA)
+ QDEL_NULL(soundloop)
+ return ..()
+
/datum/action/ambulance_alarm
name = "Toggle Sirens"
icon_icon = 'icons/obj/vehicles.dmi'
diff --git a/dreamchecker.exe b/dreamchecker.exe
index 2fa602138fd..5203df4047c 100644
Binary files a/dreamchecker.exe and b/dreamchecker.exe differ
diff --git a/icons/_nanomaps/Delta_nanomap_z1.png b/icons/_nanomaps/Delta_nanomap_z1.png
index e6de831a97d..153dd1519b4 100644
Binary files a/icons/_nanomaps/Delta_nanomap_z1.png and b/icons/_nanomaps/Delta_nanomap_z1.png differ
diff --git a/icons/mob/clothing/belt.dmi b/icons/mob/clothing/belt.dmi
index dd893450d75..63a203345ae 100644
Binary files a/icons/mob/clothing/belt.dmi and b/icons/mob/clothing/belt.dmi differ
diff --git a/icons/mob/clothing/species/vox/back.dmi b/icons/mob/clothing/species/vox/back.dmi
index a0a8445964a..7747d55af60 100644
Binary files a/icons/mob/clothing/species/vox/back.dmi and b/icons/mob/clothing/species/vox/back.dmi differ
diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi
index a0b551e83bd..b5659069b65 100644
Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ
diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi
index 39636bd7bab..f712201f5e4 100644
Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ
diff --git a/icons/obj/computer.dmi b/icons/obj/computer.dmi
index dcfe0a20810..7157f054dc7 100644
Binary files a/icons/obj/computer.dmi and b/icons/obj/computer.dmi differ
diff --git a/icons/obj/defib.dmi b/icons/obj/defib.dmi
new file mode 100644
index 00000000000..ca471bd5ffa
Binary files /dev/null and b/icons/obj/defib.dmi differ
diff --git a/icons/obj/fence.dmi b/icons/obj/fence.dmi
new file mode 100644
index 00000000000..132482b4874
Binary files /dev/null and b/icons/obj/fence.dmi differ
diff --git a/icons/obj/food/food.dmi b/icons/obj/food/food.dmi
index 3cb9250df9e..3905a885990 100644
Binary files a/icons/obj/food/food.dmi and b/icons/obj/food/food.dmi differ
diff --git a/icons/obj/guns/magic.dmi b/icons/obj/guns/magic.dmi
index 3fe76915f16..e4153046565 100644
Binary files a/icons/obj/guns/magic.dmi and b/icons/obj/guns/magic.dmi differ
diff --git a/icons/obj/items.dmi b/icons/obj/items.dmi
index d00d1d4fb94..fd2b776aa43 100644
Binary files a/icons/obj/items.dmi and b/icons/obj/items.dmi differ
diff --git a/icons/turf/floors/glass.dmi b/icons/turf/floors/glass.dmi
new file mode 100644
index 00000000000..1d0f13d0c06
Binary files /dev/null and b/icons/turf/floors/glass.dmi differ
diff --git a/icons/turf/floors/plasmaglass.dmi b/icons/turf/floors/plasmaglass.dmi
new file mode 100644
index 00000000000..629d8be41ec
Binary files /dev/null and b/icons/turf/floors/plasmaglass.dmi differ
diff --git a/icons/turf/floors/plastitaniumglass.dmi b/icons/turf/floors/plastitaniumglass.dmi
new file mode 100644
index 00000000000..39a56f32b00
Binary files /dev/null and b/icons/turf/floors/plastitaniumglass.dmi differ
diff --git a/icons/turf/floors/reinf_glass.dmi b/icons/turf/floors/reinf_glass.dmi
new file mode 100644
index 00000000000..c5072fd11e0
Binary files /dev/null and b/icons/turf/floors/reinf_glass.dmi differ
diff --git a/icons/turf/floors/reinf_plasmaglass.dmi b/icons/turf/floors/reinf_plasmaglass.dmi
new file mode 100644
index 00000000000..c46025e5291
Binary files /dev/null and b/icons/turf/floors/reinf_plasmaglass.dmi differ
diff --git a/icons/turf/floors/titaniumglass.dmi b/icons/turf/floors/titaniumglass.dmi
new file mode 100644
index 00000000000..597487ff175
Binary files /dev/null and b/icons/turf/floors/titaniumglass.dmi differ
diff --git a/paradise.dme b/paradise.dme
index b6861bb6feb..7c5da49fd52 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -1059,6 +1059,7 @@
#include "code\game\objects\structures\engicart.dm"
#include "code\game\objects\structures\extinguisher.dm"
#include "code\game\objects\structures\false_walls.dm"
+#include "code\game\objects\structures\fence.dm"
#include "code\game\objects\structures\flora.dm"
#include "code\game\objects\structures\fluff.dm"
#include "code\game\objects\structures\foodcart.dm"
@@ -1079,6 +1080,7 @@
#include "code\game\objects\structures\morgue.dm"
#include "code\game\objects\structures\noticeboard.dm"
#include "code\game\objects\structures\plasticflaps.dm"
+#include "code\game\objects\structures\railings.dm"
#include "code\game\objects\structures\reflector.dm"
#include "code\game\objects\structures\safe.dm"
#include "code\game\objects\structures\signs.dm"
@@ -1154,6 +1156,7 @@
#include "code\game\turfs\simulated\floor\misc_floor.dm"
#include "code\game\turfs\simulated\floor\plasteel_floor.dm"
#include "code\game\turfs\simulated\floor\plating.dm"
+#include "code\game\turfs\simulated\floor\transparent.dm"
#include "code\game\turfs\space\space.dm"
#include "code\game\turfs\space\transit.dm"
#include "code\game\verbs\ooc.dm"
@@ -1175,6 +1178,7 @@
#include "code\modules\admin\holder2.dm"
#include "code\modules\admin\IsBanned.dm"
#include "code\modules\admin\machine_upgrade.dm"
+#include "code\modules\admin\mute.dm"
#include "code\modules\admin\NewBan.dm"
#include "code\modules\admin\outfits.dm"
#include "code\modules\admin\player_panel.dm"
diff --git a/sound/AI/alert.ogg b/sound/AI/alert.ogg
new file mode 100644
index 00000000000..66218bb12a4
Binary files /dev/null and b/sound/AI/alert.ogg differ
diff --git a/sound/AI/aliens.ogg b/sound/AI/aliens.ogg
index f7d1746247f..998cc724357 100644
Binary files a/sound/AI/aliens.ogg and b/sound/AI/aliens.ogg differ
diff --git a/sound/AI/animes.ogg b/sound/AI/animes.ogg
index a46a207edc4..0e393fbb29f 100644
Binary files a/sound/AI/animes.ogg and b/sound/AI/animes.ogg differ
diff --git a/sound/AI/anomaly.ogg b/sound/AI/anomaly.ogg
new file mode 100644
index 00000000000..78a6b48c169
Binary files /dev/null and b/sound/AI/anomaly.ogg differ
diff --git a/sound/AI/anomaly_bluespace.ogg b/sound/AI/anomaly_bluespace.ogg
new file mode 100644
index 00000000000..457344dd0fa
Binary files /dev/null and b/sound/AI/anomaly_bluespace.ogg differ
diff --git a/sound/AI/anomaly_flux.ogg b/sound/AI/anomaly_flux.ogg
new file mode 100644
index 00000000000..ef95486b3f4
Binary files /dev/null and b/sound/AI/anomaly_flux.ogg differ
diff --git a/sound/AI/anomaly_gravity.ogg b/sound/AI/anomaly_gravity.ogg
new file mode 100644
index 00000000000..eb90d040ed4
Binary files /dev/null and b/sound/AI/anomaly_gravity.ogg differ
diff --git a/sound/AI/anomaly_pyro.ogg b/sound/AI/anomaly_pyro.ogg
new file mode 100644
index 00000000000..cbed0ce1b8b
Binary files /dev/null and b/sound/AI/anomaly_pyro.ogg differ
diff --git a/sound/AI/anomaly_vortex.ogg b/sound/AI/anomaly_vortex.ogg
new file mode 100644
index 00000000000..24483546263
Binary files /dev/null and b/sound/AI/anomaly_vortex.ogg differ
diff --git a/sound/AI/attention.ogg b/sound/AI/attention.ogg
index 912be4425eb..d8a0b98a4aa 100644
Binary files a/sound/AI/attention.ogg and b/sound/AI/attention.ogg differ
diff --git a/sound/AI/blue.ogg b/sound/AI/blue.ogg
new file mode 100644
index 00000000000..bfbd0a50b93
Binary files /dev/null and b/sound/AI/blue.ogg differ
diff --git a/sound/AI/brand_intelligence.ogg b/sound/AI/brand_intelligence.ogg
new file mode 100644
index 00000000000..bd35a794bab
Binary files /dev/null and b/sound/AI/brand_intelligence.ogg differ
diff --git a/sound/AI/commandreport.ogg b/sound/AI/commandreport.ogg
index 82e4ca425de..91569bf738a 100644
Binary files a/sound/AI/commandreport.ogg and b/sound/AI/commandreport.ogg differ
diff --git a/sound/AI/cshuttle.ogg b/sound/AI/cshuttle.ogg
new file mode 100644
index 00000000000..7c6f04e0e0a
Binary files /dev/null and b/sound/AI/cshuttle.ogg differ
diff --git a/sound/AI/cshuttle_dock.ogg b/sound/AI/cshuttle_dock.ogg
new file mode 100644
index 00000000000..7018743fab4
Binary files /dev/null and b/sound/AI/cshuttle_dock.ogg differ
diff --git a/sound/AI/delam.ogg b/sound/AI/delam.ogg
new file mode 100644
index 00000000000..b1822b5a69f
Binary files /dev/null and b/sound/AI/delam.ogg differ
diff --git a/sound/AI/delta.ogg b/sound/AI/delta.ogg
new file mode 100644
index 00000000000..5ed127bc694
Binary files /dev/null and b/sound/AI/delta.ogg differ
diff --git a/sound/AI/door_runtimes.ogg b/sound/AI/door_runtimes.ogg
new file mode 100644
index 00000000000..d67b6568b3b
Binary files /dev/null and b/sound/AI/door_runtimes.ogg differ
diff --git a/sound/AI/door_runtimes_fix.ogg b/sound/AI/door_runtimes_fix.ogg
new file mode 100644
index 00000000000..1508ff9d027
Binary files /dev/null and b/sound/AI/door_runtimes_fix.ogg differ
diff --git a/sound/AI/elec_storm.ogg b/sound/AI/elec_storm.ogg
new file mode 100644
index 00000000000..b4a417eacd4
Binary files /dev/null and b/sound/AI/elec_storm.ogg differ
diff --git a/sound/AI/epsilon.ogg b/sound/AI/epsilon.ogg
new file mode 100644
index 00000000000..0ef61026881
Binary files /dev/null and b/sound/AI/epsilon.ogg differ
diff --git a/sound/AI/eshuttle_call.ogg b/sound/AI/eshuttle_call.ogg
new file mode 100644
index 00000000000..90bbc5c59d0
Binary files /dev/null and b/sound/AI/eshuttle_call.ogg differ
diff --git a/sound/AI/eshuttle_dock.ogg b/sound/AI/eshuttle_dock.ogg
new file mode 100644
index 00000000000..246a0a603a4
Binary files /dev/null and b/sound/AI/eshuttle_dock.ogg differ
diff --git a/sound/AI/eshuttle_recall.ogg b/sound/AI/eshuttle_recall.ogg
new file mode 100644
index 00000000000..bcbcbbaec2e
Binary files /dev/null and b/sound/AI/eshuttle_recall.ogg differ
diff --git a/sound/AI/flare.ogg b/sound/AI/flare.ogg
new file mode 100644
index 00000000000..7eafb3f832b
Binary files /dev/null and b/sound/AI/flare.ogg differ
diff --git a/sound/AI/gamma.ogg b/sound/AI/gamma.ogg
new file mode 100644
index 00000000000..4738f1c8959
Binary files /dev/null and b/sound/AI/gamma.ogg differ
diff --git a/sound/AI/granomalies.ogg b/sound/AI/granomalies.ogg
deleted file mode 100644
index 89b368d596a..00000000000
Binary files a/sound/AI/granomalies.ogg and /dev/null differ
diff --git a/sound/AI/green.ogg b/sound/AI/green.ogg
new file mode 100644
index 00000000000..44cb4108976
Binary files /dev/null and b/sound/AI/green.ogg differ
diff --git a/sound/AI/harvester.ogg b/sound/AI/harvester.ogg
new file mode 100644
index 00000000000..a10b308ed09
Binary files /dev/null and b/sound/AI/harvester.ogg differ
diff --git a/sound/AI/intercept.ogg b/sound/AI/intercept.ogg
index 3569a07d401..30dba660681 100644
Binary files a/sound/AI/intercept.ogg and b/sound/AI/intercept.ogg differ
diff --git a/sound/AI/intercept2.ogg b/sound/AI/intercept2.ogg
deleted file mode 100644
index ab106a7295a..00000000000
Binary files a/sound/AI/intercept2.ogg and /dev/null differ
diff --git a/sound/AI/ions.ogg b/sound/AI/ions.ogg
new file mode 100644
index 00000000000..f92c8b0b050
Binary files /dev/null and b/sound/AI/ions.ogg differ
diff --git a/sound/AI/ionstorm.ogg b/sound/AI/ionstorm.ogg
deleted file mode 100644
index 9f39713de6e..00000000000
Binary files a/sound/AI/ionstorm.ogg and /dev/null differ
diff --git a/sound/AI/meteors.ogg b/sound/AI/meteors.ogg
index 8f1c3aeacbb..e49b92065e6 100644
Binary files a/sound/AI/meteors.ogg and b/sound/AI/meteors.ogg differ
diff --git a/sound/AI/newai.ogg b/sound/AI/newai.ogg
deleted file mode 100644
index 35aba34564f..00000000000
Binary files a/sound/AI/newai.ogg and /dev/null differ
diff --git a/sound/AI/newroundsexy.ogg b/sound/AI/newroundsexy.ogg
index afe5630f04d..1e7242dbff2 100644
Binary files a/sound/AI/newroundsexy.ogg and b/sound/AI/newroundsexy.ogg differ
diff --git a/sound/AI/newroundsexy2.ogg b/sound/AI/newroundsexy2.ogg
deleted file mode 100644
index 531bba10014..00000000000
Binary files a/sound/AI/newroundsexy2.ogg and /dev/null differ
diff --git a/sound/AI/outbreak3.ogg b/sound/AI/outbreak3.ogg
new file mode 100644
index 00000000000..c4f6668e634
Binary files /dev/null and b/sound/AI/outbreak3.ogg differ
diff --git a/sound/AI/outbreak5.ogg b/sound/AI/outbreak5.ogg
index 39c79c920fd..1eb03a059dd 100644
Binary files a/sound/AI/outbreak5.ogg and b/sound/AI/outbreak5.ogg differ
diff --git a/sound/AI/outbreak7.ogg b/sound/AI/outbreak7.ogg
index f21d4fca443..a394dc9417e 100644
Binary files a/sound/AI/outbreak7.ogg and b/sound/AI/outbreak7.ogg differ
diff --git a/sound/AI/power_overload.ogg b/sound/AI/power_overload.ogg
new file mode 100644
index 00000000000..913cae054a3
Binary files /dev/null and b/sound/AI/power_overload.ogg differ
diff --git a/sound/AI/power_restore.ogg b/sound/AI/power_restore.ogg
new file mode 100644
index 00000000000..0758d786cc3
Binary files /dev/null and b/sound/AI/power_restore.ogg differ
diff --git a/sound/AI/poweroff.ogg b/sound/AI/poweroff.ogg
deleted file mode 100644
index 1c6377c9d8d..00000000000
Binary files a/sound/AI/poweroff.ogg and /dev/null differ
diff --git a/sound/AI/poweron.ogg b/sound/AI/poweron.ogg
deleted file mode 100644
index 9d18797d6ea..00000000000
Binary files a/sound/AI/poweron.ogg and /dev/null differ
diff --git a/sound/AI/radiation.ogg b/sound/AI/radiation.ogg
index ef395af3101..d60bec74b05 100644
Binary files a/sound/AI/radiation.ogg and b/sound/AI/radiation.ogg differ
diff --git a/sound/AI/red.ogg b/sound/AI/red.ogg
new file mode 100644
index 00000000000..3f7ad4ce9e3
Binary files /dev/null and b/sound/AI/red.ogg differ
diff --git a/sound/AI/scrubbers.ogg b/sound/AI/scrubbers.ogg
new file mode 100644
index 00000000000..ccdb40949c3
Binary files /dev/null and b/sound/AI/scrubbers.ogg differ
diff --git a/sound/AI/shuttlecalled.ogg b/sound/AI/shuttlecalled.ogg
deleted file mode 100644
index 716bf824654..00000000000
Binary files a/sound/AI/shuttlecalled.ogg and /dev/null differ
diff --git a/sound/AI/shuttledock.ogg b/sound/AI/shuttledock.ogg
deleted file mode 100644
index 0f70bebc751..00000000000
Binary files a/sound/AI/shuttledock.ogg and /dev/null differ
diff --git a/sound/AI/shuttlerecalled.ogg b/sound/AI/shuttlerecalled.ogg
deleted file mode 100644
index 5f6db404b87..00000000000
Binary files a/sound/AI/shuttlerecalled.ogg and /dev/null differ
diff --git a/sound/AI/spanomalies.ogg b/sound/AI/spanomalies.ogg
index 7680726f153..44f249c24ab 100644
Binary files a/sound/AI/spanomalies.ogg and b/sound/AI/spanomalies.ogg differ
diff --git a/sound/AI/welcome.ogg b/sound/AI/welcome.ogg
index c7013dcbd5f..87354bf38e8 100644
Binary files a/sound/AI/welcome.ogg and b/sound/AI/welcome.ogg differ
diff --git a/sound/effects/footstep/glass1.ogg b/sound/effects/footstep/glass1.ogg
new file mode 100644
index 00000000000..7afddff640a
Binary files /dev/null and b/sound/effects/footstep/glass1.ogg differ
diff --git a/sound/effects/footstep/glass2.ogg b/sound/effects/footstep/glass2.ogg
new file mode 100644
index 00000000000..5bac4625b68
Binary files /dev/null and b/sound/effects/footstep/glass2.ogg differ
diff --git a/sound/effects/footstep/glass3.ogg b/sound/effects/footstep/glass3.ogg
new file mode 100644
index 00000000000..5d355e8ef17
Binary files /dev/null and b/sound/effects/footstep/glass3.ogg differ
diff --git a/sound/effects/footstep/glassbarefoot1.ogg b/sound/effects/footstep/glassbarefoot1.ogg
new file mode 100644
index 00000000000..7dd8d9e5371
Binary files /dev/null and b/sound/effects/footstep/glassbarefoot1.ogg differ
diff --git a/sound/effects/footstep/glassbarefoot2.ogg b/sound/effects/footstep/glassbarefoot2.ogg
new file mode 100644
index 00000000000..b82c3e0d9d1
Binary files /dev/null and b/sound/effects/footstep/glassbarefoot2.ogg differ
diff --git a/sound/effects/footstep/glassbarefoot3.ogg b/sound/effects/footstep/glassbarefoot3.ogg
new file mode 100644
index 00000000000..317bf53fcc8
Binary files /dev/null and b/sound/effects/footstep/glassbarefoot3.ogg differ
diff --git a/tools/announcement_voice_filter/.bitwig-project b/tools/announcement_voice_filter/.bitwig-project
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tools/announcement_voice_filter/README.md b/tools/announcement_voice_filter/README.md
new file mode 100644
index 00000000000..db389ad1183
--- /dev/null
+++ b/tools/announcement_voice_filter/README.md
@@ -0,0 +1,17 @@
+# AI Announcement Voice Effects Template
+This template file helps apply the correct sound effects to generate further Station AI announcement messages.
+
+---
+
+### Requirements
+You need a copy (full or trial) of Bitwig Studio 3.x, and access to Amazon Polly's free tier. If you are using a trial version of Bitwig Studio, you also require a free audio editor such as Audacity.
+
+
+### Amazon Polly
+Plain text, Standard engine, Englush US, Joanna - Female.
+
+
+### Bitwig studio
+All you need to do is drag your generated polly voice into the project.
+Ensure the tempo is 114.4 for your track.
+Record or export the output directly to OGG (if you own a license) or to a free editor such as Audacity via loopback recording
diff --git a/tools/announcement_voice_filter/StationAI.bwproject b/tools/announcement_voice_filter/StationAI.bwproject
new file mode 100644
index 00000000000..a7fc00a20b9
Binary files /dev/null and b/tools/announcement_voice_filter/StationAI.bwproject differ
diff --git a/tools/mapmerge2/requirements.txt b/tools/mapmerge2/requirements.txt
index f46bdc31d5f..7687c0746ed 100644
--- a/tools/mapmerge2/requirements.txt
+++ b/tools/mapmerge2/requirements.txt
@@ -1,3 +1,3 @@
pygit2==1.0.1
bidict==0.13.1
-Pillow==8.2.0
+Pillow==8.3.2