diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1/mistake_inevitable.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1/mistake_inevitable.dmm index 1deee17aee2e..091c1b2f53b1 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1/mistake_inevitable.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1/mistake_inevitable.dmm @@ -197,8 +197,8 @@ /turf/open/floor/engine, /area/ruin/syndicate_lava_base/testlab) "rX" = ( -/obj/machinery/atmospherics/components/unary/passive_vent, /obj/structure/lattice/catwalk, +/obj/machinery/atmospherics/components/unary/outlet_injector/on, /turf/template_noop, /area/ruin/syndicate_lava_base/testlab) "sa" = ( diff --git a/_maps/RandomRuins/SpaceRuins/oldstation.dmm b/_maps/RandomRuins/SpaceRuins/oldstation.dmm index 81e6e4e6245b..9771c0342e11 100644 --- a/_maps/RandomRuins/SpaceRuins/oldstation.dmm +++ b/_maps/RandomRuins/SpaceRuins/oldstation.dmm @@ -4847,10 +4847,13 @@ /area/ruin/space/ancientstation/beta/medbay) "ui" = ( /obj/effect/decal/cleanable/shreds, -/obj/structure/alien/weeds, /obj/structure/closet/crate/secure/science{ req_access = list("away_science") }, +/obj/item/encryptionkey/headset_uncommon, +/obj/item/encryptionkey/headset_uncommon, +/obj/item/encryptionkey/headset_uncommon, +/obj/structure/alien/weeds, /obj/item/transfer_valve, /obj/item/raw_anomaly_core/bluespace, /obj/item/raw_anomaly_core/random, @@ -8624,13 +8627,13 @@ /turf/open/floor/iron/solarpanel/airless, /area/ruin/space/solars/ancientstation/charlie/solars) "Uh" = ( -/obj/machinery/power/supermatter_crystal/shard, /obj/structure/closet/crate/engineering{ name = "supermatter shard crate"; secure = 1; locked = 1; icon_state = "engi_secure_crate" }, +/obj/machinery/power/supermatter_crystal/shard/oldstation, /turf/open/floor/iron/white/textured, /area/ruin/space/ancientstation/delta/proto) "Uj" = ( diff --git a/_maps/map_files/Blueshift/Blueshift.dmm b/_maps/map_files/Blueshift/Blueshift.dmm index 12e55043d40d..96658451b2c6 100644 --- a/_maps/map_files/Blueshift/Blueshift.dmm +++ b/_maps/map_files/Blueshift/Blueshift.dmm @@ -4701,6 +4701,10 @@ /obj/effect/turf_decal/tile/blue/fourcorners, /turf/open/floor/iron/white, /area/station/medical/treatment_center) +"aWw" = ( +/obj/structure/fans/tiny, +/turf/open/floor/plating, +/area/station/hallway/secondary/entry) "aWA" = ( /obj/structure/cable, /obj/effect/landmark/start/hangover, @@ -52113,9 +52117,6 @@ /obj/effect/turf_decal/trimline/red/line{ dir = 8 }, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, /turf/open/floor/iron/dark/textured, /area/station/ai_monitored/security/armory) "kdO" = ( @@ -55522,6 +55523,7 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper{ dir = 8 }, +/obj/structure/fans/tiny, /turf/open/floor/plating, /area/station/hallway/secondary/entry) "kKp" = ( @@ -170234,7 +170236,7 @@ ahn cXo cXo rMe -ivx +aWw rMe gLc gLc diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm index 82cec3c519f6..8be6a9fced01 100644 --- a/_maps/map_files/BoxStation/BoxStation.dmm +++ b/_maps/map_files/BoxStation/BoxStation.dmm @@ -26105,15 +26105,6 @@ pixel_x = 3; pixel_y = -3 }, -/obj/item/gun/energy/taser{ - pixel_y = 3; - pixel_x = -3 - }, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser{ - pixel_y = -3; - pixel_x = 3 - }, /turf/open/floor/iron/dark, /area/station/ai_monitored/security/armory) "iuJ" = ( diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index ef6c57926b9b..d1615ee5cb8e 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -48942,9 +48942,6 @@ }, /obj/effect/turf_decal/bot, /obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, /turf/open/floor/iron/dark, /area/station/ai_monitored/security/armory) "lzH" = ( diff --git a/_maps/map_files/Graveyard/Graveyard.dmm b/_maps/map_files/Graveyard/Graveyard.dmm index 5d56e33ab206..3ddc9961a7bf 100644 --- a/_maps/map_files/Graveyard/Graveyard.dmm +++ b/_maps/map_files/Graveyard/Graveyard.dmm @@ -32366,9 +32366,6 @@ /obj/item/gun/ballistic/automatic/pistol/paco/no_mag{ pixel_y = 6 }, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, /obj/effect/turf_decal/tile/dark_red/half{ dir = 1 }, diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index fbd64deb4e24..73d3fce608a4 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -8895,9 +8895,6 @@ /obj/item/gun/ballistic/automatic/pistol/paco/no_mag{ pixel_y = 0 }, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, /turf/open/floor/iron/dark/textured, /area/station/ai_monitored/security/armory/upper) "cIH" = ( diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm index 4d310a600bef..2905e94873ef 100644 --- a/_maps/map_files/KiloStation/KiloStation.dmm +++ b/_maps/map_files/KiloStation/KiloStation.dmm @@ -15352,9 +15352,6 @@ /obj/structure/window/reinforced/spawner/directional/south, /obj/structure/window/reinforced/spawner/directional/west, /obj/effect/turf_decal/tile/neutral/anticorner/contrasted, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, /turf/open/floor/iron/dark/textured_large, /area/station/ai_monitored/security/armory) "eRh" = ( diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 75b2cdf82042..427bd05d6564 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -33406,9 +33406,6 @@ /obj/item/gun/ballistic/automatic/pistol/paco/no_mag{ pixel_y = 6 }, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, /turf/open/floor/iron/dark, /area/station/ai_monitored/security/armory) "lFo" = ( diff --git a/_maps/map_files/Ouroboros/Ouroboros.dmm b/_maps/map_files/Ouroboros/Ouroboros.dmm index fd7f4d0f0e21..593c6d5967e5 100644 --- a/_maps/map_files/Ouroboros/Ouroboros.dmm +++ b/_maps/map_files/Ouroboros/Ouroboros.dmm @@ -28538,9 +28538,6 @@ c_tag = "Security - Armory" }, /obj/effect/spawner/random/armory/disablers, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, /turf/open/floor/engine, /area/station/ai_monitored/security/armory) "ixo" = ( diff --git a/_maps/map_files/Theseus/Theseus.dmm b/_maps/map_files/Theseus/Theseus.dmm index a8b4e408725a..dad22ba8ccd3 100644 --- a/_maps/map_files/Theseus/Theseus.dmm +++ b/_maps/map_files/Theseus/Theseus.dmm @@ -4443,6 +4443,15 @@ }, /turf/open/floor/iron/dark, /area/station/security/range) +"bqW" = ( +/obj/effect/turf_decal/tile/neutral/full, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/closet/emcloset, +/turf/open/floor/iron, +/area/station/security/processing) "brm" = ( /obj/machinery/chem_master/condimaster{ name = "CondiMaster Neo" @@ -9450,7 +9459,6 @@ /obj/machinery/door/airlock/external{ name = "Gulag Shuttle Airlock" }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, /turf/open/floor/plating, /area/station/security/processing) "cQj" = ( @@ -19743,10 +19751,6 @@ /turf/open/floor/iron, /area/station/construction) "fVH" = ( -/obj/machinery/computer/prisoner/gulag_teleporter_computer{ - dir = 8 - }, -/obj/effect/turf_decal/bot, /obj/effect/turf_decal/trimline/red/filled/line{ dir = 4 }, @@ -19756,6 +19760,9 @@ /obj/effect/turf_decal/stripes/line{ dir = 1 }, +/obj/machinery/gulag_teleporter, +/obj/effect/turf_decal/bot, +/obj/machinery/light/directional/east, /turf/open/floor/iron, /area/station/security/processing) "fVQ" = ( @@ -21898,21 +21905,16 @@ /turf/open/floor/iron/dark, /area/station/security/warden) "gFe" = ( -/obj/machinery/gulag_teleporter, -/obj/effect/turf_decal/bot, -/obj/machinery/gulag_item_reclaimer{ - pixel_x = 31 - }, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 4 - }, /obj/effect/turf_decal/tile/neutral/half{ dir = 8 }, /obj/effect/turf_decal/stripes/line{ dir = 1 }, -/obj/machinery/light/directional/east, +/obj/effect/turf_decal/trimline/red/filled/corner, +/obj/effect/turf_decal/trimline/red/filled/corner{ + dir = 4 + }, /turf/open/floor/iron, /area/station/security/processing) "gFf" = ( @@ -33984,6 +33986,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/effect/spawner/xmastree, /turf/open/floor/iron, /area/station/hallway/primary/central) "kgT" = ( @@ -42128,13 +42131,15 @@ /turf/closed/wall, /area/station/commons/fitness/recreation/entertainment) "mxA" = ( -/obj/structure/closet/emcloset, -/obj/effect/turf_decal/delivery, /obj/effect/turf_decal/trimline/red/filled/line{ dir = 6 }, /obj/machinery/power/apc/auto_name/directional/east, /obj/structure/cable, +/obj/machinery/computer/prisoner/gulag_teleporter_computer{ + dir = 8 + }, +/obj/effect/turf_decal/bot, /turf/open/floor/iron, /area/station/security/processing) "mxD" = ( @@ -55293,12 +55298,13 @@ /area/station/hallway/primary/central/fore) "qtB" = ( /obj/structure/table, -/obj/item/knife{ +/obj/item/knife/kitchen{ pixel_x = -1; pixel_y = 5 }, -/obj/item/knife{ - pixel_x = -10 +/obj/item/knife/kitchen{ + pixel_x = -10; + pixel_y = 0 }, /obj/item/reagent_containers/cup/rag{ pixel_y = 5; @@ -58457,6 +58463,17 @@ /obj/effect/turf_decal/bot, /turf/open/floor/plating, /area/station/maintenance/starboard/central) +"roL" = ( +/obj/machinery/door/airlock/security, +/obj/effect/mapping_helpers/airlock/access/all/security/brig, +/obj/effect/mapping_helpers/airlock/unres{ + dir = 8 + }, +/obj/effect/turf_decal/trimline/red/filled/end{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/maintenance/port/fore) "rpb" = ( /turf/open/floor/engine/plasma, /area/station/engineering/atmos) @@ -67254,7 +67271,6 @@ /turf/open/floor/carpet, /area/station/hallway/secondary/exit/departure_lounge) "tQX" = ( -/obj/item/kirbyplants/random, /obj/effect/turf_decal/stripes/line{ dir = 5 }, @@ -67263,6 +67279,10 @@ }, /obj/structure/cable, /obj/machinery/camera/directional/east, +/obj/effect/turf_decal/delivery, +/obj/machinery/gulag_item_reclaimer{ + pixel_x = 31 + }, /turf/open/floor/iron, /area/station/security/processing) "tQY" = ( @@ -77835,7 +77855,6 @@ /obj/machinery/door/airlock/external{ name = "Gulag Shuttle Airlock" }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, /obj/machinery/light/floor/has_bulb, /turf/open/floor/plating, /area/station/security/processing) @@ -109295,7 +109314,7 @@ fTB ctl fTB rtt -cze +bqW mBO gxT xhM @@ -110066,7 +110085,7 @@ ijL ijL ijL ijL -ijL +roL ijL ijL xhM diff --git a/_maps/map_files/Voidraptor/VoidRaptor.dmm b/_maps/map_files/Voidraptor/VoidRaptor.dmm index dbae07341eae..167607ecb8c0 100644 --- a/_maps/map_files/Voidraptor/VoidRaptor.dmm +++ b/_maps/map_files/Voidraptor/VoidRaptor.dmm @@ -27190,9 +27190,6 @@ dir = 8 }, /obj/effect/turf_decal/delivery, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, /turf/open/floor/iron/dark/textured_large, /area/station/ai_monitored/security/armory) "hPi" = ( diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index d733578cad42..bbd27a42418e 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -19367,6 +19367,11 @@ }, /turf/open/floor/carpet/red, /area/cruiser_dock) +"bhS" = ( +/obj/structure/table/wood, +/obj/machinery/microwave, +/turf/open/floor/iron/vaporwave, +/area/centcom/central_command_areas/adminroom) "bip" = ( /obj/effect/spawner/random/vending/snackvend, /obj/effect/turf_decal/bot_white, @@ -19542,6 +19547,12 @@ }, /turf/open/water/arena, /area/centcom/central_command_areas/admin) +"bxX" = ( +/obj/effect/spawner/random/trash/hobo_squat, +/obj/effect/spawner/random/trash/garbage, +/obj/effect/spawner/random/trash/mess, +/turf/open/floor/plating/rust, +/area/centcom/central_command_areas/adminroom) "bys" = ( /obj/structure/sign/departments/chemistry/alt/directional/east, /obj/effect/turf_decal/tile/orange/half/contrasted{ @@ -19738,6 +19749,20 @@ }, /turf/open/floor/iron/dark, /area/centcom/central_command_areas/hall) +"bYc" = ( +/obj/effect/turf_decal/siding/white{ + dir = 5 + }, +/obj/effect/spawner/random/trash/caution_sign, +/turf/open/floor/grass, +/area/centcom/central_command_areas/admin) +"bYp" = ( +/obj/effect/turf_decal/siding/white{ + dir = 1 + }, +/obj/effect/spawner/random/trash/caution_sign, +/turf/open/floor/grass, +/area/centcom/central_command_areas/admin) "bYA" = ( /obj/machinery/light/neon_lining{ dir = 1; @@ -20170,6 +20195,11 @@ }, /turf/open/floor/iron/white/textured, /area/centcom/central_command_areas/admin) +"cYr" = ( +/obj/structure/bonfire/prelit, +/obj/effect/spawner/random/trash/cigbutt, +/turf/open/floor/plating/rust, +/area/centcom/central_command_areas/adminroom) "dbJ" = ( /obj/machinery/door/airlock/centcom{ name = "Veth's Plantery" @@ -20686,6 +20716,13 @@ "exa" = ( /turf/open/floor/sandy_dirt, /area/centcom/central_command_areas/admin) +"exj" = ( +/obj/effect/spawner/random/trash/bacteria, +/obj/effect/spawner/random/trash/garbage, +/obj/effect/spawner/random/trash/cigbutt, +/obj/effect/spawner/random/trash/mess, +/turf/open/floor/iron/vaporwave, +/area/centcom/central_command_areas/adminroom) "exk" = ( /obj/structure/chair/office/light{ dir = 1 @@ -21732,6 +21769,25 @@ /obj/item/clothing/under/costume/gondola, /turf/open/floor/eighties/red, /area/centcom/central_command_areas/admin) +"gUv" = ( +/obj/effect/spawner/random/trash/bacteria, +/obj/effect/spawner/random/trash/bacteria, +/obj/effect/spawner/random/trash/graffiti, +/obj/effect/spawner/random/trash/garbage, +/obj/effect/spawner/random/trash/cigbutt, +/obj/effect/spawner/random/trash/mess, +/turf/open/floor/plating/rust, +/area/centcom/central_command_areas/adminroom) +"gWc" = ( +/obj/effect/turf_decal/caution/red{ + pixel_y = -15 + }, +/obj/effect/turf_decal/caution/red, +/obj/effect/turf_decal/caution/red{ + pixel_y = -8 + }, +/turf/open/floor/iron/dark/smooth_large, +/area/centcom/central_command_areas/admin) "gXp" = ( /obj/effect/turf_decal/siding/wood{ dir = 8 @@ -22224,6 +22280,13 @@ dir = 8 }, /area/centcom/central_command_areas/adminroom) +"hPF" = ( +/obj/effect/turf_decal/siding/white{ + dir = 9 + }, +/obj/effect/spawner/random/trash/caution_sign, +/turf/open/floor/grass, +/area/centcom/central_command_areas/admin) "hQH" = ( /obj/effect/turf_decal/plaque{ icon_state = "L13" @@ -23029,6 +23092,13 @@ /obj/structure/fake_stairs/wood/directional/north, /turf/open/misc/dirt/station, /area/centcom/central_command_areas/retirement_yard) +"jUo" = ( +/obj/machinery/computer/camera_advanced{ + dir = 8 + }, +/obj/effect/spawner/random/trash/cigbutt, +/turf/open/floor/plating/rust, +/area/centcom/central_command_areas/adminroom) "jUG" = ( /mob/living/basic/xenofauna/meatbeast, /turf/open/floor/material/meat, @@ -23351,6 +23421,10 @@ /obj/machinery/light/directional/north, /turf/open/water/arena, /area/centcom/central_command_areas/admin) +"kSK" = ( +/obj/structure/mineral_door/wood, +/turf/open/floor/plating/rust, +/area/centcom/central_command_areas/adminroom) "kSU" = ( /obj/structure/mineral_door/wood{ color = "543e27"; @@ -23773,6 +23847,12 @@ }, /turf/open/floor/wood/large, /area/centcom/central_command_areas/adminroom) +"msE" = ( +/obj/effect/spawner/random/trash/bacteria, +/obj/effect/spawner/random/trash/garbage, +/obj/effect/spawner/random/trash/garbage, +/turf/open/floor/plating/rust, +/area/centcom/central_command_areas/adminroom) "mvd" = ( /obj/effect/decal/cleanable/blood/gibs/limb{ dir = 8; @@ -24164,6 +24244,12 @@ /obj/machinery/scanner_gate, /turf/open/floor/sandy_dirt, /area/centcom/central_command_areas/admin) +"nGN" = ( +/obj/effect/spawner/random/trash/bacteria, +/obj/effect/spawner/random/trash/garbage, +/obj/effect/spawner/random/trash/mess, +/turf/open/floor/iron/vaporwave, +/area/centcom/central_command_areas/adminroom) "nHl" = ( /obj/structure/flora/grass/jungle/b/style_2, /turf/open/misc/dirt/station, @@ -24473,6 +24559,10 @@ }, /turf/open/floor/iron/dark, /area/centcom/central_command_areas/admin) +"oBb" = ( +/obj/structure/barricade/wooden/crude, +/turf/closed/indestructible/fakeglass, +/area/centcom/central_command_areas/adminroom) "oBm" = ( /obj/effect/turf_decal/trimline/dark_blue/line{ dir = 4 @@ -24759,6 +24849,13 @@ /obj/structure/mop_bucket, /turf/open/floor/iron/dark/textured_large, /area/cruiser_dock) +"ptP" = ( +/obj/machinery/computer/communications/syndicate{ + dir = 8 + }, +/obj/effect/spawner/random/trash/cigbutt, +/turf/open/floor/plating/rust, +/area/centcom/central_command_areas/adminroom) "pvq" = ( /obj/machinery/biomass_recycler, /obj/item/stack/biomass, @@ -25342,6 +25439,13 @@ }, /turf/open/misc/sandy_dirt, /area/centcom/central_command_areas/admin) +"qNR" = ( +/obj/structure/chair/comfy/teal{ + dir = 4 + }, +/obj/effect/spawner/random/trash/cigbutt, +/turf/open/floor/plating/rust, +/area/centcom/central_command_areas/adminroom) "qOJ" = ( /obj/effect/turf_decal/trimline/yellow/filled/line{ dir = 1 @@ -25972,6 +26076,10 @@ }, /turf/open/floor/carpet/neon/simple/green/nodots, /area/centcom/central_command_areas/admin) +"sHS" = ( +/obj/structure/bed/maint, +/turf/open/floor/iron/vaporwave, +/area/centcom/central_command_areas/adminroom) "sIk" = ( /obj/machinery/light/floor/has_bulb, /obj/structure/window/reinforced/spawner/directional/west{ @@ -26029,6 +26137,12 @@ }, /turf/open/floor/carpet/orange, /area/centcom/central_command_areas/admin) +"sRa" = ( +/obj/effect/spawner/random/trash/garbage, +/obj/effect/spawner/random/trash/garbage, +/obj/effect/spawner/random/trash/mess, +/turf/open/floor/iron/vaporwave, +/area/centcom/central_command_areas/adminroom) "sRv" = ( /obj/effect/turf_decal/box/corners{ dir = 1 @@ -26036,6 +26150,11 @@ /obj/structure/shipping_container/vitezstvi, /turf/open/floor/iron/dark/textured_large, /area/cruiser_dock) +"sSL" = ( +/obj/structure/barricade/wooden/crude, +/obj/effect/spawner/random/trash/cigbutt, +/turf/closed/indestructible/fakeglass, +/area/centcom/central_command_areas/adminroom) "sTV" = ( /obj/effect/turf_decal/tile/purple/half/contrasted{ dir = 4 @@ -26688,6 +26807,14 @@ }, /turf/open/floor/glass/plasma, /area/centcom/central_command_areas/adminroom) +"uFf" = ( +/obj/effect/spawner/random/trash/bacteria, +/obj/effect/spawner/random/trash/garbage, +/obj/effect/spawner/random/trash/garbage, +/obj/effect/spawner/random/trash/mess, +/obj/effect/spawner/random/trash/mess, +/turf/open/floor/plating/rust, +/area/centcom/central_command_areas/adminroom) "uGL" = ( /obj/structure/reagent_dispensers/water_cooler, /obj/effect/turf_decal/bot_white, @@ -26696,6 +26823,13 @@ }, /turf/open/floor/iron/dark/textured_large, /area/cruiser_dock) +"uHB" = ( +/obj/structure/table/wood, +/obj/machinery/chem_dispenser/drinks/beer/fullupgrade{ + dir = 4 + }, +/turf/open/floor/plating/rust, +/area/centcom/central_command_areas/adminroom) "uIG" = ( /obj/machinery/door/airlock/centcom{ name = "NT Helpdesk" @@ -26740,6 +26874,12 @@ /obj/effect/mapping_helpers/airlock/access/any/admin/captain, /turf/open/floor/iron/dark/smooth_large, /area/centcom/central_command_areas/admin) +"uPO" = ( +/obj/effect/spawner/random/trash/garbage, +/obj/effect/spawner/random/trash/cigbutt, +/obj/effect/spawner/random/trash/mess, +/turf/open/floor/iron/vaporwave, +/area/centcom/central_command_areas/adminroom) "uPS" = ( /obj/effect/turf_decal/siding/dark_green{ dir = 8 @@ -26751,6 +26891,12 @@ /obj/machinery/light/dim/directional/south, /turf/open/floor/wood/tile, /area/centcom/central_command_areas/admin) +"uRb" = ( +/obj/effect/spawner/random/trash/bacteria, +/obj/effect/spawner/random/trash/graffiti, +/obj/effect/spawner/random/trash/cigbutt, +/turf/open/floor/plating/rust, +/area/centcom/central_command_areas/adminroom) "uSs" = ( /obj/structure/statue/sandstone/venus{ dir = 1; @@ -27079,6 +27225,16 @@ "vFm" = ( /turf/open/floor/mineral/titanium/purple, /area/centcom/central_command_areas/adminroom) +"vFv" = ( +/obj/structure/closet/secure_closet/freezer/fridge, +/obj/item/food/meat/slab/human, +/obj/item/food/meat/slab/human, +/obj/item/food/meat/slab/human, +/obj/item/food/meat/slab/human, +/obj/item/food/meat/slab/human, +/obj/effect/spawner/random/trash/graffiti, +/turf/open/floor/iron/vaporwave, +/area/centcom/central_command_areas/adminroom) "vFK" = ( /obj/structure/window/reinforced/spawner/directional/south, /obj/machinery/chem_heater/debug, @@ -27318,11 +27474,23 @@ }, /turf/open/floor/iron/dark/herringbone, /area/centcom/central_command_areas/evacuation) +"wdA" = ( +/obj/effect/spawner/random/trash/bacteria, +/obj/effect/spawner/random/trash/garbage, +/obj/effect/spawner/random/trash/cigbutt, +/obj/effect/spawner/random/trash/mess, +/turf/open/floor/plating/rust, +/area/centcom/central_command_areas/adminroom) "wel" = ( /obj/effect/turf_decal/stripes/line, /obj/structure/flora/bush/pale/style_3, /turf/open/misc/dirt/station, /area/centcom/central_command_areas/admin) +"wfx" = ( +/obj/structure/barricade/wooden/crude, +/obj/structure/curtain/cloth, +/turf/closed/indestructible/fakeglass, +/area/centcom/central_command_areas/adminroom) "wge" = ( /obj/effect/turf_decal/siding/wood{ dir = 4 @@ -27660,6 +27828,11 @@ dir = 8 }, /area/cruiser_dock) +"wTG" = ( +/obj/effect/spawner/random/trash/bacteria, +/obj/effect/spawner/random/trash/garbage, +/turf/open/floor/iron/vaporwave, +/area/centcom/central_command_areas/adminroom) "wTZ" = ( /obj/machinery/vending/coffee, /obj/effect/turf_decal/bot_white, @@ -27992,6 +28165,14 @@ }, /turf/closed/indestructible/wood, /area/centcom/central_command_areas/admin) +"xQP" = ( +/obj/effect/spawner/random/trash/bacteria, +/obj/effect/spawner/random/trash/garbage, +/obj/effect/spawner/random/trash/garbage, +/obj/effect/spawner/random/trash/cigbutt, +/obj/effect/spawner/random/trash/mess, +/turf/open/floor/plating/rust, +/area/centcom/central_command_areas/adminroom) "xRr" = ( /obj/structure/chair/office/light{ dir = 1 @@ -53192,17 +53373,17 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa +aOn +aOn +aOn +aOn +aOn +kpH +kpH +kpH +kpH +kpH +kpH aaa aaa aaa @@ -53449,17 +53630,17 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa +aOn +yhF +mUv +yhF +bYp +wfx +exj +uHB +vFv +cYr +kpH aaa aaa aaa @@ -53706,17 +53887,17 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa +aOn +yhF +mUv +yhF +bYc +wfx +msE +uFf +xQP +wTG +kpH aaa aaa aaa @@ -53963,17 +54144,17 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa +aOn +yhF +mUv +yhF +gWc +kSK +nGN +uRb +sRa +bhS +kpH aaa aaa aaa @@ -54220,17 +54401,17 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa +aOn +yhF +mUv +yhF +hPF +wfx +bxX +qNR +gUv +wdA +kpH aaa aaa aaa @@ -54477,17 +54658,17 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa +aOn +yhF +mUv +yhF +bYp +wfx +uPO +ptP +jUo +sHS +kpH aaa aaa aaa @@ -54735,14 +54916,14 @@ kpH kpH kpH aOn +yhF +mUv +yhF aOn -aOn -aOn -aOn -kpH -kpH kpH +sSL kpH +oBb kpH kpH aaa diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index c5c9f195b510..2d35e94b2274 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -51354,9 +51354,6 @@ }, /obj/structure/window/reinforced/spawner/directional/north, /obj/structure/window/reinforced/spawner/directional/south, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, -/obj/item/gun/energy/taser, /turf/open/floor/iron, /area/station/ai_monitored/security/armory) "pph" = ( diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm index 6be8af3db530..844faad63cba 100644 --- a/code/__DEFINES/access.dm +++ b/code/__DEFINES/access.dm @@ -474,6 +474,7 @@ #define REGION_RESEARCH "Research" /// Used to seed the accesses_by_region list in SSid_access. A list of all research regional accesses that are overseen by the RD. #define REGION_ACCESS_RESEARCH list( \ + ACCESS_AI_UPLOAD, \ ACCESS_GENETICS, \ ACCESS_MECH_SCIENCE, \ ACCESS_MINISAT, \ diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 020efe8d0858..47d703974ae3 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -226,6 +226,9 @@ GLOBAL_LIST_INIT(ai_employers, list( /// Checks if the given mob is either a heretic, heretic monster or a lunatic. #define IS_HERETIC_OR_MONSTER(mob) (IS_HERETIC(mob) || IS_HERETIC_MONSTER(mob) || IS_LUNATIC(mob)) +/// Checks if the given mob is in the mansus realm +#define IS_IN_MANSUS(mob) (istype(get_area(mob), /area/centcom/heretic_sacrifice)) + /// Checks if the given mob is a wizard #define IS_WIZARD(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/wizard)) diff --git a/code/__DEFINES/maps.dm b/code/__DEFINES/maps.dm index d59e0477c798..1bccfebfc558 100644 --- a/code/__DEFINES/maps.dm +++ b/code/__DEFINES/maps.dm @@ -61,7 +61,7 @@ Always compile, always use that verb, and always make sure that it works for wha #define MAP_MAXZ 6 /// Path for the next_map.json file, if someone, for some messed up reason, wants to change it. -#define PATH_TO_NEXT_MAP_JSON "data/next_map.json" +#define PATH_TO_NEXT_MAP_JSON (world.GetConfig("env", "AUXTOOLS_DEBUG_DLL") ? "data/next_map.json" : "data/next_map.[world.port].json") // monkestation edit: messed up cat here, i changed it (added world.port to it if there's no debugger attached) /// List of directories we can load map .json files from #define MAP_DIRECTORY_MAPS "_maps" diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index c9d51b852c76..77b3092cfc68 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -7,6 +7,16 @@ /// if it only allows one, and new instances just instead refresh the timer #define STATUS_EFFECT_REFRESH 3 +/// Use in status effect "duration" to make it last forever +#define STATUS_EFFECT_PERMANENT -1 +/// Use in status effect "tick_interval" to prevent it from calling tick() +#define STATUS_EFFECT_NO_TICK -1 + +/// Indicates this status effect is an abstract type, ie not instantiated +/// Doesn't actually do anything in practice, primarily just a marker / used in unit tests, +/// so don't worry if your abstract status effect doesn't actually set this +#define STATUS_EFFECT_ID_ABSTRACT "abstract" + ///Processing flags - used to define the speed at which the status will work ///This is fast - 0.2s between ticks (I believe!) #define STATUS_EFFECT_FAST_PROCESS 0 diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm index 42f2d5fc31fe..7e1ba820dd8b 100644 --- a/code/__DEFINES/tgs.dm +++ b/code/__DEFINES/tgs.dm @@ -1,7 +1,7 @@ // tgstation-server DMAPI // The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in IETF RFC 2119. -#define TGS_DMAPI_VERSION "7.3.0" +#define TGS_DMAPI_VERSION "7.3.1" // All functions and datums outside this document are subject to change with any version and should not be relied on. @@ -58,6 +58,11 @@ #define TGS_FILE2TEXT_NATIVE file2text #endif +// SpacemanDMM compatibility +#ifndef CAN_BE_REDEFINED +#define CAN_BE_REDEFINED(X) +#endif + // EVENT CODES /// Before a reboot mode change, extras parameters are the current and new reboot mode enums. @@ -160,6 +165,7 @@ * * http_handler - Optional user defined [/datum/tgs_http_handler]. */ /world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE, datum/tgs_http_handler/http_handler) + CAN_BE_REDEFINED(TRUE) return /** @@ -170,6 +176,7 @@ * This function should not be called before ..() in [/world/proc/New]. */ /world/proc/TgsInitializationComplete() + CAN_BE_REDEFINED(TRUE) return /// Consumers MUST run this macro at the start of [/world/proc/Topic]. @@ -177,6 +184,7 @@ /// Consumers MUST call this as late as possible in [world/proc/Reboot] (BEFORE ..()). /world/proc/TgsReboot() + CAN_BE_REDEFINED(TRUE) return // DATUM DEFINITIONS @@ -214,6 +222,7 @@ * Returns [TRUE]/[FALSE] based on if the [/datum/tgs_version] contains wildcards. */ /datum/tgs_version/proc/Wildcard() + CAN_BE_REDEFINED(TRUE) return /** @@ -222,6 +231,7 @@ * other_version - The [/datum/tgs_version] to compare against. */ /datum/tgs_version/proc/Equals(datum/tgs_version/other_version) + CAN_BE_REDEFINED(TRUE) return /// Represents a merge of a GitHub pull request. @@ -459,16 +469,19 @@ /// Returns the maximum supported [/datum/tgs_version] of the DMAPI. /world/proc/TgsMaximumApiVersion() + CAN_BE_REDEFINED(TRUE) return /// Returns the minimum supported [/datum/tgs_version] of the DMAPI. /world/proc/TgsMinimumApiVersion() + CAN_BE_REDEFINED(TRUE) return /** * Returns [TRUE] if DreamDaemon was launched under TGS, the API matches, and was properly initialized. [FALSE] will be returned otherwise. */ /world/proc/TgsAvailable() + CAN_BE_REDEFINED(TRUE) return // No function below this succeeds if it TgsAvailable() returns FALSE or if TgsNew() has yet to be called. @@ -480,6 +493,7 @@ * If TGS has not requested a [TGS_REBOOT_MODE_SHUTDOWN] DreamDaemon will be launched again. */ /world/proc/TgsEndProcess() + CAN_BE_REDEFINED(TRUE) return /** @@ -490,6 +504,7 @@ * admin_only: If [TRUE], message will be sent to admin connected chats. Vice-versa applies. */ /world/proc/TgsTargetedChatBroadcast(datum/tgs_message_content/message, admin_only = FALSE) + CAN_BE_REDEFINED(TRUE) return /** @@ -500,6 +515,7 @@ * user: The [/datum/tgs_chat_user] to PM. */ /world/proc/TgsChatPrivateMessage(datum/tgs_message_content/message, datum/tgs_chat_user/user) + CAN_BE_REDEFINED(TRUE) return /** @@ -510,42 +526,52 @@ * channels - Optional list of [/datum/tgs_chat_channel]s to restrict the message to. */ /world/proc/TgsChatBroadcast(datum/tgs_message_content/message, list/channels = null) + CAN_BE_REDEFINED(TRUE) return /// Returns the current [/datum/tgs_version] of TGS if it is running the server, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping! /world/proc/TgsVersion() + CAN_BE_REDEFINED(TRUE) return /// Returns the running engine type /world/proc/TgsEngine() + CAN_BE_REDEFINED(TRUE) return /// Returns the current [/datum/tgs_version] of the DMAPI being used if it was activated, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping! /world/proc/TgsApiVersion() + CAN_BE_REDEFINED(TRUE) return /// Returns the name of the TGS instance running the game if TGS is present, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping! /world/proc/TgsInstanceName() + CAN_BE_REDEFINED(TRUE) return /// Return the current [/datum/tgs_revision_information] of the running server if TGS is present, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping! /world/proc/TgsRevision() + CAN_BE_REDEFINED(TRUE) return /// Returns the current BYOND security level as a TGS_SECURITY_ define if TGS is present, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping! /world/proc/TgsSecurityLevel() + CAN_BE_REDEFINED(TRUE) return /// Returns the current BYOND visibility level as a TGS_VISIBILITY_ define if TGS is present, null otherwise. Requires TGS to be using interop API version 5 or higher otherwise the string "___unimplemented" wil be returned. This function may sleep if the call to [/world/proc/TgsNew] is sleeping! /world/proc/TgsVisibility() + CAN_BE_REDEFINED(TRUE) return /// Returns a list of active [/datum/tgs_revision_information/test_merge]s if TGS is present, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping! /world/proc/TgsTestMerges() + CAN_BE_REDEFINED(TRUE) return /// Returns a list of connected [/datum/tgs_chat_channel]s if TGS is present, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping! /world/proc/TgsChatChannelInfo() + CAN_BE_REDEFINED(TRUE) return /** @@ -556,6 +582,7 @@ * wait_for_completion - If set, this function will not return until the event has run to completion. */ /world/proc/TgsTriggerEvent(event_name, list/parameters, wait_for_completion = FALSE) + CAN_BE_REDEFINED(TRUE) return /* diff --git a/code/__DEFINES/traits/monkestation/declarations.dm b/code/__DEFINES/traits/monkestation/declarations.dm index 8a07f00fb954..a26127eb0ff5 100644 --- a/code/__DEFINES/traits/monkestation/declarations.dm +++ b/code/__DEFINES/traits/monkestation/declarations.dm @@ -121,6 +121,8 @@ #define TRAIT_BYPASS_COMPRESS_CHECK "can_compress_anyways" /// This item is considered "trash" (and will be eaten by cleaner slimes) #define TRAIT_TRASH_ITEM "trash_item" +/// This item came from a gift. +#define TRAIT_GIFT_ITEM "gift_item" // /atom/movable /// Things with this trait can pass through wooden barricades. diff --git a/code/__DEFINES/~monkestation/vv.dm b/code/__DEFINES/~monkestation/vv.dm new file mode 100644 index 000000000000..8acf0c7ef495 --- /dev/null +++ b/code/__DEFINES/~monkestation/vv.dm @@ -0,0 +1 @@ +#define VV_HK_EXAMINE_GIFT "examine_gift" diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index e367535713bc..7112c0447ae6 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -301,6 +301,7 @@ GLOBAL_LIST_INIT(round_end_images, world.file2list("data/image_urls.txt")) // MO // monkestation start: token backups, monkecoin rewards, challenges, and roundend webhook save_tokens() + refund_cassette() distribute_rewards() sleep(5 SECONDS) ready_for_reboot = TRUE diff --git a/code/__HELPERS/~monkestation-helpers/atoms.dm b/code/__HELPERS/~monkestation-helpers/atoms.dm index 67b81a50dafb..44f26ac804e0 100644 --- a/code/__HELPERS/~monkestation-helpers/atoms.dm +++ b/code/__HELPERS/~monkestation-helpers/atoms.dm @@ -26,3 +26,11 @@ default_typecache ||= typecacheof(list(/obj/effect, /atom/movable/screen)) typecache = default_typecache return typecache_filter_list_reverse(src.contents, typecache) + +/// Returns a list of all items in our contents that were obtained from gifts. +/atom/proc/get_all_gift_contents() as /list + RETURN_TYPE(/list/obj/item) + . = list() + for(var/obj/item/thing as anything in get_all_contents_type(/obj/item)) + if(!QDELETED(thing) && HAS_TRAIT(thing, TRAIT_GIFT_ITEM)) + . += thing diff --git a/code/__HELPERS/~monkestation-helpers/icons.dm b/code/__HELPERS/~monkestation-helpers/icons.dm index 73b6900edc93..ec8d795bdf4b 100644 --- a/code/__HELPERS/~monkestation-helpers/icons.dm +++ b/code/__HELPERS/~monkestation-helpers/icons.dm @@ -19,3 +19,47 @@ fdel(TMP_UPSCALE_PATH) #undef TMP_UPSCALE_PATH + +/// Returns the (isolated) security HUD icon for the given job. +/proc/get_job_hud_icon(datum/job/job, include_unknown = FALSE) as /icon + RETURN_TYPE(/icon) + var/static/list/icon_cache + var/static/list/unknown_huds + if(isnull(job)) + return + else if(!is_job(job)) + if(ispath(job, /datum/job)) + job = locate(job) in SSjob.all_occupations + else if(istext(job)) + job_loop: + for(var/datum/job/job_instance as anything in SSjob.all_occupations) + if(cmptext(job_instance.title, job) || cmptext(job_instance.config_tag, job)) + job = job_instance + break + for(var/alt_title in job_instance.alt_titles) + if(cmptext(alt_title, job)) + job = job_instance + break job_loop + if(!job) + return null + + // populate the cache if it hasn't been already + if(isnull(icon_cache)) + icon_cache = list() + unknown_huds = list() + for(var/datum/job/job_instance as anything in SSjob.all_occupations) + var/datum/outfit/job_outfit = job_instance.outfit + if(!job_outfit || !job_outfit::id_trim) + continue + var/datum/id_trim/job_trim = job_outfit::id_trim + var/icon_state = job_trim::sechud_icon_state + if(!icon_state || icon_state == SECHUD_UNKNOWN) + icon_state = "hud_noid" + unknown_huds[job_instance.type] = TRUE + var/icon/sechud_icon = icon('icons/mob/huds/hud.dmi', icon_state) + sechud_icon.Crop(1, 17, 8, 24) + icon_cache[job_instance.type] = sechud_icon + + var/job_type = job.type + if(icon_cache[job_type] && (include_unknown || !unknown_huds[job_type])) + return icon(icon_cache[job_type]) diff --git a/code/__HELPERS/~monkestation-helpers/roundend.dm b/code/__HELPERS/~monkestation-helpers/roundend.dm index 082c3aaeca38..b6bfb7fc59f0 100644 --- a/code/__HELPERS/~monkestation-helpers/roundend.dm +++ b/code/__HELPERS/~monkestation-helpers/roundend.dm @@ -36,3 +36,26 @@ total_payout += listed_challenge.challenge_payout if(total_payout) client?.prefs?.adjust_metacoins(client?.ckey, total_payout, "Challenge rewards.") + +/datum/controller/subsystem/ticker/proc/refund_cassette() + if(!length(GLOB.cassette_reviews)) + return + + for(var/id in GLOB.cassette_reviews) + var/datum/cassette_review/review = GLOB.cassette_reviews[id] + if(!review || review.action_taken) // Skip if review doesn't exist or already handled (denied / approved) + continue + + var/ownerckey = review.submitted_ckey // ckey of who made the cassette. + if(!ownerckey) + continue + + var/client/client = GLOB.directory[ownerckey] // Use directory for direct lookup (Client might be a differnet mob than when review was made.) + if(client && !QDELETED(client?.prefs)) + var/adjusted = client?.prefs?.adjust_metacoins( + client?.ckey, 5000, + reason = "No action taken on cassette:\[[review.submitted_tape.name]\] before round end.", + announces = TRUE, donator_multipler = FALSE + ) + if(adjusted) + qdel(review) diff --git a/code/_experiments.dm b/code/_experiments.dm index ef2240406ed2..c7fdad2f7887 100644 --- a/code/_experiments.dm +++ b/code/_experiments.dm @@ -3,11 +3,8 @@ // Any flag you see here can be flipped with the `-D` CLI argument. // For example, if you want to enable EXPERIMENT_MY_COOL_FEATURE, compile with -DEXPERIMENT_MY_COOL_FEATURE -// EXPERIMENT_515_QDEL_HARD_REFERENCE -// - Hold a hard reference for qdeleted items, and check ref_count, rather than using refs. Requires 515+. - -// EXPERIMENT_515_DONT_CACHE_REF -// - Avoids `text_ref` caching, aided by improvements to ref() speed in 515. +// EXPERIMENT_MY_COOL_FEATURE +// - Does something really cool, just so neat, absolutely banging, gaming and chill #if DM_VERSION < 515 @@ -20,6 +17,6 @@ #define EXPERIMENT_MY_COOL_FEATURE #endif -#if DM_VERSION >= 516 - #error "Remove all 515 experiments" +#if DM_VERSION >= 517 + #error "Remove all 516 experiments" #endif diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm index c60c9105f541..bcb83d5d97e5 100644 --- a/code/_globalvars/lists/maintenance_loot.dm +++ b/code/_globalvars/lists/maintenance_loot.dm @@ -400,7 +400,7 @@ GLOBAL_LIST_INIT(maint_fauna, list(//fauna: there be critters living in yer main #define maint_uncommon_weight 900 #define maint_rarity_weight 99 #define maint_oddity_weight 4 //1 out of 10,000 would give metastation (180 spawns) a 2 in 111 chance of spawning an oddity per round, similar to xeno egg, monkestation edit: from 1 to 4 -#define maint_holiday_weight 2500 // When holiday loot is enabled, it'll give every loot item a 25% chance of being a holiday item +#define maint_holiday_weight 3500 // When holiday loot is enabled, it'll give every loot item a 25% chance of being a holiday item #define maint_fauna_weight 150 //monkestation edit: adds friendly maintenance bees, also allows for other maintenance fauna to be coded in. //Loot pool used by default maintenance loot spawners diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 7734282dd4d7..12590729049c 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -611,24 +611,31 @@ GLOBAL_LIST_INIT(traits_by_type, list( ), /obj/item = list( "TRAIT_APC_SHOCKING" = TRAIT_APC_SHOCKING, + "TRAIT_ASSISTED_BREATHING" = TRAIT_ASSISTED_BREATHING, "TRAIT_BASIC_QUALITY_BAIT" = TRAIT_BASIC_QUALITY_BAIT, "TRAIT_BELT_SATCHEL" = TRAIT_BELT_SATCHEL, "TRAIT_BLIND_TOOL" = TRAIT_BLIND_TOOL, "TRAIT_BYPASS_COMPRESS_CHECK" = TRAIT_BYPASS_COMPRESS_CHECK, "TRAIT_CUSTOM_TAP_SOUND" = TRAIT_CUSTOM_TAP_SOUND, "TRAIT_DANGEROUS_OBJECT" = TRAIT_DANGEROUS_OBJECT, + "TRAIT_FEATHERED" = TRAIT_FEATHERED, "TRAIT_FISHING_BAIT" = TRAIT_FISHING_BAIT, "TRAIT_FOOD_GRILLED" = TRAIT_FOOD_GRILLED, + "TRAIT_GIFT_ITEM" = TRAIT_GIFT_ITEM, "TRAIT_GOOD_QUALITY_BAIT" = TRAIT_GOOD_QUALITY_BAIT, "TRAIT_GREAT_QUALITY_BAIT" = TRAIT_GREAT_QUALITY_BAIT, "TRAIT_HAUNTED" = TRAIT_HAUNTED, "TRAIT_HONKSPAMMING" = TRAIT_HONKSPAMMING, "TRAIT_INNATELY_FANTASTICAL_ITEM" = TRAIT_INNATELY_FANTASTICAL_ITEM, + "TRAIT_INSTANTLY_PROCESSES_BOULDERS" = TRAIT_INSTANTLY_PROCESSES_BOULDERS, + "TRAIT_LABOURED_BREATHING" = TRAIT_LABOURED_BREATHING, "TRAIT_MAT_TRANSMUTED" = TRAIT_MAT_TRANSMUTED, "TRAIT_MAY_CONTAIN_BLENDED_DUST" = TRAIT_MAY_CONTAIN_BLENDED_DUST, "TRAIT_NEEDS_TWO_HANDS" = TRAIT_NEEDS_TWO_HANDS, "TRAIT_NODROP" = TRAIT_NODROP, + "TRAIT_NON_IMPORTANT_SHOE_BLOCK" = TRAIT_NON_IMPORTANT_SHOE_BLOCK, "TRAIT_NO_BARCODES" = TRAIT_NO_BARCODES, + "TRAIT_NO_ORGAN_DECAY" = TRAIT_NO_ORGAN_DECAY, "TRAIT_NO_STORAGE_INSERT" = TRAIT_NO_STORAGE_INSERT, "TRAIT_NO_TELEPORT" = TRAIT_NO_TELEPORT, "TRAIT_OMNI_BAIT" = TRAIT_OMNI_BAIT, @@ -639,17 +646,11 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_T_RAY_VISIBLE" = TRAIT_T_RAY_VISIBLE, "TRAIT_UNCATCHABLE" = TRAIT_UNCATCHABLE, "TRAIT_WIELDED" = TRAIT_WIELDED, - "TRAIT_FEATHERED" = TRAIT_FEATHERED, - "TRAIT_NON_IMPORTANT_SHOE_BLOCK" = TRAIT_NON_IMPORTANT_SHOE_BLOCK, - "TRAIT_LABOURED_BREATHING" = TRAIT_LABOURED_BREATHING, - "TRAIT_ASSISTED_BREATHING" = TRAIT_ASSISTED_BREATHING, - "TRAIT_NO_ORGAN_DECAY" = TRAIT_NO_ORGAN_DECAY, /* "TRAIT_BAIT_UNCONSUMABLE" = TRAIT_BAIT_UNCONSUMABLE, */ /* "TRAIT_BAKEABLE" = TRAIT_BAKEABLE, */ /* "TRAIT_BYPASS_RANGED_ARMOR" = TRAIT_BYPASS_RANGED_ARMOR, */ /* "TRAIT_CONTRABAND_BLOCKER" = TRAIT_CONTRABAND_BLOCKER, */ /* "TRAIT_GERM_SENSITIVE" = TRAIT_GERM_SENSITIVE, */ - "TRAIT_INSTANTLY_PROCESSES_BOULDERS" = TRAIT_INSTANTLY_PROCESSES_BOULDERS, /* "TRAIT_ITEM_OBJECTIVE_BLOCKED" = TRAIT_ITEM_OBJECTIVE_BLOCKED, */ /* "TRAIT_NO_SIDE_KICK" = TRAIT_NO_SIDE_KICK, */ ), diff --git a/code/_onclick/hud/new_player.dm b/code/_onclick/hud/new_player.dm index d5bee6641c3d..b50f4184c73f 100644 --- a/code/_onclick/hud/new_player.dm +++ b/code/_onclick/hud/new_player.dm @@ -160,6 +160,10 @@ if(!.) return var/mob/dead/new_player/new_player = hud.mymob + var/datum/station_trait/overflow_job_bureaucracy/overflow = locate() in SSstation.station_traits + if(!ready && overflow?.picked_job && new_player.client?.prefs?.read_preference(/datum/preference/toggle/verify_overflow)) + if(tgui_alert(new_player, "The current overflow role is [overflow.picked_job.title], are you sure you would like to ready up?", "Overflow Notice", list("Yes", "No")) != "Yes") + return ready = !ready if(ready) new_player.ready = PLAYER_READY_TO_PLAY @@ -574,3 +578,76 @@ . = ..() if(.) SEND_SOUND(usr, 'monkestation/sound/misc/menumonkey.ogg') + +/atom/movable/screen/lobby/overflow_alert + screen_loc = "TOP:-48,CENTER-2.7" + icon = 'icons/hud/lobby/overflow.dmi' + icon_state = "" + base_icon_state = "overflow" + var/datum/job/overflow_job + var/static/disabled = FALSE + var/static/mutable_appearance/job_overlay + +/atom/movable/screen/lobby/overflow_alert/Initialize(mapload) + . = ..() + if(SSticker.current_state == GAME_STATE_STARTUP) + RegisterSignal(SSticker, COMSIG_TICKER_ENTER_PREGAME, PROC_REF(initial_setup)) + else + generate_and_set_icon() + update_appearance(UPDATE_ICON) + +/atom/movable/screen/lobby/overflow_alert/Destroy() + overflow_job = null + UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_PREGAME) + return ..() + +/atom/movable/screen/lobby/overflow_alert/update_icon_state() + if(!disabled && !isnull(job_overlay)) + icon_state = base_icon_state + else + icon_state = "" + return ..() + +/atom/movable/screen/lobby/overflow_alert/update_overlays() + . = ..() + if(!disabled && job_overlay) + . += job_overlay + +/atom/movable/screen/lobby/overflow_alert/MouseEntered(location,control,params) + . = ..() + if(!disabled && overflow_job && !QDELETED(src)) + openToolTip(usr, src, params, title = "Job Overflow", content = "The overflow role this round is [html_encode(overflow_job.title)]!") + +/atom/movable/screen/lobby/overflow_alert/MouseExited() + closeToolTip(usr) + +/atom/movable/screen/lobby/overflow_alert/proc/initial_setup(datum/source) + SIGNAL_HANDLER + UnregisterSignal(SSstation, COMSIG_TICKER_ENTER_PREGAME) + var/datum/station_trait/overflow_job_bureaucracy/overflow = locate() in SSstation.station_traits + overflow_job = overflow?.picked_job + if(overflow_job) + generate_and_set_icon() + else + disabled = TRUE + update_appearance(UPDATE_ICON) + +/atom/movable/screen/lobby/overflow_alert/proc/generate_and_set_icon() + if(disabled || SSticker.current_state == GAME_STATE_STARTUP || !isnull(job_overlay)) + return + var/datum/station_trait/overflow_job_bureaucracy/overflow = locate() in SSstation.station_traits + overflow_job = overflow?.picked_job + if(!overflow_job) + disabled = TRUE + return + var/icon/job_icon = get_job_hud_icon(overflow_job, include_unknown = TRUE) + if(!job_icon) + return + var/icon/resized_icon = resize_icon(job_icon, 16, 16) + if(!resized_icon) + stack_trace("Failed to upscale icon for [overflow_job], upscaling using BYOND!") + job_icon.Scale(16, 16) + resized_icon = job_icon + job_overlay = mutable_appearance(resized_icon) + job_overlay.pixel_x = 8 + job_overlay.pixel_y = 18 diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm index 08a26d9a79cf..3dba3b00faab 100644 --- a/code/controllers/subsystem/statpanel.dm +++ b/code/controllers/subsystem/statpanel.dm @@ -99,11 +99,12 @@ SUBSYSTEM_DEF(statpanels) return /datum/controller/subsystem/statpanels/proc/set_status_tab(client/target) + var/static/list/beta_notice = list("", "You are on the BYOND 516 beta, various UIs and such may be broken!", "Please report issues, and switch back to BYOND 515 if things are causing too many issues for you.") if(!global_data)//statbrowser hasnt fired yet and we were called from immediate_send_stat_data() return target.stat_panel.send_message("update_stat", list( - "global_data" = global_data, + "global_data" = (target.byond_version < 516) ? global_data : (global_data + beta_notice), "ping_str" = "Ping: [round(target.lastping, 1)]ms (Average: [round(target.avgping, 1)]ms)", "other_str" = target.mob?.get_status_tab_items(), )) diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm index 8966193abff9..48b0f1a9db46 100644 --- a/code/datums/ai/_ai_controller.dm +++ b/code/datums/ai/_ai_controller.dm @@ -232,9 +232,10 @@ multiple modular subtrees with behaviors ///Called when the AI controller pawn changes z levels, we check if there's any clients on the new one and wake up the AI if there is. /datum/ai_controller/proc/on_changed_z_level(atom/source, turf/old_turf, turf/new_turf, same_z_layer, notify_contents) SIGNAL_HANDLER - var/mob/mob_pawn = pawn - if((mob_pawn?.client && !continue_processing_when_client)) - return + if (ismob(pawn)) + var/mob/mob_pawn = pawn + if((mob_pawn?.client && !continue_processing_when_client)) + return if(old_turf) SSai_controllers.ai_controllers_by_zlevel[old_turf.z] -= src if(new_turf) diff --git a/code/datums/elements/corrupted_organ.dm b/code/datums/elements/corrupted_organ.dm new file mode 100644 index 000000000000..ee4cf3a1003d --- /dev/null +++ b/code/datums/elements/corrupted_organ.dm @@ -0,0 +1,66 @@ +/// Component applying shared behaviour by cursed organs granted when sacrificed by a heretic +/// Mostly just does something spooky when it is removed +/datum/element/corrupted_organ + +/datum/element/corrupted_organ/Attach(datum/target) + . = ..() + if (!isinternalorgan(target)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_ORGAN_REMOVED, PROC_REF(on_removed)) + + var/atom/atom_parent = target + atom_parent.color = COLOR_VOID_PURPLE + + atom_parent.add_filter(name = "ray", priority = 1, params = list( + type = "rays", + size = 12, + color = COLOR_VOID_PURPLE, + density = 12 + )) + var/ray_filter = atom_parent.get_filter("ray") + animate(ray_filter, offset = 100, time = 2 MINUTES, loop = -1, flags = ANIMATION_PARALLEL) // Absurdly long animate so nobody notices it hitching when it loops + animate(offset = 0, time = 2 MINUTES) // I sure hope duration of animate doesnt have any performance effect + +/datum/element/corrupted_organ/Detach(datum/source) + UnregisterSignal(source, list(COMSIG_ORGAN_REMOVED)) + return ..() + +/// When we're taken out of someone, do something spooky +/datum/element/corrupted_organ/proc/on_removed(atom/organ, mob/living/carbon/loser) + SIGNAL_HANDLER + if (loser.has_reagent(/datum/reagent/water/holywater) || loser.can_block_magic(MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY) || prob(20)) + return + if (prob(75)) + organ.AddComponent(\ + /datum/component/haunted_item,\ + haunt_color = "#00000000", \ + aggro_radius = 4, \ + spawn_message = span_revenwarning("[organ] hovers ominously into the air, pulsating with unnatural vigour!"), \ + despawn_message = span_revenwarning("[organ] falls motionless to the ground."), \ + ) + return + var/turf/origin_turf = get_turf(organ) + playsound(organ, 'sound/magic/forcewall.ogg', vol = 100) + new /obj/effect/temp_visual/curse_blast(origin_turf) + organ.visible_message(span_revenwarning("[organ] explodes in a burst of dark energy!")) + for(var/mob/living/target in range(1, origin_turf)) + var/armor = target.run_armor_check(attack_flag = BOMB) + target.apply_damage(30, damagetype = BURN, blocked = armor, spread_damage = TRUE) + qdel(organ) + +/obj/effect/temp_visual/curse_blast + icon = 'icons/effects/64x64.dmi' + pixel_x = -16 + pixel_y = -16 + icon_state = "curse" + duration = 0.3 SECONDS + +/obj/effect/temp_visual/curse_blast/Initialize(mapload) + . = ..() + animate(src, transform = matrix() * 0.2, time = 0, flags = ANIMATION_PARALLEL) + animate(transform = matrix() * 2, time = duration, easing = EASE_IN) + + animate(src, alpha = 255, time = 0, flags = ANIMATION_PARALLEL) + animate(alpha = 255, time = 0.2 SECONDS) + animate(alpha = 0, time = 0.1 SECONDS) diff --git a/code/datums/elements/organ_set_bonus.dm b/code/datums/elements/organ_set_bonus.dm index aeb63356fb48..2859f7b802c5 100644 --- a/code/datums/elements/organ_set_bonus.dm +++ b/code/datums/elements/organ_set_bonus.dm @@ -42,8 +42,8 @@ /datum/status_effect/organ_set_bonus id = "organ_set_bonus" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK alert_type = null ///how many organs the carbon with this has in the set var/organs = 0 diff --git a/code/datums/elements/relay_attackers.dm b/code/datums/elements/relay_attackers.dm index cdc86e6058cb..83e83a40e7ce 100644 --- a/code/datums/elements/relay_attackers.dm +++ b/code/datums/elements/relay_attackers.dm @@ -7,14 +7,15 @@ /datum/element/relay_attackers/Attach(datum/target) . = ..() - // Boy this sure is a lot of ways to tell us that someone tried to attack us - RegisterSignal(target, COMSIG_ATOM_AFTER_ATTACKEDBY, PROC_REF(after_attackby)) - RegisterSignals(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW, COMSIG_MOB_ATTACK_ALIEN), PROC_REF(on_attack_generic)) - RegisterSignals(target, list(COMSIG_ATOM_ATTACK_BASIC_MOB, COMSIG_ATOM_ATTACK_ANIMAL), PROC_REF(on_attack_npc)) - RegisterSignal(target, COMSIG_PROJECTILE_PREHIT, PROC_REF(on_bullet_act)) - RegisterSignal(target, COMSIG_ATOM_PREHITBY, PROC_REF(on_hitby)) - RegisterSignal(target, COMSIG_ATOM_HULK_ATTACK, PROC_REF(on_attack_hulk)) - RegisterSignal(target, COMSIG_ATOM_ATTACK_MECH, PROC_REF(on_attack_mech)) + if (!HAS_TRAIT(target, TRAIT_RELAYING_ATTACKER)) // Little bit gross but we want to just apply this shit from a bunch of places + // Boy this sure is a lot of ways to tell us that someone tried to attack us + RegisterSignal(target, COMSIG_ATOM_AFTER_ATTACKEDBY, PROC_REF(after_attackby)) + RegisterSignals(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW, COMSIG_MOB_ATTACK_ALIEN), PROC_REF(on_attack_generic)) + RegisterSignals(target, list(COMSIG_ATOM_ATTACK_BASIC_MOB, COMSIG_ATOM_ATTACK_ANIMAL), PROC_REF(on_attack_npc)) + RegisterSignal(target, COMSIG_PROJECTILE_PREHIT, PROC_REF(on_bullet_act)) + RegisterSignal(target, COMSIG_ATOM_PREHITBY, PROC_REF(on_hitby)) + RegisterSignal(target, COMSIG_ATOM_HULK_ATTACK, PROC_REF(on_attack_hulk)) + RegisterSignal(target, COMSIG_ATOM_ATTACK_MECH, PROC_REF(on_attack_mech)) ADD_TRAIT(target, TRAIT_RELAYING_ATTACKER, REF(src)) /datum/element/relay_attackers/Detach(datum/source, ...) diff --git a/code/datums/id_trim/jobs.dm b/code/datums/id_trim/jobs.dm index e8227001977e..460000a14994 100644 --- a/code/datums/id_trim/jobs.dm +++ b/code/datums/id_trim/jobs.dm @@ -175,7 +175,7 @@ template_access = list( ACCESS_CAPTAIN, ACCESS_CHANGE_IDS, - ACCESS_QM, + ACCESS_HOP, ) job = /datum/job/bitrunner diff --git a/code/datums/quirks/neutral_quirks/pride_pin.dm b/code/datums/quirks/neutral_quirks/pride_pin.dm index 0bdb26260109..64fbe43c55ea 100644 --- a/code/datums/quirks/neutral_quirks/pride_pin.dm +++ b/code/datums/quirks/neutral_quirks/pride_pin.dm @@ -18,6 +18,7 @@ pin.current_skin = pride_choice pin.icon_state = pride_reskin + pin.post_reskin() //MONKESTATION EDIT START var/mob/living/carbon/human/H = quirk_holder if (istype(H)) diff --git a/code/datums/status_effects/_status_effect.dm b/code/datums/status_effects/_status_effect.dm index 49ba273c24db..b95504702c28 100644 --- a/code/datums/status_effects/_status_effect.dm +++ b/code/datums/status_effects/_status_effect.dm @@ -6,7 +6,7 @@ /// When set initially / in on_creation, this is how long the status effect lasts in deciseconds. /// While processing, this becomes the world.time when the status effect will expire. /// -1 = infinite duration. - var/duration = -1 + var/duration = STATUS_EFFECT_PERMANENT /// When set initially / in on_creation, this is how long between [proc/tick] calls in deciseconds. /// While processing, this becomes the world.time when the next tick will occur. /// -1 = will stop processing, if duration is also unlimited (-1). diff --git a/code/datums/status_effects/agent_pinpointer.dm b/code/datums/status_effects/agent_pinpointer.dm index 3f64ff252a02..7afb85805f05 100644 --- a/code/datums/status_effects/agent_pinpointer.dm +++ b/code/datums/status_effects/agent_pinpointer.dm @@ -10,7 +10,7 @@ /datum/status_effect/agent_pinpointer id = "agent_pinpointer" - duration = -1 + duration = STATUS_EFFECT_PERMANENT tick_interval = PINPOINTER_PING_TIME alert_type = /atom/movable/screen/alert/status_effect/agent_pinpointer ///The minimum range to start pointing towards your target. diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index e4edd4997c62..a2352c34b15d 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -2,7 +2,7 @@ /datum/status_effect/his_grace id = "his_grace" - duration = -1 + duration = STATUS_EFFECT_PERMANENT tick_interval = 4 alert_type = /atom/movable/screen/alert/status_effect/his_grace var/bloodlust = 0 @@ -86,7 +86,7 @@ /datum/status_effect/cult_master id = "The Cult Master" - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = null on_remove_on_mob_delete = TRUE var/alive = TRUE @@ -116,7 +116,7 @@ /datum/status_effect/blooddrunk id = "blooddrunk" duration = 10 - tick_interval = -1 // monkestation edit + tick_interval = STATUS_EFFECT_NO_TICK // monkestation edit alert_type = /atom/movable/screen/alert/status_effect/blooddrunk /atom/movable/screen/alert/status_effect/blooddrunk @@ -211,7 +211,7 @@ /datum/status_effect/hippocratic_oath id = "Hippocratic Oath" status_type = STATUS_EFFECT_UNIQUE - duration = -1 + duration = STATUS_EFFECT_PERMANENT tick_interval = 25 alert_type = null @@ -423,6 +423,7 @@ duration = 2 SECONDS status_type = STATUS_EFFECT_REPLACE show_duration = TRUE + alert_type = null /datum/status_effect/speed_boost/on_creation(mob/living/new_owner, set_duration) if(isnum(set_duration)) @@ -475,7 +476,7 @@ /datum/status_effect/nest_sustenance id = "nest_sustenance" - duration = -1 + duration = STATUS_EFFECT_PERMANENT tick_interval = 0.4 SECONDS alert_type = /atom/movable/screen/alert/status_effect/nest_sustenance @@ -504,8 +505,8 @@ */ /datum/status_effect/blessing_of_insanity id = "blessing_of_insanity" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK alert_type = /atom/movable/screen/alert/status_effect/blessing_of_insanity /atom/movable/screen/alert/status_effect/blessing_of_insanity diff --git a/code/datums/status_effects/buffs/stun_asorption.dm b/code/datums/status_effects/buffs/stun_asorption.dm index d68f2f7408cc..f6212bc88421 100644 --- a/code/datums/status_effects/buffs/stun_asorption.dm +++ b/code/datums/status_effects/buffs/stun_asorption.dm @@ -9,7 +9,7 @@ */ /datum/status_effect/stun_absorption id = "absorb_stun" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK alert_type = null status_type = STATUS_EFFECT_MULTIPLE diff --git a/code/datums/status_effects/debuffs/blindness.dm b/code/datums/status_effects/debuffs/blindness.dm index 0bfaaee7485b..310b995090c8 100644 --- a/code/datums/status_effects/debuffs/blindness.dm +++ b/code/datums/status_effects/debuffs/blindness.dm @@ -5,7 +5,7 @@ /// Nearsighted /datum/status_effect/grouped/nearsighted id = "nearsighted" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK alert_type = null // This is not "remove on fullheal" as in practice, // fullheal should instead remove all the sources and in turn cure this @@ -55,7 +55,7 @@ /// Blindness /datum/status_effect/grouped/blindness id = "blindness" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK alert_type = /atom/movable/screen/alert/status_effect/blind // This is not "remove on fullheal" as in practice, // fullheal should instead remove all the sources and in turn cure this diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index 83572e56630c..3e6491813387 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -1,7 +1,8 @@ //Largely negative status effects go here, even if they have small benificial effects //STUN EFFECTS /datum/status_effect/incapacitating - tick_interval = -1 // monkestation edit + id = STATUS_EFFECT_ID_ABSTRACT + tick_interval = STATUS_EFFECT_NO_TICK // monkestation edit status_type = STATUS_EFFECT_REPLACE alert_type = null remove_on_fullheal = TRUE @@ -134,7 +135,7 @@ if(!.) return if(HAS_TRAIT(owner, TRAIT_SLEEPIMMUNE)) - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK else ADD_TRAIT(owner, TRAIT_KNOCKEDOUT, TRAIT_STATUS_EFFECT(id)) RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_SLEEPIMMUNE), PROC_REF(on_owner_insomniac)) @@ -156,7 +157,7 @@ /datum/status_effect/incapacitating/sleeping/proc/on_owner_insomniac(mob/living/source) SIGNAL_HANDLER REMOVE_TRAIT(owner, TRAIT_KNOCKEDOUT, TRAIT_STATUS_EFFECT(id)) - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK ///If the mob has the TRAIT_SLEEPIMMUNE but somehow looses it we make him sleep and restart the tick() /datum/status_effect/incapacitating/sleeping/proc/on_owner_sleepy(mob/living/source) @@ -237,7 +238,7 @@ //STASIS /datum/status_effect/grouped/stasis id = "stasis" - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = /atom/movable/screen/alert/status_effect/stasis var/last_dead_time @@ -289,7 +290,7 @@ /datum/status_effect/his_wrath //does minor damage over time unless holding His Grace id = "his_wrath" - duration = -1 + duration = STATUS_EFFECT_PERMANENT tick_interval = 4 alert_type = /atom/movable/screen/alert/status_effect/his_wrath @@ -309,7 +310,7 @@ /datum/status_effect/cultghost //is a cult ghost and can't use manifest runes id = "cult_ghost" - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = null /datum/status_effect/cultghost/on_apply() @@ -386,7 +387,7 @@ id = "neck_slice" status_type = STATUS_EFFECT_UNIQUE alert_type = null - duration = -1 + duration = STATUS_EFFECT_PERMANENT /datum/status_effect/neck_slice/on_apply() if(!ishuman(owner)) @@ -511,7 +512,7 @@ /datum/status_effect/gonbola_pacify id = "gonbolaPacify" status_type = STATUS_EFFECT_MULTIPLE - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK alert_type = null /datum/status_effect/gonbola_pacify/on_apply() @@ -693,9 +694,9 @@ /datum/status_effect/go_away id = "go_away" - duration = 100 + duration = 10 SECONDS status_type = STATUS_EFFECT_REPLACE - tick_interval = 1 + tick_interval = 0.2 SECONDS alert_type = /atom/movable/screen/alert/status_effect/go_away var/direction @@ -716,11 +717,11 @@ /datum/status_effect/fake_virus id = "fake_virus" - duration = 1800//3 minutes + duration = 3 MINUTES //3 minutes status_type = STATUS_EFFECT_REPLACE - tick_interval = 1 + tick_interval = 0.2 SECONDS alert_type = null - var/msg_stage = 0//so you dont get the most intense messages immediately + var/msg_stage = 0//so you don't get the most intense messages immediately /datum/status_effect/fake_virus/on_apply() if(HAS_TRAIT(owner, TRAIT_VIRUSIMMUNE)) @@ -937,7 +938,8 @@ id = "teleport_madness" duration = 10 SECONDS status_type = STATUS_EFFECT_REPLACE - tick_interval = 0.1 SECONDS + tick_interval = 0.2 SECONDS + alert_type = null /datum/status_effect/teleport_madness/tick() dump_in_space(owner) diff --git a/code/datums/status_effects/debuffs/dna_transformation.dm b/code/datums/status_effects/debuffs/dna_transformation.dm index 1ffcc2f5929d..f38c88c3f2bb 100644 --- a/code/datums/status_effects/debuffs/dna_transformation.dm +++ b/code/datums/status_effects/debuffs/dna_transformation.dm @@ -2,7 +2,7 @@ /// then turns them back to how they were before transformation. /datum/status_effect/temporary_transformation id = "temp_dna_transformation" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK duration = 1 MINUTES // set in on creation, this just needs to be any value to process alert_type = null remove_on_fullheal = TRUE @@ -85,7 +85,7 @@ return // Already paused time_before_pause = duration - world.time - duration = -1 + duration = STATUS_EFFECT_PERMANENT // Resume if we're none of the above and also were paused else if(time_before_pause != -1) diff --git a/code/datums/status_effects/debuffs/drunk.dm b/code/datums/status_effects/debuffs/drunk.dm index 026577fdc972..4ccc273b0b2b 100644 --- a/code/datums/status_effects/debuffs/drunk.dm +++ b/code/datums/status_effects/debuffs/drunk.dm @@ -15,6 +15,7 @@ tick_interval = 2 SECONDS status_type = STATUS_EFFECT_REPLACE remove_on_fullheal = TRUE + alert_type = null /// The level of drunkness we are currently at. var/drunk_value = 0 diff --git a/code/datums/status_effects/debuffs/fire_stacks.dm b/code/datums/status_effects/debuffs/fire_stacks.dm index d00d0c348439..81d81deee4af 100644 --- a/code/datums/status_effects/debuffs/fire_stacks.dm +++ b/code/datums/status_effects/debuffs/fire_stacks.dm @@ -1,5 +1,6 @@ /datum/status_effect/fire_handler - duration = -1 + duration = STATUS_EFFECT_PERMANENT + id = STATUS_EFFECT_ID_ABSTRACT alert_type = null status_type = STATUS_EFFECT_REFRESH //Custom code on_remove_on_mob_delete = TRUE diff --git a/code/datums/status_effects/debuffs/genetic_damage.dm b/code/datums/status_effects/debuffs/genetic_damage.dm index 438bcc7c6905..12512efd06af 100644 --- a/code/datums/status_effects/debuffs/genetic_damage.dm +++ b/code/datums/status_effects/debuffs/genetic_damage.dm @@ -5,7 +5,7 @@ id = "genetic_damage" alert_type = null status_type = STATUS_EFFECT_REFRESH // New effects will add to total_damage - duration = -1 + duration = STATUS_EFFECT_PERMANENT tick_interval = 2 SECONDS on_remove_on_mob_delete = TRUE // Need to unregister from owner, be_replaced() would cause runtimes remove_on_fullheal = TRUE diff --git a/code/datums/status_effects/debuffs/hallucination.dm b/code/datums/status_effects/debuffs/hallucination.dm index 4c5e1c305e1b..942ab9deb0ed 100644 --- a/code/datums/status_effects/debuffs/hallucination.dm +++ b/code/datums/status_effects/debuffs/hallucination.dm @@ -82,7 +82,7 @@ /datum/status_effect/hallucination/sanity id = "low sanity" status_type = STATUS_EFFECT_REFRESH - duration = -1 // This lasts "forever", only goes away with sanity gain + duration = STATUS_EFFECT_PERMANENT // This lasts "forever", only goes away with sanity gain /datum/status_effect/hallucination/sanity/on_apply() if(!owner.mob_mood) diff --git a/code/datums/status_effects/debuffs/speech_debuffs.dm b/code/datums/status_effects/debuffs/speech_debuffs.dm index 2b095bf6047f..607f78c64af2 100644 --- a/code/datums/status_effects/debuffs/speech_debuffs.dm +++ b/code/datums/status_effects/debuffs/speech_debuffs.dm @@ -1,5 +1,5 @@ /datum/status_effect/speech - id = null + id = STATUS_EFFECT_ID_ABSTRACT alert_type = null remove_on_fullheal = TRUE diff --git a/code/datums/status_effects/debuffs/tower_of_babel.dm b/code/datums/status_effects/debuffs/tower_of_babel.dm index 1ba46d0b87b5..923938185d77 100644 --- a/code/datums/status_effects/debuffs/tower_of_babel.dm +++ b/code/datums/status_effects/debuffs/tower_of_babel.dm @@ -33,7 +33,7 @@ // Used by wizard magic and tower of babel event /datum/status_effect/tower_of_babel/magical id = "tower_of_babel_magic" // do we need a new id? - duration = -1 + duration = STATUS_EFFECT_PERMANENT trait_source = TRAUMA_TRAIT /datum/status_effect/tower_of_babel/magical/on_apply() diff --git a/code/datums/status_effects/drug_effects.dm b/code/datums/status_effects/drug_effects.dm index d01a92743b58..949c00d2edff 100644 --- a/code/datums/status_effects/drug_effects.dm +++ b/code/datums/status_effects/drug_effects.dm @@ -1,6 +1,6 @@ /datum/status_effect/woozy id = "woozy" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/woozy @@ -14,7 +14,7 @@ /datum/status_effect/high_blood_pressure id = "high_blood_pressure" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/high_blood_pressure @@ -40,7 +40,7 @@ /datum/status_effect/seizure id = "seizure" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/seizure diff --git a/code/datums/status_effects/gas.dm b/code/datums/status_effects/gas.dm index bf0822d879f8..f7b8cf2b375b 100644 --- a/code/datums/status_effects/gas.dm +++ b/code/datums/status_effects/gas.dm @@ -57,7 +57,7 @@ /datum/status_effect/freon/lasting id = "lasting_frozen" - duration = -1 + duration = STATUS_EFFECT_PERMANENT /datum/status_effect/hypernob_protection id = "hypernob_protection" diff --git a/code/datums/status_effects/grouped_effect.dm b/code/datums/status_effects/grouped_effect.dm index ade0a187e0db..403f1c740913 100644 --- a/code/datums/status_effects/grouped_effect.dm +++ b/code/datums/status_effects/grouped_effect.dm @@ -1,5 +1,7 @@ /// Status effect from multiple sources, when all sources are removed, so is the effect /datum/status_effect/grouped + id = STATUS_EFFECT_ID_ABSTRACT + alert_type = null // Grouped effects adds itself to [var/sources] and destroys itself if one exists already, there are never actually multiple status_type = STATUS_EFFECT_MULTIPLE /// A list of all sources applying this status effect. Sources are a list of keys diff --git a/code/datums/status_effects/limited_effect.dm b/code/datums/status_effects/limited_effect.dm index 0f56e72da52f..047a7a5ae07c 100644 --- a/code/datums/status_effects/limited_effect.dm +++ b/code/datums/status_effects/limited_effect.dm @@ -1,8 +1,9 @@ /// These effects reapply their on_apply() effect when refreshed while stacks < max_stacks. /datum/status_effect/limited_buff id = "limited_buff" - duration = -1 + duration = STATUS_EFFECT_PERMANENT status_type = STATUS_EFFECT_REFRESH + alert_type = null ///How many stacks we currently have var/stacks = 1 ///How many stacks we can have maximum diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm index a92823293182..062108241f86 100644 --- a/code/datums/status_effects/neutral.dm +++ b/code/datums/status_effects/neutral.dm @@ -2,8 +2,8 @@ /datum/status_effect/crusher_damage //tracks the damage dealt to this mob by kinetic crushers id = "crusher_damage" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = null var/total_damage = 0 @@ -46,7 +46,7 @@ /datum/status_effect/in_love id = "in_love" - duration = -1 + duration = STATUS_EFFECT_PERMANENT status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/in_love var/hearts @@ -84,6 +84,7 @@ /datum/status_effect/bounty id = "bounty" status_type = STATUS_EFFECT_UNIQUE + alert_type = null var/mob/living/rewarded /datum/status_effect/bounty/on_creation(mob/living/new_owner, mob/living/caster) @@ -118,8 +119,8 @@ // heldup is for the person being aimed at /datum/status_effect/grouped/heldup id = "heldup" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_MULTIPLE alert_type = /atom/movable/screen/alert/status_effect/heldup @@ -139,8 +140,8 @@ // holdup is for the person aiming /datum/status_effect/holdup id = "holdup" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/holdup @@ -152,8 +153,8 @@ // this status effect is used to negotiate the high-fiving capabilities of all concerned parties /datum/status_effect/offering id = "offering" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = null /// The people who were offered this item at the start @@ -315,8 +316,8 @@ //this effect gives the user an alert they can use to surrender quickly /datum/status_effect/grouped/surrender id = "surrender" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/surrender @@ -344,7 +345,7 @@ /datum/status_effect/caltropped id = "caltropped" duration = 1 SECONDS - tick_interval = -1 // monkestation edit + tick_interval = STATUS_EFFECT_NO_TICK // monkestation edit status_type = STATUS_EFFECT_REFRESH alert_type = null diff --git a/code/datums/status_effects/stacking_effect.dm b/code/datums/status_effects/stacking_effect.dm index 783d9334b215..5cc598f8710c 100644 --- a/code/datums/status_effects/stacking_effect.dm +++ b/code/datums/status_effects/stacking_effect.dm @@ -1,8 +1,8 @@ /// Status effects that can stack. /datum/status_effect/stacking - id = "stacking_base" - duration = -1 // Only removed under specific conditions. + id = STATUS_EFFECT_ID_ABSTRACT + duration = STATUS_EFFECT_PERMANENT // Only removed under specific conditions. tick_interval = 1 SECONDS // Deciseconds between decays, once decay starts alert_type = null /// How many stacks are currently accumulated. diff --git a/code/datums/status_effects/wound_effects.dm b/code/datums/status_effects/wound_effects.dm index e1929bbdc26b..42843ffbbd46 100644 --- a/code/datums/status_effects/wound_effects.dm +++ b/code/datums/status_effects/wound_effects.dm @@ -72,7 +72,7 @@ id = "determination_crash" alert_type = null remove_on_fullheal = TRUE - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK duration = 10 SECONDS /datum/status_effect/determination_crash/on_apply() @@ -101,7 +101,7 @@ /datum/status_effect/limp id = "limp" status_type = STATUS_EFFECT_REPLACE - tick_interval = -1 // monkestation edit + tick_interval = STATUS_EFFECT_NO_TICK // monkestation edit on_remove_on_mob_delete = TRUE alert_type = /atom/movable/screen/alert/status_effect/limp var/msg_stage = 0//so you dont get the most intense messages immediately @@ -216,6 +216,7 @@ id = "wound" status_type = STATUS_EFFECT_MULTIPLE on_remove_on_mob_delete = TRUE + alert_type = null var/obj/item/bodypart/linked_limb var/datum/wound/linked_wound diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 3f2dde1cc0ab..e9c83f11ad6a 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -225,7 +225,9 @@ Medical HUD! Basic mode needs suit sensors on. var/virus_threat = check_virus() holder.pixel_y = get_cached_height() - world.icon_size - if(HAS_TRAIT(src, TRAIT_XENO_HOST)) + if(HAS_TRAIT(src, TRAIT_BLOB_ALLY)) //Monkestation edit: In the edge where case a blob host has a xeno in them. I think the fact they are a blob host is more important. + holder.icon_state = "hudill5" + else if(HAS_TRAIT(src, TRAIT_XENO_HOST)) holder.icon_state = "hudxeno" else if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH))) if((key || get_ghost(FALSE, TRUE)) && (can_defib() & DEFIB_REVIVABLE_STATES)) diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm index 091bb5407fad..965e0417f6f9 100644 --- a/code/game/objects/effects/decals/cleanable/misc.dm +++ b/code/game/objects/effects/decals/cleanable/misc.dm @@ -174,6 +174,14 @@ reagents.trans_to(H, reagents.total_volume, transfered_by = user, methods = INGEST) qdel(src) +/// Nebula vomit with extra guests +/obj/effect/decal/cleanable/vomit/nebula/worms + +/obj/effect/decal/cleanable/vomit/nebula/worms/Initialize(mapload, list/datum/disease/diseases) + . = ..() + for (var/i in 1 to rand(2, 3)) + new /mob/living/basic/hivelord_brood(loc) + /obj/effect/decal/cleanable/vomit/old name = "crusty dried vomit" desc = "You try not to look at the chunks, and fail." diff --git a/code/game/objects/items/gift.dm b/code/game/objects/items/gift.dm index 1b419999f47c..16913a80c04d 100644 --- a/code/game/objects/items/gift.dm +++ b/code/game/objects/items/gift.dm @@ -50,6 +50,7 @@ GLOBAL_LIST_EMPTY(possible_gifts) M.investigate_log("has unwrapped a present containing [I.type].", INVESTIGATE_PRESENTS) M.put_in_hands(I) I.add_fingerprint(M) + I.AddComponent(/datum/component/gift_item, M) // monkestation edit: gift item info component else M.visible_message(span_danger("Oh no! The present that [M] opened had nothing inside it!")) @@ -116,17 +117,24 @@ GLOBAL_LIST_EMPTY(possible_gifts) // // Subtypes of these items will also be blocked. var/list/blocked_items = list( - // Can crash people if too many are spawned. - // NOTE: Not likely to be an issue if the amount is kept low - perhaps a limited variant - // of this (i.e. can only spawn up to 25 humans) could be added for players to use? - /obj/item/debug/human_spawner, // Just leaves the coordinates everywhere /obj/item/gps/visible_debug, // Can lag the hell out of the server /obj/item/gun/energy/recharge/kinetic_accelerator/meme, // Per Biddi's suggestion; plus doesn't seem to do much anyways? /obj/item/research, - ) + //only upsets people consistantly + /obj/item/gun/magic/wand/death, + /obj/item/gun/magic/wand/resurrection/debug, + //holy fuck why was this enabled + /obj/item/debug, + /obj/item/storage/box/debugtools, + /obj/item/gun/energy/beam_rifle/debug, + /obj/item/multitool/field_debug, + /obj/item/bounty_cube/debug_cube, + /obj/item/organ/internal/cyberimp/brain/nif/debug, + /obj/item/spellbook_charge/debug, + ) for(var/blocked_item as anything in blocked_items) // Block the item listed, and any subtypes too. gift_types_list -= typesof(blocked_item) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm b/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm index 526c91dbb0a4..7f758a7a6cea 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm @@ -17,6 +17,7 @@ new /obj/item/storage/bag/garment/quartermaster(src) new /obj/item/encryptionkey/headset_cargo(src) // monkestation edit - An extra encryption key for someone joining Cargyptia new /obj/item/cargo_teleporter(src) // monkestation edit - singular roundstart cargo teleporter + new /obj/item/gun/energy/disabler/cargo(src) // monkestation edit - a single disabler that can only be used in parts of cargo /obj/structure/closet/secure_closet/quartermaster/populate_contents_immediate() . = ..() diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm index 0030e5543b4e..d3c0482462e7 100644 --- a/code/game/objects/structures/flora.dm +++ b/code/game/objects/structures/flora.dm @@ -462,10 +462,10 @@ if(!unlimited) took_presents[user.ckey] = TRUE - if(prob(50)) + if(prob(5)) user.put_in_hands(new gift_type(drop_location())) return - if(prob(1)) + if(prob(1)) //evil pooba monke moment new /mob/living/carbon/human/species/monkey/angry(drop_location()) to_chat(user, span_warning("A live monkey crawls out of the gift... its PISSED!!!")) return diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 3a6bf818b77b..1c5bd2a86d9f 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -19,6 +19,7 @@ GLOBAL_PROTECT(admin_verbs_default) /client/proc/reload_admins, /client/proc/requests, /client/proc/secrets, + /client/proc/review_cassettes, /*monkestation addition Opens the Cassette Review menu*/ /client/proc/stop_sounds, /client/proc/tag_datum_mapview, ) @@ -159,7 +160,7 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list( /client/proc/toggle_random_events, )) GLOBAL_PROTECT(admin_verbs_fun) -GLOBAL_LIST_INIT(admin_verbs_spawn, list(/datum/admins/proc/spawn_atom, /datum/admins/proc/podspawn_atom, /datum/admins/proc/spawn_cargo, /datum/admins/proc/spawn_objasmob, /client/proc/respawn_character, /datum/admins/proc/beaker_panel, /client/proc/spawn_mixtape)) //Monkestation Addition: mixtape spawner +GLOBAL_LIST_INIT(admin_verbs_spawn, list(/datum/admins/proc/spawn_atom, /datum/admins/proc/podspawn_atom, /datum/admins/proc/spawn_cargo, /datum/admins/proc/spawn_objasmob, /client/proc/respawn_character, /datum/admins/proc/beaker_panel, /client/proc/spawn_mixtape,)) //Monkestation Addition: mixtape spawner GLOBAL_PROTECT(admin_verbs_spawn) GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer()) GLOBAL_PROTECT(admin_verbs_server) @@ -550,6 +551,9 @@ GLOBAL_PROTECT(admin_verbs_poll) mob.alpha = 0 //JUUUUST IN CASE mob.name = " " mob.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + var/image/invisible = image(icon = 'icons/mob/simple/mob.dmi', icon_state = null, loc = mob) + invisible.override = TRUE + mob.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/actually_everyone, "stealthmin", invisible) ADD_TRAIT(mob, TRAIT_ORBITING_FORBIDDEN, STEALTH_MODE_TRAIT) QDEL_NULL(mob.orbiters) @@ -560,6 +564,7 @@ GLOBAL_PROTECT(admin_verbs_poll) /client/proc/disable_stealth_mode() holder.fakekey = null if(isobserver(mob)) + mob.remove_alt_appearance("stealthmin") mob.invisibility = initial(mob.invisibility) mob.alpha = initial(mob.alpha) if(mob.mind) diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index 57179bd89bfc..94fde875336c 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -407,7 +407,7 @@ GLOBAL_PROTECT(href_token) /datum/admins/proc/try_give_devtools() if(!(rank_flags() & R_DEBUG) || owner.byond_version < 516) return - winset(owner, null, "browser-options=byondstorage,find,devtools") + winset(owner, null, "browser-options=byondstorage,find,refresh,devtools") /datum/admins/proc/try_give_profiling() if (CONFIG_GET(flag/forbid_admin_profiling)) diff --git a/code/modules/admin/verbs/admingame.dm b/code/modules/admin/verbs/admingame.dm index 670b2c76ec76..340cc3d17faa 100644 --- a/code/modules/admin/verbs/admingame.dm +++ b/code/modules/admin/verbs/admingame.dm @@ -45,6 +45,18 @@ full_version = "[M.client.byond_version].[M.client.byond_build ? M.client.byond_build : "xxx"]" body += "
\[Byond version: [full_version]\]
" + // monkestation start: gift info + var/list/gifts = M.get_all_gift_contents() + var/gift_amt = length(gifts) + if(gift_amt) + body += "

Gift items in contents:
" + for(var/idx in 1 to gift_amt) + var/obj/item/gift = gifts[idx] + body += VV_HREF_TARGET(gift, VV_HK_EXAMINE_GIFT, "[gift.name] ([gift.type])") + if(idx < gift_amt) + body += "
" + // monkestation end + body += "

\[ " body += "VV - " diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 6b0dcccf084d..f54444001678 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -55,6 +55,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list( /client/proc/station_food_debug, /client/proc/station_stack_debug, /client/proc/check_for_obstructed_atmospherics, + /client/proc/export_lighting_info, // monkestation addition )) GLOBAL_PROTECT(admin_verbs_debug_mapping) diff --git a/code/modules/antagonists/heretic/items/corrupted_organs.dm b/code/modules/antagonists/heretic/items/corrupted_organs.dm new file mode 100644 index 000000000000..4923487c03a3 --- /dev/null +++ b/code/modules/antagonists/heretic/items/corrupted_organs.dm @@ -0,0 +1,243 @@ +/// Renders you unable to see people who were heretics at the time that this organ is gained +/obj/item/organ/internal/eyes/corrupt + name = "corrupt orbs" + desc = "These eyes have seen something they shouldn't have." + /// The override images we are applying + var/list/hallucinations + +/obj/item/organ/internal/eyes/corrupt/Initialize(mapload) + . = ..() + AddElement(/datum/element/corrupted_organ) + AddElement(/datum/element/noticable_organ, "eyes have wide dilated pupils, and no iris. Something is moving in the darkness.", BODY_ZONE_PRECISE_EYES) + +/obj/item/organ/internal/eyes/corrupt/Insert(mob/living/carbon/organ_owner, special, drop_if_replaced) + . = ..() + if (!organ_owner.client) + return + + var/list/human_mobs = GLOB.human_list.Copy() + human_mobs -= organ_owner + for (var/mob/living/carbon/human/check_human as anything in human_mobs) + if (!IS_HERETIC(check_human) && !prob(5)) // Throw in some false positives + continue + var/image/invisible_man = image('icons/blanks/32x32.dmi', check_human, "nothing") + invisible_man.override = TRUE + LAZYADD(hallucinations, invisible_man) + + if (LAZYLEN(hallucinations)) + organ_owner.client.images |= hallucinations + +/obj/item/organ/internal/eyes/corrupt/Remove(mob/living/carbon/organ_owner, special) + . = ..() + if (!LAZYLEN(hallucinations)) + return + organ_owner.client?.images -= hallucinations + QDEL_NULL(hallucinations) + + +/// Sometimes speak in incomprehensible tongues +/obj/item/organ/internal/tongue/corrupt + name = "corrupt tongue" + desc = "This one tells only lies." + +/obj/item/organ/internal/tongue/corrupt/Initialize(mapload) + . = ..() + AddElement(/datum/element/corrupted_organ) + AddElement(/datum/element/noticable_organ, "mouth is full of stars.", BODY_ZONE_PRECISE_MOUTH) + +/obj/item/organ/internal/tongue/corrupt/Insert(mob/living/carbon/organ_owner, special, drop_if_replaced) + . = ..() + RegisterSignal(organ_owner, COMSIG_MOB_SAY, PROC_REF(on_spoken)) + +/obj/item/organ/internal/tongue/corrupt/Remove(mob/living/carbon/organ_owner, special) + . = ..() + UnregisterSignal(organ_owner, COMSIG_MOB_SAY) + +/// When the mob speaks, sometimes put it in a different language +/obj/item/organ/internal/tongue/corrupt/proc/on_spoken(mob/living/organ_owner, list/speech_args) + SIGNAL_HANDLER + if (organ_owner.has_reagent(/datum/reagent/water/holywater) || prob(60)) + return + speech_args[SPEECH_LANGUAGE] = /datum/language/shadowtongue + + +/// Randomly secretes alcohol or hallucinogens when you're drinking something +/obj/item/organ/internal/liver/corrupt + name = "corrupt liver" + desc = "After what you've seen you could really go for a drink." + /// How much extra ingredients to add? + var/amount_added = 5 + /// What extra ingredients can we add? + var/list/extra_ingredients = list( + /datum/reagent/consumable/ethanol/pina_olivada, + /datum/reagent/consumable/ethanol/rum, + /datum/reagent/consumable/ethanol/thirteenloko, + /datum/reagent/consumable/ethanol/vodka, + /datum/reagent/consumable/superlaughter, + /datum/reagent/drug/bath_salts, + /datum/reagent/drug/blastoff, + /datum/reagent/drug/happiness, + /datum/reagent/drug/mushroomhallucinogen, + ) + +/obj/item/organ/internal/liver/corrupt/Initialize(mapload) + . = ..() + AddElement(/datum/element/corrupted_organ) + +/obj/item/organ/internal/liver/corrupt/Insert(mob/living/carbon/organ_owner, special, drop_if_replaced) + . = ..() + RegisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS, PROC_REF(on_drank)) + +/obj/item/organ/internal/liver/corrupt/Remove(mob/living/carbon/organ_owner, special) + . = ..() + UnregisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS) + +/// If we drank something, add a little extra +/obj/item/organ/internal/liver/corrupt/proc/on_drank(atom/source, list/reagents, datum/reagents/source_reagents, methods) + SIGNAL_HANDLER + if (!(methods & INGEST)) + return + var/datum/reagents/extra_reagents = new() + extra_reagents.add_reagent(pick(extra_ingredients), amount_added) + extra_reagents.trans_to(source, amount_added, methods = INJECT) + if (prob(20)) + to_chat(source, span_warning("As you take a sip, you feel something bubbling in your stomach...")) + + +/// Rapidly become hungry if you are not digesting blood +/obj/item/organ/internal/stomach/corrupt + name = "corrupt stomach" + desc = "This parasite demands an unwholesome diet in order to be satisfied." + /// Do we have an unholy thirst? + var/thirst_satiated = FALSE + /// Timer for when we get thirsty again + var/thirst_timer + /// How long until we prompt the player to drink blood again? + COOLDOWN_DECLARE(message_cooldown) + +/obj/item/organ/internal/stomach/corrupt/Initialize(mapload) + . = ..() + AddElement(/datum/element/corrupted_organ) + AddElement(/datum/element/noticable_organ, "appear%PRONOUN_S to have an unhealthy pallor.") + +/obj/item/organ/internal/stomach/corrupt/Insert(mob/living/carbon/organ_owner, special, drop_if_replaced) + . = ..() + RegisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS, PROC_REF(on_drank)) + +/obj/item/organ/internal/stomach/corrupt/Remove(mob/living/carbon/organ_owner, special) + . = ..() + UnregisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS) + +/// Check if we drank a little blood +/obj/item/organ/internal/stomach/corrupt/proc/on_drank(atom/source, list/reagents, datum/reagents/source_reagents, methods) + SIGNAL_HANDLER + if (!(methods & INGEST)) + return + + var/contains_blood = locate(/datum/reagent/blood) in reagents + if (!contains_blood) + return + + if (!thirst_satiated) + to_chat(source, span_cultitalic("The thirst is satisfied... for now.")) + thirst_satiated = TRUE + deltimer(thirst_timer) + thirst_timer = addtimer(VARSET_CALLBACK(src, thirst_satiated, FALSE), 3 MINUTES, TIMER_STOPPABLE | TIMER_DELETE_ME) + +/obj/item/organ/internal/stomach/corrupt/handle_hunger(mob/living/carbon/human/human, seconds_per_tick, times_fired) + if (thirst_satiated || human.has_reagent(/datum/reagent/water/holywater)) + return ..() + + human.adjust_nutrition(-1 * seconds_per_tick) + + if (!COOLDOWN_FINISHED(src, message_cooldown)) + return ..() + COOLDOWN_START(src, message_cooldown, 30 SECONDS) + + var/static/list/blood_messages = list( + "Blood...", + "Everyone suddenly looks so tasty.", + "The blood...", + "There's an emptiness in you that only blood can fill.", + "You could really go for some blood right now.", + "You feel the blood rushing through your veins.", + "You think about biting someone's throat.", + "Your stomach growls and you feel a metallic taste in your mouth.", + ) + to_chat(human, span_cultitalic(pick(blood_messages))) + + return ..() + + +/// Occasionally bombards you with spooky hands and lets everyone hear your pulse. +/obj/item/organ/internal/heart/corrupt + name = "corrupt heart" + desc = "What corruption is this spreading along with the blood?" + /// How long until the next heart? + COOLDOWN_DECLARE(hand_cooldown) + +/obj/item/organ/internal/heart/corrupt/Initialize(mapload) + . = ..() + AddElement(/datum/element/corrupted_organ) + +/obj/item/organ/internal/heart/corrupt/on_life(seconds_per_tick, times_fired) + . = ..() + var/obj/item/organ/internal/heart/heart = owner.get_organ_slot(ORGAN_SLOT_HEART) + if (!COOLDOWN_FINISHED(src, hand_cooldown) || IS_IN_MANSUS(owner) || !owner.needs_heart() || !heart.beating || owner.has_reagent(/datum/reagent/water/holywater)) + return + fire_curse_hand(owner) + COOLDOWN_START(src, hand_cooldown, rand(6 SECONDS, 45 SECONDS)) // Wide variance to put you off guard + + +/// Sometimes cough out some kind of dangerous gas +/obj/item/organ/internal/lungs/corrupt + name = "corrupt lungs" + desc = "Some things SHOULD be drowned in tar." + /// How likely are we not to cough every time we take a breath? + var/cough_chance = 15 + /// How much gas to emit? + var/gas_amount = 30 + /// What can we cough up? + var/list/gas_types = list( + /datum/gas/bz = 30, + /datum/gas/miasma = 50, + /datum/gas/plasma = 20, + ) + +/obj/item/organ/internal/lungs/corrupt/Initialize(mapload) + . = ..() + AddElement(/datum/element/corrupted_organ) + +/obj/item/organ/internal/lungs/corrupt/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/breather) + . = ..() + if (!. || IS_IN_MANSUS(owner) || breather.has_reagent(/datum/reagent/water/holywater) || !prob(cough_chance)) + return + breather.emote("cough"); + var/chosen_gas = pick_weight(gas_types) + var/datum/gas_mixture/mix_to_spawn = new() + mix_to_spawn.add_gas(pick(chosen_gas)) + mix_to_spawn.gases[chosen_gas][MOLES] = gas_amount + mix_to_spawn.temperature = breather.bodytemperature + log_atmos("[owner] coughed some gas into the air due to their corrupted lungs.", mix_to_spawn) + var/turf/open/our_turf = get_turf(breather) + our_turf.assume_air(mix_to_spawn) + + +/// It's full of worms +/obj/item/organ/internal/appendix/corrupt + name = "corrupt appendix" + desc = "What kind of dark, cosmic force is even going to bother to corrupt an appendix?" + /// How likely are we to spawn worms? + var/worm_chance = 2 + +/obj/item/organ/internal/appendix/corrupt/Initialize(mapload) + . = ..() + AddElement(/datum/element/corrupted_organ) + AddElement(/datum/element/noticable_organ, "abdomen is distended... and wiggling.", BODY_ZONE_PRECISE_GROIN) + +/obj/item/organ/internal/appendix/corrupt/on_life(seconds_per_tick, times_fired) + . = ..() + if (owner.stat != CONSCIOUS || owner.has_reagent(/datum/reagent/water/holywater) || IS_IN_MANSUS(owner) || !SPT_PROB(worm_chance, seconds_per_tick)) + return + owner.vomit(vomit_type = /obj/effect/decal/cleanable/vomit/nebula/worms, distance = 0) + owner.Knockdown(0.5 SECONDS) diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm index 6faf49021266..1c001c57fcd2 100644 --- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm +++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm @@ -86,3 +86,17 @@ return bloodiest_wound.adjust_blood_flow(-0.5) + +/// Torment the target with a frightening hand +/proc/fire_curse_hand(mob/living/carbon/victim) + var/grab_dir = turn(victim.dir, pick(-90, 90, 180, 180)) // Not in front, favour behind + var/turf/spawn_turf = get_ranged_target_turf(victim, grab_dir, 8) + if (isnull(spawn_turf)) + return + new /obj/effect/temp_visual/dir_setting/curse/grasp_portal(spawn_turf, victim.dir) + playsound(spawn_turf, 'sound/effects/curse2.ogg', 80, TRUE, -1) + var/obj/projectile/curse_hand/hel/hand = new (spawn_turf) + hand.preparePixelProjectile(victim, spawn_turf) + if (QDELETED(hand)) // safety check if above fails - above has a stack trace if it does fail + return + hand.fire() diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_curse.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_curse.dm new file mode 100644 index 000000000000..6a12ecc30922 --- /dev/null +++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_curse.dm @@ -0,0 +1,93 @@ +/// A curse given to people to disencourage them from retaliating against someone who sacrificed them +/datum/status_effect/heretic_curse + id = "heretic_curse" + alert_type = null + status_type = STATUS_EFFECT_MULTIPLE // In case several different people sacrifice you, unlucky + /// Who cursed us? + var/mob/living/the_curser + /// Don't experience bad things too often + COOLDOWN_DECLARE(consequence_cooldown) + +/datum/status_effect/heretic_curse/on_creation(mob/living/new_owner, mob/living/the_curser) + src.the_curser = the_curser + return ..() + +/datum/status_effect/heretic_curse/Destroy() + the_curser = null + return ..() + +/datum/status_effect/heretic_curse/on_apply() + if (isnull(the_curser) || !iscarbon(owner)) + return FALSE + + the_curser.AddElement(/datum/element/relay_attackers) + RegisterSignal(the_curser, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_curser_attacked)) + RegisterSignal(the_curser, COMSIG_QDELETING, PROC_REF(on_curser_destroyed)) + + owner.AddElement(/datum/element/relay_attackers) + RegisterSignal(owner, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_owner_attacked)) + + return TRUE + +/datum/status_effect/heretic_curse/on_remove() + UnregisterSignal(owner, COMSIG_ATOM_WAS_ATTACKED) + UnregisterSignal(the_curser, COMSIG_ATOM_WAS_ATTACKED) + the_curser = null + + +/// If the heretic that cursed us is destroyed this thing is useless now +/datum/status_effect/heretic_curse/proc/on_curser_destroyed() + SIGNAL_HANDLER + qdel(src) + +/// If we attack the guy who cursed us, that's no good +/datum/status_effect/heretic_curse/proc/on_curser_attacked(datum/source, mob/attacker) + SIGNAL_HANDLER + if (attacker != owner || !HAS_TRAIT(source, TRAIT_ALLOW_HERETIC_CASTING)) + return + log_combat(owner, the_curser, "attacked", addition = "and lost some organs because they had previously been sacrificed by them.") + experience_the_consequences() + +/// If we are attacked by the guy who cursed us, that's also no good +/datum/status_effect/heretic_curse/proc/on_owner_attacked(datum/source, mob/attacker) + SIGNAL_HANDLER + if (attacker != the_curser || !HAS_TRAIT(attacker, TRAIT_ALLOW_HERETIC_CASTING)) + return + log_combat(the_curser, owner, "attacked", addition = "and as they had previously sacrificed them, removed some of their organs.") + experience_the_consequences() + +/// Experience something you may not enjoy which may also significantly shorten your lifespan +/datum/status_effect/heretic_curse/proc/experience_the_consequences() + if (!COOLDOWN_FINISHED(src, consequence_cooldown) || owner.stat != CONSCIOUS) + return + + var/mob/living/carbon/carbon_owner = owner + var/obj/item/bodypart/chest/organ_storage = owner.get_bodypart(BODY_ZONE_CHEST) + if (isnull(organ_storage)) + carbon_owner.gib() // IDK how you don't have a chest but you're not getting away that easily + return + + var/list/removable_organs = list() + for(var/obj/item/organ/internal/bodypart_organ in organ_storage.contents) + if(bodypart_organ.organ_flags & ORGAN_UNREMOVABLE) + continue + removable_organs += bodypart_organ + + if (!length(removable_organs)) + return // This one is a little more possible but they're probably already in pretty bad shape by this point + + var/obj/item/organ/internal/removing_organ = pick(removable_organs) + + if (carbon_owner.vomit(blood = TRUE)) + carbon_owner.visible_message(span_boldwarning("[carbon_owner] vomits out [carbon_owner.p_their()] [removing_organ]")) + else + carbon_owner.visible_message(span_boldwarning("[carbon_owner]'s [removing_organ] rips itself out of `[carbon_owner.p_their()] chest!")) + + removing_organ.Remove(carbon_owner) + + var/turf/land_turf = get_step(carbon_owner, carbon_owner.dir) + if (land_turf.is_blocked_turf(exclude_mobs = TRUE)) + land_turf = carbon_owner.drop_location() + + removing_organ.forceMove(land_turf) + COOLDOWN_START(src, consequence_cooldown, 10 SECONDS) diff --git a/code/modules/antagonists/heretic/magic/ash_ascension.dm b/code/modules/antagonists/heretic/magic/ash_ascension.dm index c9b6b2ce0458..61b8b66dc403 100644 --- a/code/modules/antagonists/heretic/magic/ash_ascension.dm +++ b/code/modules/antagonists/heretic/magic/ash_ascension.dm @@ -33,7 +33,7 @@ /// Simple status effect for adding a ring of fire around a mob. /datum/status_effect/fire_ring id = "fire_ring" - tick_interval = 0.1 SECONDS + tick_interval = 0.2 SECONDS status_type = STATUS_EFFECT_REFRESH alert_type = null /// The radius of the ring around us. diff --git a/code/modules/antagonists/heretic/magic/shadow_cloak.dm b/code/modules/antagonists/heretic/magic/shadow_cloak.dm index ad942c71a328..3aa0b68c7c78 100644 --- a/code/modules/antagonists/heretic/magic/shadow_cloak.dm +++ b/code/modules/antagonists/heretic/magic/shadow_cloak.dm @@ -126,7 +126,7 @@ /datum/status_effect/shadow_cloak id = "shadow_cloak" alert_type = null - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK /// How much damage we've been hit with var/damage_sustained = 0 /// How much damage we can be hit with before it starts rolling reveal chances diff --git a/code/modules/antagonists/heretic/magic/star_touch.dm b/code/modules/antagonists/heretic/magic/star_touch.dm index bab07f0871bd..6095dcd01a32 100644 --- a/code/modules/antagonists/heretic/magic/star_touch.dm +++ b/code/modules/antagonists/heretic/magic/star_touch.dm @@ -116,7 +116,7 @@ /datum/status_effect/cosmic_beam id = "cosmic_beam" - tick_interval = 0.1 SECONDS + tick_interval = 0.2 SECONDS duration = 1 MINUTES status_type = STATUS_EFFECT_REPLACE alert_type = null diff --git a/code/modules/antagonists/heretic/status_effects/buffs.dm b/code/modules/antagonists/heretic/status_effects/buffs.dm index 46f1d65acfd2..667668ae1bc7 100644 --- a/code/modules/antagonists/heretic/status_effects/buffs.dm +++ b/code/modules/antagonists/heretic/status_effects/buffs.dm @@ -109,7 +109,7 @@ id = "Silver Knives" alert_type = null status_type = STATUS_EFFECT_MULTIPLE - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK /// The number of blades we summon up to. var/max_num_blades = 4 /// The radius of the blade's orbit. @@ -242,7 +242,7 @@ /datum/status_effect/caretaker_refuge id = "Caretaker’s Last Refuge" status_type = STATUS_EFFECT_REFRESH - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = null var/static/list/caretaking_traits = list(TRAIT_HANDS_BLOCKED, TRAIT_IGNORESLOWDOWN, TRAIT_SECLUDED_LOCATION) diff --git a/code/modules/antagonists/heretic/status_effects/debuffs.dm b/code/modules/antagonists/heretic/status_effects/debuffs.dm index 1088f846525f..9050cb97d247 100644 --- a/code/modules/antagonists/heretic/status_effects/debuffs.dm +++ b/code/modules/antagonists/heretic/status_effects/debuffs.dm @@ -31,7 +31,7 @@ /datum/status_effect/void_chill/lasting id = "lasting_void_chill" - duration = -1 + duration = STATUS_EFFECT_PERMANENT /datum/movespeed_modifier/void_chill multiplicative_slowdown = 0.3 @@ -199,7 +199,7 @@ alert_type = /atom/movable/screen/alert/status_effect/heretic_lastresort duration = 12 SECONDS status_type = STATUS_EFFECT_REPLACE - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK /atom/movable/screen/alert/status_effect/heretic_lastresort name = "Last Resort" @@ -219,7 +219,7 @@ /datum/status_effect/moon_converted id = "moon converted" alert_type = /atom/movable/screen/alert/status_effect/moon_converted - duration = -1 + duration = STATUS_EFFECT_PERMANENT status_type = STATUS_EFFECT_REPLACE ///used to track damage var/damage_sustained = 0 diff --git a/code/modules/antagonists/heretic/status_effects/ghoul.dm b/code/modules/antagonists/heretic/status_effects/ghoul.dm index ff730573da59..4f0d89ee815c 100644 --- a/code/modules/antagonists/heretic/status_effects/ghoul.dm +++ b/code/modules/antagonists/heretic/status_effects/ghoul.dm @@ -1,7 +1,7 @@ /datum/status_effect/ghoul id = "ghoul" status_type = STATUS_EFFECT_UNIQUE - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = /atom/movable/screen/alert/status_effect/ghoul /// The new max health value set for the ghoul, if supplied var/new_max_health diff --git a/code/modules/asset_cache/asset_cache_client.dm b/code/modules/asset_cache/asset_cache_client.dm index 3cff8cb41f3e..cf786de09e8b 100644 --- a/code/modules/asset_cache/asset_cache_client.dm +++ b/code/modules/asset_cache/asset_cache_client.dm @@ -36,7 +36,10 @@ var/job = ++last_asset_job var/t = 0 var/timeout_time = timeout - src << browse({""}, "window=asset_cache_browser&file=asset_cache_send_verify.htm") + if(byond_version < 516) + src << browse({""}, "window=asset_cache_browser&file=asset_cache_send_verify.htm") + else + src << browse({""}, "window=asset_cache_browser&file=asset_cache_send_verify.htm") while(!completed_asset_jobs["[job]"] && t < timeout_time) // Reception is handled in Topic() stoplag(1) // Lock up the caller until this is received. diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm index b99dec3a9744..ff8f58be33a2 100644 --- a/code/modules/asset_cache/asset_list.dm +++ b/code/modules/asset_cache/asset_list.dm @@ -8,10 +8,12 @@ GLOBAL_LIST_EMPTY(asset_datums) //get an assetdatum or make a new one //does NOT ensure it's filled, if you want that use get_asset_datum() -/proc/load_asset_datum(type) +/proc/load_asset_datum(type) as /datum/asset + RETURN_TYPE(/datum/asset) return GLOB.asset_datums[type] || new type() -/proc/get_asset_datum(type) +/proc/get_asset_datum(type) as /datum/asset + RETURN_TYPE(/datum/asset) var/datum/asset/loaded_asset = GLOB.asset_datums[type] || new type() return loaded_asset.ensure_ready() diff --git a/code/modules/asset_cache/validate_assets.html b/code/modules/asset_cache/validate_assets.html index 78bcbb92a1ab..70fdca8a9d77 100644 --- a/code/modules/asset_cache/validate_assets.html +++ b/code/modules/asset_cache/validate_assets.html @@ -8,7 +8,7 @@ //this is used over window.location because window.location has a character limit in IE. function sendbyond(text) { var xhr = new XMLHttpRequest(); - xhr.open('GET', '?'+text, true); + xhr.open('GET', 'byond://?' + text, true); xhr.send(null); } var xhr = new XMLHttpRequest(); @@ -24,6 +24,6 @@ }; xhr.send(null); - + diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index dd5c2b429c12..f5afc598e84d 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -225,9 +225,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( /////////// /client/New(TopicData) - if(byond_version >= 516) // Enable 516 compat browser storage mechanisms - winset(src, "", "browser-options=byondstorage,find") - var/tdata = TopicData //save this for later use TopicData = null //Prevent calls to client.Topic from connect @@ -237,6 +234,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( GLOB.clients += src GLOB.directory[ckey] = src + if(byond_version >= 516) + winset(src, null, list("browser-options" = "find,refresh,byondstorage")) + // Instantiate stat panel stat_panel = new(src, "statbrowser") stat_panel.subscribe(src, PROC_REF(on_stat_panel_message)) @@ -460,7 +460,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( add_admin_verbs() var/memo_message = get_message_output("memo") if(memo_message) - to_chat(src, memo_message) + to_chat(src, memo_message, type = MESSAGE_TYPE_ADMINLOG, confidential = TRUE) adminGreet() if (mob && reconnecting) var/stealth_admin = mob.client?.holder?.fakekey @@ -479,7 +479,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( var/time_stamp = query_last_connected.item[1] var/unread_notes = get_message_output("note", ckey, FALSE, time_stamp) if(unread_notes) - to_chat(src, unread_notes) + to_chat(src, unread_notes, type = MESSAGE_TYPE_ADMINPM, confidential = TRUE) qdel(query_last_connected) var/cached_player_age = set_client_age_from_db(tdata) //we have to cache this because other shit may change it and we need it's current value now down below. @@ -517,7 +517,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( message_admins("[key_name_admin(src)] (IP: [address], ID: [computer_id]) is a new BYOND account [account_age] day[(account_age == 1?"":"s")] old, created on [account_join_date].") if (CONFIG_GET(flag/irc_first_connection_alert)) send2tgs_adminless_only("new_byond_user", "[key_name(src)] (IP: [address], ID: [computer_id]) is a new BYOND account [account_age] day[(account_age == 1?"":"s")] old, created on [account_join_date].") - get_message_output("watchlist entry", ckey) if(check_overwatch() && CONFIG_GET(flag/vpn_kick)) return validate_key_in_db() @@ -546,7 +545,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( convert_notes_sql(ckey) var/user_messages = get_message_output("message", ckey) if(user_messages) - to_chat(src, user_messages) + to_chat(src, user_messages, type = MESSAGE_TYPE_ADMINPM, confidential = TRUE) if(!winexists(src, "asset_cache_browser")) // The client is using a custom skin, tell them. to_chat(src, span_warning("Unable to access asset cache browser, if you are using a custom skin file, please allow DS to download the updated version, if you are not, then make a bug report. This is not a critical issue but can cause issues with resource downloading, as it is impossible to know when extra resources arrived to you.")) diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm index 69c426bf476e..af3f0c566483 100644 --- a/code/modules/clothing/under/accessories/badges.dm +++ b/code/modules/clothing/under/accessories/badges.dm @@ -195,6 +195,18 @@ GLOBAL_LIST_INIT(pride_pin_reskins, list( . = ..() unique_reskin = GLOB.pride_pin_reskins +/obj/item/clothing/accessory/pride/reskin_obj(mob/M) + . = ..() + post_reskin(M) + +/obj/item/clothing/accessory/pride/post_reskin(mob/our_mob) + for(var/pride_name as anything in GLOB.pride_pin_reskins) + if(GLOB.pride_pin_reskins[pride_name] == icon_state) + name = "[lowertext(pride_name)] pin" + return + + name = initial(name) // If we somehow fail to find our pride in the global list, just make us generic + ///Awarded for being dutiful and extinguishing the debt from the "Indebted" quirk. /obj/item/clothing/accessory/debt_payer_pin name = "debt payer pin" diff --git a/code/modules/events/ghost_role/blob.dm b/code/modules/events/ghost_role/blob.dm index 99836ba2b585..89284678f64d 100644 --- a/code/modules/events/ghost_role/blob.dm +++ b/code/modules/events/ghost_role/blob.dm @@ -1,10 +1,10 @@ /datum/round_event_control/blob name = "Blob" typepath = /datum/round_event/ghost_role/blob - weight = 5 //monkie edit: 10 to 5 + weight = 4 //monkie edit: 10 to 4 max_occurrences = 1 min_players = 35 //monkie edit: 20 to 35 - earliest_start = 80 MINUTES //monkie edit: 20 to 90 + earliest_start = 80 MINUTES //monkie edit: 20 to 80 //dynamic_should_hijack = TRUE category = EVENT_CATEGORY_ENTITIES description = "Spawns a new blob overmind." diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm index ea1426515a5d..17a2cbc9656e 100644 --- a/code/modules/holiday/holidays.dm +++ b/code/modules/holiday/holidays.dm @@ -816,8 +816,7 @@ list( /obj/item/toy/xmas_cracker = 3, /obj/item/clothing/head/costume/santa = 1, - /obj/item/a_gift/anything = 1, - /obj/item/a_gift = 4 + /obj/item/a_gift/anything = 1 ) = maint_holiday_weight, ) diff --git a/code/modules/mining/equipment/monster_organs/brimdust_sac.dm b/code/modules/mining/equipment/monster_organs/brimdust_sac.dm index a77e526a9d1f..62a3acdb4472 100644 --- a/code/modules/mining/equipment/monster_organs/brimdust_sac.dm +++ b/code/modules/mining/equipment/monster_organs/brimdust_sac.dm @@ -71,7 +71,7 @@ id = "brimdust_coating" stacks = 0 max_stacks = 3 - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK consumed_on_threshold = FALSE alert_type = /atom/movable/screen/alert/status_effect/brimdust_coating status_type = STATUS_EFFECT_REFRESH // Allows us to add one stack at a time by just applying the effect diff --git a/code/modules/mob/living/basic/lavaland/basilisk/basilisk_overheat.dm b/code/modules/mob/living/basic/lavaland/basilisk/basilisk_overheat.dm index c0b49fbdc619..71e938c5164e 100644 --- a/code/modules/mob/living/basic/lavaland/basilisk/basilisk_overheat.dm +++ b/code/modules/mob/living/basic/lavaland/basilisk/basilisk_overheat.dm @@ -2,6 +2,7 @@ /datum/status_effect/basilisk_overheat id = "basilisk_overheat" duration = 3 MINUTES + alert_type = null /// Things which will chill us out if we get hit by them var/static/list/chilling_reagents = list( /datum/reagent/medicine/cryoxadone, diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_effects.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_effects.dm index 0eeec231973e..41be027797fd 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_effects.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_effects.dm @@ -1,5 +1,7 @@ /// Parent type for all unique revenant status effects /datum/status_effect/revenant + id = STATUS_EFFECT_ID_ABSTRACT + alert_type = null /datum/status_effect/revenant/on_creation(mob/living/new_owner, duration) if(isnum(duration)) diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index bc6d946ca869..46a210893fc2 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -56,6 +56,7 @@ if(HAS_TRAIT(src, TRAIT_NOBREATH)) setOxyLoss(0) //idk how because spec life should cover this losebreath = 0 + failed_last_breath = 0 return var/next_breath = 4 diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 3ee50b9d16cf..3c9ad7ea9fed 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -457,7 +457,7 @@ return var/datum/status_effect/incapacitating/sleeping/S = IsSleeping() if(S) - S.duration = -1 + S.duration = STATUS_EFFECT_PERMANENT else S = apply_status_effect(/datum/status_effect/incapacitating/sleeping, -1) return S diff --git a/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm b/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm index d533d98cf3e1..14cf735d7da1 100644 --- a/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm +++ b/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm @@ -608,7 +608,7 @@ // Show it to ghosts var/ghost_message = span_name("[sender] [rigged ? "(as [fake_name]) Rigged " : ""]PDA Message --> [span_name("[signal.format_target()]")]: \"[signal.format_message()]\"") - for(var/mob/player_mob as anything in GLOB.current_observers_list) + for(var/mob/player_mob as anything in GLOB.dead_mob_list) if(player_mob.client && !player_mob.client?.prefs) stack_trace("[player_mob] ([player_mob.ckey]) had null prefs, which shouldn't be possible!") continue diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm index 09c2e1fb2aa0..abef4540dd71 100644 --- a/code/modules/power/lighting/light.dm +++ b/code/modules/power/lighting/light.dm @@ -196,10 +196,10 @@ /obj/machinery/light/proc/handle_fire(area/source, new_fire) SIGNAL_HANDLER - update() + update(dont_burn_out = TRUE) // update the icon_state and luminosity of the light depending on its state -/obj/machinery/light/proc/update(trigger = TRUE) +/obj/machinery/light/proc/update(trigger = TRUE, dont_burn_out = FALSE) switch(status) if(LIGHT_BROKEN,LIGHT_BURNED,LIGHT_EMPTY) on = FALSE @@ -230,11 +230,13 @@ brightness_set = bulb_outer_range * bulb_major_emergency_brightness_mul var/matching = light && brightness_set == light.light_outer_range && power_set == light.light_power && color_set == light.light_color && FC == light.light_falloff_curve && IR == light.light_inner_range if(!matching) - switchcount++ - if( prob( min(60, (switchcount**2)*0.01) ) ) - if(trigger) + var/should_set = TRUE + if(!dont_burn_out) + switchcount++ + if(trigger && prob(min(60, (switchcount ** 2) * 0.01))) burn_out() - else + should_set = FALSE + if(should_set) use_power = ACTIVE_POWER_USE set_light( l_outer_range = brightness_set, @@ -242,7 +244,7 @@ l_power = power_set, l_falloff_curve = FC, l_color = color_set - ) + ) else if(has_emergency_power(LIGHT_EMERGENCY_POWER_USE) && !turned_off()) use_power = IDLE_POWER_USE low_power_mode = TRUE diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm index 82b78a4859ed..56c3a44a5837 100644 --- a/code/modules/projectiles/guns/magic/wand.dm +++ b/code/modules/projectiles/guns/magic/wand.dm @@ -28,6 +28,11 @@ icon_state = "[base_icon_state][charges ? null : "-drained"]" return ..() +/obj/item/gun/magic/wand/can_trigger_gun(mob/living/user, akimbo_usage) + if(akimbo_usage) + return FALSE + return ..() + /obj/item/gun/magic/wand/attack(atom/target, mob/living/user) if(target == user) return diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index d47c2c05a5be..95780d649f09 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -211,6 +211,10 @@ var/parried = FALSE ///how long we paralyze for as this is a disorient var/paralyze_timer = 0 + /// If this projectile inflicts debilitating + var/debilitating = FALSE + /// How many stacks the projectile applies per hit. Default is 1, each stack adds 0.05, it stacks up to 2x stamina damage + var/debilitate_mult = 1 /obj/projectile/Initialize(mapload) . = ..() @@ -256,13 +260,13 @@ // i know that this is probably more with wands and gun mods in mind, but it's a bit silly that the projectile on_hit signal doesn't ping the projectile itself. // maybe we care what the projectile thinks! See about combining these via args some time when it's not 5AM - if(stamina >= 15 && isliving(target)) + if(debilitating == TRUE && isliving(target)) var/mob/living/living = target var/datum/status_effect/stacking/debilitated/effect = living.has_status_effect(/datum/status_effect/stacking/debilitated) if(effect) - effect.add_stacks(1) + effect.add_stacks(debilitate_mult) else - living.apply_status_effect(/datum/status_effect/stacking/debilitated, 1) + living.apply_status_effect(/datum/status_effect/stacking/debilitated, debilitate_mult) if(fired_from) SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_ON_HIT, firer, target, Angle, def_zone, blocked) diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index f4ad04bbcb49..b170a7a26db2 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -118,18 +118,23 @@ tracer_type = /obj/effect/projectile/tracer/disabler muzzle_type = /obj/effect/projectile/muzzle/disabler impact_type = /obj/effect/projectile/impact/disabler + debilitating = TRUE + debilitate_mult = 3 /obj/projectile/beam/disabler/weak stamina = 15 + debilitate_mult = 1.5 /obj/projectile/beam/disabler/smoothbore name = "unfocused disabler beam" weak_against_armour = TRUE + debilitate_mult = 2 /obj/projectile/beam/disabler/smoothbore/prime name = "focused disabler beam" weak_against_armour = FALSE stamina = 35 + debilitate_mult = 3.5 /obj/projectile/beam/pulse name = "pulse" diff --git a/code/modules/projectiles/projectile/bullets/shotgun.dm b/code/modules/projectiles/projectile/bullets/shotgun.dm index 2199b1c88c75..4d9eda9e2d04 100644 --- a/code/modules/projectiles/projectile/bullets/shotgun.dm +++ b/code/modules/projectiles/projectile/bullets/shotgun.dm @@ -19,15 +19,29 @@ name = "tungsten sabot-slug" icon_state = "gauss" damage = 30 //20 less than slugs. - speed = 0.3 //sub-caliber + lighter = speed. (Smaller number = faster) - armour_penetration = 80 //Tis a solid-tungsten penetrator, what do you expect? - wound_bonus = 5 //30 seems kinda wimpy, 5 bonus puts it over the threshold + speed = 0.25 //sub-caliber + lighter = speed. (Smaller number = faster) + armour_penetration = 70 //Tis a solid-tungsten penetrator, what do you expect? + wound_bonus = -3 //Had issues with massive wounding behind armor, thus... ricochets_max = 2 //Unlike slugs which tend to squish on impact, these are hard enough to bounce rarely. - ricochet_chance = 60 + ricochet_chance = 90 ricochet_auto_aim_range = 4 ricochet_incidence_leeway = 55 embedding = null - demolition_mod = 4 //One higher than it was at 35. It might do less damage, but it will still go through stuff just fine. + demolition_mod = 5 //High-velocity tungsten > steel doors + projectile_piercing = PASSMOB + + +/obj/projectile/bullet/shotgun_slug/apds/pierce/on_hit(atom/target, blocked = 0, pierce_hit) + if(isliving(target)) + // If the bullet has already gone through 3 people, stop it on this hit + if(pierces > 3) + projectile_piercing = NONE + + if(damage > 10) // Lets just be safe with this one + damage -= 7 + armour_penetration -= 10 + + return ..() /obj/projectile/bullet/shotgun_beanbag name = "beanbag slug" @@ -112,6 +126,8 @@ ricochet_incidence_leeway = 75 /// Subtracted from the ricochet chance for each tile traveled var/tile_dropoff_ricochet = 4 + debilitating = TRUE + debilitate_mult = 1 /obj/projectile/bullet/pellet/shotgun_rubbershot/Range() if(ricochet_chance > 0) diff --git a/code/modules/projectiles/projectile/energy/ebow.dm b/code/modules/projectiles/projectile/energy/ebow.dm index e1da23495f4a..8495cbccbdf1 100644 --- a/code/modules/projectiles/projectile/energy/ebow.dm +++ b/code/modules/projectiles/projectile/energy/ebow.dm @@ -7,6 +7,8 @@ eyeblur = 20 SECONDS knockdown = 10 slur = 10 SECONDS + debilitating = TRUE + debilitate_mult = 5 /obj/projectile/energy/bolt/halloween name = "candy corn" diff --git a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm index 1f38e6377b6e..25b2d4113204 100644 --- a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm @@ -98,18 +98,7 @@ Basically, we fill the time between now and 2s from now with hands based off the /datum/reagent/inverse/helgrasp/proc/spawn_hands(mob/living/carbon/owner) if(!owner && iscarbon(holder.my_atom))//Catch timer owner = holder.my_atom - //Adapted from the end of the curse - but lasts a short time - var/grab_dir = turn(owner.dir, pick(-90, 90, 180, 180)) //grab them from a random direction other than the one faced, favoring grabbing from behind - var/turf/spawn_turf = get_ranged_target_turf(owner, grab_dir, 8)//Larger range so you have more time to dodge - if(!spawn_turf) - return - new/obj/effect/temp_visual/dir_setting/curse/grasp_portal(spawn_turf, owner.dir) - playsound(spawn_turf, 'sound/effects/curse2.ogg', 80, TRUE, -1) - var/obj/projectile/curse_hand/hel/hand = new (spawn_turf) - hand.preparePixelProjectile(owner, spawn_turf) - if(QDELETED(hand)) //safety check if above fails - above has a stack trace if it does fail - return - hand.fire() + fire_curse_hand(owner) //At the end, we clear up any loose hanging timers just in case and spawn any remaining lag_remaining hands all at once. /datum/reagent/inverse/helgrasp/on_mob_delete(mob/living/owner) diff --git a/code/modules/religion/burdened/psyker.dm b/code/modules/religion/burdened/psyker.dm index e913584081cd..e9cf7793d8fd 100644 --- a/code/modules/religion/burdened/psyker.dm +++ b/code/modules/religion/burdened/psyker.dm @@ -270,7 +270,7 @@ id = "psychic_projection" alert_type = null remove_on_fullheal = TRUE - tick_interval = 0.1 SECONDS + tick_interval = 0.2 SECONDS /// Times the target has dry fired a weapon. var/times_dry_fired = 0 /// Needs to reach times_dry_fired for the next dry fire to happen. diff --git a/code/modules/religion/hunt/hunter_pinpointer.dm b/code/modules/religion/hunt/hunter_pinpointer.dm index d2a69d1d57f5..a2312b265a9f 100644 --- a/code/modules/religion/hunt/hunter_pinpointer.dm +++ b/code/modules/religion/hunt/hunter_pinpointer.dm @@ -15,7 +15,7 @@ alert_type = /atom/movable/screen/alert/status_effect/agent_pinpointer/hunters_sense minimum_range = HUNTER_MINIMUM_RANGE tick_interval = HUNTER_PING_TIME - duration = -1 + duration = STATUS_EFFECT_PERMANENT range_fuzz_factor = HUNTER_FUZZ_FACTOR ///Attempting to locate a nearby target to scan and point towards. diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index ca21aa697171..c3fb51a129c4 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -533,7 +533,6 @@ design_ids = list( "surgery_heal_brute_upgrade", "surgery_heal_burn_upgrade", - "surgery_heal_robot_upgrade", // monkestation edit: robot surgeries "surgery_filter_upgrade", // monke edit: improved blood filter surgery ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000) @@ -547,7 +546,6 @@ design_ids = list( "surgery_heal_brute_upgrade_femto", "surgery_heal_burn_upgrade_femto", - "surgery_heal_robot_upgrade_femto", // monkestation edit: robot surgeries "surgery_heal_combo", "surgery_lobotomy", "surgery_wing_reconstruction", diff --git a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm index 67c92ea362ca..717e56196e88 100644 --- a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm +++ b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm @@ -61,8 +61,8 @@ /datum/status_effect/slimerecall id = "slime_recall" - duration = -1 //Will be removed by the extract. - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT //Will be removed by the extract. + tick_interval = STATUS_EFFECT_NO_TICK alert_type = null var/interrupted = FALSE var/mob/target @@ -98,7 +98,7 @@ /datum/status_effect/frozenstasis id = "slime_frozen" status_type = STATUS_EFFECT_UNIQUE - duration = -1 //Will remove self when block breaks. + duration = STATUS_EFFECT_PERMANENT //Will remove self when block breaks. alert_type = /atom/movable/screen/alert/status_effect/freon/stasis var/obj/structure/ice_stasis/cube @@ -127,7 +127,7 @@ /datum/status_effect/slime_clone id = "slime_cloned" status_type = STATUS_EFFECT_UNIQUE - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = null var/mob/living/clone var/datum/mind/originalmind //For when the clone gibs. @@ -172,7 +172,7 @@ /datum/status_effect/slime_clone_decay id = "slime_clonedecay" status_type = STATUS_EFFECT_UNIQUE - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = /atom/movable/screen/alert/status_effect/clone_decay /datum/status_effect/slime_clone_decay/tick() @@ -229,7 +229,7 @@ /datum/status_effect/rebreathing id = "rebreathing" - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = null /datum/status_effect/rebreathing/tick() @@ -439,7 +439,7 @@ /datum/status_effect/stabilized //The base stabilized extract effect, has no effect of its' own. id = "stabilizedbase" - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = null /// Item which provides this buff var/obj/item/slimecross/stabilized/linked_extract @@ -830,7 +830,7 @@ /datum/status_effect/pinkdamagetracker id = "pinkdamagetracker" - duration = -1 + duration = STATUS_EFFECT_PERMANENT alert_type = null var/damage = 0 var/lasthealth diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm index 7b33538404dc..fbf1baa5edea 100644 --- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm +++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm @@ -156,6 +156,7 @@ limb_id = SPECIES_SLIMEPERSON is_dimorphic = TRUE composition_effects = list(/datum/element/soft_landing = 0.5) + wing_types = list(/obj/item/organ/external/wings/functional/slime) palette = /datum/color_palette/generic_colors palette_key = MUTANT_COLOR @@ -199,6 +200,7 @@ biological_state = (BIO_FLESH|BIO_BLOODED) limb_id = SPECIES_LUMINESCENT is_dimorphic = TRUE + wing_types = list(/obj/item/organ/external/wings/functional/slime) palette = /datum/color_palette/generic_colors palette_key = MUTANT_COLOR diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm index 31af8b7deb7c..57482868132c 100644 --- a/code/modules/surgery/organs/lungs.dm +++ b/code/modules/surgery/organs/lungs.dm @@ -114,6 +114,8 @@ ///our last lung pop adventure var/lung_pop_tick = 0 + //fucking checks if the last breath failed was actually from something or not, and if not clears it + var/failed_last_breath_checker = 0 // assign the respiration_type /obj/item/organ/internal/lungs/Initialize(mapload) @@ -640,6 +642,10 @@ // Check for moles of gas and handle partial pressures / special conditions. if(num_moles > 0 && not_low_pressure && not_high_pressure) // Breath has more than 0 moles of gas. + //checks if the last breath was one of the not- this fucking breaths so it can clear it + if(failed_last_breath_checker) + breather.failed_last_breath = FALSE + failed_last_breath_checker = FALSE // Route gases through mask filter if breather is wearing one. if(istype(breather.wear_mask) && (breather.wear_mask.clothing_flags & GAS_FILTERING) && breather.wear_mask.has_filter) breath = breather.wear_mask.consume_filter(breath) @@ -672,11 +678,13 @@ breather.cause_pain(BODY_ZONE_CHEST, 10, BRUTE) apply_organ_damage(5) breather.failed_last_breath = TRUE + failed_last_breath_checker = TRUE lung_pop_tick++ // Robot, don't care lol else if((owner && !HAS_TRAIT(owner, TRAIT_ASSISTED_BREATHING))) // Can't breathe! breather.failed_last_breath = TRUE + failed_last_breath_checker = TRUE // The list of gases in the breath. var/list/breath_gases = breath.gases diff --git a/code/modules/tgs/v5/undefs.dm b/code/modules/tgs/v5/undefs.dm index acd19dfa6411..ca49e46cdffa 100644 --- a/code/modules/tgs/v5/undefs.dm +++ b/code/modules/tgs/v5/undefs.dm @@ -18,6 +18,7 @@ #undef DMAPI5_PARAMETER_ACCESS_IDENTIFIER #undef DMAPI5_PARAMETER_CUSTOM_COMMANDS +#undef DMAPI5_PARAMETER_TOPIC_PORT #undef DMAPI5_CHUNK #undef DMAPI5_CHUNK_PAYLOAD diff --git a/code/modules/tgui_panel/external.dm b/code/modules/tgui_panel/external.dm index 692244a6e80d..e48ddeb22390 100644 --- a/code/modules/tgui_panel/external.dm +++ b/code/modules/tgui_panel/external.dm @@ -19,27 +19,19 @@ // Failed to fix, using tgalert as fallback action = tgalert(src, "Did that work?", "", "Yes", "No, switch to old ui") if (action == "No, switch to old ui") - winset(src, "output", "on-show=&is-disabled=0&is-visible=1") - winset(src, "browseroutput", "is-disabled=1;is-visible=0") + winset(src, "legacy_output_selector", "left=output_legacy") log_tgui(src, "Failed to fix.", context = "verb/fix_tgui_panel") /client/proc/nuke_chat() // Catch all solution (kick the whole thing in the pants) - winset(src, "output", "on-show=&is-disabled=0&is-visible=1") - winset(src, "browseroutput", "is-disabled=1;is-visible=0") + winset(src, "legacy_output_selector", "left=output_legacy") if(!tgui_panel || !istype(tgui_panel)) log_tgui(src, "tgui_panel datum is missing", context = "verb/fix_tgui_panel") tgui_panel = new(src) tgui_panel.initialize(force = TRUE) // Force show the panel to see if there are any errors - winset(src, "output", "is-disabled=1&is-visible=0") - winset(src, "browseroutput", "is-disabled=0;is-visible=1") - if(byond_version >= 516) - var/list/options = list("byondstorage", "find") - if(check_rights_for(src, R_DEBUG)) - options += "devtools" - winset(src, null, "browser-options=[options.Join(",")]") + winset(src, "legacy_output_selector", "left=output_browser") /client/verb/refresh_tgui() set name = "Refresh TGUI" diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index c662ec1f4b74..44e3e0078541 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -219,6 +219,7 @@ #include "spritesheets.dm" #include "stack_singular_name.dm" #include "station_trait_tests.dm" +#include "status_effect_validity.dm" #include "stomach.dm" #include "strange_reagent.dm" #include "strippable.dm" diff --git a/code/modules/unit_tests/status_effect_validity.dm b/code/modules/unit_tests/status_effect_validity.dm new file mode 100644 index 000000000000..29d48c69c6bf --- /dev/null +++ b/code/modules/unit_tests/status_effect_validity.dm @@ -0,0 +1,61 @@ +/// Validates status effect tick interval setup +/datum/unit_test/status_effect_ticks + +/datum/unit_test/status_effect_ticks/Run() + for(var/datum/status_effect/checking as anything in subtypesof(/datum/status_effect)) + if(initial(checking.id) == STATUS_EFFECT_ID_ABSTRACT) + continue + var/tick_speed = initial(checking.tick_interval) + if(tick_speed == STATUS_EFFECT_NO_TICK) + continue + if(tick_speed == INFINITY) + TEST_FAIL("Status effect [checking] has tick_interval set to INFINITY, this is not how you prevent ticks - use tick_interval = STATUS_EFFECT_NO_TICK instead.") + continue + if(tick_speed == 0) + TEST_FAIL("Status effect [checking] has tick_interval set to 0, this is not how you prevent ticks - use tick_interval = STATUS_EFFECT_NO_TICK instead.") + continue + switch(initial(checking.processing_speed)) + if(STATUS_EFFECT_FAST_PROCESS, STATUS_EFFECT_PRIORITY) // monkestation edit: STATUS_EFFECT_PRIORITY + if(tick_speed < SSfastprocess.wait) + TEST_FAIL("Status effect [checking] has tick_interval set to [tick_speed], which is faster than SSfastprocess can tick ([SSfastprocess.wait]).") + if(STATUS_EFFECT_NORMAL_PROCESS) + if(tick_speed < SSprocessing.wait) + TEST_FAIL("Status effect [checking] has tick_interval set to [tick_speed], which is faster than SSprocessing can tick ([SSprocessing.wait]).") + else + TEST_FAIL("Invalid processing speed for status effect [checking] : [initial(checking.processing_speed)]") + +/// Validates status effect alert type setup +/datum/unit_test/status_effect_alert + +/datum/unit_test/status_effect_alert/Run() + // The base typepath is used to indicate "I didn't set an alert type" + var/bad_alert_type = /datum/status_effect::alert_type + TEST_ASSERT_NOTNULL(bad_alert_type, "No alert type defined in /datum/status_effect - This test may be redundant now.") + + for(var/datum/status_effect/checking as anything in subtypesof(/datum/status_effect)) + if(initial(checking.id) == STATUS_EFFECT_ID_ABSTRACT) + continue + if(initial(checking.alert_type) != bad_alert_type) + continue + TEST_FAIL("[checking] has not set alert_type. If you don't want an alert, set alert_type = null - \ + Otherwise, give it an alert subtype.") + +/// Validates status effect id setup +/datum/unit_test/status_effect_ids + +/datum/unit_test/status_effect_ids/Run() + // The base id is used to indicate "I didn't set an id" + var/bad_id = /datum/status_effect::id + TEST_ASSERT_NOTNULL(bad_id, "No id defined in /datum/status_effect - This test may be redundant now.") + + for(var/datum/status_effect/checking as anything in subtypesof(/datum/status_effect)) + if(initial(checking.id) == STATUS_EFFECT_ID_ABSTRACT) + // we are just assuming that a child of an abstract should not be abstract. + // of course in practice, this may not always be the case - but if you're + // structuring a status effect like this, you can just change the parent id to anything else + var/datum/status_effect/checking_parent = initial(checking.parent_type) + if(initial(checking_parent.id) != STATUS_EFFECT_ID_ABSTRACT) + continue + if(initial(checking.id) != bad_id) + continue + TEST_FAIL("[checking] has not set an id. This is required for status effects.") diff --git a/html/changelogs/archive/2024-12.yml b/html/changelogs/archive/2024-12.yml index 5fd33b0fc7de..d040f0dd1b7d 100644 --- a/html/changelogs/archive/2024-12.yml +++ b/html/changelogs/archive/2024-12.yml @@ -268,6 +268,127 @@ runtime errors. - balance: Clown spawner artifacts will no longer spawn another spawner if the previous one still exists. + - bugfix: The cargo imports UI now shows the correct price when the station has + strong or distant supply lines. + - bugfix: Ordering from cargo imports now actually checks to see if you have the + correct amount of money when the station has strong or distant supply lines. + - qol: There is now a chat notice and a visual indicator in the lobby for when there's + a non-standard overflow. + - qol: Added a preference (disabled by default) to confirm readying up when there's + a non-standard overflow. + - bugfix: Fixed a bug where the overflow station trait could roll assistants, which + just did... nothing. + Gboster-0: + - qol: Made pride pins display their pride in the name, like "transexual pride pin" + instead of "pride pin" + Gw0sty: + - rscadd: brought the Christmas spirit (tree) to Thesues! + - rscadd: Gave Ancient/charlie/oldstation a supermatter shard that gives its warnings + on Uncommon frequency + - rscadd: Added three uncommon radio keys to the science box on charlie so borgs + can get the frequency + - bugfix: Replaced knifes in Theseus's kitchen with kitchen knives. Now (nothing + changes) chefs can use their knifes in peace. + - bugfix: Made it so you can actually leave the Theseus Gulag shuttle. + - bugfix: added missing job to Icons for alt title jobs. + - bugfix: Ipc surgery research now works as intended + - rscadd: Added Blob Infections as a triggerable event. A dormant host containing + acting as the vessel of a Overmind in them. + - balance: Infected Blob hosts will show as sick no matter their active state. + - balance: Lowered the weight of blobs spawning. + - balance: Research directors remote now has AI upload access. + Loiosh42: + - balance: Tweaks AP shotgun shells, and gives them pierce + - balance: Nerfs the Bobr + - balance: Gives the Wylom AM rifle pierce ThePooba: - bugfix: nerfs christmas presents and allows the real chrimas presents to spawn - bugfix: blob zombie event wont spawn you on asteroids and shit anymore + - balance: christmas presents from 50% to 5% chance to get the "toys only" christmas + present so you gusy get your op bullshit back, but added some stinky stuff to + blacklist like the item spawner wand and the var edit wand(what the fuck) + - balance: thermonuclear catsplosion more dangerous, twice as likely + - bugfix: Added no interact to hardlight spears so you cant switch them in hand + and hardlock yourself + - bugfix: i think tiny artifacts can now detonate properly now i couldnt get them + to spawn properly + Uristthedorf: + - rscadd: EMPing a holosign creator, will destroy all of the holosigns. +2024-12-20: + Absolucy: + - bugfix: The xenobio Slimeperson and Luminscent subspecies will now properly get + slime wings from drinking a flight potion. + - bugfix: Slime in vents/slime grinders/etc will no longer secrete ooze on the floor. + - admin: Added a new mapping verb, Export Lighting Info (behind Mapping Verbs - + Enable) that exports the lighting info of z levels into a json file. + Siro: + - rscadd: Refunding of unreviewed mixtapes at round end + - rscadd: Cassette Manager interface + - rscadd: Admin.Games Review Cassettes to the admin menu + - rscdel: tgui alert for already reviewed cassettes + - bugfix: tgui alert spamming the client when attempting to review an already reviewed + cassette. + - bugfix: Runtime error on cassette_review Destroy proc + Wisemonster, Jacquerel: + - rscdel: Being sacrificed by a Heretic no longer gives you an incurable phobia. + - rscadd: Being sacrificed by a Heretic will drop 2-4 of your organs on the ground + and replace them with "corrupt organs" with negative effects which can be suppressed + with Holy Water. + - rscadd: Players who have been sacrificed by Heretics will experience additional + and rapidly lethal consequences for attempting to fight someone who previously + sacrificed them, as long as that person is wearing a focus. +2024-12-21: + Absolucy: + - refactor: The effect from a monster hunter's blood vial is now just a status effect, + rather than a weird combination between a status effect and a reagent. + Coll6: + - rscadd: Ooze sucker machinery can be stored in multi tool buffer. + - bugfix: Allows ooze suckers to be linked if its inside a valid pen, that is also + linked to the same controller. + Loiosh42: + - balance: Reworks Debilitated, and by extension buffs disablers + Shoddd: + - rscadd: Explorer now has 6 slots + TheColorCyan: + - bugfix: interdyne MOD no longer looks off when worn by people without digitigrade + legs + ThePooba: + - bugfix: fixes the CANT... BREATH... moodlet but when you could breathe fine + Uristthedorf: + - rscdel: Removes tasers from the armory. + - rscadd: Tasers can be bought at cargo. + Wisemonster: + - rscadd: Added a disabler to QM's locker. It has a cargo-locked firing pin that + can only be used in the cargo's warehouse and storage. + - bugfix: Heretic sacrifice targets will have their blood restored and cleared of + chems if they are sent to the mansus. +2024-12-22: + Absolucy: + - bugfix: Properly marked the message type and confidentiality of a few admin/mentor-related + notifications. + - bugfix: The map selected for the next round should actually always be the map + that loads next round. + - rscadd: Added tinyfans to the arrivals shuttle and public mining shuttle doors + on Blueshift. + - qol: All ghosts now see PDA messages, not just observers. + Absolucy, Melbert: + - bugfix: You should be afflicted by the "Curse of Mundanity" far, far less. + Shoddd: + - bugfix: you can no longer akimbo and fire a wand forever + Tractor Mann: + - admin: Admins will no longer be told you dusted someone with power gloves when + you didnt, and that you shocked someone with power gloves when you dust them. +2024-12-23: + Absolucy: + - admin: Admins can now much more easily see which of a player's items came from + a gift. + - rscadd: Ghosts, santa, and anyone after roundend can an examine an item to see + if its a gift, who opened it, and how long ago they did so. + - rscadd: Closely examining (double-examining) an item will now tell you if it was + from a gift. + - qol: Fire alarms no longer burn out lights. +2024-12-25: + Shoddd: + - bugfix: fixes a typo in regen extract + Wisemonster: + - bugfix: Fixed the syndicate lavaland base delamming if they spawned with an SM diff --git a/icons/hud/lobby/overflow.dmi b/icons/hud/lobby/overflow.dmi new file mode 100644 index 000000000000..b7391193f0d2 Binary files /dev/null and b/icons/hud/lobby/overflow.dmi differ diff --git a/icons/mob/clothing/modsuit/mod_clothing.dmi b/icons/mob/clothing/modsuit/mod_clothing.dmi index 537b1ffd3ebd..bb7063c32ae2 100644 Binary files a/icons/mob/clothing/modsuit/mod_clothing.dmi and b/icons/mob/clothing/modsuit/mod_clothing.dmi differ diff --git a/icons/obj/clothing/modsuit/mod_clothing.dmi b/icons/obj/clothing/modsuit/mod_clothing.dmi index 7eb92f1c3519..684c99a1c190 100644 Binary files a/icons/obj/clothing/modsuit/mod_clothing.dmi and b/icons/obj/clothing/modsuit/mod_clothing.dmi differ diff --git a/interface/skin.dmf b/interface/skin.dmf index 9a4704e95120..d46f5c55b184 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -226,7 +226,7 @@ window "infowindow" window "outputwindow" elem "outputwindow" type = MAIN - pos = 281,0 + pos = 0,0 size = 640x480 anchor1 = -1,-1 anchor2 = -1,-1 @@ -285,15 +285,26 @@ window "outputwindow" anchor2 = -1,-1 is-visible = false saved-params = "" - elem "browseroutput" - type = BROWSER + elem "legacy_output_selector" + type = CHILD pos = 0,0 size = 640x456 anchor1 = 0,0 anchor2 = 100,100 - is-visible = false - is-disabled = true - saved-params = "" + saved-params = "splitter" + left = "output_legacy" + is-vert = false + +window "output_legacy" + elem "output_legacy" + type = MAIN + pos = 0,0 + size = 640x456 + anchor1 = -1,-1 + anchor2 = -1,-1 + background-color = none + saved-params = "pos;size;is-minimized;is-maximized" + is-pane = true elem "output" type = OUTPUT pos = 0,0 @@ -301,6 +312,25 @@ window "outputwindow" anchor1 = 0,0 anchor2 = 100,100 is-default = true + saved-params = "max-lines" + +window "output_browser" + elem "output_browser" + type = MAIN + pos = 0,0 + size = 640x456 + anchor1 = -1,-1 + anchor2 = -1,-1 + background-color = none + saved-params = "pos;size;is-minimized;is-maximized" + is-pane = true + elem "browseroutput" + type = BROWSER + pos = 0,0 + size = 640x456 + anchor1 = 0,0 + anchor2 = 100,100 + background-color = none saved-params = "" window "popupwindow" diff --git a/monkestation/code/datums/components/gift_item.dm b/monkestation/code/datums/components/gift_item.dm new file mode 100644 index 000000000000..d6f82e9fa89a --- /dev/null +++ b/monkestation/code/datums/components/gift_item.dm @@ -0,0 +1,86 @@ +/// Simple thing that marks an items as having come from a gift. +/datum/component/gift_item + /// The ckey of the player who opened the gift. + var/ckey + /// Weakref to the mob who opened the gift. + var/datum/weakref/giftee + /// Weakref to the mob who opened the gift. + var/datum/weakref/mind + /// The (real) name of mob who opened the gift. + var/name + /// The `world.time` when the gift was opened. + var/open_world_time + /// The `world.timeofday` when the gift was opened. + var/open_timeofday + +/datum/component/gift_item/Initialize(mob/living/giftee) + if(!isitem(parent)) + stack_trace("Tried to assign [type] to a non-item") + return COMPONENT_INCOMPATIBLE + if(!isliving(giftee)) + stack_trace("Tried to assign [type] to something that wasn't a living mob!") + return COMPONENT_INCOMPATIBLE + if(!giftee.ckey) + stack_trace("Tried to assign [type] to a non-player mob!") + return COMPONENT_INCOMPATIBLE + src.ckey = giftee.ckey + src.giftee = WEAKREF(giftee) + src.mind = WEAKREF(giftee.mind) + src.name = "[giftee.mind?.name || giftee.real_name || giftee.name || "N/A"]" + src.open_world_time = world.time + src.open_timeofday = world.timeofday + +/datum/component/gift_item/Destroy(force) + giftee = null + mind = null + return ..() + +/datum/component/gift_item/RegisterWithParent() + RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) + RegisterSignal(parent, COMSIG_ATOM_EXAMINE_MORE, PROC_REF(on_examine_more)) + RegisterSignal(parent, COMSIG_VV_TOPIC, PROC_REF(handle_vv_topic)) + ADD_TRAIT(parent, TRAIT_GIFT_ITEM, type) + +/datum/component/gift_item/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_ATOM_EXAMINE, COMSIG_ATOM_EXAMINE_MORE, COMSIG_VV_TOPIC)) + REMOVE_TRAIT(parent, TRAIT_GIFT_ITEM, type) + +/datum/component/gift_item/proc/on_examine(obj/item/source, mob/examiner, list/examine_text) + SIGNAL_HANDLER + if(check_rights_for(examiner.client, R_ADMIN)) + // ensure we always target the right mob for the admin buttons + var/mob/target_mob = resolve_opener_mob() + examine_text += "" + examine_text += span_bold("\[") + span_info(" This item came from a gift opened by [span_name(name)] ([ckey]) [ADMIN_FULLMONTY_NONAME(target_mob)] ") + span_bold("\]") + examine_text += span_bold("\[") + span_info(" It was unwrapped from a gift [span_bold(DisplayTimeText(world.time - open_world_time) + " ago")], at server time [span_bold(time2text(open_timeofday, "YYYY-MM-DD hh:mm:ss"))] ") + span_bold("\]") + examine_text += "" + else if(isobserver(examiner) || HAS_TRAIT(examiner, TRAIT_PRESENT_VISION) || SSticker.current_state >= GAME_STATE_FINISHED) + examine_text += "" + examine_text += span_bold("\[") + span_info(" This item came from a gift opened by [span_name(name)] [DisplayTimeText(world.time - open_world_time)] ago ") + span_bold("\]") + examine_text += "" + +/datum/component/gift_item/proc/on_examine_more(obj/item/source, mob/examiner, list/examine_text) + SIGNAL_HANDLER + examine_text += span_info("This item seems to have been a gift!") + +/datum/component/gift_item/proc/resolve_opener_mob() as /mob + RETURN_TYPE(/mob) + var/mob/opener = giftee.resolve() + var/datum/mind/opener_mind = mind.resolve() + if(opener?.ckey == ckey) + return opener + else if(opener_mind?.current?.ckey == ckey) + return opener_mind.current + else if(GLOB.directory[ckey]) + var/client/current_client = GLOB.directory[ckey] + return current_client.mob + else + for(var/mob/mob in GLOB.mob_list) + if(mob.ckey == ckey) + return mob + +/datum/component/gift_item/proc/handle_vv_topic(datum/source, mob/user, list/href_list) + SIGNAL_HANDLER + if(href_list[VV_HK_EXAMINE_GIFT] && check_rights(R_ADMIN)) + user.examinate(parent) + return COMPONENT_VV_HANDLED diff --git a/monkestation/code/datums/meta_tokens.dm b/monkestation/code/datums/meta_tokens.dm index b1ff622e8870..a4f278f4da68 100644 --- a/monkestation/code/datums/meta_tokens.dm +++ b/monkestation/code/datums/meta_tokens.dm @@ -59,6 +59,10 @@ GLOBAL_LIST_INIT(patreon_etoken_values, list( /datum/meta_token_holder/proc/convert_list_to_tokens(list/saved_tokens) if(!length(saved_tokens)) return + for(var/token in saved_tokens) + if(isnull(saved_tokens[token])) + saved_tokens[token] = 0 + total_low_threat_tokens = saved_tokens["low_threat"] total_medium_threat_tokens = saved_tokens["medium_threat"] total_high_threat_tokens = saved_tokens["high_threat"] @@ -92,13 +96,14 @@ GLOBAL_LIST_INIT(patreon_etoken_values, list( if(!patreon?.has_access(ACCESS_COMMAND_RANK)) return FALSE var/month_number = text2num(time2text(world.time, "MM")) - owner.prefs.token_month = month_number if(owner.prefs.token_month != month_number) owner.prefs.adjust_metacoins(owner?.ckey, 10000, "Monthly Monkecoin rations.", TRUE, FALSE, FALSE) if(!patreon.has_access(ACCESS_TRAITOR_RANK)) owner.prefs.save_preferences() + owner.prefs.token_month = month_number return FALSE if(owner.prefs.token_month == month_number) + owner.prefs.token_month = month_number return FALSE donator_token++ owner.prefs.token_month = month_number diff --git a/monkestation/code/datums/station_traits/negative_traits.dm b/monkestation/code/datums/station_traits/negative_traits.dm index 0e31a8ba8144..b760911c00a8 100644 --- a/monkestation/code/datums/station_traits/negative_traits.dm +++ b/monkestation/code/datums/station_traits/negative_traits.dm @@ -6,13 +6,55 @@ report_message = "The Clown Planet has discovered a weakness in the ID scanners of specific airlocks." trait_to_give = STATION_TRAIT_CLOWN_BRIDGE +/datum/station_trait/overflow_job_bureaucracy + /// Has the initial notification been sent to all players yet? + var/notified = FALSE + /// Current type of the picked job. + var/datum/job/picked_job + /// The icon tag for the job, used to display the job icon in the pregame/login notification, + var/icon_tag + +/datum/station_trait/overflow_job_bureaucracy/New() + . = ..() + RegisterSignal(SSdcs, COMSIG_GLOB_CLIENT_CONNECT, PROC_REF(notify_on_login)) + RegisterSignal(SSticker, COMSIG_TICKER_ENTER_PREGAME, PROC_REF(notify_all_players)) + +/// Picks and sets the overflow role. /datum/station_trait/overflow_job_bureaucracy/proc/set_overflow_job_override(datum/source) SIGNAL_HANDLER - var/datum/job/picked_job var/list/possible_jobs = SSjob.joinable_occupations.Copy() + possible_jobs -= SSjob.GetJobType(SSjob.overflow_role) while(length(possible_jobs) && !picked_job?.allow_overflow) picked_job = pick_n_take(possible_jobs) if(!picked_job) CRASH("Failed to find valid job to pick for overflow!") chosen_job_name = lowertext(picked_job.title) // like Chief Engineers vs like chief engineers SSjob.set_overflow_role(picked_job.type) + var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/job_icons) + icon_tag = sheet.icon_tag(sanitize_css_class_name(lowertext(picked_job.config_tag || picked_job.title))) + if(icon_tag) + icon_tag = "[icon_tag] " // vertically align and add a space to the end + +/// Signal handler to notify all connected clients of the overflow role, ran after all initializations are complete. +/datum/station_trait/overflow_job_bureaucracy/proc/notify_all_players(datum/source) + SIGNAL_HANDLER + notified = TRUE + for(var/mob/dead/new_player/lobby_user in GLOB.new_player_list) + INVOKE_ASYNC(src, PROC_REF(notify_client), lobby_user) +l +/// Signal handler to notify newly connecting clients of the overflow role. +/datum/station_trait/overflow_job_bureaucracy/proc/notify_on_login(datum/source, client/client) + SIGNAL_HANDLER + INVOKE_ASYNC(src, PROC_REF(notify_client), client) + +/// Notifies a single client of the current overflow role. +/datum/station_trait/overflow_job_bureaucracy/proc/notify_client(target) + if(!picked_job || !notified) + return + var/client/client = get_player_client(target) + client?.tgui_panel?.window?.send_asset(get_asset_datum(/datum/asset/spritesheet/job_icons)) + to_chat( + target = client, + html = examine_block(span_big(span_info("Current overflow role is [icon_tag][span_name(html_encode(picked_job.title))], make sure to check your job preferences!"))), + type = MESSAGE_TYPE_INFO, + ) diff --git a/monkestation/code/datums/status_effects/buffs.dm b/monkestation/code/datums/status_effects/buffs.dm index ab6bd0eb4ad7..e5da8e526408 100644 --- a/monkestation/code/datums/status_effects/buffs.dm +++ b/monkestation/code/datums/status_effects/buffs.dm @@ -18,7 +18,7 @@ /datum/status_effect/mayhem show_duration = TRUE - tick_interval = 0 // Just pass me the SSfastprocess ticks please. + tick_interval = STATUS_EFFECT_NO_TICK // Just pass me the SSfastprocess ticks please. alert_type = /atom/movable/screen/alert/status_effect/mayhem diff --git a/monkestation/code/datums/status_effects/changeling.dm b/monkestation/code/datums/status_effects/changeling.dm index 67bbb18c5fe0..1d25b4328871 100644 --- a/monkestation/code/datums/status_effects/changeling.dm +++ b/monkestation/code/datums/status_effects/changeling.dm @@ -19,7 +19,7 @@ id = "changeling_adrenaline" duration = 20 SECONDS show_duration = TRUE - tick_interval = 0 + tick_interval = 0.2 SECONDS alert_type = /atom/movable/screen/alert/status_effect/changeling/adrenaline status_type = STATUS_EFFECT_REFRESH @@ -69,7 +69,7 @@ id = "changeling_panacea" duration = 1 MINUTE show_duration = TRUE - tick_interval = 0 + tick_interval = 0.2 SECONDS alert_type = /atom/movable/screen/alert/status_effect/changeling/panacea status_type = STATUS_EFFECT_REFRESH @@ -158,7 +158,7 @@ /datum/status_effect/changeling_muscles id = "changeling_muscles" - tick_interval = 0 + tick_interval = 1 SECONDS processing_speed = STATUS_EFFECT_NORMAL_PROCESS alert_type = /atom/movable/screen/alert/status_effect/changeling/muscles diff --git a/monkestation/code/datums/status_effects/food_buffs.dm b/monkestation/code/datums/status_effects/food_buffs.dm index 44ae8e55d659..566fc6f05415 100644 --- a/monkestation/code/datums/status_effects/food_buffs.dm +++ b/monkestation/code/datums/status_effects/food_buffs.dm @@ -1,9 +1,9 @@ /datum/status_effect/food + id = STATUS_EFFECT_ID_ABSTRACT duration = 10 MINUTES status_type = STATUS_EFFECT_REPLACE show_duration = TRUE - /datum/status_effect/food/proc/apply_quality(quality) return diff --git a/monkestation/code/game/alternate_appearance.dm b/monkestation/code/game/alternate_appearance.dm new file mode 100644 index 000000000000..13e355f60e43 --- /dev/null +++ b/monkestation/code/game/alternate_appearance.dm @@ -0,0 +1,4 @@ +/datum/atom_hud/alternate_appearance/basic/actually_everyone + +/datum/atom_hud/alternate_appearance/basic/actually_everyone/mobShouldSee(mob/M) + return TRUE diff --git a/monkestation/code/game/objects/effects/anomalies/anomalies_lifebringer.dm b/monkestation/code/game/objects/effects/anomalies/anomalies_lifebringer.dm index 35582d25aa6e..5f1d506dcc06 100644 --- a/monkestation/code/game/objects/effects/anomalies/anomalies_lifebringer.dm +++ b/monkestation/code/game/objects/effects/anomalies/anomalies_lifebringer.dm @@ -43,7 +43,7 @@ active = FALSE var/turf/open/tile = get_turf(src) if(istype(tile)) - tile.atmos_spawn_air("o2=10;plasma=1;TEMP=3000") + tile.atmos_spawn_air("o2=45;plasma=15;TEMP=6000") return var/mob/living/basic/pet/chosen_pet = pick(pet_type_cache) diff --git a/monkestation/code/game/objects/items/devices/radio/encryptionkey.dm b/monkestation/code/game/objects/items/devices/radio/encryptionkey.dm index 10997d861408..e87fa9c5253c 100644 --- a/monkestation/code/game/objects/items/devices/radio/encryptionkey.dm +++ b/monkestation/code/game/objects/items/devices/radio/encryptionkey.dm @@ -8,3 +8,10 @@ /obj/item/encryptionkey/headset_uncommon name = "dusty encryption key" channels = list(RADIO_CHANNEL_UNCOMMON = 1) + +/obj/item/encryptionkey/heads/blueshield + name = "\proper the blueshield's encryption key" + icon_state = "cypherkey_centcom" + channels = list(RADIO_CHANNEL_COMMAND = 1, RADIO_CHANNEL_SECURITY = 1) + greyscale_config = /datum/greyscale_config/encryptionkey_centcom + greyscale_colors = "#1d2657#dca01b" diff --git a/monkestation/code/game/objects/items/guns/crank_guns.dm b/monkestation/code/game/objects/items/guns/crank_guns.dm index 5c96a49462cd..93f942bdb870 100644 --- a/monkestation/code/game/objects/items/guns/crank_guns.dm +++ b/monkestation/code/game/objects/items/guns/crank_guns.dm @@ -29,7 +29,7 @@ damage = 35 stamina = 60 -/obj/projectile/beam/disabler/smoothbore/prime +/obj/projectile/beam/disabler/smoothbore/prime /// ????????? why is this here *and* in beams ??????? stamina = 65 /obj/item/ammo_casing/energy/laser/musket @@ -47,3 +47,5 @@ weak_against_armour = FALSE armour_penetration = 25 //less powerful than armor piercing rounds wound_bonus = 10 + debilitating = TRUE + debilitate_mult = 2 diff --git a/monkestation/code/game/objects/items/holosign_creator.dm b/monkestation/code/game/objects/items/holosign_creator.dm new file mode 100644 index 000000000000..d66f8265aa18 --- /dev/null +++ b/monkestation/code/game/objects/items/holosign_creator.dm @@ -0,0 +1,5 @@ +/obj/item/holosign_creator/emp_act(severity) + . = ..() + for(var/obj/structure/holosign/sign as anything in signs) + if(prob(90 / severity)) + qdel(sign) diff --git a/monkestation/code/game/objects/items/implants/hardlight.dm b/monkestation/code/game/objects/items/implants/hardlight.dm index d85536126869..daa5078b77a4 100644 --- a/monkestation/code/game/objects/items/implants/hardlight.dm +++ b/monkestation/code/game/objects/items/implants/hardlight.dm @@ -189,6 +189,7 @@ righthand_file = 'monkestation/icons/mob/inhands/polearms_righthand.dmi' slot_flags = null can_charge = FALSE //ITS A SPEAR + interaction_flags_item = NONE item_flags = NEEDS_PERMIT | DROPDEL | ABSTRACT | NO_MAT_REDEMPTION resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF light_system = OVERLAY_LIGHT @@ -198,6 +199,10 @@ var/spears_left = 5 ammo_type = /obj/item/ammo_casing/magic/hardlight_spear +//obj/item/gun/magic/hardlight_spear/attack_hand(mob/user, list/modifiers) +// . = ..() + + /obj/item/gun/magic/hardlight_spear/Initialize(mapload) . = ..() AddComponent(/datum/component/jousting) diff --git a/monkestation/code/game/objects/items/objects/items/robot/robot_upgrades.dm b/monkestation/code/game/objects/items/robot/items/robot_upgrades.dm similarity index 100% rename from monkestation/code/game/objects/items/objects/items/robot/robot_upgrades.dm rename to monkestation/code/game/objects/items/robot/items/robot_upgrades.dm diff --git a/monkestation/code/modules/a_medical_day/lungless.dm b/monkestation/code/modules/a_medical_day/lungless.dm index 3611d366aa00..73da7035d906 100644 --- a/monkestation/code/modules/a_medical_day/lungless.dm +++ b/monkestation/code/modules/a_medical_day/lungless.dm @@ -1,8 +1,8 @@ /datum/status_effect/lungless id = "no_lungs" alert_type = null - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK /datum/status_effect/lungless/on_apply() if(!iscarbon(owner)) diff --git a/monkestation/code/modules/admin/verbs/mapping.dm b/monkestation/code/modules/admin/verbs/mapping.dm new file mode 100644 index 000000000000..897469af998d --- /dev/null +++ b/monkestation/code/modules/admin/verbs/mapping.dm @@ -0,0 +1,47 @@ +/// Returns a list of the lighting level of each open turf on a z-level. +/proc/encode_lighting_levels(z) as /list + RETURN_TYPE(/list) + if(!isnum(z) || !ISINRANGE(z, 1, world.maxz)) + z = SSmapping.levels_by_trait(ZTRAIT_STATION)[1] + stack_trace("Invalid z-level given, defaulting to first station z-level ([z])") + var/list/lighting = new(world.maxx, world.maxy) + for(var/turf/open/floor/floor in Z_TURFS(z)) + lighting[floor.x][floor.y] = floor.is_softly_lit() ? 0 : floor.get_lumcount() + CHECK_TICK + return lighting + +/client/proc/export_lighting_info() + set name = "Export Lighting Info" + set desc = "Exports a JSON file containing info about the lighting level of all floor turfs on a given Z-level." + set category = "Mapping" + + if(!check_rights(R_DEBUG)) + return + var/list/options = list("All Station Z-Levels") + var/turf/our_turf = get_turf(mob) + if(!isnewplayer(mob) && !isnull(our_turf)) + options += "Current Z-Level" + var/list/zs + switch(tgui_alert(src, "What Z-levels would you like to export lighting info for?", "Select Z Levels", options + list("Cancel"))) + if("All Station Z-Levels") + zs = SSmapping.levels_by_trait(ZTRAIT_STATION) + if("Current Z-Level") + zs = list(our_turf.z) + else + return + + var/list/lighting_info = list() + switch(length(zs)) + if(1) + lighting_info = encode_lighting_levels(zs[1]) + if(2 to INFINITY) + for(var/z in zs) + lighting_info["[z]"] = encode_lighting_levels(z) + else + to_chat(src, span_warning("No Z-levels selected!")) + return + + var/file_name = "[ckey]_lighting_info_[time2text(world.timeofday, "MMM_DD_YYYY_hh-mm-ss")].json" + var/json_file = file("tmp/[file_name]") + WRITE_FILE(json_file, json_encode(lighting_info)) + DIRECT_OUTPUT(src, ftp(json_file, file_name)) diff --git a/monkestation/code/modules/antagonist/blob/blob_antag.dm b/monkestation/code/modules/antagonist/blob/blob_antag.dm new file mode 100644 index 000000000000..1265d4740128 --- /dev/null +++ b/monkestation/code/modules/antagonist/blob/blob_antag.dm @@ -0,0 +1,4 @@ +/datum/antagonist/blob/infection/apply_innate_effects(mob/mob_override) + . = ..() + var/mob/target = mob_override || owner.current + ADD_TRAIT(target, TRAIT_BLOB_ALLY, type) diff --git a/monkestation/code/modules/antagonists/borers/code/status_effects.dm b/monkestation/code/modules/antagonists/borers/code/status_effects.dm index edfca2eff0ec..060702ba563e 100644 --- a/monkestation/code/modules/antagonists/borers/code/status_effects.dm +++ b/monkestation/code/modules/antagonists/borers/code/status_effects.dm @@ -1,6 +1,6 @@ /datum/status_effect/borer_sugar id = "borer_sugar" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/borer_sugar diff --git a/monkestation/code/modules/antagonists/clock_cult/status_effects.dm b/monkestation/code/modules/antagonists/clock_cult/status_effects.dm index 4243d4518a2a..5014a9514d85 100644 --- a/monkestation/code/modules/antagonists/clock_cult/status_effects.dm +++ b/monkestation/code/modules/antagonists/clock_cult/status_effects.dm @@ -2,7 +2,7 @@ id = "interdicted" duration = 2.5 SECONDS status_type = STATUS_EFFECT_REFRESH - tick_interval = 1 + tick_interval = 0.2 SECONDS alert_type = /atom/movable/screen/alert/status_effect/interdiction /// If we kicked the owner out of running mode var/running_toggled = FALSE diff --git a/monkestation/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm b/monkestation/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm index fc06138a24d0..6822992e285d 100644 --- a/monkestation/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm +++ b/monkestation/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm @@ -26,6 +26,16 @@ var/datum/mind/heretic_mind /// An assoc list of [ref] to [timers] - a list of all the timers of people in the shadow realm currently var/list/return_timers + /// Evil organs we can put in people + var/static/list/grantable_organs = list( + /obj/item/organ/internal/appendix/corrupt, + /obj/item/organ/internal/eyes/corrupt, + /obj/item/organ/internal/heart/corrupt, + /obj/item/organ/internal/liver/corrupt, + /obj/item/organ/internal/lungs/corrupt, + /obj/item/organ/internal/stomach/corrupt, + /obj/item/organ/internal/tongue/corrupt, + ) /datum/heretic_knowledge/hunt_and_sacrifice/Destroy(force) heretic_mind = null @@ -199,6 +209,8 @@ heretic_datum.total_sacrifices++ heretic_datum.knowledge_points += 2 + sacrifice.apply_status_effect(/datum/status_effect/heretic_curse, user) + if(!begin_sacrifice(sacrifice)) disembowel_target(sacrifice) @@ -255,9 +267,14 @@ // and if we fail to revive them, don't proceede the chain sac_target.adjustOxyLoss(-100, FALSE) sac_target.grab_ghost() // monke edit: try to grab their ghost + if(!sac_target.heal_and_revive(50, span_danger("[sac_target]'s heart begins to beat with an unholy force as they return from death!"))) return + //monkestation addition start: + sac_target.reagents?.remove_all(sac_target.reagents.total_volume) //stops chems from killing in the mansus + sac_target.restore_blood() //stops target from just dying from low blood in the mansus + //monkestation addition end if(sac_target.AdjustUnconscious(SACRIFICE_SLEEP_DURATION)) to_chat(sac_target, span_hypnophrase("Your mind feels torn apart as you fall into a shallow slumber...")) else @@ -291,6 +308,8 @@ disembowel_target(sac_target) return + curse_organs(sac_target) + // Send 'em to the destination. If the teleport fails, just disembowel them and stop the chain if(!destination || !do_teleport(sac_target, destination, asoundin = 'sound/magic/repulse.ogg', asoundout = 'sound/magic/blind.ogg', no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC, forced = TRUE)) disembowel_target(sac_target) @@ -308,11 +327,40 @@ to_chat(sac_target, span_big(span_hypnophrase("Unnatural forces begin to claw at your every being from beyond the veil."))) sac_target.apply_status_effect(/datum/status_effect/unholy_determination, SACRIFICE_REALM_DURATION) + //monkestation addition start: + sac_target.reagents?.remove_all(sac_target.reagents.total_volume) //stops chems from killing in the mansus + sac_target.restore_blood() //stops target from just dying from low blood in the mansus + //monkestation addition end addtimer(CALLBACK(src, PROC_REF(after_target_wakes), sac_target), SACRIFICE_SLEEP_DURATION * 0.5) // Begin the minigame RegisterSignal(sac_target, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_target_escape)) // Cheese condition RegisterSignal(sac_target, COMSIG_LIVING_DEATH, PROC_REF(on_target_death)) // Loss condition +/// Apply a sinister curse to some of the target's organs as an incentive to leave us alone +/datum/heretic_knowledge/hunt_and_sacrifice/proc/curse_organs(mob/living/carbon/human/sac_target) + var/usable_organs = grantable_organs.Copy() + if (isplasmaman(sac_target)) + usable_organs -= /obj/item/organ/internal/lungs/corrupt // Their lungs are already more cursed than anything I could give them + + var/total_implant = rand(2, 4) + var/gave_any = FALSE + + for (var/i in 1 to total_implant) + if (!length(usable_organs)) + break + var/organ_path = pick_n_take(usable_organs) + var/obj/item/organ/internal/to_give = new organ_path + if (!to_give.Insert(sac_target)) + qdel(to_give) + else + gave_any = TRUE + + if (!gave_any) + return + + new /obj/effect/gibspawner/human/bodypartless(get_turf(sac_target)) + sac_target.visible_message(span_boldwarning("Several organs force themselves out of [sac_target]!")) + /** * This proc is called from [proc/after_target_sleeps] when the [sac_target] should be waking up.) * @@ -389,8 +437,6 @@ if(IS_HERETIC(sac_target)) var/datum/antagonist/heretic/victim_heretic = sac_target.mind?.has_antag_datum(/datum/antagonist/heretic) victim_heretic.knowledge_points -= 3 - else - sac_target.gain_trauma(/datum/brain_trauma/mild/phobia/heresy, TRAUMA_RESILIENCE_LOBOTOMY) // monke edit: allow lobotomy to cure the phobia // Wherever we end up, we sure as hell won't be able to explain sac_target.adjust_timed_status_effect(40 SECONDS, /datum/status_effect/speech/slurring/heretic) sac_target.adjust_stutter(40 SECONDS) diff --git a/monkestation/code/modules/antagonists/monster_hunters/events/wonderland.dm b/monkestation/code/modules/antagonists/monster_hunters/events/wonderland.dm index 1e6d4b3a8b42..241a39663571 100644 --- a/monkestation/code/modules/antagonists/monster_hunters/events/wonderland.dm +++ b/monkestation/code/modules/antagonists/monster_hunters/events/wonderland.dm @@ -49,104 +49,3 @@ GLOBAL_LIST_EMPTY(wonderland_marks) desc = "What is this doing here?" icon = 'monkestation/icons/bloodsuckers/rabbit.dmi' icon_state = "red_queen" - -/obj/structure/blood_fountain - name = "blood fountain" - desc = "A huge resevoir of thick blood, perhaps drinking some of it would restore some vigor..." - icon = 'monkestation/icons/bloodsuckers/blood_fountain.dmi' - icon_state = "blood_fountain" - plane = ABOVE_GAME_PLANE - anchored = TRUE - density = TRUE - bound_width = 64 - bound_height = 64 - resistance_flags = INDESTRUCTIBLE - - -/obj/structure/blood_fountain/Initialize(mapload) - . = ..() - add_overlay("droplet") - - -/obj/structure/blood_fountain/attackby(obj/item/bottle, mob/living/user, params) - if(!istype(bottle, /obj/item/blood_vial)) - balloon_alert(user, "Needs a blood vial!") - return ..() - var/obj/item/blood_vial/vial = bottle - vial.fill_vial(user) - -/obj/item/blood_vial - name = "blood vial" - desc = "Used to collect samples of blood from the dead-still blood fountain." - icon = 'monkestation/icons/bloodsuckers/weapons.dmi' - icon_state = "blood_vial_empty" - inhand_icon_state = "beaker" - lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' - righthand_file = 'icons/mob/inhands/items_righthand.dmi' - w_class = WEIGHT_CLASS_TINY - item_flags = NOBLUDGEON - var/filled = FALSE ///does the bottle contain fluid - -/obj/item/blood_vial/proc/fill_vial(mob/living/user) - if(filled) - balloon_alert(user, "Vial already full!") - return - filled = TRUE - icon_state = "blood_vial" - update_appearance() - - -/obj/item/blood_vial/attack_self(mob/living/user) - if(!filled) - balloon_alert(user, "Empty!") - return - filled = FALSE - user.apply_status_effect(/datum/status_effect/cursed_blood) - icon_state = "blood_vial_empty" - update_appearance() - playsound(src, 'monkestation/sound/bloodsuckers/blood_vial_slurp.ogg',50) - -/datum/status_effect/cursed_blood - id = "Blood" - duration = 20 SECONDS - alert_type = /atom/movable/screen/alert/status_effect/cursed_blood - show_duration = TRUE - -/atom/movable/screen/alert/status_effect/cursed_blood - name = "Cursed Blood" - desc = "Something foreign is coursing through your veins." - -/datum/status_effect/cursed_blood/on_apply() - . = ..() - to_chat(owner, span_warning("You feel a great power surging through you!")) - owner.add_movespeed_modifier(/datum/movespeed_modifier/cursed_blood) - - if(iscarbon(owner)) - owner.reagents.add_reagent(/datum/reagent/medicine/blood_vial, 15) - - return TRUE - -/datum/status_effect/cursed_blood/on_remove() - . = ..() - owner.remove_movespeed_modifier(/datum/movespeed_modifier/cursed_blood) - - - -/datum/reagent/medicine/blood_vial - name = "Blood Vial" - metabolization_rate = 0.4 * REAGENTS_METABOLISM - -/datum/reagent/medicine/blood_vial/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) - if(affected_mob.health < 90 && affected_mob.health > 0) - affected_mob.adjustOxyLoss(-1 * REM * seconds_per_tick, FALSE, required_biotype = affected_biotype, required_respiration_type = affected_respiration_type) - affected_mob.adjustToxLoss(-1 * REM * seconds_per_tick, FALSE, required_biotype = affected_biotype) - affected_mob.adjustBruteLoss(-2 * REM * seconds_per_tick, FALSE, required_bodytype = affected_bodytype) - affected_mob.adjustFireLoss(-2 * REM * seconds_per_tick, FALSE, required_bodytype = affected_bodytype) - - affected_mob.AdjustAllImmobility(-60 * REM * seconds_per_tick) - affected_mob.stamina.adjust(7 * REM * seconds_per_tick, TRUE) - ..() - . = TRUE - -/datum/movespeed_modifier/cursed_blood - multiplicative_slowdown = -0.6 diff --git a/monkestation/code/modules/antagonists/monster_hunters/events/wonderland_apocalypse.dm b/monkestation/code/modules/antagonists/monster_hunters/events/wonderland_apocalypse.dm index def42a7b2b9b..5627a3fe72b6 100644 --- a/monkestation/code/modules/antagonists/monster_hunters/events/wonderland_apocalypse.dm +++ b/monkestation/code/modules/antagonists/monster_hunters/events/wonderland_apocalypse.dm @@ -123,7 +123,7 @@ /datum/status_effect/wonderland_district id = "wonderland_district" alert_type = /atom/movable/screen/alert/status_effect/wonderland_district - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK /// List of /datum/action instance that we've registered `COMSIG_ACTION_TRIGGER` on. var/list/datum/action/registered_actions /// Typecache of spells to NOT trigger the effect on. diff --git a/monkestation/code/modules/antagonists/monster_hunters/tools/blood_vial.dm b/monkestation/code/modules/antagonists/monster_hunters/tools/blood_vial.dm new file mode 100644 index 000000000000..11afb8c8f5de --- /dev/null +++ b/monkestation/code/modules/antagonists/monster_hunters/tools/blood_vial.dm @@ -0,0 +1,92 @@ +/obj/structure/blood_fountain + name = "blood fountain" + desc = "A huge resevoir of thick blood, perhaps drinking some of it would restore some vigor..." + icon = 'monkestation/icons/bloodsuckers/blood_fountain.dmi' + icon_state = "blood_fountain" + plane = ABOVE_GAME_PLANE + anchored = TRUE + density = TRUE + bound_width = 64 + bound_height = 64 + resistance_flags = INDESTRUCTIBLE + +/obj/structure/blood_fountain/Initialize(mapload) + . = ..() + add_overlay("droplet") + +/obj/structure/blood_fountain/attackby(obj/item/bottle, mob/living/user, params) + if(!istype(bottle, /obj/item/blood_vial)) + balloon_alert(user, "need a blood vial!") + return ..() + var/obj/item/blood_vial/vial = bottle + vial.fill_vial(user) + +/obj/item/blood_vial + name = "blood vial" + desc = "Used to collect samples of blood from the dead-still blood fountain." + icon = 'monkestation/icons/bloodsuckers/weapons.dmi' + base_icon_state = "blood_vial" + icon_state = "blood_vial_empty" + inhand_icon_state = "beaker" + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + w_class = WEIGHT_CLASS_TINY + item_flags = NOBLUDGEON + var/filled = FALSE ///does the bottle contain fluid + +/obj/item/blood_vial/proc/fill_vial(mob/living/user) + if(filled) + balloon_alert(user, "vial already full!") + return + filled = TRUE + update_appearance(UPDATE_ICON_STATE) + +/obj/item/blood_vial/attack_self(mob/living/user) + if(!filled) + balloon_alert(user, "empty!") + return + filled = FALSE + user.apply_status_effect(/datum/status_effect/cursed_blood) + update_appearance(UPDATE_ICON_STATE) + playsound(src, 'monkestation/sound/bloodsuckers/blood_vial_slurp.ogg', vol = 50) + +/obj/item/blood_vial/update_icon_state() + icon_state = filled ? base_icon_state : "[base_icon_state]_empty" + return ..() + +/datum/status_effect/cursed_blood + id = "cursed_blood" + duration = 20 SECONDS + tick_interval = 0.2 SECONDS + status_type = STATUS_EFFECT_REFRESH + alert_type = /atom/movable/screen/alert/status_effect/cursed_blood + show_duration = TRUE + processing_speed = STATUS_EFFECT_PRIORITY + +/atom/movable/screen/alert/status_effect/cursed_blood + name = "Cursed Blood" + desc = "Something foreign is coursing through your veins!" + icon_state = "blooddrunk" + +/datum/status_effect/cursed_blood/on_apply() + to_chat(owner, span_warning("You feel a great power surging through you!")) + owner.add_movespeed_modifier(/datum/movespeed_modifier/cursed_blood) + return TRUE + +/datum/status_effect/cursed_blood/on_remove() + owner.remove_movespeed_modifier(/datum/movespeed_modifier/cursed_blood) + +/datum/status_effect/cursed_blood/tick(seconds_per_tick, times_fired) + var/needs_update = FALSE + if(ISINRANGE(owner.health, 0, 90)) + needs_update += owner.adjustBruteLoss(-2 * seconds_per_tick, updating_health = FALSE) + needs_update += owner.adjustFireLoss(-2 * seconds_per_tick, updating_health = FALSE) + needs_update += owner.adjustToxLoss(-1 * seconds_per_tick, updating_health = FALSE, forced = TRUE) + needs_update += owner.adjustOxyLoss(-1 * seconds_per_tick, updating_health = FALSE) + owner.AdjustAllImmobility((-6 SECONDS) * seconds_per_tick) + owner.stamina.adjust(7 * seconds_per_tick, forced = TRUE) + if(needs_update) + owner.updatehealth() + +/datum/movespeed_modifier/cursed_blood + multiplicative_slowdown = -0.6 diff --git a/monkestation/code/modules/antagonists/monster_hunters/tools/bnuuy_mask.dm b/monkestation/code/modules/antagonists/monster_hunters/tools/bnuuy_mask.dm index 25694199785d..751d1a544fb5 100644 --- a/monkestation/code/modules/antagonists/monster_hunters/tools/bnuuy_mask.dm +++ b/monkestation/code/modules/antagonists/monster_hunters/tools/bnuuy_mask.dm @@ -55,7 +55,7 @@ /datum/status_effect/bnuuy_mask id = "bnuuy_mask" alert_type = null - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK var/datum/component/glitching_state/wondershift /datum/status_effect/bnuuy_mask/on_apply() diff --git a/monkestation/code/modules/antagonists/monster_hunters/weapons/hunter_revolver.dm b/monkestation/code/modules/antagonists/monster_hunters/weapons/hunter_revolver.dm index 7bf7932ff9cd..ac89464b2049 100644 --- a/monkestation/code/modules/antagonists/monster_hunters/weapons/hunter_revolver.dm +++ b/monkestation/code/modules/antagonists/monster_hunters/weapons/hunter_revolver.dm @@ -39,7 +39,7 @@ /datum/status_effect/silver_bullet id = "silver_bullet" duration = 8 SECONDS - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_REFRESH alert_type = /atom/movable/screen/alert/status_effect/silver_bullet diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/bomb.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/bomb.dm index 56ec1504836b..9aa995be713d 100644 --- a/monkestation/code/modules/art_sci_overrides/artifact_effects/bomb.dm +++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/bomb.dm @@ -97,7 +97,7 @@ /datum/artifact_effect/bomb/explosive/payload() if(!..()) return FALSE - explosion(our_artifact.holder, devast,heavy,light,light*1.5) + explosion(our_artifact.holder.loc, devast,heavy,light,light*1.5) on_destroy(src) /// DEVESTATING BOMB diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/false_rod.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/false_rod.dm index 27030a251101..26c4c0501e65 100644 --- a/monkestation/code/modules/art_sci_overrides/artifact_effects/false_rod.dm +++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/false_rod.dm @@ -44,7 +44,7 @@ /datum/status_effect/forced_oath id = "Forced Oath" status_type = STATUS_EFFECT_UNIQUE - duration = -1 + duration = STATUS_EFFECT_PERMANENT tick_interval = 25 alert_type = null var/datum/component/aura_healing/our_aura diff --git a/monkestation/code/modules/asset_cache/assets/job_icons.dm b/monkestation/code/modules/asset_cache/assets/job_icons.dm new file mode 100644 index 000000000000..a4d794f95ab5 --- /dev/null +++ b/monkestation/code/modules/asset_cache/assets/job_icons.dm @@ -0,0 +1,21 @@ +/datum/asset/spritesheet/job_icons + name = "job_icons" + /// The width and height to scale the job icons to. + var/icon_size = 24 + +/datum/asset/spritesheet/job_icons/create_spritesheets() + var/list/id_list = list() + for(var/datum/job/job as anything in SSjob.all_occupations) + var/id = sanitize_css_class_name(lowertext(job.config_tag || job.title)) + if(id_list[id]) + continue + var/icon/job_icon = get_job_hud_icon(job) + if(!job_icon) + continue + var/icon/resized_icon = resize_icon(job_icon, icon_size, icon_size) + if(!resized_icon) + stack_trace("Failed to upscale icon for [job.type], upscaling using BYOND!") + job_icon.Scale(icon_size, icon_size) + resized_icon = job_icon + Insert(id, resized_icon) + id_list[id] = TRUE diff --git a/monkestation/code/modules/blood_datum/debilitated.dm b/monkestation/code/modules/blood_datum/debilitated.dm index e21663badbb5..1549d7e0f91f 100644 --- a/monkestation/code/modules/blood_datum/debilitated.dm +++ b/monkestation/code/modules/blood_datum/debilitated.dm @@ -5,7 +5,7 @@ /datum/status_effect/stacking/debilitated id = "debilitated" stacks = 0 - max_stacks = 10 + max_stacks = 20 tick_interval = 5 SECONDS delay_before_decay = 30 SECONDS consumed_on_threshold = FALSE @@ -15,7 +15,7 @@ ///our base stamina loss multiplier var/loss_multiplier = 1 ///our per stack increase to stamina loss - var/per_stack_multiplier_increase = 0.1 + var/per_stack_multiplier_increase = 0.05 /datum/status_effect/stacking/debilitated/on_apply() . = ..() diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_frenzy.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_frenzy.dm index 0902dafbbf26..325ec0297d74 100644 --- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_frenzy.dm +++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_frenzy.dm @@ -31,7 +31,7 @@ /datum/status_effect/frenzy id = "Frenzy" status_type = STATUS_EFFECT_UNIQUE - duration = -1 + duration = STATUS_EFFECT_PERMANENT tick_interval = 1 SECONDS alert_type = /atom/movable/screen/alert/status_effect/frenzy /// The stored Bloodsucker antag datum diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_sol.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_sol.dm index e953d8822cde..51feae965738 100644 --- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_sol.dm +++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_sol.dm @@ -153,7 +153,7 @@ /datum/status_effect/bloodsucker_sol id = "bloodsucker_sol" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK alert_type = /atom/movable/screen/alert/status_effect/bloodsucker_sol var/list/datum/action/cooldown/bloodsucker/burdened_actions var/static/list/sol_traits = list( diff --git a/monkestation/code/modules/bloodsuckers/powers/masquerade.dm b/monkestation/code/modules/bloodsuckers/powers/masquerade.dm index 0d3737d97c40..840fcb35487e 100644 --- a/monkestation/code/modules/bloodsuckers/powers/masquerade.dm +++ b/monkestation/code/modules/bloodsuckers/powers/masquerade.dm @@ -79,8 +79,8 @@ /datum/status_effect/masquerade id = "masquerade" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK alert_type = /atom/movable/screen/alert/status_effect/masquerade /atom/movable/screen/alert/status_effect/masquerade diff --git a/monkestation/code/modules/bloodsuckers/vassals/vassal_pinpointer.dm b/monkestation/code/modules/bloodsuckers/vassals/vassal_pinpointer.dm index 791b9a8f9f5f..4d0c6b00f213 100644 --- a/monkestation/code/modules/bloodsuckers/vassals/vassal_pinpointer.dm +++ b/monkestation/code/modules/bloodsuckers/vassals/vassal_pinpointer.dm @@ -14,7 +14,7 @@ alert_type = /atom/movable/screen/alert/status_effect/agent_pinpointer/vassal_edition minimum_range = VASSAL_SCAN_MIN_DISTANCE tick_interval = VASSAL_SCAN_PING_TIME - duration = -1 + duration = STATUS_EFFECT_PERMANENT range_fuzz_factor = 0 /datum/status_effect/agent_pinpointer/vassal_edition/on_creation(mob/living/new_owner, ...) diff --git a/monkestation/code/modules/blueshield/clothing.dm b/monkestation/code/modules/blueshield/clothing.dm index 2a72e2317fda..25fcea522e56 100644 --- a/monkestation/code/modules/blueshield/clothing.dm +++ b/monkestation/code/modules/blueshield/clothing.dm @@ -204,7 +204,7 @@ /obj/item/radio/headset/headset_bs name = "\proper the blueshield's headset" desc = "The headset of the guy who keeps the administration alive." - icon = 'monkestation/code/modules/blueshield/icons/radio.dmi' + icon = 'monkestation/icons/obj/radio.dmi' worn_icon = 'monkestation/code/modules/blueshift/icons/mob/clothing/ears.dmi' icon_state = "bshield_headset" worn_icon_state = "bshield_headset" diff --git a/monkestation/code/modules/blueshield/devices/sensor.dm b/monkestation/code/modules/blueshield/devices/sensor.dm index 6f8a5aca717b..7dabd73dd255 100644 --- a/monkestation/code/modules/blueshield/devices/sensor.dm +++ b/monkestation/code/modules/blueshield/devices/sensor.dm @@ -1,7 +1,7 @@ /obj/item/sensor_device/blueshield name = "blueshield's handheld monitor" desc = "A unique model of handheld crew monitor that seems to have been customized for Executive Protection purposes." - icon = 'monkestation/code/modules/blueshield/icons/device.dmi' + icon = 'monkestation/icons/obj/device.dmi' icon_state = "blueshield_scanner" /obj/item/sensor_device/blueshield/attack_self(mob/user) diff --git a/monkestation/code/modules/blueshield/gun.dm b/monkestation/code/modules/blueshield/gun.dm index d4afe1d8af2d..9e3103913a26 100644 --- a/monkestation/code/modules/blueshield/gun.dm +++ b/monkestation/code/modules/blueshield/gun.dm @@ -104,7 +104,7 @@ burst_size = 4 fire_delay = 1 - icon = 'monkestation/code/modules/blueshield/icons/gun.dmi' + icon = 'monkestation/icons/obj/weapons/guns/tech9.dmi' icon_state = "tech9" fire_sound = 'monkestation/code/modules/blueshift/sounds/pistol_light.ogg' diff --git a/monkestation/code/modules/blueshield/icons/device.dmi b/monkestation/code/modules/blueshield/icons/device.dmi deleted file mode 100644 index 449e43052746..000000000000 Binary files a/monkestation/code/modules/blueshield/icons/device.dmi and /dev/null differ diff --git a/monkestation/code/modules/blueshield/icons/praetorian.dmi b/monkestation/code/modules/blueshield/icons/praetorian.dmi deleted file mode 100644 index 1632262e8b8a..000000000000 Binary files a/monkestation/code/modules/blueshield/icons/praetorian.dmi and /dev/null differ diff --git a/monkestation/code/modules/blueshield/icons/worn_praetorian.dmi b/monkestation/code/modules/blueshield/icons/worn_praetorian.dmi deleted file mode 100644 index 373986abe4de..000000000000 Binary files a/monkestation/code/modules/blueshield/icons/worn_praetorian.dmi and /dev/null differ diff --git a/monkestation/code/modules/blueshield/modsuit/suit.dm b/monkestation/code/modules/blueshield/modsuit/suit.dm index 7314f5f60f6a..7328c7f19ec5 100644 --- a/monkestation/code/modules/blueshield/modsuit/suit.dm +++ b/monkestation/code/modules/blueshield/modsuit/suit.dm @@ -1,6 +1,6 @@ /obj/item/mod/control/pre_equipped/blueshield - worn_icon = 'monkestation/code/modules/blueshield/icons/worn_praetorian.dmi' - icon = 'monkestation/code/modules/blueshield/icons/praetorian.dmi' + worn_icon = 'monkestation/icons/mob/clothing/worn_modsuit.dmi' + icon = 'monkestation/icons/obj/clothing/modsuits/modsuit.dmi' icon_state = "praetorian-control" theme = /datum/mod_theme/blueshield applied_cell = /obj/item/stock_parts/cell/high diff --git a/monkestation/code/modules/blueshield/modsuit/theme.dm b/monkestation/code/modules/blueshield/modsuit/theme.dm index ca1bd0838c52..3cf453b73e6e 100644 --- a/monkestation/code/modules/blueshield/modsuit/theme.dm +++ b/monkestation/code/modules/blueshield/modsuit/theme.dm @@ -22,8 +22,8 @@ ) skins = list( "praetorian" = list( - MOD_ICON_OVERRIDE = 'monkestation/code/modules/blueshield/icons/praetorian.dmi', - MOD_WORN_ICON_OVERRIDE = 'monkestation/code/modules/blueshield/icons/worn_praetorian.dmi', + MOD_ICON_OVERRIDE = 'monkestation/icons/obj/clothing/modsuits/modsuit.dmi', + MOD_WORN_ICON_OVERRIDE = 'monkestation/icons/mob/clothing/worn_modsuit.dmi', HELMET_FLAGS = list( UNSEALED_LAYER = null, UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, diff --git a/monkestation/code/modules/blueshield/radio.dm b/monkestation/code/modules/blueshield/radio.dm deleted file mode 100644 index deddf9bc4eb8..000000000000 --- a/monkestation/code/modules/blueshield/radio.dm +++ /dev/null @@ -1,6 +0,0 @@ -/obj/item/encryptionkey/heads/blueshield - name = "\proper the blueshield's encryption key" - icon_state = "cypherkey_centcom" - channels = list(RADIO_CHANNEL_COMMAND = 1, RADIO_CHANNEL_SECURITY = 1) - greyscale_config = /datum/greyscale_config/encryptionkey_centcom - greyscale_colors = "#1d2657#dca01b" diff --git a/monkestation/code/modules/blueshift/components/armament.dm b/monkestation/code/modules/blueshift/components/armament.dm index c7a8ea7f90f0..52e858ee876f 100644 --- a/monkestation/code/modules/blueshift/components/armament.dm +++ b/monkestation/code/modules/blueshift/components/armament.dm @@ -392,7 +392,7 @@ "ref" = REF(armament_entry), "icon" = armament_entry.cached_base64, "name" = armament_entry.name, - "cost" = armament_entry.cost, + "cost" = cost_calculate(armament_entry.cost), "buyable_ammo" = armament_entry.magazine ? TRUE : FALSE, "magazine_cost" = armament_entry.magazine_cost, "purchased" = purchased_items[armament_entry] ? purchased_items[armament_entry] : 0, @@ -492,7 +492,8 @@ if(!ishuman(user) && !issilicon(user)) return - if(!buyer.has_money(armament_entry.cost)) + var/actual_cost = cost_calculate(armament_entry.cost) + if(!buyer.has_money(actual_cost)) to_chat(user, span_warning("Not enough money!")) return @@ -524,7 +525,7 @@ var/datum/supply_pack/armament/created_pack = new created_pack.name = initial(armament_entry.item_type.name) - created_pack.cost = cost_calculate(armament_entry.cost) //Paid for seperately + created_pack.cost = actual_cost //Paid for seperately created_pack.contains = list(armament_entry.item_type) var/rank diff --git a/monkestation/code/modules/blueshift/items/ammo.dm b/monkestation/code/modules/blueshift/items/ammo.dm index 0d1441a8fb7c..895ef1b5d3cd 100644 --- a/monkestation/code/modules/blueshift/items/ammo.dm +++ b/monkestation/code/modules/blueshift/items/ammo.dm @@ -865,6 +865,18 @@ // We do 80 total damage to anything robotic, namely borgs, and robotic simplemobs AddElement(/datum/element/bane, target_type = /mob/living, mob_biotypes = MOB_ROBOTIC, damage_multiplier = 0, added_damage = anti_materiel_damage_addition) +/obj/projectile/bullet/p60strela/pierce/on_hit(atom/target, blocked = 0, pierce_hit) /// If anyone is deranged enough to use it on soft targets, you may as well let them have fun + if(isliving(target)) + // If the bullet has already gone through 3 people, stop it on this hit + if(pierces > 3) + projectile_piercing = NONE + + if(damage > 10) // Lets just be safe with this one + damage -= 10 + armour_penetration -= 10 + + return ..() + /obj/item/ammo_box/magazine/m10mm/rifle name = "rifle magazine (10mm)" desc = "A well-worn magazine fitted for the surplus rifle." diff --git a/monkestation/code/modules/blueshift/items/badges.dm b/monkestation/code/modules/blueshift/items/badges.dm index 940b1bb8a070..2e7b1b53dd03 100644 --- a/monkestation/code/modules/blueshift/items/badges.dm +++ b/monkestation/code/modules/blueshift/items/badges.dm @@ -186,7 +186,7 @@ GLOBAL_LIST_INIT(pride_pin_reskins, list( "Transgender Pride" = "pride_trans", "Intersex Pride" = "pride_intersex", "Lesbian Pride" = "pride_lesbian", - "Man-Loving-Man / Gay Pride" = "pride_mlm", + "Gay Pride" = "pride_mlm", "Genderfluid Pride" = "pride_genderfluid", "Genderqueer Pride" = "pride_genderqueer", "Aromantic Pride" = "pride_aromantic", diff --git a/monkestation/code/modules/blueshift/items/company_guns.dm b/monkestation/code/modules/blueshift/items/company_guns.dm index f99c598cfaaf..733fde05e763 100644 --- a/monkestation/code/modules/blueshift/items/company_guns.dm +++ b/monkestation/code/modules/blueshift/items/company_guns.dm @@ -963,6 +963,8 @@ icon_state = "bobr" fire_sound = 'monkestation/code/modules/blueshift/sounds/revolver_fire.ogg' spread = SAWN_OFF_ACC_PENALTY + projectile_damage_multiplier = 0.75 /// No way in hell a handgun with a 3 inch barrel should fire the same cartridge with the same force as a full-length barrel + projectile_wound_bonus = -5 /// In addition, this should help with the balance issues around the Bobr, it being a concealable shotgun with near-instant reload /obj/item/gun/ballistic/revolver/shotgun_revolver/give_manufacturer_examine() AddElement(/datum/element/manufacturer_examine, COMPANY_SZOT) diff --git a/monkestation/code/modules/blueshift/species/ashwalker.dm b/monkestation/code/modules/blueshift/species/ashwalker.dm index 773eb4b40f2c..4c7a8ec617b5 100644 --- a/monkestation/code/modules/blueshift/species/ashwalker.dm +++ b/monkestation/code/modules/blueshift/species/ashwalker.dm @@ -117,8 +117,8 @@ /datum/status_effect/ashwalker_damage //tracks the damage dealt to this mob by ashwalkers id = "ashwalker_damage" - duration = -1 - tick_interval = -1 + duration = STATUS_EFFECT_PERMANENT + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = null /// How much damage has been dealt to the mob diff --git a/monkestation/code/modules/can_spessmen_feel_pain/pain/status_effects/low_blood_pressure.dm b/monkestation/code/modules/can_spessmen_feel_pain/pain/status_effects/low_blood_pressure.dm index 30b1d0f40592..a42c1259fad9 100644 --- a/monkestation/code/modules/can_spessmen_feel_pain/pain/status_effects/low_blood_pressure.dm +++ b/monkestation/code/modules/can_spessmen_feel_pain/pain/status_effects/low_blood_pressure.dm @@ -1,6 +1,6 @@ /datum/status_effect/low_blood_pressure id = "low_blood_pressure" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/low_blood_pressure diff --git a/monkestation/code/modules/cargo/crates/security.dm b/monkestation/code/modules/cargo/crates/security.dm index 3aa70b886cea..c29129636152 100644 --- a/monkestation/code/modules/cargo/crates/security.dm +++ b/monkestation/code/modules/cargo/crates/security.dm @@ -58,3 +58,10 @@ contraband = TRUE contains = list(/obj/item/cortical_cage) crate_name = "anti-borer crate" + +/datum/supply_pack/security/armory/taser + name = "Taser Crate" + desc = "Contains two tasers, ready to tase criminals." + cost = CARGO_CRATE_VALUE * 15 + contains = list(/obj/item/gun/energy/taser = 2) + crate_name = "taser crate" diff --git a/monkestation/code/modules/cassettes/cassette_approval.dm b/monkestation/code/modules/cassettes/cassette_approval.dm index dce781859162..69f7306c8574 100644 --- a/monkestation/code/modules/cassettes/cassette_approval.dm +++ b/monkestation/code/modules/cassettes/cassette_approval.dm @@ -73,26 +73,21 @@ GLOBAL_LIST_INIT(cassette_reviews, list()) var/obj/item/device/cassette_tape/submitted_tape var/action_taken = FALSE + var/verdict = "NONE" /datum/cassette_review/Destroy(force) . = ..() - QDEL_LIST(cassette_data) + if(cassette_data) + QDEL_LIST(cassette_data) submitter = null - - GLOB.cassette_reviews["[id]"] -= src - GLOB.cassette_reviews -= id + if(id && (id in GLOB.cassette_reviews)) + GLOB.cassette_reviews -= id // Remove the key /datum/cassette_review/ui_state(mob/user) return GLOB.always_state /datum/cassette_review/ui_interact(mob/user, datum/tgui/ui) . = ..() - if(action_taken) - var/choice = tgui_alert(user, "This tape has already been actioned by another admin do you wish to look it over?", "Cassette Review", list("Yes", "No")) - if(!choice) - return - if(choice == "No") - return ui = SStgui.try_update_ui(user, src, ui) if(!ui) @@ -107,6 +102,8 @@ GLOBAL_LIST_INIT(cassette_reviews, list()) data["submitters_name"] = submitter.real_name data["side1"] = cassette_data["side1"] data["side2"] = cassette_data["side2"] + data["reviewed"] = action_taken + data["verdict"] = verdict return data @@ -121,6 +118,11 @@ GLOBAL_LIST_INIT(cassette_reviews, list()) to_chat(submitter, span_warning("You feel a wave of disapointment wash over you, you can tell that your cassette was denied by the Space Board of Music")) logger.Log(LOG_CATEGORY_MUSIC, "[submitter]'s tape has been rejected by [usr]", list("approver" = usr.name, "submitter" = submitter.name)) action_taken = TRUE + verdict = "DENIED" + +/datum/cassette_review/ui_close()// Don't leave orphaned datums laying around. Hopefully this handles timeouts? + . = ..() + qdel(src) /datum/cassette_review/proc/approve_review(mob/user) if(!check_rights_for(user.client, R_FUN)) @@ -131,8 +133,73 @@ GLOBAL_LIST_INIT(cassette_reviews, list()) message_admins("[submitter]'s tape has been approved by [user]") logger.Log(LOG_CATEGORY_MUSIC, "[submitter]'s tape has been approved by [user]", list("approver" = user.name, "submitter" = submitter.name)) action_taken = TRUE + verdict = "APPROVED" /proc/fetch_review(id) return GLOB.cassette_reviews[id] #undef ADMIN_OPEN_REVIEW + +// Handles UI to manage cassettes. +/client/proc/review_cassettes() //Creates a verb for admins to open up the ui + set name = "Review Cassettes" + set desc = "Review this rounds cassettes." + set category = "Admin.Game" + if(!check_rights(R_FUN)) + return + new /datum/review_cassettes(usr) + +/datum/review_cassettes + var/client/holder //client of whoever is using this datum + var/is_funmin = FALSE + +/datum/review_cassettes/New(user)//user can either be a client or a mob due to byondcode(tm) + holder = get_player_client(user) + is_funmin = check_rights(R_FUN) + ui_interact(holder.mob)//datum has a tgui component, here we open the window + +/datum/review_cassettes/ui_status(mob/user, datum/ui_state/state) + return (user.client == holder && is_funmin) ? UI_INTERACTIVE : UI_CLOSE + + +/datum/review_cassettes/ui_close()// Don't leave orphaned datums laying around. Hopefully this handles timeouts? + qdel(src) + +/datum/review_cassettes/ui_interact(mob/user, datum/tgui/ui) // Open UI and update as it remains open. + . = ..() + + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "CassetteManager") + ui.open() + +/datum/review_cassettes/ui_data(mob/user) + . = ..() + var/list/data = list("cassettes" = list()) // Initialize main data structure with an empty "cassettes" list + + for(var/cassette_id in GLOB.cassette_reviews) + var/datum/cassette_review/cassette = GLOB.cassette_reviews[cassette_id] + var/submitters_name = cassette.submitter + var/obj/item/tape_obj = cassette.submitted_tape + var/reviewed = cassette.action_taken + var/verdict = cassette.verdict + + // Add this cassette's data under its cassette_id + data["cassettes"][cassette_id] = list( + "submitter_name" = submitters_name, + "tape_name" = tape_obj.name, + "reviewed" = reviewed, + "verdict" = verdict, + ) + return data + +/datum/review_cassettes/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + if(!length(GLOB.cassette_reviews) || !GLOB.cassette_reviews[action]) + return + + var/datum/cassette_review/cassette = GLOB.cassette_reviews[action] + cassette.ui_interact(ui.user) + diff --git a/monkestation/code/modules/client/preferences.dm b/monkestation/code/modules/client/preferences.dm index c8c7b67c98c9..773a4b31cc59 100644 --- a/monkestation/code/modules/client/preferences.dm +++ b/monkestation/code/modules/client/preferences.dm @@ -28,6 +28,7 @@ "low_threat" = 0, "event_tokens" = 0, "event_token_month" = 0, + "donator" = 0, ) ///amount of metaconis you can earn per shift diff --git a/monkestation/code/modules/client/preferences/overflow.dm b/monkestation/code/modules/client/preferences/overflow.dm new file mode 100644 index 000000000000..742f45c6133d --- /dev/null +++ b/monkestation/code/modules/client/preferences/overflow.dm @@ -0,0 +1,5 @@ +/datum/preference/toggle/verify_overflow + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + default_value = FALSE + savefile_key = "verify_overflow" + savefile_identifier = PREFERENCE_PLAYER diff --git a/monkestation/code/modules/clothing/gloves/power_gloves.dm b/monkestation/code/modules/clothing/gloves/power_gloves.dm index 5caefddf16e9..0528ec7e1cc1 100644 --- a/monkestation/code/modules/clothing/gloves/power_gloves.dm +++ b/monkestation/code/modules/clothing/gloves/power_gloves.dm @@ -47,10 +47,10 @@ if(power >= dust_power) //Dusts if there's enough in the grid electrocute_victim.dust(TRUE, FALSE, TRUE) - log_combat(owner, target, "zapped", /obj/item/clothing/gloves/color/yellow/power_gloves, "[power] watts were used resulting in [shock_damage] damage.") + log_combat(owner, target, "zapped", /obj/item/clothing/gloves/color/yellow/power_gloves, "[power] watts were used resulting in the target dusting.") else electrocute_victim.electrocute_act(shock_damage, source, 1, SHOCK_TESLA | ((zap_flags & ZAP_MOB_STUN) ? NONE : SHOCK_NOSTUN)) - log_combat(owner, target, "zapped", /obj/item/clothing/gloves/color/yellow/power_gloves, "[power] watts were used resulting in the target dusting.") + log_combat(owner, target, "zapped", /obj/item/clothing/gloves/color/yellow/power_gloves, "[power] watts were used resulting in [shock_damage] damage.") return diff --git a/monkestation/code/modules/datums/status_effects/debuffs.dm b/monkestation/code/modules/datums/status_effects/debuffs.dm index 60b8f0587b4e..094d18bd5142 100644 --- a/monkestation/code/modules/datums/status_effects/debuffs.dm +++ b/monkestation/code/modules/datums/status_effects/debuffs.dm @@ -3,6 +3,9 @@ desc = "You've been hit with an EMP! You're malfunctioning!" icon_state = "hypnosis" +/datum/status_effect/ipc + id = STATUS_EFFECT_ID_ABSTRACT + /datum/status_effect/ipc/emp id = "ipc_emp" duration = 120 SECONDS diff --git a/monkestation/code/modules/ghost_players/centcom_grace.dm b/monkestation/code/modules/ghost_players/centcom_grace.dm index d97f50637089..ade5feb4e73a 100644 --- a/monkestation/code/modules/ghost_players/centcom_grace.dm +++ b/monkestation/code/modules/ghost_players/centcom_grace.dm @@ -1,6 +1,6 @@ /datum/status_effect/centcom_grace id = "centcom_grace" - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK alert_type = null var/last_active = FALSE diff --git a/monkestation/code/modules/jobs/departments/departments.dm b/monkestation/code/modules/jobs/departments/departments.dm new file mode 100644 index 000000000000..447e28c76174 --- /dev/null +++ b/monkestation/code/modules/jobs/departments/departments.dm @@ -0,0 +1,15 @@ +/datum/job_department/central_command + department_name = DEPARTMENT_CENTRAL_COMMAND + department_bitflags = DEPARTMENT_BITFLAG_CENTRAL_COMMAND + department_head = /datum/job/captain + department_experience_type = EXP_TYPE_CENTRAL_COMMAND + display_order = 1 + label_class = "command" + ui_color = "#86ff82" + +/datum/job_department/late + department_name = DEPARTMENT_LATE + department_bitflags = DEPARTMENT_BITFLAG_LATE + display_order = 10 + label_class = "latejoin" + ui_color = "#0b6b2b" diff --git a/monkestation/code/modules/blueshield/job.dm b/monkestation/code/modules/jobs/job_types/blueshield.dm similarity index 90% rename from monkestation/code/modules/blueshield/job.dm rename to monkestation/code/modules/jobs/job_types/blueshield.dm index 7bd45ba2b900..236225dba23a 100644 --- a/monkestation/code/modules/blueshield/job.dm +++ b/monkestation/code/modules/jobs/job_types/blueshield.dm @@ -77,12 +77,3 @@ head = /obj/item/clothing/head/helmet/space/plasmaman/blueshield uniform = /obj/item/clothing/under/plasmaman/blueshield - -/datum/job_department/central_command - department_name = DEPARTMENT_CENTRAL_COMMAND - department_bitflags = DEPARTMENT_BITFLAG_CENTRAL_COMMAND - department_head = /datum/job/captain - department_experience_type = EXP_TYPE_CENTRAL_COMMAND - display_order = 1 - label_class = "command" - ui_color = "#86ff82" diff --git a/monkestation/code/modules/jobs/job_types/departments/departments.dm b/monkestation/code/modules/jobs/job_types/departments/departments.dm deleted file mode 100644 index 6b2e4b6a896f..000000000000 --- a/monkestation/code/modules/jobs/job_types/departments/departments.dm +++ /dev/null @@ -1,6 +0,0 @@ -/datum/job_department/late - department_name = DEPARTMENT_LATE - department_bitflags = DEPARTMENT_BITFLAG_LATE - display_order = 10 - label_class = "latejoin" - ui_color = "#0b6b2b" diff --git a/monkestation/code/modules/jobs/job_types/explorer.dm b/monkestation/code/modules/jobs/job_types/explorer.dm index 5bd358a57a92..97787c038ea3 100644 --- a/monkestation/code/modules/jobs/job_types/explorer.dm +++ b/monkestation/code/modules/jobs/job_types/explorer.dm @@ -4,8 +4,8 @@ Visit strange places. Die in space." department_head = list(JOB_HEAD_OF_PERSONNEL) faction = FACTION_STATION - total_positions = 3 - spawn_positions = 3 + total_positions = 6 + spawn_positions = 6 supervisors = SUPERVISOR_QM exp_granted_type = EXP_TYPE_CREW config_tag = "EXPLORER" diff --git a/monkestation/code/modules/liquids/liquid_status_effect.dm b/monkestation/code/modules/liquids/liquid_status_effect.dm index 514667fc0272..9b31a66d3da3 100644 --- a/monkestation/code/modules/liquids/liquid_status_effect.dm +++ b/monkestation/code/modules/liquids/liquid_status_effect.dm @@ -1,7 +1,7 @@ /datum/status_effect/water_affected id = "wateraffected" alert_type = null - duration = -1 + duration = STATUS_EFFECT_PERMANENT /datum/status_effect/water_affected/on_apply() //We should be inside a liquid turf if this is applied @@ -36,8 +36,9 @@ blacklisted_movetypes = FLOATING | FLYING /datum/status_effect/ocean_affected + id = "ocean_affected" + duration = STATUS_EFFECT_PERMANENT alert_type = null - duration = -1 /datum/status_effect/ocean_affected/tick() var/turf/ocean_turf = get_turf(owner) diff --git a/monkestation/code/modules/mapfluff/ruins/spaceruin_code/oldstation.dm b/monkestation/code/modules/mapfluff/ruins/spaceruin_code/oldstation.dm index 1083c4474101..1f723d55e9f4 100644 --- a/monkestation/code/modules/mapfluff/ruins/spaceruin_code/oldstation.dm +++ b/monkestation/code/modules/mapfluff/ruins/spaceruin_code/oldstation.dm @@ -2,3 +2,10 @@ name = "Reminder - air distrubtion." default_raw_text = "Reminder!

Blue on layer 4 is for air.
Red on layer 2 is for the scrubbers.

\ The dispenser prints our pipes with this in mind so they dont get accidentally connected. Doesn't matter if 'purple is a pretty colour', it won't connect." + +/obj/machinery/power/supermatter_crystal/shard/oldstation + name = "Worn Supermatter Shard" + desc = "This shard has lost some of its lustrous shine." + radio_key = /obj/item/encryptionkey/headset_uncommon + emergency_channel = "Uncommon" + warning_channel = "Uncommon" diff --git a/monkestation/code/modules/mentor/mentor_follow.dm b/monkestation/code/modules/mentor/mentor_follow.dm index 09f8e7a058de..7a8e6ed8961c 100644 --- a/monkestation/code/modules/mentor/mentor_follow.dm +++ b/monkestation/code/modules/mentor/mentor_follow.dm @@ -8,8 +8,8 @@ mentor_datum.following = followed_guy usr.reset_perspective(followed_guy) add_verb(src, /client/proc/mentor_unfollow) - to_chat(GLOB.admins, span_adminooc("MENTOR: [key_name(usr)] is now following [key_name(followed_guy)]")) - to_chat(usr, span_info("Click the \"Stop Following\" button in the Mentor tab to stop following [key_name(followed_guy)].")) + to_chat(GLOB.admins, span_adminooc("MENTOR: [key_name(usr)] is now following [key_name(followed_guy)]"), type = MESSAGE_TYPE_ADMINLOG, confidential = TRUE) + to_chat(src, span_info("Click the \"Stop Following\" button in the Mentor tab to stop following [key_name(followed_guy)]."), type = MESSAGE_TYPE_ADMINLOG, confidential = TRUE) log_mentor("[key_name(usr)] began following [key_name(followed_guy)]") /client/proc/mentor_unfollow() @@ -19,6 +19,6 @@ remove_verb(src, /client/proc/mentor_unfollow) usr.reset_perspective() - to_chat(GLOB.admins, span_adminooc("MENTOR: [key_name(usr)] is no longer following [key_name(mentor_datum.following)]")) + to_chat(GLOB.admins, span_adminooc("MENTOR: [key_name(usr)] is no longer following [key_name(mentor_datum.following)]"), type = MESSAGE_TYPE_ADMINLOG, confidential = TRUE) log_mentor("[key_name(usr)] stopped following [key_name(mentor_datum.following)]") mentor_datum.following = null diff --git a/monkestation/code/modules/smithing/ipcs/species.dm b/monkestation/code/modules/mob/living/carbon/human/species_type/ipc.dm similarity index 100% rename from monkestation/code/modules/smithing/ipcs/species.dm rename to monkestation/code/modules/mob/living/carbon/human/species_type/ipc.dm diff --git a/monkestation/code/modules/smithing/oozelings/species.dm b/monkestation/code/modules/mob/living/carbon/human/species_type/oozeling.dm similarity index 100% rename from monkestation/code/modules/smithing/oozelings/species.dm rename to monkestation/code/modules/mob/living/carbon/human/species_type/oozeling.dm diff --git a/monkestation/code/modules/ranching/satyr/species.dm b/monkestation/code/modules/mob/living/carbon/human/species_type/satyr.dm similarity index 100% rename from monkestation/code/modules/ranching/satyr/species.dm rename to monkestation/code/modules/mob/living/carbon/human/species_type/satyr.dm diff --git a/monkestation/code/modules/projectiles/guns/energy/stun.dm b/monkestation/code/modules/projectiles/guns/energy/stun.dm new file mode 100644 index 000000000000..5fad2492f767 --- /dev/null +++ b/monkestation/code/modules/projectiles/guns/energy/stun.dm @@ -0,0 +1,2 @@ +/obj/item/gun/energy/disabler/cargo + pin = /obj/item/firing_pin/cargo/unremovable diff --git a/monkestation/code/modules/projectiles/pins.dm b/monkestation/code/modules/projectiles/pins.dm new file mode 100644 index 000000000000..e298be6e115d --- /dev/null +++ b/monkestation/code/modules/projectiles/pins.dm @@ -0,0 +1,21 @@ +/obj/item/firing_pin/cargo //Firing pin for use in cargo only + name = "cargo-locked firing pin" + desc = "A firing pin that scans the area to check if it is within the station's cargo bay or warehouse before firing." + fail_message = "Area check failed" + var/list/station_cargo = list( + /area/station/cargo/warehouse, + /area/station/cargo/storage, + /area/station/cargo/office, + /area/station/cargo/sorting, + ) + +//Checks to see if the user in cargo or it's warehouse +/obj/item/firing_pin/cargo/pin_auth(mob/living/user) + if(!istype(user)) + return FALSE + if (is_type_in_list(get_area(user), station_cargo)) + return TRUE + return FALSE + +/obj/item/firing_pin/cargo/unremovable + pin_removable = FALSE diff --git a/monkestation/code/modules/ranching/chickens/tier1/chicken.dm b/monkestation/code/modules/ranching/chickens/tier1/chicken.dm index 7ecf78db3790..6da6940f9af4 100644 --- a/monkestation/code/modules/ranching/chickens/tier1/chicken.dm +++ b/monkestation/code/modules/ranching/chickens/tier1/chicken.dm @@ -4,4 +4,6 @@ instability = 25 // 25% more likely to mutate than other chickens /datum/status_effect/ranching + id = STATUS_EFFECT_ID_ABSTRACT + alert_type = null show_duration = TRUE diff --git a/monkestation/code/modules/research/techweb/all_nodes.dm b/monkestation/code/modules/research/techweb/all_nodes.dm index 185a205b6e02..f01f962f74e6 100644 --- a/monkestation/code/modules/research/techweb/all_nodes.dm +++ b/monkestation/code/modules/research/techweb/all_nodes.dm @@ -350,3 +350,22 @@ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 500) hidden = TRUE +/datum/techweb_node/improved_robotic_tend_wounds + id = "improved_robotic_surgery" + display_name = "Improved Robotic Repair Surgeries" + description = "As it turns out, you don't actually need to cut out entire support rods if it's just scratched!" + prereq_ids = list("engineering") + design_ids = list( + "surgery_heal_robot_upgrade", + ) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 900) + +/datum/techweb_node/advanced_robotic_tend_wounds + id = "advanced_robotic_surgery" + display_name = "Advanced Robotic Surgeries" + description = "Did you know Hephaestus actually has a free online tutorial for synthetic trauma repairs? It's true!" + prereq_ids = list("improved_robotic_surgery") + design_ids = list( + "surgery_heal_robot_upgrade_femto", + ) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1300) // less expensive than the organic surgery research equivalent since its JUST tend wounds diff --git a/monkestation/code/modules/slimecore/components/liquid_secretion.dm b/monkestation/code/modules/slimecore/components/liquid_secretion.dm index f1ec48bc4f08..8df882d5ab92 100644 --- a/monkestation/code/modules/slimecore/components/liquid_secretion.dm +++ b/monkestation/code/modules/slimecore/components/liquid_secretion.dm @@ -11,6 +11,8 @@ /datum/component/liquid_secretion/Initialize(reagent_id = /datum/reagent/water, amount = 10, secretion_interval = 1 SECONDS, pre_secrete_callback) . = ..() + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE src.reagent_id = reagent_id src.secretion_interval = secretion_interval @@ -38,15 +40,18 @@ if(secretion_interval) src.secretion_interval = secretion_interval - /datum/component/liquid_secretion/process(seconds_per_tick) - if(QDELETED(parent) || !COOLDOWN_FINISHED(src, next_secrete)) + var/atom/movable/parent = src.parent + if(QDELETED(parent)) + return PROCESS_KILL + if(!COOLDOWN_FINISHED(src, next_secrete)) + return + var/turf/open/parent_turf = parent.loc + if(!isopenturf(parent_turf)) return COOLDOWN_START(src, next_secrete, secretion_interval) if(pre_secrete_callback && !pre_secrete_callback.Invoke(parent)) return - - var/turf/parent_turf = get_turf(parent) var/list/reagent_list = list() reagent_list[reagent_id] = amount parent_turf?.add_liquid_list(reagent_list, FALSE, T20C) diff --git a/monkestation/code/modules/slimecore/crossbreeding/regenerative/cooldown.dm b/monkestation/code/modules/slimecore/crossbreeding/regenerative/cooldown.dm index 0614d914d116..431e99388fb5 100644 --- a/monkestation/code/modules/slimecore/crossbreeding/regenerative/cooldown.dm +++ b/monkestation/code/modules/slimecore/crossbreeding/regenerative/cooldown.dm @@ -1,7 +1,7 @@ /datum/status_effect/slime_regen_cooldown id = "slime_regen_cooldown" status_type = STATUS_EFFECT_MULTIPLE - tick_interval = -1 + tick_interval = STATUS_EFFECT_NO_TICK alert_type = null remove_on_fullheal = TRUE heal_flag_necessary = HEAL_ADMIN diff --git a/monkestation/code/modules/slimecore/crossbreeding/regenerative/effect.dm b/monkestation/code/modules/slimecore/crossbreeding/regenerative/effect.dm index f1f4878fd76b..eea062382329 100644 --- a/monkestation/code/modules/slimecore/crossbreeding/regenerative/effect.dm +++ b/monkestation/code/modules/slimecore/crossbreeding/regenerative/effect.dm @@ -93,7 +93,7 @@ ordered_wounds[1]?.remove_wound() /datum/status_effect/regenerative_extract/get_examine_text() - return "[owner.p_They()] have a subtle, gentle glow to [owner.p_their()] skin, with slime soothing [owner.p_their()] wounds." + return "[owner.p_They()] [owner.p_have()] a subtle, gentle glow to [owner.p_their()] skin, with slime soothing [owner.p_their()] wounds." /atom/movable/screen/alert/status_effect/regen_extract name = "Slime Regeneration" diff --git a/monkestation/code/modules/slimecore/machines/ooze_sucker.dm b/monkestation/code/modules/slimecore/machines/ooze_sucker.dm index 204b2e61d097..9c9f5bb034ab 100644 --- a/monkestation/code/modules/slimecore/machines/ooze_sucker.dm +++ b/monkestation/code/modules/slimecore/machines/ooze_sucker.dm @@ -261,6 +261,14 @@ GLOBAL_LIST_EMPTY_TYPED(ooze_suckers, /obj/machinery/plumbing/ooze_sucker) balloon_alert_to_viewers("[turned_on ? "enabled" : "disabled"] ooze sucker") return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN +/obj/machinery/plumbing/ooze_sucker/multitool_act(mob/living/user, obj/item/tool) + if(!multitool_check_buffer(user, tool)) + return + var/obj/item/multitool/multitool = tool + multitool.buffer = src + to_chat(user, span_notice("You save the data in the [multitool.name]'s buffer.")) + return TOOL_ACT_TOOLTYPE_SUCCESS + /obj/item/disk/sucker_upgrade name = "ooze sucker upgrade disk" desc = "An upgrade disk for an ooze sucker." diff --git a/monkestation/code/modules/slimecore/machines/slime_pen_controller.dm b/monkestation/code/modules/slimecore/machines/slime_pen_controller.dm index 27b170683443..96b8c676bac6 100644 --- a/monkestation/code/modules/slimecore/machines/slime_pen_controller.dm +++ b/monkestation/code/modules/slimecore/machines/slime_pen_controller.dm @@ -185,6 +185,12 @@ GLOBAL_LIST_EMPTY_TYPED(slime_pen_controllers, /obj/machinery/slime_pen_controll /obj/machinery/slime_pen_controller/multitool_act(mob/living/user, obj/item/multitool/multitool) if(!multitool_check_buffer(user, multitool) || QDELETED(multitool.buffer)) return + if(linked_oozesucker(multitool.buffer, linked_data)) // Linking a new ooze sucker instead of a pen. + balloon_alert_to_viewers("linked sucker") + multitool.buffer.balloon_alert_to_viewers("linked to controller") + to_chat(user, span_notice("You link the [multitool.buffer] to the [src].")) + return TRUE + var/obj/machinery/corral_corner/pad = multitool.buffer if(!istype(pad) || !pad.connected_data) return @@ -197,6 +203,23 @@ GLOBAL_LIST_EMPTY_TYPED(slime_pen_controllers, /obj/machinery/slime_pen_controll to_chat(user, span_notice("You link the [pad] to the [src].")) return TRUE +/obj/machinery/slime_pen_controller/proc/linked_oozesucker(obj/machinery/plumbing/ooze_sucker/target, datum/corral_data/linked_pen) + if(!istype(target) || !istype(linked_pen)) + return + if(get_turf(target.loc) in linked_pen.corral_turfs) + if(linked_sucker) + UnregisterSignal(linked_sucker, COMSIG_QDELETING) + linked_sucker = target + target.linked_controller = src + RegisterSignal(linked_sucker, COMSIG_QDELETING, PROC_REF(clear_sucker_data)) + return TRUE + return + /obj/machinery/slime_pen_controller/proc/clear_data() UnregisterSignal(linked_data, COMSIG_QDELETING) linked_data = null + +/obj/machinery/slime_pen_controller/proc/clear_sucker_data() + UnregisterSignal(linked_sucker, COMSIG_QDELETING) + linked_sucker.linked_controller = null + linked_sucker = null diff --git a/monkestation/code/modules/smithing/ipcs/body/internal_organs.dm b/monkestation/code/modules/smithing/ipcs/body/internal_organs.dm deleted file mode 100644 index 3a8849a9b783..000000000000 --- a/monkestation/code/modules/smithing/ipcs/body/internal_organs.dm +++ /dev/null @@ -1,411 +0,0 @@ -/obj/item/organ/internal/brain/synth - name = "compact positronic brain" - slot = ORGAN_SLOT_BRAIN - zone = BODY_ZONE_HEAD - organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES - maxHealth = 2 * STANDARD_ORGAN_THRESHOLD - desc = "A cube of shining metal, four inches to a side and covered in shallow grooves. It has an IPC serial number engraved on the top. It is usually slotted into the chest of synthetic crewmembers." - icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' - icon_state = "posibrain-ipc" - /// The last time (in ticks) a message about brain damage was sent. Don't touch. - var/last_message_time = 0 - -/obj/item/organ/internal/brain/synth/on_insert(mob/living/carbon/brain_owner) - . = ..() - - if(brain_owner.stat != DEAD || !ishuman(brain_owner)) - return - - var/mob/living/carbon/human/user_human = brain_owner - if(HAS_TRAIT(user_human, TRAIT_REVIVES_BY_HEALING) && user_human.health > SYNTH_BRAIN_WAKE_THRESHOLD) - if(!HAS_TRAIT(user_human, TRAIT_DEFIB_BLACKLISTED)) - user_human.revive(FALSE) - -/obj/item/organ/internal/brain/synth/emp_act(severity) // EMP act against the posi, keep the cap far below the organ health - . = ..() - - if(!owner || . & EMP_PROTECT_SELF) - return - - if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. - COOLDOWN_START(src, severe_cooldown, 10 SECONDS) - - switch(severity) - if(EMP_HEAVY) - to_chat(owner, span_warning("01001001 00100111 01101101 00100000 01100110 01110101 01100011 01101011 01100101 01100100 00101110")) - apply_organ_damage(SYNTH_ORGAN_HEAVY_EMP_DAMAGE, SYNTH_EMP_BRAIN_DAMAGE_MAXIMUM, required_organtype = ORGAN_ROBOTIC) - if(EMP_LIGHT) - to_chat(owner, span_warning("Alert: Electromagnetic damage taken in central processing unit. Error Code: 401-YT")) - apply_organ_damage(SYNTH_ORGAN_LIGHT_EMP_DAMAGE, SYNTH_EMP_BRAIN_DAMAGE_MAXIMUM, required_organtype = ORGAN_ROBOTIC) - -/obj/item/organ/internal/brain/synth/apply_organ_damage(damage_amount, maximumm, required_organtype) - . = ..() - - if(owner && damage > 0 && (world.time - last_message_time) > SYNTH_BRAIN_DAMAGE_MESSAGE_INTERVAL) - last_message_time = world.time - - if(damage > BRAIN_DAMAGE_SEVERE) - to_chat(owner, span_warning("Alre: re oumtnin ilir tocorr:pa ni ne:cnrrpiioruloomatt cessingode: P1_1-H")) - return - - if(damage > BRAIN_DAMAGE_MILD) - to_chat(owner, span_warning("Alert: Minor corruption in central processing unit. Error Code: 001-HP")) - -/* -/obj/item/organ/internal/brain/synth/circuit - name = "compact AI circuit" - desc = "A compact and extremely complex circuit, perfectly dimensioned to fit in the same slot as a synthetic-compatible positronic brain. It is usually slotted into the chest of synthetic crewmembers." - icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' - icon_state = "circuit-occupied" - inhand_icon_state = "electronic" - lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi' - righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi' -*/ - -/obj/item/organ/internal/brain/synth/mmi - name = "compact man-machine interface" - desc = "A compact man-machine interface, perfectly dimensioned to fit in the same slot as a synthetic-compatible positronic brain. Unfortunately, the brain seems to be permanently attached to the circuitry, and it seems relatively sensitive to it's environment. It is usually slotted into the chest of synthetic crewmembers." - icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' - icon_state = "mmi-ipc" - -/obj/item/organ/internal/ears/synth - name = "auditory sensors" - icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' - icon_state = "ears-ipc" - desc = "A pair of microphones intended to be installed in an IPC or Synthetics head, that grant the ability to hear." - zone = BODY_ZONE_HEAD - slot = ORGAN_SLOT_EARS - gender = PLURAL - maxHealth = 1 * STANDARD_ORGAN_THRESHOLD - organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES - -/obj/item/organ/internal/ears/synth/emp_act(severity) - . = ..() - - if(!owner || . & EMP_PROTECT_SELF) - return - - if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. - COOLDOWN_START(src, severe_cooldown, 10 SECONDS) - - switch(severity) - if(EMP_HEAVY) - owner.set_jitter_if_lower(SYNTH_BAD_EFFECT_DURATION * SYNTH_HEAVY_EMP_MULTIPLIER) - owner.set_dizzy_if_lower(SYNTH_BAD_EFFECT_DURATION * SYNTH_HEAVY_EMP_MULTIPLIER) - adjustEarDamage(SYNTH_ORGAN_HEAVY_EMP_DAMAGE, SYNTH_DEAF_STACKS) - to_chat(owner, span_warning("Alert: Null feedback from auditory sensors detected, seek maintenance immediately. Error Code: AS-105")) - - if(EMP_LIGHT) - owner.set_jitter_if_lower(SYNTH_BAD_EFFECT_DURATION) - owner.set_dizzy_if_lower(SYNTH_BAD_EFFECT_DURATION) - adjustEarDamage(SYNTH_ORGAN_LIGHT_EMP_DAMAGE, SYNTH_DEAF_STACKS) - to_chat(owner, span_warning("Alert: Anomalous feedback from auditory sensors detected. Error Code: AS-50")) - -/datum/design/synth_ears - name = "Auditory Sensors" - desc = "A pair of microphones intended to be installed in an IPC or Synthetics head, that grant the ability to hear." - id = "synth_ears" - build_type = PROTOLATHE | AWAY_LATHE | MECHFAB - construction_time = 4 SECONDS - materials = list( - /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, - /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, - ) - build_path = /obj/item/organ/internal/ears/synth - category = list( - RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_1 - ) - departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE - -/obj/item/organ/internal/eyes/synth - name = "optical sensors" - icon_state = "cybernetic_eyeballs" - desc = "A very basic set of optical sensors with no extra vision modes or functions." - maxHealth = 1 * STANDARD_ORGAN_THRESHOLD - flash_protect = FLASH_PROTECTION_WELDER - organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES | ORGAN_DOESNT_PROTECT_AGAINST_CONVERSION - -/obj/item/organ/internal/eyes/synth/emp_act(severity) - . = ..() - - if(!owner || . & EMP_PROTECT_SELF) - return - - switch(severity) - if(EMP_HEAVY) - to_chat(owner, span_warning("Alert:Severe electromagnetic interference clouds your optics with static. Error Code: I-CS6")) - apply_organ_damage(SYNTH_ORGAN_HEAVY_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) - if(EMP_LIGHT) - to_chat(owner, span_warning("Alert: Mild interference clouds your optics with static. Error Code: I-CS0")) - apply_organ_damage(SYNTH_ORGAN_LIGHT_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) - -/datum/design/synth_eyes - name = "Optical Sensors" - desc = "A very basic set of optical sensors with no extra vision modes or functions." - id = "synth_eyes" - build_type = PROTOLATHE | AWAY_LATHE | MECHFAB - construction_time = 4 SECONDS - materials = list( - /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, - /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, - ) - build_path = /obj/item/organ/internal/eyes/synth - category = list( - RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_1 - ) - departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE - -/obj/item/organ/internal/heart/synth - name = "hydraulic pump engine" - desc = "An electronic device that handles the hydraulic pumps, powering one's robotic limbs. Without this, synthetics are unable to move." - organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES - icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' - icon_state = "heart-ipc-on" - base_icon_state = "heart-ipc" - maxHealth = 1.5 * STANDARD_ORGAN_THRESHOLD // 1.5x due to synthcode.tm being weird - zone = BODY_ZONE_CHEST - slot = ORGAN_SLOT_HEART - var/last_message_time = 0 - -/obj/item/organ/internal/heart/synth/emp_act(severity) - . = ..() - - if(!owner || . & EMP_PROTECT_SELF) - return - - if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. - COOLDOWN_START(src, severe_cooldown, 10 SECONDS) - - switch(severity) - if(EMP_HEAVY) - to_chat(owner, span_warning("Alert: Main hydraulic pump control has taken severe damage, seek maintenance immediately. Error code: HP300-10.")) - apply_organ_damage(SYNTH_ORGAN_HEAVY_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) - if(EMP_LIGHT) - to_chat(owner, span_warning("Alert: Main hydraulic pump control has taken light damage, seek maintenance immediately. Error code: HP300-05.")) - apply_organ_damage(SYNTH_ORGAN_LIGHT_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) - -/datum/design/synth_heart - name = "Hydraulic Pump Engine" - desc = "An electronic device that handles the hydraulic pumps, powering one's robotic limbs. Without this, synthetics are unable to move." - id = "synth_heart" - build_type = PROTOLATHE | AWAY_LATHE | MECHFAB - construction_time = 4 SECONDS - materials = list( - /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, - /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, - ) - build_path = /obj/item/organ/internal/heart/synth - category = list( - RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_1 - ) - departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE - -/obj/item/organ/internal/liver/synth - name = "reagent processing unit" - desc = "An electronic device that processes the beneficial chemicals for the synthetic user." - icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' - icon_state = "liver-ipc" - filterToxins = FALSE //We dont filter them, we're immune to them - zone = BODY_ZONE_CHEST - slot = ORGAN_SLOT_LIVER - maxHealth = 1 * STANDARD_ORGAN_THRESHOLD - organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES - -/obj/item/organ/internal/liver/synth/emp_act(severity) - . = ..() - - if(!owner || . & EMP_PROTECT_SELF) - return - - if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. - COOLDOWN_START(src, severe_cooldown, 10 SECONDS) - - switch(severity) - if(EMP_HEAVY) - to_chat(owner, span_warning("Alert: Critical! Reagent processing unit failure, seek maintenance immediately. Error Code: DR-1k")) - apply_organ_damage(SYNTH_ORGAN_HEAVY_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) - - if(EMP_LIGHT) - to_chat(owner, span_warning("Alert: Reagent processing unit failure, seek maintenance for diagnostic. Error Code: DR-0k")) - apply_organ_damage(SYNTH_ORGAN_LIGHT_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) - -/datum/design/synth_liver - name = "Reagent Processing Unit" - desc = "An electronic device that processes the beneficial chemicals for the synthetic user." - id = "synth_liver" - build_type = PROTOLATHE | AWAY_LATHE | MECHFAB - construction_time = 4 SECONDS - materials = list( - /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, - /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, - ) - build_path = /obj/item/organ/internal/liver/synth - category = list( - RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_1 - ) - departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE - -/obj/item/organ/internal/lungs/synth - name = "heatsink" - desc = "A device that transfers generated heat to a fluid medium to cool it down. Required to keep your synthetics cool-headed. It's shape resembles lungs." //Purposefully left the 'fluid medium' ambigious for interpretation of the character, whether it be air or fluid cooling - icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' - icon_state = "lungs-ipc" - safe_nitro_min = 0 - safe_co2_max = 0 - safe_plasma_min = 0 - safe_plasma_max = 0 - safe_oxygen_min = 0 //What are you doing man, dont breathe with those! - safe_oxygen_max = 0 - zone = BODY_ZONE_CHEST - slot = ORGAN_SLOT_LUNGS - maxHealth = 1.5 * STANDARD_ORGAN_THRESHOLD - organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES - -/obj/item/organ/internal/lungs/synth/emp_act(severity) - . = ..() - - if(!owner || . & EMP_PROTECT_SELF) - return - - if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. - COOLDOWN_START(src, severe_cooldown, 10 SECONDS) - - switch(severity) - if(EMP_HEAVY) - to_chat(owner, span_warning("Alert: Critical cooling system failure! Seek maintenance immediately. Error Code: 5H-17")) - owner.adjust_bodytemperature(SYNTH_HEAVY_EMP_TEMPERATURE_POWER * TEMPERATURE_DAMAGE_COEFFICIENT) - - if(EMP_LIGHT) - to_chat(owner, span_warning("Alert: Major cooling system failure!")) - owner.adjust_bodytemperature(SYNTH_LIGHT_EMP_TEMPERATURE_POWER * TEMPERATURE_DAMAGE_COEFFICIENT) - -/datum/design/synth_heatsink - name = "Heatsink" - desc = "A device that transfers generated heat to a fluid medium to cool it down. Required to keep your synthetics cool-headed. It's shape resembles lungs." - id = "synth_lungs" - build_type = PROTOLATHE | AWAY_LATHE | MECHFAB - construction_time = 4 SECONDS - materials = list( - /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, - /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, - ) - build_path = /obj/item/organ/internal/lungs/synth - category = list( - RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_1 - ) - departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE - -///IPCS NO LONGER ARE PURE ELECTRICAL BEINGS, any attempts to change this outside of Borbop will be denied. Thanks. -/obj/item/organ/internal/stomach/synth - name = "synthetic bio-reactor" - icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' - icon_state = "stomach-ipc" - w_class = WEIGHT_CLASS_NORMAL - zone = BODY_ZONE_CHEST - slot = ORGAN_SLOT_STOMACH - maxHealth = 1 * STANDARD_ORGAN_THRESHOLD - zone = "chest" - slot = "stomach" - desc = "A specialised mini reactor, for synthetic use only. Has a low-power mode to ensure baseline functions. Without this, synthetics are unable to stay powered." - organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES - -/obj/item/organ/internal/stomach/synth/emp_act(severity) - . = ..() - - if(!owner || . & EMP_PROTECT_SELF) - return - - if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. - COOLDOWN_START(src, severe_cooldown, 10 SECONDS) - - switch(severity) - if(EMP_HEAVY) - owner.nutrition = max(0, owner.nutrition - SYNTH_STOMACH_HEAVY_EMP_CHARGE_LOSS) - apply_organ_damage(SYNTH_ORGAN_HEAVY_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) - to_chat(owner, span_warning("Alert: Severe battery discharge!")) - - if(EMP_LIGHT) - owner.nutrition = max(0, owner.nutrition - SYNTH_STOMACH_LIGHT_EMP_CHARGE_LOSS) - apply_organ_damage(SYNTH_ORGAN_LIGHT_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) - to_chat(owner, span_warning("Alert: Minor battery discharge!")) - -/datum/design/synth_stomach - name = "Synthetic Bio-Reactor" - desc = "A specialised mini reactor, for synthetic use only. Has a low-power mode to ensure baseline functions. Without this, synthetics are unable to stay powered." - id = "synth_stomach" - build_type = PROTOLATHE | AWAY_LATHE | MECHFAB - construction_time = 4 SECONDS - materials = list( - /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, - /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, - ) - build_path = /obj/item/organ/internal/stomach/synth - category = list( - RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_1 - ) - departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE - -/obj/item/organ/internal/stomach/synth/Insert(mob/living/carbon/receiver, special, drop_if_replaced) - . = ..() - RegisterSignal(receiver, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(on_borg_charge)) - -/obj/item/organ/internal/stomach/synth/Remove(mob/living/carbon/stomach_owner, special) - . = ..() - UnregisterSignal(stomach_owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT) - -///Handles charging the synth from borg chargers -/obj/item/organ/internal/stomach/synth/proc/on_borg_charge(datum/source, amount) - SIGNAL_HANDLER - - if(owner.nutrition >= NUTRITION_LEVEL_ALMOST_FULL) - return - - amount /= 50 // Lowers the charging amount so it isn't instant - owner.nutrition = min((owner.nutrition + amount), NUTRITION_LEVEL_ALMOST_FULL) // Makes sure we don't make the synth too full, which would apply the overweight slowdown - -/obj/item/organ/internal/tongue/synth - name = "synthetic voicebox" - desc = "A fully-functional synthetic tongue, encased in soft silicone. Features include high-resolution vocals and taste receptors." - icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' - icon_state = "cybertongue" - say_mod = "beeps" - attack_verb_continuous = list("beeps", "boops") - attack_verb_simple = list("beep", "boop") - modifies_speech = TRUE - taste_sensitivity = 25 // not as good as an organic tongue - maxHealth = 100 //RoboTongue! - zone = BODY_ZONE_HEAD - slot = ORGAN_SLOT_TONGUE - organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES - -/obj/item/organ/internal/tongue/synth/get_scream_sound() - return 'monkestation/sound/voice/screams/silicon/scream_silicon.ogg' - -/obj/item/organ/internal/tongue/synth/get_laugh_sound() - return pick( - 'monkestation/sound/voice/laugh/silicon/laugh_siliconE1M0.ogg', - 'monkestation/sound/voice/laugh/silicon/laugh_siliconE1M1.ogg', - 'monkestation/sound/voice/laugh/silicon/laugh_siliconM2.ogg', - ) - -/obj/item/organ/internal/tongue/synth/can_speak_language(language) - return TRUE - -/obj/item/organ/internal/tongue/synth/handle_speech(datum/source, list/speech_args) - speech_args[SPEECH_SPANS] |= SPAN_ROBOT - -/datum/design/synth_tongue - name = "Synthetic Tongue" - desc = "A fully-functional synthetic tongue, encased in soft silicone. Features include high-resolution vocals and taste receptors." - id = "synth_tongue" - build_type = PROTOLATHE | AWAY_LATHE | MECHFAB - construction_time = 4 SECONDS - materials = list( - /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, - /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, - ) - build_path = /obj/item/organ/internal/tongue/synth - category = list( - RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_1 - ) - departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE diff --git a/monkestation/code/modules/smithing/ipcs/research.dm b/monkestation/code/modules/smithing/ipcs/research.dm deleted file mode 100644 index c75d134db45f..000000000000 --- a/monkestation/code/modules/smithing/ipcs/research.dm +++ /dev/null @@ -1,19 +0,0 @@ -/datum/techweb_node/improved_robotic_tend_wounds - id = "improved_robotic_surgery" - display_name = "Improved Robotic Repair Surgeries" - description = "As it turns out, you don't actually need to cut out entire support rods if it's just scratched!" - prereq_ids = list("engineering") - design_ids = list( - "robotic_heal_surgery_upgrade" - ) - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 900) - -/datum/techweb_node/advanced_robotic_tend_wounds - id = "advanced_robotic_surgery" - display_name = "Advanced Robotic Surgeries" - description = "Did you know Hephaestus actually has a free online tutorial for synthetic trauma repairs? It's true!" - prereq_ids = list("improved_robotic_surgery") - design_ids = list( - "robotic_heal_surgery_upgrade_2" - ) - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1300) // less expensive than the organic surgery research equivalent since its JUST tend wounds diff --git a/monkestation/code/modules/smithing/oozelings/body/organs.dm b/monkestation/code/modules/smithing/oozelings/body/organs.dm deleted file mode 100644 index 6e33ac028af9..000000000000 --- a/monkestation/code/modules/smithing/oozelings/body/organs.dm +++ /dev/null @@ -1,420 +0,0 @@ -/obj/item/organ/internal/eyes/jelly - name = "photosensitive eyespots" - zone = BODY_ZONE_CHEST - organ_flags = ORGAN_UNREMOVABLE - -/obj/item/organ/internal/eyes/roundstartslime - name = "photosensitive eyespots" - zone = BODY_ZONE_CHEST - organ_flags = ORGAN_UNREMOVABLE - -/obj/item/organ/internal/ears/jelly - name = "core audiosomes" - zone = BODY_ZONE_CHEST - organ_flags = ORGAN_UNREMOVABLE - -/obj/item/organ/internal/tongue/jelly - zone = BODY_ZONE_CHEST - organ_flags = ORGAN_UNREMOVABLE - -/obj/item/organ/internal/tongue/jelly/get_possible_languages() - return ..() + /datum/language/slime - -/obj/item/organ/internal/lungs/slime - zone = BODY_ZONE_CHEST - organ_flags = ORGAN_UNREMOVABLE - safe_oxygen_min = 4 //We don't need much oxygen to subsist. - -/obj/item/organ/internal/lungs/slime/on_life(seconds_per_tick, times_fired) - . = ..() - operated = FALSE - -/obj/item/organ/internal/liver/slime - name = "endoplasmic reticulum" - zone = BODY_ZONE_CHEST - organ_flags = ORGAN_UNREMOVABLE - organ_traits = list(TRAIT_TOXINLOVER) - -/obj/item/organ/internal/liver/slime/on_life(seconds_per_tick, times_fired) - . = ..() - operated = FALSE - -/obj/item/organ/internal/stomach/slime - name = "golgi apparatus" - zone = BODY_ZONE_CHEST - organ_flags = ORGAN_UNREMOVABLE - -/obj/item/organ/internal/stomach/slime/on_life(seconds_per_tick, times_fired) - . = ..() - operated = FALSE - -/obj/item/organ/internal/brain/slime - name = "core" - desc = "The center core of a slimeperson, technically their 'extract.' Where the cytoplasm, membrane, and organelles come from; perhaps this is also a mitochondria?" - zone = BODY_ZONE_CHEST - icon = 'monkestation/code/modules/smithing/icons/oozeling.dmi' - icon_state = "slime_core" - resistance_flags = FIRE_PROOF - - var/obj/effect/death_melt_type = /obj/effect/temp_visual/wizard/out - var/core_color = COLOR_WHITE - - var/core_ejected = FALSE - var/gps_active = TRUE - - var/datum/dna/stored_dna - - var/list/stored_items = list() - - var/rebuilt = TRUE - var/coredeath = TRUE - - var/datum/action/cooldown/membrane_murmur/membrane_mur - -/obj/item/organ/internal/brain/slime/Initialize(mapload, mob/living/carbon/organ_owner, list/examine_list) - . = ..() - membrane_mur = new /datum/action/cooldown/membrane_murmur() - colorize() - transform.Scale(2, 2) - -/obj/item/organ/internal/brain/slime/Destroy(force) - QDEL_NULL(membrane_mur) - return ..() - -/obj/item/organ/internal/brain/slime/examine() - . = ..() - if(gps_active) - . += span_notice("A dim light lowly pulsates from the center of the core, indicating an outgoing signal from a tracking microchip.") - . += span_red("You could probably snuff that out.") - . += span_hypnophrase("You remember that pouring plasma on it, if it's non-embodied, would make it regrow one.") - -/obj/item/organ/internal/brain/slime/attack_self(mob/living/user) // Allows a player (presumably an antag) to deactivate the GPS signal on a slime core - if(!(gps_active)) - return - user.visible_message(span_warning("[user] begins jamming their hand into a slime core! Slime goes everywhere!"), - span_notice("You jam your hand into the core, feeling for the densest point! Slime covers your arm."), - span_notice("You hear an obscene squelching sound.") - ) - playsound(user, 'sound/surgery/organ1.ogg', 80, TRUE) - - if(!do_after(user, 30 SECONDS, src)) - user.visible_message(span_warning("[user]'s hand slips out of the core before they can cause any harm!'"), - span_warning("Your hand slips out of the goopy core before you can find it's densest point."), - span_notice("You hear a resounding plop.") - ) - return - - user.visible_message(span_warning("[user] crunches something deep in the slime core! It gradually stops glowing."), - span_notice("You find the densest point, crushing it in your palm. The blinking light in the core slowly dissapates and items start to come out."), - span_notice("You hear a wet crunching sound.")) - playsound(user, 'sound/effects/wounds/crackandbleed.ogg', 80, TRUE) - - drop_items_to_ground(get_turf(user)) - -/obj/item/organ/internal/brain/slime/Insert(mob/living/carbon/organ_owner, special = FALSE, drop_if_replaced, no_id_transfer) - . = ..() - if(!.) - return - colorize() - core_ejected = FALSE - RegisterSignal(organ_owner, COMSIG_MOB_STATCHANGE, PROC_REF(on_stat_change)) - -/obj/item/organ/internal/brain/slime/proc/colorize() - if(isoozeling(owner)) - var/datum/color_palette/generic_colors/located = owner.dna.color_palettes[/datum/color_palette/generic_colors] - core_color = located.return_color(MUTANT_COLOR) - add_atom_colour(core_color, FIXED_COLOUR_PRIORITY) - -/obj/item/organ/internal/brain/slime/proc/on_stat_change(mob/living/victim, new_stat, turf/loc_override) - SIGNAL_HANDLER - - if(new_stat != DEAD) - return - - addtimer(CALLBACK(src, PROC_REF(core_ejection), victim), 0) // explode them after the current proc chain ends, to avoid weirdness - -/obj/item/organ/internal/brain/slime/proc/enable_coredeath() - coredeath = TRUE - if(owner?.stat == DEAD) - addtimer(CALLBACK(src, PROC_REF(core_ejection), owner), 0) - -/////// -/// CORE EJECTION PROC -/// Makes it so that when a slime dies, their core ejects and their body is qdel'd. - -/obj/item/organ/internal/brain/slime/proc/core_ejection(mob/living/carbon/human/victim, new_stat, turf/loc_override) - if(core_ejected || !coredeath) - return - if(QDELETED(stored_dna)) - stored_dna = new - - victim.dna.copy_dna(stored_dna) - core_ejected = TRUE - victim.visible_message(span_warning("[victim]'s body completely dissolves, collapsing outwards!"), span_notice("Your body completely dissolves, collapsing outwards!"), span_notice("You hear liquid splattering.")) - var/turf/death_turf = get_turf(victim) - - for(var/atom/movable/item as anything in victim.get_equipped_items(include_pockets = TRUE)) - victim.dropItemToGround(item) - stored_items |= item - item.forceMove(src) - - if(victim.get_organ_slot(ORGAN_SLOT_BRAIN) == src) - Remove(victim) - if(death_turf) - forceMove(death_turf) - src.wash(CLEAN_WASH) - new death_melt_type(death_turf, victim.dir) - - do_steam_effects(death_turf) - playsound(victim, 'sound/effects/blobattack.ogg', 80, TRUE) - - if(gps_active) // adding the gps signal if they have activated the ability - AddComponent(/datum/component/gps, "[victim]'s Core") - - if(brainmob) - membrane_mur.Grant(brainmob) - var/datum/antagonist/changeling/target_ling = brainmob.mind?.has_antag_datum(/datum/antagonist/changeling) - - if(target_ling) - if(target_ling.oozeling_revives > 0) - target_ling.oozeling_revives-- - addtimer(CALLBACK(src, PROC_REF(rebuild_body), null, FALSE), 30 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_DELETE_ME) - - if(IS_BLOODSUCKER(brainmob)) - var/datum/antagonist/bloodsucker/target_bloodsucker = brainmob.mind.has_antag_datum(/datum/antagonist/bloodsucker) - if(target_bloodsucker.bloodsucker_blood_volume >= OOZELING_MIN_REVIVE_BLOOD_THRESHOLD) - addtimer(CALLBACK(src, PROC_REF(rebuild_body), null, FALSE), 30 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_DELETE_ME) - target_bloodsucker.bloodsucker_blood_volume -= (OOZELING_MIN_REVIVE_BLOOD_THRESHOLD * 0.5) - - rebuilt = FALSE - victim.transfer_observers_to(src) - Remove(victim) - qdel(victim) - -/obj/item/organ/internal/brain/slime/proc/do_steam_effects(turf/loc) - var/datum/effect_system/steam_spread/steam = new() - steam.set_up(10, FALSE, loc) - steam.start() - -/////// -/// CHECK FOR REPAIR SECTION -/// Makes it so that when a slime's core has plasma poured on it, it builds a new body and moves the brain into it. - -/obj/item/organ/internal/brain/slime/check_for_repair(obj/item/item, mob/user) - if(damage && item.is_drainable() && item.reagents.has_reagent(/datum/reagent/toxin/plasma)) //attempt to heal the brain - if (item.reagents.get_reagent_amount(/datum/reagent/toxin/plasma) < 100) - user.balloon_alert(user, "too little plasma!") - return FALSE - - user.visible_message( - span_notice("[user] starts to slowly pour the contents of [item] onto [src]. It seems to bubble and roil, beginning to stretch its cytoskeleton outwards..."), - span_notice("You start to slowly pour the contents of [item] onto [src]. It seems to bubble and roil, beginning to stretch its membrane outwards..."), - span_hear("You hear bubbling.") - ) - - if(!do_after(user, 30 SECONDS, src)) - to_chat(user, span_warning("You failed to pour the contents of [item] onto [src]!")) - return FALSE - - if (item.reagents.get_reagent_amount(/datum/reagent/toxin/plasma) < 100) // minor exploit but might as well patch it - user.balloon_alert(user, "too little plasma!") - return FALSE - - user.visible_message( - span_notice("[user] pours the contents of [item] onto [src], causing it to form a proper cytoplasm and outer membrane."), - span_notice("You pour the contents of [item] onto [src], causing it to form a proper cytoplasm and outer membrane."), - span_hear("You hear a splat.") - ) - - item.reagents.remove_reagent(/datum/reagent/toxin/plasma, 100) - rebuild_body(user) - return TRUE - return ..() - -/obj/item/organ/internal/brain/slime/proc/drop_items_to_ground(turf/turf) - for(var/atom/movable/item as anything in stored_items) - item.forceMove(turf) - stored_items.Cut() - -/obj/item/organ/internal/brain/slime/proc/rebuild_body(mob/user, nugget = TRUE) as /mob/living/carbon/human - RETURN_TYPE(/mob/living/carbon/human) - if(rebuilt) - return owner - set_organ_damage(-maxHealth) // heals the brain fully - - if(gps_active) // making sure the gps signal is removed if it's active on revival - gps_active = FALSE - qdel(GetComponent(/datum/component/gps)) - - //we have the plasma. we can rebuild them. - brainmob?.mind?.grab_ghost() - if(isnull(brainmob)) - user?.balloon_alert(user, "This brain is not a viable candidate for repair!") - return null - if(isnull(brainmob.stored_dna)) - user?.balloon_alert(user, "This brain does not contain any dna!") - return null - if(isnull(brainmob.client)) - user?.balloon_alert(user, "This brain does not contain a mind!") - return null - var/mob/living/carbon/human/new_body = new /mob/living/carbon/human(drop_location()) - - rebuilt = TRUE - brainmob.client?.prefs?.safe_transfer_prefs_to(new_body) - new_body.underwear = "Nude" - new_body.undershirt = "Nude" - new_body.socks = "Nude" - stored_dna.transfer_identity(new_body, transfer_SE = TRUE) - new_body.real_name = new_body.dna.real_name - new_body.name = new_body.dna.real_name - new_body.updateappearance(mutcolor_update = TRUE) - new_body.domutcheck() - new_body.forceMove(drop_location()) - if(!nugget) - new_body.set_nutrition(NUTRITION_LEVEL_FED) - new_body.blood_volume = nugget ? (BLOOD_VOLUME_SAFE + 60) : BLOOD_VOLUME_NORMAL - REMOVE_TRAIT(new_body, TRAIT_NO_TRANSFORM, REF(src)) - if(!QDELETED(brainmob)) - SSquirks.AssignQuirks(new_body, brainmob.client) - var/obj/item/organ/internal/brain/new_body_brain = new_body.get_organ_slot(ORGAN_SLOT_BRAIN) - qdel(new_body_brain) - forceMove(new_body) - Insert(new_body) - if(nugget) - for(var/obj/item/bodypart as anything in new_body.bodyparts) - if(istype(bodypart, /obj/item/bodypart/chest)) - continue - qdel(bodypart) - new_body.visible_message(span_warning("[new_body]'s torso \"forms\" from [new_body.p_their()] core, yet to form the rest.")) - to_chat(owner, span_purple("Your torso fully forms out of your core, yet to form the rest.")) - else - new_body.visible_message(span_warning("[new_body]'s body fully forms from [new_body.p_their()] core!")) - to_chat(owner, span_purple("Your body fully forms from your core!")) - - membrane_mur.Remove(brainmob) - brainmob?.mind?.transfer_to(new_body) - new_body.grab_ghost() - transfer_observers_to(new_body) - - drop_items_to_ground(new_body.drop_location()) - return new_body - - -///The rate at which slimes regenerate their jelly normally -#define JELLY_REGEN_RATE 1.5 -///The rate at which slimes regenerate their jelly when they completely run out of it and start taking damage, usually after having cannibalized all their limbs already -#define JELLY_REGEN_RATE_EMPTY 2.5 -///The blood volume at which slimes begin to start losing nutrition -- so that IV drips can work for blood deficient slimes -#define BLOOD_VOLUME_LOSE_NUTRITION 550 - - -/obj/item/organ/internal/heart/slime - name = "slime heart" - - heart_bloodtype = /datum/blood_type/slime - var/datum/action/innate/regenerate_limbs/regenerate_limbs - -/obj/item/organ/internal/heart/slime/Insert(mob/living/carbon/receiver, special, drop_if_replaced) - . = ..() - regenerate_limbs = new - regenerate_limbs.Grant(receiver) - RegisterSignal(receiver, COMSIG_HUMAN_ON_HANDLE_BLOOD, PROC_REF(slime_blood)) - -/obj/item/organ/internal/heart/slime/Remove(mob/living/carbon/heartless, special) - . = ..() - if(regenerate_limbs) - regenerate_limbs.Remove(heartless) - qdel(regenerate_limbs) - UnregisterSignal(heartless, COMSIG_HUMAN_ON_HANDLE_BLOOD) - -/obj/item/organ/internal/heart/slime/proc/slime_blood(mob/living/carbon/human/slime, seconds_per_tick, times_fired) - SIGNAL_HANDLER - - if(slime.stat == DEAD) - return NONE - - . = HANDLE_BLOOD_NO_NUTRITION_DRAIN|HANDLE_BLOOD_NO_EFFECTS - - if(slime.blood_volume <= 0) - slime.blood_volume += JELLY_REGEN_RATE_EMPTY * seconds_per_tick - slime.adjustBruteLoss(2.5 * seconds_per_tick) - to_chat(slime, span_danger("You feel empty!")) - - if(slime.blood_volume < BLOOD_VOLUME_NORMAL) - if(slime.nutrition >= NUTRITION_LEVEL_STARVING) - slime.blood_volume += JELLY_REGEN_RATE * seconds_per_tick - if(slime.blood_volume <= BLOOD_VOLUME_LOSE_NUTRITION) // don't lose nutrition if we are above a certain threshold, otherwise slimes on IV drips will still lose nutrition - slime.adjust_nutrition(-1.25 * seconds_per_tick) - - if(HAS_TRAIT(slime, TRAIT_BLOOD_DEFICIENCY)) - var/datum/quirk/blooddeficiency/blooddeficiency = slime.get_quirk(/datum/quirk/blooddeficiency) - blooddeficiency?.lose_blood(slime, seconds_per_tick) - - if(slime.blood_volume < BLOOD_VOLUME_OKAY) - if(SPT_PROB(2.5, seconds_per_tick)) - to_chat(slime, span_danger("You feel drained!")) - - if(slime.blood_volume < BLOOD_VOLUME_BAD) - Cannibalize_Body(slime) - - regenerate_limbs?.build_all_button_icons(UPDATE_BUTTON_STATUS) - return . - -/obj/item/organ/internal/heart/slime/proc/Cannibalize_Body(mob/living/carbon/human/H) - var/list/limbs_to_consume = list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) - H.get_missing_limbs() - var/obj/item/bodypart/consumed_limb - if(!length(limbs_to_consume)) - H.losebreath++ - return - if(H.num_legs) //Legs go before arms - limbs_to_consume -= list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM) - consumed_limb = H.get_bodypart(pick(limbs_to_consume)) - consumed_limb.drop_limb() - to_chat(H, span_userdanger("Your [consumed_limb] is drawn back into your body, unable to maintain its shape!")) - qdel(consumed_limb) - H.blood_volume += 20 - -/datum/action/innate/regenerate_limbs - name = "Regenerate Limbs" - check_flags = AB_CHECK_CONSCIOUS - button_icon_state = "slimeheal" - button_icon = 'icons/mob/actions/actions_slime.dmi' - background_icon_state = "bg_alien" - overlay_icon_state = "bg_alien_border" - -/datum/action/innate/regenerate_limbs/IsAvailable(feedback = FALSE) - . = ..() - if(!.) - return - var/mob/living/carbon/human/H = owner - var/list/limbs_to_heal = H.get_missing_limbs() - if(!length(limbs_to_heal)) - return FALSE - if(H.blood_volume >= BLOOD_VOLUME_OKAY+40) - return TRUE - -/datum/action/innate/regenerate_limbs/Activate() - var/mob/living/carbon/human/H = owner - var/list/limbs_to_heal = H.get_missing_limbs() - if(!length(limbs_to_heal)) - to_chat(H, span_notice("You feel intact enough as it is.")) - return - to_chat(H, span_notice("You focus intently on your missing [length(limbs_to_heal) >= 2 ? "limbs" : "limb"]...")) - if(H.blood_volume >= 40*length(limbs_to_heal)+BLOOD_VOLUME_OKAY) - H.regenerate_limbs() - H.blood_volume -= 40*length(limbs_to_heal) - to_chat(H, span_notice("...and after a moment you finish reforming!")) - return - else if(H.blood_volume >= 40)//We can partially heal some limbs - while(H.blood_volume >= BLOOD_VOLUME_OKAY+40) - var/healed_limb = pick(limbs_to_heal) - H.regenerate_limb(healed_limb) - limbs_to_heal -= healed_limb - H.blood_volume -= 40 - to_chat(H, span_warning("...but there is not enough of you to fix everything! You must attain more mass to heal completely!")) - return - to_chat(H, span_warning("...but there is not enough of you to go around! You must attain more mass to heal!")) - -#undef JELLY_REGEN_RATE -#undef JELLY_REGEN_RATE_EMPTY -#undef BLOOD_VOLUME_LOSE_NUTRITION diff --git a/monkestation/code/modules/storytellers/converted_events/event_overrides.dm b/monkestation/code/modules/storytellers/converted_events/event_overrides.dm index 918ddfad1280..f3c81d2b60f0 100644 --- a/monkestation/code/modules/storytellers/converted_events/event_overrides.dm +++ b/monkestation/code/modules/storytellers/converted_events/event_overrides.dm @@ -22,6 +22,10 @@ tags = list(TAG_DESTRUCTIVE, TAG_COMBAT, TAG_EXTERNAL, TAG_ALIEN) checks_antag_cap = TRUE +/datum/round_event_control/antagonist/solo/blob_infection + tags = list(TAG_DESTRUCTIVE, TAG_COMBAT, TAG_EXTERNAL, TAG_ALIEN) + checks_antag_cap = TRUE + /datum/round_event_control/brain_trauma track = EVENT_TRACK_MUNDANE tags = list(TAG_TARGETED, TAG_MAGICAL) //im putting magical on this because I think this can give the magic brain traumas diff --git a/monkestation/code/modules/storytellers/converted_events/solo/blob_infection.dm b/monkestation/code/modules/storytellers/converted_events/solo/blob_infection.dm new file mode 100644 index 000000000000..21f44ba27027 --- /dev/null +++ b/monkestation/code/modules/storytellers/converted_events/solo/blob_infection.dm @@ -0,0 +1,30 @@ +/datum/round_event_control/antagonist/solo/blob_infection + name = "Blob Infection" + weight = 4 + antag_flag = ROLE_BLOB_INFECTION + antag_datum = /datum/antagonist/blob/infection + min_players = 35 + maximum_antags = 1 + max_occurrences = 1 + earliest_start = 80 MINUTES + protected_roles = list( + JOB_CAPTAIN, + JOB_NANOTRASEN_REPRESENTATIVE, + JOB_BLUESHIELD, + JOB_HEAD_OF_PERSONNEL, + JOB_CHIEF_ENGINEER, + JOB_CHIEF_MEDICAL_OFFICER, + JOB_RESEARCH_DIRECTOR, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_PRISONER, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_SECURITY_ASSISTANT, + JOB_BRIG_PHYSICIAN, + ) + restricted_roles = list( + JOB_AI, + JOB_CYBORG, + ) + description = "Infects a crewmember with the blob overmind." diff --git a/monkestation/code/modules/smithing/ipcs/body/base_bodyparts.dm b/monkestation/code/modules/surgery/bodyparts/ipc_bodyparts.dm similarity index 100% rename from monkestation/code/modules/smithing/ipcs/body/base_bodyparts.dm rename to monkestation/code/modules/surgery/bodyparts/ipc_bodyparts.dm diff --git a/monkestation/code/modules/smithing/oozelings/body/bodyparts.dm b/monkestation/code/modules/surgery/bodyparts/oozeling_bodyparts.dm similarity index 100% rename from monkestation/code/modules/smithing/oozelings/body/bodyparts.dm rename to monkestation/code/modules/surgery/bodyparts/oozeling_bodyparts.dm diff --git a/monkestation/code/modules/ranching/satyr/bodyparts.dm b/monkestation/code/modules/surgery/bodyparts/satyr_bodyparts.dm similarity index 100% rename from monkestation/code/modules/ranching/satyr/bodyparts.dm rename to monkestation/code/modules/surgery/bodyparts/satyr_bodyparts.dm diff --git a/monkestation/code/modules/surgery/organs/internal/brain.dm b/monkestation/code/modules/surgery/organs/internal/brain.dm index 54595401627b..a26374a30239 100644 --- a/monkestation/code/modules/surgery/organs/internal/brain.dm +++ b/monkestation/code/modules/surgery/organs/internal/brain.dm @@ -14,3 +14,325 @@ . = ..() if(prob(5) && !robust) SEND_SOUND(owner, sound('sound/ambience/ambiruin3.ogg', volume = 25)) + +/obj/item/organ/internal/brain/slime + name = "core" + desc = "The center core of a slimeperson, technically their 'extract.' Where the cytoplasm, membrane, and organelles come from; perhaps this is also a mitochondria?" + zone = BODY_ZONE_CHEST + icon = 'monkestation/code/modules/smithing/icons/oozeling.dmi' + icon_state = "slime_core" + resistance_flags = FIRE_PROOF + + var/obj/effect/death_melt_type = /obj/effect/temp_visual/wizard/out + var/core_color = COLOR_WHITE + + var/core_ejected = FALSE + var/gps_active = TRUE + + var/datum/dna/stored_dna + + var/list/stored_items = list() + + var/rebuilt = TRUE + var/coredeath = TRUE + + var/datum/action/cooldown/membrane_murmur/membrane_mur + +/obj/item/organ/internal/brain/slime/Initialize(mapload, mob/living/carbon/organ_owner, list/examine_list) + . = ..() + membrane_mur = new /datum/action/cooldown/membrane_murmur() + colorize() + transform.Scale(2, 2) + +/obj/item/organ/internal/brain/slime/Destroy(force) + QDEL_NULL(membrane_mur) + return ..() + +/obj/item/organ/internal/brain/slime/examine() + . = ..() + if(gps_active) + . += span_notice("A dim light lowly pulsates from the center of the core, indicating an outgoing signal from a tracking microchip.") + . += span_red("You could probably snuff that out.") + . += span_hypnophrase("You remember that pouring plasma on it, if it's non-embodied, would make it regrow one.") + +/obj/item/organ/internal/brain/slime/attack_self(mob/living/user) // Allows a player (presumably an antag) to deactivate the GPS signal on a slime core + if(!(gps_active)) + return + user.visible_message(span_warning("[user] begins jamming their hand into a slime core! Slime goes everywhere!"), + span_notice("You jam your hand into the core, feeling for the densest point! Slime covers your arm."), + span_notice("You hear an obscene squelching sound.") + ) + playsound(user, 'sound/surgery/organ1.ogg', 80, TRUE) + + if(!do_after(user, 30 SECONDS, src)) + user.visible_message(span_warning("[user]'s hand slips out of the core before they can cause any harm!'"), + span_warning("Your hand slips out of the goopy core before you can find it's densest point."), + span_notice("You hear a resounding plop.") + ) + return + + user.visible_message(span_warning("[user] crunches something deep in the slime core! It gradually stops glowing."), + span_notice("You find the densest point, crushing it in your palm. The blinking light in the core slowly dissapates and items start to come out."), + span_notice("You hear a wet crunching sound.")) + playsound(user, 'sound/effects/wounds/crackandbleed.ogg', 80, TRUE) + + drop_items_to_ground(get_turf(user)) + +/obj/item/organ/internal/brain/slime/Insert(mob/living/carbon/organ_owner, special = FALSE, drop_if_replaced, no_id_transfer) + . = ..() + if(!.) + return + colorize() + core_ejected = FALSE + RegisterSignal(organ_owner, COMSIG_MOB_STATCHANGE, PROC_REF(on_stat_change)) + +/obj/item/organ/internal/brain/slime/proc/colorize() + if(isoozeling(owner)) + var/datum/color_palette/generic_colors/located = owner.dna.color_palettes[/datum/color_palette/generic_colors] + core_color = located.return_color(MUTANT_COLOR) + add_atom_colour(core_color, FIXED_COLOUR_PRIORITY) + +/obj/item/organ/internal/brain/slime/proc/on_stat_change(mob/living/victim, new_stat, turf/loc_override) + SIGNAL_HANDLER + + if(new_stat != DEAD) + return + + addtimer(CALLBACK(src, PROC_REF(core_ejection), victim), 0) // explode them after the current proc chain ends, to avoid weirdness + +/obj/item/organ/internal/brain/slime/proc/enable_coredeath() + coredeath = TRUE + if(owner?.stat == DEAD) + addtimer(CALLBACK(src, PROC_REF(core_ejection), owner), 0) + +/////// +/// CORE EJECTION PROC +/// Makes it so that when a slime dies, their core ejects and their body is qdel'd. + +/obj/item/organ/internal/brain/slime/proc/core_ejection(mob/living/carbon/human/victim, new_stat, turf/loc_override) + if(core_ejected || !coredeath) + return + if(QDELETED(stored_dna)) + stored_dna = new + + victim.dna.copy_dna(stored_dna) + core_ejected = TRUE + victim.visible_message(span_warning("[victim]'s body completely dissolves, collapsing outwards!"), span_notice("Your body completely dissolves, collapsing outwards!"), span_notice("You hear liquid splattering.")) + var/turf/death_turf = get_turf(victim) + + for(var/atom/movable/item as anything in victim.get_equipped_items(include_pockets = TRUE)) + victim.dropItemToGround(item) + stored_items |= item + item.forceMove(src) + + if(victim.get_organ_slot(ORGAN_SLOT_BRAIN) == src) + Remove(victim) + if(death_turf) + forceMove(death_turf) + src.wash(CLEAN_WASH) + new death_melt_type(death_turf, victim.dir) + + do_steam_effects(death_turf) + playsound(victim, 'sound/effects/blobattack.ogg', 80, TRUE) + + if(gps_active) // adding the gps signal if they have activated the ability + AddComponent(/datum/component/gps, "[victim]'s Core") + + if(brainmob) + membrane_mur.Grant(brainmob) + var/datum/antagonist/changeling/target_ling = brainmob.mind?.has_antag_datum(/datum/antagonist/changeling) + + if(target_ling) + if(target_ling.oozeling_revives > 0) + target_ling.oozeling_revives-- + addtimer(CALLBACK(src, PROC_REF(rebuild_body), null, FALSE), 30 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_DELETE_ME) + + if(IS_BLOODSUCKER(brainmob)) + var/datum/antagonist/bloodsucker/target_bloodsucker = brainmob.mind.has_antag_datum(/datum/antagonist/bloodsucker) + if(target_bloodsucker.bloodsucker_blood_volume >= OOZELING_MIN_REVIVE_BLOOD_THRESHOLD) + addtimer(CALLBACK(src, PROC_REF(rebuild_body), null, FALSE), 30 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_DELETE_ME) + target_bloodsucker.bloodsucker_blood_volume -= (OOZELING_MIN_REVIVE_BLOOD_THRESHOLD * 0.5) + + rebuilt = FALSE + victim.transfer_observers_to(src) + Remove(victim) + qdel(victim) + +/obj/item/organ/internal/brain/slime/proc/do_steam_effects(turf/loc) + var/datum/effect_system/steam_spread/steam = new() + steam.set_up(10, FALSE, loc) + steam.start() + +/////// +/// CHECK FOR REPAIR SECTION +/// Makes it so that when a slime's core has plasma poured on it, it builds a new body and moves the brain into it. + +/obj/item/organ/internal/brain/slime/check_for_repair(obj/item/item, mob/user) + if(damage && item.is_drainable() && item.reagents.has_reagent(/datum/reagent/toxin/plasma)) //attempt to heal the brain + if (item.reagents.get_reagent_amount(/datum/reagent/toxin/plasma) < 100) + user.balloon_alert(user, "too little plasma!") + return FALSE + + user.visible_message( + span_notice("[user] starts to slowly pour the contents of [item] onto [src]. It seems to bubble and roil, beginning to stretch its cytoskeleton outwards..."), + span_notice("You start to slowly pour the contents of [item] onto [src]. It seems to bubble and roil, beginning to stretch its membrane outwards..."), + span_hear("You hear bubbling.") + ) + + if(!do_after(user, 30 SECONDS, src)) + to_chat(user, span_warning("You failed to pour the contents of [item] onto [src]!")) + return FALSE + + if (item.reagents.get_reagent_amount(/datum/reagent/toxin/plasma) < 100) // minor exploit but might as well patch it + user.balloon_alert(user, "too little plasma!") + return FALSE + + user.visible_message( + span_notice("[user] pours the contents of [item] onto [src], causing it to form a proper cytoplasm and outer membrane."), + span_notice("You pour the contents of [item] onto [src], causing it to form a proper cytoplasm and outer membrane."), + span_hear("You hear a splat.") + ) + + item.reagents.remove_reagent(/datum/reagent/toxin/plasma, 100) + rebuild_body(user) + return TRUE + return ..() + +/obj/item/organ/internal/brain/slime/proc/drop_items_to_ground(turf/turf) + for(var/atom/movable/item as anything in stored_items) + item.forceMove(turf) + stored_items.Cut() + +/obj/item/organ/internal/brain/slime/proc/rebuild_body(mob/user, nugget = TRUE) as /mob/living/carbon/human + RETURN_TYPE(/mob/living/carbon/human) + if(rebuilt) + return owner + set_organ_damage(-maxHealth) // heals the brain fully + + if(gps_active) // making sure the gps signal is removed if it's active on revival + gps_active = FALSE + qdel(GetComponent(/datum/component/gps)) + + //we have the plasma. we can rebuild them. + brainmob?.mind?.grab_ghost() + if(isnull(brainmob)) + user?.balloon_alert(user, "This brain is not a viable candidate for repair!") + return null + if(isnull(brainmob.stored_dna)) + user?.balloon_alert(user, "This brain does not contain any dna!") + return null + if(isnull(brainmob.client)) + user?.balloon_alert(user, "This brain does not contain a mind!") + return null + var/mob/living/carbon/human/new_body = new /mob/living/carbon/human(drop_location()) + + rebuilt = TRUE + brainmob.client?.prefs?.safe_transfer_prefs_to(new_body) + new_body.underwear = "Nude" + new_body.undershirt = "Nude" + new_body.socks = "Nude" + stored_dna.transfer_identity(new_body, transfer_SE = TRUE) + new_body.real_name = new_body.dna.real_name + new_body.name = new_body.dna.real_name + new_body.updateappearance(mutcolor_update = TRUE) + new_body.domutcheck() + new_body.forceMove(drop_location()) + if(!nugget) + new_body.set_nutrition(NUTRITION_LEVEL_FED) + new_body.blood_volume = nugget ? (BLOOD_VOLUME_SAFE + 60) : BLOOD_VOLUME_NORMAL + REMOVE_TRAIT(new_body, TRAIT_NO_TRANSFORM, REF(src)) + if(!QDELETED(brainmob)) + SSquirks.AssignQuirks(new_body, brainmob.client) + var/obj/item/organ/internal/brain/new_body_brain = new_body.get_organ_slot(ORGAN_SLOT_BRAIN) + qdel(new_body_brain) + forceMove(new_body) + Insert(new_body) + if(nugget) + for(var/obj/item/bodypart as anything in new_body.bodyparts) + if(istype(bodypart, /obj/item/bodypart/chest)) + continue + qdel(bodypart) + new_body.visible_message(span_warning("[new_body]'s torso \"forms\" from [new_body.p_their()] core, yet to form the rest.")) + to_chat(owner, span_purple("Your torso fully forms out of your core, yet to form the rest.")) + else + new_body.visible_message(span_warning("[new_body]'s body fully forms from [new_body.p_their()] core!")) + to_chat(owner, span_purple("Your body fully forms from your core!")) + + membrane_mur.Remove(brainmob) + brainmob?.mind?.transfer_to(new_body) + new_body.grab_ghost() + transfer_observers_to(new_body) + + drop_items_to_ground(new_body.drop_location()) + return new_body + +/obj/item/organ/internal/brain/synth + name = "compact positronic brain" + slot = ORGAN_SLOT_BRAIN + zone = BODY_ZONE_HEAD + organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES + maxHealth = 2 * STANDARD_ORGAN_THRESHOLD + desc = "A cube of shining metal, four inches to a side and covered in shallow grooves. It has an IPC serial number engraved on the top. It is usually slotted into the chest of synthetic crewmembers." + icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' + icon_state = "posibrain-ipc" + /// The last time (in ticks) a message about brain damage was sent. Don't touch. + var/last_message_time = 0 + +/obj/item/organ/internal/brain/synth/on_insert(mob/living/carbon/brain_owner) + . = ..() + + if(brain_owner.stat != DEAD || !ishuman(brain_owner)) + return + + var/mob/living/carbon/human/user_human = brain_owner + if(HAS_TRAIT(user_human, TRAIT_REVIVES_BY_HEALING) && user_human.health > SYNTH_BRAIN_WAKE_THRESHOLD) + if(!HAS_TRAIT(user_human, TRAIT_DEFIB_BLACKLISTED)) + user_human.revive(FALSE) + +/obj/item/organ/internal/brain/synth/emp_act(severity) // EMP act against the posi, keep the cap far below the organ health + . = ..() + + if(!owner || . & EMP_PROTECT_SELF) + return + + if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. + COOLDOWN_START(src, severe_cooldown, 10 SECONDS) + + switch(severity) + if(EMP_HEAVY) + to_chat(owner, span_warning("01001001 00100111 01101101 00100000 01100110 01110101 01100011 01101011 01100101 01100100 00101110")) + apply_organ_damage(SYNTH_ORGAN_HEAVY_EMP_DAMAGE, SYNTH_EMP_BRAIN_DAMAGE_MAXIMUM, required_organtype = ORGAN_ROBOTIC) + if(EMP_LIGHT) + to_chat(owner, span_warning("Alert: Electromagnetic damage taken in central processing unit. Error Code: 401-YT")) + apply_organ_damage(SYNTH_ORGAN_LIGHT_EMP_DAMAGE, SYNTH_EMP_BRAIN_DAMAGE_MAXIMUM, required_organtype = ORGAN_ROBOTIC) + +/obj/item/organ/internal/brain/synth/apply_organ_damage(damage_amount, maximumm, required_organtype) + . = ..() + + if(owner && damage > 0 && (world.time - last_message_time) > SYNTH_BRAIN_DAMAGE_MESSAGE_INTERVAL) + last_message_time = world.time + + if(damage > BRAIN_DAMAGE_SEVERE) + to_chat(owner, span_warning("Alre: re oumtnin ilir tocorr:pa ni ne:cnrrpiioruloomatt cessingode: P1_1-H")) + return + + if(damage > BRAIN_DAMAGE_MILD) + to_chat(owner, span_warning("Alert: Minor corruption in central processing unit. Error Code: 001-HP")) + +/* +/obj/item/organ/internal/brain/synth/circuit + name = "compact AI circuit" + desc = "A compact and extremely complex circuit, perfectly dimensioned to fit in the same slot as a synthetic-compatible positronic brain. It is usually slotted into the chest of synthetic crewmembers." + icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' + icon_state = "circuit-occupied" + inhand_icon_state = "electronic" + lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi' +*/ + +/obj/item/organ/internal/brain/synth/mmi + name = "compact man-machine interface" + desc = "A compact man-machine interface, perfectly dimensioned to fit in the same slot as a synthetic-compatible positronic brain. Unfortunately, the brain seems to be permanently attached to the circuitry, and it seems relatively sensitive to it's environment. It is usually slotted into the chest of synthetic crewmembers." + icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' + icon_state = "mmi-ipc" + diff --git a/monkestation/code/modules/surgery/organs/internal/ears.dm b/monkestation/code/modules/surgery/organs/internal/ears.dm index a448af9db39c..25ff00d61ae6 100644 --- a/monkestation/code/modules/surgery/organs/internal/ears.dm +++ b/monkestation/code/modules/surgery/organs/internal/ears.dm @@ -3,3 +3,57 @@ desc = "An odd sort of microphone that looks grown, rather than built." icon = 'monkestation/icons/obj/medical/organs/organs.dmi' icon_state = "ears-clock" + +/obj/item/organ/internal/ears/jelly + name = "core audiosomes" + zone = BODY_ZONE_CHEST + organ_flags = ORGAN_UNREMOVABLE + +/obj/item/organ/internal/ears/synth + name = "auditory sensors" + icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' + icon_state = "ears-ipc" + desc = "A pair of microphones intended to be installed in an IPC or Synthetics head, that grant the ability to hear." + zone = BODY_ZONE_HEAD + slot = ORGAN_SLOT_EARS + gender = PLURAL + maxHealth = 1 * STANDARD_ORGAN_THRESHOLD + organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES + +/obj/item/organ/internal/ears/synth/emp_act(severity) + . = ..() + + if(!owner || . & EMP_PROTECT_SELF) + return + + if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. + COOLDOWN_START(src, severe_cooldown, 10 SECONDS) + + switch(severity) + if(EMP_HEAVY) + owner.set_jitter_if_lower(SYNTH_BAD_EFFECT_DURATION * SYNTH_HEAVY_EMP_MULTIPLIER) + owner.set_dizzy_if_lower(SYNTH_BAD_EFFECT_DURATION * SYNTH_HEAVY_EMP_MULTIPLIER) + adjustEarDamage(SYNTH_ORGAN_HEAVY_EMP_DAMAGE, SYNTH_DEAF_STACKS) + to_chat(owner, span_warning("Alert: Null feedback from auditory sensors detected, seek maintenance immediately. Error Code: AS-105")) + + if(EMP_LIGHT) + owner.set_jitter_if_lower(SYNTH_BAD_EFFECT_DURATION) + owner.set_dizzy_if_lower(SYNTH_BAD_EFFECT_DURATION) + adjustEarDamage(SYNTH_ORGAN_LIGHT_EMP_DAMAGE, SYNTH_DEAF_STACKS) + to_chat(owner, span_warning("Alert: Anomalous feedback from auditory sensors detected. Error Code: AS-50")) + +/datum/design/synth_ears + name = "Auditory Sensors" + desc = "A pair of microphones intended to be installed in an IPC or Synthetics head, that grant the ability to hear." + id = "synth_ears" + build_type = PROTOLATHE | AWAY_LATHE | MECHFAB + construction_time = 4 SECONDS + materials = list( + /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, + /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, + ) + build_path = /obj/item/organ/internal/ears/synth + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_1 + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE diff --git a/monkestation/code/modules/surgery/organs/internal/eyes.dm b/monkestation/code/modules/surgery/organs/internal/eyes.dm index a70dba2e3f8f..b49eff604977 100644 --- a/monkestation/code/modules/surgery/organs/internal/eyes.dm +++ b/monkestation/code/modules/surgery/organs/internal/eyes.dm @@ -50,3 +50,51 @@ name = "tundra moth eyes" eye_icon_state = "tundramotheyes" icon_state = "eyeballs-tundramoth" + +/obj/item/organ/internal/eyes/jelly + name = "photosensitive eyespots" + zone = BODY_ZONE_CHEST + organ_flags = ORGAN_UNREMOVABLE + +/obj/item/organ/internal/eyes/roundstartslime + name = "photosensitive eyespots" + zone = BODY_ZONE_CHEST + organ_flags = ORGAN_UNREMOVABLE + +/obj/item/organ/internal/eyes/synth + name = "optical sensors" + icon_state = "cybernetic_eyeballs" + desc = "A very basic set of optical sensors with no extra vision modes or functions." + maxHealth = 1 * STANDARD_ORGAN_THRESHOLD + flash_protect = FLASH_PROTECTION_WELDER + organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES | ORGAN_DOESNT_PROTECT_AGAINST_CONVERSION + +/obj/item/organ/internal/eyes/synth/emp_act(severity) + . = ..() + + if(!owner || . & EMP_PROTECT_SELF) + return + + switch(severity) + if(EMP_HEAVY) + to_chat(owner, span_warning("Alert:Severe electromagnetic interference clouds your optics with static. Error Code: I-CS6")) + apply_organ_damage(SYNTH_ORGAN_HEAVY_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) + if(EMP_LIGHT) + to_chat(owner, span_warning("Alert: Mild interference clouds your optics with static. Error Code: I-CS0")) + apply_organ_damage(SYNTH_ORGAN_LIGHT_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) + +/datum/design/synth_eyes + name = "Optical Sensors" + desc = "A very basic set of optical sensors with no extra vision modes or functions." + id = "synth_eyes" + build_type = PROTOLATHE | AWAY_LATHE | MECHFAB + construction_time = 4 SECONDS + materials = list( + /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, + /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, + ) + build_path = /obj/item/organ/internal/eyes/synth + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_1 + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE diff --git a/monkestation/code/modules/surgery/organs/internal/heart.dm b/monkestation/code/modules/surgery/organs/internal/heart.dm index ea75864d8089..02ba08878c8a 100644 --- a/monkestation/code/modules/surgery/organs/internal/heart.dm +++ b/monkestation/code/modules/surgery/organs/internal/heart.dm @@ -5,3 +5,167 @@ icon_state = "heart-clock" organ_flags = ORGAN_SYNTHETIC status = ORGAN_ROBOTIC + +///The rate at which slimes regenerate their jelly normally +#define JELLY_REGEN_RATE 1.5 +///The rate at which slimes regenerate their jelly when they completely run out of it and start taking damage, usually after having cannibalized all their limbs already +#define JELLY_REGEN_RATE_EMPTY 2.5 +///The blood volume at which slimes begin to start losing nutrition -- so that IV drips can work for blood deficient slimes +#define BLOOD_VOLUME_LOSE_NUTRITION 550 + + +/obj/item/organ/internal/heart/slime + name = "slime heart" + + heart_bloodtype = /datum/blood_type/slime + var/datum/action/innate/regenerate_limbs/regenerate_limbs + +/obj/item/organ/internal/heart/slime/Insert(mob/living/carbon/receiver, special, drop_if_replaced) + . = ..() + regenerate_limbs = new + regenerate_limbs.Grant(receiver) + RegisterSignal(receiver, COMSIG_HUMAN_ON_HANDLE_BLOOD, PROC_REF(slime_blood)) + +/obj/item/organ/internal/heart/slime/Remove(mob/living/carbon/heartless, special) + . = ..() + if(regenerate_limbs) + regenerate_limbs.Remove(heartless) + qdel(regenerate_limbs) + UnregisterSignal(heartless, COMSIG_HUMAN_ON_HANDLE_BLOOD) + +/obj/item/organ/internal/heart/slime/proc/slime_blood(mob/living/carbon/human/slime, seconds_per_tick, times_fired) + SIGNAL_HANDLER + + if(slime.stat == DEAD) + return NONE + + . = HANDLE_BLOOD_NO_NUTRITION_DRAIN|HANDLE_BLOOD_NO_EFFECTS + + if(slime.blood_volume <= 0) + slime.blood_volume += JELLY_REGEN_RATE_EMPTY * seconds_per_tick + slime.adjustBruteLoss(2.5 * seconds_per_tick) + to_chat(slime, span_danger("You feel empty!")) + + if(slime.blood_volume < BLOOD_VOLUME_NORMAL) + if(slime.nutrition >= NUTRITION_LEVEL_STARVING) + slime.blood_volume += JELLY_REGEN_RATE * seconds_per_tick + if(slime.blood_volume <= BLOOD_VOLUME_LOSE_NUTRITION) // don't lose nutrition if we are above a certain threshold, otherwise slimes on IV drips will still lose nutrition + slime.adjust_nutrition(-1.25 * seconds_per_tick) + + if(HAS_TRAIT(slime, TRAIT_BLOOD_DEFICIENCY)) + var/datum/quirk/blooddeficiency/blooddeficiency = slime.get_quirk(/datum/quirk/blooddeficiency) + blooddeficiency?.lose_blood(slime, seconds_per_tick) + + if(slime.blood_volume < BLOOD_VOLUME_OKAY) + if(SPT_PROB(2.5, seconds_per_tick)) + to_chat(slime, span_danger("You feel drained!")) + + if(slime.blood_volume < BLOOD_VOLUME_BAD) + Cannibalize_Body(slime) + + regenerate_limbs?.build_all_button_icons(UPDATE_BUTTON_STATUS) + return . + +/obj/item/organ/internal/heart/slime/proc/Cannibalize_Body(mob/living/carbon/human/H) + var/list/limbs_to_consume = list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) - H.get_missing_limbs() + var/obj/item/bodypart/consumed_limb + if(!length(limbs_to_consume)) + H.losebreath++ + return + if(H.num_legs) //Legs go before arms + limbs_to_consume -= list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM) + consumed_limb = H.get_bodypart(pick(limbs_to_consume)) + consumed_limb.drop_limb() + to_chat(H, span_userdanger("Your [consumed_limb] is drawn back into your body, unable to maintain its shape!")) + qdel(consumed_limb) + H.blood_volume += 20 + +/datum/action/innate/regenerate_limbs + name = "Regenerate Limbs" + check_flags = AB_CHECK_CONSCIOUS + button_icon_state = "slimeheal" + button_icon = 'icons/mob/actions/actions_slime.dmi' + background_icon_state = "bg_alien" + overlay_icon_state = "bg_alien_border" + +/datum/action/innate/regenerate_limbs/IsAvailable(feedback = FALSE) + . = ..() + if(!.) + return + var/mob/living/carbon/human/H = owner + var/list/limbs_to_heal = H.get_missing_limbs() + if(!length(limbs_to_heal)) + return FALSE + if(H.blood_volume >= BLOOD_VOLUME_OKAY+40) + return TRUE + +/datum/action/innate/regenerate_limbs/Activate() + var/mob/living/carbon/human/H = owner + var/list/limbs_to_heal = H.get_missing_limbs() + if(!length(limbs_to_heal)) + to_chat(H, span_notice("You feel intact enough as it is.")) + return + to_chat(H, span_notice("You focus intently on your missing [length(limbs_to_heal) >= 2 ? "limbs" : "limb"]...")) + if(H.blood_volume >= 40*length(limbs_to_heal)+BLOOD_VOLUME_OKAY) + H.regenerate_limbs() + H.blood_volume -= 40*length(limbs_to_heal) + to_chat(H, span_notice("...and after a moment you finish reforming!")) + return + else if(H.blood_volume >= 40)//We can partially heal some limbs + while(H.blood_volume >= BLOOD_VOLUME_OKAY+40) + var/healed_limb = pick(limbs_to_heal) + H.regenerate_limb(healed_limb) + limbs_to_heal -= healed_limb + H.blood_volume -= 40 + to_chat(H, span_warning("...but there is not enough of you to fix everything! You must attain more mass to heal completely!")) + return + to_chat(H, span_warning("...but there is not enough of you to go around! You must attain more mass to heal!")) + +#undef JELLY_REGEN_RATE +#undef JELLY_REGEN_RATE_EMPTY +#undef BLOOD_VOLUME_LOSE_NUTRITION + +/obj/item/organ/internal/heart/synth + name = "hydraulic pump engine" + desc = "An electronic device that handles the hydraulic pumps, powering one's robotic limbs. Without this, synthetics are unable to move." + organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES + icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' + icon_state = "heart-ipc-on" + base_icon_state = "heart-ipc" + maxHealth = 1.5 * STANDARD_ORGAN_THRESHOLD // 1.5x due to synthcode.tm being weird + zone = BODY_ZONE_CHEST + slot = ORGAN_SLOT_HEART + var/last_message_time = 0 + +/obj/item/organ/internal/heart/synth/emp_act(severity) + . = ..() + + if(!owner || . & EMP_PROTECT_SELF) + return + + if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. + COOLDOWN_START(src, severe_cooldown, 10 SECONDS) + + switch(severity) + if(EMP_HEAVY) + to_chat(owner, span_warning("Alert: Main hydraulic pump control has taken severe damage, seek maintenance immediately. Error code: HP300-10.")) + apply_organ_damage(SYNTH_ORGAN_HEAVY_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) + if(EMP_LIGHT) + to_chat(owner, span_warning("Alert: Main hydraulic pump control has taken light damage, seek maintenance immediately. Error code: HP300-05.")) + apply_organ_damage(SYNTH_ORGAN_LIGHT_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) + +/datum/design/synth_heart + name = "Hydraulic Pump Engine" + desc = "An electronic device that handles the hydraulic pumps, powering one's robotic limbs. Without this, synthetics are unable to move." + id = "synth_heart" + build_type = PROTOLATHE | AWAY_LATHE | MECHFAB + construction_time = 4 SECONDS + materials = list( + /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, + /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, + ) + build_path = /obj/item/organ/internal/heart/synth + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_1 + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE diff --git a/monkestation/code/modules/surgery/organs/internal/liver.dm b/monkestation/code/modules/surgery/organs/internal/liver.dm index 8c04708a6c06..fff44411c88d 100644 --- a/monkestation/code/modules/surgery/organs/internal/liver.dm +++ b/monkestation/code/modules/surgery/organs/internal/liver.dm @@ -8,3 +8,58 @@ alcohol_tolerance = 0 liver_resistance = 0 toxTolerance = 1 //while the organ isn't damaged by doing its job, it doesnt do it very well + +/obj/item/organ/internal/liver/slime + name = "endoplasmic reticulum" + zone = BODY_ZONE_CHEST + organ_flags = ORGAN_UNREMOVABLE + organ_traits = list(TRAIT_TOXINLOVER) + +/obj/item/organ/internal/liver/slime/on_life(seconds_per_tick, times_fired) + . = ..() + operated = FALSE + +/obj/item/organ/internal/liver/synth + name = "reagent processing unit" + desc = "An electronic device that processes the beneficial chemicals for the synthetic user." + icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' + icon_state = "liver-ipc" + filterToxins = FALSE //We dont filter them, we're immune to them + zone = BODY_ZONE_CHEST + slot = ORGAN_SLOT_LIVER + maxHealth = 1 * STANDARD_ORGAN_THRESHOLD + organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES + +/obj/item/organ/internal/liver/synth/emp_act(severity) + . = ..() + + if(!owner || . & EMP_PROTECT_SELF) + return + + if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. + COOLDOWN_START(src, severe_cooldown, 10 SECONDS) + + switch(severity) + if(EMP_HEAVY) + to_chat(owner, span_warning("Alert: Critical! Reagent processing unit failure, seek maintenance immediately. Error Code: DR-1k")) + apply_organ_damage(SYNTH_ORGAN_HEAVY_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) + + if(EMP_LIGHT) + to_chat(owner, span_warning("Alert: Reagent processing unit failure, seek maintenance for diagnostic. Error Code: DR-0k")) + apply_organ_damage(SYNTH_ORGAN_LIGHT_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) + +/datum/design/synth_liver + name = "Reagent Processing Unit" + desc = "An electronic device that processes the beneficial chemicals for the synthetic user." + id = "synth_liver" + build_type = PROTOLATHE | AWAY_LATHE | MECHFAB + construction_time = 4 SECONDS + materials = list( + /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, + /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, + ) + build_path = /obj/item/organ/internal/liver/synth + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_1 + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE diff --git a/monkestation/code/modules/surgery/organs/internal/lungs.dm b/monkestation/code/modules/surgery/organs/internal/lungs.dm index 8b0b2f858738..cad2bda9eeb1 100644 --- a/monkestation/code/modules/surgery/organs/internal/lungs.dm +++ b/monkestation/code/modules/surgery/organs/internal/lungs.dm @@ -5,3 +5,62 @@ icon_state = "lungs-clock" organ_flags = ORGAN_SYNTHETIC status = ORGAN_ROBOTIC + +/obj/item/organ/internal/lungs/slime + zone = BODY_ZONE_CHEST + organ_flags = ORGAN_UNREMOVABLE + safe_oxygen_min = 4 //We don't need much oxygen to subsist. + +/obj/item/organ/internal/lungs/slime/on_life(seconds_per_tick, times_fired) + . = ..() + operated = FALSE + +/obj/item/organ/internal/lungs/synth + name = "heatsink" + desc = "A device that transfers generated heat to a fluid medium to cool it down. Required to keep your synthetics cool-headed. It's shape resembles lungs." //Purposefully left the 'fluid medium' ambigious for interpretation of the character, whether it be air or fluid cooling + icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' + icon_state = "lungs-ipc" + safe_nitro_min = 0 + safe_co2_max = 0 + safe_plasma_min = 0 + safe_plasma_max = 0 + safe_oxygen_min = 0 //What are you doing man, dont breathe with those! + safe_oxygen_max = 0 + zone = BODY_ZONE_CHEST + slot = ORGAN_SLOT_LUNGS + maxHealth = 1.5 * STANDARD_ORGAN_THRESHOLD + organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES + +/obj/item/organ/internal/lungs/synth/emp_act(severity) + . = ..() + + if(!owner || . & EMP_PROTECT_SELF) + return + + if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. + COOLDOWN_START(src, severe_cooldown, 10 SECONDS) + + switch(severity) + if(EMP_HEAVY) + to_chat(owner, span_warning("Alert: Critical cooling system failure! Seek maintenance immediately. Error Code: 5H-17")) + owner.adjust_bodytemperature(SYNTH_HEAVY_EMP_TEMPERATURE_POWER * TEMPERATURE_DAMAGE_COEFFICIENT) + + if(EMP_LIGHT) + to_chat(owner, span_warning("Alert: Major cooling system failure!")) + owner.adjust_bodytemperature(SYNTH_LIGHT_EMP_TEMPERATURE_POWER * TEMPERATURE_DAMAGE_COEFFICIENT) + +/datum/design/synth_heatsink + name = "Heatsink" + desc = "A device that transfers generated heat to a fluid medium to cool it down. Required to keep your synthetics cool-headed. It's shape resembles lungs." + id = "synth_lungs" + build_type = PROTOLATHE | AWAY_LATHE | MECHFAB + construction_time = 4 SECONDS + materials = list( + /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, + /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, + ) + build_path = /obj/item/organ/internal/lungs/synth + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_1 + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE diff --git a/monkestation/code/modules/surgery/organs/internal/stomach.dm b/monkestation/code/modules/surgery/organs/internal/stomach.dm index 2ef0eff5e8fc..183b9758b07c 100644 --- a/monkestation/code/modules/surgery/organs/internal/stomach.dm +++ b/monkestation/code/modules/surgery/organs/internal/stomach.dm @@ -18,3 +18,80 @@ organ_flags = ORGAN_SYNTHETIC //max_charge = 7500 //charge = 7500 //old bee code + +/obj/item/organ/internal/stomach/slime + name = "golgi apparatus" + zone = BODY_ZONE_CHEST + organ_flags = ORGAN_UNREMOVABLE + +/obj/item/organ/internal/stomach/slime/on_life(seconds_per_tick, times_fired) + . = ..() + operated = FALSE + +///IPCS NO LONGER ARE PURE ELECTRICAL BEINGS, any attempts to change this outside of Borbop will be denied. Thanks. +/obj/item/organ/internal/stomach/synth + name = "synthetic bio-reactor" + icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' + icon_state = "stomach-ipc" + w_class = WEIGHT_CLASS_NORMAL + zone = BODY_ZONE_CHEST + slot = ORGAN_SLOT_STOMACH + maxHealth = 1 * STANDARD_ORGAN_THRESHOLD + zone = "chest" + slot = "stomach" + desc = "A specialised mini reactor, for synthetic use only. Has a low-power mode to ensure baseline functions. Without this, synthetics are unable to stay powered." + organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES + +/obj/item/organ/internal/stomach/synth/emp_act(severity) + . = ..() + + if(!owner || . & EMP_PROTECT_SELF) + return + + if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. + COOLDOWN_START(src, severe_cooldown, 10 SECONDS) + + switch(severity) + if(EMP_HEAVY) + owner.nutrition = max(0, owner.nutrition - SYNTH_STOMACH_HEAVY_EMP_CHARGE_LOSS) + apply_organ_damage(SYNTH_ORGAN_HEAVY_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) + to_chat(owner, span_warning("Alert: Severe battery discharge!")) + + if(EMP_LIGHT) + owner.nutrition = max(0, owner.nutrition - SYNTH_STOMACH_LIGHT_EMP_CHARGE_LOSS) + apply_organ_damage(SYNTH_ORGAN_LIGHT_EMP_DAMAGE, maxHealth, required_organtype = ORGAN_ROBOTIC) + to_chat(owner, span_warning("Alert: Minor battery discharge!")) + +/datum/design/synth_stomach + name = "Synthetic Bio-Reactor" + desc = "A specialised mini reactor, for synthetic use only. Has a low-power mode to ensure baseline functions. Without this, synthetics are unable to stay powered." + id = "synth_stomach" + build_type = PROTOLATHE | AWAY_LATHE | MECHFAB + construction_time = 4 SECONDS + materials = list( + /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, + /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, + ) + build_path = /obj/item/organ/internal/stomach/synth + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_1 + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE + +/obj/item/organ/internal/stomach/synth/Insert(mob/living/carbon/receiver, special, drop_if_replaced) + . = ..() + RegisterSignal(receiver, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(on_borg_charge)) + +/obj/item/organ/internal/stomach/synth/Remove(mob/living/carbon/stomach_owner, special) + . = ..() + UnregisterSignal(stomach_owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT) + +///Handles charging the synth from borg chargers +/obj/item/organ/internal/stomach/synth/proc/on_borg_charge(datum/source, amount) + SIGNAL_HANDLER + + if(owner.nutrition >= NUTRITION_LEVEL_ALMOST_FULL) + return + + amount /= 50 // Lowers the charging amount so it isn't instant + owner.nutrition = min((owner.nutrition + amount), NUTRITION_LEVEL_ALMOST_FULL) // Makes sure we don't make the synth too full, which would apply the overweight slowdown diff --git a/monkestation/code/modules/surgery/organs/internal/tongue.dm b/monkestation/code/modules/surgery/organs/internal/tongue.dm index c2a45d683494..9d87b87d5cf8 100644 --- a/monkestation/code/modules/surgery/organs/internal/tongue.dm +++ b/monkestation/code/modules/surgery/organs/internal/tongue.dm @@ -38,3 +38,57 @@ /obj/item/organ/internal/tongue/arachnid/get_possible_languages() return ..() + /datum/language/buzzwords + +/obj/item/organ/internal/tongue/jelly + zone = BODY_ZONE_CHEST + organ_flags = ORGAN_UNREMOVABLE + +/obj/item/organ/internal/tongue/jelly/get_possible_languages() + return ..() + /datum/language/slime + +/obj/item/organ/internal/tongue/synth + name = "synthetic voicebox" + desc = "A fully-functional synthetic tongue, encased in soft silicone. Features include high-resolution vocals and taste receptors." + icon = 'monkestation/code/modules/smithing/icons/ipc_organ.dmi' + icon_state = "cybertongue" + say_mod = "beeps" + attack_verb_continuous = list("beeps", "boops") + attack_verb_simple = list("beep", "boop") + modifies_speech = TRUE + taste_sensitivity = 25 // not as good as an organic tongue + maxHealth = 100 //RoboTongue! + zone = BODY_ZONE_HEAD + slot = ORGAN_SLOT_TONGUE + organ_flags = ORGAN_ROBOTIC | ORGAN_SYNTHETIC_FROM_SPECIES + +/obj/item/organ/internal/tongue/synth/get_scream_sound() + return 'monkestation/sound/voice/screams/silicon/scream_silicon.ogg' + +/obj/item/organ/internal/tongue/synth/get_laugh_sound() + return pick( + 'monkestation/sound/voice/laugh/silicon/laugh_siliconE1M0.ogg', + 'monkestation/sound/voice/laugh/silicon/laugh_siliconE1M1.ogg', + 'monkestation/sound/voice/laugh/silicon/laugh_siliconM2.ogg', + ) + +/obj/item/organ/internal/tongue/synth/can_speak_language(language) + return TRUE + +/obj/item/organ/internal/tongue/synth/handle_speech(datum/source, list/speech_args) + speech_args[SPEECH_SPANS] |= SPAN_ROBOT + +/datum/design/synth_tongue + name = "Synthetic Tongue" + desc = "A fully-functional synthetic tongue, encased in soft silicone. Features include high-resolution vocals and taste receptors." + id = "synth_tongue" + build_type = PROTOLATHE | AWAY_LATHE | MECHFAB + construction_time = 4 SECONDS + materials = list( + /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, + /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, + ) + build_path = /obj/item/organ/internal/tongue/synth + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ORGANS_1 + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE diff --git a/monkestation/code/modules/smithing/ipcs/surgeries/robot_brain_healing.dm b/monkestation/code/modules/surgery/robot_brain_healing.dm similarity index 100% rename from monkestation/code/modules/smithing/ipcs/surgeries/robot_brain_healing.dm rename to monkestation/code/modules/surgery/robot_brain_healing.dm diff --git a/monkestation/code/modules/smithing/ipcs/surgeries/robot_chest_repair.dm b/monkestation/code/modules/surgery/robot_chest_repair.dm similarity index 100% rename from monkestation/code/modules/smithing/ipcs/surgeries/robot_chest_repair.dm rename to monkestation/code/modules/surgery/robot_chest_repair.dm diff --git a/monkestation/code/modules/smithing/ipcs/surgeries/robot_healing.dm b/monkestation/code/modules/surgery/robot_healing.dm similarity index 100% rename from monkestation/code/modules/smithing/ipcs/surgeries/robot_healing.dm rename to monkestation/code/modules/surgery/robot_healing.dm diff --git a/monkestation/code/modules/smithing/ipcs/surgeries/steps.dm b/monkestation/code/modules/surgery/steps.dm similarity index 100% rename from monkestation/code/modules/smithing/ipcs/surgeries/steps.dm rename to monkestation/code/modules/surgery/steps.dm diff --git a/monkestation/icons/mob/clothing/worn_modsuit.dmi b/monkestation/icons/mob/clothing/worn_modsuit.dmi index 900e2c30e61e..9d9f37d5a897 100644 Binary files a/monkestation/icons/mob/clothing/worn_modsuit.dmi and b/monkestation/icons/mob/clothing/worn_modsuit.dmi differ diff --git a/monkestation/icons/mob/mod.dmi b/monkestation/icons/mob/mod.dmi index 8f053f47ecaa..caddf21df107 100644 Binary files a/monkestation/icons/mob/mod.dmi and b/monkestation/icons/mob/mod.dmi differ diff --git a/monkestation/icons/obj/clothing/modsuits/modsuit.dmi b/monkestation/icons/obj/clothing/modsuits/modsuit.dmi index 3d550c1034cd..9be9e19751f1 100644 Binary files a/monkestation/icons/obj/clothing/modsuits/modsuit.dmi and b/monkestation/icons/obj/clothing/modsuits/modsuit.dmi differ diff --git a/monkestation/icons/obj/device.dmi b/monkestation/icons/obj/device.dmi index 10a21daaf598..669e7a795f2d 100644 Binary files a/monkestation/icons/obj/device.dmi and b/monkestation/icons/obj/device.dmi differ diff --git a/monkestation/code/modules/blueshield/icons/radio.dmi b/monkestation/icons/obj/radio.dmi similarity index 100% rename from monkestation/code/modules/blueshield/icons/radio.dmi rename to monkestation/icons/obj/radio.dmi diff --git a/monkestation/code/modules/blueshield/icons/gun.dmi b/monkestation/icons/obj/weapons/guns/tech9.dmi similarity index 100% rename from monkestation/code/modules/blueshield/icons/gun.dmi rename to monkestation/icons/obj/weapons/guns/tech9.dmi diff --git a/tgstation.dme b/tgstation.dme index 7e012e82b910..7800a0e9d3ca 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -470,6 +470,7 @@ #include "code\__DEFINES\~monkestation\twitch.dm" #include "code\__DEFINES\~monkestation\uplink.dm" #include "code\__DEFINES\~monkestation\virology.dm" +#include "code\__DEFINES\~monkestation\vv.dm" #include "code\__DEFINES\~monkestation\dcs\signals\signals_atom.dm" #include "code\__DEFINES\~monkestation\dcs\signals\signals_blueshift.dm" #include "code\__DEFINES\~monkestation\dcs\signals\signals_carbon.dm" @@ -1393,6 +1394,7 @@ #include "code\datums\elements\climbable.dm" #include "code\datums\elements\connect_loc.dm" #include "code\datums\elements\content_barfer.dm" +#include "code\datums\elements\corrupted_organ.dm" #include "code\datums\elements\crackable.dm" #include "code\datums\elements\crusher_loot.dm" #include "code\datums\elements\cult_eyes.dm" @@ -2946,6 +2948,7 @@ #include "code\modules\antagonists\heretic\moon_lunatic.dm" #include "code\modules\antagonists\heretic\rust_effect.dm" #include "code\modules\antagonists\heretic\transmutation_rune.dm" +#include "code\modules\antagonists\heretic\items\corrupted_organs.dm" #include "code\modules\antagonists\heretic\items\eldritch_flask.dm" #include "code\modules\antagonists\heretic\items\eldritch_painting.dm" #include "code\modules\antagonists\heretic\items\forbidden_book.dm" @@ -2976,6 +2979,7 @@ #include "code\modules\antagonists\heretic\knowledge\starting_lore.dm" #include "code\modules\antagonists\heretic\knowledge\void_lore.dm" #include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_buff.dm" +#include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_curse.dm" #include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_knowledge.dm" #include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_map.dm" #include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_moodlets.dm" @@ -5962,6 +5966,7 @@ #include "monkestation\code\datums\components\carbon_sprint.dm" #include "monkestation\code\datums\components\charge_adjuster.dm" #include "monkestation\code\datums\components\crafting.dm" +#include "monkestation\code\datums\components\gift_item.dm" #include "monkestation\code\datums\components\gps.dm" #include "monkestation\code\datums\components\irradiated.dm" #include "monkestation\code\datums\components\lock_on_cursor.dm" @@ -6016,6 +6021,7 @@ #include "monkestation\code\datums\wires\mulebot.dm" #include "monkestation\code\datums\wires\particle_accelerator.dm" #include "monkestation\code\datums\wires\vending.dm" +#include "monkestation\code\game\alternate_appearance.dm" #include "monkestation\code\game\atom.dm" #include "monkestation\code\game\say.dm" #include "monkestation\code\game\sound.dm" @@ -6072,6 +6078,7 @@ #include "monkestation\code\game\objects\items\cirno_plush.dm" #include "monkestation\code\game\objects\items\emags.dm" #include "monkestation\code\game\objects\items\gravity_gun.dm" +#include "monkestation\code\game\objects\items\holosign_creator.dm" #include "monkestation\code\game\objects\items\jukebox_beacon.dm" #include "monkestation\code\game\objects\items\miningweapons.dm" #include "monkestation\code\game\objects\items\mop.dm" @@ -6120,10 +6127,10 @@ #include "monkestation\code\game\objects\items\guns\SRN.dm" #include "monkestation\code\game\objects\items\guns\wt_ammo.dm" #include "monkestation\code\game\objects\items\implants\hardlight.dm" -#include "monkestation\code\game\objects\items\objects\items\robot\robot_upgrades.dm" #include "monkestation\code\game\objects\items\rayne_corp\rayne_lantern.dm" #include "monkestation\code\game\objects\items\rayne_corp\rayne_mender.dm" #include "monkestation\code\game\objects\items\robot\items\hypo.dm" +#include "monkestation\code\game\objects\items\robot\items\robot_upgrades.dm" #include "monkestation\code\game\objects\items\stacks\tile_types.dm" #include "monkestation\code\game\objects\items\storage\book.dm" #include "monkestation\code\game\objects\items\storage\boxes.dm" @@ -6184,6 +6191,7 @@ #include "monkestation\code\modules\admin\smites\where_are_your_fingers.dm" #include "monkestation\code\modules\admin\verbs\getlogs.dm" #include "monkestation\code\modules\admin\verbs\kick_player_by_ckey.dm" +#include "monkestation\code\modules\admin\verbs\mapping.dm" #include "monkestation\code\modules\admin\verbs\selectequipment.dm" #include "monkestation\code\modules\admin\verbs\spawn_mixtape.dm" #include "monkestation\code\modules\admin\verbs\tracy.dm" @@ -6202,6 +6210,7 @@ #include "monkestation\code\modules\and_roll_credits\_credits.dm" #include "monkestation\code\modules\and_roll_credits\credits_subsystem.dm" #include "monkestation\code\modules\and_roll_credits\episode_names.dm" +#include "monkestation\code\modules\antagonist\blob\blob_antag.dm" #include "monkestation\code\modules\antagonists\_common\antag_datum.dm" #include "monkestation\code\modules\antagonists\_common\antag_hud.dm" #include "monkestation\code\modules\antagonists\abductor\abductor.dm" @@ -6413,6 +6422,7 @@ #include "monkestation\code\modules\antagonists\monster_hunters\monsters\monster_effects\killer_rabbit_effects.dm" #include "monkestation\code\modules\antagonists\monster_hunters\monsters\monster_effects\white_rabbit.dm" #include "monkestation\code\modules\antagonists\monster_hunters\monsters\monster_powers\killer_rabbit_powers.dm" +#include "monkestation\code\modules\antagonists\monster_hunters\tools\blood_vial.dm" #include "monkestation\code\modules\antagonists\monster_hunters\tools\bnuuy_mask.dm" #include "monkestation\code\modules\antagonists\monster_hunters\tools\jack_bomb.dm" #include "monkestation\code\modules\antagonists\monster_hunters\tools\rabbit_eye.dm" @@ -6551,6 +6561,7 @@ #include "monkestation\code\modules\assembly\flash.dm" #include "monkestation\code\modules\asset_cache\assets\botanical_lexicon.dm" #include "monkestation\code\modules\asset_cache\assets\chicken_book.dm" +#include "monkestation\code\modules\asset_cache\assets\job_icons.dm" #include "monkestation\code\modules\asset_cache\assets\loadout_store.dm" #include "monkestation\code\modules\atmospherics\machinery\air_alarm\air_alarm_ac.dm" #include "monkestation\code\modules\balloon_alert\balloon_alert.dm" @@ -6645,9 +6656,7 @@ #include "monkestation\code\modules\blueshield\closet.dm" #include "monkestation\code\modules\blueshield\clothing.dm" #include "monkestation\code\modules\blueshield\gun.dm" -#include "monkestation\code\modules\blueshield\job.dm" #include "monkestation\code\modules\blueshield\landmarks.dm" -#include "monkestation\code\modules\blueshield\radio.dm" #include "monkestation\code\modules\blueshield\trim.dm" #include "monkestation\code\modules\blueshield\devices\crew_monitor.dm" #include "monkestation\code\modules\blueshield\devices\sensor.dm" @@ -7075,6 +7084,7 @@ #include "monkestation\code\modules\client\preferences\inventory.dm" #include "monkestation\code\modules\client\preferences\loadout_override_preference.dm" #include "monkestation\code\modules\client\preferences\multiline_preferences.dm" +#include "monkestation\code\modules\client\preferences\overflow.dm" #include "monkestation\code\modules\client\preferences\prude.dm" #include "monkestation\code\modules\client\preferences\runechat.dm" #include "monkestation\code\modules\client\preferences\show_roundend_credits.dm" @@ -7341,8 +7351,10 @@ #include "monkestation\code\modules\job_xp\milestones\botany_milestones.dm" #include "monkestation\code\modules\job_xp\preferences\base_preferences.dm" #include "monkestation\code\modules\job_xp\preferences\xp_handlers.dm" +#include "monkestation\code\modules\jobs\departments\departments.dm" #include "monkestation\code\modules\jobs\job_types\_job.dm" #include "monkestation\code\modules\jobs\job_types\barber.dm" +#include "monkestation\code\modules\jobs\job_types\blueshield.dm" #include "monkestation\code\modules\jobs\job_types\brig_physician.dm" #include "monkestation\code\modules\jobs\job_types\candysalesman.dm" #include "monkestation\code\modules\jobs\job_types\chaplain.dm" @@ -7361,7 +7373,6 @@ #include "monkestation\code\modules\jobs\job_types\skeleton.dm" #include "monkestation\code\modules\jobs\job_types\virologist.dm" #include "monkestation\code\modules\jobs\job_types\yellowclown.dm" -#include "monkestation\code\modules\jobs\job_types\departments\departments.dm" #include "monkestation\code\modules\jobs\job_types\spawner\bar_drone.dm" #include "monkestation\code\modules\library\bookcase.dm" #include "monkestation\code\modules\library\skill_learning\job_skillchips\shaft_miner.dm" @@ -7605,8 +7616,11 @@ #include "monkestation\code\modules\mob\living\carbon\human\species_type\floran.dm" #include "monkestation\code\modules\mob\living\carbon\human\species_type\flypeople.dm" #include "monkestation\code\modules\mob\living\carbon\human\species_type\goblin.dm" +#include "monkestation\code\modules\mob\living\carbon\human\species_type\ipc.dm" #include "monkestation\code\modules\mob\living\carbon\human\species_type\lizardpeople.dm" +#include "monkestation\code\modules\mob\living\carbon\human\species_type\oozeling.dm" #include "monkestation\code\modules\mob\living\carbon\human\species_type\plasmamen.dm" +#include "monkestation\code\modules\mob\living\carbon\human\species_type\satyr.dm" #include "monkestation\code\modules\mob\living\carbon\human\species_type\simian.dm" #include "monkestation\code\modules\mob\living\carbon\human\species_type\skeletons.dm" #include "monkestation\code\modules\mob\living\carbon\human\species_type\teratoma.dm" @@ -7822,9 +7836,11 @@ #include "monkestation\code\modules\power\singularity\particle_accelerator\particle_controller.dm" #include "monkestation\code\modules\power\singularity\particle_accelerator\particle_emitter.dm" #include "monkestation\code\modules\projectiles\gun.dm" +#include "monkestation\code\modules\projectiles\pins.dm" #include "monkestation\code\modules\projectiles\ammunition\_ammunition.dm" #include "monkestation\code\modules\projectiles\guns\ballistic\revolver.dm" #include "monkestation\code\modules\projectiles\guns\ballistic\ryanecorp_whispering_jester.dm" +#include "monkestation\code\modules\projectiles\guns\energy\stun.dm" #include "monkestation\code\modules\projectiles\guns\special\meat_hook.dm" #include "monkestation\code\modules\projectiles\projectile\spells.dm" #include "monkestation\code\modules\projectiles\projectile\bullets\c45_caseless.dm" @@ -7893,9 +7909,7 @@ #include "monkestation\code\modules\ranching\mutations\tier3.dm" #include "monkestation\code\modules\ranching\name_tags\name_tag.dm" #include "monkestation\code\modules\ranching\satyr\abilities.dm" -#include "monkestation\code\modules\ranching\satyr\bodyparts.dm" #include "monkestation\code\modules\ranching\satyr\external_organs.dm" -#include "monkestation\code\modules\ranching\satyr\species.dm" #include "monkestation\code\modules\ranching\satyr\accessories\prefs.dm" #include "monkestation\code\modules\ranching\satyr\accessories\sprites\fluff.dm" #include "monkestation\code\modules\ranching\satyr\accessories\sprites\horns.dm" @@ -8090,15 +8104,8 @@ #include "monkestation\code\modules\smithing\assembly_bench\assembly_bench.dm" #include "monkestation\code\modules\smithing\assembly_bench\assembly_recipes\_base_recipe.dm" #include "monkestation\code\modules\smithing\assembly_bench\assembly_recipes\weapons.dm" -#include "monkestation\code\modules\smithing\ipcs\species.dm" -#include "monkestation\code\modules\smithing\ipcs\body\base_bodyparts.dm" -#include "monkestation\code\modules\smithing\ipcs\body\internal_organs.dm" #include "monkestation\code\modules\smithing\ipcs\reagents\medical_supplies.dm" #include "monkestation\code\modules\smithing\ipcs\reagents\reagents.dm" -#include "monkestation\code\modules\smithing\ipcs\surgeries\robot_brain_healing.dm" -#include "monkestation\code\modules\smithing\ipcs\surgeries\robot_chest_repair.dm" -#include "monkestation\code\modules\smithing\ipcs\surgeries\robot_healing.dm" -#include "monkestation\code\modules\smithing\ipcs\surgeries\steps.dm" #include "monkestation\code\modules\smithing\items\clothing.dm" #include "monkestation\code\modules\smithing\items\merged_material.dm" #include "monkestation\code\modules\smithing\machines\arc_forge.dm" @@ -8127,9 +8134,6 @@ #include "monkestation\code\modules\smithing\mining_changes\mineral_datums.dm" #include "monkestation\code\modules\smithing\mining_changes\mineral_sample.dm" #include "monkestation\code\modules\smithing\oozelings\actions.dm" -#include "monkestation\code\modules\smithing\oozelings\species.dm" -#include "monkestation\code\modules\smithing\oozelings\body\bodyparts.dm" -#include "monkestation\code\modules\smithing\oozelings\body\organs.dm" #include "monkestation\code\modules\smithing\research\circuits_and_research.dm" #include "monkestation\code\modules\smithing\TEG\circulators.dm" #include "monkestation\code\modules\smithing\TEG\reagents.dm" @@ -8170,6 +8174,7 @@ #include "monkestation\code\modules\storytellers\antag_rep\helper_procs.dm" #include "monkestation\code\modules\storytellers\converted_events\_base_event.dm" #include "monkestation\code\modules\storytellers\converted_events\event_overrides.dm" +#include "monkestation\code\modules\storytellers\converted_events\solo\blob_infection.dm" #include "monkestation\code\modules\storytellers\converted_events\solo\bloodcult.dm" #include "monkestation\code\modules\storytellers\converted_events\solo\bloodsuckers.dm" #include "monkestation\code\modules\storytellers\converted_events\solo\brother.dm" @@ -8212,6 +8217,10 @@ #include "monkestation\code\modules\surgery\hepatectomy.dm" #include "monkestation\code\modules\surgery\lobectomy.dm" #include "monkestation\code\modules\surgery\nif_debonding.dm" +#include "monkestation\code\modules\surgery\robot_brain_healing.dm" +#include "monkestation\code\modules\surgery\robot_chest_repair.dm" +#include "monkestation\code\modules\surgery\robot_healing.dm" +#include "monkestation\code\modules\surgery\steps.dm" #include "monkestation\code\modules\surgery\advanced\brainwashing.dm" #include "monkestation\code\modules\surgery\advanced\lobotomy.dm" #include "monkestation\code\modules\surgery\advanced\pacification.dm" @@ -8219,7 +8228,10 @@ #include "monkestation\code\modules\surgery\bodyparts\clockwork_bodyparts.dm" #include "monkestation\code\modules\surgery\bodyparts\ethereal_bodyparts.dm" #include "monkestation\code\modules\surgery\bodyparts\floran_bodyparts.dm" +#include "monkestation\code\modules\surgery\bodyparts\ipc_bodyparts.dm" #include "monkestation\code\modules\surgery\bodyparts\monkey_bodyparts.dm" +#include "monkestation\code\modules\surgery\bodyparts\oozeling_bodyparts.dm" +#include "monkestation\code\modules\surgery\bodyparts\satyr_bodyparts.dm" #include "monkestation\code\modules\surgery\bodyparts\teratoma_bodyparts.dm" #include "monkestation\code\modules\surgery\organs\augments.dm" #include "monkestation\code\modules\surgery\organs\autosurgeon.dm" diff --git a/tgui/global.d.ts b/tgui/global.d.ts index b1697d5fcdbc..b64bd4006dbe 100644 --- a/tgui/global.d.ts +++ b/tgui/global.d.ts @@ -51,6 +51,11 @@ type ByondType = { */ TRIDENT: number | null; + /** + * Version of Blink engine of WebView2. Null if N/A. + */ + BLINK: number | null; + /** * True if browser is IE8 or lower. */ @@ -162,6 +167,11 @@ type ByondType = { */ parseJson(text: string): any; + /** + * Downloads a blob, platform-agnostic + */ + saveBlob(blob: Blob, filename: string, ext: string): void; + /** * Sends a message to `/datum/tgui_window` which hosts this window instance. */ @@ -205,4 +215,13 @@ interface Window { Byond: ByondType; __store__: Store; __augmentStack__: (store: Store) => StackAugmentor; + + // IE IndexedDB stuff. + msIndexedDB: IDBFactory; + msIDBTransaction: IDBTransaction; + + // 516 byondstorage API. + hubStorage: Storage; + domainStorage: Storage; + serverStorage: Storage; } diff --git a/tgui/packages/common/keys.ts b/tgui/packages/common/keys.ts index 61b79992b486..abbd6b49bb70 100644 --- a/tgui/packages/common/keys.ts +++ b/tgui/packages/common/keys.ts @@ -25,7 +25,8 @@ export enum KEY { Down = 'Down', End = 'End', Enter = 'Enter', - Escape = 'Esc', + Esc = 'Esc', + Escape = 'Escape', Home = 'Home', Insert = 'Insert', Left = 'Left', @@ -37,3 +38,18 @@ export enum KEY { Tab = 'Tab', Up = 'Up', } + +/** + * ### isEscape + * + * Checks if the user has hit the 'ESC' key on their keyboard. + * There's a weirdness in BYOND where this could be either the string + * 'Escape' or 'Esc' depending on the browser. This function handles + * both cases. + * + * @param key - the key to check, typically from event.key + * @returns true if key is Escape or Esc, false otherwise + */ +export const isEscape = (key: string): boolean => { + return key === KEY.Esc || key === KEY.Escape; +}; diff --git a/tgui/packages/common/storage.js b/tgui/packages/common/storage.ts similarity index 62% rename from tgui/packages/common/storage.js rename to tgui/packages/common/storage.ts index 9a47ecc2b54c..b2564acf36dc 100644 --- a/tgui/packages/common/storage.js +++ b/tgui/packages/common/storage.ts @@ -10,6 +10,11 @@ export const IMPL_MEMORY = 0; export const IMPL_HUB_STORAGE = 1; export const IMPL_INDEXED_DB = 2; +type StorageImplementation = + | typeof IMPL_MEMORY + | typeof IMPL_HUB_STORAGE + | typeof IMPL_INDEXED_DB; + const INDEXED_DB_VERSION = 1; const INDEXED_DB_NAME = 'tgui'; const INDEXED_DB_STORE_NAME = 'storage-v1'; @@ -17,7 +22,15 @@ const INDEXED_DB_STORE_NAME = 'storage-v1'; const READ_ONLY = 'readonly'; const READ_WRITE = 'readwrite'; -const testGeneric = (testFn) => () => { +type StorageBackend = { + impl: StorageImplementation; + get(key: string): Promise; + set(key: string, value: any): Promise; + remove(key: string): Promise; + clear(): Promise; +}; + +const testGeneric = (testFn: () => boolean) => (): boolean => { try { return Boolean(testFn()); } catch { @@ -26,69 +39,76 @@ const testGeneric = (testFn) => () => { }; const testHubStorage = testGeneric( - () => window.hubStorage && window.hubStorage.getItem, + () => window.hubStorage && !!window.hubStorage.getItem, ); // TODO: Remove with 516 // prettier-ignore const testIndexedDb = testGeneric(() => ( (window.indexedDB || window.msIndexedDB) - && (window.IDBTransaction || window.msIDBTransaction) + && !!(window.IDBTransaction || window.msIDBTransaction) )); -class MemoryBackend { +class MemoryBackend implements StorageBackend { + private store: Record; + public impl: StorageImplementation; + constructor() { this.impl = IMPL_MEMORY; this.store = {}; } - async get(key) { + async get(key: string): Promise { return this.store[key]; } - async set(key, value) { + async set(key: string, value: any): Promise { this.store[key] = value; } - async remove(key) { + async remove(key: string): Promise { this.store[key] = undefined; } - async clear() { + async clear(): Promise { this.store = {}; } } -class HubStorageBackend { +class HubStorageBackend implements StorageBackend { + public impl: StorageImplementation; + constructor() { this.impl = IMPL_HUB_STORAGE; } - async get(key) { + async get(key: string): Promise { const value = await window.hubStorage.getItem(key); if (typeof value === 'string') { return JSON.parse(value); } + return undefined; } - set(key, value) { + async set(key: string, value: any): Promise { window.hubStorage.setItem(key, JSON.stringify(value)); } - remove(key) { + async remove(key: string): Promise { window.hubStorage.removeItem(key); } - clear() { + async clear(): Promise { window.hubStorage.clear(); } } -class IndexedDbBackend { - // TODO: Remove with 516 +class IndexedDbBackend implements StorageBackend { + public impl: StorageImplementation; + public dbPromise: Promise; + constructor() { this.impl = IMPL_INDEXED_DB; - /** @type {Promise} */ this.dbPromise = new Promise((resolve, reject) => { const indexedDB = window.indexedDB || window.msIndexedDB; const req = indexedDB.open(INDEXED_DB_NAME, INDEXED_DB_VERSION); @@ -96,7 +116,12 @@ class IndexedDbBackend { try { req.result.createObjectStore(INDEXED_DB_STORE_NAME); } catch (err) { - reject(new Error('Failed to upgrade IDB: ' + req.error)); + reject( + new Error( + 'Failed to upgrade IDB: ' + + (err instanceof Error ? err.message : String(err)), + ), + ); } }; req.onsuccess = () => resolve(req.result); @@ -106,14 +131,14 @@ class IndexedDbBackend { }); } - async getStore(mode) { - // prettier-ignore - return this.dbPromise.then((db) => db + private async getStore(mode: IDBTransactionMode): Promise { + const db = await this.dbPromise; + return db .transaction(INDEXED_DB_STORE_NAME, mode) - .objectStore(INDEXED_DB_STORE_NAME)); + .objectStore(INDEXED_DB_STORE_NAME); } - async get(key) { + async get(key: string): Promise { const store = await this.getStore(READ_ONLY); return new Promise((resolve, reject) => { const req = store.get(key); @@ -122,19 +147,19 @@ class IndexedDbBackend { }); } - async set(key, value) { + async set(key: string, value: any): Promise { // NOTE: We deliberately make this operation transactionless const store = await this.getStore(READ_WRITE); store.put(value, key); } - async remove(key) { + async remove(key: string): Promise { // NOTE: We deliberately make this operation transactionless const store = await this.getStore(READ_WRITE); store.delete(key); } - async clear() { + async clear(): Promise { // NOTE: We deliberately make this operation transactionless const store = await this.getStore(READ_WRITE); store.clear(); @@ -145,7 +170,10 @@ class IndexedDbBackend { * Web Storage Proxy object, which selects the best backend available * depending on the environment. */ -class StorageProxy { +class StorageProxy implements StorageBackend { + private backendPromise: Promise; + public impl: StorageImplementation = IMPL_MEMORY; + constructor() { this.backendPromise = (async () => { if (!Byond.TRIDENT && testHubStorage()) { @@ -166,22 +194,22 @@ class StorageProxy { })(); } - async get(key) { + async get(key: string): Promise { const backend = await this.backendPromise; return backend.get(key); } - async set(key, value) { + async set(key: string, value: any): Promise { const backend = await this.backendPromise; return backend.set(key, value); } - async remove(key) { + async remove(key: string): Promise { const backend = await this.backendPromise; return backend.remove(key); } - async clear() { + async clear(): Promise { const backend = await this.backendPromise; return backend.clear(); } diff --git a/tgui/packages/tgui-panel/chat/renderer.jsx b/tgui/packages/tgui-panel/chat/renderer.jsx index fa04d94f38c6..e19c7471888c 100644 --- a/tgui/packages/tgui-panel/chat/renderer.jsx +++ b/tgui/packages/tgui-panel/chat/renderer.jsx @@ -80,7 +80,7 @@ const createReconnectedNode = () => { const handleImageError = (e) => { setTimeout(() => { /** @type {HTMLImageElement} */ - const node = Byond.BLINK !== null ? e : e.target; + const node = e.target; const attempts = parseInt(node.getAttribute('data-reload-n'), 10) || 0; if (attempts >= IMAGE_RETRY_LIMIT) { logger.error(`failed to load an image after ${attempts} attempts`); @@ -612,13 +612,13 @@ class ChatRenderer { + '\n' + '\n'; // Create and send a nice blob - const blob = new Blob([pageHtml]); + const blob = new Blob([pageHtml], { type: 'text/plain' }); const timestamp = new Date() .toISOString() .substring(0, 19) .replace(/[-:]/g, '') .replace('T', '-'); - window.navigator.msSaveBlob(blob, `ss13-chatlog-${timestamp}.html`); + Byond.saveBlob(blob, `ss13-chatlog-${timestamp}.html`, '.html'); } } diff --git a/tgui/packages/tgui-panel/index.jsx b/tgui/packages/tgui-panel/index.jsx index 95559291ee2d..9f43a3091e25 100644 --- a/tgui/packages/tgui-panel/index.jsx +++ b/tgui/packages/tgui-panel/index.jsx @@ -78,14 +78,8 @@ const setupApp = () => { Byond.subscribe((type, payload) => store.dispatch({ type, payload })); // Unhide the panel - Byond.winset('output', { - 'is-visible': false, - }); - Byond.winset('browseroutput', { - 'is-visible': true, - 'is-disabled': false, - pos: '0x0', - size: '0x0', + Byond.winset('legacy_output_selector', { + left: 'output_browser', }); // Resize the panel to match the non-browser output diff --git a/tgui/packages/tgui-say/TguiSay.tsx b/tgui/packages/tgui-say/TguiSay.tsx index 0a1c9c7e4efa..aee0d12a5cf5 100644 --- a/tgui/packages/tgui-say/TguiSay.tsx +++ b/tgui/packages/tgui-say/TguiSay.tsx @@ -6,7 +6,7 @@ import { byondMessages } from './timers'; import { dragStartHandler } from 'tgui/drag'; import { windowOpen, windowClose, windowSet } from './helpers'; import { BooleanLike } from 'common/react'; -import { KEY } from 'common/keys'; +import { isEscape, KEY } from 'common/keys'; type ByondOpen = { channel: Channel; @@ -252,9 +252,10 @@ export class TguiSay extends Component<{}, State> { this.handleIncrementChannel(); break; - case KEY.Escape: - this.handleClose(); - break; + default: + if (isEscape(event.key)) { + this.handleClose(); + } } } diff --git a/tgui/packages/tgui/interfaces/CassetteManager.tsx b/tgui/packages/tgui/interfaces/CassetteManager.tsx new file mode 100644 index 000000000000..f1d596966167 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CassetteManager.tsx @@ -0,0 +1,97 @@ +import { useBackend } from '../backend'; +import { Section, Table, Button } from '../components'; +import { Window } from '../layouts'; +type CassetteDetails = { + submitter_name: string; + tape_name: string; + reviewed: boolean; + verdict: string; +}; + +type CassetteReviewData = { + // List of submitted cassettes. Each cassette id is in hexID_ckey format. + cassettes: Record; +}; + +export const CassetteManager = (props) => { + const { act, data } = useBackend(); + const { cassettes } = data; + + // Convert cassettes object into an array to iterate our buttons + const cassetteEntries = Object.entries(cassettes); + + return ( + + +
+ act(id)} + /> +
+
+
+ ); +}; + +const CassetteTable = ({ + entries, + onReview, +}: { + entries: [string, CassetteDetails][]; + onReview: (id: string) => void; +}) => { + // If entries is not provided or is empty, show a message instead of the table. + if (!entries || entries.length === 0) { + return Nothing to review. The Jam's must flow.; + } + return ( + + + Cassette ID_CKey + Submitter + Title + Reviewed + Action + + + {entries.map(([id, cassette]) => ( + + {id} + {cassette.submitter_name} + {cassette.tape_name} + {cassette.reviewed ? 'Yes' : 'No'} + + onReview(id)} + /> + + + ))} +
+ ); +}; + +const ReviewButton = ({ + reviewed, + verdict, + onClick, +}: { + reviewed: boolean; + verdict: string; + onClick: () => void; +}) => { + const { buttonText, buttonColor } = reviewed + ? verdict === 'APPROVED' + ? { buttonText: 'Approved', buttonColor: 'good' } + : { buttonText: 'Denied', buttonColor: 'bad' } + : { buttonText: 'Pending', buttonColor: undefined }; + + return ( + + ); +}; diff --git a/tgui/packages/tgui/interfaces/CassetteReview.jsx b/tgui/packages/tgui/interfaces/CassetteReview.jsx index 40475e57ec02..9f68a50218a2 100644 --- a/tgui/packages/tgui/interfaces/CassetteReview.jsx +++ b/tgui/packages/tgui/interfaces/CassetteReview.jsx @@ -4,7 +4,7 @@ import { Section, Box, Button, Flex } from '../components'; export const CassetteReview = (props) => { const { act, data } = useBackend(); - const { ckey, submitters_name, side1, side2 } = data; + const { ckey, submitters_name, side1, side2, reviewed, verdict } = data; return ( @@ -34,8 +34,22 @@ export const CassetteReview = (props) => { - - + {reviewed ? ( + verdict === 'APPROVED' ? ( + +
Approved
+
+ ) : ( + +
Denied
+
+ ) + ) : ( + <> + + + + )}
diff --git a/tgui/packages/tgui/interfaces/KeyComboModal.tsx b/tgui/packages/tgui/interfaces/KeyComboModal.tsx index 6d618fe01214..515f7162fb3f 100644 --- a/tgui/packages/tgui/interfaces/KeyComboModal.tsx +++ b/tgui/packages/tgui/interfaces/KeyComboModal.tsx @@ -1,4 +1,4 @@ -import { KEY } from 'common/keys'; +import { KEY, isEscape } from 'common/keys'; import { useBackend, useLocalState } from '../backend'; import { Autofocus, Box, Button, Section, Stack } from '../components'; import { Window } from '../layouts'; @@ -18,7 +18,7 @@ const isStandardKey = (event: KeyboardEvent): boolean => { event.key !== KEY.Alt && event.key !== KEY.Control && event.key !== KEY.Shift && - event.key !== KEY.Escape + !isEscape(event.key) ); }; @@ -93,7 +93,7 @@ export const KeyComboModal = (props) => { if (event.key === KEY.Enter) { act('submit', { entry: input }); } - if (event.key === KEY.Escape) { + if (isEscape(event.key)) { act('cancel'); } return; @@ -105,7 +105,7 @@ export const KeyComboModal = (props) => { setValue(formatKeyboardEvent(event)); setBinding(false); return; - } else if (event.key === KEY.Escape) { + } else if (isEscape(event.key)) { setValue(init_value); setBinding(false); return; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/monkestation/overflow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/monkestation/overflow.tsx new file mode 100644 index 000000000000..fe9a3beebdaf --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/monkestation/overflow.tsx @@ -0,0 +1,9 @@ +import { CheckboxInput, FeatureToggle } from '../../base'; + +export const verify_overflow: FeatureToggle = { + name: 'Confirm Ready During Job Overflow', + category: 'GAMEPLAY', + description: + 'When enabled, readying up will bring up a confirmation prompt if the current round has a different overflow job.', + component: CheckboxInput, +}; diff --git a/tgui/packages/tgui/interfaces/common/JobToIcon.ts b/tgui/packages/tgui/interfaces/common/JobToIcon.ts index b93296c08c3a..aff948908689 100644 --- a/tgui/packages/tgui/interfaces/common/JobToIcon.ts +++ b/tgui/packages/tgui/interfaces/common/JobToIcon.ts @@ -82,6 +82,7 @@ const ALTTITLES = { Artist: BASEICONS['Assistant'], 'Off-Duty Staff': BASEICONS['Assistant'], 'Off-Duty Crew': BASEICONS['Assistant'], + 'Test Subject': BASEICONS['Assistant'], // Atmospheric Technician - fan 'Life Support Technician': BASEICONS['Atmospheric Technician'], 'Emergency Fire Technician': BASEICONS['Atmospheric Technician'], @@ -98,6 +99,10 @@ const ALTTITLES = { // Blueshield - shield-dog 'Command Bodyguard': BASEICONS['Blueshield'], 'Executive Protection Agent': BASEICONS['Blueshield'], + Bodyguard: BASEICONS['Blueshield'], + 'Revolutionary Repellent': BASEICONS['Blueshield'], + 'Heavily Armed Butler': BASEICONS['Blueshield'], + 'Honor Guard': BASEICONS['Blueshield'], // Botanist - seedling Hydroponicist: BASEICONS['Botanist'], Gardener: BASEICONS['Botanist'], @@ -110,6 +115,8 @@ const ALTTITLES = { 'Station Commander': BASEICONS['Captain'], 'Commanding Officer': BASEICONS['Captain'], 'Site Manager': BASEICONS['Captain'], + 'Criminally Underpaid Babysitter': BASEICONS['Captain'], + Princess: BASEICONS['Captain'], // Cargo Technician - box 'Warehouse Technician': BASEICONS['Cargo Technician'], 'Deck Worker': BASEICONS['Cargo Technician'], @@ -147,6 +154,7 @@ const ALTTITLES = { Butcher: BASEICONS['Cook'], 'Culinary Artist': BASEICONS['Cook'], 'Sous-Chef': BASEICONS['Cook'], + Pizzaiolo: BASEICONS['Cook'], // Coroner - skull Mortician: BASEICONS['Coroner'], 'Funeral Director': BASEICONS['Coroner'], @@ -154,6 +162,7 @@ const ALTTITLES = { Librarian: BASEICONS['Curator'], Journalist: BASEICONS['Curator'], Archivist: BASEICONS['Curator'], + 'Radio Host': BASEICONS['Curator'], // Cyborg - robot Robot: BASEICONS['Cyborg'], Android: BASEICONS['Cyborg'], @@ -198,6 +207,10 @@ const ALTTITLES = { Pantomimist: BASEICONS['Mime'], // Nanotrasen Consultant - clipboard-check 'Nanotrasen Diplomat': BASEICONS['Nanotrasen Consultant'], + 'Corporate Liaison': BASEICONS['Nanotrasen Consultant'], + 'Nanotrasen Fax Operater': BASEICONS['Nanotrasen Consultant'], + 'Nanotrasen Official': BASEICONS['Nanotrasen Consultant'], + 'Nanotrasen Informant': BASEICONS['Nanotrasen Consultant'], // Paramedic - truck-medical 'Emergency Medical Technician': BASEICONS['Paramedic'], 'Search and Rescue Technician': BASEICONS['Paramedic'], @@ -209,6 +222,7 @@ const ALTTITLES = { Convict: BASEICONS['Prisoner'], Felon: BASEICONS['Prisoner'], Inmate: BASEICONS['Prisoner'], + Gamer: BASEICONS['Prisoner'], // Psychologist - brain Psychiatrist: BASEICONS['Psychologist'], Therapist: BASEICONS['Psychologist'], @@ -220,6 +234,7 @@ const ALTTITLES = { 'Supply Foreman': BASEICONS['Quartermaster'], 'Head of Supply': BASEICONS['Quartermaster'], 'Logistics Coordinator': BASEICONS['Quartermaster'], + 'Cargyptian Overseer': BASEICONS['Quartermaster'], // Research Director - user-graduate 'Silicon Administrator': BASEICONS['Research Director'], 'Lead Researcher': BASEICONS['Research Director'], @@ -231,6 +246,7 @@ const ALTTITLES = { 'Biomechanical Engineer': BASEICONS['Roboticist'], 'Mechatronic Engineer': BASEICONS['Roboticist'], 'Apprentice Roboticist': BASEICONS['Roboticist'], + Ripperdoc: BASEICONS['Roboticist'], // Scientist - flask 'Circuitry Designer': BASEICONS['Scientist'], Xenobiologist: BASEICONS['Scientist'], @@ -250,6 +266,8 @@ const ALTTITLES = { Peacekeeper: BASEICONS['Security Officer'], 'Security Cadet': BASEICONS['Security Officer'], // Security Assistant - file-invoice-dollar + 'Correctional Officer': BASEICONS['Security Assistant'], + Deputy: BASEICONS['Security Assistant'], 'Hall Monitor': BASEICONS['Security Assistant'], 'Assistant Officer': BASEICONS['Security Assistant'], 'Professional Snitch': BASEICONS['Security Assistant'], @@ -272,7 +290,9 @@ const ALTTITLES = { 'Engineering Trainee': BASEICONS['Station Engineer'], // Virologist - virus Pathologist: BASEICONS['Virologist'], + 'Fish Doctor': BASEICONS['Virologist'], 'Junior Pathologist': BASEICONS['Virologist'], + 'Plague Doctor': BASEICONS['Virologist'], // Warden - handcuffs 'Brig Sergeant': BASEICONS['Warden'], 'Dispatch Officer': BASEICONS['Warden'], diff --git a/tgui/public/tgui.html b/tgui/public/tgui.html index 7dded63eee27..8937a9443bc4 100644 --- a/tgui/public/tgui.html +++ b/tgui/public/tgui.html @@ -1,673 +1,730 @@ + - - - - - - - - - + + - - + + + + - - - - - - - - -
- - -
- - - A fatal exception has occurred at 002B:C562F1B7 in TGUI. The current - application will be terminated. Please remain calm. Get to the nearest - NTNet workstation and send the copy of the following stack trace to: - https://github.com/Monkestation/Monkestation2.0/. Thank you for your cooperation. - -
- -
- -