From 99019166e454d605bc780107c12bcd9b76d6569d Mon Sep 17 00:00:00 2001
From: LetterN <24603524+LetterN@users.noreply.github.com>
Date: Sat, 30 Oct 2021 11:45:50 +0800
Subject: [PATCH] i think it works MAJOR antag backend update - you cannot
bruteforce the pen anymore - uplink now uses bitflag to lock purchases -
poplock is handled entirely by the buyable uplink items - tgui antag intro
(for selected ones)
---
code/__DEFINES/antagonists.dm | 9 +
code/__DEFINES/colors.dm | 238 +++--
code/__DEFINES/layers_planes.dm | 3 +
code/__DEFINES/lighting.dm | 25 -
code/__DEFINES/subsystems.dm | 16 +-
code/__DEFINES/text.dm | 5 +
code/__DEFINES/uplink.dm | 10 +
code/__HELPERS/_lists.dm | 152 ++--
code/__HELPERS/game.dm | 6 +-
code/__HELPERS/global_lists.dm | 10 -
code/__HELPERS/icons.dm | 2 +-
code/__HELPERS/radio.dm | 20 +-
code/_globalvars/lists/flavor_misc.dm | 8 +-
code/_globalvars/lists/objects.dm | 1 -
code/_onclick/hud/credits.dm | 2 +-
.../subsystem/processing/processing.dm | 5 +-
code/controllers/subsystem/sounds.dm | 12 +-
code/datums/chatmessage.dm | 4 +-
code/datums/components/uplink.dm | 150 ++--
code/datums/datum.dm | 2 +-
code/datums/mind.dm | 77 +-
.../datums/weather/weather_types/ash_storm.dm | 57 +-
code/game/atoms.dm | 4 +-
code/game/machinery/colormate.dm | 1 +
code/game/machinery/cryopod.dm | 2 +-
code/game/objects/items.dm | 11 +
.../objects/items/implants/implant_uplink.dm | 5 +-
code/game/objects/structures/noticeboard.dm | 2 +-
.../admin/view_variables/filterrific.dm | 2 +-
.../antagonists/_common/antag_datum.dm | 304 +++++--
.../antagonists/brainwashing/brainwashing.dm | 35 +-
.../antagonists/clockcult/clockcult.dm | 9 +
code/modules/antagonists/morph/morph_antag.dm | 1 +
.../antagonists/nightmare/nightmare.dm | 2 +
code/modules/antagonists/nukeop/nukeop.dm | 2 +
.../antagonists/overthrow/overthrow.dm | 2 +-
.../traitor/IAA/internal_affairs.dm | 124 +--
.../antagonists/traitor/classes/martyr.dm | 2 +-
.../traitor/classes/traitor_class.dm | 2 +
.../antagonists/traitor/datum_traitor.dm | 148 ++--
code/modules/antagonists/wizard/wizard.dm | 76 +-
code/modules/asset_cache/asset_list.dm | 28 -
code/modules/asset_cache/asset_list_items.dm | 259 +++---
.../asset_cache/transports/asset_transport.dm | 2 +-
code/modules/balloon_alert/balloon_alert.dm | 89 ++
code/modules/cargo/packs/vending.dm | 2 +-
code/modules/holiday/halloween/jacqueen.dm | 2 +-
.../integrated_electronics/core/detailer.dm | 2 +-
.../computers/item/computer.dm | 157 ++--
.../computers/item/computer_ui.dm | 6 -
.../file_system/programs/crewmanifest.dm | 2 +-
.../file_system/programs/jobmanagement.dm | 3 +-
.../file_system/programs/portrait_printer.dm | 2 +-
code/modules/paperwork/paper.dm | 118 ++-
code/modules/power/singularity/narsie.dm | 2 +-
code/modules/power/supermatter/supermatter.dm | 51 ++
code/modules/uplink/uplink_devices.dm | 60 +-
code/modules/uplink/uplink_items.dm | 159 ++--
.../uplink/uplink_items/uplink_ammo.dm | 44 +-
.../uplink/uplink_items/uplink_badass.dm | 6 +-
.../uplink/uplink_items/uplink_bundles.dm | 82 +-
.../uplink/uplink_items/uplink_clothing.dm | 24 +-
.../uplink/uplink_items/uplink_dangerous.dm | 55 +-
.../uplink/uplink_items/uplink_devices.dm | 14 +-
.../uplink/uplink_items/uplink_explosives.dm | 22 +-
.../uplink/uplink_items/uplink_implants.dm | 16 +-
.../uplink/uplink_items/uplink_roles.dm | 3 -
.../uplink/uplink_items/uplink_stealth.dm | 19 +-
.../uplink_items/uplink_stealthdevices.dm | 13 +-
.../uplink/uplink_items/uplink_support.dm | 6 +-
code/modules/uplink/uplink_purchase_log.dm | 4 +-
code/modules/vending/_vending.dm | 811 ++++++++++--------
code/modules/vending/assist.dm | 13 +-
code/modules/vending/autodrobe.dm | 258 +++---
code/modules/vending/boozeomat.dm | 14 +-
code/modules/vending/cartridge.dm | 15 +-
code/modules/vending/cigarette.dm | 9 +-
code/modules/vending/clothesmate.dm | 7 +-
code/modules/vending/coffee.dm | 5 +-
code/modules/vending/cola.dm | 27 +-
code/modules/vending/engineering.dm | 14 +-
code/modules/vending/engivend.dm | 4 +-
code/modules/vending/games.dm | 14 +-
code/modules/vending/liberation.dm | 16 +-
code/modules/vending/liberation_toy.dm | 19 +-
code/modules/vending/magivend.dm | 21 +-
code/modules/vending/medical.dm | 10 +-
code/modules/vending/medical_wall.dm | 20 +-
code/modules/vending/megaseed.dm | 8 +-
code/modules/vending/nutrimax.dm | 9 +-
code/modules/vending/plasmaresearch.dm | 7 +-
code/modules/vending/robotics.dm | 13 +-
code/modules/vending/security.dm | 17 +-
code/modules/vending/snack.dm | 3 +-
code/modules/vending/sovietsoda.dm | 12 +-
code/modules/vending/sustenance.dm | 15 +-
code/modules/vending/toys.dm | 3 +-
code/modules/vending/wardrobes.dm | 141 ++-
code/modules/vending/youtool.dm | 9 +-
icons/obj/modular_laptop.dmi | Bin 31654 -> 31910 bytes
icons/obj/modular_tablet.dmi | Bin 18695 -> 20551 bytes
icons/obj/vending.dmi | Bin 115969 -> 128713 bytes
icons/obj/vending_restock.dmi | Bin 2524 -> 2816 bytes
sound/machines/scanbuzz.ogg | Bin 0 -> 11613 bytes
tgstation.dme | 4 +
tgui/packages/tgui/components/Tooltip.tsx | 152 +++-
.../tgui/interfaces/AntagInfoClockwork.tsx | 108 +++
.../packages/tgui/interfaces/ChemDispenser.js | 8 +-
tgui/packages/tgui/interfaces/Secrets.js | 64 +-
tgui/packages/tgui/interfaces/Vending.tsx | 7 +-
.../tgui/interfaces/common/AtmosControls.js | 2 +-
.../tgui/interfaces/common/BeakerContents.js | 8 +-
.../tgui/interfaces/common/Materials.tsx | 153 ++++
.../tgui/interfaces/common/PortableAtmos.js | 9 +-
.../tgui/interfaces/common/RecipeLookup.js | 80 +-
tgui/packages/tgui/layouts/NtosWindow.js | 6 +-
.../tgui/styles/components/Tooltip.scss | 2 +-
117 files changed, 3049 insertions(+), 1868 deletions(-)
create mode 100644 code/__DEFINES/text.dm
create mode 100644 code/__DEFINES/uplink.dm
create mode 100644 code/modules/balloon_alert/balloon_alert.dm
create mode 100644 sound/machines/scanbuzz.ogg
create mode 100644 tgui/packages/tgui/interfaces/AntagInfoClockwork.tsx
create mode 100644 tgui/packages/tgui/interfaces/common/Materials.tsx
diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index 9a778aafc7..11189cf1af 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -111,3 +111,12 @@ GLOBAL_LIST_EMPTY(living_heart_cache) //A list of all living hearts in existance
#define BLOB_SPREAD_COST 4
#define BLOB_ATTACK_REFUND 2 //blob refunds this much if it attacks and doesn't spread
#define BLOB_REFLECTOR_COST 15
+
+/// How many telecrystals a normal traitor starts with
+#define TELECRYSTALS_DEFAULT 20
+/// How many telecrystals mapper/admin only "precharged" uplink implant
+#define TELECRYSTALS_PRELOADED_IMPLANT 10
+/// The normal cost of an uplink implant; used for calcuating how many
+/// TC to charge someone if they get a free implant through choice or
+/// because they have nothing else that supports an implant.
+#define UPLINK_IMPLANT_TELECRYSTAL_COST 4
diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm
index 3a0af1394a..664b8c0c0d 100644
--- a/code/__DEFINES/colors.dm
+++ b/code/__DEFINES/colors.dm
@@ -1,65 +1,119 @@
// This is eventually for wjohn to add more color standardization stuff like I keep asking him >:(
-#define COLOR_INPUT_DISABLED "#F0F0F0"
-#define COLOR_INPUT_ENABLED "#D3B5B5"
-#define COLOR_FLOORTILE_GRAY "#8D8B8B"
-#define COLOR_ALMOST_BLACK "#333333"
-#define COLOR_BLACK "#000000"
-#define COLOR_RED "#FF0000"
-#define COLOR_RED_LIGHT "#FF3333"
-#define COLOR_MAROON "#800000"
-#define COLOR_YELLOW "#FFFF00"
-#define COLOR_OLIVE "#808000"
-#define COLOR_LIME "#32CD32"
-#define COLOR_GREEN "#008000"
-#define COLOR_CYAN "#00FFFF"
-#define COLOR_TEAL "#008080"
-#define COLOR_BLUE "#0000FF"
-#define COLOR_BLUE_LIGHT "#33CCFF"
-#define COLOR_NAVY "#000080"
-#define COLOR_PINK "#FFC0CB"
-#define COLOR_MAGENTA "#FF00FF"
-#define COLOR_PURPLE "#800080"
-#define COLOR_ORANGE "#FF9900"
-#define COLOR_BEIGE "#CEB689"
-#define COLOR_BLUE_GRAY "#75A2BB"
-#define COLOR_BROWN "#BA9F6D"
-#define COLOR_SOFT_RED "#FA8282"
-#define COLOR_DARK_BROWN "#997C4F"
-#define COLOR_DARK_ORANGE "#C3630C"
-#define COLOR_GREEN_GRAY "#99BB76"
-#define COLOR_RED_GRAY "#B4696A"
-#define COLOR_PALE_BLUE_GRAY "#98C5DF"
-#define COLOR_PALE_GREEN_GRAY "#B7D993"
-#define COLOR_PALE_ORANGE "#FFC066"
-#define COLOR_PALE_RED_GRAY "#D59998"
-#define COLOR_PALE_PURPLE_GRAY "#CBB1CA"
-#define COLOR_PURPLE_GRAY "#AE8CA8"
+#define COLOR_INPUT_DISABLED "#F0F0F0"
+#define COLOR_INPUT_ENABLED "#D3B5B5"
-// Color defines used by the assembly detailer.
-#define COLOR_ASSEMBLY_BLACK "#545454"
-#define COLOR_ASSEMBLY_BGRAY "#9497AB"
-#define COLOR_ASSEMBLY_WHITE "#E2E2E2"
-#define COLOR_ASSEMBLY_RED "#CC4242"
-#define COLOR_ASSEMBLY_ORANGE "#E39751"
-#define COLOR_ASSEMBLY_BEIGE "#AF9366"
-#define COLOR_ASSEMBLY_BROWN "#97670E"
-#define COLOR_ASSEMBLY_GOLD "#AA9100"
-#define COLOR_ASSEMBLY_YELLOW "#CECA2B"
-#define COLOR_ASSEMBLY_GURKHA "#999875"
-#define COLOR_ASSEMBLY_LGREEN "#789876"
-#define COLOR_ASSEMBLY_GREEN "#44843C"
-#define COLOR_ASSEMBLY_LBLUE "#5D99BE"
-#define COLOR_ASSEMBLY_BLUE "#38559E"
-#define COLOR_ASSEMBLY_PURPLE "#6F6192"
-#define COLOR_ASSEMBLY_PINK "#ff4adc"
+#define COLOR_DARKMODE_BACKGROUND "#202020"
+#define COLOR_DARKMODE_DARKBACKGROUND "#171717"
+#define COLOR_DARKMODE_TEXT "#a4bad6"
-#define COLOR_WHITE "#FFFFFF"
-#define COLOR_VERY_LIGHT_GRAY "#EEEEEE"
-#define COLOR_SILVER "#C0C0C0"
-#define COLOR_GRAY "#808080"
+#define COLOR_WHITE "#FFFFFF"
+#define COLOR_VERY_LIGHT_GRAY "#EEEEEE"
+#define COLOR_SILVER "#C0C0C0"
+#define COLOR_GRAY "#808080"
+#define COLOR_FLOORTILE_GRAY "#8D8B8B"
+#define COLOR_DARK "#454545"
+#define COLOR_ALMOST_BLACK "#333333"
+#define COLOR_BLACK "#000000"
#define COLOR_HALF_TRANSPARENT_BLACK "#0000007A"
+
+#define COLOR_RED "#FF0000"
+#define COLOR_MOSTLY_PURE_RED "#FF3300"
+#define COLOR_DARK_RED "#A50824"
+#define COLOR_RED_LIGHT "#FF3333"
+#define COLOR_MAROON "#800000"
+#define COLOR_VIVID_RED "#FF3232"
+#define COLOR_LIGHT_GRAYISH_RED "#E4C7C5"
+#define COLOR_SOFT_RED "#FA8282"
+#define COLOR_BUBBLEGUM_RED "#950A0A"
+
+#define COLOR_YELLOW "#FFFF00"
+#define COLOR_VIVID_YELLOW "#FBFF23"
+#define COLOR_VERY_SOFT_YELLOW "#FAE48E"
+
+#define COLOR_OLIVE "#808000"
+#define COLOR_VIBRANT_LIME "#00FF00"
+#define COLOR_LIME "#32CD32"
+#define COLOR_DARK_LIME "#00aa00"
+#define COLOR_VERY_PALE_LIME_GREEN "#DDFFD3"
+#define COLOR_VERY_DARK_LIME_GREEN "#003300"
+#define COLOR_GREEN "#008000"
+#define COLOR_DARK_MODERATE_LIME_GREEN "#44964A"
+
+#define COLOR_CYAN "#00FFFF"
+#define COLOR_DARK_CYAN "#00A2FF"
+#define COLOR_TEAL "#008080"
+#define COLOR_BLUE "#0000FF"
+#define COLOR_STRONG_BLUE "#1919c8"
#define COLOR_BRIGHT_BLUE "#2CB2E8"
+#define COLOR_MODERATE_BLUE "#555CC2"
+#define COLOR_AMETHYST "#822BFF"
+#define COLOR_BLUE_LIGHT "#33CCFF"
+#define COLOR_NAVY "#000080"
+#define COLOR_BLUE_GRAY "#75A2BB"
+
+#define COLOR_PINK "#FFC0CB"
+#define COLOR_LIGHT_PINK "#ff3cc8"
+#define COLOR_MOSTLY_PURE_PINK "#E4005B"
+#define COLOR_MAGENTA "#FF00FF"
+#define COLOR_STRONG_MAGENTA "#B800B8"
+#define COLOR_PURPLE "#800080"
+#define COLOR_VIOLET "#B900F7"
+#define COLOR_STRONG_VIOLET "#6927c5"
+
+#define COLOR_ORANGE "#FF9900"
+#define COLOR_MOSTLY_PURE_ORANGE "#ff8000"
+#define COLOR_TAN_ORANGE "#FF7B00"
+#define COLOR_BRIGHT_ORANGE "#E2853D"
+#define COLOR_LIGHT_ORANGE "#ffc44d"
+#define COLOR_PALE_ORANGE "#FFBE9D"
+#define COLOR_BEIGE "#CEB689"
+#define COLOR_DARK_ORANGE "#C3630C"
+#define COLOR_DARK_MODERATE_ORANGE "#8B633B"
+
+#define COLOR_BROWN "#BA9F6D"
+#define COLOR_DARK_BROWN "#997C4F"
+#define COLOR_ORANGE_BROWN "#a9734f"
+
+//Color defines used by the soapstone (based on readability against grey tiles)
+#define COLOR_SOAPSTONE_PLASTIC "#a19d94"
+#define COLOR_SOAPSTONE_IRON "#b2b2b2"
+#define COLOR_SOAPSTONE_BRONZE "#FE8001"
+#define COLOR_SOAPSTONE_SILVER "#FFFFFF"
+#define COLOR_SOAPSTONE_GOLD "#FFD900"
+#define COLOR_SOAPSTONE_DIAMOND "#00ffee"
+
+#define COLOR_GREEN_GRAY "#99BB76"
+#define COLOR_RED_GRAY "#B4696A"
+#define COLOR_PALE_BLUE_GRAY "#98C5DF"
+#define COLOR_PALE_GREEN_GRAY "#B7D993"
+#define COLOR_PALE_RED_GRAY "#D59998"
+#define COLOR_PALE_PURPLE_GRAY "#CBB1CA"
+#define COLOR_PURPLE_GRAY "#AE8CA8"
+
+//Color defines used by the assembly detailer.
+#define COLOR_ASSEMBLY_BLACK "#545454"
+#define COLOR_ASSEMBLY_BGRAY "#9497AB"
+#define COLOR_ASSEMBLY_WHITE "#E2E2E2"
+#define COLOR_ASSEMBLY_RED "#CC4242"
+#define COLOR_ASSEMBLY_ORANGE "#E39751"
+#define COLOR_ASSEMBLY_BEIGE "#AF9366"
+#define COLOR_ASSEMBLY_BROWN "#97670E"
+#define COLOR_ASSEMBLY_GOLD "#AA9100"
+#define COLOR_ASSEMBLY_YELLOW "#CECA2B"
+#define COLOR_ASSEMBLY_GURKHA "#999875"
+#define COLOR_ASSEMBLY_LGREEN "#789876"
+#define COLOR_ASSEMBLY_GREEN "#44843C"
+#define COLOR_ASSEMBLY_LBLUE "#5D99BE"
+#define COLOR_ASSEMBLY_BLUE "#38559E"
+#define COLOR_ASSEMBLY_PURPLE "#6F6192"
+
+///Colors for xenobiology vatgrowing
+#define COLOR_SAMPLE_YELLOW "#c0b823"
+#define COLOR_SAMPLE_PURPLE "#342941"
+#define COLOR_SAMPLE_GREEN "#98b944"
+#define COLOR_SAMPLE_BROWN "#91542d"
+#define COLOR_SAMPLE_GRAY "#5e5856"
///Main colors for UI themes
#define COLOR_THEME_MIDNIGHT "#6086A0"
@@ -69,3 +123,75 @@
#define COLOR_THEME_OPERATIVE "#B8221F"
#define COLOR_THEME_GLASS "#75A4C4"
#define COLOR_THEME_CLOCKWORK "#CFBA47"
+
+///Colors for eigenstates
+#define COLOR_PERIWINKLEE "#9999FF"
+/**
+ * Some defines to generalise colours used in lighting.
+ *
+ * Important note: colors can end up significantly different from the basic html picture, especially when saturated
+ */
+/// Bright but quickly dissipating neon green. rgb(100, 200, 100)
+#define LIGHT_COLOR_GREEN "#64C864"
+/// Electric green. rgb(0, 255, 0)
+#define LIGHT_COLOR_ELECTRIC_GREEN "#00FF00"
+/// Cold, diluted blue. rgb(100, 150, 250)
+#define LIGHT_COLOR_BLUE "#6496FA"
+/// Light blueish green. rgb(125, 225, 175)
+#define LIGHT_COLOR_BLUEGREEN "#7DE1AF"
+/// Diluted cyan. rgb(125, 225, 225)
+#define LIGHT_COLOR_CYAN "#7DE1E1"
+/// Electric cyan rgb(0, 255, 255)
+#define LIGHT_COLOR_ELECTRIC_CYAN "#00FFFF"
+/// More-saturated cyan. rgb(64, 206, 255)
+#define LIGHT_COLOR_LIGHT_CYAN "#40CEFF"
+/// Saturated blue. rgb(51, 117, 248)
+#define LIGHT_COLOR_DARK_BLUE "#6496FA"
+/// Diluted, mid-warmth pink. rgb(225, 125, 225)
+#define LIGHT_COLOR_PINK "#E17DE1"
+/// Dimmed yellow, leaning kaki. rgb(225, 225, 125)
+#define LIGHT_COLOR_YELLOW "#E1E17D"
+/// Clear brown, mostly dim. rgb(150, 100, 50)
+#define LIGHT_COLOR_BROWN "#966432"
+/// Mostly pure orange. rgb(250, 150, 50)
+#define LIGHT_COLOR_ORANGE "#FA9632"
+/// Light Purple. rgb(149, 44, 244)
+#define LIGHT_COLOR_PURPLE "#952CF4"
+/// Less-saturated light purple. rgb(155, 81, 255)
+#define LIGHT_COLOR_LAVENDER "#9B51FF"
+///slightly desaturated bright yellow.
+#define LIGHT_COLOR_HOLY_MAGIC "#FFF743"
+/// deep crimson
+#define LIGHT_COLOR_BLOOD_MAGIC "#D00000"
+
+/* These ones aren't a direct colour like the ones above, because nothing would fit */
+/// Warm orange color, leaning strongly towards yellow. rgb(250, 160, 25)
+#define LIGHT_COLOR_FIRE "#FAA019"
+/// Very warm yellow, leaning slightly towards orange. rgb(196, 138, 24)
+#define LIGHT_COLOR_LAVA "#C48A18"
+/// Bright, non-saturated red. Leaning slightly towards pink for visibility. rgb(250, 100, 75)
+#define LIGHT_COLOR_FLARE "#FA644B"
+/// Weird color, between yellow and green, very slimy. rgb(175, 200, 75)
+#define LIGHT_COLOR_SLIME_LAMP "#AFC84B"
+/// Extremely diluted yellow, close to skin color (for some reason). rgb(250, 225, 175)
+#define LIGHT_COLOR_TUNGSTEN "#FAE1AF"
+/// Barely visible cyan-ish hue, as the doctor prescribed. rgb(240, 250, 250)
+#define LIGHT_COLOR_HALOGEN "#F0FAFA"
+
+//The GAGS greyscale_colors for each department's computer/machine circuits
+#define CIRCUIT_COLOR_GENERIC "#1A7A13"
+#define CIRCUIT_COLOR_COMMAND "#1B4594"
+#define CIRCUIT_COLOR_SECURITY "#9A151E"
+#define CIRCUIT_COLOR_SCIENCE "#BC4A9B"
+#define CIRCUIT_COLOR_SERVICE "#92DCBA"
+#define CIRCUIT_COLOR_MEDICAL "#00CCFF"
+#define CIRCUIT_COLOR_ENGINEERING "#F8D700"
+#define CIRCUIT_COLOR_SUPPLY "#C47749"
+
+/// Colors for pride week
+#define COLOR_PRIDE_RED "#FF6666"
+#define COLOR_PRIDE_ORANGE "#FC9F3C"
+#define COLOR_PRIDE_YELLOW "#EAFF51"
+#define COLOR_PRIDE_GREEN "#41FC66"
+#define COLOR_PRIDE_BLUE "#42FFF2"
+#define COLOR_PRIDE_PURPLE "#5D5DFC"
diff --git a/code/__DEFINES/layers_planes.dm b/code/__DEFINES/layers_planes.dm
index c7be89721e..a2d6bb4da6 100644
--- a/code/__DEFINES/layers_planes.dm
+++ b/code/__DEFINES/layers_planes.dm
@@ -38,6 +38,9 @@
#define CHAT_PLANE -1 //We don't want heard messages to be hidden by FoV.
+/// Plane for balloon text (text that fades up)
+#define BALLOON_CHAT_PLANE -1.2
+
#define CHAT_LAYER 12.0001 // Do not insert layers between these two values
#define CHAT_LAYER_MAX 12.9999
diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm
index 95d91da14a..e448cd23d4 100644
--- a/code/__DEFINES/lighting.dm
+++ b/code/__DEFINES/lighting.dm
@@ -30,31 +30,6 @@
//Important note on colors. Colors can end up significantly different from the basic html picture, especially when saturated
#define LIGHT_COLOR_WHITE "#FFFFFF"
#define LIGHT_COLOR_RED "#FA8282" //Warm but extremely diluted red. rgb(250, 130, 130)
-#define LIGHT_COLOR_GREEN "#64C864" //Bright but quickly dissipating neon green. rgb(100, 200, 100)
-#define LIGHT_COLOR_BLUE "#6496FA" //Cold, diluted blue. rgb(100, 150, 250)
-
-#define LIGHT_COLOR_BLUEGREEN "#7DE1AF" //Light blueish green. rgb(125, 225, 175)
-#define LIGHT_COLOR_PALEBLUE "#7DAFE1" //A pale blue-ish color. rgb(125, 175, 225)
-#define LIGHT_COLOR_CYAN "#7DE1E1" //Diluted cyan. rgb(125, 225, 225)
-#define LIGHT_COLOR_LIGHT_CYAN "#40CEFF" //More-saturated cyan. rgb(64, 206, 255)
-#define LIGHT_COLOR_DARK_BLUE "#6496FA" //Saturated blue. rgb(51, 117, 248)
-#define LIGHT_COLOR_PINK "#E17DE1" //Diluted, mid-warmth pink. rgb(225, 125, 225)
-#define LIGHT_COLOR_YELLOW "#E1E17D" //Dimmed yellow, leaning kaki. rgb(225, 225, 125)
-#define LIGHT_COLOR_BROWN "#966432" //Clear brown, mostly dim. rgb(150, 100, 50)
-#define LIGHT_COLOR_ORANGE "#FA9632" //Mostly pure orange. rgb(250, 150, 50)
-#define LIGHT_COLOR_PURPLE "#952CF4" //Light Purple. rgb(149, 44, 244)
-#define LIGHT_COLOR_LAVENDER "#9B51FF" //Less-saturated light purple. rgb(155, 81, 255)
-
-#define LIGHT_COLOR_HOLY_MAGIC "#FFF743" //slightly desaturated bright yellow.
-#define LIGHT_COLOR_BLOOD_MAGIC "#D00000" //deep crimson
-
-//These ones aren't a direct colour like the ones above, because nothing would fit
-#define LIGHT_COLOR_FIRE "#FAA019" //Warm orange color, leaning strongly towards yellow. rgb(250, 160, 25)
-#define LIGHT_COLOR_LAVA "#C48A18" //Very warm yellow, leaning slightly towards orange. rgb(196, 138, 24)
-#define LIGHT_COLOR_FLARE "#FA644B" //Bright, non-saturated red. Leaning slightly towards pink for visibility. rgb(250, 100, 75)
-#define LIGHT_COLOR_SLIME_LAMP "#AFC84B" //Weird color, between yellow and green, very slimy. rgb(175, 200, 75)
-#define LIGHT_COLOR_TUNGSTEN "#FAE1AF" //Extremely diluted yellow, close to skin color (for some reason). rgb(250, 225, 175)
-#define LIGHT_COLOR_HALOGEN "#F0FAFA" //Barely visible cyan-ish hue, as the doctor prescribed. rgb(240, 250, 250)
#define LIGHT_RANGE_FIRE 3 //How many tiles standard fires glow.
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index 30899c0ca2..91b7c2ac7b 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -29,10 +29,10 @@
* if the arguments to addtimer are the same as an existing timer, it doesn't create a new timer,
* and returns the id of the existing timer
*/
-#define TIMER_UNIQUE (1<<0)
+#define TIMER_UNIQUE (1<<0)
///For unique timers: Replace the old timer rather then not start this one
-#define TIMER_OVERRIDE (1<<1)
+#define TIMER_OVERRIDE (1<<1)
/**
* Timing should be based on how timing progresses on clients, not the server.
@@ -41,20 +41,23 @@
* should only be used in conjuction with things that have to progress client side, such as
* animate() or sound()
*/
-#define TIMER_CLIENT_TIME (1<<2)
+#define TIMER_CLIENT_TIME (1<<2)
///Timer can be stopped using deltimer()
-#define TIMER_STOPPABLE (1<<3)
+#define TIMER_STOPPABLE (1<<3)
///prevents distinguishing identical timers with the wait variable
///
///To be used with TIMER_UNIQUE
-#define TIMER_NO_HASH_WAIT (1<<4)
+#define TIMER_NO_HASH_WAIT (1<<4)
///Loops the timer repeatedly until qdeleted
///
///In most cases you want a subsystem instead, so don't use this unless you have a good reason
-#define TIMER_LOOP (1<<5)
+#define TIMER_LOOP (1<<5)
+
+///Delete the timer on parent datum Destroy() and when deltimer'd
+#define TIMER_DELETE_ME (1<<6)
///Empty ID define
#define TIMER_ID_NULL -1
@@ -188,6 +191,7 @@
#define FIRE_PRIORITY_CALLBACKS 600
// #define FIRE_PRIORITY_EXPLOSIONS 666
#define FIRE_PRIORITY_TIMER 700
+#define FIRE_PRIORITY_SOUND_LOOPS 800
#define FIRE_PRIORITY_INPUT 1000 // This must always always be the max highest priority. Player input must never be lost.
// SS runlevels
diff --git a/code/__DEFINES/text.dm b/code/__DEFINES/text.dm
new file mode 100644
index 0000000000..58c24747e0
--- /dev/null
+++ b/code/__DEFINES/text.dm
@@ -0,0 +1,5 @@
+/// Prepares a text to be used for maptext. Use this so it doesn't look hideous.
+#define MAPTEXT(text) {"[##text]"}
+
+/// Macro from Lummox used to get height from a MeasureText proc
+#define WXH_TO_HEIGHT(x) text2num(copytext(x, findtextEx(x, "x") + 1))
diff --git a/code/__DEFINES/uplink.dm b/code/__DEFINES/uplink.dm
new file mode 100644
index 0000000000..4aad3e6b88
--- /dev/null
+++ b/code/__DEFINES/uplink.dm
@@ -0,0 +1,10 @@
+// These are used in uplink_devices.dm to determine whether or not an item is purchasable.
+
+/// This item is purchasable to traitors
+#define UPLINK_TRAITORS (1 << 0)
+
+/// This item is purchasable to nuke ops
+#define UPLINK_NUKE_OPS (1 << 1)
+
+/// This item is purchasable to clown ops
+#define UPLINK_CLOWN_OPS (1 << 2)
diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm
index 69dd54e810..966138eab7 100644
--- a/code/__HELPERS/_lists.dm
+++ b/code/__HELPERS/_lists.dm
@@ -1,32 +1,45 @@
/*
* Holds procs to help with list operations
* Contains groups:
- * Misc
- * Sorting
+ * Misc
+ * Sorting
*/
/*
* Misc
*/
-#define LAZYINITLIST(L) if (!L) L = list()
+#define LAZYINITLIST(L) if (!L) { L = list(); }
#define UNSETEMPTY(L) if (L && !length(L)) L = null
-#define LAZYCOPY(L) (L ? L.Copy() : list() )
+///Like LAZYCOPY - copies an input list if the list has entries, If it doesn't the assigned list is nulled
+#define LAZYLISTDUPLICATE(L) (L ? L.Copy() : null )
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } }
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
#define LAZYOR(L, I) if(!L) { L = list(); } L |= I;
#define LAZYFIND(L, V) (L ? L.Find(V) : 0)
+///returns L[I] if L exists and I is a valid index of L, runtimes if L is not a list
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
#define LAZYLEN(L) length(L)
-//Sets a list to null
+///Sets a list to null
#define LAZYNULL(L) L = null
-#define LAZYCLEARLIST(L) if(L) L.Cut()
-#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
-#define reverseList(L) reverseRange(L.Copy())
+#define LAZYADDASSOC_TG(L, K, V) if(!L) { L = list(); } L[K] += V;
+///This is used to add onto lazy assoc list when the value you're adding is a /list/. This one has extra safety over lazyaddassoc because the value could be null (and thus cant be used to += objects)
#define LAZYADDASSOC(L, K, V) if(!L) { L = list(); } L[K] += list(V);
#define LAZYREMOVEASSOC(L, K, V) if(L) { if(L[K]) { L[K] -= V; if(!length(L[K])) L -= K; } if(!length(L)) L = null; }
#define LAZYACCESSASSOC(L, I, K) L ? L[I] ? L[I][K] ? L[I][K] : null : null : null
+#define QDEL_LAZYLIST(L) for(var/I in L) qdel(I); L = null;
+//These methods don't null the list
+#define LAZYCOPY(L) (L ? L.Copy() : list() ) //Use LAZYLISTDUPLICATE instead if you want it to null with no entries
+#define LAZYCLEARLIST(L) if(L) L.Cut() // Consider LAZYNULL instead
+#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
+#define reverseList(L) reverseRange(L.Copy())
+
+/// Performs an insertion on the given lazy list with the given key and value. If the value already exists, a new one will not be made.
+#define LAZYORASSOCLIST(lazy_list, key, value) \
+ LAZYINITLIST(lazy_list); \
+ LAZYINITLIST(lazy_list[key]); \
+ lazy_list[key] |= value;
/// Passed into BINARY_INSERT to compare keys
#define COMPARE_KEY __BIN_LIST[__BIN_MID]
@@ -69,7 +82,7 @@
} while(FALSE)
//Returns a list in plain english as a string
-/proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "")
+/proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" )
var/total = length(input)
switch(total)
if (0)
@@ -92,6 +105,7 @@
/**
* English_list but associative supporting. Higher overhead.
+ * @depricated
*/
/proc/english_list_assoc(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "")
var/total = length(input)
@@ -119,6 +133,7 @@
return "[output][and_text][input[index]]"
//Returns list element or null. Should prevent "index out of bounds" error.
+/// @depricated
/proc/listgetindex(list/L, index)
if(LAZYLEN(L))
if(isnum(index) && ISINTEGER(index))
@@ -129,17 +144,19 @@
return
//Return either pick(list) or null if list is not of type /list or is empty
+/// @depricated
/proc/safepick(list/L)
if(LAZYLEN(L))
return pick(L)
//Checks if the list is empty
+/// @depricated
/proc/isemptylist(list/L)
if(!L.len)
return TRUE
return FALSE
-//Checks for specific types in a list
+//Checks for specific types in a listc
/proc/is_type_in_list(atom/A, list/L)
if(!LAZYLEN(L) || !A)
return FALSE
@@ -181,45 +198,45 @@
/proc/typecache_filter_list_reverse(list/atoms, list/typecache)
RETURN_TYPE(/list)
. = list()
- for(var/thing in atoms)
- var/atom/A = thing
- if(!typecache[A.type])
- . += A
+ for(var/atom/atom as anything in atoms)
+ if(!typecache[atom.type])
+ . += atom
/proc/typecache_filter_multi_list_exclusion(list/atoms, list/typecache_include, list/typecache_exclude)
. = list()
- for(var/thing in atoms)
- var/atom/A = thing
- if(typecache_include[A.type] && !typecache_exclude[A.type])
- . += A
+ for(var/atom/atom as anything in atoms)
+ if(typecache_include[atom.type] && !typecache_exclude[atom.type])
+ . += atom
-//Like typesof() or subtypesof(), but returns a typecache instead of a list
+///Like typesof() or subtypesof(), but returns a typecache instead of a list
/proc/typecacheof(path, ignore_root_path, only_root_path = FALSE)
if(ispath(path))
- var/list/types = list()
+ var/list/types
+ var/list/output = list()
if(only_root_path)
- types = list(path)
+ output[path] = TRUE
else
types = ignore_root_path ? subtypesof(path) : typesof(path)
- var/list/L = list()
- for(var/T in types)
- L[T] = TRUE
- return L
+ for(var/T in types)
+ output[T] = TRUE
+ return output
else if(islist(path))
var/list/pathlist = path
- var/list/L = list()
+ var/list/output = list()
if(ignore_root_path)
- for(var/P in pathlist)
- for(var/T in subtypesof(P))
- L[T] = TRUE
+ for(var/current_path in pathlist)
+ for(var/subtype in subtypesof(current_path))
+ output[subtype] = TRUE
+ return output
+
+ if(only_root_path)
+ for(var/current_path in pathlist)
+ output[current_path] = TRUE
else
- for(var/P in pathlist)
- if(only_root_path)
- L[P] = TRUE
- else
- for(var/T in typesof(P))
- L[T] = TRUE
- return L
+ for(var/current_path in pathlist)
+ for(var/subpath in typesof(current_path))
+ output[subpath] = TRUE
+ return output
/proc/typecacheof_assoc_list(list/pathlist, ignore_root_path = FALSE)
. = list()
@@ -278,9 +295,10 @@
//Picks a random element from a list based on a weighting system:
//1. Adds up the total of weights for each element
-//2. Gets the total from 0% to 100% of previous total value.
+//2. Gets a number between 1 and that total
//3. For each element in the list, subtracts its weighting from that number
//4. If that makes the number 0 or less, return that element.
+//Will output null sometimes if you use decimals (e.g. 0.1 instead of 10) as rand() uses integers, not floats
/proc/pickweight(list/L, base_weight = 1)
var/total = 0
var/item
@@ -289,12 +307,14 @@
L[item] = base_weight
total += L[item]
- total = rand() * total
+ total = rand(base_weight, total)
for (item in L)
- total -= L[item]
+ total -=L [item]
if (total <= 0)
return item
+ return null
+
/proc/pickweightAllowZero(list/L) //The original pickweight proc will sometimes pick entries with zero weight. I'm not sure if changing the original will break anything, so I left it be.
var/total = 0
var/item
@@ -366,13 +386,13 @@
//Results will have a length of quality
return results
-//Pick a random element from the list and remove it from the list.
+/// Pick a random element from the list and remove it from the list.
/proc/pick_n_take(list/L)
RETURN_TYPE(L[_].type)
if(L.len)
var/picked = rand(1,L.len)
. = L[picked]
- L.Cut(picked,picked+1) //Cut is far more efficient that Remove()
+ L.Cut(picked,picked+1) //Cut is far more efficient that Remove()
//Pick a random element from the list by weight and remove it from the list.
//Result is returned as a list in the format list(key, value)
@@ -468,7 +488,7 @@
//uses sortList() but uses the var's name specifically. This should probably be using mergeAtom() instead
/proc/sortNames(list/L, order=1)
- return sortTim(L, order >= 0 ? /proc/cmp_name_asc : /proc/cmp_name_dsc)
+ return sortTim(L.Copy(), order >= 0 ? /proc/cmp_name_asc : /proc/cmp_name_dsc)
//Converts a bitfield to a list of numbers (or words if a wordlist is provided)
@@ -504,10 +524,12 @@
if (L[i] == val)
.++
+/// Returns datum/data/record
/proc/find_record(field, value, list/L)
for(var/datum/data/record/R in L)
if(R.fields[field] == value)
return R
+ return null
//Move a single element from position fromIndex within a list, to position toIndex
@@ -517,10 +539,10 @@
//fromIndex and toIndex must be in the range [1,L.len+1]
//This will preserve associations ~Carnie
/proc/moveElement(list/L, fromIndex, toIndex)
- if(fromIndex == toIndex || fromIndex+1 == toIndex) //no need to move
+ if(fromIndex == toIndex || fromIndex+1 == toIndex) //no need to move
return
if(fromIndex > toIndex)
- ++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one
+ ++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one
L.Insert(toIndex, null)
L.Swap(fromIndex, toIndex)
@@ -532,10 +554,10 @@
//This will preserve associations ~Carnie
/proc/moveRange(list/L, fromIndex, toIndex, len=1)
var/distance = abs(toIndex - fromIndex)
- if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements
+ if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements
if(fromIndex <= toIndex)
- return //no need to move
- fromIndex += len //we want to shift left instead of right
+ return //no need to move
+ fromIndex += len //we want to shift left instead of right
for(var/i=0, i distance) //there is an overlap, therefore swapping each element will require more swaps than inserting new elements
+ if(len > distance) //there is an overlap, therefore swapping each element will require more swaps than inserting new elements
if(fromIndex < toIndex)
toIndex += len
else
@@ -601,6 +623,12 @@
if(D.vars[varname] == value)
return D
+//remove all nulls from a list
+/proc/removeNullsFromList(list/L)
+ while(L.Remove(null))
+ continue
+ return L
+
//Copies a list, and all lists inside it recusively
//Does not copy any other reference type
/proc/deepCopyList(list/l)
@@ -633,11 +661,6 @@
used_key_list[input_key] = 1
return input_key
-#if DM_VERSION > 514
-#error Remie said that lummox was adding a way to get a lists
-#error contents via list.values, if that is true remove this
-#error otherwise, update the version and bug lummox
-#endif
//Flattens a keyed list into a list of it's contents
/proc/flatten_list(list/key_list)
if(!islist(key_list))
@@ -714,6 +737,29 @@
ret += key
return ret
+/proc/compare_list(list/l,list/d)
+ if(!islist(l) || !islist(d))
+ return FALSE
+
+ if(l.len != d.len)
+ return FALSE
+
+ for(var/i in 1 to l.len)
+ if(l[i] != d[i])
+ return FALSE
+
+ return TRUE
+
+#define LAZY_LISTS_OR(left_list, right_list)\
+ ( length(left_list)\
+ ? length(right_list)\
+ ? (left_list | right_list)\
+ : left_list.Copy()\
+ : length(right_list)\
+ ? right_list.Copy()\
+ : null\
+ )
+
/proc/is_type_in_ref_list(path, list/L)
if(!ispath(path))//not a path
return
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index e55faab1be..5578b1f22a 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -396,12 +396,16 @@
/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480)
if(!isobj(O))
O = new /atom/movable/screen/text()
- O.maptext = maptext
+ O.maptext = MAPTEXT(maptext)
O.maptext_height = maptext_height
O.maptext_width = maptext_width
O.screen_loc = screen_loc
return O
+/// Removes an image from a client's `.images`. Useful as a callback.
+/proc/remove_image_from_client(image/image, client/remove_from)
+ remove_from?.images -= image
+
/proc/remove_images_from_clients(image/I, list/show_to)
for(var/client/C in show_to)
C.images -= I
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 19aac1aee0..888aec5a85 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -86,16 +86,6 @@
// Keybindings
init_keybindings()
- //Uplink Items
- for(var/path in subtypesof(/datum/uplink_item))
- var/datum/uplink_item/I = path
- if(!initial(I.item)) //We add categories to a separate list.
- GLOB.uplink_categories |= initial(I.category)
- continue
- GLOB.uplink_items += path
- //(sub)typesof entries are listed by the order they are loaded in the code, so we'll have to rearrange them here.
- GLOB.uplink_items = sortList(GLOB.uplink_items, /proc/cmp_uplink_items_dsc)
-
init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes)
INVOKE_ASYNC(GLOBAL_PROC, /proc/init_ref_coin_values) //so the current procedure doesn't sleep because of UNTIL()
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index fabe70c929..579126257f 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -992,7 +992,7 @@ world
letter = lowertext(letter)
var/image/text_image = new(loc = A)
- text_image.maptext = "[letter]"
+ text_image.maptext = MAPTEXT("[letter]")
text_image.pixel_x = 7
text_image.pixel_y = 5
qdel(atom_icon)
diff --git a/code/__HELPERS/radio.dm b/code/__HELPERS/radio.dm
index dc52299025..e761a3a23c 100644
--- a/code/__HELPERS/radio.dm
+++ b/code/__HELPERS/radio.dm
@@ -1,4 +1,4 @@
-// Ensure the frequency is within bounds of what it should be sending/receiving at
+/// Ensure the frequency is within bounds of what it should be sending/receiving at
/proc/sanitize_frequency(frequency, free = FALSE)
frequency = round(frequency)
if(free)
@@ -8,12 +8,26 @@
if(!(. % 2)) // Ensure the last digit is an odd number
. += 1
-// Format frequency by moving the decimal.
+/// Format frequency by moving the decimal.
/proc/format_frequency(frequency)
frequency = text2num(frequency)
return "[round(frequency / 10)].[frequency % 10]"
-//Opposite of format, returns as a number
+///Opposite of format, returns as a number
/proc/unformat_frequency(frequency)
frequency = text2num(frequency)
return frequency * 10
+
+///returns a random unused frequency between MIN_FREE_FREQ & MAX_FREE_FREQ if free = TRUE, and MIN_FREQ & MAX_FREQ if FALSE
+/proc/return_unused_frequency(free = FALSE)
+ var/start = free ? MIN_FREE_FREQ : MIN_FREQ
+ var/end = free ? MAX_FREE_FREQ : MAX_FREQ
+
+ var/freq_to_check = 0
+ do
+ freq_to_check = rand(start, end)
+ if(!(freq_to_check % 2)) // Ensure the last digit is an odd number
+ freq_to_check++
+ while((freq_to_check == 0) || ("[freq_to_check]" in GLOB.reverseradiochannels))
+
+ return freq_to_check
diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm
index 0110d43e04..599f90b0af 100644
--- a/code/_globalvars/lists/flavor_misc.dm
+++ b/code/_globalvars/lists/flavor_misc.dm
@@ -216,7 +216,13 @@ GLOBAL_LIST_INIT(jumpsuitlist, list(PREF_SUIT, PREF_SKIRT))
#define UPLINK_PDA "PDA"
#define UPLINK_RADIO "Radio"
#define UPLINK_PEN "Pen" //like a real spy!
-GLOBAL_LIST_INIT(uplink_spawn_loc_list, list(UPLINK_PDA, UPLINK_RADIO, UPLINK_PEN))
+#define UPLINK_IMPLANT "Implant"
+#define UPLINK_IMPLANT_WITH_PRICE "[UPLINK_IMPLANT] (-[UPLINK_IMPLANT_TELECRYSTAL_COST] TC)"
+// What we show to the user
+GLOBAL_LIST_INIT(uplink_spawn_loc_list, list(UPLINK_PDA, UPLINK_RADIO, UPLINK_PEN, UPLINK_IMPLANT_WITH_PRICE))
+// What is actually saved; if the uplink implant price changes, it won't affect save files then
+GLOBAL_LIST_INIT(uplink_spawn_loc_list_save, list(UPLINK_PDA, UPLINK_RADIO, UPLINK_PEN, UPLINK_IMPLANT))
+
//List of cached alpha masked icons.
GLOBAL_LIST_EMPTY(alpha_masked_worn_icons)
diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm
index 9abc37c7e4..14969e6c92 100644
--- a/code/_globalvars/lists/objects.dm
+++ b/code/_globalvars/lists/objects.dm
@@ -19,7 +19,6 @@ GLOBAL_LIST(chemical_reactions_list) //list of all /datum/chemical_reaction d
GLOBAL_LIST(chemical_reagents_list) //list of all /datum/reagent datums indexed by reagent id. Used by chemistry stuff
GLOBAL_LIST_EMPTY(tech_list) //list of all /datum/tech datums indexed by id.
GLOBAL_LIST_EMPTY(surgeries_list) //list of all surgeries by name, associated with their path.
-GLOBAL_LIST_EMPTY(uplink_items) //list of all uplink item typepaths, ascendingly sorted by their initial name.
GLOBAL_LIST_EMPTY(uplink_categories) //list of all uplink categories, listed by the order they are loaded in code. Be careful.
GLOBAL_LIST_EMPTY(crafting_recipes) //list of all table craft recipes
GLOBAL_LIST_EMPTY(rcd_list) //list of Rapid Construction Devices.
diff --git a/code/_onclick/hud/credits.dm b/code/_onclick/hud/credits.dm
index 974f158323..aaf423ecbc 100644
--- a/code/_onclick/hud/credits.dm
+++ b/code/_onclick/hud/credits.dm
@@ -44,7 +44,7 @@
icon = I
parent = P
icon_state = credited
- maptext = credited
+ maptext = MAPTEXT(credited)
maptext_x = world.icon_size + 8
maptext_y = (world.icon_size / 2) - 4
maptext_width = world.icon_size * 3
diff --git a/code/controllers/subsystem/processing/processing.dm b/code/controllers/subsystem/processing/processing.dm
index 661eafc2a5..058645da49 100644
--- a/code/controllers/subsystem/processing/processing.dm
+++ b/code/controllers/subsystem/processing/processing.dm
@@ -17,7 +17,6 @@ SUBSYSTEM_DEF(processing)
/datum/controller/subsystem/processing/fire(resumed = FALSE)
if (!resumed)
currentrun = processing.Copy()
- var/delta_time = (flags & SS_TICKER)? (wait * world.tick_lag * 0.1) : (wait * 0.1)
//cache for sanic speed (lists are references anyways)
var/list/current_run = currentrun
@@ -26,7 +25,7 @@ SUBSYSTEM_DEF(processing)
current_run.len--
if(QDELETED(thing))
processing -= thing
- else if(thing.process(delta_time) == PROCESS_KILL)
+ else if(thing.process(wait * 0.1) == PROCESS_KILL)
// fully stop so that a future START_PROCESSING will work
STOP_PROCESSING(src, thing)
if (MC_TICK_CHECK)
@@ -47,5 +46,5 @@ SUBSYSTEM_DEF(processing)
* If you override this do not call parent, as it will return PROCESS_KILL. This is done to prevent objects that dont override process() from staying in the processing list
*/
/datum/proc/process(delta_time)
- SHOULD_NOT_SLEEP(TRUE)
+ set waitfor = FALSE
return PROCESS_KILL
diff --git a/code/controllers/subsystem/sounds.dm b/code/controllers/subsystem/sounds.dm
index fa9ba3c472..7d94a3d21d 100644
--- a/code/controllers/subsystem/sounds.dm
+++ b/code/controllers/subsystem/sounds.dm
@@ -4,7 +4,7 @@ SUBSYSTEM_DEF(sounds)
name = "Sounds"
flags = SS_NO_FIRE
init_order = INIT_ORDER_SOUNDS
- var/static/using_channels_max = CHANNEL_HIGHEST_AVAILABLE //BYOND max channels
+ var/static/using_channels_max = CHANNEL_HIGHEST_AVAILABLE //BYOND max channels
/// Amount of channels to reserve for random usage rather than reservations being allowed to reserve all channels. Also a nice safeguard for when someone screws up.
var/static/random_channels_min = 50
@@ -42,7 +42,7 @@ SUBSYSTEM_DEF(sounds)
var/text_channel = num2text(channel)
var/using = using_channels[text_channel]
using_channels -= text_channel
- if(using != TRUE) // datum channel
+ if(using != TRUE) // datum channel
using_channels_by_datum[using] -= channel
if(!length(using_channels_by_datum[using]))
using_channels_by_datum -= using
@@ -65,7 +65,7 @@ SUBSYSTEM_DEF(sounds)
/// NO AUTOMATIC CLEANUP - If you use this, you better manually free it later! Returns an integer for channel.
/datum/controller/subsystem/sounds/proc/reserve_sound_channel_datumless()
. = reserve_channel()
- if(!.) //oh no..
+ if(!.) //oh no..
return FALSE
var/text_channel = num2text(.)
using_channels[text_channel] = DATUMLESS
@@ -74,7 +74,7 @@ SUBSYSTEM_DEF(sounds)
/// Reserves a channel for a datum. Automatic cleanup only when the datum is deleted. Returns an integer for channel.
/datum/controller/subsystem/sounds/proc/reserve_sound_channel(datum/D)
- if(!D) //i don't like typechecks but someone will fuck it up
+ if(!D) //i don't like typechecks but someone will fuck it up
CRASH("Attempted to reserve sound channel without datum using the managed proc.")
.= reserve_channel()
if(!.)
@@ -89,7 +89,7 @@ SUBSYSTEM_DEF(sounds)
*/
/datum/controller/subsystem/sounds/proc/reserve_channel()
PRIVATE_PROC(TRUE)
- if(channel_reserve_high <= random_channels_min) // out of channels
+ if(channel_reserve_high <= random_channels_min) // out of channels
return
var/channel = channel_list[channel_reserve_high]
reserved_channels[num2text(channel)] = channel_reserve_high--
@@ -112,7 +112,7 @@ SUBSYSTEM_DEF(sounds)
// now, an existing reserved channel will likely (exception: unreserving last reserved channel) be at index
// get it, and update position.
var/text_reserved = num2text(channel_list[index])
- if(!reserved_channels[text_reserved]) //if it isn't already reserved make sure we don't accidently mistakenly put it on reserved list!
+ if(!reserved_channels[text_reserved]) //if it isn't already reserved make sure we don't accidently mistakenly put it on reserved list!
return
reserved_channels[text_reserved] = index
diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm
index 14b6306542..651807613d 100644
--- a/code/datums/chatmessage.dm
+++ b/code/datums/chatmessage.dm
@@ -18,8 +18,6 @@
#define CHAT_LAYER_Z_STEP 0.0001
/// The number of z-layer 'slices' usable by the chat message layering
#define CHAT_LAYER_MAX_Z (CHAT_LAYER_MAX - CHAT_LAYER) / CHAT_LAYER_Z_STEP
-/// Macro from Lummox used to get height from a MeasureText proc
-#define WXH_TO_HEIGHT(x) text2num(copytext(x, findtextEx(x, "x") + 1))
/**
* # Chat Message Overlay
@@ -165,7 +163,7 @@
message.maptext_width = CHAT_MESSAGE_WIDTH
message.maptext_height = mheight
message.maptext_x = (CHAT_MESSAGE_WIDTH - owner.bound_width) * -0.5
- message.maptext = complete_text
+ message.maptext = MAPTEXT(complete_text)
// View the message
LAZYADDASSOC(owned_by.seen_messages, message_loc, src)
diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm
index 433e57abbe..c0ec25dfc2 100644
--- a/code/datums/components/uplink.dm
+++ b/code/datums/components/uplink.dm
@@ -1,4 +1,5 @@
GLOBAL_LIST_EMPTY(uplinks)
+#define PEN_ROTATIONS 2
/**
* Uplinks
@@ -17,7 +18,7 @@ GLOBAL_LIST_EMPTY(uplinks)
var/telecrystals
var/selected_cat
var/owner = null
- var/datum/game_mode/gamemode
+ var/uplink_flag
var/datum/uplink_purchase_log/purchase_log
var/list/uplink_items
var/hidden_crystals = 0
@@ -26,11 +27,11 @@ GLOBAL_LIST_EMPTY(uplinks)
var/failsafe_code
var/compact_mode = FALSE
var/debug = FALSE
- var/saved_player_population = 0
- var/list/filters = list()
+ ///Instructions on how to access the uplink based on location
+ var/unlock_text
+ var/list/previous_attempts
-
-/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/traitor_class/traitor_class)
+/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, uplink_flag = UPLINK_TRAITORS, starting_tc = TELECRYSTALS_DEFAULT)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
@@ -44,16 +45,13 @@ GLOBAL_LIST_EMPTY(uplinks)
RegisterSignal(parent, COMSIG_IMPLANT_EXISTING_UPLINK, .proc/new_implant)
else if(istype(parent, /obj/item/pda))
RegisterSignal(parent, COMSIG_PDA_CHANGE_RINGTONE, .proc/new_ringtone)
+ // RegisterSignal(parent, COMSIG_PDA_CHECK_DETONATE, .proc/check_detonate)
else if(istype(parent, /obj/item/radio))
RegisterSignal(parent, COMSIG_RADIO_NEW_FREQUENCY, .proc/new_frequency)
else if(istype(parent, /obj/item/pen))
RegisterSignal(parent, COMSIG_PEN_ROTATED, .proc/pen_rotation)
- GLOB.uplinks += src
- if(istype(traitor_class))
- filters = traitor_class.uplink_filters
- starting_tc = traitor_class.TC
- uplink_items = get_uplink_items(gamemode, TRUE, allow_restricted, filters)
+ GLOB.uplinks |= src
if(_owner)
owner = _owner
@@ -64,44 +62,58 @@ GLOBAL_LIST_EMPTY(uplinks)
purchase_log = new(owner, src)
lockable = _lockable
active = _enabled
- gamemode = _gamemode
+ src.uplink_flag = uplink_flag
+ update_items()
telecrystals = starting_tc
if(!lockable)
active = TRUE
locked = FALSE
- saved_player_population = GLOB.joined_player_list.len
+
+ previous_attempts = list()
/datum/component/uplink/InheritComponent(datum/component/uplink/U)
lockable |= U.lockable
active |= U.active
- if(!gamemode)
- gamemode = U.gamemode
+ uplink_flag |= U.uplink_flag
telecrystals += U.telecrystals
if(purchase_log && U.purchase_log)
purchase_log.MergeWithAndDel(U.purchase_log)
/datum/component/uplink/Destroy()
GLOB.uplinks -= src
- gamemode = null
purchase_log = null
return ..()
+/datum/component/uplink/proc/update_items()
+ var/updated_items
+ updated_items = get_uplink_items(uplink_flag, TRUE, allow_restricted)
+ update_sales(updated_items)
+ uplink_items = updated_items
+
+/datum/component/uplink/proc/update_sales(updated_items)
+ var/discount_categories = list("Discounted Gear", "Discounted Team Gear", "Limited Stock Team Gear")
+ if (uplink_items == null)
+ return
+ for (var/category in discount_categories) // Makes sure discounted items aren't renewed or replaced
+ if (uplink_items[category] != null && updated_items[category] != null)
+ updated_items[category] = uplink_items[category]
+
/datum/component/uplink/proc/LoadTC(mob/user, obj/item/stack/telecrystal/TC, silent = FALSE)
if(!silent)
- to_chat(user, "You slot [TC] into [parent] and charge its internal uplink.")
+ to_chat(user, span_notice("You slot [TC] into [parent] and charge its internal uplink."))
var/amt = TC.amount
telecrystals += amt
TC.use(amt)
-
-/datum/component/uplink/proc/set_gamemode(_gamemode)
- gamemode = _gamemode
- uplink_items = get_uplink_items(gamemode, TRUE, allow_restricted)
+ // log_uplink("[key_name(user)] loaded [amt] telecrystals into [parent]'s uplink")
/datum/component/uplink/proc/OnAttackBy(datum/source, obj/item/I, mob/user)
+ SIGNAL_HANDLER
+
if(!active)
- return //no hitting everyone/everything just to try to slot tcs in!
+ return //no hitting everyone/everything just to try to slot tcs in!
if(istype(I, /obj/item/stack/telecrystal))
LoadTC(user, I)
+ // CIT SPECIFIC: STEALING from unlocked uplink.
if(active)
if(I.GetComponent(/datum/component/uplink))
var/datum/component/uplink/hidden_uplink = I.GetComponent(/datum/component/uplink)
@@ -118,31 +130,26 @@ GLOBAL_LIST_EMPTY(uplinks)
var/cost = UI.refund_amount || UI.cost
if(I.type == path && UI.refundable && I.check_uplink_validity())
telecrystals += cost
- purchase_log.total_spent -= cost
- to_chat(user, "[I] refunded.")
+ // log_uplink("[key_name(user)] refunded [UI] for [cost] telecrystals using [parent]'s uplink")
+ if(purchase_log)
+ purchase_log.total_spent -= cost
+ to_chat(user, span_notice("[I] refunded."))
qdel(I)
return
/datum/component/uplink/proc/interact(datum/source, mob/user)
+ SIGNAL_HANDLER
+
if(locked)
return
active = TRUE
+ update_items()
if(user)
- //update the saved population
- var/previous_player_population = saved_player_population
- saved_player_population = GLOB.joined_player_list.len
- //if population has changed, update uplink items
- if(saved_player_population != previous_player_population)
- //make sure discounts are not rerolled
- var/old_discounts = uplink_items["Discounted Gear"]
- uplink_items = get_uplink_items(gamemode, FALSE, allow_restricted, filters)
- if(old_discounts)
- uplink_items["Discounted Gear"] = old_discounts
- ui_interact(user)
-
+ INVOKE_ASYNC(src, .proc/ui_interact, user)
// an unlocked uplink blocks also opening the PDA or headset menu
return COMPONENT_NO_INTERACT
+
/datum/component/uplink/ui_state(mob/user)
if(istype(parent, /obj/item/implant/uplink))
return GLOB.not_incapacitated_state
@@ -178,15 +185,10 @@ GLOBAL_LIST_EMPTY(uplinks)
var/datum/uplink_item/I = uplink_items[category][item]
if(I.limited_stock == 0)
continue
- if(I.restricted_roles.len)
- var/is_inaccessible = TRUE
- for(var/R in I.restricted_roles)
- if(R == user.mind.assigned_role || debug)
- is_inaccessible = FALSE
- if(is_inaccessible)
+ if(length(I.restricted_roles))
+ if(!debug && !(user.mind.assigned_role in I.restricted_roles))
continue
- /*
- if(I.restricted_species) //catpeople specfic gloves.
+ if(I.restricted_species)
if(ishuman(user))
var/is_inaccessible = TRUE
var/mob/living/carbon/human/H = user
@@ -196,7 +198,6 @@ GLOBAL_LIST_EMPTY(uplinks)
break
if(is_inaccessible)
continue
- */
cat["items"] += list(list(
"name" = I.name,
"cost" = I.cost,
@@ -255,25 +256,41 @@ GLOBAL_LIST_EMPTY(uplinks)
// Implant signal responses
/datum/component/uplink/proc/implant_activation()
+ SIGNAL_HANDLER
+
var/obj/item/implant/implant = parent
locked = FALSE
interact(null, implant.imp_in)
/datum/component/uplink/proc/implanting(datum/source, list/arguments)
+ SIGNAL_HANDLER
+
var/mob/user = arguments[2]
- owner = "[user.key]"
+ owner = user?.key
+ if(owner && !purchase_log)
+ LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
+ if(GLOB.uplink_purchase_logs_by_key[owner])
+ purchase_log = GLOB.uplink_purchase_logs_by_key[owner]
+ else
+ purchase_log = new(owner, src)
/datum/component/uplink/proc/old_implant(datum/source, list/arguments, obj/item/implant/new_implant)
+ SIGNAL_HANDLER
+
// It kinda has to be weird like this until implants are components
return SEND_SIGNAL(new_implant, COMSIG_IMPLANT_EXISTING_UPLINK, src)
/datum/component/uplink/proc/new_implant(datum/source, datum/component/uplink/uplink)
+ SIGNAL_HANDLER
+
uplink.telecrystals += telecrystals
return COMPONENT_DELETE_NEW_IMPLANT
// PDA signal responses
/datum/component/uplink/proc/new_ringtone(datum/source, mob/living/user, new_ring_text)
+ SIGNAL_HANDLER
+
var/obj/item/pda/master = parent
if(trim(lowertext(new_ring_text)) != trim(lowertext(unlock_code)))
if(trim(lowertext(new_ring_text)) == trim(lowertext(failsafe_code)))
@@ -282,14 +299,21 @@ GLOBAL_LIST_EMPTY(uplinks)
return
locked = FALSE
interact(null, user)
- to_chat(user, "The PDA softly beeps.")
+ to_chat(user, span_hear("The PDA softly beeps."))
user << browse(null, "window=pda")
master.mode = 0
return COMPONENT_STOP_RINGTONE_CHANGE
+/datum/component/uplink/proc/check_detonate()
+ SIGNAL_HANDLER
+
+ // return COMPONENT_PDA_NO_DETONATE
+
// Radio signal responses
/datum/component/uplink/proc/new_frequency(datum/source, list/arguments)
+ SIGNAL_HANDLER
+
var/obj/item/radio/master = parent
var/frequency = arguments[1]
if(frequency != unlock_code)
@@ -303,15 +327,22 @@ GLOBAL_LIST_EMPTY(uplinks)
// Pen signal responses
/datum/component/uplink/proc/pen_rotation(datum/source, degrees, mob/living/carbon/user)
+ SIGNAL_HANDLER
+
var/obj/item/pen/master = parent
- if(degrees != unlock_code)
- if(degrees == failsafe_code) //Getting failsafes on pens is risky business
- failsafe()
- return
- locked = FALSE
- master.degrees = 0
- interact(null, user)
- to_chat(user, "Your pen makes a clicking noise, before quickly rotating back to 0 degrees!")
+ previous_attempts += degrees
+ if(length(previous_attempts) > PEN_ROTATIONS)
+ popleft(previous_attempts)
+
+ if(compare_list(previous_attempts, unlock_code))
+ locked = FALSE
+ previous_attempts.Cut()
+ master.degrees = 0
+ interact(null, user)
+ to_chat(user, span_warning("Your pen makes a clicking noise, before quickly rotating back to 0 degrees!"))
+
+ else if(compare_list(previous_attempts, failsafe_code))
+ failsafe(user)
/datum/component/uplink/proc/setup_unlock_code()
unlock_code = generate_code()
@@ -321,15 +352,18 @@ GLOBAL_LIST_EMPTY(uplinks)
else if(istype(parent,/obj/item/radio))
unlock_note = "Radio Frequency: [format_frequency(unlock_code)] ([P.name])."
else if(istype(parent,/obj/item/pen))
- unlock_note = "Uplink Degrees: [unlock_code] ([P.name])."
+ unlock_note = "Uplink Degrees: [english_list(unlock_code)] ([P.name])."
/datum/component/uplink/proc/generate_code()
if(istype(parent,/obj/item/pda))
return "[rand(100,999)] [pick(GLOB.phonetic_alphabet)]"
else if(istype(parent,/obj/item/radio))
- return sanitize_frequency(rand(MIN_FREQ, MAX_FREQ))
+ return return_unused_frequency()
else if(istype(parent,/obj/item/pen))
- return rand(1, 360)
+ var/list/L = list()
+ for(var/i in 1 to PEN_ROTATIONS)
+ L += rand(1, 360)
+ return L
/datum/component/uplink/proc/failsafe(mob/living/carbon/user)
if(!parent)
@@ -339,5 +373,5 @@ GLOBAL_LIST_EMPTY(uplinks)
return
message_admins("[ADMIN_LOOKUPFLW(user)] has triggered an uplink failsafe explosion at [AREACOORD(T)] The owner of the uplink was [ADMIN_LOOKUPFLW(owner)].")
log_game("[key_name(user)] triggered an uplink failsafe explosion. The owner of the uplink was [key_name(owner)].")
- explosion(T,1,2,3)
+ explosion(parent, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 3)
qdel(parent) //Alternatively could brick the uplink.
diff --git a/code/datums/datum.dm b/code/datums/datum.dm
index 164cda63e0..5f9bf37040 100644
--- a/code/datums/datum.dm
+++ b/code/datums/datum.dm
@@ -106,7 +106,7 @@
active_timers = null
for(var/thing in timers)
var/datum/timedevent/timer = thing
- if (timer.spent)
+ if (timer.spent && !(timer.flags & TIMER_DELETE_ME))
continue
qdel(timer)
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index d1aef6ceae..94526bb956 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -267,11 +267,18 @@
remove_rev()
SSticker.mode.update_cult_icons_removed(src)
-/datum/mind/proc/equip_traitor(datum/traitor_class/traitor_class, silent = FALSE, datum/antagonist/uplink_owner)
+/**
+ * ## give_uplink
+ *
+ * A mind proc for giving anyone an uplink.
+ * arguments:
+ * * traitor_class: (cit specific) the type of antag the user is.
+ * * silent: if this should send a message to the mind getting the uplink. traitors do not use this silence, but the silence var on their antag datum.
+ * * antag_datum: the antag datum of the uplink owner, for storing it in antag memory. optional!
+ */
+/datum/mind/proc/equip_traitor(silent = FALSE, datum/antagonist/antag_datum)
if(!current)
return
- if(!traitor_class)
- traitor_class = GLOB.traitor_classes[TRAITOR_HUMAN]
var/mob/living/carbon/human/traitor_mob = current
if (!istype(traitor_mob))
return
@@ -285,16 +292,9 @@
P = locate() in PDA
if (!P) // If we couldn't find a pen in the PDA, or we didn't even have a PDA, do it the old way
P = locate() in all_contents
- if(!P) // I do not have a pen.
- var/obj/item/pen/inowhaveapen
- if(istype(traitor_mob.back,/obj/item/storage)) //ok buddy you better have a backpack!
- inowhaveapen = new /obj/item/pen(traitor_mob.back)
- else
- inowhaveapen = new /obj/item/pen(traitor_mob.loc)
- traitor_mob.put_in_hands(inowhaveapen) // I hope you don't have arms and your traitor pen gets stolen for all this trouble you've caused.
- P = inowhaveapen
var/obj/item/uplink_loc
+ var/implant = FALSE
if(traitor_mob.client && traitor_mob.client.prefs)
switch(traitor_mob.client.prefs.uplink_spawn_loc)
@@ -312,33 +312,38 @@
uplink_loc = P
if(UPLINK_PEN)
uplink_loc = P
- if(!uplink_loc)
- uplink_loc = PDA
- if(!uplink_loc)
- uplink_loc = R
+ if(UPLINK_IMPLANT)
+ implant = TRUE
- if (!uplink_loc)
- if(!silent)
- to_chat(traitor_mob, "Unfortunately, [traitor_class.employer] wasn't able to get you an Uplink.")
- . = 0
- else
- . = uplink_loc
- var/datum/component/uplink/U = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key,traitor_class)
- if(!U)
- CRASH("Uplink creation failed.")
- U.setup_unlock_code()
- if(!silent)
- if(uplink_loc == R)
- to_chat(traitor_mob, "[traitor_class.employer] has cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(U.unlock_code)] to unlock its hidden features.")
- else if(uplink_loc == PDA)
- to_chat(traitor_mob, "[traitor_class.employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[U.unlock_code]\" into the ringtone select to unlock its hidden features.")
- else if(uplink_loc == P)
- to_chat(traitor_mob, "[traitor_class.employer] has cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [U.unlock_code] from its starting position to unlock its hidden features.")
+ if(!uplink_loc) // We've looked everywhere, let's just implant you
+ implant = TRUE
- if(uplink_owner)
- uplink_owner.antag_memory += U.unlock_note + "
"
- else
- traitor_mob.mind.store_memory(U.unlock_note)
+ if(implant)
+ var/obj/item/implant/uplink/starting/new_implant = new(traitor_mob)
+ new_implant.implant(traitor_mob, null, silent = TRUE)
+ if(!silent)
+ to_chat(traitor_mob, span_boldnotice("Your Syndicate Uplink has been cunningly implanted in you, for a small TC fee. Simply trigger the uplink to access it."))
+ return new_implant
+
+ . = uplink_loc
+ var/unlock_text
+ var/datum/component/uplink/new_uplink = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key)
+ if(!new_uplink)
+ CRASH("Uplink creation failed.")
+ new_uplink.setup_unlock_code()
+ if(uplink_loc == R)
+ unlock_text = "Your Uplink is cunningly disguised as your [R.name]. Simply dial the frequency [format_frequency(new_uplink.unlock_code)] to unlock its hidden features."
+ else if(uplink_loc == PDA)
+ unlock_text = "Your Uplink is cunningly disguised as your [PDA.name]. Simply enter the code \"[new_uplink.unlock_code]\" into the ringtone select to unlock its hidden features."
+ else if(uplink_loc == P)
+ unlock_text = "Your Uplink is cunningly disguised as your [P.name]. Simply twist the top of the pen [english_list(new_uplink.unlock_code)] from its starting position to unlock its hidden features."
+ new_uplink.unlock_text = unlock_text
+ if(!silent)
+ to_chat(traitor_mob, span_boldnotice(unlock_text))
+ if(!antag_datum)
+ traitor_mob.mind.store_memory(new_uplink.unlock_note)
+ return
+ antag_datum.antag_memory += new_uplink.unlock_note + "
"
//Link a new mobs mind to the creator of said mob. They will join any team they are currently on, and will only switch teams when their creator does.
diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm
index 6248be0de5..14b514b30a 100644
--- a/code/datums/weather/weather_types/ash_storm.dm
+++ b/code/datums/weather/weather_types/ash_storm.dm
@@ -1,3 +1,5 @@
+//A reference to this list is passed into area sound managers, and it's modified in a manner that preserves that reference in ash_storm.dm
+GLOBAL_LIST_EMPTY(ash_storm_sounds)
//Ash storms happen frequently on lavaland. They heavily obscure vision, and cause high fire damage to anyone caught outside.
/datum/weather/ash_storm
name = "ash storm"
@@ -25,55 +27,41 @@
probability = 90
barometer_predictable = TRUE
-
- var/datum/looping_sound/active_outside_ashstorm/sound_ao = new(list(), FALSE, TRUE)
- var/datum/looping_sound/active_inside_ashstorm/sound_ai = new(list(), FALSE, TRUE)
- var/datum/looping_sound/weak_outside_ashstorm/sound_wo = new(list(), FALSE, TRUE)
- var/datum/looping_sound/weak_inside_ashstorm/sound_wi = new(list(), FALSE, TRUE)
+ var/list/weak_sounds = list()
+ var/list/strong_sounds = list()
/datum/weather/ash_storm/telegraph()
- . = ..()
- var/list/inside_areas = list()
- var/list/outside_areas = list()
var/list/eligible_areas = list()
for (var/z in impacted_z_levels)
eligible_areas += SSmapping.areas_in_z["[z]"]
for(var/i in 1 to eligible_areas.len)
var/area/place = eligible_areas[i]
if(place.outdoors)
- outside_areas += place
+ weak_sounds[place] = /datum/looping_sound/weak_outside_ashstorm
+ strong_sounds[place] = /datum/looping_sound/active_outside_ashstorm
else
- inside_areas += place
+ weak_sounds[place] = /datum/looping_sound/weak_inside_ashstorm
+ strong_sounds[place] = /datum/looping_sound/active_inside_ashstorm
CHECK_TICK
- sound_ao.output_atoms = outside_areas
- sound_ai.output_atoms = inside_areas
- sound_wo.output_atoms = outside_areas
- sound_wi.output_atoms = inside_areas
-
- sound_wo.start()
- sound_wi.start()
+ //We modify this list instead of setting it to weak/stron sounds in order to preserve things that hold a reference to it
+ //It's essentially a playlist for a bunch of components that chose what sound to loop based on the area a player is in
+ GLOB.ash_storm_sounds += weak_sounds
+ return ..()
/datum/weather/ash_storm/start()
- . = ..()
- sound_wo.stop()
- sound_wi.stop()
-
- sound_ao.start()
- sound_ai.start()
+ GLOB.ash_storm_sounds -= weak_sounds
+ GLOB.ash_storm_sounds += strong_sounds
+ return ..()
/datum/weather/ash_storm/wind_down()
- . = ..()
- sound_ao.stop()
- sound_ai.stop()
-
- sound_wo.start()
- sound_wi.start()
+ GLOB.ash_storm_sounds -= strong_sounds
+ GLOB.ash_storm_sounds += weak_sounds
+ return ..()
/datum/weather/ash_storm/end()
- . = ..()
- sound_wo.stop()
- sound_wi.stop()
+ GLOB.ash_storm_sounds -= weak_sounds
+ return ..()
/datum/weather/ash_storm/proc/is_ash_immune(atom/L)
while (L && !isturf(L))
@@ -88,6 +76,11 @@
var/mob/living/the_mob = L
if("ash" in the_mob.weather_immunities)
return TRUE
+ // if(istype(L, /obj/structure/closet))
+ // var/obj/structure/closet/the_locker = L
+ // if(the_locker.weather_protection)
+ // if("ash" in the_locker.weather_protection)
+ // return TRUE
L = L.loc //Check parent items immunities (recurses up to the turf)
return FALSE //RIP you
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 81194fd781..694ab71dfe 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1287,9 +1287,7 @@
/obj/item/update_filters()
. = ..()
- for(var/X in actions)
- var/datum/action/A = X
- A.UpdateButtonIcon()
+ update_action_buttons()
/atom/proc/get_filter(name)
if(filter_data && filter_data[name])
diff --git a/code/game/machinery/colormate.dm b/code/game/machinery/colormate.dm
index 281538e7a8..1528142a3b 100644
--- a/code/game/machinery/colormate.dm
+++ b/code/game/machinery/colormate.dm
@@ -3,6 +3,7 @@
desc = "A machine to give your apparel a fresh new color! Recommended to use with white items for best results."
icon = 'icons/obj/vending.dmi'
icon_state = "colormate"
+ // light_mask = "colormate-light-mask"
density = TRUE
anchored = TRUE
circuit = /obj/item/circuitboard/machine/colormate
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 461a2888ab..421c9feded 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -14,7 +14,7 @@ GLOBAL_LIST_EMPTY(cryopod_computers)
/obj/machinery/computer/cryopod
name = "cryogenic oversight console"
desc = "An interface between crew and the cryogenic storage oversight systems."
- icon = 'icons/obj/machines/cryopod.dmi'
+ icon = 'icons/obj/Cryogenic2.dmi'
icon_state = "cellconsole_1"
icon_keyboard = null
// circuit = /obj/item/circuitboard/cryopodcontrol
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 34f32edb47..df122c8165 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -1220,3 +1220,14 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
pain_stam_pct = (!isnull(embedding["pain_stam_pct"]) ? embedding["pain_stam_pct"] : EMBEDDED_PAIN_STAM_PCT),\
embed_chance_turf_mod = (!isnull(embedding["embed_chance_turf_mod"]) ? embedding["embed_chance_turf_mod"] : EMBED_CHANCE_TURF_MOD))
return TRUE
+
+/**
+ * Updates all action buttons associated with this item
+ *
+ * Arguments:
+ * * status_only - Update only current availability status of the buttons to show if they are ready or not to use
+ * * force - Force buttons update even if the given button icon state has not changed
+ */
+/obj/item/proc/update_action_buttons(status_only = FALSE, force = FALSE)
+ for(var/datum/action/current_action as anything in actions)
+ current_action.UpdateButtonIcon(status_only, force)
diff --git a/code/game/objects/items/implants/implant_uplink.dm b/code/game/objects/items/implants/implant_uplink.dm
index 0cac8f838a..bd861013dd 100644
--- a/code/game/objects/items/implants/implant_uplink.dm
+++ b/code/game/objects/items/implants/implant_uplink.dm
@@ -20,4 +20,7 @@
imp_type = /obj/item/implant/uplink/precharged
/obj/item/implant/uplink/precharged
- starting_tc = 10
+ starting_tc = TELECRYSTALS_PRELOADED_IMPLANT
+
+/obj/item/implant/uplink/starting
+ starting_tc = TELECRYSTALS_DEFAULT - UPLINK_IMPLANT_TELECRYSTAL_COST
diff --git a/code/game/objects/structures/noticeboard.dm b/code/game/objects/structures/noticeboard.dm
index cf608ce39e..34fbdb3029 100644
--- a/code/game/objects/structures/noticeboard.dm
+++ b/code/game/objects/structures/noticeboard.dm
@@ -121,7 +121,7 @@
/obj/structure/noticeboard/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
- new /obj/item/stack/sheet/iron (loc, 1)
+ new /obj/item/stack/sheet/metal (loc, 1)
for(var/obj/item/content in contents)
remove_item(content)
qdel(src)
diff --git a/code/modules/admin/view_variables/filterrific.dm b/code/modules/admin/view_variables/filterrific.dm
index e651028cbe..6cef9b178a 100644
--- a/code/modules/admin/view_variables/filterrific.dm
+++ b/code/modules/admin/view_variables/filterrific.dm
@@ -79,7 +79,7 @@
. = TRUE
if("mass_apply")
if(!check_rights_for(usr.client, R_FUN))
- to_chat(usr, "[message]")
- else
- H.dna.add_mutation(CLOWNMUT) // We're removing their antag status, add back clumsy
+ if(!ishuman(mob_override) || owner.assigned_role != "Clown")
+ return
+ var/mob/living/carbon/human/human_override = mob_override
+ if(removing) // They're a clown becoming an antag, remove clumsy
+ human_override.dna.remove_mutation(CLOWNMUT)
+ if(!silent && message)
+ to_chat(human_override, span_boldnotice("[message]"))
+ else
+ human_override.dna.add_mutation(CLOWNMUT) // We're removing their antag status, add back clumsy
+
//Assign default team and creates one for one of a kind team antagonists
/datum/antagonist/proc/create_team(datum/team/team)
return
- ///Called by the add_antag_datum() mind proc after the instanced datum is added to the mind's antag_datums list.
+///Called by the add_antag_datum() mind proc after the instanced datum is added to the mind's antag_datums list.
/datum/antagonist/proc/on_gain()
SHOULD_CALL_PARENT(TRUE)
- set waitfor = FALSE
- if(!(owner?.current))
- return
+ if(!owner)
+ CRASH("[src] ran on_gain() without a mind")
+ if(!owner.current)
+ CRASH("[src] ran on_gain() on a mind without a mob")
+ if(ui_name)//in the future, this should entirely replace greet.
+ info_button = new(owner.current, src)
+ info_button.Grant(owner.current)
if(!silent)
greet()
+ if(ui_name)
+ to_chat(owner.current, span_big("You are \a [src]."))
+ to_chat(owner.current, span_boldnotice("For more info, read the panel. you can always come back to it using the button in the top left."))
+ info_button.Trigger()
apply_innate_effects()
give_antag_moodies()
remove_blacklisted_quirks()
+ // RegisterSignal(owner, COMSIG_PRE_MINDSHIELD_IMPLANT, .proc/pre_mindshield)
+ // RegisterSignal(owner, COMSIG_MINDSHIELD_IMPLANTED, .proc/on_mindshield)
if(is_banned(owner.current) && replace_banned)
replace_banned_player()
+ // else if(owner.current.client?.holder && (CONFIG_GET(flag/auto_deadmin_antagonists) || owner.current.client.prefs?.toggles & DEADMIN_ANTAGONIST))
+ // owner.current.client.holder.auto_deadmin()
+ if(!soft_antag && owner.current.stat != DEAD)
+ owner.current.add_to_current_living_antags()
+
+ // cit skill
if(skill_modifiers)
for(var/A in skill_modifiers)
ADD_SINGLETON_SKILL_MODIFIER(owner, A, type)
@@ -120,61 +189,95 @@ GLOBAL_LIST_EMPTY(antagonists)
if(istype(M))
M.name = "[name] Training"
owner.current.AddComponent(/datum/component/activity)
- if(owner.current.stat != DEAD)
- owner.current.add_to_current_living_antags()
SEND_SIGNAL(owner.current, COMSIG_MOB_ANTAG_ON_GAIN, src)
+
+/**
+ * Proc that checks the sent mob aganst the banlistfor this antagonist.
+ * Returns FALSE if no mob is sent, or the mob is not found to be banned.
+ *
+ * * mob/M: The mob that you are looking for on the banlist.
+ */
/datum/antagonist/proc/is_banned(mob/M)
if(!M)
return FALSE
. = (jobban_isbanned(M, ROLE_SYNDICATE) || QDELETED(M) || (job_rank && (jobban_isbanned(M,job_rank) || QDELETED(M))))
+/**
+ * Proc that replaces a player who cannot play a specific antagonist due to being banned via a poll, and alerts the player of their being on the banlist.
+ */
/datum/antagonist/proc/replace_banned_player()
set waitfor = FALSE
- var/list/mob/candidates = pollCandidatesForMob("Do you want to play as a [name]?", "[name]", null, job_rank, 50, owner.current)
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", "[name]", job_rank, 50, owner.current)
if(LAZYLEN(candidates))
- var/mob/C = pick(candidates)
+ var/mob/dead/observer/C = pick(candidates)
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
- message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner.current)]) to replace a jobbaned player.")
+ message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner)]) to replace a jobbanned player.")
owner.current.ghostize(0)
C.transfer_ckey(owner.current, FALSE)
-///Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion.
+/**
+ * Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion.
+ */
/datum/antagonist/proc/on_removal()
SHOULD_CALL_PARENT(TRUE)
+ if(!owner)
+ CRASH("Antag datum with no owner.")
+
remove_innate_effects()
clear_antag_moodies()
- if(owner)
- LAZYREMOVE(owner.antag_datums, src)
- for(var/A in skill_modifiers)
- owner.remove_skill_modifier(GET_SKILL_MOD_ID(A, type))
- if(!LAZYLEN(owner.antag_datums))
- owner.current.remove_from_current_living_antags()
- if(!silent && owner.current)
- farewell()
+ LAZYREMOVE(owner.antag_datums, src)
+ // cit skill
+ for(var/A in skill_modifiers)
+ owner.remove_skill_modifier(GET_SKILL_MOD_ID(A, type))
+ // end
+ if(!LAZYLEN(owner.antag_datums) && !soft_antag)
+ owner.current.remove_from_current_living_antags()
+ if(info_button)
+ QDEL_NULL(info_button)
+ if(!silent && owner.current)
+ farewell()
+ // UnregisterSignal(owner, COMSIG_PRE_MINDSHIELD_IMPLANT)
+ // UnregisterSignal(owner, COMSIG_MINDSHIELD_IMPLANTED)
var/datum/team/team = get_team()
if(team)
team.remove_member(owner)
- // we don't remove the activity component on purpose--no real point to it
qdel(src)
+/**
+ * Proc that sends fluff or instructional messages to the player when they are given this antag datum.
+ * Use this proc for playing sounds, sending alerts, or helping to setup non-gameplay influencing aspects of the antagonist type.
+ */
/datum/antagonist/proc/greet()
return
+/**
+ * Proc that sends fluff or instructional messages to the player when they lose this antag datum.
+ * Use this proc for playing sounds, sending alerts, or otherwise informing the player that they're no longer a specific antagonist type.
+ */
/datum/antagonist/proc/farewell()
return
+/**
+ * Proc that assigns this antagonist's ascribed moodlet to the player.
+ */
/datum/antagonist/proc/give_antag_moodies()
if(!antag_moodlet)
return
SEND_SIGNAL(owner.current, COMSIG_ADD_MOOD_EVENT, "antag_moodlet", antag_moodlet)
+/**
+ * Proc that removes this antagonist's ascribed moodlet from the player.
+ */
/datum/antagonist/proc/clear_antag_moodies()
if(!antag_moodlet)
return
SEND_SIGNAL(owner.current, COMSIG_CLEAR_MOOD_EVENT, "antag_moodlet")
+/**
+ * Removes invalid quirks.
+ */
/datum/antagonist/proc/remove_blacklisted_quirks()
var/mob/living/L = owner.current
if(istype(L))
@@ -185,16 +288,22 @@ GLOBAL_LIST_EMPTY(antagonists)
to_chat(L, "[initial(Q.antag_removal_text)]")
qdel(Q)
-//Returns the team antagonist belongs to if any.
+/**
+ * Proc that will return the team this antagonist belongs to, when called. Helpful with antagonists that may belong to multiple potential teams in a single round, like families.
+ */
/datum/antagonist/proc/get_team()
return
-//Individual roundend report
+/**
+ * Proc that sends string information for the end-round report window to the server.
+ * This runs on every instance of every antagonist that exists at the end of the round.
+ * This is the body of the message, sandwiched between roundend_report_header and roundend_report_footer.
+ */
/datum/antagonist/proc/roundend_report()
var/list/report = list()
if(!owner)
- CRASH("antagonist datum without owner")
+ CRASH("Antagonist datum without owner")
report += printplayer(owner)
@@ -213,11 +322,19 @@ GLOBAL_LIST_EMPTY(antagonists)
return report.Join("
")
-//Displayed at the start of roundend_category section, default to roundend_category header
+/**
+ * Proc that sends string data for the round-end report.
+ * Displayed before roundend_report and roundend_report_footer.
+ * Appears at start of roundend_catagory section.
+ */
/datum/antagonist/proc/roundend_report_header()
- return "
"
+ return "
"
-//Displayed at the end of roundend_category section
+/**
+ * Proc that sends string data for the round-end report.
+ * Displayed after roundend_report and roundend_report_footer.
+ * Appears at the end of the roundend_catagory section.
+ */
/datum/antagonist/proc/roundend_report_footer()
return
@@ -226,22 +343,24 @@ GLOBAL_LIST_EMPTY(antagonists)
//Called when using admin tools to give antag status
/datum/antagonist/proc/admin_add(datum/mind/new_owner,mob/admin)
- message_admins("[key_name_admin(admin)] made [new_owner.current] into [name].")
- log_admin("[key_name(admin)] made [new_owner.current] into [name].")
+ message_admins("[key_name_admin(admin)] made [key_name_admin(new_owner)] into [name].")
+ log_admin("[key_name(admin)] made [key_name(new_owner)] into [name].")
new_owner.add_antag_datum(src)
//Called when removing antagonist using admin tools
/datum/antagonist/proc/admin_remove(mob/user)
if(!user)
return
- message_admins("[key_name_admin(user)] has removed [name] antagonist status from [owner.current].")
- log_admin("[key_name(user)] has removed [name] antagonist status from [owner.current].")
+ message_admins("[key_name_admin(user)] has removed [name] antagonist status from [key_name_admin(owner)].")
+ log_admin("[key_name(user)] has removed [name] antagonist status from [key_name(owner)].")
on_removal()
//gamemode/proc/is_mode_antag(antagonist/A) => TRUE/FALSE
-//Additional data to display in antagonist panel section
-//nuke disk code, genome count, etc
+/**
+ * Additional data to display in the antagonist panel section.
+ * For example, nuke disk code, genome count, etc
+ */
/datum/antagonist/proc/antag_panel_data()
return ""
@@ -253,7 +372,7 @@ GLOBAL_LIST_EMPTY(antagonists)
return FALSE
return TRUE
-// List if ["Command"] = CALLBACK(), user will be appeneded to callback arguments on execution
+/// List of ["Command"] = CALLBACK(), user will be appeneded to callback arguments on execution
/datum/antagonist/proc/get_admin_commands()
. = list()
@@ -283,14 +402,19 @@ GLOBAL_LIST_EMPTY(antagonists)
return
antag_memory = new_memo
-/// Gets how fast we can hijack the shuttle, return 0 for can not hijack. Defaults to hijack_speed var, override for custom stuff like buffing hijack speed for hijack objectives or something.
+/**
+ * Gets how fast we can hijack the shuttle, return 0 for can not hijack.
+ * Defaults to hijack_speed var, override for custom stuff like buffing hijack speed for hijack objectives or something.
+ */
/datum/antagonist/proc/hijack_speed()
var/datum/objective/hijack/H = locate() in objectives
if(!isnull(H?.hijack_speed_override))
return H.hijack_speed_override
return hijack_speed
-/// Gets our threat level. Defaults to threat var, override for custom stuff like different traitor goals having different threats.
+/**
+ * Gets our threat level. Override this proc for custom functionality/dynamic threat level.
+ */
/datum/antagonist/proc/threat()
. = CONFIG_GET(keyed_list/antag_threat)[lowertext(name)]
if(. == null)
@@ -300,6 +424,13 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/custom
antagpanel_category = "Custom"
show_name_in_check_antagonists = TRUE //They're all different
+ var/datum/team/custom_team
+
+/datum/antagonist/custom/create_team(datum/team/team)
+ custom_team = team
+
+/datum/antagonist/custom/get_team()
+ return custom_team
/datum/antagonist/custom/admin_add(datum/mind/new_owner,mob/admin)
var/custom_name = stripped_input(admin, "Custom antagonist name:", "Custom antag", "Antagonist")
@@ -308,3 +439,48 @@ GLOBAL_LIST_EMPTY(antagonists)
else
return
..()
+
+///ANTAGONIST UI STUFF
+
+/datum/antagonist/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, ui_name, name)
+ ui.open()
+
+/datum/antagonist/ui_state(mob/user)
+ return GLOB.always_state
+
+///generic helper to send objectives as data through tgui. supports smart objectives too!
+/datum/antagonist/proc/get_objectives()
+ var/objective_count = 1
+ var/list/objective_data = list()
+ //all obj
+ for(var/datum/objective/objective in objectives)
+ objective_data += list(list(
+ "count" = objective_count,
+ "name" = objective.name,
+ "explanation" = objective.explanation_text,
+ "complete" = objective.completed,
+ ))
+ objective_count++
+ return objective_data
+
+//button for antags to review their descriptions/info
+
+/datum/action/antag_info
+ name = "Open Antag Information:"
+ button_icon_state = "round_end"
+ var/datum/antagonist/antag_datum
+
+/datum/action/antag_info/New(Target, datum/antagonist/antag_datum)
+ . = ..()
+ src.antag_datum = antag_datum
+ name += " [antag_datum.name]"
+
+/datum/action/antag_info/Trigger()
+ if(antag_datum)
+ antag_datum.ui_interact(owner)
+
+/datum/action/antag_info/IsAvailable()
+ return TRUE
diff --git a/code/modules/antagonists/brainwashing/brainwashing.dm b/code/modules/antagonists/brainwashing/brainwashing.dm
index 0bcdd980bf..bd7a9a43cb 100644
--- a/code/modules/antagonists/brainwashing/brainwashing.dm
+++ b/code/modules/antagonists/brainwashing/brainwashing.dm
@@ -17,11 +17,13 @@
B.objectives += objective
M.add_antag_datum(B)
- var/begin_message = "[L] has been brainwashed with the following objectives: "
+ var/begin_message = " has been brainwashed with the following objectives: "
var/obj_message = english_list(directives)
- var/end_message = "."
+ var/end_message = "."
var/rendered = begin_message + obj_message + end_message
- deadchat_broadcast(rendered, follow_target = L, turf_target = get_turf(L), message_type=DEADCHAT_REGULAR)
+ deadchat_broadcast(rendered, "[L]", follow_target = L, turf_target = get_turf(L), message_type=DEADCHAT_ANNOUNCEMENT)
+ if(prob(1) || SSevents.holidays && SSevents.holidays[APRIL_FOOLS])
+ L.say("You son of a bitch! I'm in.", forced = "That son of a bitch! They're in.")
/datum/antagonist/brainwashed
name = "Brainwashed Victim"
@@ -30,19 +32,21 @@
show_in_antagpanel = TRUE
antagpanel_category = "Other"
show_name_in_check_antagonists = TRUE
+ ui_name = "AntagInfoBrainwashed"
+ suicide_cry = "FOR... SOMEONE!!"
-/datum/antagonist/brainwashed/greet()
- to_chat(owner, "Your mind reels as it begins focusing on a single purpose...")
- to_chat(owner, "Follow the Directives, at any cost!")
- var/i = 1
- for(var/X in objectives)
- var/datum/objective/O = X
- to_chat(owner, "[i]. [O.explanation_text]")
- i++
+/datum/antagonist/brainwashed/ui_static_data(mob/user)
+ . = ..()
+ var/list/data = list()
+ data["objectives"] = get_objectives()
+ return data
/datum/antagonist/brainwashed/farewell()
- to_chat(owner, "Your mind suddenly clears...")
- to_chat(owner, "You feel the weight of the Directives disappear! You no longer have to obey them.")
+ to_chat(owner, span_warning("Your mind suddenly clears..."))
+ to_chat(owner, "[span_warning("You feel the weight of the Directives disappear! You no longer have to obey them.")]")
+ if(owner.current)
+ var/mob/living/owner_mob = owner.current
+ owner_mob.log_message("is no longer brainwashed with the objectives: [english_list(objectives)].", LOG_ATTACK)
owner.announce_objectives()
/datum/antagonist/brainwashed/admin_add(datum/mind/new_owner,mob/admin)
@@ -54,9 +58,9 @@
var/objective = stripped_input(admin, "Add an objective, or leave empty to finish.", "Brainwashing", null, MAX_MESSAGE_LEN)
if(objective)
objectives += objective
- while(tgui_alert(admin, "Add another objective?","More Brainwashing",list("Yes","No")) == "Yes")
+ while(tgui_alert(admin,"Add another objective?","More Brainwashing",list("Yes","No")) == "Yes")
- if(tgui_alert(admin, "Confirm Brainwashing?","Are you sure?",list("Yes","No")) == "No")
+ if(tgui_alert(admin,"Confirm Brainwashing?","Are you sure?",list("Yes","No")) == "No")
return
if(!LAZYLEN(objectives))
@@ -69,6 +73,7 @@
brainwash(C, objectives)
var/obj_list = english_list(objectives)
message_admins("[key_name_admin(admin)] has brainwashed [key_name_admin(C)] with the following objectives: [obj_list].")
+ C.log_message("has been force-brainwashed with the objective '[obj_list]' by admin [key_name(admin)]", LOG_ATTACK, log_globally = FALSE)
log_admin("[key_name(admin)] has brainwashed [key_name(C)] with the following objectives: [obj_list].")
/datum/objective/brainwashing
diff --git a/code/modules/antagonists/clockcult/clockcult.dm b/code/modules/antagonists/clockcult/clockcult.dm
index 8bf26d6397..de34b031d9 100644
--- a/code/modules/antagonists/clockcult/clockcult.dm
+++ b/code/modules/antagonists/clockcult/clockcult.dm
@@ -6,6 +6,7 @@
job_rank = ROLE_SERVANT_OF_RATVAR
antag_moodlet = /datum/mood_event/cult
skill_modifiers = list(/datum/skill_modifier/job/level/wiring, /datum/skill_modifier/job/level/dwarfy/blacksmithing)
+ ui_name = "AntagInfoClockwork"
var/datum/action/innate/hierophant/hierophant_network = new
threat = 3
var/datum/team/clockcult/clock_team
@@ -14,6 +15,12 @@
var/ignore_eligibility_check = FALSE
var/ignore_holy_water = FALSE
+/datum/antagonist/brainwashed/ui_static_data(mob/user)
+ . = ..()
+ var/list/data = list()
+ data["objectives"] = get_objectives()
+ return data
+
/datum/antagonist/clockcult/silent
name = "Silent Clock Cultist"
silent = TRUE
@@ -22,6 +29,8 @@
/datum/antagonist/clockcult/neutered
name = "Neutered Clock Cultist"
neutered = TRUE
+ soft_antag = TRUE
+ ui_name = null // no.
/datum/antagonist/clockcult/neutered/traitor
name = "Traitor Clock Cultist"
diff --git a/code/modules/antagonists/morph/morph_antag.dm b/code/modules/antagonists/morph/morph_antag.dm
index 07781ce60a..928236c053 100644
--- a/code/modules/antagonists/morph/morph_antag.dm
+++ b/code/modules/antagonists/morph/morph_antag.dm
@@ -3,5 +3,6 @@
show_name_in_check_antagonists = TRUE
show_in_antagpanel = FALSE
threat = 2
+ ui_name = "AntagInfoMorph"
//It does nothing! (Besides tracking)
diff --git a/code/modules/antagonists/nightmare/nightmare.dm b/code/modules/antagonists/nightmare/nightmare.dm
index f5b10de5c2..185a2be9a7 100644
--- a/code/modules/antagonists/nightmare/nightmare.dm
+++ b/code/modules/antagonists/nightmare/nightmare.dm
@@ -4,3 +4,5 @@
show_name_in_check_antagonists = TRUE
threat = 5
show_to_ghosts = TRUE
+ ui_name = "AntagInfoNightmare"
+ suicide_cry = "FOR THE DARKNESS!!"
diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm
index 652b19a8e7..9875eb1581 100644
--- a/code/modules/antagonists/nukeop/nukeop.dm
+++ b/code/modules/antagonists/nukeop/nukeop.dm
@@ -236,6 +236,8 @@
var/obj/machinery/nuclearbomb/tracked_nuke
var/core_objective = /datum/objective/nuclear
var/memorized_code
+ var/list/team_discounts
+ var/datum/weakref/war_button_ref
/datum/team/nuclear/New()
..()
diff --git a/code/modules/antagonists/overthrow/overthrow.dm b/code/modules/antagonists/overthrow/overthrow.dm
index 8fa5517b4f..f086f5db97 100644
--- a/code/modules/antagonists/overthrow/overthrow.dm
+++ b/code/modules/antagonists/overthrow/overthrow.dm
@@ -121,7 +121,7 @@
return
var/mob/living/carbon/human/H = owner.current
// Give uplink
- var/obj/item/uplink_holder = owner.equip_traitor(uplink_owner = src)
+ var/obj/item/uplink_holder = owner.equip_traitor()
var/datum/component/uplink/uplink = uplink_holder.GetComponent(/datum/component/uplink)
uplink.telecrystals = INITIAL_CRYSTALS
// Give AI hacking board
diff --git a/code/modules/antagonists/traitor/IAA/internal_affairs.dm b/code/modules/antagonists/traitor/IAA/internal_affairs.dm
index 4414fe8257..796365a459 100644
--- a/code/modules/antagonists/traitor/IAA/internal_affairs.dm
+++ b/code/modules/antagonists/traitor/IAA/internal_affairs.dm
@@ -7,37 +7,51 @@
/datum/antagonist/traitor/internal_affairs
name = "Internal Affairs Agent"
employer = "Nanotrasen"
- special_role = "internal affairs agent"
+ suicide_cry = "FOR THE COMPANY!!"
antagpanel_category = "IAA"
+ var/special_role = "internal affairs agent"
var/syndicate = FALSE
var/last_man_standing = FALSE
var/list/datum/mind/targets_stolen
+/datum/antagonist/traitor/internal_affairs/New()
+ . = ..()
+ LAZYADD(targets_stolen, src)
/datum/antagonist/traitor/internal_affairs/proc/give_pinpointer()
- if(owner && owner.current)
+ if(!owner)
+ CRASH("Antag datum with no owner.")
+
+ if(owner.current)
owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer)
/datum/antagonist/traitor/internal_affairs/apply_innate_effects()
- .=..() //in case the base is used in future
- if(owner && owner.current)
+ . = ..()
+
+ if(!owner)
+ CRASH("Antag datum with no owner.")
+
+ if(owner.current)
give_pinpointer(owner.current)
/datum/antagonist/traitor/internal_affairs/remove_innate_effects()
- .=..()
- if(owner && owner.current)
+ . = ..()
+
+ if(!owner)
+ CRASH("Antag datum with no owner.")
+
+ if(owner.current)
owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer)
/datum/antagonist/traitor/internal_affairs/on_gain()
START_PROCESSING(SSprocessing, src)
- .=..()
+ . = ..()
/datum/antagonist/traitor/internal_affairs/on_removal()
STOP_PROCESSING(SSprocessing,src)
- .=..()
+ . = ..()
/datum/antagonist/traitor/internal_affairs/process()
iaa_process()
-
/datum/status_effect/agent_pinpointer
id = "agent_pinpointer"
duration = -1
@@ -46,6 +60,8 @@
var/minimum_range = PINPOINTER_MINIMUM_RANGE
var/range_fuzz_factor = PINPOINTER_EXTRA_RANDOM_RANGE
var/mob/scan_target = null
+ var/range_mid = 8
+ var/range_far = 16
/atom/movable/screen/alert/status_effect/agent_pinpointer
name = "Internal Affairs Integrated Pinpointer"
@@ -66,13 +82,13 @@
linked_alert.icon_state = "pinondirect"
else
linked_alert.setDir(get_dir(here, there))
- switch(get_dist(here, there))
- if(1 to 8)
- linked_alert.icon_state = "pinonclose"
- if(9 to 16)
- linked_alert.icon_state = "pinonmedium"
- if(16 to INFINITY)
- linked_alert.icon_state = "pinonfar"
+ var/dist = (get_dist(here, there))
+ if(dist >= 1 && dist <= range_mid)
+ linked_alert.icon_state = "pinonclose"
+ else if(dist > range_mid && dist <= range_far)
+ linked_alert.icon_state = "pinonmedium"
+ else if(dist > range_far)
+ linked_alert.icon_state = "pinonfar"
/datum/status_effect/agent_pinpointer/proc/scan_for_target()
scan_target = null
@@ -99,37 +115,42 @@
return (istype(O, /datum/objective/assassinate/internal)||istype(O, /datum/objective/destroy/internal))
/datum/antagonist/traitor/proc/replace_escape_objective()
- if(!owner || !objectives.len)
+ if(!owner)
+ CRASH("Antag datum with no owner.")
+ if(!objectives.len)
return
- for (var/objective_ in objectives)
- if(!(istype(objective_, /datum/objective/escape)||istype(objective_, /datum/objective/survive)))
+ for (var/objective in objectives)
+ if(!(istype(objective, /datum/objective/escape) || istype(objective, /datum/objective/survive)))
continue
- remove_objective(objective_)
+ objectives -= objective
var/datum/objective/martyr/martyr_objective = new
martyr_objective.owner = owner
add_objective(martyr_objective)
/datum/antagonist/traitor/proc/reinstate_escape_objective()
- if(!owner||!objectives.len)
+ if(!owner)
+ CRASH("Antag datum with no owner.")
+ if(!objectives.len)
return
- for (var/objective_ in objectives)
- if(!istype(objective_, /datum/objective/martyr))
+ for (var/objective in objectives)
+ if(!istype(objective, /datum/objective/martyr))
continue
- remove_objective(objective_)
+ remove_objective(objective)
/datum/antagonist/traitor/internal_affairs/reinstate_escape_objective()
..()
- var/objtype = !istype(traitor_kind,TRAITOR_AI) ? /datum/objective/escape : /datum/objective/survive
- var/datum/objective/escape_objective = new objtype
+ for (var/datum/objective/martyr/martyr_objective in objectives)
+ objectives -= martyr_objective
+
+ var/datum/objective/escape_objective = new /datum/objective/escape()
escape_objective.owner = owner
- add_objective(escape_objective)
+ objectives += escape_objective
/datum/antagonist/traitor/internal_affairs/proc/steal_targets(datum/mind/victim)
if(!owner.current||owner.current.stat==DEAD)
return
- to_chat(owner.current, " Target eliminated: [victim.name]")
- LAZYINITLIST(targets_stolen)
+ to_chat(owner.current, span_userdanger("Target eliminated: [victim.name]"))
for(var/objective_ in victim.get_all_objectives())
if(istype(objective_, /datum/objective/assassinate/internal))
var/datum/objective/assassinate/internal/objective = objective_
@@ -143,7 +164,7 @@
add_objective(new_objective)
targets_stolen += objective.target
var/status_text = objective.check_completion() ? "neutralised" : "active"
- to_chat(owner.current, " New target added to database: [objective.target.name] ([status_text]) ")
+ to_chat(owner.current, span_userdanger("New target added to database: [objective.target.name] ([status_text])"))
else if(istype(objective_, /datum/objective/destroy/internal))
var/datum/objective/destroy/internal/objective = objective_
var/datum/objective/destroy/internal/new_objective = new
@@ -156,7 +177,7 @@
add_objective(new_objective)
targets_stolen += objective.target
var/status_text = objective.check_completion() ? "neutralised" : "active"
- to_chat(owner.current, " New target added to database: [objective.target.name] ([status_text]) ")
+ to_chat(owner.current, span_userdanger("New target added to database: [objective.target.name] ([status_text])"))
last_man_standing = TRUE
for(var/objective_ in objectives)
if(!is_internal_objective(objective_))
@@ -167,13 +188,15 @@
return
if(last_man_standing)
if(syndicate)
- to_chat(owner.current,"All the suspected agents are dead, and no more is required of you. Die a glorious death, agent.")
- replace_escape_objective(owner)
+ to_chat(owner.current,span_userdanger("All the suspected agents are dead, and no more is required of you. Die a glorious death, agent."))
else
- to_chat(owner.current,"All the other agents are dead. You have done us all a great service and shall be honorably exiled upon returning to base.")
+ to_chat(owner.current,span_userdanger("All the other agents are dead. You have done us all a great service and shall be honorably exiled upon returning to base."))
+ replace_escape_objective(owner)
/datum/antagonist/traitor/internal_affairs/proc/iaa_process()
- if(owner&&owner.current&&owner.current.stat!=DEAD)
+ if(!owner)
+ CRASH("Antag datum with no owner.")
+ if(owner.current && owner.current.stat != DEAD)
for(var/objective_ in objectives)
if(!is_internal_objective(objective_))
continue
@@ -188,12 +211,12 @@
objective.stolen = TRUE
else
if(objective.stolen)
- var/fail_msg = "Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, pulled one over on you, and is still alive - do the job properly this time! "
+ var/fail_msg = span_userdanger("Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, pulled one over on you, and is still alive - do the job properly this time! ")
if(last_man_standing)
if(syndicate)
- fail_msg += " You no longer have permission to die. "
+ fail_msg += span_userdanger(" You no longer have permission to die. ")
else
- fail_msg += " The truth could still slip out! Cease any terrorist actions as soon as possible, unneeded property damage or loss of employee life will lead to great shame."
+ fail_msg += span_userdanger(" The truth could still slip out! Cease any terrorist actions as soon as possible, unneeded property damage or loss of employee life will lead to your contract being terminated.")
reinstate_escape_objective(owner)
last_man_standing = FALSE
to_chat(owner.current, fail_msg)
@@ -220,29 +243,24 @@
/datum/antagonist/traitor/internal_affairs/forge_traitor_objectives()
forge_iaa_objectives()
- var/objtype = !istype(traitor_kind,TRAITOR_AI) ? /datum/objective/escape : /datum/objective/survive
- var/datum/objective/escape_objective = new objtype
+ var/datum/objective/escape_objective = new /datum/objective/escape()
escape_objective.owner = owner
add_objective(escape_objective)
/datum/antagonist/traitor/internal_affairs/proc/greet_iaa()
- var/crime = pick("distribution of contraband" , "embezzlement", "piloting under the influence", "dereliction of duty", "syndicate collaboration", "mutiny", "multiple homicides", "corporate espionage", "receiving bribes", "malpractice", "worship of prohibited life forms", "possession of profane texts", "murder", "arson", "insulting their manager", "grand theft", "conspiracy", "attempting to unionize", "vandalism", "gross incompetence")
+ var/crime = pick("distribution of contraband", "embezzlement", "piloting under the influence", "dereliction of duty", "syndicate collaboration", "mutiny", "multiple homicides", "corporate espionage", "receiving bribes", "malpractice", "worship of prohibited life forms", "possession of profane texts", "murder", "arson", "insulting their manager", "grand theft", "conspiracy", "attempting to unionize", "vandalism", "gross incompetence")
- to_chat(owner.current, "You are the [special_role].")
+ to_chat(owner.current, span_userdanger("You are the [special_role]."))
if(syndicate)
- to_chat(owner.current, "GREAT LEADER IS DEAD. NANOTRASEN MUST FALL.")
- to_chat(owner.current, "Your have infiltrated this vessel to cause chaos and assassinate targets known to have conspired against the Syndicate.")
- to_chat(owner.current, "Any damage you cause will be a further embarrassment to Nanotrasen, so you have no limits on collateral damage.")
- to_chat(owner.current, "You have been provided with a standard uplink to accomplish your task. ")
- to_chat(owner.current, "By no means reveal that you are a Syndicate agent. By no means reveal that your targets are being hunted.")
+ to_chat(owner.current, span_userdanger("Your target has been framed for [crime], and you have been tasked with eliminating them to prevent them defending themselves in court."))
+ to_chat(owner.current, "Any damage you cause will be a further embarrassment to Nanotrasen, so you have no limits on collateral damage.")
+ to_chat(owner.current, span_userdanger("You have been provided with a standard uplink to accomplish your task."))
else
- to_chat(owner.current, "CAUTION: Your legal status as a citizen of NanoTrasen will be permanently revoked upon completion of your first contract.")
- to_chat(owner.current, "Your target has been suspected of [crime], and must be removed from this plane.")
- to_chat(owner.current, "While you have a license to kill, you are to eliminate your targets with no collateral or unrelated deaths.")
- to_chat(owner.current, "For the sake of plausable deniability, you have been equipped with captured Syndicate equipment via uplink.")
- to_chat(owner.current, "By no means reveal that you, or any other NT employees, are undercover agents.")
+ to_chat(owner.current, span_userdanger("Your target is suspected of [crime], and you have been tasked with eliminating them by any means necessary to avoid a costly and embarrassing public trial."))
+ to_chat(owner.current, "While you have a license to kill, unneeded property damage or loss of employee life will lead to your contract being terminated.")
+ to_chat(owner.current, span_userdanger("For the sake of plausible deniability, you have been equipped with an array of captured Syndicate weaponry available via uplink."))
- to_chat(owner.current, "Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them.")
+ to_chat(owner.current, span_userdanger("Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them."))
owner.announce_objectives()
/datum/antagonist/traitor/internal_affairs/greet()
diff --git a/code/modules/antagonists/traitor/classes/martyr.dm b/code/modules/antagonists/traitor/classes/martyr.dm
index 5c407b70fd..b0a6494de7 100644
--- a/code/modules/antagonists/traitor/classes/martyr.dm
+++ b/code/modules/antagonists/traitor/classes/martyr.dm
@@ -5,7 +5,7 @@
chaos = 5
threat = 5
min_players = 20
- uplink_filters = list(/datum/uplink_item/stealthy_weapons/romerol_kit,/datum/uplink_item/bundles_TC/contract_kit)
+ uplink_filters = list(/datum/uplink_item/stealthy_weapons/romerol_kit,/datum/uplink_item/bundles_tc/contract_kit)
/datum/traitor_class/human/martyr/forge_objectives(datum/antagonist/traitor/T)
var/datum/objective/martyr/O = new
diff --git a/code/modules/antagonists/traitor/classes/traitor_class.dm b/code/modules/antagonists/traitor/classes/traitor_class.dm
index bcc6fc18dd..bc24aecd9c 100644
--- a/code/modules/antagonists/traitor/classes/traitor_class.dm
+++ b/code/modules/antagonists/traitor/classes/traitor_class.dm
@@ -11,6 +11,8 @@ GLOBAL_LIST_EMPTY(traitor_classes)
/// Minimum players for this to randomly roll via get_random_traitor_kind().
var/min_players = 0
var/list/uplink_filters
+ /// Specific tgui theme for the player's antag info panel.
+ var/tgui_theme = "syndicate"
/datum/traitor_class/New()
..()
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index b6fc397233..78403d4e27 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -5,14 +5,22 @@
job_rank = ROLE_TRAITOR
antag_moodlet = /datum/mood_event/focused
skill_modifiers = list(/datum/skill_modifier/job/level/wiring/basic)
- var/special_role = ROLE_TRAITOR
+ hijack_speed = 0.5 //10 seconds per hijack stage by default
+ ui_name = "AntagInfoTraitor"
+ suicide_cry = "FOR THE SYNDICATE!!"
var/employer = "The Syndicate"
var/give_objectives = TRUE
var/should_give_codewords = TRUE
var/should_equip = TRUE
+
+ ///special datum about what kind of employer the trator has
var/datum/traitor_class/traitor_kind
+
+ ///reference to the uplink this traitor was given, if they were.
+ var/datum/component/uplink/uplink
+
var/datum/contractor_hub/contractor_hub
- hijack_speed = 0.5 //10 seconds per hijack stage by default
+
threat = 5
/datum/antagonist/traitor/New()
@@ -45,7 +53,7 @@
/datum/antagonist/traitor/process()
traitor_kind.on_process(src)
-/proc/get_random_traitor_kind(var/list/blacklist = list())
+/proc/get_random_traitor_kind(list/blacklist = list())
var/list/weights = list()
for(var/C in GLOB.traitor_classes)
if(!(C in blacklist))
@@ -62,23 +70,24 @@
return choice
/datum/antagonist/traitor/on_gain()
+ owner.special_role = job_rank
if(owner.current && isAI(owner.current))
set_traitor_kind(TRAITOR_AI)
else
set_traitor_kind(get_random_traitor_kind())
SSticker.mode.traitors += owner
- owner.special_role = special_role
finalize_traitor()
- ..()
+ uplink = owner.find_syndicate_uplink()
+ return ..()
/datum/antagonist/traitor/on_removal()
+ if(!silent && owner.current)
+ to_chat(owner.current,span_userdanger("You are no longer the [job_rank]!"))
//Remove malf powers.
traitor_kind.on_removal(src)
SSticker.mode.traitors -= owner
- if(!silent && owner.current)
- to_chat(owner.current," You are no longer the [special_role]! ")
owner.special_role = null
- . = ..()
+ return ..()
/datum/antagonist/traitor/proc/handle_hearing(datum/source, list/hearing_args)
var/message = hearing_args[HEARING_RAW_MESSAGE]
@@ -93,6 +102,7 @@
/datum/antagonist/traitor/proc/remove_objective(datum/objective/O)
objectives -= O
+/// Generates a complete set of traitor objectives up to the traitor objective limit, including non-generic objectives such as martyr and hijack.
/datum/antagonist/traitor/proc/forge_traitor_objectives()
traitor_kind.forge_objectives(src)
@@ -151,21 +161,42 @@
H.dna.add_mutation(CLOWNMUT)
UnregisterSignal(M, COMSIG_MOVABLE_HEAR)
+
+/datum/antagonist/traitor/ui_static_data(mob/user)
+ var/list/data = list()
+ data["phrases"] = jointext(GLOB.syndicate_code_phrase, ", ")
+ data["responses"] = jointext(GLOB.syndicate_code_response, ", ")
+ data["theme"] = traitor_kind.tgui_theme //traitor_flavor["ui_theme"]
+ data["code"] = uplink.unlock_code
+ data["intro"] = "You are from [traitor_kind.employer]." //traitor_flavor["introduction"]
+ data["allies"] = "Most other syndicate operatives are not to be trusted (but try not to rat them out), as they might have been assigned opposing objectives." //traitor_flavor["allies"]
+ data["goal"] = "We do not approve of mindless killing of innocent workers; \"get in, get done, get out\" is our motto." //traitor_flavor["goal"]
+ data["has_uplink"] = uplink ? TRUE : FALSE
+ if(uplink)
+ data["uplink_intro"] = "You have been provided with a standard uplink to accomplish your task." //traitor_flavor["uplink"]
+ data["uplink_unlock_info"] = uplink.unlock_text
+ data["objectives"] = get_objectives()
+ return data
+
+/// Outputs this shift's codewords and responses to the antag's chat and copies them to their memory.
/datum/antagonist/traitor/proc/give_codewords()
if(!owner.current)
return
- var/mob/traitor_mob=owner.current
+
+ var/mob/traitor_mob = owner.current
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
var/responses = jointext(GLOB.syndicate_code_response, ", ")
- var/dat = "The Syndicate have provided you with the following codewords to identify fellow agents:\n"
- dat += "Code Phrase: [phrases]\n"
- dat += "Code Response: [responses]"
- to_chat(traitor_mob, dat)
+ to_chat(traitor_mob, "The Syndicate have provided you with the following codewords to identify fellow agents:")
+ to_chat(traitor_mob, "Code Phrase: [span_blue("[phrases]")]")
+ to_chat(traitor_mob, "Code Response: [span_red("[responses]")]")
- antag_memory += "Code Phrase: [phrases]
"
- antag_memory += "Code Response: [responses]
"
+ antag_memory += "Code Phrase: [span_blue("[phrases]")]
"
+ antag_memory += "Code Response: [span_red("[responses]")]
"
+
+ to_chat(traitor_mob, "Use the codewords during regular conversation to identify other agents. Proceed with caution, however, as everyone is a potential foe.")
+ to_chat(traitor_mob, span_alertwarning("You memorize the codewords, allowing you to recognise them when heard."))
/datum/antagonist/traitor/proc/add_law_zero()
var/mob/living/silicon/ai/killer = owner.current
@@ -220,44 +251,43 @@
where = "In your [equipped_slot]"
to_chat(mob, "
[where] is a folder containing secret documents that another Syndicate group wants. We have set up a meeting with one of their agents on station to make an exchange. Exercise extreme caution as they cannot be trusted and may be hostile.
")
-//TODO Collate
/datum/antagonist/traitor/roundend_report()
var/list/result = list()
- var/traitorwin = TRUE
+ var/traitor_won = TRUE
result += printplayer(owner)
- var/TC_uses = 0
- var/uplink_true = FALSE
+ var/used_telecrystals = 0
+ var/uplink_owned = FALSE
var/purchases = ""
+
LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
- var/datum/uplink_purchase_log/H = GLOB.uplink_purchase_logs_by_key[owner.key]
- if(H)
- TC_uses = H.total_spent
- uplink_true = TRUE
- purchases += H.generate_render(FALSE)
+ // Uplinks add an entry to uplink_purchase_logs_by_key on init.
+ var/datum/uplink_purchase_log/purchase_log = GLOB.uplink_purchase_logs_by_key[owner.key]
+ if(purchase_log)
+ used_telecrystals = purchase_log.total_spent
+ uplink_owned = TRUE
+ purchases += purchase_log.generate_render(FALSE)
var/objectives_text = ""
- if(objectives.len)//If the traitor had no objectives, don't need to process this.
+ if(objectives.len) //If the traitor had no objectives, don't need to process this.
var/count = 1
for(var/datum/objective/objective in objectives)
- if(objective.completable)
- var/completion = objective.check_completion()
- if(completion >= 1)
- objectives_text += "
Objective #[count]: [objective.explanation_text] Success!"
- else if(completion <= 0)
- objectives_text += "
Objective #[count]: [objective.explanation_text] Fail."
- traitorwin = FALSE
- else
- objectives_text += "
Objective #[count]: [objective.explanation_text] [completion*100]%"
+ var/completion = objective.check_completion()
+ if(completion >= 1)
+ objectives_text += "
Objective #[count]: [objective.explanation_text] [span_greentext("Success!")]"
+ else if(completion <= 0)
+ objectives_text += "
Objective #[count]: [objective.explanation_text] [span_redtext("Fail.")]"
+ traitor_won = FALSE
else
- objectives_text += "
Objective #[count]: [objective.explanation_text]"
+ objectives_text += "
Objective #[count]: [objective.explanation_text] [completion*100]%"
+
count++
- if(uplink_true)
- var/uplink_text = "(used [TC_uses] TC) [purchases]"
- if(TC_uses==0 && traitorwin)
+ if(uplink_owned)
+ var/uplink_text = "(used [used_telecrystals] TC) [purchases]"
+ if((used_telecrystals == 0) && traitor_won)
var/static/icon/badass = icon('icons/badass.dmi', "badass")
uplink_text += "[icon2html(badass, world)]"
result += uplink_text
@@ -266,55 +296,59 @@
var/special_role_text = lowertext(name)
- if(contractor_hub)
+ if (contractor_hub)
result += contractor_round_end()
- if(traitorwin)
- result += "The [special_role_text] was successful!"
+ if(traitor_won)
+ result += span_greentext("The [special_role_text] was successful!")
else
- result += "The [special_role_text] has failed!"
+ result += span_redtext("The [special_role_text] has failed!")
SEND_SOUND(owner.current, 'sound/ambience/ambifailure.ogg')
return result.Join("
")
/// Proc detailing contract kit buys/completed contracts/additional info
/datum/antagonist/traitor/proc/contractor_round_end()
- var result = ""
- var total_spent_rep = 0
+ var/result = ""
+ var/total_spent_rep = 0
- var/completed_contracts = 0
+ var/completed_contracts = contractor_hub.contracts_completed
var/tc_total = contractor_hub.contract_TC_payed_out + contractor_hub.contract_TC_to_redeem
- for(var/datum/syndicate_contract/contract in contractor_hub.assigned_contracts)
- if(contract.status == CONTRACT_STATUS_COMPLETE)
- completed_contracts++
var/contractor_item_icons = "" // Icons of purchases
var/contractor_support_unit = "" // Set if they had a support unit - and shows appended to their contracts completed
- for(var/datum/contractor_item/contractor_purchase in contractor_hub.purchased_items) // Get all the icons/total cost for all our items bought
+ /// Get all the icons/total cost for all our items bought
+ for (var/datum/contractor_item/contractor_purchase in contractor_hub.purchased_items)
contractor_item_icons += "\[ [contractor_purchase.name] - [contractor_purchase.cost] Rep
[contractor_purchase.desc] \]"
+
total_spent_rep += contractor_purchase.cost
- if(istype(contractor_purchase, /datum/contractor_item/contractor_partner)) // Special case for reinforcements, we want to show their ckey and name on round end.
+
+ /// Special case for reinforcements, we want to show their ckey and name on round end.
+ if (istype(contractor_purchase, /datum/contractor_item/contractor_partner))
var/datum/contractor_item/contractor_partner/partner = contractor_purchase
contractor_support_unit += "
[partner.partner_mind.key] played [partner.partner_mind.current.name], their contractor support unit."
+
if (contractor_hub.purchased_items.len)
- result += "
(used [total_spent_rep] Rep)"
+ result += "
(used [total_spent_rep] Rep) "
result += contractor_item_icons
result += "
"
- if(completed_contracts > 0)
+ if (completed_contracts > 0)
var/pluralCheck = "contract"
- if(completed_contracts > 1)
+ if (completed_contracts > 1)
pluralCheck = "contracts"
- result += "Completed [completed_contracts] [pluralCheck] for a total of \
- [tc_total] TC!
"
+
+ result += "Completed [span_greentext("[completed_contracts]")] [pluralCheck] for a total of \
+ [span_greentext("[tc_total] TC")]![contractor_support_unit]
"
+
return result
/datum/antagonist/traitor/roundend_report_footer()
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
var/responses = jointext(GLOB.syndicate_code_response, ", ")
- var message = "
The code phrases were: [phrases]
\
- The code responses were: [responses]
"
+ var/message = "
The code phrases were: [phrases]
\
+ The code responses were: [span_redtext("[responses]")]
"
return message
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index 23e870a0ec..c52436a3c4 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -3,12 +3,15 @@
roundend_category = "wizards/witches"
antagpanel_category = "Wizard"
job_rank = ROLE_WIZARD
+ antag_hud_type = ANTAG_HUD_WIZ
+ antag_hud_name = "wizard"
antag_moodlet = /datum/mood_event/focused
- threat = 30
+ hijack_speed = 0.5
+ ui_name = "AntagInfoWizard"
+ suicide_cry = "FOR THE FEDERATION!!"
var/give_objectives = TRUE
var/strip = TRUE //strip before equipping
var/allow_rename = TRUE
- var/hud_version = "wizard"
var/datum/team/wizard/wiz_team //Only created if wizard summons apprentices
var/move_to_lair = TRUE
var/outfit_type = /datum/outfit/wizard
@@ -50,16 +53,17 @@
wiz_team = new(owner)
wiz_team.name = "[owner.current.real_name] team"
wiz_team.master_wizard = src
- update_wiz_icons_added(owner.current)
+ add_antag_hud(antag_hud_type, antag_hud_name, owner.current)
/datum/antagonist/wizard/proc/send_to_lair()
- if(!owner || !owner.current)
+ if(!owner)
+ CRASH("Antag datum with no owner.")
+ if(!owner.current)
return
if(!GLOB.wizardstart.len)
SSjob.SendToLateJoin(owner.current)
to_chat(owner, "HOT INSERTION, GO GO GO")
- else
- owner.current.forceMove(pick(GLOB.wizardstart))
+ owner.current.forceMove(pick(GLOB.wizardstart))
/datum/antagonist/wizard/proc/create_objectives()
var/datum/objective/flavor/wizard/new_objective = new
@@ -79,7 +83,7 @@
/datum/antagonist/wizard/proc/equip_wizard()
if(!owner)
- return
+ CRASH("Antag datum with no owner.")
var/mob/living/carbon/human/H = owner.current
if(!istype(H))
return
@@ -91,18 +95,14 @@
H.age = wiz_age
H.equipOutfit(outfit_type)
-/datum/antagonist/wizard/greet()
- to_chat(owner, "You are the Space Wizard!")
- to_chat(owner, "The Space Wizards Federation has given you the following tasks:")
- owner.announce_objectives()
- to_chat(owner, "These are merely guidelines! The federation are your masters, but you forge your own path!")
- to_chat(owner, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.")
- to_chat(owner, "The spellbook is bound to you, and others cannot use it.")
- to_chat(owner, "In your pockets you will find a teleport scroll. Use it as needed.")
- to_chat(owner,"Remember: do not forget to prepare your spells.")
+/datum/antagonist/wizard/ui_static_data(mob/user)
+ . = ..()
+ var/list/data = list()
+ data["objectives"] = get_objectives()
+ return data
/datum/antagonist/wizard/farewell()
- to_chat(owner, "You have been brainwashed! You are no longer a wizard!")
+ to_chat(owner, span_userdanger("You have been brainwashed! You are no longer a wizard!"))
/datum/antagonist/wizard/proc/rename_wizard()
set waitfor = FALSE
@@ -111,7 +111,7 @@
var/wizard_name_second = pick(GLOB.wizard_second)
var/randomname = "[wizard_name_first] [wizard_name_second]"
var/mob/living/wiz_mob = owner.current
- var/newname = reject_bad_name(stripped_input(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname, MAX_NAME_LEN))
+ var/newname = sanitize_name(reject_bad_text(stripped_input(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname, MAX_NAME_LEN)))
if (!newname)
newname = randomname
@@ -120,12 +120,12 @@
/datum/antagonist/wizard/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
- update_wiz_icons_added(M, wiz_team ? TRUE : FALSE) //Don't bother showing the icon if you're solo wizard
+ add_antag_hud(antag_hud_type, antag_hud_name, M)
M.faction |= ROLE_WIZARD
/datum/antagonist/wizard/remove_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
- update_wiz_icons_removed(M)
+ remove_antag_hud(antag_hud_type, M)
M.faction -= ROLE_WIZARD
@@ -138,7 +138,7 @@
/datum/antagonist/wizard/apprentice
name = "Wizard Apprentice"
- hud_version = "apprentice"
+ antag_hud_name = "apprentice"
var/datum/mind/master
var/school = APPRENTICE_DESTRUCTION
outfit_type = /datum/outfit/wizard/apprentice
@@ -157,7 +157,7 @@
/datum/antagonist/wizard/apprentice/equip_wizard()
. = ..()
if(!owner)
- return
+ CRASH("Antag datum with no owner.")
var/mob/living/carbon/human/H = owner.current
if(!istype(H))
return
@@ -169,12 +169,12 @@
if(APPRENTICE_BLUESPACE)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/turf_teleport/blink(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null))
- to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned reality bending mobility spells. You are able to cast teleport and ethereal jaunt.")
+ to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned reality-bending mobility spells. You are able to cast teleport and ethereal jaunt.")
if(APPRENTICE_HEALING)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/charge(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/forcewall(null))
H.put_in_hands(new /obj/item/gun/magic/staff/healing(H))
- to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall.")
+ to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned life-saving survival spells. You are able to cast charge and forcewall.")
if(APPRENTICE_ROBELESS)
owner.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/pointed/mind_transfer(null))
@@ -194,6 +194,7 @@
//Random event wizard
/datum/antagonist/wizard/apprentice/imposter
name = "Wizard Imposter"
+ show_in_antagpanel = FALSE
allow_rename = FALSE
move_to_lair = FALSE
@@ -224,20 +225,11 @@
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/turf_teleport/blink(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null))
-/datum/antagonist/wizard/proc/update_wiz_icons_added(mob/living/wiz,join = TRUE)
- var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ]
- wizhud.join_hud(wiz)
- set_antag_hud(wiz, hud_version)
-
-/datum/antagonist/wizard/proc/update_wiz_icons_removed(mob/living/wiz)
- var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ]
- wizhud.leave_hud(wiz)
- set_antag_hud(wiz, null)
-
-
/datum/antagonist/wizard/academy
name = "Academy Teacher"
+ show_in_antagpanel = FALSE
outfit_type = /datum/outfit/wizard/academy
+ move_to_lair = FALSE
/datum/antagonist/wizard/academy/equip_wizard()
. = ..()
@@ -250,7 +242,7 @@
if(!istype(M))
return
- var/obj/item/implant/exile/Implant = new
+ var/obj/item/implant/exile/Implant = new/obj/item/implant/exile(M)
Implant.implant(M)
/datum/antagonist/wizard/academy/create_objectives()
@@ -265,25 +257,25 @@
parts += printplayer(owner)
var/count = 1
- var/wizardwin = 1
+ var/wizardwin = TRUE
for(var/datum/objective/objective in objectives)
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
- parts += "Objective #[count]: [objective.explanation_text] Success!"
+ parts += "
Objective #[count]: [objective.explanation_text] [span_greentext("Success!")]"
else if(completion <= 0)
- parts += "Objective #[count]: [objective.explanation_text] Fail."
+ parts += "
Objective #[count]: [objective.explanation_text] [span_redtext("Fail.")]"
wizardwin = FALSE
else
- parts += "Objective #[count]: [objective.explanation_text] [completion*100]%"
+ parts += "
Objective #[count]: [objective.explanation_text] [completion*100]%"
else
parts += "Objective #[count]: [objective.explanation_text]"
count++
if(wizardwin)
- parts += "The wizard was successful!"
+ parts += span_greentext("The wizard was successful!")
else
- parts += "The wizard has failed!"
+ parts += span_redtext("The wizard has failed!")
if(owner.spell_list.len>0)
parts += "[owner.name] used the following spells: "
diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm
index 94c7630bd9..f88f57eacc 100644
--- a/code/modules/asset_cache/asset_list.dm
+++ b/code/modules/asset_cache/asset_list.dm
@@ -13,9 +13,6 @@ GLOBAL_LIST_EMPTY(asset_datums)
var/_abstract = /datum/asset
var/cached_url_mappings
- /// Whether or not this asset should be loaded in the "early assets" SS
- var/early = FALSE
-
/datum/asset/New()
GLOB.asset_datums[type] = src
register()
@@ -368,28 +365,3 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/simple/namespaced/proc/get_htmlloader(filename)
return url2htmlloader(SSassets.transport.get_asset_url(filename, assets[filename]))
-/// A subtype to generate a JSON file from a list
-/datum/asset/json
- _abstract = /datum/asset/json
- /// The filename, will be suffixed with ".json"
- var/name
-
-/datum/asset/json/send(client)
- return SSassets.transport.send_assets(client, "data/[name].json")
-
-/datum/asset/json/get_url_mappings()
- return list(
- "[name].json" = SSassets.transport.get_asset_url("data/[name].json"),
- )
-
-/datum/asset/json/register()
- var/filename = "data/[name].json"
- fdel(filename)
- text2file(json_encode(generate()), filename)
- SSassets.transport.register_asset(filename, fcopy_rsc(filename))
- fdel(filename)
-
-/// Returns the data that will be JSON encoded
-/datum/asset/json/proc/generate()
- SHOULD_CALL_PARENT(FALSE)
- CRASH("generate() not implemented for [type]!")
diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm
index 2d680fe212..2f2edfb595 100644
--- a/code/modules/asset_cache/asset_list_items.dm
+++ b/code/modules/asset_cache/asset_list_items.dm
@@ -1,11 +1,5 @@
//DEFINITIONS FOR ASSET DATUMS START HERE.
-/datum/asset/simple/tgui_common
- keep_local_name = TRUE
- assets = list(
- "tgui-common.bundle.js" = file("tgui/public/tgui-common.bundle.js"),
- )
-
/datum/asset/simple/tgui
keep_local_name = TRUE
assets = list(
@@ -54,9 +48,14 @@
/datum/asset/simple/radar_assets
assets = list(
- "ntosradarbackground.png" = 'icons/UI_Icons/tgui/ntosradar_background.png',
- "ntosradarpointer.png" = 'icons/UI_Icons/tgui/ntosradar_pointer.png',
- "ntosradarpointerS.png" = 'icons/UI_Icons/tgui/ntosradar_pointer_S.png'
+ "ntosradarbackground.png" = 'icons/ui_icons/tgui/ntosradar_background.png',
+ "ntosradarpointer.png" = 'icons/ui_icons/tgui/ntosradar_pointer.png',
+ "ntosradarpointerS.png" = 'icons/ui_icons/tgui/ntosradar_pointer_S.png'
+ )
+
+/datum/asset/simple/circuit_assets
+ assets = list(
+ "grid_background.png" = 'icons/ui_icons/tgui/grid_background.png'
)
/datum/asset/spritesheet/simple/pda
@@ -91,6 +90,7 @@
"status" = 'icons/pda_icons/pda_status.png',
"dronephone" = 'icons/pda_icons/pda_dronephone.png',
"emoji" = 'icons/pda_icons/pda_emoji.png'
+ // "droneblacklist" = 'icons/pda_icons/pda_droneblacklist.png',
)
/datum/asset/spritesheet/simple/paper
@@ -116,7 +116,7 @@
/datum/asset/simple/irv
assets = list(
- "jquery-ui.custom-core-widgit-mouse-sortable-min.js" = 'html/IRV/jquery-ui.custom-core-widgit-mouse-sortable-min.js',
+ "jquery-ui.custom-core-widgit-mouse-sortable-min.js" = 'html/jquery/jquery-ui.custom-core-widgit-mouse-sortable.min.js',
)
/datum/asset/group/irv
@@ -149,11 +149,10 @@
)
parents = list("changelog.html" = 'html/changelog.html')
-
/datum/asset/simple/jquery
legacy = TRUE
assets = list(
- "jquery.min.js" = 'html/jquery.min.js',
+ "jquery.min.js" = 'html/jquery/jquery.min.js',
)
/datum/asset/simple/namespaced/fontawesome
@@ -223,102 +222,102 @@
/datum/asset/simple/arcade
assets = list(
- "boss1.gif" = 'icons/UI_Icons/Arcade/boss1.gif',
- "boss2.gif" = 'icons/UI_Icons/Arcade/boss2.gif',
- "boss3.gif" = 'icons/UI_Icons/Arcade/boss3.gif',
- "boss4.gif" = 'icons/UI_Icons/Arcade/boss4.gif',
- "boss5.gif" = 'icons/UI_Icons/Arcade/boss5.gif',
- "boss6.gif" = 'icons/UI_Icons/Arcade/boss6.gif',
+ "boss1.gif" = 'icons/ui_icons/arcade/boss1.gif',
+ "boss2.gif" = 'icons/ui_icons/arcade/boss2.gif',
+ "boss3.gif" = 'icons/ui_icons/arcade/boss3.gif',
+ "boss4.gif" = 'icons/ui_icons/arcade/boss4.gif',
+ "boss5.gif" = 'icons/ui_icons/arcade/boss5.gif',
+ "boss6.gif" = 'icons/ui_icons/arcade/boss6.gif',
)
/datum/asset/spritesheet/simple/achievements
name ="achievements"
assets = list(
- "default" = 'icons/UI_Icons/Achievements/default.png',
- "basemisc" = 'icons/UI_Icons/Achievements/basemisc.png',
- "baseboss" = 'icons/UI_Icons/Achievements/baseboss.png',
- "baseskill" = 'icons/UI_Icons/Achievements/baseskill.png',
- "bbgum" = 'icons/UI_Icons/Achievements/Boss/bbgum.png',
- "colossus" = 'icons/UI_Icons/Achievements/Boss/colossus.png',
- "hierophant" = 'icons/UI_Icons/Achievements/Boss/hierophant.png',
- "drake" = 'icons/UI_Icons/Achievements/Boss/drake.png',
- "legion" = 'icons/UI_Icons/Achievements/Boss/legion.png',
- "miner" = 'icons/UI_Icons/Achievements/Boss/miner.png',
- "swarmer" = 'icons/UI_Icons/Achievements/Boss/swarmer.png',
- "tendril" = 'icons/UI_Icons/Achievements/Boss/tendril.png',
- "featofstrength" = 'icons/UI_Icons/Achievements/Misc/featofstrength.png',
- "helbital" = 'icons/UI_Icons/Achievements/Misc/helbital.png',
- "jackpot" = 'icons/UI_Icons/Achievements/Misc/jackpot.png',
- "meteors" = 'icons/UI_Icons/Achievements/Misc/meteors.png',
- "timewaste" = 'icons/UI_Icons/Achievements/Misc/timewaste.png',
- "upgrade" = 'icons/UI_Icons/Achievements/Misc/upgrade.png',
- "clownking" = 'icons/UI_Icons/Achievements/Misc/clownking.png',
- "clownthanks" = 'icons/UI_Icons/Achievements/Misc/clownthanks.png',
- "rule8" = 'icons/UI_Icons/Achievements/Misc/rule8.png',
- "longshift" = 'icons/UI_Icons/Achievements/Misc/longshift.png',
- "snail" = 'icons/UI_Icons/Achievements/Misc/snail.png',
- "ascension" = 'icons/UI_Icons/Achievements/Misc/ascension.png',
- "ashascend" = 'icons/UI_Icons/Achievements/Misc/ashascend.png',
- "fleshascend" = 'icons/UI_Icons/Achievements/Misc/fleshascend.png',
- "rustascend" = 'icons/UI_Icons/Achievements/Misc/rustascend.png',
- "voidascend" = 'icons/UI_Icons/Achievements/Misc/voidascend.png',
- "toolbox_soul" = 'icons/UI_Icons/Achievements/Misc/toolbox_soul.png',
- "chem_tut" = 'icons/UI_Icons/Achievements/Misc/chem_tut.png',
- "mining" = 'icons/UI_Icons/Achievements/Skills/mining.png',
- "mafia" = 'icons/UI_Icons/Achievements/Mafia/mafia.png',
- "town" = 'icons/UI_Icons/Achievements/Mafia/town.png',
- "neutral" = 'icons/UI_Icons/Achievements/Mafia/neutral.png',
- "hated" = 'icons/UI_Icons/Achievements/Mafia/hated.png',
- "basemafia" ='icons/UI_Icons/Achievements/basemafia.png',
- "frenching" = 'icons/UI_Icons/Achievements/Misc/frenchingthebubble.png'
+ "default" = 'icons/ui_icons/achievements/default.png',
+ "basemisc" = 'icons/ui_icons/achievements/basemisc.png',
+ "baseboss" = 'icons/ui_icons/achievements/baseboss.png',
+ "baseskill" = 'icons/ui_icons/achievements/baseskill.png',
+ "bbgum" = 'icons/ui_icons/achievements/Boss/bbgum.png',
+ "colossus" = 'icons/ui_icons/achievements/Boss/colossus.png',
+ "hierophant" = 'icons/ui_icons/achievements/Boss/hierophant.png',
+ "drake" = 'icons/ui_icons/achievements/Boss/drake.png',
+ "legion" = 'icons/ui_icons/achievements/Boss/legion.png',
+ "miner" = 'icons/ui_icons/achievements/Boss/miner.png',
+ "swarmer" = 'icons/ui_icons/achievements/Boss/swarmer.png',
+ "tendril" = 'icons/ui_icons/achievements/Boss/tendril.png',
+ "featofstrength" = 'icons/ui_icons/achievements/Misc/featofstrength.png',
+ "helbital" = 'icons/ui_icons/achievements/Misc/helbital.png',
+ "jackpot" = 'icons/ui_icons/achievements/Misc/jackpot.png',
+ "meteors" = 'icons/ui_icons/achievements/Misc/meteors.png',
+ "timewaste" = 'icons/ui_icons/achievements/Misc/timewaste.png',
+ "upgrade" = 'icons/ui_icons/achievements/Misc/upgrade.png',
+ "clownking" = 'icons/ui_icons/achievements/Misc/clownking.png',
+ "clownthanks" = 'icons/ui_icons/achievements/Misc/clownthanks.png',
+ "rule8" = 'icons/ui_icons/achievements/Misc/rule8.png',
+ "longshift" = 'icons/ui_icons/achievements/Misc/longshift.png',
+ "snail" = 'icons/ui_icons/achievements/Misc/snail.png',
+ "ascension" = 'icons/ui_icons/achievements/Misc/ascension.png',
+ "ashascend" = 'icons/ui_icons/achievements/Misc/ashascend.png',
+ "fleshascend" = 'icons/ui_icons/achievements/Misc/fleshascend.png',
+ "rustascend" = 'icons/ui_icons/achievements/Misc/rustascend.png',
+ "voidascend" = 'icons/ui_icons/achievements/Misc/voidascend.png',
+ "toolbox_soul" = 'icons/ui_icons/achievements/Misc/toolbox_soul.png',
+ "chem_tut" = 'icons/ui_icons/achievements/Misc/chem_tut.png',
+ "mining" = 'icons/ui_icons/achievements/Skills/mining.png',
+ "mafia" = 'icons/ui_icons/achievements/Mafia/mafia.png',
+ "town" = 'icons/ui_icons/achievements/Mafia/town.png',
+ "neutral" = 'icons/ui_icons/achievements/Mafia/neutral.png',
+ "hated" = 'icons/ui_icons/achievements/Mafia/hated.png',
+ "basemafia" ='icons/ui_icons/achievements/basemafia.png',
+ "frenching" = 'icons/ui_icons/achievements/Misc/frenchingthebubble.png'
)
/datum/asset/spritesheet/simple/pills
name = "pills"
assets = list(
- "pill1" = 'icons/UI_Icons/Pills/pill1.png',
- "pill2" = 'icons/UI_Icons/Pills/pill2.png',
- "pill3" = 'icons/UI_Icons/Pills/pill3.png',
- "pill4" = 'icons/UI_Icons/Pills/pill4.png',
- "pill5" = 'icons/UI_Icons/Pills/pill5.png',
- "pill6" = 'icons/UI_Icons/Pills/pill6.png',
- "pill7" = 'icons/UI_Icons/Pills/pill7.png',
- "pill8" = 'icons/UI_Icons/Pills/pill8.png',
- "pill9" = 'icons/UI_Icons/Pills/pill9.png',
- "pill10" = 'icons/UI_Icons/Pills/pill10.png',
- "pill11" = 'icons/UI_Icons/Pills/pill11.png',
- "pill12" = 'icons/UI_Icons/Pills/pill12.png',
- "pill13" = 'icons/UI_Icons/Pills/pill13.png',
- "pill14" = 'icons/UI_Icons/Pills/pill14.png',
- "pill15" = 'icons/UI_Icons/Pills/pill15.png',
- "pill16" = 'icons/UI_Icons/Pills/pill16.png',
- "pill17" = 'icons/UI_Icons/Pills/pill17.png',
- "pill18" = 'icons/UI_Icons/Pills/pill18.png',
- "pill19" = 'icons/UI_Icons/Pills/pill19.png',
- "pill20" = 'icons/UI_Icons/Pills/pill20.png',
- "pill21" = 'icons/UI_Icons/Pills/pill21.png',
- "pill22" = 'icons/UI_Icons/Pills/pill22.png',
+ "pill1" = 'icons/ui_icons/pills/pill1.png',
+ "pill2" = 'icons/ui_icons/pills/pill2.png',
+ "pill3" = 'icons/ui_icons/pills/pill3.png',
+ "pill4" = 'icons/ui_icons/pills/pill4.png',
+ "pill5" = 'icons/ui_icons/pills/pill5.png',
+ "pill6" = 'icons/ui_icons/pills/pill6.png',
+ "pill7" = 'icons/ui_icons/pills/pill7.png',
+ "pill8" = 'icons/ui_icons/pills/pill8.png',
+ "pill9" = 'icons/ui_icons/pills/pill9.png',
+ "pill10" = 'icons/ui_icons/pills/pill10.png',
+ "pill11" = 'icons/ui_icons/pills/pill11.png',
+ "pill12" = 'icons/ui_icons/pills/pill12.png',
+ "pill13" = 'icons/ui_icons/pills/pill13.png',
+ "pill14" = 'icons/ui_icons/pills/pill14.png',
+ "pill15" = 'icons/ui_icons/pills/pill15.png',
+ "pill16" = 'icons/ui_icons/pills/pill16.png',
+ "pill17" = 'icons/ui_icons/pills/pill17.png',
+ "pill18" = 'icons/ui_icons/pills/pill18.png',
+ "pill19" = 'icons/ui_icons/pills/pill19.png',
+ "pill20" = 'icons/ui_icons/pills/pill20.png',
+ "pill21" = 'icons/ui_icons/pills/pill21.png',
+ "pill22" = 'icons/ui_icons/pills/pill22.png',
)
// /datum/asset/spritesheet/simple/condiments
// name = "condiments"
// assets = list(
-// CONDIMASTER_STYLE_FALLBACK = 'icons/UI_Icons/Condiments/emptycondiment.png',
-// "enzyme" = 'icons/UI_Icons/Condiments/enzyme.png',
-// "flour" = 'icons/UI_Icons/Condiments/flour.png',
-// "mayonnaise" = 'icons/UI_Icons/Condiments/mayonnaise.png',
-// "milk" = 'icons/UI_Icons/Condiments/milk.png',
-// "blackpepper" = 'icons/UI_Icons/Condiments/peppermillsmall.png',
-// "rice" = 'icons/UI_Icons/Condiments/rice.png',
-// "sodiumchloride" = 'icons/UI_Icons/Condiments/saltshakersmall.png',
-// "soymilk" = 'icons/UI_Icons/Condiments/soymilk.png',
-// "soysauce" = 'icons/UI_Icons/Condiments/soysauce.png',
-// "sugar" = 'icons/UI_Icons/Condiments/sugar.png',
-// "ketchup" = 'icons/UI_Icons/Condiments/ketchup.png',
-// "capsaicin" = 'icons/UI_Icons/Condiments/hotsauce.png',
-// "frostoil" = 'icons/UI_Icons/Condiments/coldsauce.png',
-// "bbqsauce" = 'icons/UI_Icons/Condiments/bbqsauce.png',
-// "cornoil" = 'icons/UI_Icons/Condiments/oliveoil.png',
+// CONDIMASTER_STYLE_FALLBACK = 'icons/ui_icons/condiments/emptycondiment.png',
+// "enzyme" = 'icons/ui_icons/condiments/enzyme.png',
+// "flour" = 'icons/ui_icons/condiments/flour.png',
+// "mayonnaise" = 'icons/ui_icons/condiments/mayonnaise.png',
+// "milk" = 'icons/ui_icons/condiments/milk.png',
+// "blackpepper" = 'icons/ui_icons/condiments/peppermillsmall.png',
+// "rice" = 'icons/ui_icons/condiments/rice.png',
+// "sodiumchloride" = 'icons/ui_icons/condiments/saltshakersmall.png',
+// "soymilk" = 'icons/ui_icons/condiments/soymilk.png',
+// "soysauce" = 'icons/ui_icons/condiments/soysauce.png',
+// "sugar" = 'icons/ui_icons/condiments/sugar.png',
+// "ketchup" = 'icons/ui_icons/condiments/ketchup.png',
+// "capsaicin" = 'icons/ui_icons/condiments/hotsauce.png',
+// "frostoil" = 'icons/ui_icons/condiments/coldsauce.png',
+// "bbqsauce" = 'icons/ui_icons/condiments/bbqsauce.png',
+// "cornoil" = 'icons/ui_icons/condiments/oliveoil.png',
// )
//this exists purely to avoid meta by pre-loading all language icons.
@@ -410,9 +409,15 @@
if (machine)
item = machine
+ // Check for GAGS support where necessary
+ // var/greyscale_config = initial(item.greyscale_config)
+ // var/greyscale_colors = initial(item.greyscale_colors)
+ // if (greyscale_config && greyscale_colors)
+ // icon_file = SSgreyscale.GetColoredIconByType(greyscale_config, greyscale_colors)
+ // else
icon_file = initial(item.icon)
- icon_state = initial(item.icon_state)
+ icon_state = initial(item.icon_state)
if(!(icon_state in icon_states(icon_file)))
warning("design [D] with icon '[icon_file]' missing state '[icon_state]'")
continue
@@ -441,7 +446,11 @@
if (!ispath(item, /atom))
continue
- var/icon_file = initial(item.icon)
+ var/icon_file
+ // if (initial(item.greyscale_colors) && initial(item.greyscale_config))
+ // icon_file = SSgreyscale.GetColoredIconByType(initial(item.greyscale_config), initial(item.greyscale_colors))
+ // else
+ icon_file = initial(item.icon)
var/icon_state = initial(item.icon_state)
var/icon/I
@@ -543,6 +552,35 @@
// Insert(id, fish_icon, fish_icon_state)
// ..()
+/datum/asset/simple/adventure
+ assets = list(
+ "default" = 'icons/ui_icons/adventure/default.png',
+ "grue" = 'icons/ui_icons/adventure/grue.png',
+ "signal_lost" ='icons/ui_icons/adventure/signal_lost.png',
+ "trade" = 'icons/ui_icons/adventure/trade.png',
+ )
+
+/datum/asset/simple/inventory
+ assets = list(
+ "inventory-glasses.png" = 'icons/ui_icons/inventory/glasses.png',
+ "inventory-head.png" = 'icons/ui_icons/inventory/head.png',
+ "inventory-neck.png" = 'icons/ui_icons/inventory/neck.png',
+ "inventory-mask.png" = 'icons/ui_icons/inventory/mask.png',
+ "inventory-ears.png" = 'icons/ui_icons/inventory/ears.png',
+ "inventory-uniform.png" = 'icons/ui_icons/inventory/uniform.png',
+ "inventory-suit.png" = 'icons/ui_icons/inventory/suit.png',
+ "inventory-gloves.png" = 'icons/ui_icons/inventory/gloves.png',
+ "inventory-hand_l.png" = 'icons/ui_icons/inventory/hand_l.png',
+ "inventory-hand_r.png" = 'icons/ui_icons/inventory/hand_r.png',
+ "inventory-shoes.png" = 'icons/ui_icons/inventory/shoes.png',
+ "inventory-suit_storage.png" = 'icons/ui_icons/inventory/suit_storage.png',
+ "inventory-id.png" = 'icons/ui_icons/inventory/id.png',
+ "inventory-belt.png" = 'icons/ui_icons/inventory/belt.png',
+ "inventory-back.png" = 'icons/ui_icons/inventory/back.png',
+ "inventory-pocket.png" = 'icons/ui_icons/inventory/pocket.png',
+ "inventory-collar.png" = 'icons/ui_icons/inventory/collar.png',
+ )
+
/// Removes all non-alphanumerics from the text, keep in mind this can lead to id conflicts
/proc/sanitize_css_class_name(name)
var/static/regex/regex = new(@"[^a-zA-Z0-9]","g")
@@ -550,5 +588,34 @@
/datum/asset/simple/tutorial_advisors
assets = list(
- "chem_help_advisor.gif" = 'icons/UI_Icons/Advisors/chem_help_advisor.gif',
+ "chem_help_advisor.gif" = 'icons/ui_icons/advisors/chem_help_advisor.gif',
)
+
+// /datum/asset/spritesheet/moods
+// name = "moods"
+// var/iconinserted = 1
+
+// /datum/asset/spritesheet/moods/register()
+// for(var/i in 1 to 9)
+// var/target_to_insert = "mood"+"[iconinserted]"
+// Insert(target_to_insert, 'icons/hud/screen_gen.dmi', target_to_insert)
+// iconinserted++
+// ..()
+
+// /datum/asset/spritesheet/moods/ModifyInserted(icon/pre_asset)
+// var/blended_color
+// switch(iconinserted)
+// if(1)
+// blended_color = "#f15d36"
+// if(2 to 3)
+// blended_color = "#f38943"
+// if(4)
+// blended_color = "#dfa65b"
+// if(5)
+// blended_color = "#4b96c4"
+// if(6)
+// blended_color = "#86d656"
+// else
+// blended_color = "#2eeb9a"
+// pre_asset.Blend(blended_color, ICON_MULTIPLY)
+// return pre_asset
diff --git a/code/modules/asset_cache/transports/asset_transport.dm b/code/modules/asset_cache/transports/asset_transport.dm
index f5a1af4f05..b2da2602ae 100644
--- a/code/modules/asset_cache/transports/asset_transport.dm
+++ b/code/modules/asset_cache/transports/asset_transport.dm
@@ -115,7 +115,7 @@
if (unreceived.len)
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
- to_chat(client, "Sending Resources...")
+ to_chat(client, "Sending Resources...")
for (var/asset_name in unreceived)
var/new_asset_name = asset_name
diff --git a/code/modules/balloon_alert/balloon_alert.dm b/code/modules/balloon_alert/balloon_alert.dm
new file mode 100644
index 0000000000..d6f17d6ffe
--- /dev/null
+++ b/code/modules/balloon_alert/balloon_alert.dm
@@ -0,0 +1,89 @@
+#define BALLOON_TEXT_WIDTH 200
+#define BALLOON_TEXT_SPAWN_TIME (0.2 SECONDS)
+#define BALLOON_TEXT_FADE_TIME (0.1 SECONDS)
+#define BALLOON_TEXT_FULLY_VISIBLE_TIME (0.7 SECONDS)
+#define BALLOON_TEXT_TOTAL_LIFETIME(mult) (BALLOON_TEXT_SPAWN_TIME + BALLOON_TEXT_FULLY_VISIBLE_TIME*mult + BALLOON_TEXT_FADE_TIME)
+/// The increase in duration per character in seconds
+#define BALLOON_TEXT_CHAR_LIFETIME_INCREASE_MULT (0.05)
+/// The amount of characters needed before this increase takes into effect
+#define BALLOON_TEXT_CHAR_LIFETIME_INCREASE_MIN 10
+
+/// Creates text that will float from the atom upwards to the viewer.
+/atom/proc/balloon_alert(mob/viewer, text)
+ SHOULD_NOT_SLEEP(TRUE)
+
+ INVOKE_ASYNC(src, .proc/balloon_alert_perform, viewer, text)
+
+/// Create balloon alerts (text that floats up) to everything within range.
+/// Will only display to people who can see.
+/atom/proc/balloon_alert_to_viewers(message, self_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs)
+ SHOULD_NOT_SLEEP(TRUE)
+
+ var/list/hearers = get_hearers_in_view(vision_distance, src)
+ hearers -= ignored_mobs
+
+ for (var/mob/hearer in hearers)
+ if (hearer.is_blind())
+ continue
+
+ balloon_alert(hearer, (hearer == src && self_message) || message)
+
+// Do not use.
+// MeasureText blocks. I have no idea for how long.
+// I would've made the maptext_height update on its own, but I don't know
+// if this would look bad on laggy clients.
+/atom/proc/balloon_alert_perform(mob/viewer, text)
+ var/client/viewer_client = viewer.client
+ if (isnull(viewer_client))
+ return
+
+ var/bound_width = world.icon_size
+ if (ismovable(src))
+ var/atom/movable/movable_source = src
+ bound_width = movable_source.bound_width
+
+ var/image/balloon_alert = image(loc = get_atom_on_turf(src), layer = ABOVE_MOB_LAYER)
+ balloon_alert.plane = BALLOON_CHAT_PLANE
+ balloon_alert.alpha = 0
+ balloon_alert.maptext = MAPTEXT("[text]")
+ balloon_alert.maptext_x = (BALLOON_TEXT_WIDTH - bound_width) * -0.5
+ balloon_alert.maptext_height = WXH_TO_HEIGHT(viewer_client?.MeasureText(text, null, BALLOON_TEXT_WIDTH))
+ balloon_alert.maptext_width = BALLOON_TEXT_WIDTH
+
+ viewer_client?.images += balloon_alert
+
+ var/duration_mult = 1
+ var/duration_length = length(text) - BALLOON_TEXT_CHAR_LIFETIME_INCREASE_MIN
+
+ if(duration_length > 0)
+ duration_mult += duration_length*BALLOON_TEXT_CHAR_LIFETIME_INCREASE_MULT
+
+ animate(
+ balloon_alert,
+ pixel_y = world.icon_size * 1.2,
+ time = BALLOON_TEXT_TOTAL_LIFETIME(1),
+ easing = SINE_EASING | EASE_OUT,
+ )
+
+ animate(
+ alpha = 255,
+ time = BALLOON_TEXT_SPAWN_TIME,
+ easing = CUBIC_EASING | EASE_OUT,
+ flags = ANIMATION_PARALLEL,
+ )
+
+ animate(
+ alpha = 0,
+ time = BALLOON_TEXT_FULLY_VISIBLE_TIME*duration_mult,
+ easing = CUBIC_EASING | EASE_IN,
+ )
+
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/remove_image_from_client, balloon_alert, viewer_client), BALLOON_TEXT_TOTAL_LIFETIME(duration_mult))
+
+#undef BALLOON_TEXT_CHAR_LIFETIME_INCREASE_MIN
+#undef BALLOON_TEXT_CHAR_LIFETIME_INCREASE_MULT
+#undef BALLOON_TEXT_FADE_TIME
+#undef BALLOON_TEXT_FULLY_VISIBLE_TIME
+#undef BALLOON_TEXT_SPAWN_TIME
+#undef BALLOON_TEXT_TOTAL_LIFETIME
+#undef BALLOON_TEXT_WIDTH
diff --git a/code/modules/cargo/packs/vending.dm b/code/modules/cargo/packs/vending.dm
index 6d978d629f..f0f89a49e6 100644
--- a/code/modules/cargo/packs/vending.dm
+++ b/code/modules/cargo/packs/vending.dm
@@ -52,7 +52,7 @@
desc = "Packs of tools waiting to be used for repairing. Contains a tool and engineering vending machine refill. Requires CE access."
cost = 5500 //Powerfull
access = ACCESS_CE
- contains = list(/obj/item/vending_refill/tool,
+ contains = list(/obj/item/vending_refill/youtool,
/obj/item/vending_refill/engivend)
crate_name = "engineering supply crate"
crate_type = /obj/structure/closet/crate/secure/engineering
diff --git a/code/modules/holiday/halloween/jacqueen.dm b/code/modules/holiday/halloween/jacqueen.dm
index 49cb3ff630..e3c4ca44f6 100644
--- a/code/modules/holiday/halloween/jacqueen.dm
+++ b/code/modules/holiday/halloween/jacqueen.dm
@@ -141,7 +141,7 @@
pop_areas += A
var/list/targets = list()
- for(var/H in GLOB.network_holopads)
+ for(var/H in GLOB.the_station_areas)
var/area/A = get_area(H)
if(!A || findtextEx(A.name, "AI") || !(A in pop_areas) || !is_station_level(H))
continue
diff --git a/code/modules/integrated_electronics/core/detailer.dm b/code/modules/integrated_electronics/core/detailer.dm
index 387ec22e94..40b78227e8 100644
--- a/code/modules/integrated_electronics/core/detailer.dm
+++ b/code/modules/integrated_electronics/core/detailer.dm
@@ -26,7 +26,7 @@
"light blue" = COLOR_ASSEMBLY_LBLUE,
"blue" = COLOR_ASSEMBLY_BLUE,
"purple" = COLOR_ASSEMBLY_PURPLE,
- "pink" = COLOR_ASSEMBLY_PINK,
+ "pink" = LIGHT_COLOR_PINK,
"custom" = COLOR_ASSEMBLY_WHITE
)
diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm
index 3cfd342122..bf885526c5 100644
--- a/code/modules/modular_computers/computers/item/computer.dm
+++ b/code/modules/modular_computers/computers/item/computer.dm
@@ -12,13 +12,13 @@
rad_flags = RAD_PROTECT_CONTENTS
armor = list("melee" = 0, "bullet" = 20, "laser" = 20, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 0, "acid" = 0)
- var/enabled = 0 // Whether the computer is turned on.
- var/screen_on = 1 // Whether the computer is active/opened/it's screen is on.
- var/device_theme = "ntos" // Sets the theme for the main menu, hardware config, and file browser apps. Overridden by certain non-NT devices.
- var/datum/computer_file/program/active_program = null // A currently active program running on the computer.
- var/hardware_flag = 0 // A flag that describes this device type
+ var/enabled = 0 // Whether the computer is turned on.
+ var/screen_on = 1 // Whether the computer is active/opened/it's screen is on.
+ var/device_theme = "ntos" // Sets the theme for the main menu, hardware config, and file browser apps. Overridden by certain non-NT devices.
+ var/datum/computer_file/program/active_program = null // A currently active program running on the computer.
+ var/hardware_flag = 0 // A flag that describes this device type
var/last_power_usage = 0
- var/last_battery_percent = 0 // Used for deciding if battery percentage has chandged
+ var/last_battery_percent = 0 // Used for deciding if battery percentage has chandged
var/last_world_time = "00:00"
var/list/last_header_icons
///Looping sound for when the computer is on
@@ -26,19 +26,19 @@
///Whether or not this modular computer uses the looping sound
var/looping_sound = TRUE
- var/base_active_power_usage = 50 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
- var/base_idle_power_usage = 5 // Power usage when the computer is idle and screen is off (currently only applies to laptops)
+ var/base_active_power_usage = 50 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
+ var/base_idle_power_usage = 5 // Power usage when the computer is idle and screen is off (currently only applies to laptops)
// Modular computers can run on various devices. Each DEVICE (Laptop, Console, Tablet,..)
// must have it's own DMI file. Icon states must be called exactly the same in all files, but may look differently
// If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example.
- var/icon_state_unpowered = null // Icon state when the computer is turned off.
- var/icon_state_powered = null // Icon state when the computer is turned on.
- var/icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
- var/display_overlays = TRUE // If FALSE, don't draw overlays on this device at all
- var/max_hardware_size = 0 // Maximal hardware w_class. Tablets/PDAs have 1, laptops 2, consoles 4.
- var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer.
+ var/icon_state_unpowered = null // Icon state when the computer is turned off.
+ var/icon_state_powered = null // Icon state when the computer is turned on.
+ var/icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
+ var/display_overlays = TRUE // If FALSE, don't draw overlays on this device at all
+ var/max_hardware_size = 0 // Maximal hardware w_class. Tablets/PDAs have 1, laptops 2, consoles 4.
+ var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer.
/// List of "connection ports" in this computer and the components with which they are plugged
var/list/all_components = list()
@@ -47,11 +47,11 @@
/// Number of total expansion bays this computer has available.
var/max_bays = 0
- var/list/idle_threads // Idle programs on background. They still receive process calls but can't be interacted with.
- var/obj/physical = null // Object that represents our computer. It's used for Adjacent() and UI visibility checks.
- var/has_light = FALSE //If the computer has a flashlight/LED light/what-have-you installed
- var/comp_light_luminosity = 3 //The brightness of that light
- var/comp_light_color //The color of that light
+ var/list/idle_threads // Idle programs on background. They still receive process calls but can't be interacted with.
+ var/obj/physical = null // Object that represents our computer. It's used for Adjacent() and UI visibility checks.
+ var/has_light = FALSE //If the computer has a flashlight/LED light/what-have-you installed
+ var/comp_light_luminosity = 3 //The brightness of that light
+ var/comp_light_color //The color of that light
/obj/item/modular_computer/Initialize()
@@ -62,13 +62,12 @@
comp_light_color = "#FFFFFF"
idle_threads = list()
if(looping_sound)
- soundloop = new(list(src), enabled)
- update_icon()
+ soundloop = new(src, enabled)
+ update_appearance()
/obj/item/modular_computer/Destroy()
kill_program(forced = TRUE)
STOP_PROCESSING(SSobj, src)
- QDEL_NULL(soundloop)
for(var/H in all_components)
var/obj/item/computer_hardware/CH = all_components[H]
if(CH.holder == src)
@@ -76,9 +75,19 @@
CH.holder = null
all_components.Remove(CH.device_type)
qdel(CH)
+ //Some components will actually try and interact with this, so let's do it later
+ QDEL_NULL(soundloop)
physical = null
return ..()
+/**
+ * Plays a ping sound.
+ *
+ * Timers runtime if you try to make them call playsound. Yep.
+ */
+/obj/item/modular_computer/proc/play_ping()
+ playsound(loc, 'sound/machines/ping.ogg', get_clamped_volume(), FALSE, -1)
+
/obj/item/modular_computer/AltClick(mob/user)
..()
if(issilicon(user))
@@ -98,14 +107,34 @@
/obj/item/modular_computer/GetID()
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
- if(card_slot)
- return card_slot.GetID()
+ var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
+
+ var/obj/item/card/id/first_id = card_slot?.GetID()
+ var/obj/item/card/id/second_id = card_slot2?.GetID()
+
+ // If we don't have both ID slots filled, pick the one that is filled.
+ if(first_id)
+ return first_id
+ if(second_id)
+ return second_id
+
+ // Otherwise, we have no ID at all.
return ..()
/obj/item/modular_computer/RemoveID()
var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
- return (card_slot2?.try_eject() || card_slot?.try_eject()) //Try the secondary one first.
+
+ var/removed_id = (card_slot2?.try_eject() || card_slot?.try_eject())
+ if(removed_id)
+ if(ishuman(loc))
+ var/mob/living/carbon/human/human_wearer = loc
+ if(human_wearer.wear_id == src)
+ human_wearer.sec_hud_set_ID()
+ update_slot_icon()
+ return removed_id
+
+ return ..()
/obj/item/modular_computer/InsertID(obj/item/inserting_item)
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
@@ -118,7 +147,13 @@
return FALSE
if((card_slot?.try_insert(inserting_id)) || (card_slot2?.try_insert(inserting_id)))
+ if(ishuman(loc))
+ var/mob/living/carbon/human/human_wearer = loc
+ if(human_wearer.wear_id == src)
+ human_wearer.sec_hud_set_ID()
+ update_slot_icon()
return TRUE
+
return FALSE
/obj/item/modular_computer/MouseDrop(obj/over_object, src_location, over_location)
@@ -142,9 +177,8 @@
turn_on(user)
/obj/item/modular_computer/emag_act(mob/user)
- . = ..()
if(!enabled)
- to_chat(user, "You'd need to turn the [src] on first.")
+ to_chat(user, span_warning("You'd need to turn the [src] on first."))
return FALSE
obj_flags |= EMAGGED //Mostly for consistancy purposes; the programs will do their own emag handling
var/newemag = FALSE
@@ -155,36 +189,31 @@
if(app.run_emag())
newemag = TRUE
if(newemag)
- to_chat(user, "You swipe \the [src]. A console window momentarily fills the screen, with white text rapidly scrolling past.")
+ to_chat(user, span_notice("You swipe \the [src]. A console window momentarily fills the screen, with white text rapidly scrolling past."))
return TRUE
- to_chat(user, "You swipe \the [src]. A console window fills the screen, but it quickly closes itself after only a few lines are written to it.")
+ to_chat(user, span_notice("You swipe \the [src]. A console window fills the screen, but it quickly closes itself after only a few lines are written to it."))
return FALSE
/obj/item/modular_computer/examine(mob/user)
. = ..()
if(obj_integrity <= integrity_failure * max_integrity)
- . += "It is heavily damaged!"
+ . += span_danger("It is heavily damaged!")
else if(obj_integrity < max_integrity)
- . += "It is damaged."
+ . += span_warning("It is damaged.")
. += get_modular_computer_parts_examine(user)
/obj/item/modular_computer/update_icon_state()
- if(!enabled)
- icon_state = icon_state_unpowered
- else
- icon_state = icon_state_powered
+ icon_state = enabled ? icon_state_powered : icon_state_unpowered
+ return ..()
/obj/item/modular_computer/update_overlays()
. = ..()
if(!display_overlays)
return
- if(enabled)
- if(active_program)
- . += active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu
- else
- . += icon_state_menu
+ if(enabled)
+ . += active_program?.program_icon_state || icon_state_menu
if(obj_integrity <= integrity_failure * max_integrity)
. += "bsod"
. += "broken"
@@ -201,9 +230,9 @@
var/issynth = issilicon(user) // Robots and AIs get different activation messages.
if(obj_integrity <= integrity_failure * max_integrity)
if(issynth)
- to_chat(user, "You send an activation signal to \the [src], but it responds with an error code. It must be damaged.")
+ to_chat(user, span_warning("You send an activation signal to \the [src], but it responds with an error code. It must be damaged."))
else
- to_chat(user, "You press the power button, but the computer fails to boot up, displaying variety of errors before shutting down again.")
+ to_chat(user, span_warning("You press the power button, but the computer fails to boot up, displaying variety of errors before shutting down again."))
return FALSE
// If we have a recharger, enable it automatically. Lets computer without a battery work.
@@ -213,20 +242,20 @@
if(all_components[MC_CPU] && use_power()) // use_power() checks if the PC is powered
if(issynth)
- to_chat(user, "You send an activation signal to \the [src], turning it on.")
+ to_chat(user, span_notice("You send an activation signal to \the [src], turning it on."))
else
- to_chat(user, "You press the power button and start up \the [src].")
+ to_chat(user, span_notice("You press the power button and start up \the [src]."))
if(looping_sound)
soundloop.start()
enabled = 1
- update_icon()
+ update_appearance()
ui_interact(user)
return TRUE
else // Unpowered
if(issynth)
- to_chat(user, "You send an activation signal to \the [src] but it does not respond.")
+ to_chat(user, span_warning("You send an activation signal to \the [src] but it does not respond."))
else
- to_chat(user, "You press the power button but \the [src] does not respond.")
+ to_chat(user, span_warning("You press the power button but \the [src] does not respond."))
return FALSE
// Process currently calls handle_power(), may be expanded in future if more things are added.
@@ -282,10 +311,10 @@
if(!caller || !caller.alert_able || caller.alert_silenced || !alerttext) //Yeah, we're checking alert_able. No, you don't get to make alerts that the user can't silence.
return
playsound(src, sound, 50, TRUE)
- visible_message("The [src] displays a [caller.filedesc] notification: [alerttext]")
+ visible_message(span_notice("The [src] displays a [caller.filedesc] notification: [alerttext]"))
var/mob/living/holder = loc
if(istype(holder))
- to_chat(holder, "[icon2html(src)] The [src] displays a [caller.filedesc] notification: [alerttext]")
+ to_chat(holder, "[icon2html(src)] [span_notice("The [src] displays a [caller.filedesc] notification: [alerttext]")]")
// Function used by NanoUI's to obtain data for header. All relevant entries begin with "PC_"
/obj/item/modular_computer/proc/get_header_data()
@@ -349,14 +378,13 @@
// Relays kill program request to currently active program. Use this to quit current program.
/obj/item/modular_computer/proc/kill_program(forced = FALSE)
- set waitfor = FALSE
if(active_program)
active_program.kill_program(forced)
active_program = null
var/mob/user = usr
if(user && istype(user))
ui_interact(user) // Re-open the UI on this computer. It should show the main screen now.
- update_icon()
+ update_appearance()
// Returns 0 for No Signal, 1 for Low Signal and 2 for Good Signal. 3 is for wired connection (always-on)
/obj/item/modular_computer/proc/get_ntnet_status(specific_action = 0)
@@ -370,6 +398,7 @@
if(!get_ntnet_status())
return FALSE
var/obj/item/computer_hardware/network_card/network_card = all_components[MC_NET]
+
return SSnetworks.station_network.add_log(text, network_card)
/obj/item/modular_computer/proc/shutdown_computer(loud = 1)
@@ -380,9 +409,9 @@
if(looping_sound)
soundloop.stop()
if(loud)
- physical.visible_message("\The [src] shuts down.")
+ physical.visible_message(span_notice("\The [src] shuts down."))
enabled = 0
- update_icon()
+ update_appearance()
/**
* Toggles the computer's flashlight, if it has one.
@@ -412,20 +441,20 @@
if(!has_light || !color)
return FALSE
comp_light_color = color
- // set_light_color(color)
+ set_light_color(color)
update_light()
return TRUE
/obj/item/modular_computer/screwdriver_act(mob/user, obj/item/tool)
if(!all_components.len)
- to_chat(user, "This device doesn't have any components installed.")
+ to_chat(user, span_warning("This device doesn't have any components installed."))
return
var/list/component_names = list()
for(var/h in all_components)
var/obj/item/computer_hardware/H = all_components[h]
component_names.Add(H.name)
- var/choice = tgui_input_list(user, "Which component do you want to uninstall?", "Computer maintenance", sortList(component_names))
+ var/choice = input(user, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in sortList(component_names)
if(!choice)
return
@@ -460,28 +489,34 @@
if(W.tool_behaviour == TOOL_WRENCH)
if(all_components.len)
- to_chat(user, "Remove all components from \the [src] before disassembling it.")
+ to_chat(user, span_warning("Remove all components from \the [src] before disassembling it."))
return
new /obj/item/stack/sheet/metal( get_turf(src.loc), steel_sheet_cost )
- physical.visible_message("\The [src] is disassembled by [user].")
+ physical.visible_message(span_notice("\The [src] is disassembled by [user]."))
relay_qdel()
qdel(src)
return
if(W.tool_behaviour == TOOL_WELDER)
if(obj_integrity == max_integrity)
- to_chat(user, "\The [src] does not require repairs.")
+ to_chat(user, span_warning("\The [src] does not require repairs."))
return
if(!W.tool_start_check(user, amount=1))
return
- to_chat(user, "You begin repairing damage to \the [src]...")
+ to_chat(user, span_notice("You begin repairing damage to \the [src]..."))
if(W.use_tool(src, user, 20, volume=50, amount=1))
obj_integrity = max_integrity
- to_chat(user, "You repair \the [src].")
+ to_chat(user, span_notice("You repair \the [src]."))
return
+ var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
+ // Check to see if we have an ID inside, and a valid input for money
+ if(card_slot?.GetID() && iscash(W))
+ var/obj/item/card/id/id = card_slot.GetID()
+ id.attackby(W, user) // If we do, try and put that attacking object in
+ return
..()
// Used by processor to relay qdel() to machinery type.
diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm
index 9695c0b0a5..93324bffe8 100644
--- a/code/modules/modular_computers/computers/item/computer_ui.dm
+++ b/code/modules/modular_computers/computers/item/computer_ui.dm
@@ -118,12 +118,6 @@
update_appearance()
if(user && istype(user))
ui_interact(user) // Re-open the UI on this computer. It should show the main screen now.
- if("eject_pen")
- if(istype(src, /obj/item/modular_computer/tablet))
- var/obj/item/modular_computer/tablet/self = src
- if(self.can_have_pen)
- self.remove_pen()
- return
if("PC_killprogram")
var/prog = params["name"]
diff --git a/code/modules/modular_computers/file_system/programs/crewmanifest.dm b/code/modules/modular_computers/file_system/programs/crewmanifest.dm
index bc6923773e..6837f315a4 100644
--- a/code/modules/modular_computers/file_system/programs/crewmanifest.dm
+++ b/code/modules/modular_computers/file_system/programs/crewmanifest.dm
@@ -12,7 +12,7 @@
/datum/computer_file/program/crew_manifest/ui_static_data(mob/user)
var/list/data = list()
- data["manifest"] = GLOB.data_core.get_manifest()
+ data["manifest"] = GLOB.data_core.get_manifest_tg()
return data
/datum/computer_file/program/crew_manifest/ui_data(mob/user)
diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
index a15016ac0e..29f9a37433 100644
--- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm
+++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
@@ -18,6 +18,7 @@
var/list/blacklisted = list(
"AI",
"Assistant",
+ "Prisoner",
"Cyborg",
"Captain",
"Head of Personnel",
@@ -98,7 +99,7 @@
if("PRG_priority")
var/priority_target = params["target"]
var/datum/job/j = SSjob.GetJob(priority_target)
- if(!j || (job?.title in blacklisted))
+ if(!j || (j?.title in blacklisted))
return
if(j.total_positions <= j.current_positions)
return
diff --git a/code/modules/modular_computers/file_system/programs/portrait_printer.dm b/code/modules/modular_computers/file_system/programs/portrait_printer.dm
index 12a2187550..ac3b7e5a88 100644
--- a/code/modules/modular_computers/file_system/programs/portrait_printer.dm
+++ b/code/modules/modular_computers/file_system/programs/portrait_printer.dm
@@ -25,7 +25,7 @@
var/list/data = list()
data["library"] = SSpersistence.paintings["library"] ? SSpersistence.paintings["library"] : 0
data["library_secure"] = SSpersistence.paintings["library_secure"] ? SSpersistence.paintings["library_secure"] : 0
- data["library_private"] = SSpersistence.paintings["library_private"] ? SSpersistence.paintings["library_private"] : 0 //i'm gonna regret this, won't i?
+ data["library_private"] = SSpersistence.paintings["library_private"] ? SSpersistence.paintings["library_private"] : 0 //i'm gonna regret this, won't i? Yes you should.
return data
/datum/computer_file/program/portrait_printer/ui_assets(mob/user)
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index 7a98287809..366d1a3f68 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -5,7 +5,7 @@
* lipstick wiping is in code/game/objects/items/weapons/cosmetics.dm!
*/
#define MAX_PAPER_LENGTH 5000
-#define MAX_PAPER_STAMPS 30 // Too low?
+#define MAX_PAPER_STAMPS 30 // Too low?
#define MAX_PAPER_STAMPS_OVERLAYS 4
#define MODE_READING 0
#define MODE_WRITING 1
@@ -22,6 +22,8 @@
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "paper"
item_state = "paper"
+ // worn_icon_state = "paper"
+ // custom_fire_overlay = "paper_onfire_overlay"
throwforce = 0
w_class = WEIGHT_CLASS_TINY
throw_range = 1
@@ -41,8 +43,8 @@
var/show_written_words = TRUE
/// The (text for the) stamps on the paper.
- var/list/stamps /// Positioning for the stamp in tgui
- var/list/stamped /// Overlay info
+ var/list/stamps /// Positioning for the stamp in tgui
+ var/list/stamped /// Overlay info
var/contact_poison // Reagent ID to transfer on contact
var/contact_poison_volume = 0
@@ -61,20 +63,23 @@
/**
* This proc copies this sheet of paper to a new
- * sheet, Makes it nice and easy for carbon and
- * the copyer machine
+ * sheet. Used by carbon papers and the photocopier machine.
*/
-/obj/item/paper/proc/copy()
- var/obj/item/paper/N = new(arglist(args))
- N.info = info
- N.color = color
- N.update_icon_state()
- N.stamps = stamps
- N.stamped = stamped.Copy()
- N.form_fields = form_fields.Copy()
- N.field_counter = field_counter
- copy_overlays(N, TRUE)
- return N
+/obj/item/paper/proc/copy(paper_type = /obj/item/paper, atom/location = loc, colored = TRUE)
+ var/obj/item/paper/new_paper = new paper_type (location)
+ if(colored)
+ new_paper.color = color
+ new_paper.info = info
+ else //This basically just breaks the existing color tag, which we need to do because the innermost tag takes priority.
+ var/static/greyscale_info = regex("You cut yourself on the paper! Ahhhh! Ahhhhh!")
+ to_chat(H, span_warning("You cut yourself on the paper! Ahhhh! Ahhhhh!"))
H.damageoverlaytemp = 9001
H.update_damage_hud()
return
var/n_name = stripped_input(usr, "What would you like to label the paper?", "Paper Labelling", null, MAX_NAME_LEN)
- if((loc == usr && usr.stat == CONSCIOUS))
+ if(((loc == usr || istype(loc, /obj/item/clipboard)) && usr.stat == CONSCIOUS))
name = "paper[(n_name ? text("- '[n_name]'") : null)]"
add_fingerprint(usr)
/obj/item/paper/suicide_act(mob/user)
- user.visible_message("[user] scratches a grid on [user.p_their()] wrist with the paper! It looks like [user.p_theyre()] trying to commit sudoku...")
+ user.visible_message(span_suicide("[user] scratches a grid on [user.p_their()] wrist with the paper! It looks like [user.p_theyre()] trying to commit sudoku..."))
return (BRUTELOSS)
/obj/item/paper/proc/clearpaper()
@@ -139,18 +145,18 @@
/obj/item/paper/examine(mob/user)
. = ..()
if(!in_range(user, src) && !isobserver(user))
- . += "You're too far away to read it!"
+ . += span_warning("You're too far away to read it!")
return
if(user.can_read(src))
ui_interact(user)
return
- . += "You cannot read it!"
+ . += span_warning("You cannot read it!")
/obj/item/paper/ui_status(mob/user,/datum/ui_state/state)
// Are we on fire? Hard ot read if so
if(resistance_flags & ON_FIRE)
return UI_CLOSE
- if(!in_range(user,src))
+ if(!in_range(user, src) && !isobserver(user))
return UI_CLOSE
if(user.incapacitated(TRUE, TRUE) || (isobserver(user) && !IsAdminGhost(user)))
return UI_UPDATE
@@ -158,7 +164,7 @@
// .. or if you cannot read
if(!user.can_read(src))
return UI_CLOSE
- if(in_contents_of(/obj/machinery/door/airlock))
+ if(in_contents_of(/obj/machinery/door/airlock) || in_contents_of(/obj/item/clipboard))
return UI_INTERACTIVE
return ..()
@@ -176,8 +182,8 @@
return
. = TRUE
if(!bypass_clumsy && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(10) && Adjacent(user))
- user.visible_message("[user] accidentally ignites [user.p_them()]self!", \
- "You miss [src] and accidentally light yourself on fire!")
+ user.visible_message(span_warning("[user] accidentally ignites [user.p_them()]self!"), \
+ span_userdanger("You miss [src] and accidentally light yourself on fire!"))
if(user.is_holding(I)) //checking if they're holding it in case TK is involved
user.dropItemToGround(I)
user.adjust_fire_stacks(1)
@@ -195,19 +201,23 @@
SStgui.close_uis(src)
return
- if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon))
+ // Enable picking paper up by clicking on it with the clipboard or folder
+ if(istype(P, /obj/item/clipboard) || istype(P, /obj/item/folder))
+ P.attackby(src, user)
+ return
+ else if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon))
if(length(info) >= MAX_PAPER_LENGTH) // Sheet must have less than 1000 charaters
- to_chat(user, "This sheet of paper is full!")
+ to_chat(user, span_warning("This sheet of paper is full!"))
return
ui_interact(user)
return
else if(istype(P, /obj/item/stamp))
- to_chat(user, "You ready your stamp over the paper! ")
+ to_chat(user, span_notice("You ready your stamp over the paper! "))
ui_interact(user)
return /// Normaly you just stamp, you don't need to read the thing
else
// cut paper? the sky is the limit!
- ui_interact(user) // The other ui will be created with just read mode outside of this
+ ui_interact(user) // The other ui will be created with just read mode outside of this
return ..()
@@ -234,8 +244,8 @@
. = list()
.["text"] = info
.["max_length"] = MAX_PAPER_LENGTH
- .["paper_color"] = !color || color == "white" ? "#FFFFFF" : color // color might not be set
- .["paper_state"] = icon_state /// TODO: show the sheet will bloodied or crinkling?
+ .["paper_color"] = !color || color == "white" ? "#FFFFFF" : color // color might not be set
+ .["paper_state"] = icon_state /// TODO: show the sheet will bloodied or crinkling?
.["stamps"] = stamps
@@ -244,27 +254,34 @@
var/list/data = list()
data["edit_usr"] = "[user]"
- var/obj/O = user.get_active_held_item()
- if(istype(O, /obj/item/toy/crayon))
- var/obj/item/toy/crayon/PEN = O
+ var/obj/holding = user.get_active_held_item()
+ // Use a clipboard's pen, if applicable
+ if(istype(loc, /obj/item/clipboard))
+ var/obj/item/clipboard/clipboard = loc
+ // This is just so you can still use a stamp if you're holding one. Otherwise, it'll
+ // use the clipboard's pen, if applicable.
+ if(!istype(holding, /obj/item/stamp) && clipboard.pen)
+ holding = clipboard.pen
+ if(istype(holding, /obj/item/toy/crayon))
+ var/obj/item/toy/crayon/PEN = holding
data["pen_font"] = CRAYON_FONT
data["pen_color"] = PEN.paint_color
data["edit_mode"] = MODE_WRITING
data["is_crayon"] = TRUE
data["stamp_class"] = "FAKE"
data["stamp_icon_state"] = "FAKE"
- else if(istype(O, /obj/item/pen))
- var/obj/item/pen/PEN = O
+ else if(istype(holding, /obj/item/pen))
+ var/obj/item/pen/PEN = holding
data["pen_font"] = PEN.font
data["pen_color"] = PEN.colour
data["edit_mode"] = MODE_WRITING
data["is_crayon"] = FALSE
data["stamp_class"] = "FAKE"
data["stamp_icon_state"] = "FAKE"
- else if(istype(O, /obj/item/stamp))
+ else if(istype(holding, /obj/item/stamp))
var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/simple/paper)
- data["stamp_icon_state"] = O.icon_state
- data["stamp_class"] = sheet.icon_class_name(O.icon_state)
+ data["stamp_icon_state"] = holding.icon_state
+ data["stamp_class"] = sheet.icon_class_name(holding.icon_state)
data["edit_mode"] = MODE_STAMPING
data["pen_font"] = "FAKE"
data["pen_color"] = "FAKE"
@@ -276,6 +293,10 @@
data["is_crayon"] = FALSE
data["stamp_icon_state"] = "FAKE"
data["stamp_class"] = "FAKE"
+ if(istype(loc, /obj/structure/noticeboard))
+ var/obj/structure/noticeboard/noticeboard = loc
+ if(!noticeboard.allowed(user))
+ data["edit_mode"] = MODE_READING
data["field_counter"] = field_counter
data["form_fields"] = form_fields
@@ -289,14 +310,14 @@
if("stamp")
var/stamp_x = text2num(params["x"])
var/stamp_y = text2num(params["y"])
- var/stamp_r = text2num(params["r"]) // rotation in degrees
+ var/stamp_r = text2num(params["r"]) // rotation in degrees
var/stamp_icon_state = params["stamp_icon_state"]
var/stamp_class = params["stamp_class"]
if (isnull(stamps))
stamps = list()
if(stamps.len < MAX_PAPER_STAMPS)
// I hate byond when dealing with freaking lists
- stamps[++stamps.len] = list(stamp_class, stamp_x, stamp_y, stamp_r) /// WHHHHY
+ stamps[++stamps.len] = list(stamp_class, stamp_x, stamp_y, stamp_r) /// WHHHHY
/// This does the overlay stuff
if (isnull(stamped))
@@ -307,10 +328,11 @@
stampoverlay.pixel_y = rand(-3, 2)
add_overlay(stampoverlay)
LAZYADD(stamped, stamp_icon_state)
+ update_icon()
update_static_data(usr,ui)
var/obj/O = ui.user.get_active_held_item()
- ui.user.visible_message("[ui.user] stamps [src] with \the [O.name]!", "You stamp [src] with \the [O.name]!")
+ ui.user.visible_message(span_notice("[ui.user] stamps [src] with \the [O.name]!"), span_notice("You stamp [src] with \the [O.name]!"))
else
to_chat(usr, pick("You try to stamp but you miss!", "There is no where else you can stamp!"))
. = TRUE
@@ -336,9 +358,14 @@
update_static_data(usr,ui)
- update_icon()
+ update_appearance()
. = TRUE
+/obj/item/paper/ui_host(mob/user)
+ if(istype(loc, /obj/structure/noticeboard))
+ return loc
+ return ..()
+
/**
* Construction paper
*/
@@ -361,9 +388,6 @@
slot_flags = null
show_written_words = FALSE
-/obj/item/paper/crumpled/update_icon_state()
- return
-
/obj/item/paper/crumpled/bloody
icon_state = "scrap_bloodied"
diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm
index 0ebbce53cf..2f6a76469a 100644
--- a/code/modules/power/singularity/narsie.dm
+++ b/code/modules/power/singularity/narsie.dm
@@ -62,7 +62,7 @@
for(var/datum/mind/cult_mind in SSticker.mode.cult)
if(isliving(cult_mind.current))
var/mob/living/L = cult_mind.current
- L.narsie_act()
+ INVOKE_ASYNC(L, /atom.proc/narsie_act)
for(var/mob/living/player in GLOB.player_list)
if(player.stat != DEAD && player.loc && is_station_level(player.loc.z) && !iscultist(player) && !isanimal(player))
souls_needed[player] = TRUE
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index 7f1d23e43c..8da3433381 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -229,6 +229,57 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
if (!istype(C.glasses, /obj/item/clothing/glasses/meson) && (get_dist(user, src) < HALLUCINATION_RANGE(power)))
. += "You get headaches just from looking at it."
+// SupermatterMonitor UI for ghosts only. Inherited attack_ghost will call this.
+/obj/machinery/power/supermatter_crystal/ui_interact(mob/user, datum/tgui/ui)
+ if(!isobserver(user))
+ return FALSE
+ . = ..()
+ ui = SStgui.try_update_ui(user, src, ui)
+ if (!ui)
+ ui = new(user, src, "SupermatterMonitor")
+ ui.open()
+
+/obj/machinery/power/supermatter_crystal/ui_data(mob/user)
+ var/list/data = list()
+
+ var/turf/local_turf = get_turf(src)
+
+ var/datum/gas_mixture/air = local_turf.return_air()
+
+ // singlecrystal set to true eliminates the back sign on the gases breakdown.
+ data["singlecrystal"] = TRUE
+ data["active"] = TRUE
+ data["SM_integrity"] = get_integrity()
+ data["SM_power"] = power
+ data["SM_ambienttemp"] = air.return_temperature()
+ data["SM_ambientpressure"] = air.return_pressure()
+ data["SM_bad_moles_amount"] = MOLE_PENALTY_THRESHOLD / gasefficency
+ data["SM_moles"] = 0
+ data["SM_uid"] = uid
+ var/area/active_supermatter_area = get_area(src)
+ data["SM_area_name"] = active_supermatter_area.name
+
+ var/list/gasdata = list()
+
+ if(air.total_moles())
+ data["SM_moles"] = air.total_moles()
+ for(var/id in air.get_gases())
+ var/gas_level = air.get_moles(id)/air.total_moles()
+ if(gas_level > 0)
+ gasdata.Add(list(list(
+ "name"= "[GLOB.gas_data.names[id]]",
+ "amount" = round(gas_level*100, 0.01))))
+
+ else
+ for(var/id in air.get_gases())
+ gasdata.Add(list(list(
+ "name"= "[GLOB.gas_data.names[id]]",
+ "amount" = 0)))
+
+ data["gases"] = gasdata
+
+ return data
+
/obj/machinery/power/supermatter_crystal/proc/get_status()
var/turf/T = get_turf(src)
if(!T)
diff --git a/code/modules/uplink/uplink_devices.dm b/code/modules/uplink/uplink_devices.dm
index 9c09a7334a..9660718bb6 100644
--- a/code/modules/uplink/uplink_devices.dm
+++ b/code/modules/uplink/uplink_devices.dm
@@ -19,25 +19,46 @@
throw_range = 7
w_class = WEIGHT_CLASS_SMALL
+ /// The uplink flag for this type.
+ /// See [`code/__DEFINES/uplink.dm`]
+ var/uplink_flag = UPLINK_TRAITORS
+
/obj/item/uplink/Initialize(mapload, owner, tc_amount = 20)
. = ..()
- AddComponent(/datum/component/uplink, owner, FALSE, TRUE, null, tc_amount)
+ AddComponent(/datum/component/uplink, owner, FALSE, TRUE, uplink_flag, tc_amount)
-/obj/item/uplink/nuclear/Initialize()
+/obj/item/uplink/debug
+ name = "debug uplink"
+
+/obj/item/uplink/debug/Initialize(mapload, owner, tc_amount = 9000)
. = ..()
var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink)
- hidden_uplink.set_gamemode(/datum/game_mode/nuclear)
+ hidden_uplink.name = "debug uplink"
+ hidden_uplink.debug = TRUE
+
+/obj/item/uplink/nuclear
+ uplink_flag = UPLINK_NUKE_OPS
+
+/obj/item/uplink/nuclear/debug
+ name = "debug nuclear uplink"
+ uplink_flag = UPLINK_NUKE_OPS
+
+/obj/item/uplink/nuclear/debug/Initialize(mapload, owner, tc_amount = 9000)
+ . = ..()
+ var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink)
+ hidden_uplink.name = "debug nuclear uplink"
+ hidden_uplink.debug = TRUE
+
+/obj/item/uplink/nuclear_restricted
+ uplink_flag = UPLINK_NUKE_OPS
/obj/item/uplink/nuclear_restricted/Initialize()
. = ..()
var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink)
hidden_uplink.allow_restricted = FALSE
- hidden_uplink.set_gamemode(/datum/game_mode/nuclear)
-/obj/item/uplink/clownop/Initialize()
- . = ..()
- var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink)
- hidden_uplink.set_gamemode(/datum/game_mode/nuclear/clown_ops)
+/obj/item/uplink/clownop
+ uplink_flag = UPLINK_CLOWN_OPS
/obj/item/uplink/old
name = "dusty radio"
@@ -51,28 +72,9 @@
// Multitool uplink
/obj/item/multitool/uplink/Initialize(mapload, owner, tc_amount = 20)
. = ..()
- AddComponent(/datum/component/uplink, owner, FALSE, TRUE, null, tc_amount)
+ AddComponent(/datum/component/uplink, owner, FALSE, TRUE, UPLINK_TRAITORS, tc_amount)
// Pen uplink
/obj/item/pen/uplink/Initialize(mapload, owner, tc_amount = 20)
. = ..()
- AddComponent(/datum/component/uplink, owner, TRUE, FALSE, null, tc_amount)
-
-/obj/item/uplink/debug
- name = "debug uplink"
-
-/obj/item/uplink/debug/Initialize(mapload, owner, tc_amount = 9000)
- . = ..()
- var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink)
- hidden_uplink.name = "debug uplink"
- hidden_uplink.debug = TRUE
-
-/obj/item/uplink/nuclear/debug
- name = "debug nuclear uplink"
-
-/obj/item/uplink/nuclear/debug/Initialize(mapload, owner, tc_amount = 9000)
- . = ..()
- var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink)
- hidden_uplink.set_gamemode(/datum/game_mode/nuclear)
- hidden_uplink.name = "debug nuclear uplink"
- hidden_uplink.debug = TRUE
+ AddComponent(/datum/component/uplink, owner, TRUE, FALSE, UPLINK_TRAITORS, tc_amount)
diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm
index cc676777db..e33b675921 100644
--- a/code/modules/uplink/uplink_items.dm
+++ b/code/modules/uplink/uplink_items.dm
@@ -1,54 +1,76 @@
-/proc/get_uplink_items(datum/game_mode/gamemode, allow_sales = TRUE, allow_restricted = TRUE, other_filter = list())
- var/list/filtered_uplink_items = GLOB.uplink_categories.Copy() // list of uplink categories without associated values.
+GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
+
+/proc/get_uplink_items(uplink_flag, allow_sales = TRUE, allow_restricted = TRUE)
+ var/list/filtered_uplink_items = list()
var/list/sale_items = list()
for(var/path in GLOB.uplink_items)
var/datum/uplink_item/I = new path
- if(I.include_modes.len)
- if(!gamemode && SSticker.mode && !(SSticker.mode.type in I.include_modes))
- continue
- if(gamemode && !(gamemode in I.include_modes))
- continue
- if(I.exclude_modes.len)
- if(!gamemode && SSticker.mode && (SSticker.mode.type in I.exclude_modes))
- continue
- if(gamemode && (gamemode in I.exclude_modes))
- continue
+ if(!I.item)
+ continue
+ if (!(I.purchasable_from & uplink_flag))
+ continue
if(I.player_minimum && I.player_minimum > GLOB.joined_player_list.len)
continue
if (I.restricted && !allow_restricted)
continue
- if (I.type in other_filter)
- continue
- LAZYSET(filtered_uplink_items[I.category], I.name, I)
+ if(!filtered_uplink_items[I.category])
+ filtered_uplink_items[I.category] = list()
+ filtered_uplink_items[I.category][I.name] = I
if(I.limited_stock < 0 && !I.cant_discount && I.item && I.cost > 1)
sale_items += I
if(allow_sales)
- for(var/i in 1 to 3)
- var/datum/uplink_item/I = pick_n_take(sale_items)
- var/datum/uplink_item/A = new I.type
- var/discount = A.get_discount()
- var/list/disclaimer = list("Void where prohibited.", "Not recommended for children.", "Contains small parts.", "Check local laws for legality in region.", "Do not taunt.", "Not responsible for direct, indirect, incidental or consequential damages resulting from any defect, error or failure to perform.", "Keep away from fire or flames.", "Product is provided \"as is\" without any implied or expressed warranties.", "As seen on TV.", "For recreational use only.", "Use only as directed.", "16% sales tax will be charged for orders originating within Space Nebraska.")
- A.limited_stock = 1
- I.refundable = FALSE //THIS MAN USES ONE WEIRD TRICK TO GAIN FREE TC, CODERS HATES HIM!
- A.refundable = FALSE
- if(A.cost >= 20) //Tough love for nuke ops
- discount *= 0.5
- A.cost = max(round(A.cost * discount),1)
- A.category = "Discounted Gear"
- A.name += " ([round(((initial(A.cost)-A.cost)/initial(A.cost))*100)]% off!)"
- A.desc += " Normally costs [initial(A.cost)] TC. All sales final. [pick(disclaimer)]"
- A.item = I.item
+ var/datum/team/nuclear/nuclear_team
+ // if (uplink_flag & UPLINK_NUKE_OPS) // uplink code kind of needs a redesign
+ // nuclear_team = locate() in GLOB.antagonist_teams // the team discounts could be in a GLOB with this design but it would make sense for them to be team specific...
+ if (!nuclear_team)
+ create_uplink_sales(3, "Discounted Gear", 1, sale_items, filtered_uplink_items)
+ else
+ if (!nuclear_team.team_discounts)
+ // create 5 unlimited stock discounts
+ create_uplink_sales(5, "Discounted Team Gear", -1, sale_items, filtered_uplink_items)
+ // Create 10 limited stock discounts
+ create_uplink_sales(10, "Limited Stock Team Gear", 1, sale_items, filtered_uplink_items)
+ nuclear_team.team_discounts = list("Discounted Team Gear" = filtered_uplink_items["Discounted Team Gear"], "Limited Stock Team Gear" = filtered_uplink_items["Limited Stock Team Gear"])
+ else
+ for(var/cat in nuclear_team.team_discounts)
+ for(var/item in nuclear_team.team_discounts[cat])
+ var/datum/uplink_item/D = nuclear_team.team_discounts[cat][item]
+ var/datum/uplink_item/O = filtered_uplink_items[initial(D.category)][initial(D.name)]
+ O.refundable = FALSE
- LAZYSET(filtered_uplink_items[A.category], A.name, A)
+ filtered_uplink_items["Discounted Team Gear"] = nuclear_team.team_discounts["Discounted Team Gear"]
+ filtered_uplink_items["Limited Stock Team Gear"] = nuclear_team.team_discounts["Limited Stock Team Gear"]
- for(var/category in filtered_uplink_items)
- if(!filtered_uplink_items[category]) //empty categories with no associated uplink item. Remove.
- filtered_uplink_items -= category
return filtered_uplink_items
+/proc/create_uplink_sales(num, category_name, limited_stock, sale_items, uplink_items)
+ if (num <= 0)
+ return
+
+ if(!uplink_items[category_name])
+ uplink_items[category_name] = list()
+
+ for (var/i in 1 to num)
+ var/datum/uplink_item/I = pick_n_take(sale_items)
+ var/datum/uplink_item/A = new I.type
+ var/discount = A.get_discount()
+ var/list/disclaimer = list("Void where prohibited.", "Not recommended for children.", "Contains small parts.", "Check local laws for legality in region.", "Do not taunt.", "Not responsible for direct, indirect, incidental or consequential damages resulting from any defect, error or failure to perform.", "Keep away from fire or flames.", "Product is provided \"as is\" without any implied or expressed warranties.", "As seen on TV.", "For recreational use only.", "Use only as directed.", "16% sales tax will be charged for orders originating within Space Nebraska.")
+ A.limited_stock = limited_stock
+ I.refundable = FALSE //THIS MAN USES ONE WEIRD TRICK TO GAIN FREE TC, CODERS HATES HIM!
+ A.refundable = FALSE
+ if(A.cost >= 20) //Tough love for nuke ops
+ discount *= 0.5
+ A.category = category_name
+ A.cost = max(round(A.cost * discount),1)
+ A.name += " ([round(((initial(A.cost)-A.cost)/initial(A.cost))*100)]% off!)"
+ A.desc += " Normally costs [initial(A.cost)] TC. All sales final. [pick(disclaimer)]"
+ A.item = I.item
+
+ uplink_items[category_name][A.name] = A
+
/**
* Uplink Items
@@ -67,12 +89,14 @@
var/surplus = 100 // Chance of being included in the surplus crate.
var/cant_discount = FALSE
var/limited_stock = -1 //Setting this above zero limits how many times this item can be bought by the same traitor in a round, -1 is unlimited
- var/list/include_modes = list() // Game modes to allow this item in.
- var/list/exclude_modes = list() // Game modes to disallow this item from.
+ /// A bitfield to represent what uplinks can purchase this item.
+ /// See [`code/__DEFINES/uplink.dm`].
+ var/purchasable_from = ALL
var/list/restricted_roles = list() //If this uplink item is only available to certain roles. Roles are dependent on the frequency chip or stored ID.
var/player_minimum //The minimum crew size needed for this item to be added to uplinks.
var/purchase_log_vis = TRUE // Visible in the purchase log?
var/restricted = FALSE // Adds restrictions for VR/Events
+ var/list/restricted_species //Limits items to a specific species. Hopefully.
var/illegal_tech = TRUE // Can this item be deconstructed to unlock certain techweb research nodes?
/datum/uplink_item/proc/get_discount()
@@ -80,6 +104,7 @@
/datum/uplink_item/proc/purchase(mob/user, datum/component/uplink/U)
var/atom/A = spawn_item(item, user, U)
+ // log_uplink("[key_name(user)] purchased [src] for [cost] telecrystals from [U.parent]'s uplink")
if(purchase_log_vis && U.purchase_log)
U.purchase_log.LogPurchase(A, src, cost)
@@ -94,70 +119,66 @@
if(ishuman(user) && istype(A, /obj/item))
var/mob/living/carbon/human/H = user
if(H.put_in_hands(A))
- to_chat(H, "[A] materializes into your hands!")
+ to_chat(H, span_boldnotice("[A] materializes into your hands!"))
return A
- to_chat(user, "[A] materializes onto the floor.")
+ to_chat(user, span_boldnotice("[A] materializes onto the floor!"))
return A
-/*
- Uplink Categories:
- Due to how the typesof() in-built byond proc works, it should be kept in mind
- the order categories are displayed in the uplink UI is same to the order they are loaded in the code.
- I trust no extra filter is needed as long as they are all contained within the following lines.
- When adding new uplink categories, please keep them separate from their sub paths here and without set item.
- Failure to comply may result in the new categories being listed at the bottom of the UI.
-*/
+//Discounts (dynamically filled above)
+/datum/uplink_item/discounts
+ category = "Discounts"
+// cit specific. idk
/datum/uplink_item/holiday
category = "Holiday"
-/datum/uplink_item/bundles_TC
- category = "Telecrystals and Bundles"
+//All bundles and telecrystals
+/datum/uplink_item/bundles_tc
+ category = "Bundles"
surplus = 0
cant_discount = TRUE
+// Dangerous Items
/datum/uplink_item/dangerous
category = "Conspicuous Weapons"
-/datum/uplink_item/stealthy_weapons
- category = "Stealthy Weapons"
-
-/datum/uplink_item/ammo
- category = "Ammunition"
- surplus = 40
-
-/datum/uplink_item/explosives
- category = "Explosives"
-
+//Support and Mechs
/datum/uplink_item/support
category = "Support and Exosuits"
surplus = 0
- include_modes = list(/datum/game_mode/nuclear)
-
-/datum/uplink_item/suits
- category = "Clothing"
- surplus = 40
+ purchasable_from = UPLINK_NUKE_OPS
+// Stealth Items
/datum/uplink_item/stealthy_tools
category = "Stealth Gadgets"
+//Space Suits and Hardsuits
+/datum/uplink_item/suits
+ category = "Space Suits"
+ surplus = 40
+
+// Devices and Tools
/datum/uplink_item/device_tools
category = "Misc. Gadgets"
+// Implants
/datum/uplink_item/implants
category = "Implants"
surplus = 50
+//Race-specific items
+/datum/uplink_item/race_restricted
+ category = "Species-Restricted"
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
+ surplus = 0
+
+// Role-specific items
/datum/uplink_item/role_restricted
category = "Role-Restricted"
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
surplus = 0
- cant_discount = TRUE
+// Pointless
/datum/uplink_item/badass
category = "(Pointless) Badassery"
surplus = 0
-
-//Discounts (dynamically filled above)
-/datum/uplink_item/discounts
- category = "Discounted Gear"
diff --git a/code/modules/uplink/uplink_items/uplink_ammo.dm b/code/modules/uplink/uplink_items/uplink_ammo.dm
index a7f3f5321d..a1c96d1082 100644
--- a/code/modules/uplink/uplink_items/uplink_ammo.dm
+++ b/code/modules/uplink/uplink_items/uplink_ammo.dm
@@ -12,7 +12,7 @@
desc = "Contains 10 additional .45-70 GOVT rounds. Caliber is exceedingly rare, and thus, comes at a premium."
item = /obj/item/ammo_box/g4570
cost = 5
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/pistol
name = "10mm Handgun Magazine"
@@ -20,7 +20,7 @@
are dirt cheap but are half as effective as .357 rounds."
item = /obj/item/ammo_box/magazine/m10mm
cost = 1
- exclude_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~UPLINK_CLOWN_OPS
/datum/uplink_item/ammo/pistol/box
name = "Ammo Box - 10mm"
@@ -34,7 +34,7 @@
These rounds are less effective at injuring the target but penetrate protective gear."
item = /obj/item/ammo_box/magazine/m10mm/ap
cost = 2
- exclude_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~UPLINK_CLOWN_OPS
/datum/uplink_item/ammo/pistolap/box
name = "Ammo Box - 10mm Armour Piercing"
@@ -48,7 +48,7 @@
These rounds are more damaging but ineffective against armour."
item = /obj/item/ammo_box/magazine/m10mm/hp
cost = 3
- exclude_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~UPLINK_CLOWN_OPS
/datum/uplink_item/ammo/pistolhp/box
name = "Ammo Box - 10mm Hollow Point"
@@ -62,7 +62,7 @@
Loaded with incendiary rounds which inflict little damage, but ignite the target."
item = /obj/item/ammo_box/magazine/m10mm/fire
cost = 2
- exclude_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~UPLINK_CLOWN_OPS
/datum/uplink_item/ammo/pistolfire/box
name = "Ammo Box - 10mm Incendiary"
@@ -85,7 +85,7 @@
/datum/uplink_item/ammo/shotgun
cost = 2
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/shotgun/bag
name = "12g Ammo Duffel Bag"
@@ -136,7 +136,7 @@
For when you really need a lot of things dead."
item = /obj/item/ammo_box/a357
cost = 3
- exclude_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~UPLINK_CLOWN_OPS
/datum/uplink_item/ammo/revolver/ap
name = ".357 Armor Piercing Speed Loader"
@@ -150,25 +150,25 @@
Your teammates will ask you to not shoot these down small hallways."
item = /obj/item/ammo_casing/a40mm
cost = 2
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/smg/bag
name = ".45 Ammo Duffel Bag"
desc = "A duffel bag filled with enough .45 ammo to supply an entire team, at a discounted price."
item = /obj/item/storage/backpack/duffelbag/syndie/ammo/smg
cost = 20 //instead of 27 TC
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/smg
name = ".45 SMG Magazine"
desc = "An additional 24-round .45 magazine suitable for use with the C-20r submachine gun."
item = /obj/item/ammo_box/magazine/smgm45
cost = 3
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/sniper
cost = 4
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/sniper/basic
name = ".50 Magazine"
@@ -194,7 +194,7 @@
These bullets pack less punch than 7.12x82mm rounds, but they still offer more power than .45 ammo."
item = /obj/item/ammo_box/magazine/m556
cost = 4
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/machinegun/match
name = "7.12x82mm (Match) Box Magazine"
@@ -206,7 +206,7 @@
/datum/uplink_item/ammo/machinegun
cost = 6
surplus = 0
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/machinegun/basic
name = "1.95x129mm Box Magazine"
@@ -234,7 +234,7 @@
item = /obj/item/ammo_box/magazine/mm195x129/incen
/datum/uplink_item/ammo/rocket
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/rocket/basic
name = "84mm HE Rocket"
@@ -254,7 +254,7 @@
desc = "An additional 15-round 9mm magazine, compatible with the Stechkin APS pistol, found in the Spetsnaz Pyro bundle."
item = /obj/item/ammo_box/magazine/pistolm9mm
cost = 2
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/pistolaps
name = "Ammo Box - 9mm"
@@ -268,7 +268,7 @@
Loaded with armor piercing flechettes that very nearly ignore armor, but are not very effective against flesh."
item = /obj/item/ammo_box/magazine/flechette
cost = 2
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/flechettes
name = "Serrated Flechette Magazine"
@@ -277,7 +277,7 @@
These flechettes are highly likely to sever arteries, and even limbs."
item = /obj/item/ammo_box/magazine/flechette/s
cost = 2
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/toydarts
name = "Box of Riot Darts"
@@ -293,32 +293,32 @@
and broca systems, making it impossible for them to move or speak for some time."
item = /obj/item/storage/box/syndie_kit/bioterror
cost = 6
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/ammo/bolt_action
name = "Surplus Rifle Clip"
desc = "A stripper clip used to quickly load bolt action rifles. Contains 5 rounds."
item = /obj/item/ammo_box/a762
cost = 1
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/bolt_action_bulk
name = "Surplus Rifle Clip Box"
desc = "An ammo box we found in a warehouse, holding 7 clips of 5 rounds for bolt-action rifles. Yes, the cheap ones."
item = /obj/item/storage/toolbox/ammo
cost = 4
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/dark_gygax/bag
name = "Dark Gygax Ammo Bag"
desc = "A duffel bag containing ammo for three full reloads of the incendiary carbine and flash bang launcher that are equipped on a standard Dark Gygax exosuit."
item = /obj/item/storage/backpack/duffelbag/syndie/ammo/dark_gygax
cost = 4
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/ammo/mauler/bag
name = "Mauler Ammo Bag"
desc = "A duffel bag containing ammo for three full reloads of the LMG, scattershot carbine, and SRM-8 missile laucher that are equipped on a standard Mauler exosuit."
item = /obj/item/storage/backpack/duffelbag/syndie/ammo/mauler
cost = 6
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
diff --git a/code/modules/uplink/uplink_items/uplink_badass.dm b/code/modules/uplink/uplink_items/uplink_badass.dm
index ec0ebf66d1..23f7063c7e 100644
--- a/code/modules/uplink/uplink_items/uplink_badass.dm
+++ b/code/modules/uplink/uplink_items/uplink_badass.dm
@@ -14,9 +14,9 @@
item = /obj/item/storage/box/syndie_kit/chameleon/broken
/datum/uplink_item/badass/costumes
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
cost = 4
cant_discount = TRUE
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/badass/costumes/centcom_official
name = "CentCom Official Costume"
@@ -77,14 +77,14 @@
cost = 4
limited_stock = 1
cant_discount = TRUE
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/badass/gaming_cardpack
name = "TCG Card Operatives Bundle"
desc = "A bundle full of goodies required to work as a TCG Card Operative. A warm pajama, a mug of cocoa, a plushie and a two packs full of rare 2560 Core Set cards!"
item = /obj/item/storage/box/syndie_kit/sleepytime/cardpack
cost = 20
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/badass/cardpack
name = "TCG Nuclear Cardpack"
diff --git a/code/modules/uplink/uplink_items/uplink_bundles.dm b/code/modules/uplink/uplink_items/uplink_bundles.dm
index cf33c893ad..a7ff04abbc 100644
--- a/code/modules/uplink/uplink_items/uplink_bundles.dm
+++ b/code/modules/uplink/uplink_items/uplink_bundles.dm
@@ -7,30 +7,30 @@
When adding new entries to the file, please keep them sorted by category.
*/
-/datum/uplink_item/bundles_TC/chemical
+/datum/uplink_item/bundles_tc/chemical
name = "Bioterror bundle"
desc = "For the madman: Contains a handheld Bioterror chem sprayer, a Bioterror foam grenade, a box of lethal chemicals, a dart pistol, \
box of syringes, Donksoft assault rifle, and some riot darts. Remember: Seal suit and equip internals before use."
item = /obj/item/storage/backpack/duffelbag/syndie/med/bioterrorbundle
cost = 30 // normally 42
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
-/datum/uplink_item/bundles_TC/bulldog
+/datum/uplink_item/bundles_tc/bulldog
name = "Bulldog bundle"
desc = "Lean and mean: Optimized for people that want to get up close and personal. Contains the popular \
Bulldog shotgun, a 12g buckshot drum, a 12g taser slug drum and a pair of Thermal imaging goggles."
item = /obj/item/storage/backpack/duffelbag/syndie/bulldogbundle
cost = 13 // normally 16
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
-/datum/uplink_item/bundles_TC/c20r
+/datum/uplink_item/bundles_tc/c20r
name = "C-20r bundle"
desc = "Old Faithful: The classic C-20r, bundled with two magazines, and a (surplus) suppressor at discount price."
item = /obj/item/storage/backpack/duffelbag/syndie/c20rbundle
cost = 14 // normally 16
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
-/datum/uplink_item/bundles_TC/contract_kit
+/datum/uplink_item/bundles_tc/contract_kit
name = "Contract Kit"
desc = "The Syndicate have offered you the chance to become a contractor, take on kidnapping contracts for TC and cash payouts. Upon purchase, \
you'll be granted your own contract uplink embedded within the supplied tablet computer. Additionally, you'll be granted \
@@ -39,10 +39,10 @@
item = /obj/item/storage/box/syndie_kit/contract_kit
cost = 20
player_minimum = 25
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
restricted = TRUE
-/datum/uplink_item/bundles_TC/northstar_bundle
+/datum/uplink_item/bundles_tc/northstar_bundle
name = "Northstar Bundle"
desc = "An item usually reserved for the Gorlex Marauders and their operatives, now available for recreational use. \
These armbands let the user punch people very fast and with the lethality of a legendary martial artist. \
@@ -50,16 +50,16 @@
Combines with all martial arts, but the user will be unable to bring themselves to use guns, nor remove the armbands."
item = /obj/item/storage/box/syndie_kit/northstar
cost = 20
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops, /datum/game_mode/traitor/internal_affairs)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
-/datum/uplink_item/bundles_TC/scarp_bundle
+/datum/uplink_item/bundles_tc/scarp_bundle
name = "Sleeping Carp Bundle"
desc = "Become one with your inner carp! Your ancient fish masters leave behind their legacy, and bestow to you their teachings, sacred uniform, and staff. \
Please be aware that you will not be able to use dishonerable ranged weapons."
item = /obj/item/storage/box/syndie_kit/scarp
cost = 20
player_minimum = 20
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops, /datum/game_mode/traitor/internal_affairs)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/suits/infiltrator_bundle
name = "Insidious Infiltration Gear Case"
@@ -69,86 +69,85 @@
item = /obj/item/storage/toolbox/infiltrator
cost = 5
limited_stock = 1 //you only get one so you don't end up with too many gun cases
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
-/datum/uplink_item/bundles_TC/cybernetics_bundle
+/datum/uplink_item/bundles_tc/cybernetics_bundle
name = "Cybernetic Implants Bundle"
desc = "A random selection of cybernetic implants. Guaranteed 5 high quality implants. Comes with an autosurgeon."
item = /obj/item/storage/box/cyber_implants
cost = 40
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
-/datum/uplink_item/bundles_TC/medical
+/datum/uplink_item/bundles_tc/medical
name = "Medical bundle"
desc = "The support specialist: Aid your fellow operatives with this medical bundle. Contains a tactical medkit, \
a Donksoft LMG, a box of riot darts and a pair of magboots to rescue your friends in no-gravity environments."
item = /obj/item/storage/backpack/duffelbag/syndie/med/medicalbundle
cost = 15 // normally 20
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
-/datum/uplink_item/bundles_TC/modular
+/datum/uplink_item/bundles_tc/modular
name = "Modular Pistol Kit"
desc = "A heavy briefcase containing one modular pistol (chambered in 10mm), one supressor, and spare ammunition, including a box of soporific ammo. \
Includes a suit jacket that is padded with a robust liner."
item = /obj/item/storage/briefcase/modularbundle
cost = 12
-/datum/uplink_item/bundles_TC/shredder
+/datum/uplink_item/bundles_tc/shredder
name = "Shredder bundle"
desc = "A truly horrific weapon designed simply to maim its victim, the CX Shredder is banned by several intergalactic treaties. \
You'll get two of them with this. And spare ammo to boot. And we'll throw in an extra elite hardsuit and chest rig to hold them all!"
item = /obj/item/storage/backpack/duffelbag/syndie/shredderbundle
cost = 30 // normally 41
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
-/datum/uplink_item/bundles_TC/sniper
+/datum/uplink_item/bundles_tc/sniper
name = "Sniper bundle"
desc = "Elegant and refined: Contains a collapsed sniper rifle in an expensive carrying case, \
two soporific knockout magazines, a free surplus supressor, and a sharp-looking tactical turtleneck suit. \
We'll throw in a free red tie if you order NOW."
item = /obj/item/storage/briefcase/sniperbundle
cost = 20 // normally 26
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
-/datum/uplink_item/bundles_TC/firestarter
+/datum/uplink_item/bundles_tc/firestarter
name = "Spetsnaz Pyro bundle"
desc = "For systematic suppression of carbon lifeforms in close quarters: Contains a lethal New Russian backpack spray, Elite hardsuit, \
Stechkin APS pistol, two magazines, a minibomb and a stimulant syringe. \
Order NOW and comrade Boris will throw in an extra tracksuit."
item = /obj/item/storage/backpack/duffelbag/syndie/firestarter
cost = 30
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
-/datum/uplink_item/bundles_TC/bundle
+/datum/uplink_item/bundles_tc/bundle
name = "Syndicate Bundle"
desc = "Syndicate Bundles are specialized groups of items that arrive in a plain box. \
These items are collectively worth more than 20 telecrystals, but you do not know which specialization \
you will receive. May contain discontinued and/or exotic items."
item = /obj/item/storage/box/syndicate
cost = 20
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/traitor/internal_affairs)
+ purchasable_from = ~(UPLINK_NUKE_OPS)
cant_discount = TRUE
-/datum/uplink_item/bundles_TC/surplus
+/datum/uplink_item/bundles_tc/surplus
name = "Syndicate Surplus Crate"
desc = "A dusty crate from the back of the Syndicate warehouse. Rumored to contain a valuable assortment of items, \
but you never know. Contents are sorted to always be worth 50 TC."
item = /obj/structure/closet/crate
cost = 20
- player_minimum = 20
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops, /datum/game_mode/traitor/internal_affairs)
- cant_discount = TRUE
+ player_minimum = 25
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
var/starting_crate_value = 50
-/datum/uplink_item/bundles_TC/surplus/super
+/datum/uplink_item/bundles_tc/surplus/super
name = "Super Surplus Crate"
desc = "A dusty SUPER-SIZED from the back of the Syndicate warehouse. Rumored to contain a valuable assortment of items, \
but you never know. Contents are sorted to always be worth 125 TC."
cost = 40
- player_minimum = 30
+ player_minimum = 40
starting_crate_value = 125
-/datum/uplink_item/bundles_TC/surplus/purchase(mob/user, datum/component/uplink/U)
+/datum/uplink_item/bundles_tc/surplus/purchase(mob/user, datum/component/uplink/U)
var/list/uplink_items = get_uplink_items(SSticker && SSticker.mode? SSticker.mode : null, FALSE)
var/crate_value = starting_crate_value
@@ -170,7 +169,7 @@
U.purchase_log.LogPurchase(goods, I, 0)
return C
-/datum/uplink_item/bundles_TC/reroll
+/datum/uplink_item/bundles_tc/reroll
name = "Renegotiate Contract"
desc = "Selecting this will inform your employers that you wish for new objectives. Can only be done twice."
item = /obj/effect/gibspawner/generic
@@ -179,22 +178,21 @@
restricted = TRUE
limited_stock = 2
-/datum/uplink_item/bundles_TC/reroll/purchase(mob/user, datum/component/uplink/U)
+/datum/uplink_item/bundles_tc/reroll/purchase(mob/user, datum/component/uplink/U)
var/datum/antagonist/traitor/T = user?.mind?.has_antag_datum(/datum/antagonist/traitor)
if(istype(T))
T.set_traitor_kind(get_random_traitor_kind(blacklist = list(/datum/traitor_class/human/freeform, /datum/traitor_class/human/hijack, /datum/traitor_class/human/martyr)))
else
to_chat(user,"Invalid user for contract renegotiation.")
-/datum/uplink_item/bundles_TC/random
+/datum/uplink_item/bundles_tc/random
name = "Random Item"
desc = "Picking this will purchase a random item. Useful if you have some TC to spare or if you haven't decided on a strategy yet."
item = /obj/effect/gibspawner/generic // non-tangible item because techwebs use this path to determine illegal tech
cost = 0
cant_discount = TRUE
- exclude_modes = list(/datum/game_mode/traitor/internal_affairs)
-/datum/uplink_item/bundles_TC/random/purchase(mob/user, datum/component/uplink/U)
+/datum/uplink_item/bundles_tc/random/purchase(mob/user, datum/component/uplink/U)
var/list/uplink_items = U.uplink_items
var/list/possible_items = list()
for(var/category in uplink_items)
@@ -202,7 +200,7 @@
var/datum/uplink_item/I = uplink_items[category][item]
if(src == I || !I.item)
continue
- if(istype(I, /datum/uplink_item/bundles_TC/reroll)) //oops!
+ if(istype(I, /datum/uplink_item/bundles_tc/reroll)) //oops!
continue
if(U.telecrystals < I.cost)
continue
@@ -215,7 +213,7 @@
SSblackbox.record_feedback("tally", "traitor_random_uplink_items_gotten", 1, initial(I.name))
U.MakePurchase(user, I)
-/datum/uplink_item/bundles_TC/telecrystal
+/datum/uplink_item/bundles_tc/telecrystal
name = "1 Raw Telecrystal"
desc = "A telecrystal in its rawest and purest form; can be utilized on active uplinks to increase their telecrystal count."
item = /obj/item/stack/telecrystal
@@ -226,13 +224,13 @@
// it's just used to buy more items (including itself!)
purchase_log_vis = FALSE
-/datum/uplink_item/bundles_TC/telecrystal/five
+/datum/uplink_item/bundles_tc/telecrystal/five
name = "5 Raw Telecrystals"
desc = "Five telecrystals in their rawest and purest form; can be utilized on active uplinks to increase their telecrystal count."
item = /obj/item/stack/telecrystal/five
cost = 5
-/datum/uplink_item/bundles_TC/telecrystal/twenty
+/datum/uplink_item/bundles_tc/telecrystal/twenty
name = "20 Raw Telecrystals"
desc = "Twenty telecrystals in their rawest and purest form; can be utilized on active uplinks to increase their telecrystal count."
item = /obj/item/stack/telecrystal/twenty
diff --git a/code/modules/uplink/uplink_items/uplink_clothing.dm b/code/modules/uplink/uplink_items/uplink_clothing.dm
index a9a9050903..142e501ec3 100644
--- a/code/modules/uplink/uplink_items/uplink_clothing.dm
+++ b/code/modules/uplink/uplink_items/uplink_clothing.dm
@@ -12,35 +12,35 @@
desc = "A slightly armored conspicious jumpsuit that has no suit sensors attached to them, if someone sees you in this hope they think its a fake."
item = /obj/item/clothing/under/syndicate
cost = 1
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) //They already get these
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/suits/turtlenck_skirt
name = "Tactical Skirtleneck"
desc = "A slightly armored conspicious jumpsuit that has no suit sensors attached to them, if someone sees you in this hope they think its a fake."
item = /obj/item/clothing/under/syndicate/skirt
cost = 1
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) //They already get these
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/suits/padding
name = "Soft Padding"
desc = "An inconspicious soft padding meant to be worn underneath jumpsuits, will cushion the user from melee harm."
item = /obj/item/clothing/accessory/padding
cost = 2
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/suits/kevlar
name = "Kevlar Padding"
desc = "An inconspicious kevlar padding meant to be worn underneath jumpsuits, will cushion the wearer from ballistic harm."
item = /obj/item/clothing/accessory/kevlar
cost = 2
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/suits/plastic
name = "Ablative Padding"
desc = "An inconspicious ablative padding meant to be worn underneath jumpsuits, will cushion the wearer from energy lasers harm."
item = /obj/item/clothing/accessory/plastics
cost = 2
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/suits/space_suit
name = "Syndicate Space Suit"
@@ -59,7 +59,7 @@
Nanotrasen crew who spot these suits are known to panic."
item = /obj/item/clothing/suit/space/hardsuit/syndi
cost = 8
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) //you can't buy it in nuke, because the elite hardsuit costs the same while being better
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS) //you can't buy it in nuke, because the elite hardsuit costs the same while being better
/datum/uplink_item/suits/hardsuit/elite
name = "Elite Syndicate Hardsuit"
@@ -67,8 +67,7 @@
provides the user with superior armor and mobility compared to the standard Syndicate hardsuit."
item = /obj/item/clothing/suit/space/hardsuit/syndi/elite
cost = 8
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
- exclude_modes = list()
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/suits/hardsuit/shielded
name = "Shielded Syndicate Hardsuit"
@@ -76,8 +75,7 @@
The shields can handle up to three impacts within a short duration and will rapidly recharge while not under fire."
item = /obj/item/clothing/suit/space/hardsuit/shielded/syndi
cost = 30
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
- exclude_modes = list()
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/suits/thiefgloves
name = "Thieving Gloves"
@@ -90,13 +88,13 @@
desc = "Through bluespace magic stolen from an organisation that hoards technology, these boots simply allow you to slip through the atoms that make up anything, but only while walking, for safety reasons. As well as this, they unfortunately cause minor breath loss as the majority of atoms in your lungs are sucked out into any solid object you walk through."
item = /obj/item/clothing/shoes/wallwalkers
cost = 6
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/device_tools/guerrillagloves
name = "Guerrilla Gloves"
desc = "A pair of highly robust combat gripper gloves that excels at performing takedowns at close range, with an added lining of insulation. Careful not to hit a wall!"
item = /obj/item/clothing/gloves/tackler/combat/insulated
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
cost = 2
illegal_tech = FALSE
@@ -111,4 +109,4 @@
desc = "A pair of highly reinforced armwraps allowing the user to parry almost anything. Fully reflects projectiles, no downsides to failing, but is very hard to parry melee with."
cost = 6
item = /obj/item/clothing/gloves/fingerless/ablative
- exclude_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
diff --git a/code/modules/uplink/uplink_items/uplink_dangerous.dm b/code/modules/uplink/uplink_items/uplink_dangerous.dm
index 0032f83d09..dac426db2b 100644
--- a/code/modules/uplink/uplink_items/uplink_dangerous.dm
+++ b/code/modules/uplink/uplink_items/uplink_dangerous.dm
@@ -13,7 +13,7 @@
with suppressors."
item = /obj/item/storage/box/syndie_kit/pistol
cost = 7
- exclude_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~UPLINK_CLOWN_OPS
/datum/uplink_item/dangerous/revolver
name = "Syndicate Revolver Kit"
@@ -22,7 +22,7 @@
cost = 13
player_minimum = 15
surplus = 50
- exclude_modes = list(/datum/game_mode/nuclear/clown_ops, /datum/game_mode/traitor/internal_affairs)
+ purchasable_from = ~UPLINK_CLOWN_OPS
/datum/uplink_item/dangerous/rawketlawnchair
name = "84mm Rocket Propelled Grenade Launcher"
@@ -31,7 +31,7 @@
item = /obj/item/gun/ballistic/rocketlauncher
cost = 8
surplus = 30
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/dangerous/antitank
name = "Anti Tank Pistol"
@@ -42,7 +42,7 @@
item = /obj/item/gun/ballistic/automatic/pistol/antitank/syndicate
cost = 14
surplus = 25
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/dangerous/pie_cannon
name = "Banana Cream Pie Cannon"
@@ -50,7 +50,7 @@
cost = 10
item = /obj/item/pneumatic_cannon/pie/selfcharge
surplus = 0
- include_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_CLOWN_OPS
/datum/uplink_item/dangerous/bananashield
name = "Bananium Energy Shield"
@@ -60,7 +60,7 @@
item = /obj/item/shield/energy/bananium
cost = 16
surplus = 0
- include_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_CLOWN_OPS
/datum/uplink_item/dangerous/clownsword
name = "Bananium Energy Sword"
@@ -69,7 +69,7 @@
item = /obj/item/melee/transforming/energy/sword/bananium
cost = 3
surplus = 0
- include_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_CLOWN_OPS
/datum/uplink_item/dangerous/bioterror
name = "Biohazardous Chemical Sprayer"
@@ -79,7 +79,7 @@
item = /obj/item/reagent_containers/spray/chemsprayer/bioterror
cost = 20
surplus = 0
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/dangerous/throwingweapons
name = "Box of Throwing Weapons"
@@ -95,7 +95,7 @@
item = /obj/item/gun/ballistic/automatic/shotgun/bulldog
cost = 8
surplus = 40
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/dangerous/smg
name = "C-20r Submachine Gun"
@@ -104,7 +104,7 @@
item = /obj/item/gun/ballistic/automatic/c20r
cost = 10
surplus = 40
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/dangerous/doublesword
name = "Double-Bladed Energy Sword"
@@ -113,7 +113,7 @@
item = /obj/item/dualsaber
player_minimum = 25
cost = 16
- exclude_modes = list(/datum/game_mode/nuclear/clown_ops, /datum/game_mode/traitor/internal_affairs)
+ purchasable_from = ~UPLINK_CLOWN_OPS
/datum/uplink_item/dangerous/doublesword/get_discount()
return pick(4;0.8,2;0.65,1;0.5)
@@ -125,7 +125,7 @@
item = /obj/item/dualsaber/hypereutactic
player_minimum = 25
cost = 16
- exclude_modes = list(/datum/game_mode/nuclear/clown_ops, /datum/game_mode/traitor/internal_affairs)
+ purchasable_from = ~UPLINK_CLOWN_OPS
/datum/uplink_item/dangerous/hyperblade/get_discount()
return pick(4;0.8,2;0.65,1;0.5)
@@ -136,7 +136,7 @@
pocketed when inactive. Activating it produces a loud, distinctive noise."
item = /obj/item/melee/transforming/energy/sword/saber
cost = 8
- exclude_modes = list(/datum/game_mode/nuclear/clown_ops, /datum/game_mode/traitor/internal_affairs)
+ purchasable_from = ~UPLINK_CLOWN_OPS
/datum/uplink_item/dangerous/shield
name = "Energy Shield"
@@ -145,7 +145,7 @@
item = /obj/item/shield/energy
cost = 16
surplus = 20
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/dangerous/rapier
name = "Rapier"
@@ -154,7 +154,7 @@
However, due to the size of the blade and obvious nature of the sheath, the weapon stands out as being obviously nefarious."
item = /obj/item/storage/belt/sabre/rapier
cost = 8
- exclude_modes = list(/datum/game_mode/nuclear/clown_ops, /datum/game_mode/traitor/internal_affairs)
+ purchasable_from = ~UPLINK_CLOWN_OPS
/datum/uplink_item/dangerous/flamethrower
name = "Flamethrower"
@@ -163,7 +163,7 @@
item = /obj/item/flamethrower/full/tank
cost = 4
surplus = 40
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/dangerous/flechettegun
name = "Flechette Launcher"
@@ -173,7 +173,7 @@
item = /obj/item/gun/ballistic/automatic/flechette
cost = 12
surplus = 30
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/dangerous/rapid
name = "Bands of the North Star"
@@ -182,7 +182,7 @@
Combines with all martial arts, but the user will be unable to bring themselves to use guns, nor remove the armbands."
item = /obj/item/clothing/gloves/fingerless/pugilist/rapid
cost = 30
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/dangerous/guardian
name = "Holoparasites"
@@ -194,7 +194,7 @@
refundable = TRUE
cant_discount = TRUE
surplus = 0
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops, /datum/game_mode/traitor/internal_affairs)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
player_minimum = 25
restricted = TRUE
refund_path = /obj/item/guardiancreator/tech/choose/traitor
@@ -208,7 +208,7 @@
refundable = TRUE
surplus = 50
refund_path = /obj/item/guardiancreator/tech/choose/nukie
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/dangerous/machinegun
name = "L6 Squad Automatic Weapon"
@@ -217,7 +217,7 @@
item = /obj/item/gun/ballistic/automatic/l6_saw
cost = 18
surplus = 0
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/dangerous/carbine
name = "M-90gl Carbine"
@@ -226,7 +226,7 @@
item = /obj/item/gun/ballistic/automatic/m90
cost = 18
surplus = 50
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/dangerous/maulergauntlets
name = "Mauler Gauntlets"
@@ -245,7 +245,6 @@
deal extra damage and hit targets further. Use a screwdriver to take out any attached tanks."
item = /obj/item/melee/powerfist
cost = 8
- exclude_modes = list(/datum/game_mode/traitor/internal_affairs)
/datum/uplink_item/dangerous/sniper
name = "Sniper Rifle"
@@ -253,14 +252,14 @@
item = /obj/item/gun/ballistic/automatic/sniper_rifle/syndicate
cost = 16
surplus = 25
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/dangerous/bolt_action
name = "Surplus Rifle"
desc = "A horribly outdated bolt action weapon. You've got to be desperate to use this."
item = /obj/item/gun/ballistic/shotgun/boltaction
cost = 2
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/dangerous/foamsmg
name = "Toy Submachine Gun"
@@ -268,7 +267,7 @@
item = /obj/item/gun/ballistic/automatic/c20r/toy
cost = 5
surplus = 0
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/dangerous/foammachinegun
name = "Toy Machine Gun"
@@ -277,7 +276,7 @@
item = /obj/item/gun/ballistic/automatic/l6_saw/toy
cost = 10
surplus = 0
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/dangerous/foampistol
name = "Toy Pistol with Riot Darts"
@@ -293,4 +292,4 @@
Allows you to cut from a far distance!"
item = /obj/item/gun/magic/staff/motivation
cost = 10
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops, /datum/game_mode/traitor/internal_affairs)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
diff --git a/code/modules/uplink/uplink_items/uplink_devices.dm b/code/modules/uplink/uplink_items/uplink_devices.dm
index 21889219cf..44dcedac34 100644
--- a/code/modules/uplink/uplink_items/uplink_devices.dm
+++ b/code/modules/uplink/uplink_items/uplink_devices.dm
@@ -46,7 +46,7 @@
item = /obj/item/assault_pod
cost = 30
surplus = 0
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
restricted = TRUE
/datum/uplink_item/device_tools/binary
@@ -66,7 +66,7 @@
'Advanced Magboots' slow you down in simulated-gravity environments much like the standard issue variety."
item = /obj/item/clothing/shoes/magboots/syndie
cost = 2
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/device_tools/compressionkit
name = "Bluespace Compression Kit"
@@ -98,7 +98,7 @@
desc = "A robust seven-slot set of webbing that is capable of holding all manner of tactical equipment."
item = /obj/item/storage/belt/military
cost = 1
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/device_tools/ammo_pouch
name = "Ammo Pouch"
@@ -137,7 +137,7 @@
desc = "A cheap bottle of one use syndicate brand super glue. \
Use on any item to make it undroppable. \
Be careful not to glue an item you're already holding!"
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
item = /obj/item/syndie_glue
cost = 2
@@ -166,7 +166,7 @@
operatives in the fight, even while under fire. Don't cross the streams!"
item = /obj/item/gun/medbeam
cost = 15
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/device_tools/nutcracker
name = "Nutcracker"
@@ -224,7 +224,7 @@
and other supplies helpful for a field medic."
item = /obj/item/storage/firstaid/tactical/nukeop
cost = 4
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/device_tools/surgerybag
name = "Syndicate Surgery Duffel Bag"
@@ -272,7 +272,7 @@
desc = "A potion recovered at great risk by undercover Syndicate operatives and then subsequently modified with Syndicate technology. \
Using it will make any animal sentient, and bound to serve you, as well as implanting an internal radio for communication and an internal ID card for opening doors."
cost = 2
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
restricted = TRUE
/* for now
diff --git a/code/modules/uplink/uplink_items/uplink_explosives.dm b/code/modules/uplink/uplink_items/uplink_explosives.dm
index f44966fb3b..11b892a208 100644
--- a/code/modules/uplink/uplink_items/uplink_explosives.dm
+++ b/code/modules/uplink/uplink_items/uplink_explosives.dm
@@ -15,7 +15,7 @@
item = /obj/item/grenade/chem_grenade/bioterrorfoam
cost = 5
surplus = 35
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/explosives/bombanana
name = "Bombanana"
@@ -24,7 +24,7 @@
item = /obj/item/reagent_containers/food/snacks/grown/banana/bombanana
cost = 4 //it is a bit cheaper than a minibomb because you have to take off your helmet to eat it, which is how you arm it
surplus = 0
- include_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_CLOWN_OPS
/datum/uplink_item/explosives/buzzkill
name = "Buzzkill Grenade Box"
@@ -33,7 +33,7 @@
item = /obj/item/storage/box/syndie_kit/bee_grenades
cost = 15
surplus = 35
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/explosives/c4
name = "Composition C-4"
@@ -58,7 +58,6 @@
item = /obj/item/storage/backpack/duffelbag/syndie/x4
cost = 4 //
cant_discount = TRUE
- exclude_modes = list(/datum/game_mode/traitor/internal_affairs)
/datum/uplink_item/explosives/clown_bomb_clownops
name = "Clown Bomb"
@@ -70,7 +69,7 @@
item = /obj/item/sbeacondrop/clownbomb
cost = 15
surplus = 0
- include_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_CLOWN_OPS
/datum/uplink_item/explosives/detomatix
name = "Detomatix PDA Cartridge"
@@ -97,14 +96,14 @@
item = /obj/item/storage/box/syndie_kit/tuberculosisgrenade
cost = 8
surplus = 35
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
restricted = TRUE
/datum/uplink_item/explosives/grenadier
name = "Grenadier's belt"
desc = "A belt containing 26 lethally dangerous and destructive grenades. Comes with an extra multitool and screwdriver."
item = /obj/item/storage/belt/grenade/full
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
cost = 22
surplus = 0
@@ -125,7 +124,6 @@
be defused, and some crew may attempt to do so."
item = /obj/item/sbeacondrop/bomb
cost = 11
- exclude_modes = list(/datum/game_mode/traitor/internal_affairs)
/datum/uplink_item/explosives/syndicate_detonator
name = "Syndicate Detonator"
@@ -135,7 +133,7 @@
the blast radius before using the detonator."
item = /obj/item/syndicatedetonator
cost = 3
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/explosives/syndicate_minibomb
name = "Syndicate Minibomb"
@@ -143,7 +141,7 @@
in addition to dealing high amounts of damage to nearby personnel."
item = /obj/item/grenade/syndieminibomb
cost = 6
- exclude_modes = list(/datum/game_mode/nuclear/clown_ops, /datum/game_mode/traitor/internal_affairs)
+ purchasable_from = ~UPLINK_CLOWN_OPS
/datum/uplink_item/explosives/tearstache
name = "Teachstache Grenade"
@@ -152,7 +150,7 @@
item = /obj/item/grenade/chem_grenade/teargas/moustache
cost = 3
surplus = 0
- include_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_CLOWN_OPS
/datum/uplink_item/explosives/viscerators
name = "Viscerator Delivery Grenade"
@@ -161,4 +159,4 @@
item = /obj/item/grenade/spawnergrenade/manhacks
cost = 5
surplus = 35
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
diff --git a/code/modules/uplink/uplink_items/uplink_implants.dm b/code/modules/uplink/uplink_items/uplink_implants.dm
index 19dab96ccb..e3fe151143 100644
--- a/code/modules/uplink/uplink_items/uplink_implants.dm
+++ b/code/modules/uplink/uplink_items/uplink_implants.dm
@@ -20,7 +20,7 @@
desc = "This implant will help you get back up on your feet faster after being stunned. Comes with an autosurgeon."
item = /obj/item/autosurgeon/anti_stun
cost = 12
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/implants/deathrattle
name = "Box of Deathrattle Implants"
@@ -31,7 +31,7 @@
item = /obj/item/storage/box/syndie_kit/imp_deathrattle
cost = 4
surplus = 0
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/implants/freedom
name = "Freedom Implant"
@@ -45,7 +45,7 @@
desc = "An implant injected into the body and later activated at the user's will. Allows the user to teleport to where they were 10 seconds ago. Has a 10 second cooldown."
item = /obj/item/storage/box/syndie_kit/imp_warp
cost = 6
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/implants/hijack
name = "Hijack Implant"
@@ -70,7 +70,7 @@
This will permanently destroy your body, however."
item = /obj/item/storage/box/syndie_kit/imp_microbomb
cost = 2
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/implants/macrobomb
name = "Macrobomb Implant"
@@ -78,7 +78,7 @@
Upon death, releases a massive explosion that will wipe out everything nearby."
item = /obj/item/storage/box/syndie_kit/imp_macrobomb
cost = 20
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
restricted = TRUE
/datum/uplink_item/implants/reviver
@@ -86,7 +86,7 @@
desc = "This implant will attempt to revive and heal you if you lose consciousness. Comes with an autosurgeon."
item = /obj/item/autosurgeon/reviver
cost = 8
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/implants/stealthimplant
name = "Stealth Implant"
@@ -107,7 +107,7 @@
desc = "These cybernetic eyes will give you thermal vision. Comes with a free autosurgeon."
item = /obj/item/autosurgeon/thermal_eyes
cost = 8
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
/datum/uplink_item/implants/uplink
name = "Uplink Implant"
@@ -125,4 +125,4 @@
item = /obj/item/autosurgeon/xray_eyes
cost = 10
surplus = 0
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
diff --git a/code/modules/uplink/uplink_items/uplink_roles.dm b/code/modules/uplink/uplink_items/uplink_roles.dm
index 72e0111c41..d741013ad2 100644
--- a/code/modules/uplink/uplink_items/uplink_roles.dm
+++ b/code/modules/uplink/uplink_items/uplink_roles.dm
@@ -30,7 +30,6 @@
item = /obj/item/gun/blastcannon
cost = 14 //High cost because of the potential for extreme damage in the hands of a skilled gas masked scientist.
restricted_roles = list("Research Director", "Scientist")
- exclude_modes = list(/datum/game_mode/traitor/internal_affairs)
/datum/uplink_item/role_restricted/alientech
name = "Alien Research Disk"
@@ -99,7 +98,6 @@
player_minimum = 20
refundable = TRUE
restricted_roles = list("Chaplain")
- exclude_modes = list(/datum/game_mode/traitor/internal_affairs)
/datum/uplink_item/role_restricted/arcane_tome
name = "Arcane Tome"
@@ -109,7 +107,6 @@
player_minimum = 20
refundable = TRUE
restricted_roles = list("Chaplain")
- exclude_modes = list(/datum/game_mode/traitor/internal_affairs)
/datum/uplink_item/role_restricted/explosive_hot_potato
name = "Exploding Hot Potato"
diff --git a/code/modules/uplink/uplink_items/uplink_stealth.dm b/code/modules/uplink/uplink_items/uplink_stealth.dm
index f401514542..8b79b15c80 100644
--- a/code/modules/uplink/uplink_items/uplink_stealth.dm
+++ b/code/modules/uplink/uplink_items/uplink_stealth.dm
@@ -19,14 +19,14 @@
to learn the abilities of krav maga to the wearer."
item = /obj/item/clothing/gloves/krav_maga/combatglovesplus
cost = 5
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
surplus = 0
/datum/uplink_item/stealthy_weapons/cqc
name = "CQC Manual"
desc = "A manual that teaches a single user tactical Close-Quarters Combat before self-destructing."
item = /obj/item/book/granter/martial/cqc
- include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS
cost = 13
surplus = 0
@@ -61,7 +61,7 @@
name = "Antique Derringer"
desc = "An easy to conceal, yet extremely deadly handgun, capable of firing .45-70 Govt rounds. Comes in a unique pack of cigarettes with additional munitions."
item = /obj/item/storage/fancy/cigarettes/derringer/midworld
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
cost = 10
surplus = 2
@@ -79,7 +79,7 @@
cost = 17
player_minimum = 20
surplus = 0
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/stealthy_weapons/martialartstwo
name = "Rising Bass Scroll"
@@ -89,7 +89,7 @@
cost = 18
player_minimum = 20
surplus = 0
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/stealthy_weapons/martialartsthree
name = "Krav Maga Scroll"
@@ -99,7 +99,6 @@
cost = 16
player_minimum = 25
surplus = 0
- include_modes = list(/datum/game_mode/traitor/internal_affairs)
/datum/uplink_item/stealthy_weapons/crossbow
name = "Miniature Energy Crossbow"
@@ -112,7 +111,7 @@
item = /obj/item/gun/energy/kinetic_accelerator/crossbow
cost = 12
surplus = 50
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/stealthy_weapons/traitor_chem_bottle
name = "Poison Kit"
@@ -130,7 +129,7 @@
cost = 25
player_minimum = 25
cant_discount = TRUE
- exclude_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = ~UPLINK_NUKE_OPS
/datum/uplink_item/stealthy_weapons/sleepy_pen
name = "Sleepy Pen"
@@ -140,14 +139,14 @@
falls asleep, they will be able to move and act."
item = /obj/item/pen/sleepy
cost = 4
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/stealthy_weapons/taeclowndo_shoes
name = "Tae-clown-do Shoes"
desc = "A pair of shoes for the most elite agents of the honkmotherland. They grant the mastery of taeclowndo with some honk-fu moves as long as they're worn."
cost = 12
item = /obj/item/clothing/shoes/clown_shoes/taeclowndo
- include_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_CLOWN_OPS
/datum/uplink_item/stealthy_weapons/suppressor
name = "Suppressor"
diff --git a/code/modules/uplink/uplink_items/uplink_stealthdevices.dm b/code/modules/uplink/uplink_items/uplink_stealthdevices.dm
index 0a82b3b8b3..c2a915a28f 100644
--- a/code/modules/uplink/uplink_items/uplink_stealthdevices.dm
+++ b/code/modules/uplink/uplink_items/uplink_stealthdevices.dm
@@ -33,7 +33,7 @@
Due to budget cuts, the shoes don't provide protection against slipping."
item = /obj/item/storage/box/syndie_kit/chameleon
cost = 2
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/stealthy_tools/chameleon_proj
name = "Chameleon Projector"
@@ -57,7 +57,7 @@
item = /obj/item/clothing/shoes/clown_shoes/banana_shoes/combat
cost = 6
surplus = 0
- include_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_CLOWN_OPS
/datum/uplink_item/stealthy_tools/emplight
name = "EMP Flashlight"
@@ -75,7 +75,7 @@
cost = 1
surplus = 0
restricted = TRUE
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/stealthy_tools/failsafe/spawn_item(spawn_path, mob/user, datum/component/uplink/U)
if(!U)
@@ -93,7 +93,7 @@
item = /obj/item/reagent_containers/syringe/mulligan
cost = 3
surplus = 30
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/stealthy_tools/syndigaloshes
name = "No-Slip Chameleon Shoes"
@@ -101,13 +101,12 @@
They do not work on heavily lubricated surfaces."
item = /obj/item/clothing/shoes/chameleon/noslip
cost = 2
- exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/stealthy_tools/syndigaloshes/nuke
item = /obj/item/clothing/shoes/chameleon/noslip
cost = 4
- exclude_modes = list()
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
/datum/uplink_item/stealthy_tools/jammer
name = "Radio Jammer"
diff --git a/code/modules/uplink/uplink_items/uplink_support.dm b/code/modules/uplink/uplink_items/uplink_support.dm
index fe1415adc8..efdfe1b06a 100644
--- a/code/modules/uplink/uplink_items/uplink_support.dm
+++ b/code/modules/uplink/uplink_items/uplink_support.dm
@@ -12,7 +12,7 @@
desc = "Call in an additional clown to share the fun, equipped with full starting gear, but no telecrystals."
item = /obj/item/antag_spawner/nuke_ops/clown
cost = 20
- include_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_CLOWN_OPS
restricted = TRUE
/datum/uplink_item/support/reinforcement
@@ -22,7 +22,7 @@
item = /obj/item/antag_spawner/nuke_ops
cost = 25
refundable = TRUE
- include_modes = list(/datum/game_mode/nuclear)
+ purchasable_from = UPLINK_NUKE_OPS
restricted = TRUE
/datum/uplink_item/support/reinforcement/assault_borg
@@ -66,7 +66,7 @@
desc = "A clown combat mech equipped with bombanana peel and tearstache grenade launchers, as well as the ubiquitous HoNkER BlAsT 5000."
item = /obj/mecha/combat/honker/dark/loaded
cost = 80
- include_modes = list(/datum/game_mode/nuclear/clown_ops)
+ purchasable_from = UPLINK_CLOWN_OPS
/datum/uplink_item/support/mauler
name = "Mauler Exosuit"
diff --git a/code/modules/uplink/uplink_purchase_log.dm b/code/modules/uplink/uplink_purchase_log.dm
index 293191b170..c9ac13fa13 100644
--- a/code/modules/uplink/uplink_purchase_log.dm
+++ b/code/modules/uplink/uplink_purchase_log.dm
@@ -1,8 +1,8 @@
-GLOBAL_LIST(uplink_purchase_logs_by_key) //assoc key = /datum/uplink_purchase_log
+GLOBAL_LIST(uplink_purchase_logs_by_key) //assoc key = /datum/uplink_purchase_log
/datum/uplink_purchase_log
var/owner
- var/list/purchase_log //assoc path-of-item = /datum/uplink_purchase_entry
+ var/list/purchase_log //assoc path-of-item = /datum/uplink_purchase_entry
var/total_spent = 0
/datum/uplink_purchase_log/New(_owner, datum/component/uplink/_parent)
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index 6434a694b1..32e3433495 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -12,16 +12,14 @@
products = list()
contraband = list()
premium = list()
-
-IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY CANISTER CHARGES in vending_items.dm
*/
#define MAX_VENDING_INPUT_AMOUNT 30
/**
- * # vending record datum
- *
- * A datum that represents a product that is vendable
- */
+ * # vending record datum
+ *
+ * A datum that represents a product that is vendable
+ */
/datum/data/vending_product
name = "generic"
///Typepath of the product that is created when this record "sells"
@@ -34,12 +32,16 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
var/custom_price
///Does the item have a custom premium price override
var/custom_premium_price
+ ///Whether spessmen with an ID with an age below AGE_MINOR (20 by default) can buy this item
+ var/age_restricted = FALSE // @unimplimented
+ ///Whether the product can be recolored by the GAGS system
+ var/colorable // @unimplimented
/**
- * # vending machines
- *
- * Captalism in the year 2525, everything in a vending machine, even love
- */
+ * # vending machines
+ *
+ * Captalism in the year 2525, everything in a vending machine, even love
+ */
/obj/machinery/vending
name = "\improper Vendomat"
desc = "A generic vending machine."
@@ -55,36 +57,43 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
armor = list("melee" = 20, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 70)
circuit = /obj/item/circuitboard/machine/vendor
payment_department = ACCOUNT_SRV
- light_power = 0.3
+ light_power = 0.5
light_range = MINIMUM_USEFUL_LIGHT_RANGE
/// Is the machine active (No sales pitches if off)!
- var/active = TRUE
+ var/active = 1
///Are we ready to vend?? Is it time??
var/vend_ready = TRUE
///Next world time to send a purchase message
var/purchase_message_cooldown
- ///Last mob to shop with us
+ ///The ref of the last mob to shop with us
+ var/last_shopper
+ var/tilted = FALSE
+ var/tiltable = TRUE
+ var/squish_damage = 75
+ var/forcecrit = 0
+ var/num_shards = 7
+ var/list/pinned_mobs = list()
/**
* List of products this machine sells
*
- * form should be list(/type/path = amount, /type/path2 = amount2)
+ * form should be list(/type/path = amount, /type/path2 = amount2)
*/
- var/list/products = list()
+ var/list/products = list()
/**
* List of products this machine sells when you hack it
*
- * form should be list(/type/path = amount, /type/path2 = amount2)
+ * form should be list(/type/path = amount, /type/path2 = amount2)
*/
- var/list/contraband = list()
+ var/list/contraband = list()
/**
* List of premium products this machine sells
*
- * form should be list(/type/path, /type/path2) as there is only ever one in stock
+ * form should be list(/type/path, /type/path2) as there is only ever one in stock
*/
- var/list/premium = list()
+ var/list/premium = list()
///String of slogans separated by semicolons, optional
var/product_slogans = ""
@@ -97,20 +106,12 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
var/list/slogan_list = list()
///Small ad messages in the vending screen - random chance of popping up whenever you open it
var/list/small_ads = list()
- var/dish_quants = list() //used by the snack machine's custom compartment to count dishes.
///Message sent post vend (Thank you for shopping!)
var/vend_reply
///Last world tick we sent a vent reply
- var/last_reply
+ var/last_reply = 0
///Last world tick we sent a slogan message out
- var/last_slogan
- var/last_shopper
- var/tilted = FALSE
- var/tiltable = TRUE
- var/squish_damage = 75
- var/forcecrit = 0
- var/num_shards = 7
- var/list/pinned_mobs = list()
+ var/last_slogan = 0
///How many ticks until we can send another
var/slogan_delay = 6000
///Icon when vending an item to the user
@@ -120,32 +121,37 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
///World ticks the machine is electified for
var/seconds_electrified = MACHINE_NOT_ELECTRIFIED
///When this is TRUE, we fire items at customers! We're broken!
- var/shoot_inventory
- ///How likely this is to happen (prob 100)
- var/shoot_inventory_chance = 2
+ var/shoot_inventory = 0
+ ///How likely this is to happen (prob 100) per second
+ var/shoot_inventory_chance = 1
//Stop spouting those godawful pitches!
- var/shut_up
+ var/shut_up = 0
///can we access the hidden inventory?
- var/extended_inventory
+ var/extended_inventory = 0
///Are we checking the users ID
- var/scan_id = TRUE
+ var/scan_id = 1
+ ///Coins that we accept?
var/obj/item/coin/coin
+ ///Bills we accept?
+ var/obj/item/stack/spacecash/bill
///Default price of items if not overridden
var/default_price = 25
///Default price of premium items if not overridden
var/extra_price = 50
+ ///Whether our age check is currently functional
+ var/age_restrictions = TRUE
///cost multiplier per department or access
var/list/cost_multiplier_per_dept = list()
- /**
+ /**
* Is this item on station or not
*
* if it doesn't originate from off-station during mapload, everything is free
*/
var/onstation = TRUE //if it doesn't originate from off-station during mapload, everything is free
- ///A variable to change on a per instance basis on the map that allows the instance to force cost and ID requirements
+ ///A variable to change on a per instance basis on the map that allows the instance to force cost and ID requirements
var/onstation_override = FALSE //change this on the object on the map to override the onstation check. DO NOT APPLY THIS GLOBALLY.
- ///ID's that can load this vending machine wtih refills
+ ///ID's that can load this vending machine wtih refills
var/list/canload_access_list
@@ -162,15 +168,19 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
///Name of lighting mask for the vending machine
var/light_mask
+ /// used for narcing on underages
+ var/obj/item/radio/Radio
+
+
/**
- * Initialize the vending machine
- *
- * Builds the vending machine inventory, sets up slogans and other such misc work
- *
- * This also sets the onstation var to:
- * * FALSE - if the machine was maploaded on a zlevel that doesn't pass the is_station_level check
- * * TRUE - all other cases
- */
+ * Initialize the vending machine
+ *
+ * Builds the vending machine inventory, sets up slogans and other such misc work
+ *
+ * This also sets the onstation var to:
+ * * FALSE - if the machine was maploaded on a zlevel that doesn't pass the is_station_level check
+ * * TRUE - all other cases
+ */
/obj/machinery/vending/Initialize(mapload)
var/build_inv = FALSE
if(!refill_canister)
@@ -189,18 +199,25 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
// so if slogantime is 10 minutes, it will say it at somewhere between 10 and 20 minutes after the machine is crated.
last_slogan = world.time + rand(0, slogan_delay)
power_change()
+
if(onstation_override) //overrides the checks if true.
onstation = TRUE
return
- if(mapload && !is_station_level(z)) //check if it was initially created off station during mapload.
- onstation = FALSE
- if(circuit)
- circuit.onstation = onstation //sync up the circuit so the pricing schema is carried over if it's reconstructed.
+ if(mapload) //check if it was initially created off station during mapload.
+ if(!is_station_level(z))
+ onstation = FALSE
+ if(circuit)
+ circuit.onstation = onstation //sync up the circuit so the pricing schema is carried over if it's reconstructed.
else if(circuit && (circuit.onstation != onstation)) //check if they're not the same to minimize the amount of edited values.
onstation = circuit.onstation //if it was constructed outside mapload, sync the vendor up with the circuit's var so you can't bypass price requirements by moving / reconstructing it off station.
+ Radio = new /obj/item/radio(src)
+ Radio.listening = 0
/obj/machinery/vending/Destroy()
QDEL_NULL(wires)
+ QDEL_NULL(coin)
+ QDEL_NULL(bill)
+ QDEL_NULL(Radio)
return ..()
/obj/machinery/vending/can_speak()
@@ -227,16 +244,20 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
else
..()
+/obj/machinery/vending/update_appearance(updates=ALL)
+ . = ..()
+ if(stat & BROKEN)
+ set_light(0)
+ return
+ set_light(powered() ? MINIMUM_USEFUL_LIGHT_RANGE : 0)
+
+
/obj/machinery/vending/update_icon_state()
if(stat & BROKEN)
icon_state = "[initial(icon_state)]-broken"
- set_light(0)
- else if(powered())
- icon_state = initial(icon_state)
- set_light(1.4)
- else
- icon_state = "[initial(icon_state)]-off"
- set_light(0)
+ return ..()
+ icon_state = "[initial(icon_state)][powered() ? null : "-off"]"
+ return ..()
/obj/machinery/vending/update_overlays()
@@ -276,14 +297,14 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
GLOBAL_LIST_EMPTY(vending_products)
/**
- * Build the inventory of the vending machine from it's product and record lists
- *
- * This builds up a full set of /datum/data/vending_products from the product list of the vending machine type
- * Arguments:
- * * productlist - the list of products that need to be converted
- * * recordlist - the list containing /datum/data/vending_product datums
- * * startempty - should we set vending_product record amount from the product list (so it's prefilled at roundstart)
- */
+ * Build the inventory of the vending machine from it's product and record lists
+ *
+ * This builds up a full set of /datum/data/vending_products from the product list of the vending machine type
+ * Arguments:
+ * * productlist - the list of products that need to be converted
+ * * recordlist - the list containing /datum/data/vending_product datums
+ * * startempty - should we set vending_product record amount from the product list (so it's prefilled at roundstart)
+ */
/obj/machinery/vending/proc/build_inventory(list/productlist, list/recordlist, start_empty = FALSE)
for(var/typepath in productlist)
var/amount = productlist[typepath]
@@ -298,17 +319,21 @@ GLOBAL_LIST_EMPTY(vending_products)
if(!start_empty)
R.amount = amount
R.max_amount = amount
+ ///Prices of vending machines are all increased uniformly.
R.custom_price = initial(temp.custom_price)
R.custom_premium_price = initial(temp.custom_premium_price)
+ // R.age_restricted = initial(temp.age_restricted)
+ // R.colorable = !!(initial(temp.greyscale_config) && initial(temp.greyscale_colors) && (initial(temp.flags_1) & IS_PLAYER_COLORABLE_1))
recordlist += R
+
/**
- * Refill a vending machine from a refill canister
- *
- * This takes the products from the refill canister and then fills the products,contraband and premium product categories
- *
- * Arguments:
- * * canister - the vending canister we are refilling from
- */
+ * Refill a vending machine from a refill canister
+ *
+ * This takes the products from the refill canister and then fills the products,contraband and premium product categories
+ *
+ * Arguments:
+ * * canister - the vending canister we are refilling from
+ */
/obj/machinery/vending/proc/restock(obj/item/vending_refill/canister)
if (!canister.products)
canister.products = products.Copy()
@@ -321,12 +346,12 @@ GLOBAL_LIST_EMPTY(vending_products)
. += refill_inventory(canister.contraband, hidden_records)
. += refill_inventory(canister.premium, coin_records)
/**
- * Refill our inventory from the passed in product list into the record list
- *
- * Arguments:
- * * productlist - list of types -> amount
- * * recordlist - existing record datums
- */
+ * Refill our inventory from the passed in product list into the record list
+ *
+ * Arguments:
+ * * productlist - list of types -> amount
+ * * recordlist - existing record datums
+ */
/obj/machinery/vending/proc/refill_inventory(list/productlist, list/recordlist)
. = 0
for(var/R in recordlist)
@@ -337,10 +362,10 @@ GLOBAL_LIST_EMPTY(vending_products)
record.amount += diff
. += diff
/**
- * Set up a refill canister that matches this machines products
- *
- * This is used when the machine is deconstructed, so the items aren't "lost"
- */
+ * Set up a refill canister that matches this machines products
+ *
+ * This is used when the machine is deconstructed, so the items aren't "lost"
+ */
/obj/machinery/vending/proc/update_canister()
if (!component_parts)
return
@@ -354,8 +379,8 @@ GLOBAL_LIST_EMPTY(vending_products)
R.premium = unbuild_inventory(coin_records)
/**
- * Given a record list, go through and and return a list of type -> amount
- */
+ * Given a record list, go through and and return a list of type -> amount
+ */
/obj/machinery/vending/proc/unbuild_inventory(list/recordlist)
. = list()
for(var/R in recordlist)
@@ -385,32 +410,32 @@ GLOBAL_LIST_EMPTY(vending_products)
add_overlay("[initial(icon_state)]-panel")
updateUsrDialog()
else
- to_chat(user, "You must first secure [src].")
+ to_chat(user, span_warning("You must first secure [src]."))
return TRUE
-/obj/machinery/vending/attackby(obj/item/I, mob/user, params)
+/obj/machinery/vending/attackby(obj/item/I, mob/living/user, params)
if(panel_open && is_wire_tool(I))
wires.interact(user)
return
+
if(refill_canister && istype(I, refill_canister))
if (!panel_open)
- to_chat(user, "You should probably unscrew the service panel first!")
+ to_chat(user, span_warning("You should probably unscrew the service panel first!"))
else if (stat & (BROKEN|NOPOWER))
- to_chat(user, "[src] does not respond.")
+ to_chat(user, span_notice("[src] does not respond."))
else
//if the panel is open we attempt to refill the machine
var/obj/item/vending_refill/canister = I
if(canister.get_part_rating() == 0)
- to_chat(user, "[canister] is empty!")
+ to_chat(user, span_warning("[canister] is empty!"))
else
// instantiate canister if needed
var/transferred = restock(canister)
if(transferred)
- to_chat(user, "You loaded [transferred] items in [src].")
+ to_chat(user, span_notice("You loaded [transferred] items in [src]."))
else
- to_chat(user, "There's nothing to restock!")
+ to_chat(user, span_warning("There's nothing to restock!"))
return
-
if(compartmentLoadAccessCheck(user) && user.a_intent != INTENT_HARM)
if(canLoadItem(I))
loadingAttempt(I,user)
@@ -422,7 +447,7 @@ GLOBAL_LIST_EMPTY(vending_products)
var/denied_items = 0
for(var/obj/item/the_item in T.contents)
if(contents.len >= MAX_VENDING_INPUT_AMOUNT) // no more than 30 item can fit inside, legacy from snack vending although not sure why it exists
- to_chat(user, "[src]'s compartment is full.")
+ to_chat(user, span_warning("[src]'s compartment is full."))
break
if(canLoadItem(the_item) && loadingAttempt(the_item,user))
SEND_SIGNAL(T, COMSIG_TRY_STORAGE_TAKE, the_item, src, TRUE)
@@ -430,9 +455,9 @@ GLOBAL_LIST_EMPTY(vending_products)
else
denied_items++
if(denied_items)
- to_chat(user, "[src] refuses some items!")
+ to_chat(user, span_warning("[src] refuses some items!"))
if(loaded)
- to_chat(user, "You insert [loaded] dishes into [src]'s compartment.")
+ to_chat(user, span_notice("You insert [loaded] dishes into [src]'s compartment."))
updateUsrDialog()
else
. = ..()
@@ -450,7 +475,7 @@ GLOBAL_LIST_EMPTY(vending_products)
tilt(user, crit=TRUE)
/obj/machinery/vending/proc/freebie(mob/fatty, freebies)
- visible_message("[src] yields [freebies > 1 ? "several free goodies" : "a free goody"]!")
+ visible_message(span_notice("[src] yields [freebies > 1 ? "several free goodies" : "a free goody"]!"))
for(var/i in 1 to freebies)
playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3)
@@ -466,8 +491,9 @@ GLOBAL_LIST_EMPTY(vending_products)
new dump_path(get_turf(src))
break
-/obj/machinery/vending/proc/tilt(mob/fatty, crit=FALSE)
- visible_message("[src] tips over!")
+///Tilts ontop of the atom supplied, if crit is true some extra shit can happen. Returns TRUE if it dealt damage to something.
+/obj/machinery/vending/proc/tilt(atom/fatty, crit=FALSE)
+ visible_message(span_danger("[src] tips over!"))
tilted = TRUE
layer = ABOVE_MOB_LAYER
@@ -478,16 +504,21 @@ GLOBAL_LIST_EMPTY(vending_products)
if(forcecrit)
crit_case = forcecrit
+ . = FALSE
+
if(in_range(fatty, src))
for(var/mob/living/L in get_turf(fatty))
+ var/was_alive = (L.stat != DEAD)
var/mob/living/carbon/C = L
+ // SEND_SIGNAL(L, COMSIG_ON_VENDOR_CRUSH)
+
if(istype(C))
var/crit_rebate = 0 // lessen the normal damage we deal for some of the crits
- if(crit_case < 5) // the head asplode case has its own description
- C.visible_message("[C] is crushed by [src]!", \
- "You are crushed by [src]!")
+ if(crit_case < 5) // the body/head asplode case has its own description
+ C.visible_message(span_danger("[C] is crushed by [src]!"), \
+ span_userdanger("You are crushed by [src]!"))
switch(crit_case) // only carbons can have the fun crits
if(1) // shatter their legs and bleed 'em
@@ -500,13 +531,13 @@ GLOBAL_LIST_EMPTY(vending_products)
if(r)
r.receive_damage(brute=200, updating_health=TRUE)
if(l || r)
- C.visible_message("[C]'s legs shatter with a sickening crunch!", \
- "Your legs shatter with a sickening crunch!")
+ C.visible_message(span_danger("[C]'s legs shatter with a sickening crunch!"), \
+ span_userdanger("Your legs shatter with a sickening crunch!"))
if(2) // pin them beneath the machine until someone untilts it
forceMove(get_turf(C))
buckle_mob(C, force=TRUE)
- C.visible_message("[C] is pinned underneath [src]!", \
- "You are pinned down by [src]!")
+ C.visible_message(span_danger("[C] is pinned underneath [src]!"), \
+ span_userdanger("You are pinned down by [src]!"))
if(3) // glass candy
crit_rebate = 50
for(var/i = 0, i < num_shards, i++)
@@ -518,7 +549,7 @@ GLOBAL_LIST_EMPTY(vending_products)
shard.updateEmbedding()
if(4) // paralyze this binch
// the new paraplegic gets like 4 lines of losing their legs so skip them
- visible_message("[C]'s spinal cord is obliterated with a sickening crunch!", ignored_mobs = list(C))
+ visible_message(span_danger("[C]'s spinal cord is obliterated with a sickening crunch!"), ignored_mobs = list(C))
C.gain_trauma(/datum/brain_trauma/severe/paralysis/spinesnapped)
if(5) // limb squish!
for(var/i in C.bodyparts)
@@ -528,13 +559,13 @@ GLOBAL_LIST_EMPTY(vending_products)
squish_part.force_wound_upwards(type_wound)
else
squish_part.receive_damage(brute=30)
- C.visible_message("[C]'s body is maimed underneath the mass of [src]!", \
- "Your body is maimed underneath the mass of [src]!")
+ C.visible_message(span_danger("[C]'s body is maimed underneath the mass of [src]!"), \
+ span_userdanger("Your body is maimed underneath the mass of [src]!"))
if(6) // skull squish!
var/obj/item/bodypart/head/O = C.get_bodypart(BODY_ZONE_HEAD)
if(O)
- C.visible_message("[O] explodes in a shower of gore beneath [src]!", \
- "Oh f-")
+ C.visible_message(span_danger("[O] explodes in a shower of gore beneath [src]!"), \
+ span_userdanger("Oh f-"))
O.dismember()
O.drop_organs()
qdel(O)
@@ -545,19 +576,19 @@ GLOBAL_LIST_EMPTY(vending_products)
else
C.take_bodypart_damage((squish_damage - crit_rebate)*0.5, wound_bonus = 5) // otherwise, deal it to 2 random limbs (or the same one) which will likely shatter something
C.take_bodypart_damage((squish_damage - crit_rebate)*0.5, wound_bonus = 5)
- C.AddElement(/datum/element/squish, 18 SECONDS)
+ C.AddElement(/datum/element/squish, 80 SECONDS)
else
- L.visible_message("[L] is crushed by [src]!", \
- "You are crushed by [src]!")
+ L.visible_message(span_danger("[L] is crushed by [src]!"), \
+ span_userdanger("You are crushed by [src]!"))
L.apply_damage(squish_damage, forced=TRUE)
if(crit_case)
L.apply_damage(squish_damage, forced=TRUE)
-
- if(L.stat == DEAD && L.client)
+ if(was_alive && L.stat == DEAD && L.client)
L.client.give_award(/datum/award/achievement/misc/vendor_squish, L) // good job losing a fight with an inanimate object idiot
L.Paralyze(60)
L.emote("scream")
+ . = TRUE
playsound(L, 'sound/effects/blobattack.ogg', 40, TRUE)
playsound(L, 'sound/effects/splat.ogg', 50, TRUE)
@@ -566,14 +597,15 @@ GLOBAL_LIST_EMPTY(vending_products)
transform = M
if(get_turf(fatty) != get_turf(src))
- throw_at(get_turf(fatty), 1, 1, spin=FALSE)
+ throw_at(get_turf(fatty), 1, 1, spin=FALSE) //, quickstart=FALSE)
/obj/machinery/vending/proc/untilt(mob/user)
- user.visible_message("[user] rights [src].", \
- "You right [src].")
+ if(user)
+ user.visible_message(span_notice("[user] rights [src]."), \
+ span_notice("You right [src]."))
unbuckle_all_mobs(TRUE)
- anchored = FALSE //so you can push it back into position
+
tilted = FALSE
layer = initial(layer)
@@ -589,21 +621,20 @@ GLOBAL_LIST_EMPTY(vending_products)
vending_machine_input[format_text(I.name)]++
else
vending_machine_input[format_text(I.name)] = 1
- to_chat(user, "You insert [I] into [src]'s input compartment.")
+ to_chat(user, span_notice("You insert [I] into [src]'s input compartment."))
loaded_items++
-
/obj/machinery/vending/unbuckle_mob(mob/living/buckled_mob, force=FALSE)
if(!force)
return
. = ..()
/**
- * Is the passed in user allowed to load this vending machines compartments
- *
- * Arguments:
- * * user - mob that is doing the loading of the vending machine
- */
+ * Is the passed in user allowed to load this vending machines compartments
+ *
+ * Arguments:
+ * * user - mob that is doing the loading of the vending machine
+ */
/obj/machinery/vending/proc/compartmentLoadAccessCheck(mob/user)
if(!canload_access_list)
return TRUE
@@ -621,7 +652,7 @@ GLOBAL_LIST_EMPTY(vending_products)
if(do_you_have_access)
return TRUE
else
- to_chat(user, "[src]'s input compartment blinks red: Access denied.")
+ to_chat(user, span_warning("[src]'s input compartment blinks red: Access denied."))
return FALSE
/obj/machinery/vending/exchange_parts(mob/user, obj/item/storage/part_replacer/W)
@@ -642,7 +673,7 @@ GLOBAL_LIST_EMPTY(vending_products)
else
display_parts(user)
if(moved)
- to_chat(user, "[moved] items restocked.")
+ to_chat(user, span_notice("[moved] items restocked."))
W.play_rped_sound()
return TRUE
@@ -651,22 +682,22 @@ GLOBAL_LIST_EMPTY(vending_products)
. = ..()
/obj/machinery/vending/emag_act(mob/user)
- . = ..()
if(obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
- to_chat(user, "You short out the product lock on [src].")
- return TRUE
+ to_chat(user, span_notice("You short out the product lock on [src]."))
/obj/machinery/vending/_try_interact(mob/user)
if(seconds_electrified && !(stat & NOPOWER))
if(shock(user, 100))
return
+
if(tilted && !user.buckled && !isAI(user))
- to_chat(user, "You begin righting [src].")
+ to_chat(user, span_notice("You begin righting [src]."))
if(do_after(user, 50, target=src))
untilt(user)
return
+
return ..()
/obj/machinery/vending/ui_assets(mob/user)
@@ -736,7 +767,12 @@ GLOBAL_LIST_EMPTY(vending_products)
.["user"]["department"] = "No Department"
.["stock"] = list()
for (var/datum/data/vending_product/R in product_records + coin_records + hidden_records)
- .["stock"][R.name] = R.amount
+ var/list/product_data = list(
+ name = R.name,
+ amount = R.amount,
+ colorable = FALSE, // R.colorable,
+ )
+ .["stock"][R.name] = product_data
.["extended_inventory"] = extended_inventory
/obj/machinery/vending/ui_act(action, params)
@@ -745,92 +781,150 @@ GLOBAL_LIST_EMPTY(vending_products)
return
switch(action)
if("vend")
- . = TRUE
- if(!vend_ready)
- return
- if(panel_open)
- to_chat(usr, "The vending machine cannot dispense products while its service panel is open!")
- return
- vend_ready = FALSE //One thing at a time!!
- var/datum/data/vending_product/R = locate(params["ref"])
- var/list/record_to_check = product_records + coin_records
- if(extended_inventory)
- record_to_check = product_records + coin_records + hidden_records
- if(!R || !istype(R) || !R.product_path)
- vend_ready = TRUE
- return
- var/price_to_use = default_price
- if(R.custom_price)
- price_to_use = R.custom_price
- if(R in hidden_records)
- if(!extended_inventory)
- vend_ready = TRUE
- return
- else if (!(R in record_to_check))
- vend_ready = TRUE
- message_admins("Vending machine exploit attempted by [ADMIN_LOOKUPFLW(usr)]!")
- return
- if (R.amount <= 0)
- say("Sold out of [R.name].")
- flick(icon_deny,src)
- vend_ready = TRUE
- return
- if(onstation && ishuman(usr))
- var/mob/living/carbon/human/H = usr
- var/obj/item/card/id/C = H.get_idcard(TRUE)
+ . = vend(params)
+ // if("select_colors")
+ // . = select_colors(params)
- if(!C)
- say("No card found.")
- flick(icon_deny,src)
- vend_ready = TRUE
- return
- else if (!C.registered_account)
- say("No account found.")
- flick(icon_deny,src)
- vend_ready = TRUE
- return
- // else if(age_restrictions && R.age_restricted && (!C.registered_age || C.registered_age < AGE_MINOR))
- // say("You are not of legal age to purchase [R.name].")
- // if(!(usr in GLOB.narcd_underages))
- // Radio.set_frequency(FREQ_SECURITY)
- // Radio.talk_into(src, "SECURITY ALERT: Underaged crewmember [H] recorded attempting to purchase [R.name] in [get_area(src)]. Please watch for substance abuse.", FREQ_SECURITY)
- // GLOB.narcd_underages += H
- // flick(icon_deny,src)
- // vend_ready = TRUE
- // return
- var/datum/bank_account/account = C.registered_account
- if(account.account_job && account.account_job.paycheck_department == payment_department)
- price_to_use = 0
- if(coin_records.Find(R) || hidden_records.Find(R))
- price_to_use = R.custom_premium_price ? R.custom_premium_price : extra_price
- if(price_to_use && !account.adjust_money(-price_to_use))
- say("You do not possess the funds to purchase [R.name].")
- flick(icon_deny,src)
- vend_ready = TRUE
- return
- var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department)
- if(D)
- D.adjust_money(price_to_use)
- SSblackbox.record_feedback("amount", "vending_spent", price_to_use)
- //log_econ("[price_to_use] credits were inserted into [src] by [D.account_holder] to buy [R].")
- if(last_shopper != usr || purchase_message_cooldown < world.time)
- say("Thank you for shopping with [src]!")
- purchase_message_cooldown = world.time + 5 SECONDS
- last_shopper = usr
- use_power(5)
- if(icon_vend) //Show the vending animation if needed
- flick(icon_vend,src)
- playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3)
- var/obj/item/vended = new R.product_path(get_turf(src))
- R.amount--
- if(usr.CanReach(src) && usr.put_in_hands(vended))
- to_chat(usr, "You take [R.name] out of the slot.")
- else
- to_chat(usr, "[capitalize(R.name)] falls onto the floor!")
- SSblackbox.record_feedback("nested tally", "vending_machine_usage", 1, list("[type]", "[R.product_path]"))
+/obj/machinery/vending/proc/can_vend(user, silent=FALSE)
+ . = FALSE
+ if(!vend_ready)
+ return
+ if(panel_open)
+ to_chat(user, span_warning("The vending machine cannot dispense products while its service panel is open!"))
+ return
+ return TRUE
+
+// /obj/machinery/vending/proc/select_colors(list/params)
+// . = TRUE
+// if(!can_vend(usr))
+// return
+// var/datum/data/vending_product/product = locate(params["ref"])
+// var/atom/fake_atom = product.product_path
+
+// var/list/allowed_configs = list()
+// var/config = initial(fake_atom.greyscale_config)
+// if(!config)
+// return
+// allowed_configs += "[config]"
+// if(ispath(fake_atom, /obj/item))
+// var/obj/item/item = fake_atom
+// if(initial(item.greyscale_config_worn))
+// allowed_configs += "[initial(item.greyscale_config_worn)]"
+// if(initial(item.greyscale_config_inhand_left))
+// allowed_configs += "[initial(item.greyscale_config_inhand_left)]"
+// if(initial(item.greyscale_config_inhand_right))
+// allowed_configs += "[initial(item.greyscale_config_inhand_right)]"
+
+// var/datum/greyscale_modify_menu/menu = new(
+// src, usr, allowed_configs, CALLBACK(src, .proc/vend_greyscale, params),
+// starting_icon_state=initial(fake_atom.icon_state),
+// starting_config=initial(fake_atom.greyscale_config),
+// starting_colors=initial(fake_atom.greyscale_colors)
+// )
+// menu.ui_interact(usr)
+
+// /obj/machinery/vending/proc/vend_greyscale(list/params, datum/greyscale_modify_menu/menu)
+// if(usr != menu.user)
+// return
+// if(!menu.target.can_interact(usr))
+// return
+// vend(params, menu.split_colors)
+
+/obj/machinery/vending/proc/vend(list/params, list/greyscale_colors)
+ . = TRUE
+ if(!can_vend(usr))
+ return
+ vend_ready = FALSE //One thing at a time!!
+ var/datum/data/vending_product/R = locate(params["ref"])
+ var/list/record_to_check = product_records + coin_records
+ if(extended_inventory)
+ record_to_check = product_records + coin_records + hidden_records
+ if(!R || !istype(R) || !R.product_path)
+ vend_ready = TRUE
+ return
+ var/price_to_use = default_price
+ if(R.custom_price)
+ price_to_use = R.custom_price
+ if(R in hidden_records)
+ if(!extended_inventory)
vend_ready = TRUE
+ return
+ else if (!(R in record_to_check))
+ vend_ready = TRUE
+ message_admins("Vending machine exploit attempted by [ADMIN_LOOKUPFLW(usr)]!")
+ return
+ if (R.amount <= 0)
+ say("Sold out of [R.name].")
+ flick(icon_deny,src)
+ vend_ready = TRUE
+ return
+ if(onstation)
+ var/obj/item/card/id/C
+ if(isliving(usr))
+ var/mob/living/L = usr
+ C = L.get_idcard(TRUE)
+ if(!C)
+ say("No card found.")
+ flick(icon_deny,src)
+ vend_ready = TRUE
+ return
+ else if (!C.registered_account)
+ say("No account found.")
+ flick(icon_deny,src)
+ vend_ready = TRUE
+ return
+ else if(!C.registered_account.account_job)
+ say("Departmental accounts have been blacklisted from personal expenses due to embezzlement.")
+ flick(icon_deny, src)
+ vend_ready = TRUE
+ return
+ // else if(age_restrictions && R.age_restricted && (!C.registered_age || C.registered_age < AGE_MINOR))
+ // say("You are not of legal age to purchase [R.name].")
+ // if(!(usr in GLOB.narcd_underages))
+ // Radio.set_frequency(FREQ_SECURITY)
+ // Radio.talk_into(src, "SECURITY ALERT: Underaged crewmember [usr] recorded attempting to purchase [R.name] in [get_area(src)]. Please watch for substance abuse.", FREQ_SECURITY)
+ // GLOB.narcd_underages += usr
+ // flick(icon_deny,src)
+ // vend_ready = TRUE
+ // return
+ var/datum/bank_account/account = C.registered_account
+ // || cost_multiplier_per_dept[account.account_job.access] != null
+ // var/list/difflist = uniquemergelist(cost_multiplier_per_dept, account.account_job.access)
+ if(account.account_job && account.account_job.paycheck_department == payment_department || (cost_multiplier_per_dept.len > 0 && (cost_multiplier_per_dept & account.account_job.access) > 0))
+ price_to_use = 0 // it's free shut up
+ if(coin_records.Find(R) || hidden_records.Find(R))
+ price_to_use = R.custom_premium_price ? R.custom_premium_price : extra_price
+ if(price_to_use && !account.adjust_money(-price_to_use))
+ say("You do not possess the funds to purchase [R.name].")
+ flick(icon_deny,src)
+ vend_ready = TRUE
+ return
+ var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department)
+ if(D)
+ D.adjust_money(price_to_use)
+ SSblackbox.record_feedback("amount", "vending_spent", price_to_use)
+ // log_econ("[price_to_use] credits were inserted into [src] by [D.account_holder] to buy [R].")
+ if(last_shopper != REF(usr) || purchase_message_cooldown < world.time)
+ say("Thank you for shopping with [src]!")
+ purchase_message_cooldown = world.time + 5 SECONDS
+ //This is not the best practice, but it's safe enough here since the chances of two people using a machine with the same ref in 5 seconds is fuck low
+ last_shopper = REF(usr)
+ use_power(5)
+ if(icon_vend) //Show the vending animation if needed
+ flick(icon_vend,src)
+ playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3)
+ var/obj/item/vended_item = new R.product_path(get_turf(src))
+ // if(greyscale_colors)
+ // vended_item.set_greyscale(colors=greyscale_colors)
+ R.amount--
+ if(usr.CanReach(src) && usr.put_in_hands(vended_item))
+ to_chat(usr, span_notice("You take [R.name] out of the slot."))
+ else
+ to_chat(usr, span_warning("[capitalize(R.name)] falls onto the floor!"))
+ SSblackbox.record_feedback("nested tally", "vending_machine_usage", 1, list("[type]", "[R.product_path]"))
+ vend_ready = TRUE
-/obj/machinery/vending/process()
+/obj/machinery/vending/process(delta_time)
if(stat & (BROKEN|NOPOWER))
return PROCESS_KILL
if(!active)
@@ -840,21 +934,21 @@ GLOBAL_LIST_EMPTY(vending_products)
seconds_electrified--
//Pitch to the people! Really sell it!
- if(last_slogan + slogan_delay <= world.time && slogan_list.len > 0 && !shut_up && prob(5))
+ if(last_slogan + slogan_delay <= world.time && slogan_list.len > 0 && !shut_up && DT_PROB(2.5, delta_time))
var/slogan = pick(slogan_list)
speak(slogan)
last_slogan = world.time
- if(shoot_inventory && prob(shoot_inventory_chance))
+ if(shoot_inventory && DT_PROB(shoot_inventory_chance, delta_time))
throw_item()
/**
- * Speak the given message verbally
- *
- * Checks if the machine is powered and the message exists
- *
- * Arguments:
- * * message - the message to speak
- */
+ * Speak the given message verbally
+ *
+ * Checks if the machine is powered and the message exists
+ *
+ * Arguments:
+ * * message - the message to speak
+ */
/obj/machinery/vending/proc/speak(message)
if(stat & (BROKEN|NOPOWER))
return
@@ -863,25 +957,6 @@ GLOBAL_LIST_EMPTY(vending_products)
say(message)
-/**
- * Gets the best discount from a given ID card, comparing its access and paycheck depart with cost_multiplier_per_dept.
- * It only applies to the regular selection, not premium or contraband. And a bank account is still required.
- * But it can also be used to charge more to certain departments or accesses. :)
- *
- * Arguments:
- * * list/dept_access_list - the list to compare
- */
-/obj/machinery/vending/proc/get_best_discount(obj/item/card/id/C)
- var/list/discounts = NUMLIST2TEXTLIST(C.GetAccess())
- if(C.registered_account?.account_job)
- discounts += C.registered_account.account_job.paycheck_department
- discounts &= cost_multiplier_per_dept
- if(!length(discounts))
- return 1
- . = INFINITY
- for(var/k in discounts)
- . = min(cost_multiplier_per_dept[k], .)
-
/obj/machinery/vending/power_change()
. = ..()
if(powered())
@@ -889,22 +964,24 @@ GLOBAL_LIST_EMPTY(vending_products)
//Somebody cut an important wire and now we're following a new definition of "pitch."
/**
- * Throw an item from our internal inventory out in front of us
- *
- * This is called when we are hacked, it selects a random product from the records that has an amount > 0
- * This item is then created and tossed out in front of us with a visible message
- */
+ * Throw an item from our internal inventory out in front of us
+ *
+ * This is called when we are hacked, it selects a random product from the records that has an amount > 0
+ * This item is then created and tossed out in front of us with a visible message
+ */
/obj/machinery/vending/proc/throw_item()
var/obj/throw_item = null
- var/mob/living/target = locate() in view(7, src)
+ var/mob/living/target = locate() in view(7,src)
if(!target)
return FALSE
+
for(var/datum/data/vending_product/R in shuffle(product_records))
if(R.amount <= 0) //Try to use a record that actually has something to dump.
continue
var/dump_path = R.product_path
if(!dump_path)
continue
+
R.amount--
throw_item = new dump_path(loc)
break
@@ -914,30 +991,30 @@ GLOBAL_LIST_EMPTY(vending_products)
pre_throw(throw_item)
throw_item.throw_at(target, 16, 3)
- visible_message("[src] launches [throw_item] at [target]!")
+ visible_message(span_danger("[src] launches [throw_item] at [target]!"))
return TRUE
/**
- * A callback called before an item is tossed out
- *
- * Override this if you need to do any special case handling
- *
- * Arguments:
- * * I - obj/item being thrown
- */
+ * A callback called before an item is tossed out
+ *
+ * Override this if you need to do any special case handling
+ *
+ * Arguments:
+ * * I - obj/item being thrown
+ */
/obj/machinery/vending/proc/pre_throw(obj/item/I)
return
/**
- * Shock the passed in user
- *
- * This checks we have power and that the passed in prob is passed, then generates some sparks
- * and calls electrocute_mob on the user
- *
- * Arguments:
- * * user - the user to shock
- * * prb - probability the shock happens
- */
+ * Shock the passed in user
+ *
+ * This checks we have power and that the passed in prob is passed, then generates some sparks
+ * and calls electrocute_mob on the user
+ *
+ * Arguments:
+ * * user - the user to shock
+ * * prb - probability the shock happens
+ */
/obj/machinery/vending/proc/shock(mob/living/user, prb)
- if(!istype(user) || stat & (BROKEN|NOPOWER)) // unpowered, no shock
+ if(!istype(user) || stat & (BROKEN|NOPOWER)) // unpowered, no shock
return FALSE
if(!prob(prb))
return FALSE
@@ -948,24 +1025,39 @@ GLOBAL_LIST_EMPTY(vending_products)
else
return FALSE
/**
- * Are we able to load the item passed in
- *
- * Arguments:
- * * I - the item being loaded
- * * user - the user doing the loading
- */
+ * Are we able to load the item passed in
+ *
+ * Arguments:
+ * * I - the item being loaded
+ * * user - the user doing the loading
+ */
/obj/machinery/vending/proc/canLoadItem(obj/item/I, mob/user)
return FALSE
-/obj/machinery/vending/onTransitZ()
- return
+/obj/machinery/vending/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
+ . = ..()
+ var/mob/living/L = AM
+ if(tilted || !istype(L) || !prob(20 * (throwingdatum.speed - L.throw_speed))) // hulk throw = +20%, neckgrab throw = +20%
+ return
+
+ tilt(L)
+
+/obj/machinery/vending/attack_tk_grab(mob/user)
+ to_chat(user, span_warning("[src] seems to resist your mental grasp!"))
+
+///Crush the mob that the vending machine got thrown at
+/obj/machinery/vending/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ if(isliving(hit_atom))
+ tilt(fatty=hit_atom)
+ return ..()
/obj/machinery/vending/custom
name = "Custom Vendor"
- icon_state = "robotics"
- icon_deny = "robotics-deny"
+ icon_state = "custom"
+ icon_deny = "custom-deny"
max_integrity = 400
payment_department = NO_FREEBIES
+ light_mask = "custom-light-mask"
refill_canister = /obj/item/vending_refill/custom
/// where the money is sent
var/datum/bank_account/private_a
@@ -976,21 +1068,23 @@ GLOBAL_LIST_EMPTY(vending_products)
/obj/machinery/vending/custom/compartmentLoadAccessCheck(mob/user)
. = FALSE
- var/mob/living/carbon/human/H
- var/obj/item/card/id/C
- if(ishuman(user))
- H = user
- C = H.get_idcard(FALSE)
- if(C?.registered_account && C.registered_account == private_a)
- return TRUE
+ if(!isliving(user))
+ return FALSE
+ var/mob/living/L = user
+ var/obj/item/card/id/C = L.get_idcard(FALSE)
+ if(C?.registered_account && C.registered_account == private_a)
+ return TRUE
/obj/machinery/vending/custom/canLoadItem(obj/item/I, mob/user)
. = FALSE
+ if(I.flags_1 & HOLOGRAM_1)
+ say("This vendor cannot accept nonexistent items.")
+ return
if(loaded_items >= max_loaded_items)
say("There are too many items in stock.")
return
if(istype(I, /obj/item/stack))
- say("Loose items may cause problems, try use it inside wrapping paper.")
+ say("Loose items may cause problems, try to use it inside wrapping paper.")
return
if(I.custom_price)
return TRUE
@@ -1004,19 +1098,21 @@ GLOBAL_LIST_EMPTY(vending_products)
var/base64
var/price = 0
for(var/obj/T in contents)
- if(T.name == O)
+ if(format_text(T.name) == O)
price = T.custom_price
if(!base64)
if(base64_cache[T.type])
base64 = base64_cache[T.type]
else
- base64 = icon2base64(icon(T.icon, T.icon_state))
+ base64 = icon2base64(getFlatIcon(T, no_anim=TRUE))
base64_cache[T.type] = base64
break
var/list/data = list(
name = O,
price = price,
- img = base64
+ img = base64,
+ amount = vending_machine_input[O],
+ colorable = FALSE
)
.["vending_machine_input"] += list(data)
@@ -1032,64 +1128,63 @@ GLOBAL_LIST_EMPTY(vending_products)
var/N = params["item"]
var/obj/S
vend_ready = FALSE
- if(ishuman(usr))
- var/mob/living/carbon/human/H = usr
- var/obj/item/card/id/C = H.get_idcard(TRUE)
-
- if(!C)
- say("No card found.")
- flick(icon_deny,src)
+ var/obj/item/card/id/C
+ if(isliving(usr))
+ var/mob/living/L = usr
+ C = L.get_idcard(TRUE)
+ if(!C)
+ say("No card found.")
+ flick(icon_deny,src)
+ vend_ready = TRUE
+ return
+ else if (!C.registered_account)
+ say("No account found.")
+ flick(icon_deny,src)
+ vend_ready = TRUE
+ return
+ var/datum/bank_account/account = C.registered_account
+ for(var/obj/O in contents)
+ if(format_text(O.name) == N)
+ S = O
+ break
+ if(S)
+ if(compartmentLoadAccessCheck(usr))
+ vending_machine_input[N] = max(vending_machine_input[N] - 1, 0)
+ S.forceMove(drop_location())
+ loaded_items--
+ use_power(5)
vend_ready = TRUE
+ updateUsrDialog()
return
- else if (!C.registered_account)
- say("No account found.")
- flick(icon_deny,src)
+ if(account.has_money(S.custom_price))
+ account.adjust_money(-S.custom_price)
+ var/datum/bank_account/owner = private_a
+ if(owner)
+ owner.adjust_money(S.custom_price)
+ SSblackbox.record_feedback("amount", "vending_spent", S.custom_price)
+ // log_econ("[S.custom_price] credits were spent on [src] buying a [S] by [owner.account_holder], owned by [private_a.account_holder].")
+ vending_machine_input[N] = max(vending_machine_input[N] - 1, 0)
+ S.forceMove(drop_location())
+ loaded_items--
+ use_power(5)
+ if(last_shopper != REF(usr) || purchase_message_cooldown < world.time)
+ say("Thank you for buying local and purchasing [S]!")
+ purchase_message_cooldown = world.time + 5 SECONDS
+ last_shopper = REF(usr)
vend_ready = TRUE
+ updateUsrDialog()
return
- var/datum/bank_account/account = C.registered_account
- for(var/obj/O in contents)
- if(O.name == N)
- S = O
- break
- if(S)
- if(compartmentLoadAccessCheck(usr))
- vending_machine_input[N] = max(vending_machine_input[N] - 1, 0)
- S.forceMove(drop_location())
- loaded_items--
- use_power(5)
- vend_ready = TRUE
- updateUsrDialog()
- return
- if(account.has_money(S.custom_price))
- account.adjust_money(-S.custom_price)
- var/datum/bank_account/owner = private_a
- if(owner)
- owner.adjust_money(S.custom_price)
- vending_machine_input[N] = max(vending_machine_input[N] - 1, 0)
- S.forceMove(drop_location())
- loaded_items--
- use_power(5)
- if(last_shopper != usr || purchase_message_cooldown < world.time)
- say("Thank you for buying local and purchasing [S]!")
- purchase_message_cooldown = world.time + 5 SECONDS
- last_shopper = usr
- vend_ready = TRUE
- updateUsrDialog()
- return
- else
- say("You do not possess the funds to purchase this.")
+ else
+ say("You do not possess the funds to purchase this.")
vend_ready = TRUE
/obj/machinery/vending/custom/attackby(obj/item/I, mob/user, params)
- if(!private_a)
- var/mob/living/carbon/human/H
- var/obj/item/card/id/C
- if(ishuman(user))
- H = user
- C = H.get_idcard(TRUE)
- if(C?.registered_account)
- private_a = C.registered_account
- say("[src] has been linked to [C].")
+ if(!private_a && isliving(user))
+ var/mob/living/L = user
+ var/obj/item/card/id/C = L.get_idcard(TRUE)
+ if(C?.registered_account)
+ private_a = C.registered_account
+ say("\The [src] has been linked to [C].")
if(compartmentLoadAccessCheck(user))
if(istype(I, /obj/item/pen))
@@ -1099,15 +1194,6 @@ GLOBAL_LIST_EMPTY(vending_products)
last_slogan = world.time + rand(0, slogan_delay)
return
- if(canLoadItem(I))
- loadingAttempt(I,user)
- updateUsrDialog()
- return
-
- if(panel_open && is_wire_tool(I))
- wires.interact(user)
- return
-
return ..()
/obj/machinery/vending/custom/crowbar_act(mob/living/user, obj/item/I)
@@ -1119,7 +1205,7 @@ GLOBAL_LIST_EMPTY(vending_products)
if(T)
for(var/obj/item/I in contents)
I.forceMove(T)
- explosion(T, -1, 0, 3)
+ explosion(src, devastation_range = -1, light_impact_range = 3)
return ..()
/obj/machinery/vending/custom/unbreakable
@@ -1141,8 +1227,8 @@ GLOBAL_LIST_EMPTY(vending_products)
var/price = 1
/obj/item/price_tagger/attack_self(mob/user)
- price = max(1, round(tgui_input_num(user,"set price","price"), 1))
- to_chat(user, " The [src] will now give things a [price] cr tag.")
+ price = max(1, round(input(user,"set price","price") as num|null, 1))
+ to_chat(user, span_notice(" The [src] will now give things a [price] cr tag."))
/obj/item/price_tagger/afterattack(atom/target, mob/user, proximity)
. = ..()
@@ -1151,4 +1237,23 @@ GLOBAL_LIST_EMPTY(vending_products)
if(isitem(target))
var/obj/item/I = target
I.custom_price = price
- to_chat(user, "You set the price of [I] to [price] cr.")
+ to_chat(user, span_notice("You set the price of [I] to [price] cr."))
+
+/obj/machinery/vending/custom/greed //name and like decided by the spawn
+ icon_state = "greed"
+ icon_deny = "greed-deny"
+ light_mask = "greed-light-mask"
+ custom_materials = list(/datum/material/gold = MINERAL_MATERIAL_AMOUNT * 5)
+
+/obj/machinery/vending/custom/greed/Initialize(mapload)
+ . = ..()
+ //starts in a state where you can move it
+ panel_open = TRUE
+ set_anchored(FALSE)
+ add_overlay("[initial(icon_state)]-panel")
+ //and references the deity
+ name = "[GLOB.deity]'s Consecrated Vendor"
+ desc = "A vending machine created by [GLOB.deity]."
+ slogan_list = list("[GLOB.deity] says: It's your divine right to buy!")
+ add_filter("vending_outline", 9, list("type" = "outline", "color" = COLOR_VERY_SOFT_YELLOW))
+ add_filter("vending_rays", 10, list("type" = "rays", "size" = 35, "color" = COLOR_VIVID_YELLOW))
diff --git a/code/modules/vending/assist.dm b/code/modules/vending/assist.dm
index 29d1e760d4..9ab384111b 100644
--- a/code/modules/vending/assist.dm
+++ b/code/modules/vending/assist.dm
@@ -1,4 +1,9 @@
/obj/machinery/vending/assist
+ name = "\improper Part-Mart"
+ desc = "All the finest of miscellaneous electronics one could ever need! Not responsible for any injuries caused by reckless misuse of parts."
+ // icon_state = "parts"
+ // icon_deny = "parts-deny"
+
products = list(/obj/item/assembly/prox_sensor = 7,
/obj/item/assembly/igniter = 6,
/obj/item/assembly/playback = 4,
@@ -15,13 +20,13 @@
premium = list(/obj/item/stock_parts/cell/upgraded/plus = 2,
/obj/item/flashlight/lantern = 2,
/obj/item/beacon = 2)
- product_ads = "Only the finest!;Have some tools.;The most robust equipment.;The finest gear in space!"
- armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
refill_canister = /obj/item/vending_refill/assist
- resistance_flags = FIRE_PROOF
+ product_ads = "Only the finest!;Have some tools.;The most robust equipment.;The finest gear in space!"
default_price = PRICE_REALLY_CHEAP
extra_price = PRICE_ALMOST_CHEAP
payment_department = NO_FREEBIES
+ // light_mask = "parts-light-mask"
/obj/item/vending_refill/assist
- icon_state = "refill_engi"
+ machine_name = "Part-Mart"
+ icon_state = "refill_parts"
diff --git a/code/modules/vending/autodrobe.dm b/code/modules/vending/autodrobe.dm
index 264d262a1d..f1fbb95ff2 100644
--- a/code/modules/vending/autodrobe.dm
+++ b/code/modules/vending/autodrobe.dm
@@ -3,32 +3,39 @@
desc = "A vending machine for costumes."
icon_state = "theater"
icon_deny = "theater-deny"
+ req_access = list(ACCESS_THEATRE)
product_slogans = "Dress for success!;Suited and booted!;It's show time!;Why leave style up to fate? Use AutoDrobe!"
vend_reply = "Thank you for using AutoDrobe!"
products = list(/obj/item/clothing/suit/chickensuit = 1,
- /obj/item/clothing/head/chicken = 1,
- /obj/item/clothing/under/costume/gladiator = 1,
- /obj/item/clothing/head/helmet/gladiator = 1,
- /obj/item/clothing/under/rank/captain/suit = 1,
- /obj/item/clothing/head/flatcap = 1,
- /obj/item/clothing/suit/toggle/labcoat/mad = 1,
- /obj/item/clothing/shoes/jackboots = 1,
- /obj/item/clothing/under/costume/schoolgirl = 1,
- /obj/item/clothing/under/costume/schoolgirl/red = 1,
- /obj/item/clothing/under/costume/schoolgirl/green = 1,
- /obj/item/clothing/under/costume/schoolgirl/orange = 1,
- /obj/item/clothing/head/kitty = 1,
- /obj/item/clothing/under/dress/skirt = 1,
- /obj/item/clothing/head/beret = 1,
- /obj/item/clothing/accessory/waistcoat = 1,
- /obj/item/clothing/under/suit/black = 1,
- /obj/item/clothing/head/that = 1,
- /obj/item/clothing/under/costume/kilt = 1,
- /obj/item/clothing/head/beret = 1,
- /obj/item/clothing/accessory/waistcoat = 1,
- /obj/item/clothing/glasses/monocle =1,
- /obj/item/clothing/head/bowler = 1,
- /obj/item/cane = 1,
+ /obj/item/clothing/head/chicken = 1,
+ /obj/item/clothing/under/rank/civilian/clown/blue = 1,
+ /obj/item/clothing/under/rank/civilian/clown/green = 1,
+ /obj/item/clothing/under/rank/civilian/clown/yellow = 1,
+ /obj/item/clothing/under/rank/civilian/clown/orange = 1,
+ /obj/item/clothing/under/rank/civilian/clown/purple = 1,
+ /obj/item/clothing/under/costume/gladiator = 1,
+ /obj/item/clothing/head/helmet/gladiator = 1,
+ /obj/item/clothing/under/rank/captain/suit = 1,
+ /obj/item/clothing/under/rank/captain/suit/skirt = 1,
+ /obj/item/clothing/head/flatcap = 1,
+ /obj/item/clothing/suit/toggle/labcoat/mad = 1,
+ /obj/item/clothing/shoes/jackboots = 1,
+ /obj/item/clothing/under/costume/schoolgirl = 1,
+ /obj/item/clothing/under/costume/schoolgirl/red = 1,
+ /obj/item/clothing/under/costume/schoolgirl/green = 1,
+ /obj/item/clothing/under/costume/schoolgirl/orange = 1,
+ /obj/item/clothing/head/kitty = 1,
+ /obj/item/clothing/under/dress/skirt = 1,
+ /obj/item/clothing/head/beret = 1,
+ /obj/item/clothing/accessory/waistcoat = 1,
+ /obj/item/clothing/under/suit/black = 1,
+ /obj/item/clothing/head/that = 1,
+ /obj/item/clothing/under/costume/kilt = 1,
+ /obj/item/clothing/head/beret = 3,
+ /obj/item/clothing/accessory/waistcoat = 1,
+ /obj/item/clothing/glasses/monocle =1,
+ /obj/item/clothing/head/bowler = 1,
+ /obj/item/cane = 1,
/obj/item/clothing/under/rank/civilian/victorian_redsleeves = 1,
/obj/item/clothing/under/rank/civilian/victorian_redvest = 1,
/obj/item/clothing/under/rank/civilian/victorian_vest = 1,
@@ -39,110 +46,111 @@
/obj/item/clothing/under/rank/civilian/victorianblackdress = 1,
/obj/item/clothing/suit/vickyblack =1,
/obj/item/clothing/under/rank/civilian/dutch = 2,
- /obj/item/clothing/under/suit/sl = 1,
- /obj/item/clothing/mask/fakemoustache = 1,
- /obj/item/clothing/suit/bio_suit/plaguedoctorsuit = 1,
- /obj/item/clothing/head/plaguedoctorhat = 1,
- /obj/item/clothing/mask/gas/plaguedoctor = 1,
- /obj/item/clothing/suit/toggle/owlwings = 1,
- /obj/item/clothing/under/costume/owl = 1,
- /obj/item/clothing/mask/gas/owl_mask = 1,
- /obj/item/clothing/suit/toggle/owlwings/griffinwings = 1,
- /obj/item/clothing/under/costume/griffin = 1,
- /obj/item/clothing/shoes/griffin = 1,
- /obj/item/clothing/head/griffin = 1,
- /obj/item/clothing/suit/apron = 1,
- /obj/item/clothing/under/suit/waiter = 1,
- /obj/item/clothing/suit/jacket/miljacket = 1,
- /obj/item/clothing/under/costume/pirate = 1,
- /obj/item/clothing/suit/pirate = 1,
- /obj/item/clothing/head/pirate = 1,
- /obj/item/clothing/head/bandana = 1,
- /obj/item/clothing/head/bandana = 1,
- /obj/item/clothing/under/costume/soviet = 1,
- /obj/item/clothing/head/ushanka = 1,
- /obj/item/clothing/suit/imperium_monk = 1,
- /obj/item/clothing/mask/gas/cyborg = 1,
- /obj/item/clothing/suit/chaplain/holidaypriest = 1,
- /obj/item/clothing/head/wizard/marisa/fake = 1,
- /obj/item/clothing/suit/wizrobe/marisa/fake = 1,
- /obj/item/clothing/under/dress/sundress = 1,
- /obj/item/clothing/head/witchwig = 1,
- /obj/item/staff/broom = 1,
- /obj/item/clothing/suit/wizrobe/fake = 1,
- /obj/item/clothing/head/wizard/fake = 1,
- /obj/item/staff = 3,
- /obj/item/clothing/under/rank/civilian/mime/skirt = 1,
- /obj/item/clothing/under/rank/captain/suit/skirt = 1,
+ /obj/item/clothing/under/suit/sl = 1,
+ /obj/item/clothing/mask/fakemoustache = 1,
+ /obj/item/clothing/suit/bio_suit/plaguedoctorsuit = 1,
+ /obj/item/clothing/head/plaguedoctorhat = 1,
+ /obj/item/clothing/mask/gas/plaguedoctor = 1,
+ /obj/item/clothing/suit/toggle/owlwings = 1,
+ /obj/item/clothing/under/costume/owl = 1,
+ /obj/item/clothing/mask/gas/owl_mask = 1,
+ /obj/item/clothing/suit/toggle/owlwings/griffinwings = 1,
+ /obj/item/clothing/under/costume/griffin = 1,
+ /obj/item/clothing/shoes/griffin = 1,
+ /obj/item/clothing/head/griffin = 1,
+ /obj/item/clothing/suit/apron = 1,
+ /obj/item/clothing/under/suit/waiter = 1,
+ /obj/item/clothing/suit/jacket/miljacket = 1,
+ /obj/item/clothing/under/costume/pirate = 1,
+ /obj/item/clothing/suit/pirate = 1,
+ /obj/item/clothing/head/pirate = 1,
+ /obj/item/clothing/head/bandana = 1,
+ /obj/item/clothing/head/bandana = 1,
+ /obj/item/clothing/under/costume/soviet = 1,
+ /obj/item/clothing/head/ushanka = 1,
+ /obj/item/clothing/suit/imperium_monk = 1,
+ /obj/item/clothing/mask/gas/cyborg = 1,
+ /obj/item/clothing/suit/chaplain/holidaypriest = 1,
+ /obj/item/clothing/head/wizard/marisa/fake = 1,
+ /obj/item/clothing/suit/wizrobe/marisa/fake = 1,
+ /obj/item/clothing/under/dress/sundress = 1,
+ /obj/item/clothing/head/witchwig = 1,
+ /obj/item/staff/broom = 1,
+ /obj/item/clothing/suit/wizrobe/fake = 1,
+ /obj/item/clothing/head/wizard/fake = 1,
+ /obj/item/staff = 3,
/obj/item/clothing/mask/gas/clown_hat/sexy = 1,
- /obj/item/clothing/under/rank/civilian/clown/sexy = 1,
+ /obj/item/clothing/under/rank/civilian/clown/sexy = 1,
/obj/item/clothing/mask/gas/mime/sexy = 1,
- /obj/item/clothing/under/rank/civilian/mime/sexy = 1,
- /obj/item/clothing/mask/rat/bat = 1,
- /obj/item/clothing/mask/rat/bee = 1,
- /obj/item/clothing/mask/rat/bear = 1,
- /obj/item/clothing/mask/rat/raven = 1,
- /obj/item/clothing/mask/rat/jackal = 1,
- /obj/item/clothing/mask/rat/fox = 1,
- /obj/item/clothing/mask/frog = 1,
- /obj/item/clothing/mask/rat/tribal = 1,
- /obj/item/clothing/mask/rat = 1,
- /obj/item/clothing/suit/apron/overalls = 1,
- /obj/item/clothing/head/rabbitears =1,
- /obj/item/clothing/head/sombrero = 1,
- /obj/item/clothing/head/sombrero/green = 1,
- /obj/item/clothing/suit/poncho = 1,
- /obj/item/clothing/suit/poncho/green = 1,
- /obj/item/clothing/suit/poncho/red = 1,
- /obj/item/clothing/head/maid = 1,
- /obj/item/clothing/under/costume/maid = 1,
- /obj/item/clothing/under/rank/civilian/janitor/maid = 1,
+ /obj/item/clothing/under/rank/civilian/mime/sexy = 1,
+ /obj/item/clothing/under/rank/civilian/mime/skirt = 1,
+ /obj/item/clothing/mask/rat/bat = 1,
+ /obj/item/clothing/mask/rat/bee = 1,
+ /obj/item/clothing/mask/rat/bear = 1,
+ /obj/item/clothing/mask/rat/raven = 1,
+ /obj/item/clothing/mask/rat/jackal = 1,
+ /obj/item/clothing/mask/rat/fox = 1,
+ /obj/item/clothing/mask/frog = 1,
+ /obj/item/clothing/mask/rat/tribal = 1,
+ /obj/item/clothing/mask/rat = 1,
+ /obj/item/clothing/suit/apron/overalls = 1,
+ /obj/item/clothing/head/rabbitears =1,
+ /obj/item/clothing/head/sombrero = 1,
+ /obj/item/clothing/head/sombrero/green = 1,
+ /obj/item/clothing/suit/poncho = 1,
+ /obj/item/clothing/suit/poncho/green = 1,
+ /obj/item/clothing/suit/poncho/red = 1,
+ /obj/item/clothing/head/maid = 1,
+ /obj/item/clothing/under/costume/maid = 1,
+ /obj/item/clothing/under/rank/civilian/janitor/maid = 1,
/obj/item/clothing/gloves/evening = 1,
- /obj/item/clothing/glasses/cold=1,
- /obj/item/clothing/glasses/heat=1,
- /obj/item/clothing/suit/whitedress = 1,
- /obj/item/clothing/under/rank/civilian/clown/jester = 1,
- /obj/item/clothing/head/jester = 1,
- /obj/item/clothing/under/costume/villain = 1,
- /obj/item/clothing/shoes/singery = 1,
- /obj/item/clothing/under/costume/singer/yellow = 1,
- /obj/item/clothing/shoes/singerb = 1,
- /obj/item/clothing/under/costume/singer/blue = 1,
- /obj/item/clothing/suit/hooded/carp_costume = 1,
- /obj/item/clothing/suit/hooded/ian_costume = 1,
- /obj/item/clothing/suit/hooded/bee_costume = 1,
- /obj/item/clothing/suit/snowman = 1,
- /obj/item/clothing/head/snowman = 1,
- /obj/item/clothing/mask/joy = 1,
- /obj/item/clothing/head/cueball = 1,
- /obj/item/clothing/under/suit/white_on_white = 1,
- /obj/item/clothing/under/costume/sailor = 1,
- /obj/item/clothing/ears/headphones = 2,
- /obj/item/clothing/head/wig/random = 3,
- /obj/item/clothing/suit/ran = 2,
- /obj/item/clothing/head/ran = 2,
- /obj/item/clothing/mask/gas/timidcostume = 3,
- /obj/item/clothing/suit/hooded/wintercoat/timidcostume = 3,
- /obj/item/clothing/shoes/timidcostume = 3,
- /obj/item/clothing/mask/gas/timidcostume/man = 3,
- /obj/item/clothing/suit/hooded/wintercoat/timidcostume/man = 3,
- /obj/item/clothing/shoes/timidcostume/man = 3,
- )
+ /obj/item/clothing/glasses/cold=1,
+ /obj/item/clothing/glasses/heat=1,
+ /obj/item/clothing/suit/whitedress = 1,
+ /obj/item/clothing/under/rank/civilian/clown/jester = 1,
+ /obj/item/clothing/head/jester = 1,
+ /obj/item/clothing/under/costume/villain = 1,
+ /obj/item/clothing/shoes/singery = 1,
+ /obj/item/clothing/under/costume/singer/yellow = 1,
+ /obj/item/clothing/shoes/singerb = 1,
+ /obj/item/clothing/under/costume/singer/blue = 1,
+ /obj/item/clothing/suit/hooded/carp_costume = 1,
+ /obj/item/clothing/suit/hooded/ian_costume = 1,
+ /obj/item/clothing/suit/hooded/bee_costume = 1,
+ /obj/item/clothing/suit/snowman = 1,
+ /obj/item/clothing/head/snowman = 1,
+ /obj/item/clothing/mask/joy = 1,
+ /obj/item/clothing/head/cueball = 1,
+ /obj/item/clothing/under/suit/white_on_white = 1,
+ /obj/item/clothing/under/costume/sailor = 1,
+ /obj/item/clothing/ears/headphones = 2,
+ /obj/item/clothing/head/wig/random = 3,
+ /obj/item/clothing/suit/ran = 2,
+ /obj/item/clothing/head/ran = 2,
+ /obj/item/clothing/mask/gas/timidcostume = 3,
+ /obj/item/clothing/suit/hooded/wintercoat/timidcostume = 3,
+ /obj/item/clothing/shoes/timidcostume = 3,
+ /obj/item/clothing/mask/gas/timidcostume/man = 3,
+ /obj/item/clothing/suit/hooded/wintercoat/timidcostume/man = 3,
+ /obj/item/clothing/shoes/timidcostume/man = 3,
+ )
contraband = list(/obj/item/clothing/suit/judgerobe = 1,
- /obj/item/clothing/head/powdered_wig = 1,
- /obj/item/gun/magic/wand = 2,
- /obj/item/clothing/glasses/sunglasses/garb = 2,
- /obj/item/clothing/glasses/sunglasses/blindfold = 1,
- /obj/item/clothing/mask/muzzle = 2,
- /obj/item/clothing/under/syndicate/camo/cosmetic = 3)
+ /obj/item/clothing/head/powdered_wig = 1,
+ /obj/item/gun/magic/wand = 2,
+ /obj/item/clothing/glasses/sunglasses/garb = 2,
+ /obj/item/clothing/glasses/sunglasses/blindfold = 1,
+ /obj/item/clothing/mask/muzzle = 2,
+ /obj/item/clothing/under/syndicate/camo/cosmetic = 3
+ )
premium = list(/obj/item/clothing/suit/pirate/captain = 2,
- /obj/item/clothing/head/pirate/captain = 2,
- /obj/item/clothing/head/helmet/roman/fake = 1,
- /obj/item/clothing/head/helmet/roman/legionnaire/fake = 1,
- /obj/item/clothing/under/costume/roman = 1,
- /obj/item/clothing/shoes/roman = 1,
- /obj/item/shield/riot/roman/fake = 1,
- /obj/item/skub = 1,
+ /obj/item/clothing/head/pirate/captain = 2,
+ /obj/item/clothing/under/rank/civilian/clown/rainbow = 1,
+ /obj/item/clothing/head/helmet/roman/fake = 1,
+ /obj/item/clothing/head/helmet/roman/legionnaire/fake = 1,
+ /obj/item/clothing/under/costume/roman = 1,
+ /obj/item/clothing/shoes/roman = 1,
+ /obj/item/shield/riot/roman/fake = 1,
+ /obj/item/skub = 1,
/obj/item/clothing/under/costume/lobster = 1,
/obj/item/clothing/head/lobsterhat = 1,
/obj/item/clothing/head/drfreezehat = 1,
@@ -157,11 +165,11 @@
/obj/item/clothing/head/christmashat = 3,
/obj/item/clothing/head/christmashatg = 3,
/obj/item/clothing/under/costume/drfreeze = 1)
-
refill_canister = /obj/item/vending_refill/autodrobe
default_price = PRICE_ALMOST_CHEAP
extra_price = PRICE_ALMOST_EXPENSIVE
payment_department = ACCOUNT_SRV
+ light_mask="theater-light-mask"
/obj/machinery/vending/autodrobe/Initialize()
. = ..()
@@ -170,6 +178,10 @@
/obj/machinery/vending/autodrobe/canLoadItem(obj/item/I,mob/user)
return (I.type in products)
+/obj/machinery/vending/autodrobe/all_access
+ desc = "A vending machine for costumes. This model appears to have no access restrictions."
+ req_access = null
+
/obj/item/vending_refill/autodrobe
machine_name = "AutoDrobe"
icon_state = "refill_costume"
diff --git a/code/modules/vending/boozeomat.dm b/code/modules/vending/boozeomat.dm
index 87f2a0940b..df57dadd2e 100644
--- a/code/modules/vending/boozeomat.dm
+++ b/code/modules/vending/boozeomat.dm
@@ -41,13 +41,15 @@
premium = list(/obj/item/reagent_containers/glass/bottle/ethanol = 4,
/obj/item/reagent_containers/food/drinks/bottle/champagne = 5,
/obj/item/reagent_containers/food/drinks/bottle/trappist = 5)
+
product_slogans = "I hope nobody asks me for a bloody cup o' tea...;Alcohol is humanity's friend. Would you abandon a friend?;Quite delighted to serve you!;Is nobody thirsty on this station?"
product_ads = "Drink up!;Booze is good for you!;Alcohol is humanity's best friend.;Quite delighted to serve you!;Care for a nice, cold beer?;Nothing cures you like booze!;Have a sip!;Have a drink!;Have a beer!;Beer is good for you!;Only the finest alcohol!;Best quality booze since 2053!;Award-winning wine!;Maximum alcohol!;Man loves beer.;A toast for progress!"
+
refill_canister = /obj/item/vending_refill/boozeomat
default_price = PRICE_ALMOST_CHEAP
extra_price = PRICE_EXPENSIVE
payment_department = ACCOUNT_SRV
- cost_multiplier_per_dept = list(ACCOUNT_SRV = 0)
+ light_mask = "boozeomat-light-mask"
/obj/machinery/vending/boozeomat/pubby_maint //abandoned bar on Pubbystation
products = list(/obj/item/reagent_containers/food/drinks/bottle/whiskey = 1,
@@ -67,12 +69,14 @@
/obj/item/reagent_containers/food/drinks/drinkingglass = 6,
/obj/item/reagent_containers/food/drinks/ice = 1,
/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass = 4);
+ payment_department = ACCOUNT_SEC
-/obj/machinery/vending/boozeomat/pubby_captain/Initialize()
- . = ..()
- cost_multiplier_per_dept = list("[ACCESS_CAPTAIN]" = 0)
-
+/obj/machinery/vending/boozeomat/all_access
+ desc = "A technological marvel, supposedly able to mix just the mixture you'd like to drink the moment you ask for one. This model appears to have no access restrictions."
+ req_access = null
/obj/machinery/vending/boozeomat/syndicate_access
+ req_access = list(ACCESS_SYNDICATE)
+ age_restrictions = FALSE
payment_department = NO_FREEBIES
/obj/machinery/vending/boozeomat/syndicate_access/Initialize()
diff --git a/code/modules/vending/cartridge.dm b/code/modules/vending/cartridge.dm
index 69635007c9..db8db77ad4 100644
--- a/code/modules/vending/cartridge.dm
+++ b/code/modules/vending/cartridge.dm
@@ -11,19 +11,18 @@
/obj/item/cartridge/janitor = 10,
/obj/item/cartridge/signal/toxins = 10,
/obj/item/cartridge/roboticist = 10,
- /obj/item/pda/heads = 10)
- premium = list(/obj/item/cartridge/captain = 2,
- /obj/item/cartridge/quartermaster = 2)
+ /obj/item/pda/heads = 10,
+ /obj/item/cartridge/captain = 3,
+ /obj/item/cartridge/quartermaster = 10)
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
refill_canister = /obj/item/vending_refill/cart
resistance_flags = FIRE_PROOF
default_price = PRICE_ALMOST_EXPENSIVE
extra_price = PRICE_ALMOST_ONE_GRAND
payment_department = ACCOUNT_SRV
-
-/obj/machinery/vending/cart/Initialize()
- . = ..()
- cost_multiplier_per_dept = list("[ACCESS_CHANGE_IDS]" = 0)
+ light_mask="cart-light-mask"
/obj/item/vending_refill/cart
- icon_state = "refill_pda"
+ machine_name = "PTech"
+ icon_state = "refill_smoke"
+
diff --git a/code/modules/vending/cigarette.dm b/code/modules/vending/cigarette.dm
index fa4247575f..269e0aaa7b 100644
--- a/code/modules/vending/cigarette.dm
+++ b/code/modules/vending/cigarette.dm
@@ -17,13 +17,15 @@
/obj/item/storage/fancy/cigarettes/cigpack_shadyjims = 1,
/obj/item/clothing/mask/cigarette/dart = 3)
premium = list(/obj/item/storage/fancy/cigarettes/cigpack_robustgold = 3,
- /obj/item/storage/fancy/cigarettes/cigars = 1,
- /obj/item/storage/fancy/cigarettes/cigars/havana = 1,
- /obj/item/storage/fancy/cigarettes/cigars/cohiba = 1)
+ /obj/item/lighter = 3,
+ /obj/item/storage/fancy/cigarettes/cigars = 1,
+ /obj/item/storage/fancy/cigarettes/cigars/havana = 1,
+ /obj/item/storage/fancy/cigarettes/cigars/cohiba = 1)
refill_canister = /obj/item/vending_refill/cigarette
default_price = PRICE_ALMOST_CHEAP
extra_price = PRICE_ABOVE_NORMAL
payment_department = ACCOUNT_SRV
+ light_mask = "cigs-light-mask"
/obj/machinery/vending/cigarette/syndicate
products = list(/obj/item/storage/fancy/cigarettes/cigpack_syndicate = 7,
@@ -34,7 +36,6 @@
/obj/item/storage/box/matches = 10,
/obj/item/lighter/greyscale = 4,
/obj/item/storage/fancy/rollingpapers = 5)
- payment_department = NO_FREEBIES
/obj/machinery/vending/cigarette/syndicate/Initialize()
. = ..()
diff --git a/code/modules/vending/clothesmate.dm b/code/modules/vending/clothesmate.dm
index 8822ef2ff0..c8cbae3054 100644
--- a/code/modules/vending/clothesmate.dm
+++ b/code/modules/vending/clothesmate.dm
@@ -1,10 +1,9 @@
-//DON'T FORGET TO CHANGE THE REFILL SIZE IF YOU CHANGE THE MACHINE'S CONTENTS!
/obj/machinery/vending/clothing
name = "ClothesMate" //renamed to make the slogan rhyme
desc = "A vending machine for clothing."
icon_state = "clothes"
icon_deny = "clothes-deny"
- product_slogans = "Dress for success!;Prepare to look swagalicious!;Look at all this free swag!;Why leave style up to fate? Use the ClothesMate!"
+ product_slogans = "Dress for success!;Prepare to look swagalicious!;Look at all this swag!;Why leave style up to fate? Use the ClothesMate!"
vend_reply = "Thank you for using the ClothesMate!"
products = list(/obj/item/clothing/head/that = 4,
/obj/item/clothing/head/fedora = 3,
@@ -231,9 +230,11 @@
/obj/item/clothing/suit/jacket/bluehoodie = 4,
/obj/item/clothing/suit/toggle/jacket/whitehoodie = 4)
refill_canister = /obj/item/vending_refill/clothing
- default_price = PRICE_CHEAP
+ default_price = PRICE_CHEAP //Default of
extra_price = PRICE_BELOW_NORMAL
payment_department = NO_FREEBIES
+ light_mask = "wardrobe-light-mask"
+ light_color = LIGHT_COLOR_ELECTRIC_GREEN
/obj/machinery/vending/clothing/canLoadItem(obj/item/I,mob/user)
return (I.type in products)
diff --git a/code/modules/vending/coffee.dm b/code/modules/vending/coffee.dm
index fd555526c6..cdc794f61b 100644
--- a/code/modules/vending/coffee.dm
+++ b/code/modules/vending/coffee.dm
@@ -1,7 +1,7 @@
/obj/machinery/vending/coffee
name = "\improper Solar's Best Hot Drinks"
desc = "A vending machine which dispenses hot drinks."
- product_ads = "Just what you need!;Have a drink!;Drink up!;It's good for you!;Would you like a hot joe?;I'd kill for some coffee!;The best beans in the galaxy.;Only the finest brew for you.;Mmmm. Nothing like a coffee.;I like coffee, don't you?;Coffee helps you work!;Try some tea.;We hope you like the best!;Try our new chocolate!;Admin conspiracies"
+ product_ads = "Have a drink!;Drink up!;It's good for you!;Would you like a hot joe?;I'd kill for some coffee!;The best beans in the galaxy.;Only the finest brew for you.;Mmmm. Nothing like a coffee.;I like coffee, don't you?;Coffee helps you work!;Try some tea.;We hope you like the best!;Try our new chocolate!;Admin conspiracies"
icon_state = "coffee"
icon_vend = "coffee-vend"
products = list(/obj/item/reagent_containers/food/drinks/coffee = 25,
@@ -15,11 +15,12 @@
/obj/item/reagent_containers/food/drinks/bottle/cream = 2,
/obj/item/reagent_containers/food/condiment/sugar = 1,
/obj/item/reagent_containers/food/drinks/mug/tea/forest = 3,)
-
refill_canister = /obj/item/vending_refill/coffee
default_price = PRICE_REALLY_CHEAP
extra_price = PRICE_PRETTY_CHEAP
payment_department = ACCOUNT_SRV
+ light_mask = "coffee-light-mask"
+ light_color = COLOR_DARK_MODERATE_ORANGE
/obj/item/vending_refill/coffee
machine_name = "Solar's Best Hot Drinks"
diff --git a/code/modules/vending/cola.dm b/code/modules/vending/cola.dm
index bb5b8ef288..6bc5b9cb93 100644
--- a/code/modules/vending/cola.dm
+++ b/code/modules/vending/cola.dm
@@ -6,7 +6,7 @@
product_slogans = "Robust Softdrinks: More robust than a toolbox to the head!"
product_ads = "Refreshing!;Hope you're thirsty!;Over 1 million drinks sold!;Thirsty? Why not cola?;Please, have a drink!;Drink up!;The best drinks in space."
products = list(/obj/item/reagent_containers/food/drinks/soda_cans/cola = 10,
- /obj/item/reagent_containers/food/drinks/soda_cans/space_mountain_wind = 10,
+ /obj/item/reagent_containers/food/drinks/soda_cans/space_mountain_wind = 10,
/obj/item/reagent_containers/food/drinks/soda_cans/dr_gibb = 10,
/obj/item/reagent_containers/food/drinks/soda_cans/starkist = 10,
/obj/item/reagent_containers/food/drinks/soda_cans/space_up = 10,
@@ -18,14 +18,15 @@
/obj/item/reagent_containers/food/drinks/soda_cans/shamblers = 6,
/obj/item/reagent_containers/glass/beaker/waterbottle/wataur = 2)
premium = list(/obj/item/reagent_containers/food/drinks/drinkingglass/filled/nuka_cola = 1,
- /obj/item/reagent_containers/food/drinks/soda_cans/air = 1,
- /obj/item/reagent_containers/food/drinks/soda_cans/grey_bull = 1,
- /obj/item/reagent_containers/food/drinks/soda_cans/monkey_energy = 1)
+ /obj/item/reagent_containers/food/drinks/soda_cans/air = 1,
+ /obj/item/reagent_containers/food/drinks/soda_cans/monkey_energy = 1,
+ /obj/item/reagent_containers/food/drinks/soda_cans/grey_bull = 1)
refill_canister = /obj/item/vending_refill/cola
default_price = PRICE_CHEAP_AS_FREE
extra_price = PRICE_ABOVE_NORMAL
payment_department = ACCOUNT_SRV
+
/obj/item/vending_refill/cola
machine_name = "Robust Softdrinks"
icon_state = "refill_cola"
@@ -43,43 +44,56 @@
/obj/machinery/vending/cola/blue
icon_state = "Cola_Machine"
+ light_mask = "cola-light-mask"
+ light_color = COLOR_MODERATE_BLUE
/obj/machinery/vending/cola/black
icon_state = "cola_black"
+ light_mask = "cola-light-mask"
/obj/machinery/vending/cola/red
icon_state = "red_cola"
name = "\improper Space Cola Vendor"
desc = "It vends cola, in space."
product_slogans = "Cola in space!"
+ light_mask = "red_cola-light-mask"
+ light_color = COLOR_DARK_RED
/obj/machinery/vending/cola/space_up
icon_state = "space_up"
name = "\improper Space-up! Vendor"
desc = "Indulge in an explosion of flavor."
product_slogans = "Space-up! Like a hull breach in your mouth."
+ light_mask = "space_up-light-mask"
+ light_color = COLOR_DARK_MODERATE_LIME_GREEN
/obj/machinery/vending/cola/starkist
icon_state = "starkist"
name = "\improper Star-kist Vendor"
desc = "The taste of a star in liquid form."
product_slogans = "Drink the stars! Star-kist!"
+ light_mask = "starkist-light-mask"
+ light_color = COLOR_LIGHT_ORANGE
/obj/machinery/vending/cola/sodie
icon_state = "soda"
+ light_mask = "soda-light-mask"
+ light_color = COLOR_WHITE
/obj/machinery/vending/cola/pwr_game
icon_state = "pwr_game"
name = "\improper Pwr Game Vendor"
desc = "You want it, we got it. Brought to you in partnership with Vlad's Salads."
product_slogans = "The POWER that gamers crave! PWR GAME!"
+ light_mask = "pwr_game-light-mask"
+ light_color = COLOR_STRONG_VIOLET
/obj/machinery/vending/cola/shamblers
name = "\improper Shambler's Vendor"
desc = "~Shake me up some of that Shambler's Juice!~"
icon_state = "shamblers_juice"
products = list(/obj/item/reagent_containers/food/drinks/soda_cans/cola = 10,
- /obj/item/reagent_containers/food/drinks/soda_cans/space_mountain_wind = 10,
+ /obj/item/reagent_containers/food/drinks/soda_cans/space_mountain_wind = 10,
/obj/item/reagent_containers/food/drinks/soda_cans/dr_gibb = 10,
/obj/item/reagent_containers/food/drinks/soda_cans/starkist = 10,
/obj/item/reagent_containers/food/drinks/soda_cans/space_up = 10,
@@ -89,7 +103,8 @@
/obj/item/reagent_containers/food/drinks/soda_cans/shamblers = 10)
product_slogans = "~Shake me up some of that Shambler's Juice!~"
product_ads = "Refreshing!;Jyrbv dv lg jfdv fw kyrk Jyrdscvi'j Alztv!;Over 1 trillion souls drank!;Thirsty? Nyp efk uizeb kyv uribevjj?;Kyv Jyrdscvi uizebj kyv ezxyk!;Drink up!;Krjkp."
-
+ light_mask = "shamblers-light-mask"
+ light_color = COLOR_MOSTLY_PURE_PINK
/obj/machinery/vending/cola/buzz_fuzz
name = "\improper Buzz Fuzz Vendor"
desc = "~A hive of Flavour!~"
diff --git a/code/modules/vending/engineering.dm b/code/modules/vending/engineering.dm
index ef4edd6b67..fc88e6404c 100644
--- a/code/modules/vending/engineering.dm
+++ b/code/modules/vending/engineering.dm
@@ -5,9 +5,9 @@
icon_state = "engi"
icon_deny = "engi-deny"
products = list(/obj/item/clothing/under/rank/engineering/chief_engineer = 4,
- /obj/item/clothing/under/rank/engineering/engineer = 4,
- /obj/item/clothing/shoes/sneakers/orange = 4,
- /obj/item/clothing/head/hardhat = 4,
+ /obj/item/clothing/under/rank/engineering/engineer = 4,
+ /obj/item/clothing/shoes/sneakers/orange = 4,
+ /obj/item/clothing/head/hardhat = 4,
/obj/item/storage/belt/utility = 4,
/obj/item/clothing/glasses/meson/engine = 4,
/obj/item/clothing/gloves/color/yellow = 4,
@@ -26,9 +26,13 @@
/obj/item/stock_parts/micro_laser = 5,
/obj/item/stock_parts/matter_bin = 5,
/obj/item/stock_parts/manipulator = 5)
- armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
+ refill_canister = /obj/item/vending_refill/engineering
resistance_flags = FIRE_PROOF
default_price = PRICE_NORMAL
extra_price = PRICE_NORMAL
payment_department = ACCOUNT_ENG
- cost_multiplier_per_dept = list(ACCOUNT_ENG = 0)
+ light_mask = "engi-light-mask"
+
+/obj/item/vending_refill/engineering
+ machine_name = "Robco Tool Maker"
+ icon_state = "refill_engi"
diff --git a/code/modules/vending/engivend.dm b/code/modules/vending/engivend.dm
index 965ebddd15..559e56eec7 100644
--- a/code/modules/vending/engivend.dm
+++ b/code/modules/vending/engivend.dm
@@ -27,13 +27,13 @@
/obj/item/rcd_ammo/large = 5,
/obj/item/storage/bag/material = 3
)
- armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
refill_canister = /obj/item/vending_refill/engivend
resistance_flags = FIRE_PROOF
default_price = PRICE_ALMOST_EXPENSIVE
extra_price = PRICE_ABOVE_EXPENSIVE
payment_department = ACCOUNT_ENG
- cost_multiplier_per_dept = list(ACCOUNT_ENG = 0)
+ light_mask = "engivend-light-mask"
/obj/item/vending_refill/engivend
+ machine_name = "Engi-Vend"
icon_state = "refill_engi"
diff --git a/code/modules/vending/games.dm b/code/modules/vending/games.dm
index 7664a191c8..e6a6eb01e9 100644
--- a/code/modules/vending/games.dm
+++ b/code/modules/vending/games.dm
@@ -4,15 +4,17 @@
product_ads = "Escape to a fantasy world!;Fuel your gambling addiction!;Ruin your friendships!;Roll for initiative!;Elves and dwarves!;Paranoid computers!;Totally not satanic!;Fun times forever!"
icon_state = "games"
products = list(/obj/item/toy/cards/deck = 5,
- /obj/item/storage/dice = 10,
- /obj/item/toy/cards/deck/cas = 3,
- /obj/item/toy/cards/deck/cas/black = 3,
+ /obj/item/storage/dice = 10,
+ /obj/item/toy/cards/deck/cas = 3,
+ /obj/item/toy/cards/deck/cas/black = 3,
/obj/item/toy/cards/deck/unum = 3,
+ /obj/item/camera = 3,
/obj/item/cardpack/series_one = 10,
- /obj/item/dyespray=3,
/obj/item/tcgcard_binder = 5,
/obj/item/canvas = 3,
- /obj/item/toy/crayon/spraycan = 3)
+ /obj/item/toy/crayon/spraycan = 3,
+ /obj/item/dyespray=3,
+ )
contraband = list(/obj/item/dice/fudge = 9)
premium = list(/obj/item/melee/skateboard/pro = 3,
/obj/item/melee/skateboard/hoverboard = 1)
@@ -20,7 +22,7 @@
default_price = PRICE_CHEAP
extra_price = PRICE_ALMOST_EXPENSIVE
payment_department = ACCOUNT_SRV
- cost_multiplier_per_dept = list(ACCOUNT_SRV = 0)
+ light_mask = "games-light-mask"
/obj/item/vending_refill/games
machine_name = "\improper Good Clean Fun"
diff --git a/code/modules/vending/liberation.dm b/code/modules/vending/liberation.dm
index 81afab3a70..36bde6dd6b 100644
--- a/code/modules/vending/liberation.dm
+++ b/code/modules/vending/liberation.dm
@@ -2,15 +2,15 @@
name = "\improper Liberation Station"
desc = "An overwhelming amount of ancient patriotism washes over you just by looking at the machine."
icon_state = "liberationstation"
- product_slogans = "Liberation Station: Your one-stop shop for all things second ammendment!;Be a patriot today, pick up a gun!;Quality weapons for cheap prices!;Better dead than red!"
- product_ads = "Float like an astronaut, sting like a bullet!;Express your second ammendment today!;Guns don't kill people, but you can!;Who needs responsibilities when you have guns?"
+ product_slogans = "Liberation Station: Your one-stop shop for all things second amendment!;Be a patriot today, pick up a gun!;Quality weapons for cheap prices!;Better dead than red!"
+ product_ads = "Float like an astronaut, sting like a bullet!;Express your second amendment today!;Guns don't kill people, but you can!;Who needs responsibilities when you have guns?"
vend_reply = "Remember the name: Liberation Station!"
products = list(/obj/item/reagent_containers/food/snacks/burger/plain = 5, //O say can you see, by the dawn's early light
/obj/item/reagent_containers/food/snacks/burger/baseball = 3, //What so proudly we hailed at the twilight's last gleaming
/obj/item/reagent_containers/food/snacks/fries = 5, //Whose broad stripes and bright stars through the perilous fight
/obj/item/reagent_containers/food/drinks/beer/light = 10, //O'er the ramparts we watched, were so gallantly streaming?
/obj/item/gun/ballistic/automatic/pistol/deagle/gold = 2,
- /obj/item/gun/ballistic/automatic/pistol/deagle/camo = 2,
+ /obj/item/gun/ballistic/automatic/pistol/deagle/camo = 2,
/obj/item/gun/ballistic/automatic/pistol/m1911 = 2,
/obj/item/gun/ballistic/automatic/proto/unrestricted = 2,
/obj/item/gun/ballistic/shotgun/automatic/combat = 2,
@@ -18,16 +18,16 @@
/obj/item/gun/ballistic/shotgun = 2,
/obj/item/gun/ballistic/automatic/ar = 2)
premium = list(/obj/item/ammo_box/magazine/smgm9mm = 2,
- /obj/item/ammo_box/magazine/m50 = 4,
- /obj/item/ammo_box/magazine/m45 = 2,
- /obj/item/ammo_box/magazine/m75 = 2,
+ /obj/item/ammo_box/magazine/m50 = 4,
+ /obj/item/ammo_box/magazine/m45 = 2,
+ /obj/item/ammo_box/magazine/m75 = 2,
/obj/item/reagent_containers/food/snacks/cheesyfries = 5,
/obj/item/reagent_containers/food/snacks/burger/baconburger = 5) //Premium burgers for the premium section
contraband = list(/obj/item/clothing/under/misc/patriotsuit = 3,
- /obj/item/bedsheet/patriot = 5,
+ /obj/item/bedsheet/patriot = 5,
/obj/item/reagent_containers/food/snacks/burger/superbite = 3) //U S A
- armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
resistance_flags = FIRE_PROOF
default_price = PRICE_ABOVE_NORMAL
extra_price = PRICE_ABOVE_EXPENSIVE
payment_department = ACCOUNT_SEC
+ light_mask = "liberation-light-mask"
diff --git a/code/modules/vending/liberation_toy.dm b/code/modules/vending/liberation_toy.dm
index 9093d55b0d..eea1150abf 100644
--- a/code/modules/vending/liberation_toy.dm
+++ b/code/modules/vending/liberation_toy.dm
@@ -16,19 +16,16 @@
/obj/item/clothing/suit/syndicatefake = 5,
/obj/item/clothing/head/syndicatefake = 5) //OPS IN DORMS oh wait it's just an assistant
contraband = list(/obj/item/gun/ballistic/shotgun/toy/crossbow = 10, //Congrats, you unlocked the +18 setting!
- /obj/item/gun/ballistic/automatic/c20r/toy/unrestricted/riot = 10,
- /obj/item/gun/ballistic/automatic/l6_saw/toy/unrestricted/riot = 10,
- /obj/item/ammo_box/foambox/riot = 20,
- /obj/item/toy/katana = 10,
- /obj/item/dualsaber/toy = 5,
- /obj/item/toy/cards/deck/syndicate = 10) //Gambling and it hurts, making it a +18 item
+ /obj/item/gun/ballistic/automatic/c20r/toy/unrestricted/riot = 10,
+ /obj/item/gun/ballistic/automatic/l6_saw/toy/unrestricted/riot = 10,
+ /obj/item/ammo_box/foambox/riot = 20,
+ /obj/item/toy/katana = 10,
+ /obj/item/dualsaber/toy = 5,
+ /obj/item/toy/cards/deck/syndicate = 10) //Gambling and it hurts, making it a +18 item
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
resistance_flags = FIRE_PROOF
refill_canister = /obj/item/vending_refill/donksoft
default_price = PRICE_ABOVE_NORMAL
extra_price = PRICE_EXPENSIVE
- payment_department = NO_FREEBIES
-
-/obj/machinery/vending/toyliberationstation/Initialize()
- . = ..()
- cost_multiplier_per_dept = list("[ACCESS_SYNDICATE]" = 0)
+ payment_department = ACCOUNT_SRV
+ light_mask = "donksoft-light-mask"
diff --git a/code/modules/vending/magivend.dm b/code/modules/vending/magivend.dm
index 9dcd77e4ab..104f7b3c02 100644
--- a/code/modules/vending/magivend.dm
+++ b/code/modules/vending/magivend.dm
@@ -4,18 +4,19 @@
icon_state = "MagiVend"
product_slogans = "Sling spells the proper way with MagiVend!;Be your own Houdini! Use MagiVend!"
vend_reply = "Have an enchanted evening!"
- product_ads = "EI NATH;Destroy the station!;Admin conspiracies since forever!;Space-time bending hardware!;Now-magic proofing venders!"
+ product_ads = "FJKLFJSD;AJKFLBJAKL;1234 LOONIES LOL!;>MFW;Kill them fuckers!;GET DAT FUKKEN DISK;HONK!;EI NATH;Destroy the station!;Admin conspiracies since forever!;Space-time bending hardware!"
products = list(/obj/item/clothing/head/wizard = 1,
- /obj/item/clothing/suit/wizrobe = 1,
- /obj/item/clothing/head/wizard/red = 1,
- /obj/item/clothing/suit/wizrobe/red = 1,
- /obj/item/clothing/head/wizard/yellow = 1,
- /obj/item/clothing/suit/wizrobe/yellow = 1,
- /obj/item/clothing/shoes/sandal/magic = 1,
- /obj/item/staff = 2)
- contraband = list(/obj/item/reagent_containers/glass/bottle/wizarditis = 1) //No one can get to the machine to hack it anyways; for the lulz - Microwave
+ /obj/item/clothing/suit/wizrobe = 1,
+ /obj/item/clothing/head/wizard/red = 1,
+ /obj/item/clothing/suit/wizrobe/red = 1,
+ /obj/item/clothing/head/wizard/yellow = 1,
+ /obj/item/clothing/suit/wizrobe/yellow = 1,
+ /obj/item/clothing/shoes/sandal/magic = 1,
+ /obj/item/staff = 2)
+ contraband = list(/obj/item/reagent_containers/glass/bottle/wizarditis = 1) //No one can get to the machine to hack it anyways; for the lulz - Microwave
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50, "magic" = 100)
resistance_flags = FIRE_PROOF
- default_price = PRICE_EXPENSIVE
+ default_price = 0 //Just in case, since it's primary use is storage.
extra_price = PRICE_ABOVE_EXPENSIVE
payment_department = ACCOUNT_SRV
+ light_mask = "magivend-light-mask"
diff --git a/code/modules/vending/medical.dm b/code/modules/vending/medical.dm
index a24233b17c..0158547515 100644
--- a/code/modules/vending/medical.dm
+++ b/code/modules/vending/medical.dm
@@ -4,6 +4,7 @@
icon_state = "med"
icon_deny = "med-deny"
product_ads = "Go save some lives!;The best stuff for your medbay.;Only the finest tools.;Natural chemicals!;This stuff saves lives.;Don't you want some?;Ping!"
+ req_access = list(ACCESS_MEDICAL)
products = list(/obj/item/reagent_containers/syringe = 12,
/obj/item/reagent_containers/dropper = 3,
/obj/item/healthanalyzer = 4,
@@ -50,13 +51,11 @@
/obj/item/storage/briefcase/medical = 2,
/obj/item/plunger/reinforced = 2)
- armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
- resistance_flags = FIRE_PROOF
refill_canister = /obj/item/vending_refill/medical
default_price = PRICE_ALMOST_CHEAP
extra_price = PRICE_ABOVE_NORMAL
payment_department = ACCOUNT_MED
- cost_multiplier_per_dept = list(ACCOUNT_MED = 0)
+ light_mask = "med-light-mask"
/obj/item/vending_refill/medical
machine_name = "NanoMed Plus"
@@ -64,8 +63,9 @@
/obj/machinery/vending/medical/syndicate_access
name = "\improper SyndiMed Plus"
- payment_department = NO_FREEBIES
+ req_access = list(ACCESS_SYNDICATE)
-/obj/machinery/vending/medical/syndicate_access/Initialize()
+
+/obj/machinery/vending/medical/syndicate_access/Initialize(mapload)
. = ..()
cost_multiplier_per_dept = list("[ACCESS_SYNDICATE]" = 0)
diff --git a/code/modules/vending/medical_wall.dm b/code/modules/vending/medical_wall.dm
index 2d4c30080d..fdc062b2cb 100644
--- a/code/modules/vending/medical_wall.dm
+++ b/code/modules/vending/medical_wall.dm
@@ -18,14 +18,28 @@
contraband = list(/obj/item/reagent_containers/pill/tox = 2,
/obj/item/reagent_containers/pill/morphine = 2)
premium = list(/obj/item/reagent_containers/medspray/synthflesh = 2)
- armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
- resistance_flags = FIRE_PROOF
refill_canister = /obj/item/vending_refill/wallmed
default_price = PRICE_FREE
extra_price = PRICE_NORMAL
payment_department = ACCOUNT_MED
- cost_multiplier_per_dept = list(ACCOUNT_MED = 0)
tiltable = FALSE
+ light_mask = "wallmed-light-mask"
+
+/obj/machinery/vending/wallmed/directional/north
+ dir = SOUTH
+ pixel_y = 32
+
+/obj/machinery/vending/wallmed/directional/south
+ dir = NORTH
+ pixel_y = -32
+
+/obj/machinery/vending/wallmed/directional/east
+ dir = WEST
+ pixel_x = 32
+
+/obj/machinery/vending/wallmed/directional/west
+ dir = EAST
+ pixel_x = -32
/obj/item/vending_refill/wallmed
machine_name = "NanoMed"
diff --git a/code/modules/vending/megaseed.dm b/code/modules/vending/megaseed.dm
index 45199298ca..790bf551b7 100644
--- a/code/modules/vending/megaseed.dm
+++ b/code/modules/vending/megaseed.dm
@@ -4,6 +4,7 @@
product_slogans = "THIS'S WHERE TH' SEEDS LIVE! GIT YOU SOME!;Hands down the best seed selection on the station!;Also certain mushroom varieties available, more for experts! Get certified today!"
product_ads = "We like plants!;Grow some crops!;Grow, baby, growww!;Aw h'yeah son!"
icon_state = "seeds"
+ light_mask = "seeds-light-mask"
products = list(/obj/item/seeds/aloe = 3,
/obj/item/seeds/ambrosia = 3,
/obj/item/seeds/apple = 3,
@@ -20,6 +21,7 @@
/obj/item/seeds/cotton = 3,
/obj/item/seeds/corn = 3,
/obj/item/seeds/eggplant = 3,
+ /obj/item/seeds/garlic = 3,
/obj/item/seeds/grape = 3,
/obj/item/seeds/grass = 3,
/obj/item/seeds/lemon = 3,
@@ -56,13 +58,11 @@
/obj/item/seeds/starthistle = 2,
/obj/item/seeds/random = 2)
premium = list(/obj/item/reagent_containers/spray/waterflower = 1)
- armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
refill_canister = /obj/item/vending_refill/hydroseeds
- resistance_flags = FIRE_PROOF
default_price = PRICE_ALMOST_CHEAP
extra_price = PRICE_NORMAL
payment_department = ACCOUNT_SRV
- cost_multiplier_per_dept = list(ACCOUNT_SRV = 0)
/obj/item/vending_refill/hydroseeds
- icon_state = "refill_hydro"
+ machine_name = "MegaSeed Servitor"
+ icon_state = "refill_plant"
diff --git a/code/modules/vending/nutrimax.dm b/code/modules/vending/nutrimax.dm
index 40ca06b78b..38bab5c599 100644
--- a/code/modules/vending/nutrimax.dm
+++ b/code/modules/vending/nutrimax.dm
@@ -5,6 +5,7 @@
product_ads = "We like plants!;Don't you want some?;The greenest thumbs ever.;We like big plants.;Soft soil..."
icon_state = "nutri"
icon_deny = "nutri-deny"
+ light_mask = "nutri-light-mask"
products = list(/obj/item/reagent_containers/glass/bottle/nutrient/ez = 30,
/obj/item/reagent_containers/glass/bottle/nutrient/l4z = 20,
/obj/item/reagent_containers/glass/bottle/nutrient/rh = 10,
@@ -15,14 +16,12 @@
/obj/item/shovel/spade = 3,
/obj/item/plant_analyzer = 4)
contraband = list(/obj/item/reagent_containers/glass/bottle/ammonia = 10,
- /obj/item/reagent_containers/glass/bottle/diethylamine = 5)
- armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
+ /obj/item/reagent_containers/glass/bottle/diethylamine = 5)
refill_canister = /obj/item/vending_refill/hydronutrients
- resistance_flags = FIRE_PROOF
default_price = PRICE_REALLY_CHEAP
extra_price = PRICE_CHEAP
payment_department = ACCOUNT_SRV
- cost_multiplier_per_dept = list(ACCOUNT_SRV = 0)
/obj/item/vending_refill/hydronutrients
- icon_state = "refill_hydro"
+ machine_name = "NutriMax"
+ icon_state = "refill_plant"
diff --git a/code/modules/vending/plasmaresearch.dm b/code/modules/vending/plasmaresearch.dm
index bb16570ab3..2bd022eeb2 100644
--- a/code/modules/vending/plasmaresearch.dm
+++ b/code/modules/vending/plasmaresearch.dm
@@ -1,10 +1,10 @@
//This one's from bay12
/obj/machinery/vending/plasmaresearch
- name = "\improper Toximate 3000"
+ name = "\improper Bombuddy 3000"
desc = "All the fine parts you need in one vending machine!"
products = list(/obj/item/clothing/under/rank/rnd/scientist = 6,
- /obj/item/clothing/suit/bio_suit = 6,
- /obj/item/clothing/head/bio_hood = 6,
+ /obj/item/clothing/suit/bio_suit = 6,
+ /obj/item/clothing/head/bio_hood = 6,
/obj/item/transfer_valve = 6,
/obj/item/assembly/timer = 6,
/obj/item/assembly/signaler = 6,
@@ -14,4 +14,3 @@
default_price = PRICE_EXPENSIVE
extra_price = PRICE_REALLY_EXPENSIVE
payment_department = ACCOUNT_SCI
- cost_multiplier_per_dept = list(ACCOUNT_SCI = 0)
diff --git a/code/modules/vending/robotics.dm b/code/modules/vending/robotics.dm
index 2d77b2fc51..714f05a60d 100644
--- a/code/modules/vending/robotics.dm
+++ b/code/modules/vending/robotics.dm
@@ -4,6 +4,8 @@
desc = "All the tools you need to create your own robot army."
icon_state = "robotics"
icon_deny = "robotics-deny"
+ light_mask = "robotics-light-mask"
+ req_access = list(ACCESS_ROBOTICS)
products = list(/obj/item/clothing/suit/toggle/labcoat = 4,
/obj/item/clothing/under/rank/rnd/roboticist = 4,
/obj/item/stack/cable_coil = 4,
@@ -14,13 +16,16 @@
/obj/item/healthanalyzer = 3,
/obj/item/scalpel = 2,
/obj/item/circular_saw = 2,
+ /obj/item/bonesetter = 2,
/obj/item/tank/internals/anesthetic = 2,
/obj/item/clothing/mask/breath/medical = 5,
/obj/item/screwdriver = 5,
- /obj/item/crowbar = 6,
+ /obj/item/crowbar = 5,
/obj/item/stack/medical/nanogel = 5)
- armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
- resistance_flags = FIRE_PROOF
+ refill_canister = /obj/item/vending_refill/robotics
default_price = PRICE_EXPENSIVE
payment_department = ACCOUNT_SCI
- cost_multiplier_per_dept = list(ACCOUNT_SCI = 0)
+
+/obj/item/vending_refill/robotics
+ machine_name = "Robotech Deluxe"
+ icon_state = "refill_engi"
diff --git a/code/modules/vending/security.dm b/code/modules/vending/security.dm
index c3540777ab..52fbd7dac4 100644
--- a/code/modules/vending/security.dm
+++ b/code/modules/vending/security.dm
@@ -1,9 +1,11 @@
/obj/machinery/vending/security
name = "\improper SecTech"
desc = "A security equipment vendor."
- product_ads = "Crack capitalist skulls!;Beat some heads in!;Don't forget - harm is good!;Your weapons are right here.;Handcuffs!;Freeze, scumbag!;Don't tase me bro!;Tase them, bro.;Why not have a donut?"
+ product_ads = "Crack communist skulls!;Beat some heads in!;Don't forget - harm is good!;Your weapons are right here.;Handcuffs!;Freeze, scumbag!;Don't tase me bro!;Tase them, bro.;Why not have a donut?"
icon_state = "sec"
icon_deny = "sec-deny"
+ light_mask = "sec-light-mask"
+ req_access = list(ACCESS_SECURITY)
products = list(/obj/item/restraints/handcuffs = 8,
/obj/item/restraints/handcuffs/cable/zipties = 10,
/obj/item/grenade/flashbang = 4,
@@ -17,20 +19,17 @@
/obj/item/storage/fancy/donut_box = 2,
/obj/item/storage/belt/sabre/secbelt = 1)
premium = list(/obj/item/coin/antagtoken = 1,
- /obj/item/clothing/head/helmet/blueshirt = 1,
- /obj/item/clothing/suit/armor/vest/blueshirt = 1,
+ /obj/item/clothing/head/helmet/blueshirt = 1,
+ /obj/item/clothing/suit/armor/vest/blueshirt = 1,
/obj/item/clothing/under/rank/security/officer/blueshirt = 1,
- /obj/item/clothing/gloves/tackler = 5,
- /obj/item/grenade/stingbang = 1,
+ /obj/item/clothing/gloves/tackler = 5,
+ /obj/item/grenade/stingbang = 1,
/obj/item/ssword_kit = 1,
/obj/item/storage/bag/ammo = 3)
- armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
- resistance_flags = FIRE_PROOF
refill_canister = /obj/item/vending_refill/security
default_price = PRICE_ALMOST_EXPENSIVE
extra_price = PRICE_REALLY_EXPENSIVE
payment_department = ACCOUNT_SEC
- cost_multiplier_per_dept = list(ACCOUNT_SEC = 0)
/obj/machinery/vending/security/pre_throw(obj/item/I)
if(istype(I, /obj/item/grenade))
@@ -42,4 +41,4 @@
F.update_brightness()
/obj/item/vending_refill/security
- icon_state = "refill_games"
+ icon_state = "refill_sec"
diff --git a/code/modules/vending/snack.dm b/code/modules/vending/snack.dm
index ff8fd46676..edd4802dc2 100644
--- a/code/modules/vending/snack.dm
+++ b/code/modules/vending/snack.dm
@@ -4,6 +4,7 @@
product_slogans = "Try our new nougat bar!;Twice the calories for half the price!"
product_ads = "The healthiest!;Award-winning chocolate bars!;Mmm! So good!;Oh my god it's so juicy!;Have a snack.;Snacks are good for you!;Have some more Getmore!;Best quality snacks straight from mars.;We love chocolate!;Try our new jerky!"
icon_state = "snack"
+ light_mask = "snack-light-mask"
products = list(/obj/item/reagent_containers/food/snacks/candy = 5,
/obj/item/reagent_containers/food/snacks/chocolatebar = 5,
/obj/item/reagent_containers/food/drinks/dry_ramen = 5,
@@ -25,13 +26,11 @@
/obj/item/reagent_containers/food/snacks/chococoin = 1,
/obj/item/storage/box/marshmallow = 1,
/obj/item/storage/box/donkpockets = 2)
-
refill_canister = /obj/item/vending_refill/snack
canload_access_list = list(ACCESS_KITCHEN)
default_price = PRICE_REALLY_CHEAP
extra_price = PRICE_ALMOST_CHEAP
payment_department = ACCOUNT_SRV
- cost_multiplier_per_dept = list(ACCOUNT_SRV = 0)
input_display_header = "Chef's Food Selection"
/obj/item/vending_refill/snack
diff --git a/code/modules/vending/sovietsoda.dm b/code/modules/vending/sovietsoda.dm
index 88aba484e1..f28dbd7116 100644
--- a/code/modules/vending/sovietsoda.dm
+++ b/code/modules/vending/sovietsoda.dm
@@ -2,11 +2,17 @@
name = "\improper BODA"
desc = "Old sweet water vending machine."
icon_state = "sovietsoda"
+ light_mask = "soviet-light-mask"
product_ads = "For Tsar and Country.;Have you fulfilled your nutrition quota today?;Very nice!;We are simple people, for this is all we eat.;If there is a person, there is a problem. If there is no person, then there is no problem."
products = list(/obj/item/reagent_containers/food/drinks/drinkingglass/filled/soda = 30)
contraband = list(/obj/item/reagent_containers/food/drinks/drinkingglass/filled/cola = 20)
- armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
resistance_flags = FIRE_PROOF
- default_price = PRICE_FREE
- extra_price = PRICE_FREE
+ refill_canister = /obj/item/vending_refill/sovietsoda
+ default_price = 1
+ extra_price = 2 //One credit for every state of FREEDOM
payment_department = NO_FREEBIES
+ light_color = COLOR_PALE_ORANGE
+
+/obj/item/vending_refill/sovietsoda
+ machine_name = "BODA"
+ icon_state = "refill_cola"
diff --git a/code/modules/vending/sustenance.dm b/code/modules/vending/sustenance.dm
index 2dcc1e99f7..9370cf0c24 100644
--- a/code/modules/vending/sustenance.dm
+++ b/code/modules/vending/sustenance.dm
@@ -2,21 +2,24 @@
name = "\improper Sustenance Vendor"
desc = "A vending machine which vends food, as required by section 47-C of the NT's Prisoner Ethical Treatment Agreement."
product_slogans = "Enjoy your meal.;Enough calories to support strenuous labor."
- product_ads = "Sufficiently healthy.;Efficiently produced tofu!;Mmm! So good!;Have a meal.;You need food to live!;Have some more candy corn!;Try our new ice cups!"
+ product_ads = "Sufficiently healthy.;Efficiently produced tofu!;Mmm! So good!;Have a meal.;You need food to live!;Even prisoners deserve their daily bread!;Have some more candy corn!;Try our new ice cups!"
+ light_mask = "snack-light-mask"
icon_state = "sustenance"
products = list(/obj/item/reagent_containers/food/snacks/tofu = 24,
/obj/item/reagent_containers/food/drinks/ice/sustanance = 12,
- /obj/item/reagent_containers/food/snacks/candy_corn = 6)
+ /obj/item/reagent_containers/food/snacks/candy_corn = 6
+ )
contraband = list(/obj/item/kitchen/knife = 6,
/obj/item/reagent_containers/food/drinks/coffee = 12,
/obj/item/tank/internals/emergency_oxygen = 6,
- /obj/item/clothing/mask/breath = 6)
- armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
+ /obj/item/clothing/mask/breath = 6
+ )
+
refill_canister = /obj/item/vending_refill/sustenance
- resistance_flags = FIRE_PROOF
default_price = PRICE_FREE
extra_price = PRICE_FREE
payment_department = NO_FREEBIES
/obj/item/vending_refill/sustenance
- icon_state = "refill_cook"
+ machine_name = "Sustenance Vendor"
+ icon_state = "refill_snack"
diff --git a/code/modules/vending/toys.dm b/code/modules/vending/toys.dm
index c5095ebff8..683f2d9f23 100644
--- a/code/modules/vending/toys.dm
+++ b/code/modules/vending/toys.dm
@@ -5,6 +5,7 @@
product_slogans = "Get your cool toys today!;Trigger a valid hunter today!;Quality toy weapons for cheap prices!;Give them to HoPs for all access!;Give them to HoS to get permabrigged!"
product_ads = "Feel robust with your toys!;Express your inner child today!;Toy weapons don't kill people, but valid hunters do!;Who needs responsibilities when you have toy weapons?;Make your next murder FUN!"
vend_reply = "Come back for more!"
+ light_mask = "donksoft-light-mask"
circuit = /obj/item/circuitboard/machine/vending/donksofttoyvendor
products = list(
/obj/item/gun/ballistic/automatic/toy/unrestricted = 10,
@@ -22,8 +23,6 @@
/obj/item/gun/ballistic/automatic/l6_saw/toy/unrestricted = 10,
/obj/item/toy/katana = 10,
/obj/item/dualsaber/toy = 5)
- armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
- resistance_flags = FIRE_PROOF
refill_canister = /obj/item/vending_refill/donksoft
default_price = PRICE_ABOVE_NORMAL
extra_price = PRICE_EXPENSIVE
diff --git a/code/modules/vending/wardrobes.dm b/code/modules/vending/wardrobes.dm
index eebb07938b..e1a5b69d72 100644
--- a/code/modules/vending/wardrobes.dm
+++ b/code/modules/vending/wardrobes.dm
@@ -6,6 +6,7 @@
extra_price = PRICE_EXPENSIVE
payment_department = NO_FREEBIES
input_display_header = "Returned Clothing"
+ light_mask = "wardrobe-light-mask"
/obj/machinery/vending/wardrobe/canLoadItem(obj/item/I,mob/user)
return (I.type in products)
@@ -35,51 +36,11 @@
/obj/item/clothing/head/beret/sec/navyofficer = 5)
refill_canister = /obj/item/vending_refill/wardrobe/sec_wardrobe
payment_department = ACCOUNT_SEC
- cost_multiplier_per_dept = list(ACCOUNT_SEC = 0)
- default_price = PRICE_ABOVE_NORMAL
- extra_price = PRICE_EXPENSIVE
+ light_color = COLOR_MOSTLY_PURE_RED
/obj/item/vending_refill/wardrobe/sec_wardrobe
machine_name = "SecDrobe"
-
-/obj/machinery/vending/wardrobe/det_wardrobe
- name = "\improper DetDrobe"
- desc = "A machine for all your detective needs, as long as you need clothes."
- icon_state = "detdrobe"
- product_ads = "Apply your brilliant deductive methods in style!"
- vend_reply = "Thank you for using the DetDrobe!"
- products = list(/obj/item/clothing/under/rank/security/detective = 2,
- /obj/item/clothing/under/rank/security/detective/skirt = 2,
- /obj/item/clothing/under/rank/security/detective/brown = 2,
- /obj/item/clothing/under/rank/security/detective/brown/brown2 = 2,
- /obj/item/clothing/under/rank/security/officer/blueshirt/seccorp/detcorp = 2,
- /obj/item/clothing/under/rank/security/officer/util = 2,
- /obj/item/clothing/shoes/sneakers/brown = 2,
- /obj/item/clothing/suit/det_suit = 2,
- /obj/item/clothing/head/fedora/det_hat = 2,
- /obj/item/clothing/under/rank/security/detective/grey = 2,
- /obj/item/clothing/under/rank/security/detective/grey/skirt = 2,
- /obj/item/clothing/accessory/waistcoat = 2,
- /obj/item/clothing/shoes/laceup = 2,
- /obj/item/clothing/suit/det_suit/grey = 1,
- /obj/item/clothing/suit/det_suit/forensicsred = 1,
- /obj/item/clothing/suit/det_suit/forensicsred/long = 1,
- /obj/item/clothing/suit/det_suit/forensicsblue = 1,
- /obj/item/clothing/suit/det_suit/forensicsblue/long = 1,
- /obj/item/clothing/head/fedora = 2,
- /obj/item/clothing/gloves/color/black = 2,
- /obj/item/clothing/gloves/color/latex = 2,
- /obj/item/reagent_containers/food/drinks/flask/det = 2,
- /obj/item/storage/fancy/cigarettes = 5)
- premium = list(/obj/item/clothing/head/flatcap = 1)
- refill_canister = /obj/item/vending_refill/wardrobe/det_wardrobe
- extra_price = 350
- payment_department = ACCOUNT_SEC
-
-/obj/item/vending_refill/wardrobe/det_wardrobe
- machine_name = "DetDrobe"
-
/obj/machinery/vending/wardrobe/medi_wardrobe
name = "\improper MediDrobe"
desc = "A vending machine rumoured to be capable of dispensing clothing for medical personnel."
@@ -118,7 +79,6 @@
/obj/item/clothing/suit/toggle/labcoat/emt/highvis = 5)
refill_canister = /obj/item/vending_refill/wardrobe/medi_wardrobe
payment_department = ACCOUNT_MED
- cost_multiplier_per_dept = list(ACCOUNT_MED = 0)
/obj/item/vending_refill/wardrobe/medi_wardrobe
machine_name = "MediDrobe"
@@ -149,7 +109,7 @@
/obj/item/clothing/head/hardhat/weldhat = 3)
refill_canister = /obj/item/vending_refill/wardrobe/engi_wardrobe
payment_department = ACCOUNT_ENG
- cost_multiplier_per_dept = list(ACCOUNT_ENG = 0)
+ light_color = COLOR_VIVID_YELLOW
/obj/item/vending_refill/wardrobe/engi_wardrobe
machine_name = "EngiDrobe"
@@ -172,7 +132,7 @@
/obj/item/clothing/shoes/sneakers/black = 5)
refill_canister = /obj/item/vending_refill/wardrobe/atmos_wardrobe
payment_department = ACCOUNT_ENG
- cost_multiplier_per_dept = list(ACCOUNT_ENG = 0)
+ light_color = COLOR_VIVID_YELLOW
/obj/item/vending_refill/wardrobe/atmos_wardrobe
machine_name = "AtmosDrobe"
@@ -195,7 +155,6 @@
/obj/item/radio/headset/headset_cargo = 3)
refill_canister = /obj/item/vending_refill/wardrobe/cargo_wardrobe
payment_department = ACCOUNT_CAR
- cost_multiplier_per_dept = list(ACCOUNT_CAR = 0)
/obj/item/vending_refill/wardrobe/cargo_wardrobe
machine_name = "CargoDrobe"
@@ -223,17 +182,16 @@
/obj/item/clothing/under/misc/mechsuitblue = 1)
contraband = list(/obj/item/clothing/suit/hooded/techpriest = 2)
refill_canister = /obj/item/vending_refill/wardrobe/robo_wardrobe
+ extra_price = PRICE_EXPENSIVE * 1.2
payment_department = ACCOUNT_SCI
- cost_multiplier_per_dept = list(ACCOUNT_SCI = 0)
-
/obj/item/vending_refill/wardrobe/robo_wardrobe
machine_name = "RoboDrobe"
/obj/machinery/vending/wardrobe/science_wardrobe
name = "SciDrobe"
- desc = "A simple vending machine suitable to dispense well tailored science clothing. Endorsed by Cubans."
+ desc = "A simple vending machine suitable to dispense well tailored science clothing. Endorsed by Space Cubans."
icon_state = "scidrobe"
- product_ads = "Longing for the smell of flesh plasma? Buy your science clothing now!;Made with 10% Auxetics, so you don't have to worry losing your arm!"
+ product_ads = "Longing for the smell of plasma burnt flesh? Buy your science clothing now!;Made with 10% Auxetics, so you don't have to worry about losing your arm!"
vend_reply = "Thank you for using the SciDrobe!"
products = list(/obj/item/clothing/accessory/pocketprotector = 5,
/obj/item/clothing/head/beret/sci = 3,
@@ -251,8 +209,6 @@
/obj/item/clothing/mask/gas = 5)
refill_canister = /obj/item/vending_refill/wardrobe/science_wardrobe
payment_department = ACCOUNT_SCI
- cost_multiplier_per_dept = list(ACCOUNT_SCI = 0)
-
/obj/item/vending_refill/wardrobe/science_wardrobe
machine_name = "SciDrobe"
@@ -274,7 +230,7 @@
/obj/item/clothing/mask/bandana = 4)
refill_canister = /obj/item/vending_refill/wardrobe/hydro_wardrobe
payment_department = ACCOUNT_SRV
- cost_multiplier_per_dept = list(ACCOUNT_SRV = 0)
+ light_color = LIGHT_COLOR_ELECTRIC_GREEN
/obj/item/vending_refill/wardrobe/hydro_wardrobe
machine_name = "HyDrobe"
@@ -299,9 +255,7 @@
/obj/item/clothing/glasses/regular/jamjar = 1,
/obj/item/storage/bag/books = 1)
refill_canister = /obj/item/vending_refill/wardrobe/curator_wardrobe
- payment_department = ACCOUNT_CIV
- cost_multiplier_per_dept = list(ACCOUNT_CIV = 0)
-
+ payment_department = ACCOUNT_SRV
/obj/item/vending_refill/wardrobe/curator_wardrobe
machine_name = "CuraDrobe"
@@ -331,8 +285,6 @@
/obj/item/storage/belt/bandolier = 1)
refill_canister = /obj/item/vending_refill/wardrobe/bar_wardrobe
payment_department = ACCOUNT_SRV
- cost_multiplier_per_dept = list(ACCOUNT_SRV = 0)
-
/obj/item/vending_refill/wardrobe/bar_wardrobe
machine_name = "BarDrobe"
@@ -340,8 +292,8 @@
name = "ChefDrobe"
desc = "This vending machine might not dispense meat, but it certainly dispenses chef related clothing."
icon_state = "chefdrobe"
- product_ads = "Our clothes are guaranteed to protect you from food splatters!;Now stocking recipe books!"
- vend_reply = "Thank you for using the ChefDrobe!;Just like your grandmother's old recipes!"
+ product_ads = "Our clothes are guaranteed to protect you from food splatters!"
+ vend_reply = "Thank you for using the ChefDrobe!"
products = list(/obj/item/clothing/under/suit/waiter = 3,
/obj/item/radio/headset/headset_srv = 4,
/obj/item/clothing/accessory/waistcoat = 3,
@@ -358,8 +310,6 @@
/obj/item/book/granter/crafting_recipe/coldcooking = 2)
refill_canister = /obj/item/vending_refill/wardrobe/chef_wardrobe
payment_department = ACCOUNT_SRV
- cost_multiplier_per_dept = list(ACCOUNT_SRV = 0)
-
/obj/item/vending_refill/wardrobe/chef_wardrobe
machine_name = "ChefDrobe"
@@ -390,8 +340,10 @@
/obj/item/screwdriver = 2,
/obj/item/stack/cable_coil/random = 4)
refill_canister = /obj/item/vending_refill/wardrobe/jani_wardrobe
+ default_price = PRICE_CHEAP
+ extra_price = PRICE_EXPENSIVE * 0.8
payment_department = ACCOUNT_SRV
- cost_multiplier_per_dept = list(ACCOUNT_SRV = 0)
+ light_color = COLOR_STRONG_MAGENTA
/obj/item/vending_refill/wardrobe/jani_wardrobe
machine_name = "JaniDrobe"
@@ -423,18 +375,16 @@
/obj/item/clothing/shoes/laceup = 3,
/obj/item/clothing/accessory/lawyers_badge = 3)
refill_canister = /obj/item/vending_refill/wardrobe/law_wardrobe
- payment_department = ACCOUNT_CIV
- cost_multiplier_per_dept = list(ACCOUNT_CIV = 0)
-
+ payment_department = ACCOUNT_SRV
/obj/item/vending_refill/wardrobe/law_wardrobe
machine_name = "LawDrobe"
/obj/machinery/vending/wardrobe/chap_wardrobe
- name = "ChapDrobe"
- desc = "This most blessed and holy machine vends clothing only suitable for chaplains to gaze upon."
+ name = "DeusVend"
+ desc = "God wills your purchase."
icon_state = "chapdrobe"
product_ads = "Are you being bothered by cultists or pesky revenants? Then come and dress like the holy man!;Clothes for men of the cloth!"
- vend_reply = "Thank you for using the ChapDrobe!"
+ vend_reply = "Thank you for using the DeusVend!"
products = list(/obj/item/choice_beacon/holy = 1,
/obj/item/storage/backpack/cultpack = 2,
/obj/item/clothing/accessory/pocketprotector/cosmetology = 2,
@@ -451,11 +401,9 @@
premium = list(/obj/item/toy/plush/plushvar = 1,
/obj/item/toy/plush/narplush = 1)
refill_canister = /obj/item/vending_refill/wardrobe/chap_wardrobe
- payment_department = ACCOUNT_CIV
- cost_multiplier_per_dept = list(ACCOUNT_CIV = 0)
-
+ payment_department = ACCOUNT_SRV
/obj/item/vending_refill/wardrobe/chap_wardrobe
- machine_name = "ChapDrobe"
+ machine_name = "DeusVend"
/obj/machinery/vending/wardrobe/chem_wardrobe
name = "ChemDrobe"
@@ -475,8 +423,6 @@
/obj/item/fermichem/pHbooklet = 3)
refill_canister = /obj/item/vending_refill/wardrobe/chem_wardrobe
payment_department = ACCOUNT_MED
- cost_multiplier_per_dept = list(ACCOUNT_MED = 0)
-
/obj/item/vending_refill/wardrobe/chem_wardrobe
machine_name = "ChemDrobe"
@@ -494,9 +440,7 @@
/obj/item/storage/backpack/genetics = 3,
/obj/item/storage/backpack/satchel/gen = 3)
refill_canister = /obj/item/vending_refill/wardrobe/gene_wardrobe
- payment_department = ACCOUNT_MED
- cost_multiplier_per_dept = list(ACCOUNT_MED = 0)
-
+ payment_department = ACCOUNT_SCI
/obj/item/vending_refill/wardrobe/gene_wardrobe
machine_name = "GeneDrobe"
@@ -517,11 +461,46 @@
/obj/item/storage/backpack/satchel/vir = 3)
refill_canister = /obj/item/vending_refill/wardrobe/viro_wardrobe
payment_department = ACCOUNT_MED
- cost_multiplier_per_dept = list(ACCOUNT_MED = 0)
-
/obj/item/vending_refill/wardrobe/viro_wardrobe
machine_name = "ViroDrobe"
+/obj/machinery/vending/wardrobe/det_wardrobe
+ name = "\improper DetDrobe"
+ desc = "A machine for all your detective needs, as long as you need clothes."
+ icon_state = "detdrobe"
+ product_ads = "Apply your brilliant deductive methods in style!"
+ vend_reply = "Thank you for using the DetDrobe!"
+ products = list(/obj/item/clothing/under/rank/security/detective = 2,
+ /obj/item/clothing/under/rank/security/detective/skirt = 2,
+ /obj/item/clothing/under/rank/security/detective/brown = 2,
+ /obj/item/clothing/under/rank/security/detective/brown/brown2 = 2,
+ /obj/item/clothing/under/rank/security/officer/blueshirt/seccorp/detcorp = 2,
+ /obj/item/clothing/under/rank/security/officer/util = 2,
+ /obj/item/clothing/shoes/sneakers/brown = 2,
+ /obj/item/clothing/suit/det_suit = 2,
+ /obj/item/clothing/head/fedora/det_hat = 2,
+ /obj/item/clothing/under/rank/security/detective/grey = 2,
+ /obj/item/clothing/under/rank/security/detective/grey/skirt = 2,
+ /obj/item/clothing/accessory/waistcoat = 2,
+ /obj/item/clothing/shoes/laceup = 2,
+ /obj/item/clothing/suit/det_suit/grey = 1,
+ /obj/item/clothing/suit/det_suit/forensicsred = 1,
+ /obj/item/clothing/suit/det_suit/forensicsred/long = 1,
+ /obj/item/clothing/suit/det_suit/forensicsblue = 1,
+ /obj/item/clothing/suit/det_suit/forensicsblue/long = 1,
+ /obj/item/clothing/head/fedora = 2,
+ /obj/item/clothing/gloves/color/black = 2,
+ /obj/item/clothing/gloves/color/latex = 2,
+ /obj/item/reagent_containers/food/drinks/flask/det = 2,
+ /obj/item/storage/fancy/cigarettes = 5)
+ premium = list(/obj/item/clothing/head/flatcap = 1)
+ refill_canister = /obj/item/vending_refill/wardrobe/det_wardrobe
+ extra_price = PRICE_EXPENSIVE * 1.75
+ payment_department = ACCOUNT_SEC
+
+/obj/item/vending_refill/wardrobe/det_wardrobe
+ machine_name = "DetDrobe"
+
/obj/machinery/vending/wardrobe/cap_wardrobe
name = "Captain's Wardrobe"
desc = "The latest and greatest in Nanotrasen fashion for your great leader."
@@ -552,10 +531,6 @@
default_price = PRICE_ALMOST_EXPENSIVE
extra_price = PRICE_ABOVE_EXPENSIVE
-/obj/machinery/vending/wardrobe/cap_wardrobe/Initialize()
- . = ..()
- cost_multiplier_per_dept = list("[ACCESS_CAPTAIN]" = 0)
-
/obj/item/vending_refill/wardrobe/cap_wardrobe
machine_name = "Captain's Wardrobe"
icon_state = "refill_caps"
diff --git a/code/modules/vending/youtool.dm b/code/modules/vending/youtool.dm
index 2119197aed..4b8a8c27e5 100644
--- a/code/modules/vending/youtool.dm
+++ b/code/modules/vending/youtool.dm
@@ -3,6 +3,7 @@
desc = "Tools for tools."
icon_state = "tool"
icon_deny = "tool-deny"
+ light_mask = "tool-light-mask"
products = list(/obj/item/stack/cable_coil/random = 15,
/obj/item/crowbar = 10,
/obj/item/weldingtool = 6,
@@ -19,13 +20,11 @@
/obj/item/multitool = 2)
premium = list(/obj/item/clothing/gloves/color/yellow = 2,
/obj/item/weldingtool/hugetank = 2)
- armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70)
- refill_canister = /obj/item/vending_refill/tool
- resistance_flags = FIRE_PROOF
+ refill_canister = /obj/item/vending_refill/youtool
default_price = PRICE_REALLY_CHEAP
extra_price = PRICE_EXPENSIVE
payment_department = ACCOUNT_ENG
- cost_multiplier_per_dept = list(ACCOUNT_ENG = 0)
-/obj/item/vending_refill/tool
+/obj/item/vending_refill/youtool
+ machine_name = "YouTool"
icon_state = "refill_engi"
diff --git a/icons/obj/modular_laptop.dmi b/icons/obj/modular_laptop.dmi
index 4c7f838db41acbd0f7efbcea463f54177cbe77be..92536f9ca6c036331965ed5f1dc9df602c253c7f 100644
GIT binary patch
delta 10892
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
delta 10634
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 (!pzT-;=zFl1Otr`R^7
zJEHpM2jpASzE9@g5D497&fFDkb>tqU5e{*U^C73HAVyeXpUpjv(cF
zQnwydR7s#iX9?nLN?kdScnTWN;6_KPZ9m#`Hvgvl0j~=8&zew6JHee;KFU0STKF6p
z1@goF*yBM#aXMG;_Kjc0`fk5^;LHMZ9>@>zM?XIRl#cuBY(wzEc4Rczsy{o9j46=S
Ws)x)hk|zTo@ObR=-dl+eN%{}yrFN?T
literal 18695
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