diff --git a/SQL/tgstation_schema.sql b/SQL/tgstation_schema.sql index 9696082994db..9991a037ee06 100644 --- a/SQL/tgstation_schema.sql +++ b/SQL/tgstation_schema.sql @@ -802,6 +802,23 @@ CREATE TABLE `subsystem_metrics` ( PRIMARY KEY (`id`) USING BTREE ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +-- +-- Table structure for table `cassettes` +-- +DROP TABLE IF EXISTS `cassettes`; +CREATE TABLE `cassettes` ( + `id` VARCHAR(255) NOT NULL PRIMARY KEY, + `name` VARCHAR(42) NOT NULL, + `desc` VARCHAR(144) NOT NULL, + `status` TINYINT UNSIGNED NOT NULL, + `author_name` VARCHAR(42) NOT NULL, + `author_ckey` VARCHAR(30) NOT NULL, + `front` TEXT NOT NULL DEFAULT '{}', + `back` TEXT NOT NULL DEFAULT '{}', + CONSTRAINT `front` CHECK (json_valid(`front`)), + CONSTRAINT `back` CHECK (json_valid(`back`)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm index caa3b936439a..616e6a194c19 100644 --- a/_maps/map_files/BoxStation/BoxStation.dmm +++ b/_maps/map_files/BoxStation/BoxStation.dmm @@ -1643,16 +1643,6 @@ /obj/effect/spawner/random/structure/crate, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) -"aAY" = ( -/obj/effect/turf_decal/box, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/cable, -/obj/machinery/holopad, -/obj/machinery/duct, -/mob/living/basic/bot/cleanbot/medbay, -/turf/open/floor/iron/white/smooth_large, -/area/station/medical/storage) "aBj" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/line{ @@ -3633,6 +3623,16 @@ }, /turf/open/floor/iron/dark, /area/station/hallway/primary/port) +"bjF" = ( +/obj/effect/turf_decal/trimline/yellow/filled/warning, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/machinery/duct, +/obj/effect/mapping_helpers/mail_sorting/engineering/general, +/obj/structure/disposalpipe/sorting/mail, +/turf/open/floor/iron/dark/side, +/area/station/engineering/break_room) "bjY" = ( /obj/effect/turf_decal/stripes/corner{ dir = 1 @@ -18604,6 +18604,26 @@ /obj/effect/spawner/random/trash/garbage, /turf/open/floor/plating, /area/station/maintenance/department/cargo) +"gbO" = ( +/obj/effect/turf_decal/tile/darkest_green/full, +/obj/structure/table/reinforced/rglass, +/obj/structure/window/reinforced/spawner/directional/east, +/obj/structure/window/reinforced/spawner/directional/south, +/obj/machinery/door/window/brigdoor/left/directional/west{ + name = "Core Modules"; + req_access = list("rd") + }, +/obj/effect/spawner/random/aimodule/neutral{ + pixel_x = -3; + pixel_y = -3 + }, +/obj/effect/spawner/random/aimodule/neutral{ + pixel_y = 3; + pixel_x = 3 + }, +/obj/effect/spawner/random/aimodule/harmless, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai_monitored/turret_protected/ai_upload) "gcp" = ( /obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ dir = 1 @@ -21387,53 +21407,6 @@ /obj/machinery/quantum_server, /turf/open/floor/iron/dark, /area/station/security/bitden) -"gSC" = ( -/obj/structure/sign/painting/library_private{ - pixel_y = -32 - }, -/obj/structure/table/wood, -/obj/item/device/cassette_tape/friday{ - pixel_y = 2; - pixel_x = 9 - }, -/obj/item/device/walkman{ - pixel_y = 7; - pixel_x = -8 - }, -/obj/item/device/walkman{ - pixel_y = 7; - pixel_x = -8 - }, -/obj/item/device/walkman{ - pixel_y = 7; - pixel_x = -8 - }, -/obj/item/device/walkman{ - pixel_y = 7; - pixel_x = -8 - }, -/obj/item/device/walkman{ - pixel_y = 7; - pixel_x = -8 - }, -/obj/item/device/walkman{ - pixel_y = 7; - pixel_x = -8 - }, -/obj/item/device/walkman{ - pixel_y = 7; - pixel_x = -8 - }, -/obj/item/device/walkman{ - pixel_y = 7; - pixel_x = -8 - }, -/obj/item/device/walkman{ - pixel_y = 7; - pixel_x = -8 - }, -/turf/open/floor/iron/vaporwave, -/area/station/service/library/printer) "gSE" = ( /turf/open/floor/carpet/purple, /area/station/command/heads_quarters/rd) @@ -45064,26 +45037,6 @@ /obj/machinery/airalarm/directional/east, /turf/open/floor/iron/dark, /area/station/command/bridge) -"oFm" = ( -/obj/effect/turf_decal/tile/darkest_green/full, -/obj/structure/table/reinforced/rglass, -/obj/structure/window/reinforced/spawner/directional/east, -/obj/structure/window/reinforced/spawner/directional/south, -/obj/machinery/door/window/brigdoor/left/directional/west{ - name = "Core Modules"; - req_access = list("rd") - }, -/obj/effect/spawner/random/aimodule/neutral{ - pixel_x = -3; - pixel_y = -3 - }, -/obj/effect/spawner/random/aimodule/neutral{ - pixel_y = 3; - pixel_x = 3 - }, -/obj/effect/spawner/random/aimodule/harmless, -/turf/open/floor/iron/dark/smooth_large, -/area/station/ai_monitored/turret_protected/ai_upload) "oFy" = ( /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/trimline/red/filled/corner{ @@ -46333,6 +46286,16 @@ /obj/machinery/stasis, /turf/open/floor/iron/white, /area/station/medical/exam_room) +"oZG" = ( +/obj/effect/turf_decal/box, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/machinery/holopad, +/obj/machinery/duct, +/mob/living/basic/bot/cleanbot/medbay, +/turf/open/floor/iron/white/smooth_large, +/area/station/medical/storage) "oZQ" = ( /obj/effect/turf_decal/trimline/neutral/corner{ dir = 4 @@ -55210,6 +55173,53 @@ dir = 4 }, /area/station/cargo/office) +"rYS" = ( +/obj/structure/sign/painting/library_private{ + pixel_y = -32 + }, +/obj/structure/table/wood, +/obj/item/cassette_tape/friday{ + pixel_y = 2; + pixel_x = 9 + }, +/obj/item/device/walkman{ + pixel_y = 7; + pixel_x = -8 + }, +/obj/item/device/walkman{ + pixel_y = 7; + pixel_x = -8 + }, +/obj/item/device/walkman{ + pixel_y = 7; + pixel_x = -8 + }, +/obj/item/device/walkman{ + pixel_y = 7; + pixel_x = -8 + }, +/obj/item/device/walkman{ + pixel_y = 7; + pixel_x = -8 + }, +/obj/item/device/walkman{ + pixel_y = 7; + pixel_x = -8 + }, +/obj/item/device/walkman{ + pixel_y = 7; + pixel_x = -8 + }, +/obj/item/device/walkman{ + pixel_y = 7; + pixel_x = -8 + }, +/obj/item/device/walkman{ + pixel_y = 7; + pixel_x = -8 + }, +/turf/open/floor/iron/vaporwave, +/area/station/service/library/printer) "rZq" = ( /turf/closed/wall/r_wall, /area/station/ai_monitored/turret_protected/aisat/foyer) @@ -59783,6 +59793,15 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /turf/open/floor/engine, /area/station/ai_monitored/turret_protected/aisat/atmos) +"tys" = ( +/obj/effect/turf_decal/trimline/yellow/filled/line{ + dir = 5 + }, +/obj/machinery/rnd/production/circuit_imprinter/department/engineering, +/turf/open/floor/iron/dark/side{ + dir = 5 + }, +/area/station/engineering/break_room) "tyt" = ( /obj/machinery/exodrone_launcher, /obj/item/exodrone, @@ -60851,16 +60870,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/engineering/break_room) -"tQC" = ( -/obj/effect/turf_decal/trimline/yellow/filled/warning, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/cable, -/obj/machinery/duct, -/obj/effect/mapping_helpers/mail_sorting/engineering/general, -/obj/structure/disposalpipe/sorting/mail, -/turf/open/floor/iron/dark/side, -/area/station/engineering/break_room) "tQD" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/spawner/random/trash/graffiti, @@ -60901,22 +60910,6 @@ /obj/effect/spawner/random/trash/garbage, /turf/open/floor/plating, /area/station/maintenance/department/cargo) -"tRj" = ( -/obj/effect/turf_decal/tile/dark_red/full, -/obj/structure/table/reinforced/rglass, -/obj/structure/window/reinforced/spawner/directional/east, -/obj/structure/window/reinforced/spawner/directional/north, -/obj/machinery/door/window/brigdoor/right/directional/west{ - name = "High-Risk Modules"; - req_access = list("rd") - }, -/obj/item/ai_module/reset/purge{ - pixel_y = 4; - pixel_x = -5 - }, -/obj/item/ai_module/reset, -/turf/open/floor/iron/dark/smooth_large, -/area/station/ai_monitored/turret_protected/ai_upload) "tRo" = ( /obj/machinery/door/airlock/maintenance{ name = "Chapel Office Maintenance" @@ -63585,6 +63578,22 @@ /obj/effect/mapping_helpers/requests_console/information, /turf/open/floor/wood, /area/station/command/meeting_room) +"uLm" = ( +/obj/effect/turf_decal/tile/dark_red/full, +/obj/structure/table/reinforced/rglass, +/obj/structure/window/reinforced/spawner/directional/east, +/obj/structure/window/reinforced/spawner/directional/north, +/obj/machinery/door/window/brigdoor/right/directional/west{ + name = "High-Risk Modules"; + req_access = list("rd") + }, +/obj/item/ai_module/reset/purge{ + pixel_y = 4; + pixel_x = -5 + }, +/obj/item/ai_module/reset, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai_monitored/turret_protected/ai_upload) "uLo" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -74246,15 +74255,6 @@ dir = 4 }, /area/station/service/hydroponics) -"yaQ" = ( -/obj/effect/turf_decal/trimline/yellow/filled/line{ - dir = 5 - }, -/obj/machinery/rnd/production/circuit_imprinter/department/engineering, -/turf/open/floor/iron/dark/side{ - dir = 5 - }, -/area/station/engineering/break_room) "yaX" = ( /obj/effect/turf_decal/siding/wood{ dir = 8 @@ -98110,11 +98110,11 @@ btp sYe ufV nga -tRj +uLm uDB hfF sTW -oFm +gbO jFo jFo jFo @@ -105628,7 +105628,7 @@ iUc aFN nBJ nBp -tQC +bjF bfT tmQ dPO @@ -106650,7 +106650,7 @@ jnS xtS jnS ufs -yaQ +tys onk oNF tMn @@ -112290,7 +112290,7 @@ hDo sxS uvI skz -aAY +oZG sna pFu ocd @@ -123078,7 +123078,7 @@ qaG gji dOL nVc -gSC +rYS nBE nep mhc diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 1db384df2751..121d9d78560e 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -974,10 +974,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/grimy, /area/station/tcommsat/computer) -"arW" = ( -/obj/machinery/light/floor/has_bulb, -/turf/open/floor/wood, -/area/station/commons/lounge) "ase" = ( /obj/machinery/airalarm/directional/west, /obj/effect/mapping_helpers/airalarm/tlv_no_checks, @@ -2991,6 +2987,14 @@ /obj/structure/trash_pile, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"aYp" = ( +/obj/structure/cable, +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, +/obj/machinery/light/floor/has_bulb, +/turf/open/floor/wood/large, +/area/station/commons/lounge) "aYt" = ( /obj/machinery/door/firedoor, /obj/machinery/door/poddoor/shutters/preopen{ @@ -3114,6 +3118,10 @@ }, /turf/open/floor/plating, /area/station/maintenance/aft/lesser) +"aZI" = ( +/obj/machinery/light/floor/has_bulb, +/turf/open/floor/wood, +/area/station/commons/lounge) "aZL" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -8323,6 +8331,11 @@ }, /turf/open/floor/iron, /area/station/science/lab) +"dbg" = ( +/obj/machinery/duct, +/obj/machinery/light/floor/has_bulb, +/turf/open/floor/iron/white, +/area/station/science/xenobiology) "dbh" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -10063,13 +10076,6 @@ /obj/machinery/door/window/right/directional/west, /turf/open/floor/wood, /area/station/command/heads_quarters/captain/private) -"dJj" = ( -/obj/effect/turf_decal/siding/wood{ - dir = 1 - }, -/obj/machinery/light/floor/has_bulb, -/turf/open/floor/wood/large, -/area/station/commons/lounge) "dJK" = ( /turf/open/floor/iron/stairs/right{ dir = 1 @@ -12973,6 +12979,14 @@ "eIO" = ( /turf/closed/wall, /area/station/maintenance/department/medical/central) +"eJe" = ( +/obj/structure/cable, +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, +/obj/machinery/light/floor/has_bulb, +/turf/open/floor/wood, +/area/station/commons/lounge) "eJo" = ( /obj/structure/anvil, /turf/open/floor/wood, @@ -16321,14 +16335,6 @@ }, /turf/open/floor/iron/dark, /area/station/ai_monitored/aisat/exterior) -"fTy" = ( -/obj/structure/cable, -/obj/effect/turf_decal/siding/wood{ - dir = 1 - }, -/obj/machinery/light/floor/has_bulb, -/turf/open/floor/wood/large, -/area/station/commons/lounge) "fTE" = ( /obj/machinery/duct, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -19705,6 +19711,11 @@ /obj/structure/cable, /turf/open/floor/iron/dark/corner, /area/station/engineering/atmos/storage/gas) +"hcd" = ( +/obj/structure/window/spawner/directional/west, +/obj/machinery/light/floor/has_bulb, +/turf/open/floor/carpet, +/area/station/service/theater) "hcv" = ( /obj/effect/landmark/observer_start, /obj/effect/turf_decal/plaque{ @@ -20189,6 +20200,11 @@ }, /turf/open/floor/plating, /area/station/security/execution/transfer) +"hjN" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/light/floor/has_bulb, +/turf/open/floor/wood, +/area/station/commons/lounge) "hjS" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -20982,6 +20998,23 @@ /obj/effect/spawner/random/maintenance, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"hwx" = ( +/obj/machinery/flasher/directional/north{ + id = "AI" + }, +/obj/structure/table/wood/fancy/blue, +/obj/effect/spawner/random/aimodule/neutral, +/obj/machinery/door/window{ + base_state = "right"; + dir = 4; + icon_state = "right"; + name = "Core Modules"; + req_access = list("rd") + }, +/obj/structure/window/reinforced/spawner/directional/south, +/obj/item/ai_module/core/freeformcore, +/turf/open/floor/circuit, +/area/station/ai_monitored/turret_protected/ai_upload) "hwz" = ( /obj/effect/turf_decal/stripes/line{ dir = 5 @@ -24625,15 +24658,6 @@ /obj/effect/spawner/random/trash/grille_or_waste, /turf/open/floor/plating, /area/station/maintenance/aft/lesser) -"iHy" = ( -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/yellow{ - dir = 4 - }, -/obj/machinery/airalarm/directional/east, -/obj/machinery/rnd/production/circuit_imprinter/department/engineering, -/turf/open/floor/iron/dark/corner, -/area/station/engineering/storage_shared) "iHD" = ( /turf/closed/wall/r_wall, /area/station/security/courtroom) @@ -33418,6 +33442,34 @@ }, /turf/open/floor/iron/dark, /area/station/ai_monitored/security/armory) +"lDj" = ( +/obj/structure/table/wood, +/obj/item/device/walkman{ + pixel_y = 4; + pixel_x = -6 + }, +/obj/item/device/walkman{ + pixel_y = 4; + pixel_x = -6 + }, +/obj/item/device/walkman{ + pixel_y = 4; + pixel_x = -6 + }, +/obj/item/cassette_tape/blank{ + pixel_y = 3; + pixel_x = 5 + }, +/obj/item/cassette_tape/blank{ + pixel_y = 3; + pixel_x = 5 + }, +/obj/item/cassette_tape/blank{ + pixel_y = 3; + pixel_x = 5 + }, +/turf/open/floor/wood, +/area/station/service/library) "lDo" = ( /obj/item/radio/off, /obj/effect/turf_decal/stripes/line{ @@ -34045,22 +34097,6 @@ }, /turf/open/floor/iron/dark, /area/station/medical/medbay/central) -"lOY" = ( -/obj/structure/table/glass, -/obj/machinery/reagentgrinder{ - pixel_x = -1; - pixel_y = 8 - }, -/obj/machinery/button/door{ - id = "XenoPens"; - layer = 3.3; - name = "Xenobiology Lockdown"; - pixel_y = 0; - req_access = list("xenobiology"); - pixel_x = -24 - }, -/turf/open/floor/iron/white, -/area/station/science/xenobiology) "lOZ" = ( /obj/machinery/power/port_gen/pacman/pre_loaded, /turf/open/floor/plating, @@ -38396,11 +38432,6 @@ /obj/effect/turf_decal/tile/red/half/contrasted, /turf/open/floor/iron, /area/station/security/office) -"nlu" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/light/floor/has_bulb, -/turf/open/floor/wood, -/area/station/commons/lounge) "nlL" = ( /obj/effect/turf_decal/trimline/yellow/filled/line{ dir = 8 @@ -38924,14 +38955,6 @@ /obj/effect/mapping_helpers/airlock/access/all/science/general, /turf/open/floor/iron/white, /area/station/science/lab) -"nti" = ( -/obj/structure/cable, -/obj/effect/turf_decal/siding/wood{ - dir = 1 - }, -/obj/machinery/light/floor/has_bulb, -/turf/open/floor/wood, -/area/station/commons/lounge) "ntk" = ( /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 8 @@ -41733,6 +41756,13 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/maintenance/aft/lesser) +"orS" = ( +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, +/obj/machinery/light/floor/has_bulb, +/turf/open/floor/wood/large, +/area/station/commons/lounge) "orU" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 1 @@ -43658,11 +43688,6 @@ }, /turf/open/floor/iron/white, /area/station/medical/chemistry) -"pbB" = ( -/obj/machinery/duct, -/obj/machinery/light/floor/has_bulb, -/turf/open/floor/iron/white, -/area/station/science/xenobiology) "pbL" = ( /obj/machinery/door/firedoor, /turf/open/floor/iron/white/side, @@ -44668,6 +44693,22 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/fore) +"puj" = ( +/obj/structure/table/glass, +/obj/machinery/reagentgrinder{ + pixel_x = -1; + pixel_y = 8 + }, +/obj/machinery/button/door{ + id = "XenoPens"; + layer = 3.3; + name = "Xenobiology Lockdown"; + pixel_y = 0; + req_access = list("xenobiology"); + pixel_x = -24 + }, +/turf/open/floor/iron/white, +/area/station/science/xenobiology) "pul" = ( /obj/machinery/atmospherics/pipe/smart/simple/purple/visible{ dir = 4 @@ -50483,6 +50524,23 @@ }, /turf/open/floor/iron, /area/station/engineering/main) +"ruy" = ( +/obj/structure/window/reinforced/spawner/directional/south, +/obj/machinery/flasher/directional/north{ + id = "AI" + }, +/obj/effect/spawner/random/aimodule/harmful, +/obj/structure/table/wood/fancy/red, +/obj/machinery/door/window/brigdoor/left/directional/south{ + dir = 8; + name = "High-Risk Modules"; + req_access = list("rd") + }, +/obj/item/ai_module/reset/purge{ + pixel_y = 11 + }, +/turf/open/floor/circuit/red, +/area/station/ai_monitored/turret_protected/ai_upload) "ruz" = ( /obj/structure/chair/stool/directional/north, /turf/open/floor/wood, @@ -51311,34 +51369,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) -"rIa" = ( -/obj/structure/table/wood, -/obj/item/device/walkman{ - pixel_y = 4; - pixel_x = -6 - }, -/obj/item/device/walkman{ - pixel_y = 4; - pixel_x = -6 - }, -/obj/item/device/walkman{ - pixel_y = 4; - pixel_x = -6 - }, -/obj/item/device/cassette_tape/blank{ - pixel_y = 3; - pixel_x = 5 - }, -/obj/item/device/cassette_tape/blank{ - pixel_y = 3; - pixel_x = 5 - }, -/obj/item/device/cassette_tape/blank{ - pixel_y = 3; - pixel_x = 5 - }, -/turf/open/floor/wood, -/area/station/service/library) "rIh" = ( /obj/machinery/light/directional/east, /obj/effect/turf_decal/tile/neutral{ @@ -52150,23 +52180,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/construction/storage_wing) -"rVO" = ( -/obj/structure/window/reinforced/spawner/directional/south, -/obj/machinery/flasher/directional/north{ - id = "AI" - }, -/obj/effect/spawner/random/aimodule/harmful, -/obj/structure/table/wood/fancy/red, -/obj/machinery/door/window/brigdoor/left/directional/south{ - dir = 8; - name = "High-Risk Modules"; - req_access = list("rd") - }, -/obj/item/ai_module/reset/purge{ - pixel_y = 11 - }, -/turf/open/floor/circuit/red, -/area/station/ai_monitored/turret_protected/ai_upload) "rVY" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -58793,11 +58806,6 @@ /obj/machinery/light/directional/west, /turf/open/floor/iron/white, /area/station/security/prison/mess) -"umP" = ( -/obj/structure/window/spawner/directional/west, -/obj/machinery/light/floor/has_bulb, -/turf/open/floor/carpet, -/area/station/service/theater) "umS" = ( /obj/item/radio/intercom/directional/west, /obj/machinery/computer/records/security{ @@ -59524,6 +59532,15 @@ }, /turf/open/floor/iron/white, /area/station/command/heads_quarters/cmo) +"uyZ" = ( +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/yellow{ + dir = 4 + }, +/obj/machinery/airalarm/directional/east, +/obj/machinery/rnd/production/circuit_imprinter/department/engineering, +/turf/open/floor/iron/dark/corner, +/area/station/engineering/storage_shared) "uza" = ( /turf/closed/wall/r_wall, /area/station/security/prison/visit) @@ -66440,23 +66457,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/commons/fitness/recreation) -"wWd" = ( -/obj/machinery/flasher/directional/north{ - id = "AI" - }, -/obj/structure/table/wood/fancy/blue, -/obj/effect/spawner/random/aimodule/neutral, -/obj/machinery/door/window{ - base_state = "right"; - dir = 4; - icon_state = "right"; - name = "Core Modules"; - req_access = list("rd") - }, -/obj/structure/window/reinforced/spawner/directional/south, -/obj/item/ai_module/core/freeformcore, -/turf/open/floor/circuit, -/area/station/ai_monitored/turret_protected/ai_upload) "wWk" = ( /obj/effect/turf_decal/tile/blue{ dir = 1 @@ -89368,7 +89368,7 @@ nxF sVY cyk qzS -rIa +lDj ahD wki jIY @@ -94741,7 +94741,7 @@ qWF aaa aJS aJS -wWd +hwx pQG kUq dvk @@ -96283,7 +96283,7 @@ qWF aaa aJS aJS -rVO +ruy vrJ iXa uGb @@ -104269,11 +104269,11 @@ fRS tLx bvJ dYK -arW +aZI gae uFf qVt -dJj +orS qPd oqq mva @@ -105297,11 +105297,11 @@ fRS cAf bvJ eHL -nlu +hjN fSd fSd gqp -dJj +orS qiU pPb mTm @@ -106325,11 +106325,11 @@ pKi axU rac hiG -nti +eJe wKu wKu iLL -fTy +aYp lOB qnf mTm @@ -107098,7 +107098,7 @@ obG buf mzn foU -umP +hcd gMz otc etz @@ -112739,7 +112739,7 @@ aaa aaa aaa uJz -iHy +uyZ xgR nmb rtP @@ -118197,7 +118197,7 @@ eJI ycv kCw ycv -lOY +puj shY gjv aft @@ -118452,7 +118452,7 @@ mtu vHm fHs ycv -pbB +dbg kCw gRY xCA diff --git a/_maps/map_files/Voidraptor/VoidRaptor.dmm b/_maps/map_files/Voidraptor/VoidRaptor.dmm index 50a131a3a333..df0e56f44dd2 100644 --- a/_maps/map_files/Voidraptor/VoidRaptor.dmm +++ b/_maps/map_files/Voidraptor/VoidRaptor.dmm @@ -953,53 +953,6 @@ }, /turf/open/floor/iron/large, /area/station/hallway/primary/fore) -"amZ" = ( -/obj/machinery/firealarm/directional/west, -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/bot, -/obj/machinery/light/cold/directional/west, -/obj/structure/table, -/obj/item/storage/medkit{ - pixel_x = -3; - pixel_y = 8 - }, -/obj/item/healthanalyzer{ - pixel_x = -3; - pixel_y = 7 - }, -/obj/item/storage/medkit{ - pixel_x = -3; - pixel_y = 0 - }, -/obj/item/healthanalyzer{ - pixel_x = -3; - pixel_y = -2 - }, -/obj/item/assembly/prox_sensor{ - pixel_x = 10; - pixel_y = 14 - }, -/obj/item/assembly/prox_sensor{ - pixel_x = 11; - pixel_y = 8 - }, -/obj/item/assembly/prox_sensor{ - pixel_x = 10; - pixel_y = 2 - }, -/obj/item/assembly/prox_sensor{ - pixel_x = 11; - pixel_y = -4 - }, -/turf/open/floor/iron/dark/textured_edge{ - dir = 4 - }, -/area/station/science/robotics/lab) "anq" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -1126,6 +1079,14 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/textured, /area/station/science/ordnance/storage) +"apj" = ( +/obj/effect/turf_decal/trimline/yellow/filled/line{ + dir = 1 + }, +/obj/structure/chair, +/obj/effect/landmark/start/depsec/engineering, +/turf/open/floor/iron/textured_edge, +/area/station/security/checkpoint/engineering) "apl" = ( /obj/structure/spider/stickyweb, /obj/machinery/power/port_gen/pacman, @@ -2617,17 +2578,6 @@ /obj/machinery/status_display/evac, /turf/closed/wall/r_wall, /area/station/ai_monitored/turret_protected/aisat_interior) -"aMf" = ( -/obj/structure/extinguisher_cabinet/directional/east, -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 5 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 5 - }, -/obj/structure/reagent_dispensers/fueltank/large, -/turf/open/floor/iron/dark/textured, -/area/station/science/robotics/lab) "aMk" = ( /obj/machinery/growing/tray, /turf/open/floor/iron/dark/textured_large, @@ -3837,26 +3787,6 @@ }, /turf/open/floor/iron/textured, /area/station/engineering/lobby) -"bbW" = ( -/obj/machinery/door/firedoor, -/obj/structure/cable, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/door/airlock/engineering/glass{ - name = "Shared Engineering Storage" - }, -/obj/effect/mapping_helpers/airlock/access/any/engineering/construction, -/turf/open/floor/iron/textured_large, -/area/station/engineering/storage_shared) "bcf" = ( /obj/structure/table_frame, /turf/open/floor/iron/textured, @@ -4719,6 +4649,24 @@ }, /turf/open/floor/iron/freezer, /area/station/medical/chemistry) +"bqS" = ( +/obj/structure/chair/sofa/corp/right{ + color = "#DE3A3A" + }, +/obj/machinery/airalarm/directional/north, +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, +/obj/machinery/camera/directional/north{ + c_tag = "Research Division - Port"; + dir = 9; + name = "science camera"; + network = list("ss13","rd") + }, +/obj/item/pai_card, +/obj/effect/landmark/start/depsec/science, +/turf/open/floor/wood/large, +/area/station/science/research) "brb" = ( /obj/effect/landmark/start/hangover, /obj/effect/landmark/event_spawn, @@ -6262,6 +6210,22 @@ /obj/effect/turf_decal/trimline/purple/filled/corner, /turf/open/floor/iron/dark/textured, /area/station/security/corrections_officer) +"bPX" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/effect/turf_decal/trimline/purple/filled/corner{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/effect/turf_decal/bot, +/obj/machinery/autolathe, +/turf/open/floor/iron/dark/textured_corner{ + dir = 8 + }, +/area/station/science/robotics/lab) "bPZ" = ( /turf/closed/wall, /area/station/service/cafeteria) @@ -6396,6 +6360,15 @@ /obj/effect/turf_decal/bot, /turf/open/floor/iron/dark/textured_edge, /area/station/security/lockers) +"bSm" = ( +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/structure/sink/directional/south, +/turf/open/floor/iron/white/textured_edge, +/area/station/science/genetics) "bSp" = ( /obj/structure/closet/emcloset/wall{ pixel_x = -32 @@ -6615,6 +6588,13 @@ /obj/effect/turf_decal/siding/wood, /turf/open/floor/wood, /area/station/service/library/abandoned) +"bWv" = ( +/obj/structure/table, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/dark_red/anticorner/contrasted, +/obj/item/surgery_tray, +/turf/open/floor/iron/dark/textured, +/area/station/science/robotics/lab) "bWK" = ( /obj/machinery/ai_slipper{ uses = 10 @@ -8003,17 +7983,6 @@ }, /turf/open/floor/iron/dark/textured_large, /area/station/hallway/secondary/exit/departure_lounge) -"cuq" = ( -/obj/item/radio/intercom/directional/south, -/obj/effect/turf_decal/trimline/purple/filled/line, -/obj/effect/turf_decal/stripes/line, -/obj/machinery/modular_computer/preset/civilian{ - dir = 4 - }, -/turf/open/floor/iron/dark/textured_edge{ - dir = 1 - }, -/area/station/science/robotics/lab) "cuA" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 4 @@ -10022,22 +9991,6 @@ /obj/structure/sign/warning/secure_area, /turf/closed/wall/r_wall, /area/station/maintenance/aft/greater) -"cYI" = ( -/obj/structure/table/wood, -/obj/item/radio/intercom/directional/east, -/obj/machinery/light/warm/directional/east, -/obj/effect/turf_decal/siding/thinplating/dark{ - dir = 4 - }, -/obj/item/radio/radio_mic{ - pixel_y = 7 - }, -/obj/item/device/cassette_tape/friday{ - pixel_y = -6; - pixel_x = -8 - }, -/turf/open/floor/cult, -/area/station/service/library) "cYN" = ( /obj/structure/chair/sofa/corp/right{ dir = 8; @@ -10331,15 +10284,6 @@ dir = 4 }, /area/station/cargo/storage) -"dcF" = ( -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/structure/sink/directional/south, -/turf/open/floor/iron/white/textured_edge, -/area/station/science/genetics) "dcJ" = ( /obj/effect/turf_decal/trimline/green/filled/warning{ dir = 4 @@ -10803,15 +10747,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron/freezer, /area/station/medical/chemistry) -"diH" = ( -/obj/structure/table/wood/fancy/blue, -/obj/effect/spawner/random/aimodule/neutral, -/obj/machinery/door/window/left/directional/south{ - name = "Core Modules"; - req_access = list("rd") - }, -/turf/open/floor/circuit, -/area/station/ai_monitored/turret_protected/ai_upload) "diL" = ( /obj/effect/turf_decal/nova_decals/enclave/top/middle{ color = "#52B4E9" @@ -11735,6 +11670,21 @@ /obj/effect/turf_decal/trimline/neutral/mid_joiner, /turf/open/floor/iron/textured_large, /area/station/cargo/sorting) +"dwh" = ( +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/bot, +/obj/machinery/light/cold/directional/east, +/obj/effect/landmark/start/cyborg, +/obj/machinery/recharge_station, +/turf/open/floor/iron/dark/textured_edge{ + dir = 8 + }, +/area/station/science/robotics/lab) "dwi" = ( /obj/effect/turf_decal/siding/wood{ dir = 1 @@ -11742,19 +11692,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/wood, /area/station/maintenance/port/aft) -"dwE" = ( -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 9 - }, -/obj/machinery/airalarm/directional/west, -/obj/effect/turf_decal/delivery, -/obj/machinery/requests_console/directional/north{ - department = "Genetics"; - name = "Genetics Requests console" - }, -/obj/machinery/clonepod, -/turf/open/floor/iron/white, -/area/station/science/genetics) "dwH" = ( /obj/effect/decal/cleanable/blood/old, /obj/effect/decal/remains/human, @@ -11934,6 +11871,13 @@ /obj/effect/turf_decal/bot, /turf/open/floor/iron/textured, /area/station/hallway/primary/aft) +"dzC" = ( +/obj/effect/turf_decal/tile/blue/fourcorners, +/obj/item/radio/intercom/directional/west, +/obj/structure/sink/directional/east, +/obj/effect/landmark/start/depsec/medical, +/turf/open/floor/iron/freezer, +/area/station/security/checkpoint/medical) "dzE" = ( /obj/structure/chair/comfy/beige{ dir = 1 @@ -14749,6 +14693,22 @@ /obj/structure/flora/bush/flowers_pp/style_random, /turf/open/floor/grass, /area/station/service/chapel) +"ekr" = ( +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 4 + }, +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 4 + }, +/obj/structure/disposalpipe/segment, +/obj/machinery/vending/wardrobe/gene_wardrobe, +/obj/item/toy/figure/geneticist{ + pixel_y = 18 + }, +/turf/open/floor/iron/white/textured_edge{ + dir = 8 + }, +/area/station/science/genetics) "eks" = ( /obj/machinery/door/firedoor, /obj/structure/table/reinforced, @@ -15592,22 +15552,6 @@ }, /turf/open/floor/iron/dark/textured, /area/station/security/prison/mess) -"exy" = ( -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 4 - }, -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 4 - }, -/obj/structure/disposalpipe/segment, -/obj/machinery/vending/wardrobe/gene_wardrobe, -/obj/item/toy/figure/geneticist{ - pixel_y = 18 - }, -/turf/open/floor/iron/white/textured_edge{ - dir = 8 - }, -/area/station/science/genetics) "exF" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -17673,14 +17617,6 @@ /obj/effect/turf_decal/tile/purple/diagonal_centre, /turf/open/floor/iron/white/diagonal, /area/station/science/research) -"fch" = ( -/obj/effect/turf_decal/tile/blue/fourcorners, -/obj/structure/chair/office/light{ - dir = 4 - }, -/obj/effect/landmark/start/depsec/medical, -/turf/open/floor/iron/freezer, -/area/station/security/checkpoint/medical) "fci" = ( /obj/effect/turf_decal/siding/wood{ dir = 6 @@ -20540,14 +20476,6 @@ dir = 6 }, /area/station/service/chapel) -"fWZ" = ( -/obj/effect/turf_decal/trimline/yellow/filled/line{ - dir = 1 - }, -/obj/structure/chair, -/obj/effect/landmark/start/depsec/engineering, -/turf/open/floor/iron/textured_edge, -/area/station/security/checkpoint/engineering) "fXi" = ( /obj/structure/railing/wood{ dir = 8 @@ -21551,6 +21479,18 @@ }, /turf/open/floor/catwalk_floor/iron_smooth, /area/station/maintenance/department/science/xenobiology) +"gkd" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + dir = 8 + }, +/obj/structure/chair/office{ + dir = 1 + }, +/obj/effect/landmark/start/depsec/supply, +/turf/open/floor/iron/textured_edge{ + dir = 4 + }, +/area/station/security/checkpoint/supply) "gkk" = ( /obj/machinery/atmospherics/pipe/heat_exchanging/simple{ dir = 8 @@ -22335,13 +22275,6 @@ "gvG" = ( /turf/open/floor/engine, /area/station/science/auxlab/firing_range) -"gvI" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/duct, -/mob/living/basic/bot/cleanbot/medbay, -/turf/open/floor/iron/white/textured_large, -/area/station/medical/storage) "gvO" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/siding/dark, @@ -22917,15 +22850,6 @@ /obj/machinery/door/airlock/maintenance_hatch, /turf/open/floor/catwalk_floor/iron_smooth, /area/station/maintenance/port/aft) -"gEJ" = ( -/obj/structure/table/wood/fancy/red, -/obj/effect/spawner/random/aimodule/harmful, -/obj/machinery/door/window/brigdoor/left/directional/north{ - name = "High-Risk Modules"; - req_access = list("rd") - }, -/turf/open/floor/circuit/red, -/area/station/ai_monitored/turret_protected/ai_upload) "gEO" = ( /obj/structure/rack/gunrack, /obj/effect/turf_decal/tile/dark_red/anticorner{ @@ -23169,19 +23093,6 @@ }, /turf/open/floor/iron/dark/textured_large, /area/station/command/bridge) -"gIr" = ( -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 8 - }, -/obj/effect/turf_decal/bot, -/obj/machinery/light/cold/directional/west, -/obj/machinery/computer/cloning{ - dir = 4 - }, -/turf/open/floor/iron/white/textured_edge{ - dir = 4 - }, -/area/station/science/genetics) "gIu" = ( /obj/effect/turf_decal/box, /obj/machinery/portable_atmospherics/canister, @@ -23361,13 +23272,6 @@ "gKO" = ( /turf/open/floor/catwalk_floor/iron_smooth, /area/station/hallway/primary/aft) -"gKY" = ( -/obj/structure/table, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/dark_red/anticorner/contrasted, -/obj/item/surgery_tray, -/turf/open/floor/iron/dark/textured, -/area/station/science/robotics/lab) "gKZ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -25056,6 +24960,21 @@ /obj/effect/turf_decal/caution/stand_clear, /turf/open/floor/iron/textured_large, /area/station/cargo/storage) +"hii" = ( +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 8 + }, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 4 + }, +/obj/structure/sign/nanotrasen{ + pixel_x = -32 + }, +/obj/effect/landmark/start/depsec/science, +/turf/open/floor/iron/white/textured_edge{ + dir = 4 + }, +/area/station/security/checkpoint/science/research) "hio" = ( /obj/effect/turf_decal/trimline/green/corner{ dir = 8 @@ -27812,12 +27731,6 @@ /obj/machinery/digital_clock, /turf/closed/wall/r_wall, /area/station/cargo/quartermaster) -"hXj" = ( -/obj/item/radio/intercom/directional/west, -/obj/effect/turf_decal/bot, -/obj/machinery/rnd/production/circuit_imprinter/department/engineering, -/turf/open/floor/iron/dark/textured_large, -/area/station/engineering/storage_shared) "hXs" = ( /obj/structure/table/wood/fancy, /obj/item/folder{ @@ -28547,6 +28460,15 @@ /obj/item/storage/box/lights/mixed, /turf/open/floor/iron/textured, /area/station/maintenance/department/bridge) +"ijh" = ( +/obj/item/radio/intercom/directional/south, +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 6 + }, +/obj/effect/turf_decal/box, +/obj/machinery/mechpad, +/turf/open/floor/iron/dark/textured, +/area/station/science/robotics/mechbay) "ijj" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, @@ -29960,21 +29882,6 @@ }, /turf/open/floor/iron/checker, /area/station/service/theater) -"izJ" = ( -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 8 - }, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 4 - }, -/obj/structure/sign/nanotrasen{ - pixel_x = -32 - }, -/obj/effect/landmark/start/depsec/science, -/turf/open/floor/iron/white/textured_edge{ - dir = 4 - }, -/area/station/security/checkpoint/science/research) "izY" = ( /obj/machinery/door/airlock/grunge{ name = "Chapel" @@ -30329,15 +30236,6 @@ /obj/effect/spawner/random/engineering/vending_restock, /turf/open/floor/iron/textured, /area/station/maintenance/starboard/lesser) -"iGc" = ( -/obj/effect/landmark/start/roboticist, -/obj/effect/turf_decal/tile/dark_red{ - dir = 4 - }, -/turf/open/floor/iron/dark/textured_corner{ - dir = 4 - }, -/area/station/science/robotics/lab) "iGg" = ( /obj/machinery/atmospherics/pipe/smart/manifold/purple/visible{ dir = 8 @@ -31251,13 +31149,6 @@ "iTA" = ( /turf/closed/wall, /area/station/hallway/secondary/entry) -"iTJ" = ( -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 1 - }, -/obj/effect/landmark/start/depsec/science, -/turf/open/floor/iron/white/textured_edge, -/area/station/security/checkpoint/science/research) "iTS" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /obj/effect/turf_decal/stripes/line{ @@ -31696,13 +31587,6 @@ }, /turf/open/floor/carpet/blue, /area/station/command/bridge) -"iYZ" = ( -/obj/structure/chair{ - dir = 8 - }, -/obj/effect/landmark/start/depsec/engineering, -/turf/open/floor/iron/textured_large, -/area/station/security/checkpoint/engineering) "iZa" = ( /obj/effect/landmark/event_spawn, /obj/machinery/duct, @@ -31878,22 +31762,6 @@ }, /turf/open/floor/pod/dark, /area/station/service/chapel/funeral) -"jbi" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/effect/turf_decal/trimline/purple/filled/corner{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/effect/turf_decal/bot, -/obj/machinery/autolathe, -/turf/open/floor/iron/dark/textured_corner{ - dir = 8 - }, -/area/station/science/robotics/lab) "jbo" = ( /obj/effect/turf_decal/siding/wood{ dir = 8 @@ -31910,6 +31778,22 @@ /obj/effect/turf_decal/tile/bar/opposingcorners, /turf/open/floor/iron, /area/station/service/bar) +"jbG" = ( +/obj/structure/table/wood, +/obj/item/radio/intercom/directional/east, +/obj/machinery/light/warm/directional/east, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 4 + }, +/obj/item/radio/radio_mic{ + pixel_y = 7 + }, +/obj/item/cassette_tape/friday{ + pixel_y = -6; + pixel_x = -8 + }, +/turf/open/floor/cult, +/area/station/service/library) "jbI" = ( /obj/effect/turf_decal/trimline/yellow/filled/line{ dir = 1 @@ -35101,6 +34985,19 @@ /obj/structure/chair/stool/directional/west, /turf/open/floor/wood/large, /area/station/commons/fitness/recreation) +"jTK" = ( +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 8 + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/effect/turf_decal/bot, +/obj/machinery/dna_scannernew, +/turf/open/floor/iron/white/textured_edge{ + dir = 4 + }, +/area/station/science/genetics) "jTQ" = ( /obj/effect/turf_decal/trimline/brown/filled/warning{ dir = 4 @@ -37028,6 +36925,15 @@ /obj/effect/spawner/random/entertainment/money, /turf/open/floor/iron/textured_large, /area/station/maintenance/disposal) +"kxM" = ( +/obj/structure/table/wood/fancy/red, +/obj/effect/spawner/random/aimodule/harmful, +/obj/machinery/door/window/brigdoor/left/directional/north{ + name = "High-Risk Modules"; + req_access = list("rd") + }, +/turf/open/floor/circuit/red, +/area/station/ai_monitored/turret_protected/ai_upload) "kxN" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -37374,18 +37280,6 @@ /obj/effect/spawner/random/trash/garbage, /turf/open/floor/catwalk_floor/iron_smooth, /area/station/maintenance/port/upper) -"kBs" = ( -/obj/effect/turf_decal/trimline/brown/filled/line{ - dir = 8 - }, -/obj/structure/chair/office{ - dir = 1 - }, -/obj/effect/landmark/start/depsec/supply, -/turf/open/floor/iron/textured_edge{ - dir = 4 - }, -/area/station/security/checkpoint/supply) "kBS" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 4 @@ -38056,11 +37950,6 @@ }, /turf/open/floor/iron/textured, /area/station/maintenance/aft/upper) -"kJi" = ( -/obj/machinery/holopad, -/obj/effect/turf_decal/bot, -/turf/open/floor/iron/dark/textured_large, -/area/station/science/robotics/lab) "kJk" = ( /obj/machinery/atmospherics/components/unary/vent_pump/siphon/monitored/oxygen_output{ dir = 4 @@ -38702,6 +38591,11 @@ /obj/effect/mapping_helpers/airlock/access/all/engineering/general, /turf/open/floor/engine, /area/station/engineering/supermatter) +"kTa" = ( +/obj/effect/turf_decal/tile/blue/fourcorners, +/obj/effect/landmark/start/depsec/medical, +/turf/open/floor/iron/freezer, +/area/station/security/checkpoint/medical) "kTc" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -40312,6 +40206,12 @@ /obj/effect/landmark/start/chief_medical_officer, /turf/open/floor/carpet/blue, /area/station/command/heads_quarters/cmo) +"lqg" = ( +/obj/item/radio/intercom/directional/west, +/obj/effect/turf_decal/bot, +/obj/machinery/rnd/production/circuit_imprinter/department/engineering, +/turf/open/floor/iron/dark/textured_large, +/area/station/engineering/storage_shared) "lqw" = ( /obj/machinery/status_display/evac/directional/east, /obj/structure/closet/secure_closet/detective, @@ -41140,18 +41040,6 @@ /obj/item/kirbyplants/random, /turf/open/floor/vault, /area/station/ai_monitored/turret_protected/aisat/foyer) -"lCb" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/brown/filled/warning{ - dir = 4 - }, -/obj/effect/landmark/start/depsec/supply, -/turf/open/floor/iron/textured_edge{ - dir = 8 - }, -/area/station/security/checkpoint/supply) "lCg" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/siding/green, @@ -41284,15 +41172,6 @@ /obj/structure/window/fulltile, /turf/open/floor/grass, /area/station/common/cryopods) -"lDK" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 1 - }, -/mob/living/basic/pet/dog/corgi{ - name = "Dirk" - }, -/turf/open/floor/iron/dark/textured_large, -/area/station/science/robotics/lab) "lDP" = ( /obj/structure/frame/machine, /obj/effect/decal/cleanable/dirt, @@ -42120,6 +41999,13 @@ /obj/effect/turf_decal/bot, /turf/open/floor/iron/dark/textured, /area/station/ai_monitored/command/storage/eva) +"lPj" = ( +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 1 + }, +/obj/effect/landmark/start/depsec/science, +/turf/open/floor/iron/white/textured_edge, +/area/station/security/checkpoint/science/research) "lPo" = ( /obj/effect/spawner/structure/window, /obj/structure/curtain/cloth/fancy/mechanical{ @@ -42694,17 +42580,6 @@ /obj/effect/turf_decal/tile/blue/diagonal_centre, /turf/open/floor/iron/diagonal, /area/station/hallway/secondary/exit/departure_lounge) -"lWk" = ( -/obj/structure/chair/sofa/corp{ - color = "#DE3A3A" - }, -/obj/effect/turf_decal/siding/wood{ - dir = 1 - }, -/obj/item/radio/intercom/directional/north, -/obj/effect/landmark/start/research_director, -/turf/open/floor/wood/large, -/area/station/science/research) "lWr" = ( /obj/machinery/door/airlock/external/ruin, /obj/effect/mapping_helpers/airlock/cyclelink_helper{ @@ -43377,18 +43252,6 @@ "mhj" = ( /turf/open/floor/iron/white/small, /area/station/common/pool) -"mhn" = ( -/obj/structure/cable, -/obj/machinery/power/apc/auto_name/directional/west, -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 10 - }, -/obj/effect/turf_decal/box, -/obj/machinery/computer/mechpad{ - dir = 1 - }, -/turf/open/floor/iron/dark/textured, -/area/station/science/robotics/mechbay) "mhx" = ( /turf/open/floor/engine, /area/station/engineering/supermatter) @@ -43477,15 +43340,6 @@ /obj/structure/table/wood/fancy, /turf/open/floor/pod/dark, /area/station/service/chapel/office) -"miV" = ( -/obj/effect/turf_decal/trimline/purple/filled/warning{ - dir = 5 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment, -/turf/open/floor/iron/white, -/area/station/science/research) "miZ" = ( /obj/effect/turf_decal/siding/wood{ dir = 4 @@ -44033,6 +43887,18 @@ /obj/effect/turf_decal/siding/wood, /turf/open/floor/wood/large, /area/station/commons/fitness/recreation/entertainment) +"mqD" = ( +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 8 + }, +/obj/effect/turf_decal/trimline/brown/filled/warning{ + dir = 4 + }, +/obj/effect/landmark/start/depsec/supply, +/turf/open/floor/iron/textured_edge{ + dir = 8 + }, +/area/station/security/checkpoint/supply) "mqJ" = ( /obj/effect/turf_decal/trimline/brown/filled/line{ dir = 9 @@ -45311,6 +45177,15 @@ dir = 8 }, /area/station/hallway/primary/fore) +"mLe" = ( +/obj/structure/table/wood/fancy/blue, +/obj/effect/spawner/random/aimodule/neutral, +/obj/machinery/door/window/left/directional/south{ + name = "Core Modules"; + req_access = list("rd") + }, +/turf/open/floor/circuit, +/area/station/ai_monitored/turret_protected/ai_upload) "mLj" = ( /obj/structure/plasticflaps/opaque{ name = "Security Deliveries" @@ -45887,15 +45762,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/wood, /area/station/hallway/secondary/entry) -"mSx" = ( -/obj/item/radio/intercom/directional/south, -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 6 - }, -/obj/effect/turf_decal/box, -/obj/machinery/mechpad, -/turf/open/floor/iron/dark/textured, -/area/station/science/robotics/mechbay) "mSI" = ( /obj/machinery/portable_atmospherics/canister/carbon_dioxide, /obj/effect/turf_decal/box, @@ -46010,6 +45876,26 @@ /obj/structure/chair/stool/directional/west, /turf/open/floor/iron/dark/textured_large, /area/station/service/theater) +"mUG" = ( +/obj/machinery/door/firedoor, +/obj/structure/cable, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/door/airlock/engineering/glass{ + name = "Shared Engineering Storage" + }, +/obj/effect/mapping_helpers/airlock/access/any/engineering/construction, +/turf/open/floor/iron/textured_large, +/area/station/engineering/storage_shared) "mUM" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -48820,11 +48706,6 @@ /obj/effect/turf_decal/tile/neutral/diagonal_centre, /turf/open/floor/iron/diagonal, /area/station/commons/dorms) -"nJe" = ( -/obj/effect/turf_decal/tile/blue/fourcorners, -/obj/effect/landmark/start/depsec/medical, -/turf/open/floor/iron/freezer, -/area/station/security/checkpoint/medical) "nJj" = ( /obj/effect/turf_decal/siding/wood{ dir = 5 @@ -48960,19 +48841,6 @@ }, /turf/open/floor/iron/textured_large, /area/station/cargo/sorting) -"nLM" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 10 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 10 - }, -/obj/machinery/rnd/production/circuit_imprinter/department/science, -/turf/open/floor/iron/dark/textured, -/area/station/science/robotics/lab) "nMf" = ( /obj/structure/reagent_dispensers/plumbed{ name = "medbay water reservoir" @@ -49848,6 +49716,15 @@ /obj/machinery/light/directional/north, /turf/open/floor/iron/textured_edge, /area/station/cargo/warehouse) +"nWp" = ( +/obj/effect/landmark/start/roboticist, +/obj/effect/turf_decal/tile/dark_red{ + dir = 4 + }, +/turf/open/floor/iron/dark/textured_corner{ + dir = 4 + }, +/area/station/science/robotics/lab) "nWq" = ( /obj/effect/turf_decal/trimline/brown/filled/line{ dir = 8 @@ -49931,23 +49808,6 @@ }, /turf/open/floor/iron/dark/textured, /area/station/security/brig) -"nXH" = ( -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 4 - }, -/obj/structure/chair/office, -/obj/machinery/computer/security/telescreen{ - desc = "Used for monitoring medbay to ensure patient safety."; - dir = 8; - name = "Science Monitor"; - network = list("rd","toxins","minisat","xeno","test"); - pixel_x = 32 - }, -/obj/effect/landmark/start/depsec/science, -/turf/open/floor/iron/white/textured_edge{ - dir = 8 - }, -/area/station/security/checkpoint/science/research) "nXM" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -50312,6 +50172,19 @@ dir = 4 }, /area/station/commons/fitness/recreation/entertainment) +"oeG" = ( +/obj/structure/sign/poster/contraband/borg_fancy_1/directional/west, +/obj/structure/window/reinforced/spawner/directional/north, +/obj/effect/turf_decal/tile/dark_red/anticorner/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/bot_red, +/obj/structure/table, +/obj/item/clothing/gloves/latex, +/obj/item/clothing/mask/surgical, +/obj/item/clothing/suit/apron/surgical, +/turf/open/floor/iron/dark/textured, +/area/station/science/robotics/lab) "oeK" = ( /obj/effect/turf_decal/trimline/purple/filled/line{ dir = 8 @@ -53293,24 +53166,6 @@ }, /turf/open/floor/iron/dark/textured, /area/station/medical/break_room) -"oVw" = ( -/obj/structure/chair/sofa/corp/right{ - color = "#DE3A3A" - }, -/obj/machinery/airalarm/directional/north, -/obj/effect/turf_decal/siding/wood{ - dir = 1 - }, -/obj/machinery/camera/directional/north{ - c_tag = "Research Division - Port"; - dir = 9; - name = "science camera"; - network = list("ss13","rd") - }, -/obj/item/pai_card, -/obj/effect/landmark/start/depsec/science, -/turf/open/floor/wood/large, -/area/station/science/research) "oVM" = ( /obj/structure/railing, /obj/structure/cable, @@ -55425,6 +55280,15 @@ /obj/structure/cable, /turf/closed/wall/r_wall, /area/station/security/checkpoint/science/research) +"pxC" = ( +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 1 + }, +/mob/living/basic/pet/dog/corgi{ + name = "Dirk" + }, +/turf/open/floor/iron/dark/textured_large, +/area/station/science/robotics/lab) "pxK" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -56011,6 +55875,19 @@ /obj/machinery/light_switch/directional/east, /turf/open/floor/iron/textured, /area/station/engineering/atmos/storage/gas) +"pFH" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 10 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 10 + }, +/obj/machinery/rnd/production/circuit_imprinter/department/science, +/turf/open/floor/iron/dark/textured, +/area/station/science/robotics/lab) "pGh" = ( /turf/open/floor/iron/white/textured_large, /area/station/hallway/primary/fore) @@ -56116,13 +55993,6 @@ /obj/effect/landmark/start/hangover, /turf/open/floor/iron/textured_edge, /area/station/hallway/primary/aft) -"pHC" = ( -/obj/effect/turf_decal/trimline/brown/filled/line, -/obj/effect/landmark/start/depsec/supply, -/turf/open/floor/iron/textured_edge{ - dir = 1 - }, -/area/station/security/checkpoint/supply) "pHG" = ( /obj/structure/flora/grass/jungle, /obj/structure/flora/bush/grassy, @@ -58435,6 +58305,13 @@ dir = 8 }, /area/station/medical/exam_room) +"qkd" = ( +/obj/effect/turf_decal/trimline/brown/filled/line, +/obj/effect/landmark/start/depsec/supply, +/turf/open/floor/iron/textured_edge{ + dir = 1 + }, +/area/station/security/checkpoint/supply) "qki" = ( /obj/structure/table/reinforced, /obj/item/assembly/voice{ @@ -60462,6 +60339,17 @@ dir = 1 }, /area/station/cargo/storage) +"qND" = ( +/obj/structure/chair/sofa/corp{ + color = "#DE3A3A" + }, +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, +/obj/item/radio/intercom/directional/north, +/obj/effect/landmark/start/research_director, +/turf/open/floor/wood/large, +/area/station/science/research) "qNW" = ( /obj/structure/window/spawner/directional/west, /obj/structure/window/spawner/directional/north, @@ -61546,6 +61434,53 @@ }, /turf/open/floor/iron/dark/textured, /area/station/security/execution/education) +"rdQ" = ( +/obj/machinery/firealarm/directional/west, +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/bot, +/obj/machinery/light/cold/directional/west, +/obj/structure/table, +/obj/item/storage/medkit{ + pixel_x = -3; + pixel_y = 8 + }, +/obj/item/healthanalyzer{ + pixel_x = -3; + pixel_y = 7 + }, +/obj/item/storage/medkit{ + pixel_x = -3; + pixel_y = 0 + }, +/obj/item/healthanalyzer{ + pixel_x = -3; + pixel_y = -2 + }, +/obj/item/assembly/prox_sensor{ + pixel_x = 10; + pixel_y = 14 + }, +/obj/item/assembly/prox_sensor{ + pixel_x = 11; + pixel_y = 8 + }, +/obj/item/assembly/prox_sensor{ + pixel_x = 10; + pixel_y = 2 + }, +/obj/item/assembly/prox_sensor{ + pixel_x = 11; + pixel_y = -4 + }, +/turf/open/floor/iron/dark/textured_edge{ + dir = 4 + }, +/area/station/science/robotics/lab) "rdT" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -62037,6 +61972,26 @@ /obj/machinery/firealarm/directional/north, /turf/open/floor/wood/large, /area/station/service/theater) +"rkz" = ( +/obj/machinery/door/firedoor, +/obj/structure/cable, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/line, +/obj/structure/disposalpipe/segment, +/obj/machinery/door/poddoor/preopen{ + id = "engielock"; + name = "Engineering Lockdown Blast Door" + }, +/obj/machinery/door/airlock/engineering/glass{ + name = "EngineRoom" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/mapping_helpers/airlock/access/any/engineering/construction, +/turf/open/floor/iron/textured_large, +/area/station/engineering/main) "rkG" = ( /obj/effect/turf_decal/trimline/brown/filled/warning, /turf/open/floor/iron/textured_edge{ @@ -62819,6 +62774,11 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/catwalk_floor/iron_smooth, /area/station/maintenance/starboard/lesser) +"rxL" = ( +/obj/machinery/holopad, +/obj/effect/turf_decal/bot, +/turf/open/floor/iron/dark/textured_large, +/area/station/science/robotics/lab) "rxO" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/carpet, @@ -63895,6 +63855,13 @@ }, /turf/open/floor/catwalk_floor/iron_smooth, /area/station/maintenance/starboard/lesser) +"rOg" = ( +/obj/structure/chair{ + dir = 8 + }, +/obj/effect/landmark/start/depsec/engineering, +/turf/open/floor/iron/textured_large, +/area/station/security/checkpoint/engineering) "rOi" = ( /obj/structure/window/reinforced/spawner/directional/south, /obj/effect/artifact_spawner, @@ -64403,6 +64370,19 @@ dir = 1 }, /area/station/cargo/lobby) +"rUV" = ( +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 8 + }, +/obj/effect/turf_decal/bot, +/obj/machinery/light/cold/directional/west, +/obj/machinery/computer/cloning{ + dir = 4 + }, +/turf/open/floor/iron/white/textured_edge{ + dir = 4 + }, +/area/station/science/genetics) "rVl" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -65514,13 +65494,6 @@ dir = 1 }, /area/station/security/prison/work) -"snJ" = ( -/obj/effect/turf_decal/tile/blue/fourcorners, -/obj/item/radio/intercom/directional/west, -/obj/structure/sink/directional/east, -/obj/effect/landmark/start/depsec/medical, -/turf/open/floor/iron/freezer, -/area/station/security/checkpoint/medical) "snO" = ( /obj/machinery/chem_dispenser/drinks{ dir = 4 @@ -71075,6 +71048,23 @@ /obj/structure/cable, /turf/open/floor/engine, /area/station/engineering/supermatter) +"tHk" = ( +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 4 + }, +/obj/structure/chair/office, +/obj/machinery/computer/security/telescreen{ + desc = "Used for monitoring medbay to ensure patient safety."; + dir = 8; + name = "Science Monitor"; + network = list("rd","toxins","minisat","xeno","test"); + pixel_x = 32 + }, +/obj/effect/landmark/start/depsec/science, +/turf/open/floor/iron/white/textured_edge{ + dir = 8 + }, +/area/station/security/checkpoint/science/research) "tHC" = ( /obj/effect/spawner/random/trash/grille_or_waste, /turf/open/floor/iron/textured, @@ -73358,6 +73348,15 @@ /obj/effect/turf_decal/bot, /turf/open/floor/wood/large, /area/station/commons/fitness/recreation/entertainment) +"uqv" = ( +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 4 + }, +/obj/machinery/vending/mechcomp, +/turf/open/floor/iron/white/textured_edge{ + dir = 8 + }, +/area/station/science/research) "uqw" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/engineering{ @@ -73707,6 +73706,17 @@ /obj/structure/displaycase/trophy, /turf/open/floor/iron/grimy, /area/station/hallway/primary/central/fore) +"utL" = ( +/obj/structure/extinguisher_cabinet/directional/east, +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 5 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 5 + }, +/obj/structure/reagent_dispensers/fueltank/large, +/turf/open/floor/iron/dark/textured, +/area/station/science/robotics/lab) "utM" = ( /obj/effect/turf_decal/trimline/brown/filled/warning{ dir = 4 @@ -74314,18 +74324,6 @@ /obj/machinery/newscaster/directional/south, /turf/open/floor/iron/textured, /area/station/maintenance/department/electrical) -"uAE" = ( -/obj/effect/turf_decal/trimline/yellow/filled/corner{ - dir = 8 - }, -/obj/structure/chair{ - dir = 8 - }, -/obj/effect/landmark/start/depsec/engineering, -/turf/open/floor/iron/textured_corner{ - dir = 8 - }, -/area/station/security/checkpoint/engineering) "uAN" = ( /obj/structure/flora/grass/jungle, /obj/structure/window/fulltile, @@ -75959,15 +75957,6 @@ "uZu" = ( /turf/open/floor/grass, /area/station/security/prison/garden) -"uZB" = ( -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 4 - }, -/obj/machinery/vending/mechcomp, -/turf/open/floor/iron/white/textured_edge{ - dir = 8 - }, -/area/station/science/research) "uZU" = ( /obj/machinery/atmospherics/components/unary/portables_connector/visible{ dir = 8 @@ -76784,19 +76773,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/textured, /area/station/maintenance/aft/lesser) -"vmI" = ( -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 8 - }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/effect/turf_decal/bot, -/obj/machinery/dna_scannernew, -/turf/open/floor/iron/white/textured_edge{ - dir = 4 - }, -/area/station/science/genetics) "vmX" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/door/airlock{ @@ -78458,6 +78434,13 @@ dir = 8 }, /area/station/engineering/atmos) +"vLC" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/duct, +/mob/living/basic/bot/cleanbot/medbay, +/turf/open/floor/iron/white/textured_large, +/area/station/medical/storage) "vLO" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -78841,16 +78824,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/freezer, /area/station/medical/pharmacy) -"vRB" = ( -/obj/structure/extinguisher_cabinet/directional/west, -/obj/effect/turf_decal/tile/dark_red/half/contrasted{ - dir = 8 - }, -/obj/structure/sink/directional/south, -/turf/open/floor/iron/dark/textured_edge{ - dir = 4 - }, -/area/station/science/robotics/lab) "vRH" = ( /obj/effect/turf_decal/stripes/line{ dir = 6 @@ -80076,6 +80049,15 @@ /obj/effect/mapping_helpers/airalarm/tlv_no_checks, /turf/open/floor/engine, /area/station/engineering/supermatter) +"wjg" = ( +/obj/effect/turf_decal/trimline/purple/filled/warning{ + dir = 5 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment, +/turf/open/floor/iron/white, +/area/station/science/research) "wjk" = ( /obj/machinery/drone_dispenser, /obj/effect/turf_decal/bot, @@ -80958,6 +80940,14 @@ /obj/structure/window/reinforced/plasma/spawner/directional/east, /turf/open/floor/iron/textured, /area/station/commons/vacant_room/office) +"wuV" = ( +/obj/effect/turf_decal/tile/blue/fourcorners, +/obj/structure/chair/office/light{ + dir = 4 + }, +/obj/effect/landmark/start/depsec/medical, +/turf/open/floor/iron/freezer, +/area/station/security/checkpoint/medical) "wuW" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -81362,6 +81352,18 @@ }, /turf/open/floor/iron/white, /area/station/medical/surgery) +"wzR" = ( +/obj/structure/cable, +/obj/machinery/power/apc/auto_name/directional/west, +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 10 + }, +/obj/effect/turf_decal/box, +/obj/machinery/computer/mechpad{ + dir = 1 + }, +/turf/open/floor/iron/dark/textured, +/area/station/science/robotics/mechbay) "wzT" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/orange/visible, /obj/effect/turf_decal/stripes/line{ @@ -82814,19 +82816,6 @@ }, /turf/open/floor/iron/textured_large, /area/station/hallway/secondary/construction) -"wVw" = ( -/obj/structure/sign/poster/contraband/borg_fancy_1/directional/west, -/obj/structure/window/reinforced/spawner/directional/north, -/obj/effect/turf_decal/tile/dark_red/anticorner/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/bot_red, -/obj/structure/table, -/obj/item/clothing/gloves/latex, -/obj/item/clothing/mask/surgical, -/obj/item/clothing/suit/apron/surgical, -/turf/open/floor/iron/dark/textured, -/area/station/science/robotics/lab) "wVD" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -82984,6 +82973,16 @@ dir = 8 }, /area/station/commons/fitness/recreation/entertainment) +"xac" = ( +/obj/structure/extinguisher_cabinet/directional/west, +/obj/effect/turf_decal/tile/dark_red/half/contrasted{ + dir = 8 + }, +/obj/structure/sink/directional/south, +/turf/open/floor/iron/dark/textured_edge{ + dir = 4 + }, +/area/station/science/robotics/lab) "xag" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -83225,6 +83224,19 @@ /obj/machinery/airalarm/directional/west, /turf/open/floor/carpet/cyan, /area/station/command/heads_quarters/blueshield) +"xfs" = ( +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 9 + }, +/obj/machinery/airalarm/directional/west, +/obj/effect/turf_decal/delivery, +/obj/machinery/requests_console/directional/north{ + department = "Genetics"; + name = "Genetics Requests console" + }, +/obj/machinery/clonepod, +/turf/open/floor/iron/white, +/area/station/science/genetics) "xfx" = ( /obj/structure/railing{ dir = 4 @@ -83283,6 +83295,17 @@ /obj/structure/sign/poster/official/random/directional/west, /turf/open/floor/iron/dark/textured_large, /area/station/engineering/break_room) +"xgi" = ( +/obj/item/radio/intercom/directional/south, +/obj/effect/turf_decal/trimline/purple/filled/line, +/obj/effect/turf_decal/stripes/line, +/obj/machinery/modular_computer/preset/civilian{ + dir = 4 + }, +/turf/open/floor/iron/dark/textured_edge{ + dir = 1 + }, +/area/station/science/robotics/lab) "xgj" = ( /obj/machinery/atmospherics/pipe/smart/simple/green/visible{ dir = 10 @@ -83424,6 +83447,18 @@ }, /turf/open/floor/carpet, /area/station/medical/psychology) +"xib" = ( +/obj/effect/turf_decal/trimline/yellow/filled/corner{ + dir = 8 + }, +/obj/structure/chair{ + dir = 8 + }, +/obj/effect/landmark/start/depsec/engineering, +/turf/open/floor/iron/textured_corner{ + dir = 8 + }, +/area/station/security/checkpoint/engineering) "xig" = ( /obj/machinery/light/small/directional/east, /obj/machinery/growing/soil, @@ -85590,26 +85625,6 @@ /obj/structure/cable, /turf/open/floor/plating/airless, /area/station/command/heads_quarters/rd) -"xNl" = ( -/obj/machinery/door/firedoor, -/obj/structure/cable, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/effect/turf_decal/stripes/line, -/obj/structure/disposalpipe/segment, -/obj/machinery/door/poddoor/preopen{ - id = "engielock"; - name = "Engineering Lockdown Blast Door" - }, -/obj/machinery/door/airlock/engineering/glass{ - name = "EngineRoom" - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/mapping_helpers/airlock/access/any/engineering/construction, -/turf/open/floor/iron/textured_large, -/area/station/engineering/main) "xNE" = ( /obj/machinery/portable_atmospherics/canister/air, /obj/effect/decal/cleanable/dirt, @@ -86631,21 +86646,6 @@ /obj/machinery/light/floor/has_bulb, /turf/open/floor/iron/textured_large, /area/station/engineering/atmos) -"ybV" = ( -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/turf_decal/bot, -/obj/machinery/light/cold/directional/east, -/obj/effect/landmark/start/cyborg, -/obj/machinery/recharge_station, -/turf/open/floor/iron/dark/textured_edge{ - dir = 8 - }, -/area/station/science/robotics/lab) "ycf" = ( /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/tile/neutral/diagonal_centre, @@ -111825,7 +111825,7 @@ uyk uyk byq xEs -gvI +vLC gJS urP niG @@ -114589,7 +114589,7 @@ cIG qpn jgD dow -uZB +uqv jDA pxK dlB @@ -114648,7 +114648,7 @@ ixX gzZ qxO voi -snJ +dzC qxO kCj qft @@ -115102,7 +115102,7 @@ gvl uwZ opQ jgD -oVw +bqS oee lXR eaV @@ -115112,7 +115112,7 @@ rQr xrI qHf wuT -izJ +hii bJt dje fKS @@ -115342,7 +115342,7 @@ iqc sfj fXK hIA -diH +mLe fgT wKt xyx @@ -115350,7 +115350,7 @@ bES kTS wKt caG -gEJ +kxM bFO uex pjh @@ -115359,7 +115359,7 @@ oFy aKW fDJ jgD -lWk +qND oee mwx uln @@ -115418,7 +115418,7 @@ cnC ixX lZg nFV -nJe +kTa rLB lbI nmy @@ -115625,7 +115625,7 @@ wCL lSX rhM qHf -iTJ +lPj wpv lBP dje @@ -115675,7 +115675,7 @@ cnC abF lZg uHn -fch +wuV tKO ivR wBL @@ -115883,7 +115883,7 @@ sms cuZ dje hMD -nXH +tHk lDz dje lPo @@ -116899,7 +116899,7 @@ gkm lai neu vuI -miV +wjg ipW eoB jzi @@ -117405,9 +117405,9 @@ aIL jjg ijg vBP -dwE -gIr -vmI +xfs +rUV +jTK gWa tqZ oMC @@ -117424,10 +117424,10 @@ aZy heq iSr hYl -amZ -nLM -wVw -vRB +rdQ +pFH +oeG +xac dIO tlq wvU @@ -117684,7 +117684,7 @@ lTr kPu elp oEs -iGc +nWp aqg tlq bmt @@ -117920,7 +117920,7 @@ for frk vBP mVv -exy +ekr vrs hyx akq @@ -117936,13 +117936,13 @@ nEm swP qpy ajz -lDK -kJi +pxC +rxL lTr -jbi +bPX rlk fnq -gKY +bWv tlq bmt euG @@ -118197,7 +118197,7 @@ dEb rqo qEU wvu -cuq +xgi qlN tBV qlN @@ -118279,8 +118279,8 @@ erh sxd hFa hpe -iYZ -uAE +rOg +xib tlS hxy tQP @@ -118435,7 +118435,7 @@ irl vBP jYI eKU -dcF +bSm hyx uNE lTE @@ -118706,8 +118706,8 @@ gMy qlN uPU qlN -aMf -ybV +utL +dwh vOI imz oqe @@ -118791,7 +118791,7 @@ uWW gXL erh sxd -fWZ +apj hpe qks hOM @@ -119055,7 +119055,7 @@ czC fmD bYQ daQ -hXj +lqg xpC hgs wBI @@ -119225,7 +119225,7 @@ lue sYe yeD uVY -mhn +wzR xqf pDu nqS @@ -119482,7 +119482,7 @@ oUE dvq vNz dHE -mSx +ijh nmx dto dto @@ -120341,7 +120341,7 @@ qfk wBI wBI uIw -bbW +mUG uIw wBI jtJ @@ -120852,7 +120852,7 @@ fol oxL enl lOY -xNl +rkz sJO qQu pkH @@ -125164,7 +125164,7 @@ jzQ nQl fLp dWg -cYI +jbG vZM uuO rCr @@ -129327,7 +129327,7 @@ uho cHk shC mqJ -kBs +gkd nnr oLo feh @@ -129585,7 +129585,7 @@ iZL oLo nGM lGO -pHC +qkd shC dsj idx @@ -130098,7 +130098,7 @@ htz yaa oLo iiU -lCb +mqD ovi oLo phj diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 78d1283bcc46..3692aa4d4b9a 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -164,6 +164,7 @@ #define INIT_ORDER_OUTPUTS 35 #define INIT_ORDER_RESTAURANT 34 #define INIT_ORDER_POLLUTION 32 +#define INIT_ORDER_CASSETTES 31 // monkestation addition: cassettes initialize before atoms, so that cassette stuff can be used in Initialize() #define INIT_ORDER_ATOMS 30 #define INIT_ORDER_ARMAMENTS 27 #define INIT_ORDER_LANGUAGE 25 diff --git a/code/__DEFINES/traits/monkestation/declarations.dm b/code/__DEFINES/traits/monkestation/declarations.dm index 29777a2e6fbc..7863be4ac5d9 100644 --- a/code/__DEFINES/traits/monkestation/declarations.dm +++ b/code/__DEFINES/traits/monkestation/declarations.dm @@ -1,5 +1,9 @@ // BEGIN TRAIT DEFINES +// /mob +/// This mob can hear the music from the DJ station. +#define TRAIT_CAN_HEAR_MUSIC "can_hear_radio" + // /mob/living /// Monkeys are friendly/neutral to this mob by defaulot. #define TRAIT_MONKEYFRIEND "monkeyfriend" diff --git a/code/__DEFINES/~monkestation/cassettes.dm b/code/__DEFINES/~monkestation/cassettes.dm new file mode 100644 index 000000000000..e4ec6a1cdcb2 --- /dev/null +++ b/code/__DEFINES/~monkestation/cassettes.dm @@ -0,0 +1,13 @@ +/// Path to the base directory for cassette stuff +#define CASSETTE_BASE_DIR "data/cassette_storage/" +/// Path to the file containing a list of cassette IDs. +#define CASSETTE_ID_FILE (CASSETTE_BASE_DIR + "ids.json") +/// Path to the data for the cassette of the given ID. +#define CASSETTE_FILE(id) (CASSETTE_BASE_DIR + "[id].json") + +/// This cassette is unapproved, and has not been submitted for review. +#define CASSETTE_STATUS_UNAPPROVED 0 +/// This cassette is under review. +#define CASSETTE_STATUS_REVIEWING 1 +/// This cassette has been approved. +#define CASSETTE_STATUS_APPROVED 2 diff --git a/code/__DEFINES/~monkestation/dcs/signals/signals_global.dm b/code/__DEFINES/~monkestation/dcs/signals/signals_global.dm index 7505478a365c..409cb8bf4043 100644 --- a/code/__DEFINES/~monkestation/dcs/signals/signals_global.dm +++ b/code/__DEFINES/~monkestation/dcs/signals/signals_global.dm @@ -2,3 +2,7 @@ #define COMSIG_GLOB_GOLDENEYE_KEY_CREATED "!goldeneye_key_created" /// Sent whenever a camera network broadcast is started/stopped/updated: (camera_net, is_show_active, announcement) #define COMSIG_GLOB_NETWORK_BROADCAST_UPDATED "!network_broadcast_updated" +/// Sent whenever a mob becomes capable of hearing DJ music: (mob/listener) +#define COMSIG_GLOB_ADD_MUSIC_LISTENER "!add_music_listener" +/// Sent whenever a mob becomes no longer capable of hearing DJ music: (mob/listener) +#define COMSIG_GLOB_REMOVE_MUSIC_LISTENER "!remove_music_listener" diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 7112c0447ae6..4fedbe19a931 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -301,7 +301,8 @@ 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() +#warn TODO: cassette refunds + // refund_cassette() distribute_rewards() sleep(5 SECONDS) ready_for_reboot = TRUE diff --git a/code/__HELPERS/~monkestation-helpers/roundend.dm b/code/__HELPERS/~monkestation-helpers/roundend.dm index 5351e9d2bcff..544031be499f 100644 --- a/code/__HELPERS/~monkestation-helpers/roundend.dm +++ b/code/__HELPERS/~monkestation-helpers/roundend.dm @@ -37,6 +37,8 @@ if(total_payout) client?.prefs?.adjust_metacoins(client?.ckey, total_payout, "Challenge rewards.") +#warn TODO: cassette refunds +/* /datum/controller/subsystem/ticker/proc/refund_cassette() if(!length(GLOB.cassette_reviews)) return @@ -62,3 +64,4 @@ message_admins("Balance not adjusted for Cassette:[review.submitted_tape.name], Balance for [client]; Previous:[prev_bal], Expected:[prev_bal + 5000], Current:[client?.prefs?.metacoins]. Issue logged.") log_admin("Balance not adjusted for Cassette:[review.submitted_tape.name], Balance for [client]; Previous:[prev_bal], Expected:[prev_bal + 5000], Current:[client?.prefs?.metacoins].") qdel(review) +*/ diff --git a/code/__HELPERS/~monkestation-helpers/text.dm b/code/__HELPERS/~monkestation-helpers/text.dm new file mode 100644 index 000000000000..cb6383f2e031 --- /dev/null +++ b/code/__HELPERS/~monkestation-helpers/text.dm @@ -0,0 +1,6 @@ +/// Checks to see if a string starts with http:// or https:// +/proc/is_http_protocol(text) + var/static/regex/http_regex + if(isnull(http_regex)) + http_regex = new("^https?://") + return findtext(text, http_regex) diff --git a/code/_globalvars/_regexes.dm b/code/_globalvars/_regexes.dm index 7297d509a918..e850889a42b7 100644 --- a/code/_globalvars/_regexes.dm +++ b/code/_globalvars/_regexes.dm @@ -1,7 +1,4 @@ //These are a bunch of regex datums for use /((any|every|no|some|head|foot)where(wolf)?\sand\s)+(\.[\.\s]+\s?where\?)?/i -GLOBAL_DATUM_INIT(is_http_protocol, /regex, regex("^https?://")) -GLOBAL_DATUM_INIT(is_http_protocol_non_secure, /regex, regex("^http?://")) - GLOBAL_DATUM_INIT(is_website, /regex, regex("http|www.|\[a-z0-9_-]+.(com|org|net|mil|edu)+", "i")) GLOBAL_DATUM_INIT(is_email, /regex, regex("\[a-z0-9_-]+@\[a-z0-9_-]+.\[a-z0-9_-]+", "i")) diff --git a/code/controllers/configuration/entries/monkestation.dm b/code/controllers/configuration/entries/monkestation.dm index 988fae0853c4..d9e5cdf60084 100644 --- a/code/controllers/configuration/entries/monkestation.dm +++ b/code/controllers/configuration/entries/monkestation.dm @@ -69,6 +69,8 @@ default = "http://127.0.0.1:1330" /datum/config_entry/string/plexora_url/ValidateAndSet(str_val) - if(!findtext(str_val, GLOB.is_http_protocol)) + if(!is_http_protocol(str_val)) return FALSE return ..() + +/datum/config_entry/flag/cassettes_in_db diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index d2077b1fed80..d61b48fb7b4e 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -19,7 +19,8 @@ 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*/ +#warn TODO: cassette reviews + // /client/proc/review_cassettes, /*monkestation addition Opens the Cassette Review menu*/ /client/proc/stop_sounds, /client/proc/tag_datum_mapview, ) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index fcc56af55df6..3ae0bff78b46 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1806,6 +1806,8 @@ message_admins("[key_name_admin(owner)] rejected a [token_holder.in_queue] token from [ADMIN_LOOKUPFLW(user_client)]") log_admin("[user_client]'s [token_holder.in_queue] token has been rejected by [owner].") +#warn TODO: cassette reviews +/* else if(href_list["open_music_review"]) if(!check_rights(R_ADMIN)) return @@ -1814,7 +1816,7 @@ if(!istype(cassette_review)) return cassette_review.ui_interact(usr) - +*/ else if(href_list["approve_token_event"]) if(!check_rights(R_ADMIN)) return diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index 784963f00d13..15e12625c8a9 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -150,7 +150,7 @@ message_admins("[key_name(user)] stopped web sounds.") web_sound_url = null stop_web_sounds = TRUE - if(web_sound_url && !findtext(web_sound_url, GLOB.is_http_protocol)) + if(web_sound_url && !is_http_protocol(web_sound_url)) tgui_alert(user, "The media provider returned a content URL that isn't using the HTTP or HTTPS protocol. This is a security risk and the sound will not be played.", "Security Risk", list("OK")) to_chat(user, span_boldwarning("BLOCKED: Content URL not using HTTP(S) Protocol!"), confidential = TRUE) @@ -183,7 +183,7 @@ if(length(web_sound_input)) web_sound_input = trim(web_sound_input) - if(findtext(web_sound_input, ":") && !findtext(web_sound_input, GLOB.is_http_protocol)) + if(findtext(web_sound_input, ":") && !is_http_protocol(web_sound_input)) to_chat(src, span_boldwarning("Non-http(s) URIs are not allowed."), confidential = TRUE) to_chat(src, span_warning("For youtube-dl shortcuts like ytsearch: please use the appropriate full URL from the website."), confidential = TRUE) return diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 6fb429925942..5500820e12b7 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -244,12 +244,6 @@ humanc.load_persistent_scars() SSpersistence.load_modular_persistence(humanc.get_organ_slot(ORGAN_SLOT_BRAIN)) - //monkestation edit start - if(GLOB.dj_booth) - var/obj/machinery/cassette/dj_station/dj = GLOB.dj_booth - dj.add_new_player(humanc) - //monkestation edit end - if(GLOB.curse_of_madness_triggered) give_madness(humanc, GLOB.curse_of_madness_triggered) diff --git a/code/modules/requests/request_manager.dm b/code/modules/requests/request_manager.dm index b39cb2d9ad72..34e987799e2d 100644 --- a/code/modules/requests/request_manager.dm +++ b/code/modules/requests/request_manager.dm @@ -246,7 +246,7 @@ GLOBAL_DATUM_INIT(requests, /datum/request_manager, new) if(request.req_type != REQUEST_INTERNET_SOUND) to_chat(usr, "Request doesn't have a sound to play.", confidential = TRUE) return TRUE - if(findtext(request.message, ":") && !findtext(request.message, GLOB.is_http_protocol)) + if(findtext(request.message, ":") && !is_http_protocol(request.message)) to_chat(usr, "Request is not a valid URL.", confidential = TRUE) return TRUE diff --git a/code/modules/tgui_panel/audio.dm b/code/modules/tgui_panel/audio.dm index 680696159943..3397434b8970 100644 --- a/code/modules/tgui_panel/audio.dm +++ b/code/modules/tgui_panel/audio.dm @@ -20,9 +20,7 @@ * optional extra_data list Optional settings. */ /datum/tgui_panel/proc/play_music(url, extra_data) - if(!is_ready()) - return - if(!findtext(url, GLOB.is_http_protocol)) + if(!is_ready() || !is_http_protocol(url)) return var/list/payload = list() if(length(extra_data) > 0) diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm index 1877abe8ddf5..2b1656903f0a 100644 --- a/code/modules/unit_tests/unit_test.dm +++ b/code/modules/unit_tests/unit_test.dm @@ -328,8 +328,8 @@ GLOBAL_VAR_INIT(focused_tests, focused_tests()) ///we generate mobs in these and create destroy does this in null space ignore += typesof(/obj/item/loot_table_maker) ///we need to use json_decode to run randoms properly - ignore += typesof(/obj/item/device/cassette_tape) - ignore += typesof(/datum/cassette/cassette_tape) + ignore += typesof(/obj/item/cassette_tape) + ignore += typesof(/datum/cassette) ///we also dont want weathers or weather events as they will hold refs to alot of stuff as they shouldn't be deleted ignore += typesof(/datum/weather_event) ignore += typesof(/datum/particle_weather) diff --git a/config/config.txt b/config/config.txt index 1078e1d6e593..98867df83c30 100644 --- a/config/config.txt +++ b/config/config.txt @@ -567,3 +567,5 @@ CONFIG_ERRORS_RUNTIME ## The age in days if minimum account age is on #MINIMUM_AGE +## If enabled, cassette tapes will be stored in the database, rather than JSON files on-disk. +#CASSETTES_IN_DB diff --git a/monkestation/code/game/objects/items/devices/radio/headset.dm b/monkestation/code/game/objects/items/devices/radio/headset.dm index 8be0cd0cbe8d..d51020d250b9 100644 --- a/monkestation/code/game/objects/items/devices/radio/headset.dm +++ b/monkestation/code/game/objects/items/devices/radio/headset.dm @@ -1,3 +1,12 @@ +/obj/item/radio/headset/equipped(mob/user, slot, initial) + . = ..() + if(slot_flags & slot) + ADD_TRAIT(user, TRAIT_CAN_HEAR_MUSIC, REF(src)) + +/obj/item/radio/headset/dropped(mob/user, silent) + . = ..() + REMOVE_TRAIT(user, TRAIT_CAN_HEAR_MUSIC, REF(src)) + /obj/item/radio/headset/headset_secmed name = "brig physician radio headset" desc = "This is used by your secure doctor." diff --git a/monkestation/code/game/objects/items/implants/implant_misc.dm b/monkestation/code/game/objects/items/implants/implant_misc.dm new file mode 100644 index 000000000000..a84c63c522fd --- /dev/null +++ b/monkestation/code/game/objects/items/implants/implant_misc.dm @@ -0,0 +1,9 @@ +/obj/item/implant/radio/implant(mob/living/target, mob/user, silent, force) + . = ..() + if(.) + ADD_TRAIT(target, TRAIT_CAN_HEAR_MUSIC, REF(src)) + +/obj/item/implant/radio/removed(mob/living/source, silent, special) + . = ..() + if(.) + REMOVE_TRAIT(source, TRAIT_CAN_HEAR_MUSIC, REF(src)) diff --git a/monkestation/code/modules/admin/verbs/spawn_mixtape.dm b/monkestation/code/modules/admin/verbs/spawn_mixtape.dm index bffca51317a0..db39c7529087 100644 --- a/monkestation/code/modules/admin/verbs/spawn_mixtape.dm +++ b/monkestation/code/modules/admin/verbs/spawn_mixtape.dm @@ -3,19 +3,22 @@ set name = "Spawn Mixtape" set desc = "Select an approved mixtape to spawn at your location." - var/datum/mixtape_spawner/tgui = new(usr)//create the datum - tgui.ui_interact(usr)//datum has a tgui component, here we open the window + if(!check_rights(R_ADMIN)) + return + new /datum/mixtape_spawner(src) /datum/mixtape_spawner - var/client/holder //client of whoever is using this datum + /// The client of whoever is using this datum. + var/client/holder /datum/mixtape_spawner/New(user)//user can either be a client or a mob due to byondcode(tm) - if (istype(user, /client)) - var/client/user_client = user - holder = user_client //if its a client, assign it to holder - else - var/mob/user_mob = user - holder = user_mob.client //if its a mob, assign the mob's client to holder + . = ..() + holder = get_player_client(user) + ui_interact(holder.mob) + +/datum/mixtape_spawner/Destroy(force) + holder = null + return ..() /datum/mixtape_spawner/ui_state(mob/user) return GLOB.admin_state @@ -27,31 +30,38 @@ ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "MixtapeSpawner") + ui.set_autoupdate(FALSE) ui.open() -/datum/mixtape_spawner/ui_data(mob/user) - var/list/data = list() - if(!length(SScassette_storage.cassette_datums)) - return - for(var/datum/cassette_data/cassette in SScassette_storage.cassette_datums) - data["approved_cassettes"] += list(list( - "name" = cassette.cassette_name, - "desc" = cassette.cassette_desc, - "cassette_design_front" = cassette.cassette_design_front, - "creator_ckey" = cassette.cassette_author_ckey, - "creator_name" = cassette.cassette_author, - "song_names" = cassette.song_names, - "id" = cassette.cassette_id +/datum/mixtape_spawner/ui_static_data(mob/user) + var/list/approved_cassettes = list() + for(var/datum/cassette/cassette as anything in SScassettes.cassettes) + if(cassette.status != CASSETTE_STATUS_APPROVED) + continue + approved_cassettes += list(list( + "name" = cassette.name, + "desc" = cassette.desc, + "cassette_design_front" = cassette.front.design, + "creator_ckey" = ckey(cassette.author.ckey), + "creator_name" = cassette.author.name, + "song_names" = cassette.list_song_names(), + "id" = cassette.id, )) - return data + return list("approved_cassettes" = approved_cassettes) -/datum/mixtape_spawner/ui_act(action, params) +/datum/mixtape_spawner/ui_act(action, list/params, datum/tgui/ui) . = ..() if(.) return + var/mob/user = ui.user switch(action) if("spawn") - if (params["id"]) - new/obj/item/device/cassette_tape(usr.loc, params["id"]) - SSblackbox.record_feedback("tally", "admin_verb", 1, "Spawn Mixtape") - log_admin("[key_name(usr)] created mixtape [params["id"]] at [usr.loc].") + var/id = params["id"] + if(!id) + return + var/atom/spawn_loc = user.drop_location() + new /obj/item/cassette_tape(spawn_loc, id) + SSblackbox.record_feedback("tally", "admin_verb", 1, "Spawn Mixtape") + message_admins("[key_name_admin(user)] spawned mixtape [id] at [ADMIN_COORDJMP(spawn_loc)].") + log_admin("[key_name(user)] spawned mixtape [id] at [loc_name(spawn_loc)].") + return TRUE diff --git a/monkestation/code/modules/cargo/crates/goodies.dm b/monkestation/code/modules/cargo/crates/goodies.dm index 79ed63cfb20d..d199df9b2f9f 100644 --- a/monkestation/code/modules/cargo/crates/goodies.dm +++ b/monkestation/code/modules/cargo/crates/goodies.dm @@ -8,10 +8,10 @@ name = "Cassette Mini-Pack" desc = "Alright, we'll admit it, 10 cassettes are too much for the majority of our users. Contains 3 Approved Cassettes." cost = PAYCHECK_CREW * 5 - contains = list(/obj/item/device/cassette_tape/random = 3) + contains = list(/obj/item/cassette_tape/random = 3) /datum/supply_pack/goody/blankcassette name = "Blank Cassette Mini-Pack" desc = "NO! We wont admit defeat! You will march yourself down to the Service section and purchase the 10 Blank Cassette pack instead of this Weak 3 Blank Cassette Pack!" cost = PAYCHECK_CREW * 3 - contains = list(/obj/item/device/cassette_tape/blank = 3) + contains = list(/obj/item/cassette_tape/blank = 3) diff --git a/monkestation/code/modules/cargo/crates/service.dm b/monkestation/code/modules/cargo/crates/service.dm index 3b0c69c0efe9..fe7fbcd48ba3 100644 --- a/monkestation/code/modules/cargo/crates/service.dm +++ b/monkestation/code/modules/cargo/crates/service.dm @@ -45,13 +45,13 @@ /datum/supply_pack/service/cassettes/fill(obj/structure/closet/crate/our_crate) for(var/id in unique_random_tapes(10)) - new /obj/item/device/cassette_tape(our_crate, id) + new /obj/item/cassette_tape(our_crate, id) /datum/supply_pack/service/blankcassettes name = "Blank Cassettes Crate" desc = "in the VERY unlikely event you have run out of blank cassettes, you can get 10 blank ones here. Contains 10 blank cassettes for use in Walkmans." cost = CARGO_CRATE_VALUE * 2 - contains = list(/obj/item/device/cassette_tape/blank = 10) + contains = list(/obj/item/cassette_tape/blank = 10) crate_name = "cassette crate" /datum/supply_pack/service/walkmen diff --git a/monkestation/code/modules/cassettes/cassette.dm b/monkestation/code/modules/cassettes/cassette.dm index 9fdfe816cb7e..e43e88b4aa12 100644 --- a/monkestation/code/modules/cassettes/cassette.dm +++ b/monkestation/code/modules/cassettes/cassette.dm @@ -1,86 +1,75 @@ -/obj/item/device/cassette_tape +/obj/item/cassette_tape name = "Debug Cassette Tape" desc = "You shouldn't be seeing this!" icon = 'monkestation/code/modules/cassettes/icons/walkman.dmi' icon_state = "cassette_flip" w_class = WEIGHT_CLASS_SMALL - ///icon of the cassettes front side - var/side1_icon = "cassette_worstmap" - var/side2_icon = "cassette_worstmap" - ///if the cassette is flipped, for playing second list of songs + item_flags = NOBLUDGEON + /// If the cassette is flipped, for playing second list of songs. var/flipped = FALSE - ///list of songs each side has to play - var/list/songs = list("side1" = list(), - "side2" = list()) - ///list of each songs name in the order they appear - var/list/song_names = list("side1" = list(), - "side2" = list()) - ///the id of the cassette - var/id - ///the ckey of the cassette author - var/ckey_author - ///the authors name displayed in examine text - var/author_name - ///are we an approved tape? - var/approved_tape = FALSE - ///are we random? + /// The data for this cassette. + var/datum/cassette/cassette_data + /// Should we just spawn a random cassette? var/random = FALSE - var/cassette_desc_string = "Generic Desc" + /// ID of the cassette to spawn in as by default. + var/id -/obj/item/device/cassette_tape/Initialize(mapload, spawned_id) +/obj/item/cassette_tape/Initialize(mapload, spawned_id) . = ..() - if(!length(GLOB.approved_ids)) - GLOB.approved_ids = initialize_approved_ids() - - if(length(GLOB.approved_ids)) - if(spawned_id && (spawned_id in GLOB.approved_ids)) - id = spawned_id - else if(random) - id = pick(GLOB.approved_ids) - - var/file = file("data/cassette_storage/[id].json") - if(!fexists(file)) - return + spawned_id ||= id + if(!isnull(spawned_id)) + cassette_data = SScassettes.load_cassette(spawned_id) + else if(random) + var/list/random_cassette = SScassettes.unique_random_cassettes(amount = 1, status = CASSETTE_STATUS_APPROVED) + if(length(random_cassette)) + cassette_data = random_cassette[1] + cassette_data ||= new + update_appearance(UPDATE_DESC | UPDATE_ICON_STATE) - var/list/data = json_decode(file2text(file)) - name = data["name"] - cassette_desc_string = data["desc"] - icon_state = data["side1_icon"] - side1_icon = data["side1_icon"] - side2_icon = data["side2_icon"] - songs = data["songs"] - song_names = data["song_names"] - author_name = data["author_name"] - ckey_author = data["author_ckey"] - approved_tape = data["approved"] +/obj/item/cassette_tape/Destroy(force) + cassette_data = null + return ..() - update_appearance() - -/obj/item/device/cassette_tape/attack_self(mob/user) - ..() - icon_state = flipped ? side1_icon : side2_icon +/obj/item/cassette_tape/attack_self(mob/user) + . = ..() flipped = !flipped to_chat(user, span_notice("You flip [src].")) + update_appearance(UPDATE_ICON_STATE) + +/obj/item/cassette_tape/update_desc(updates) + desc = cassette_data.desc || "A generic cassette." + return ..() -/obj/item/device/cassette_tape/update_desc(updates) +/obj/item/cassette_tape/update_icon_state() + icon_state = cassette_data.get_side(!flipped)?.design || src::icon_state + return ..() + +/obj/item/cassette_tape/examine(mob/user) . = ..() - desc = cassette_desc_string - desc += "\n" - if(!approved_tape) - desc += span_warning("It appears to be a bootleg tape, quality is not a guarantee!\n") - if(author_name) - desc += span_notice("Mixed by [author_name]\n") + switch(cassette_data.status) + if(CASSETTE_STATUS_UNAPPROVED) + . += span_warning("It appears to be a bootleg tape, quality is not a guarantee!") + . += span_notice("In order to play this tape for the whole station, it must be submitted to the Space Board of Music and approved.") + if(CASSETTE_STATUS_REVIEWING) + . += span_warning("It seems this tape is still being reviewed by the Space Board of Music.") + if(CASSETTE_STATUS_APPROVED) + . += span_info("This cassette has been approved by the Space Board of Music, and can be played for the whole station with the Cassette Player.") + else + stack_trace("Unknown status [cassette_data.status] for cassette [cassette_data.name] ([cassette_data.id])") -/obj/item/device/cassette_tape/attackby(obj/item/item, mob/living/user) + if(cassette_data.author.name) + . += span_info("Mixed by [span_name(cassette_data.author.name)]") + +/obj/item/cassette_tape/attackby(obj/item/item, mob/living/user) if(!istype(item, /obj/item/pen)) return ..() - var/choice = tgui_input_list(usr, "What would you like to change?", items = list("Cassette Name", "Cassette Description", "Cancel")) + var/choice = tgui_input_list(user, "What would you like to change?", items = list("Cassette Name", "Cassette Description", "Cancel")) switch(choice) if("Cassette Name") ///the name we are giving the cassette - var/newcassettename = reject_bad_text(tgui_input_text(user, "Write a new Cassette name:", name, name, max_length = MAX_NAME_LEN)) - if(!user.can_perform_action (src, TRUE)) + var/newcassettename = reject_bad_text(tgui_input_text(user, "Write a new Cassette name:", name, html_decode(name), max_length = MAX_NAME_LEN)) + if(!user.can_perform_action(src, TRUE)) return if(length(newcassettename) > MAX_NAME_LEN) to_chat(user, span_warning("That name is too long!")) @@ -92,7 +81,7 @@ name = "[lowertext(newcassettename)]" if("Cassette Description") ///the description we are giving the cassette - var/newdesc = tgui_input_text(user, "Write a new description:", name, desc, max_length = 180) + var/newdesc = tgui_input_text(user, "Write a new description:", name, html_decode(desc), max_length = 180) if(!user.can_perform_action(src, TRUE)) return if (length(newdesc) > 180) @@ -101,29 +90,11 @@ if(!newdesc) to_chat(user, span_warning("That description is invalid.")) return - cassette_desc_string = newdesc - update_appearance() - else - return - -/datum/cassette/cassette_tape - var/name = "Broken Cassette" - var/desc = "You shouldn't be seeing this! Make an issue about it" - var/icon_state = "cassette_flip" - var/side1_icon = "cassette_flip" - var/side2_icon = "cassette_flip" - var/id = "blank" - var/creator_ckey = "Dwasint" - var/creator_name = "Collects-The-Candy" - var/approved = TRUE - var/list/song_names = list("side1" = list(), - "side2" = list()) - - var/list/songs = list("side1" = list(), - "side2" = list()) + cassette_data.desc = newdesc + update_appearance(UPDATE_DESC) -/obj/item/device/cassette_tape/blank +/obj/item/cassette_tape/blank id = "blank" -/obj/item/device/cassette_tape/friday +/obj/item/cassette_tape/friday id = "friday" diff --git a/monkestation/code/modules/cassettes/cassette_approval.dm b/monkestation/code/modules/cassettes/cassette_approval.dm index e53173d972aa..099307a903e5 100644 --- a/monkestation/code/modules/cassettes/cassette_approval.dm +++ b/monkestation/code/modules/cassettes/cassette_approval.dm @@ -1,7 +1,9 @@ +#warn TODO: cassette reviewing/approvals +/* GLOBAL_LIST_INIT(cassette_reviews, list()) #define ADMIN_OPEN_REVIEW(id) "(Open Review)" -/proc/submit_cassette_for_review(obj/item/device/cassette_tape/submitted, mob/user) +/proc/submit_cassette_for_review(obj/item/cassette_tape/submitted, mob/user) if(!user.client) return var/datum/cassette_review/new_review = new @@ -25,7 +27,7 @@ GLOBAL_LIST_INIT(cassette_reviews, list()) has requested a review on their cassette."))]") to_chat(user, span_notice("Your Cassette has been sent to the Space Board of Music for review, you will be notified when an outcome has been made.")) -/obj/item/device/cassette_tape/proc/generate_cassette_json() +/obj/item/cassette_tape/proc/generate_cassette_json() if(approved_tape) return if(!length(GLOB.approved_ids)) @@ -70,7 +72,7 @@ GLOBAL_LIST_INIT(cassette_reviews, list()) "song_url" = list() ) ) - var/obj/item/device/cassette_tape/submitted_tape + var/obj/item/cassette_tape/submitted_tape var/action_taken = FALSE var/verdict = "NONE" @@ -220,3 +222,4 @@ GLOBAL_LIST_INIT(cassette_reviews, list()) return +*/ diff --git a/monkestation/code/modules/cassettes/cassette_db/cassette_datum.dm b/monkestation/code/modules/cassettes/cassette_db/cassette_datum.dm index 07efe51ea95e..3671e96c50c1 100644 --- a/monkestation/code/modules/cassettes/cassette_db/cassette_datum.dm +++ b/monkestation/code/modules/cassettes/cassette_db/cassette_datum.dm @@ -1,61 +1,182 @@ -/datum/cassette_data - var/cassette_name - var/cassette_author - var/cassette_desc - var/cassette_author_ckey - - var/cassette_design_front - var/cassette_design_back - - var/list/songs - - var/list/song_names - - var/cassette_id - var/approved - var/file_name - - -/datum/cassette_data/proc/populate_data(file_id) - var/file = file("data/cassette_storage/[file_id].json") - if(!fexists(file)) +/datum/cassette + /// The unique ID of the cassette. + var/id + /// The name of the cassette. + var/name + /// The description of the cassette. + var/desc + /// The status of this cassette. + var/status = CASSETTE_STATUS_UNAPPROVED + /// Information about the author of this cassette. + var/datum/cassette_author/author + + /// The front side of the cassette. + var/datum/cassette_side/front + /// The back side of the cassette. + var/datum/cassette_side/back + +/datum/cassette/New() + . = ..() + author = new + front = new + back = new + +/datum/cassette/Destroy(force) + QDEL_NULL(author) + QDEL_NULL(front) + QDEL_NULL(back) + return ..() + +/// Imports cassette date from the old format. +/datum/cassette/proc/import_old_format(list/data) + name = data["name"] + desc = data["desc"] + if("status" in data) + status = data["status"] + else + status = data["approved"] ? CASSETTE_STATUS_APPROVED : CASSETTE_STATUS_UNAPPROVED + + author.name = data["author_name"] + author.ckey = ckey(data["author_ckey"]) + + for(var/i in 1 to 2) + var/datum/cassette_side/side = get_side(i % 2) // side2 = 0, side1 = 1 + var/side_name = "side[i]" + var/list/song_urls = data["songs"][side_name] + var/list/song_names = data["song_names"][side_name] + if(length(song_urls) != length(song_names)) + stack_trace("amount of song urls for [side_name] ([length(song_urls)]) did not match amount of song names for [side_name] ([length(song_names)])") + continue + side.design = data["[side_name]_icon"] + for(var/idx in 1 to length(song_urls)) + side.songs += new /datum/cassette_song(song_names[idx], song_urls[idx]) + +/// Exports cassette date in the old format. +/datum/cassette/proc/export_old_format() as /list + RETURN_TYPE(/list) + . = list( + "name" = name, + "desc" = desc, + "side1_icon" = /datum/cassette_side::design, + "side2_icon" = /datum/cassette_side::design, + "author_name" = author.name, + "author_ckey" = ckey(author.ckey), + "approved" = status == CASSETTE_STATUS_APPROVED, + "status" = status, + "songs" = list( + "side1" = list(), + "side2" = list(), + ), + "song_names" = list( + "side1" = list(), + "side2" = list(), + ), + ) + for(var/i in 1 to 2) + var/datum/cassette_side/side = get_side(i % 2) // side2 = 0, side1 = 1 + var/side_name = "side[i]" + var/list/names = list() + var/list/urls = list() + .["[side_name]_icon"] = side.design + for(var/datum/cassette_song/song as anything in side.songs) + names += song.name + urls += song.url + .["song_names"][side_name] = names + .["songs"][side_name] = urls + +/// Saves the cassette to the data folder, in JSON format. +/datum/cassette/proc/save_to_file() + if(!id) + CRASH("Attempted to save cassette without an ID to disk") + rustg_file_write(json_encode(export_old_format(), JSON_PRETTY_PRINT), CASSETTE_FILE(id)) + +/// Saves the cassette to the database. +/// Returns TRUE if successful, FALSE otherwise. +/datum/cassette/proc/save_to_db() + if(!id) + CRASH("Attempted to save cassette without an ID to database") + if(!SSdbcore.Connect()) + CRASH("Could not save cassette [id], database not connected") + var/datum/db_query/query_save_cassette = SSdbcore.NewQuery({" + INSERT INTO [format_table_name("cassettes")] + (id, name, desc, status, author_name, author_ckey, front, back) + VALUES + (:id, :name, :desc, :status, :author_name, :author_ckey, :front, :back) + ON DUPLICATE KEY UPDATE + name = VALUES(name), + desc = VALUES(desc), + status = VALUES(status), + author_name = VALUES(author_name), + author_ckey = VALUES(author_ckey), + front = VALUES(front), + back = VALUES(back) + "}, list( + "id" = id, + "name" = name, + "desc" = desc, + "status" = status, + "author_name" = author.name, + "author_name" = ckey(author.ckey), + "front" = json_encode(front.export_for_db()), + "back" = json_encode(back.export_for_db()), + )) + if(!query_save_cassette.warn_execute()) + qdel(query_save_cassette) + stack_trace("Failed to save cassette [id] to database") return FALSE - var/list/data = json_decode(file2text(file)) - - cassette_name = data["name"] - cassette_desc = data["desc"] - - cassette_design_front = data["side1_icon"] - cassette_design_back = data["side2_icon"] - - songs = data["songs"] - - song_names = data["song_names"] - - cassette_author = data["author_name"] - cassette_author_ckey = data["author_ckey"] - - cassette_id = file_id - - approved = data["approved"] - - file_name = "data/cassette_storage/[file_id].json" - + qdel(query_save_cassette) return TRUE -/datum/cassette_data/proc/generate_cassette(turf/location) - if(!location) - return - var/obj/item/device/cassette_tape/new_tape = new(location) - new_tape.name = cassette_name - new_tape.cassette_desc_string = cassette_desc - new_tape.icon_state = cassette_design_front - new_tape.side1_icon = cassette_design_front - new_tape.side2_icon = cassette_design_back - new_tape.songs = songs - new_tape.song_names = song_names - new_tape.author_name = cassette_author - new_tape.ckey_author = cassette_author_ckey - new_tape.approved_tape = approved - - new_tape.update_appearance() + +/// Simple helper to get a side of the cassette. +/// TRUE is front side, FALSE is back side. +/datum/cassette/proc/get_side(front_side = TRUE) as /datum/cassette_side + RETURN_TYPE(/datum/cassette_side) + return front_side ? front : back + +/// Returns a list of all the song names in this cassette. +/// Really only useful for searching for cassettes via contained song names. +/datum/cassette/proc/list_song_names() as /list + RETURN_TYPE(/list) + . = list() + for(var/datum/cassette_song/song as anything in front.songs + back.songs) + . |= song.name + +/datum/cassette_author + /// The character name of the cassette author. + var/name + /// The ckey of the cassette author. + var/ckey + +/datum/cassette_side + /// The design of this side of the cassette. + var/design = "cassette_flip" + /// The songs on this side of the cassette. + var/list/datum/cassette_song/songs = list() + +/// Imports data for this cassette side to the JSON format used by the database. +/datum/cassette_side/proc/import_from_db(list/data) + design = data["design"] + for(var/list/song as anything in data["songs"]) + songs += new /datum/cassette_song(song["name"], song["url"]) + +/// Exports data from this cassette side in the JSON format used by the database. +/datum/cassette_side/proc/export_for_db() + . = list("design" = design, "songs" = list()) + for(var/datum/cassette_song/song as anything in songs) + .["songs"] += list(list("name" = song.name, "url" = song.url)) + +/datum/cassette_side/Destroy(force) + QDEL_LIST(songs) + return ..() + +/datum/cassette_song + /// The name of the song. + var/name + /// The URL of the song. + var/url + +/datum/cassette_song/New(name, url) + . = ..() + src.name = name + src.url = url diff --git a/monkestation/code/modules/cassettes/cassette_db/cassette_manager.dm b/monkestation/code/modules/cassettes/cassette_db/cassette_manager.dm new file mode 100644 index 000000000000..043786409120 --- /dev/null +++ b/monkestation/code/modules/cassettes/cassette_db/cassette_manager.dm @@ -0,0 +1,206 @@ +SUBSYSTEM_DEF(cassettes) + name = "Cassetes" + init_order = INIT_ORDER_CASSETTES + flags = SS_NO_FIRE + /// An associative list of IDs to cassette data. + var/list/datum/cassette/cassettes = list() + +/datum/controller/subsystem/cassettes/Initialize() + . = SS_INIT_FAILURE + if(CONFIG_GET(flag/cassettes_in_db) && !CONFIG_GET(flag/sql_enabled)) + stack_trace("CASSETTES_IN_DB was enabled, despite the SQL database not being enabled! Disabling CASSETTES_IN_DB.") + CONFIG_SET(flag/cassettes_in_db, FALSE) + if(CONFIG_GET(flag/cassettes_in_db)) + if(!SSdbcore.Connect()) + CRASH("Database-based cassettes are enabled, but a connection to the database could not be established!") + if(!load_all_cassettes_from_db()) + CRASH("Failed to load all cassettes from database!") + else + if(!load_all_cassettes_from_json()) + CRASH("Failed to load all cassettes from data folder!") + return SS_INIT_SUCCESS + +/datum/controller/subsystem/cassettes/Recover() + flags |= SS_NO_INIT + cassettes = SScassettes.cassettes + +/// Loads the cassette with the given ID. +/// If `db` is TRUE, it will load the cassette from the database. +/// If `db` is FALSE, the cassette will be loaded from a JSON in the `data/cassette_storage` folder. +/// If `db` is null (the default), it will load from the database if the `CASSETTES_IN_DB` config option is set, otherwise it will load from the JSON files. +/datum/controller/subsystem/cassettes/proc/load_cassette(id, db = null) as /datum/cassette + RETURN_TYPE(/datum/cassette) + if(!id) + return null + else if(istype(id, /datum/cassette)) // so i can be lazy + return id + if(id in cassettes) + return cassettes[id] + if(isnull(db)) + db = CONFIG_GET(flag/cassettes_in_db) + var/datum/cassette/cassette_data = db ? load_cassette_from_db_raw(id) : load_cassette_from_json_raw(id) + if(cassette_data) + cassettes[id] = cassette_data + return cassette_data + +/// Loads the cassette with the given ID from a JSON in the `data/cassette_storage` folder. +/// This does not check the SScassettes.cassettes cache, and you should not use this - this is only used to initialize SScassettes.cassettes +/datum/controller/subsystem/cassettes/proc/load_cassette_from_json_raw(id) as /datum/cassette + RETURN_TYPE(/datum/cassette) + var/cassette_file = CASSETTE_FILE(id) + if(!rustg_file_exists(cassette_file)) + return null + var/cassette_file_data = rustg_file_read(cassette_file) + if(!rustg_json_is_valid(cassette_file_data)) + CRASH("Cassette file [cassette_file] had invalid JSON!") + var/list/cassette_json = json_decode(cassette_file_data) + var/datum/cassette/cassette_data = new + cassette_data.import_old_format(cassette_json) + cassette_data.id = id + return cassette_data + +/// Loads the cassette with the given ID from the database. +/datum/controller/subsystem/cassettes/proc/load_cassette_from_db_raw(id) as /datum/cassette + RETURN_TYPE(/datum/cassette) + if(!SSdbcore.Connect() || !id) + return + var/datum/db_query/query_cassette = SSdbcore.NewQuery("SELECT name, desc, status, author_name, author_ckey, front, back FROM [format_table_name("cassettes")] WHERE id = :id", list("id" = id)) + if(!query_cassette.Execute() || !query_cassette.NextRow()) + qdel(query_cassette) + return + var/name = query_cassette.item[1] + var/desc = query_cassette.item[2] + var/status = query_cassette.item[3] + var/author_name = query_cassette.item[4] + var/author_ckey = query_cassette.item[5] + var/list/front = json_decode(query_cassette.item[6]) + var/list/back = json_decode(query_cassette.item[7]) + qdel(query_cassette) + + var/datum/cassette/cassette = new + cassette.id = id + cassette.name = name + cassette.desc = desc + cassette.status = status + cassette.author.name = author_name + cassette.author.ckey = author_ckey + cassette.front.import_from_db(front) + cassette.back.import_from_db(back) + return cassette + +/// Returns an associative list of id to cassette datums, of all existing saved cassettes. +/// This uses the database. +/datum/controller/subsystem/cassettes/proc/load_all_cassettes_from_db() + . = FALSE + if(!SSdbcore.Connect()) + CRASH("Failed to connect to database") + var/datum/db_query/query_cassettes = SSdbcore.NewQuery("SELECT id, name, desc, status, author_name, author_ckey, front, back FROM [format_table_name("cassettes")]") + if(!query_cassettes.Execute()) + qdel(query_cassettes) + CRASH("Failed to load cassettes from database") + while(query_cassettes.NextRow()) + var/id = query_cassettes.item[1] + var/name = query_cassettes.item[2] + var/desc = query_cassettes.item[3] + var/status = query_cassettes.item[4] + var/author_name = query_cassettes.item[5] + var/author_ckey = query_cassettes.item[6] + var/list/front = json_decode(query_cassettes.item[7]) + var/list/back = json_decode(query_cassettes.item[8]) + + var/datum/cassette/cassette = new + cassette.id = id + cassette.name = name + cassette.desc = desc + cassette.status = status + cassette.author.name = author_name + cassette.author.ckey = author_ckey + cassette.front.import_from_db(front) + cassette.back.import_from_db(back) + + cassettes[id] = cassette + qdel(query_cassettes) + return TRUE + +/// Returns an associative list of id to cassette datums, of all existing saved cassettes. +/// This uses JSON files. +/datum/controller/subsystem/cassettes/proc/load_all_cassettes_from_json() + . = FALSE + if(!rustg_file_exists(CASSETTE_ID_FILE)) // this just means there's no cassettes at all i guess? which is valid. + return TRUE + var/list/ids = json_decode(rustg_file_read(CASSETTE_ID_FILE)) + for(var/id in ids) + if(!ids) + continue + var/datum/cassette/cassette_data = load_cassette_from_json_raw(id) + if(isnull(cassette_data)) + stack_trace("Failed to load cassette [id]") + continue + cassettes[id] = cassette_data + return TRUE + +/// Updates the ids.json file on-disk. +/datum/controller/subsystem/cassettes/proc/save_ids_json() + var/list/ids = list() + if(rustg_file_exists(CASSETTE_ID_FILE)) + // Verify that each cassette ID still exists and is still considered "approved" before adding them to the list. + for(var/id in json_decode(rustg_file_read(CASSETTE_ID_FILE))) + if(!rustg_file_exists(CASSETTE_FILE(id))) + continue + ids += id + for(var/id in cassettes) + var/datum/cassette/cassette = cassettes[id] + if(cassette.status == CASSETTE_STATUS_UNAPPROVED) + ids -= id + else + ids |= id + rustg_file_write(ids, CASSETTE_ID_FILE) + +/// Returns all the cassettes that match the given arguments. +/datum/controller/subsystem/cassettes/proc/filtered_cassettes(status, user_ckey, list/id_blacklist) as /list + RETURN_TYPE(/list/datum/cassette) + . = list() + if(!isnull(user_ckey)) + user_ckey = ckey(user_ckey) + for(var/id in cassettes) + if(!isnull(id_blacklist) && (id in id_blacklist)) + continue + var/datum/cassette/cassette = cassettes[id] + if(!isnull(user_ckey) && ckey(cassette.author.ckey) != user_ckey) + continue + if(!isnull(status) && cassette.status != status) + continue + . += cassette + +/// Returns a list containing up to the specified amount of random, unique cassettes that match the given arguments. +/datum/controller/subsystem/cassettes/proc/unique_random_cassettes(amount = 1, status = CASSETTE_STATUS_APPROVED, user_ckey, list/id_blacklist) as /list + RETURN_TYPE(/list/datum/cassette) + . = list() + var/list/cassettes = filtered_cassettes(status, user_ckey, id_blacklist) + for(var/i in min(amount, length(cassettes))) + . += pick_n_take(cassettes) + +/datum/controller/subsystem/cassettes/proc/migrate_json_cassettes_to_db() + if(!SSdbcore.Connect()) + CRASH("Cannot migrate JSON cassettes to the database if we can't even connect to the database!") + var/list/old_cassettes = cassettes.Copy() + cassettes.Cut() + if(!load_all_cassettes_from_json()) + cassettes = old_cassettes + CRASH("Failed to load cassettes from JSON") + var/list/sql_cassettes = list() + for(var/id in cassettes) + var/datum/cassette/cassette = cassettes[id] + sql_cassettes += list(list( + "id" = id, + "name" = cassette.name, + "desc" = cassette.desc, + "status" = cassette.status, + "author_name" = cassette.author.name, + "author_ckey" = ckey(cassette.author.ckey), + "front" = cassette.front.export_for_db(), + "back" = cassette.back.export_for_db(), + )) + if(!length(sql_cassettes)) + return + SSdbcore.MassInsert(format_table_name("cassettes"), sql_cassettes, duplicate_key = TRUE, warn = TRUE) diff --git a/monkestation/code/modules/cassettes/cassette_db/subsystem.dm b/monkestation/code/modules/cassettes/cassette_db/subsystem.dm deleted file mode 100644 index bbca8076a20c..000000000000 --- a/monkestation/code/modules/cassettes/cassette_db/subsystem.dm +++ /dev/null @@ -1,30 +0,0 @@ -SUBSYSTEM_DEF(cassette_storage) - name = "Cassette Storage" - flags = SS_NO_FIRE - runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT - var/list/cassette_datums = list() - - -/datum/controller/subsystem/cassette_storage/Initialize() - if(!length(GLOB.approved_ids)) - GLOB.approved_ids = initialize_approved_ids() - generate_cassette_datums() - return SS_INIT_SUCCESS - -/datum/controller/subsystem/cassette_storage/proc/generate_cassette_datums() - for(var/id in GLOB.approved_ids) - var/datum/cassette_data/new_data = new - if(!new_data.populate_data(id)) - qdel(new_data) - continue - cassette_datums += new_data - -/datum/controller/subsystem/cassette_storage/proc/get_cassettes_by_ckey(user_ckey) as /list - RETURN_TYPE(/list) - . = list() - if(!user_ckey) - return - user_ckey = ckey(user_ckey) - for(var/datum/cassette_data/tape as anything in SScassette_storage.cassette_datums) - if(ckey(tape.cassette_author_ckey) == user_ckey) - . += tape diff --git a/monkestation/code/modules/cassettes/dj/dj_music_field.dm b/monkestation/code/modules/cassettes/dj/dj_music_field.dm new file mode 100644 index 000000000000..7ae01f489138 --- /dev/null +++ b/monkestation/code/modules/cassettes/dj/dj_music_field.dm @@ -0,0 +1,48 @@ +/// A proximity monitor field that allows mobs near objects to hear DJ music. +/datum/proximity_monitor/advanced/dj_music + edge_is_a_field = TRUE + /// List of mobs that can currently hear music from this field. + var/list/mob/listeners + +/datum/proximity_monitor/advanced/dj_music/Destroy() + for(var/mob/listener as anything in listeners) + remove_mob(listener) + return ..() + +/datum/proximity_monitor/advanced/dj_music/field_turf_crossed(atom/movable/crosser, turf/old_location, turf/new_location) + if(isliving(crosser)) + add_mob(crosser) + var/list/hearing_contents = crosser.important_recursive_contents?[RECURSIVE_CONTENTS_HEARING_SENSITIVE] + for(var/mob/living/target in hearing_contents) + add_mob(target) + +/datum/proximity_monitor/advanced/dj_music/field_turf_uncrossed(atom/movable/crosser, turf/old_location, turf/new_location) + if(isliving(crosser)) + remove_mob(crosser) + var/list/hearing_contents = crosser.important_recursive_contents?[RECURSIVE_CONTENTS_HEARING_SENSITIVE] + for(var/mob/living/target in hearing_contents) + remove_mob(target) + +/datum/proximity_monitor/advanced/dj_music/setup_field_turf(turf/target) + for(var/atom/movable/thing in target) + if(isliving(thing) || length(thing.important_recursive_contents?[RECURSIVE_CONTENTS_HEARING_SENSITIVE])) + field_turf_crossed(thing) + +/datum/proximity_monitor/advanced/dj_music/cleanup_field_turf(turf/target) + for(var/atom/movable/thing in target) + if(isliving(thing) || length(thing.important_recursive_contents?[RECURSIVE_CONTENTS_HEARING_SENSITIVE])) + field_turf_uncrossed(thing) + +/datum/proximity_monitor/advanced/dj_music/proc/add_mob(mob/living/target) + if(QDELING(src) || !isliving(target) || QDELING(target) || HAS_TRAIT_FROM(target, TRAIT_CAN_HEAR_MUSIC, INNATE_TRAIT) || (target in listeners)) + return + LAZYADD(listeners, target) + ADD_TRAIT(target, TRAIT_CAN_HEAR_MUSIC, REF(src)) + RegisterSignal(target, COMSIG_QDELETING, PROC_REF(remove_mob)) + +/datum/proximity_monitor/advanced/dj_music/proc/remove_mob(mob/living/target) + if(!isliving(target) || !(target in listeners)) + return + LAZYREMOVE(listeners, target) + REMOVE_TRAIT(target, TRAIT_CAN_HEAR_MUSIC, REF(src)) + UnregisterSignal(target, COMSIG_QDELETING) diff --git a/monkestation/code/modules/cassettes/dj/intercom.dm b/monkestation/code/modules/cassettes/dj/intercom.dm new file mode 100644 index 000000000000..5c12cc6cd5ac --- /dev/null +++ b/monkestation/code/modules/cassettes/dj/intercom.dm @@ -0,0 +1,13 @@ +/obj/item/radio/intercom + /// The proximity monitor used to allow people to hear DJ music while in hearing range. + var/datum/proximity_monitor/advanced/dj_music/music_field + +/obj/item/radio/intercom/Initialize(mapload, ndir, building) + . = ..() + var/range = isnull(listening_range) ? canhear_range : listening_range + if(isturf(loc) && range > 0 && (is_station_level(loc.z) || is_centcom_level(loc.z))) + music_field = new(src, range) + +/obj/item/radio/intercom/Destroy() + QDEL_NULL(music_field) + return ..() diff --git a/monkestation/code/modules/cassettes/dj/mob_can_hear.dm b/monkestation/code/modules/cassettes/dj/mob_can_hear.dm new file mode 100644 index 000000000000..4c09e1cd1406 --- /dev/null +++ b/monkestation/code/modules/cassettes/dj/mob_can_hear.dm @@ -0,0 +1,29 @@ +/// A list of all mobs that can hear music. +GLOBAL_LIST_EMPTY_TYPED(music_listeners, /mob) + +/mob/Initialize(mapload) + . = ..() + RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_CAN_HEAR_MUSIC), PROC_REF(on_can_hear_music_trait_gain)) + RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_CAN_HEAR_MUSIC), PROC_REF(on_can_hear_music_trait_loss)) + + // just in case we already have the trait + if(HAS_TRAIT(src, TRAIT_CAN_HEAR_MUSIC)) + on_can_hear_music_trait_gain(src) + +/mob/Destroy(force) + on_can_hear_music_trait_loss(src) + return ..() + +/mob/proc/on_can_hear_music_trait_gain(datum/source) + SIGNAL_HANDLER + if(src in GLOB.music_listeners) + return + GLOB.music_listeners += src + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_ADD_MUSIC_LISTENER, src) + +/mob/proc/on_can_hear_music_trait_loss(datum/source) + SIGNAL_HANDLER + if(!(src in GLOB.music_listeners)) + return + GLOB.music_listeners -= src + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_REMOVE_MUSIC_LISTENER, src) diff --git a/monkestation/code/modules/cassettes/machines/cassette_rack.dm b/monkestation/code/modules/cassettes/machines/cassette_rack.dm index 9abbeabb0371..f6c683baadba 100644 --- a/monkestation/code/modules/cassettes/machines/cassette_rack.dm +++ b/monkestation/code/modules/cassettes/machines/cassette_rack.dm @@ -29,7 +29,7 @@ /datum/storage/cassette_rack/New() . = ..() - set_holdable(/obj/item/device/cassette_tape) + set_holdable(/obj/item/cassette_tape) // Allow opening on a normal left click /datum/storage/cassette_rack/on_attack(datum/source, mob/user) @@ -50,9 +50,9 @@ REGISTER_REQUIRED_MAP_ITEM(1, INFINITY) RegisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED, PROC_REF(spawn_curator_tapes)) for(var/i in 1 to spawn_blanks) - new /obj/item/device/cassette_tape/blank(src) + new /obj/item/cassette_tape/blank(src) for(var/id in unique_random_tapes(spawn_random)) - new /obj/item/device/cassette_tape(src, id) + new /obj/item/cassette_tape(src, id) update_appearance() /obj/structure/cassette_rack/prefilled/Destroy() @@ -68,23 +68,17 @@ add_user_tapes(new_crewmember.ckey) /obj/structure/cassette_rack/prefilled/proc/add_user_tapes(user_ckey, max_amt = 3, expand_max_size = TRUE) - var/list/user_tapes = SScassette_storage.get_cassettes_by_ckey(user_ckey) - if(!length(user_tapes)) - return FALSE - var/list/existing_tapes = list() - for(var/obj/item/device/cassette_tape/tape in src) - if(tape.id) - existing_tapes[tape.id] = TRUE - for(var/iter in 1 to max_amt) - if(!length(user_tapes)) - break - var/datum/cassette_data/tape = pick_n_take(user_tapes) - if(existing_tapes[tape.cassette_id]) - continue - new /obj/item/device/cassette_tape(src, tape.cassette_id) - if(expand_max_size && !QDELETED(atom_storage)) - atom_storage.max_slots += max_amt - atom_storage.max_total_storage += max_amt * WEIGHT_CLASS_SMALL + var/list/existing_cassettes = list() + for(var/obj/item/cassette_tape/tape in src) + if(tape.cassette_data.id) + existing_cassettes |= tape.cassette_data.id + var/amount_spawned = 0 + for(var/datum/cassette/cassette as anything in SScassettes.unique_random_cassettes(max_amt, CASSETTE_STATUS_APPROVED, user_ckey, existing_cassettes)) + new /obj/item/cassette_tape(src, cassette) + amount_spawned++ + if(expand_max_size && !QDELETED(atom_storage) && amount_spawned > 0) + atom_storage.max_slots += amount_spawned + atom_storage.max_total_storage += amount_spawned * WEIGHT_CLASS_SMALL return TRUE #undef DEFAULT_BLANKS_TO_SPAWN diff --git a/monkestation/code/modules/cassettes/machines/dj_station.dm b/monkestation/code/modules/cassettes/machines/dj_station.dm index 386de43c5f57..a7742247f810 100644 --- a/monkestation/code/modules/cassettes/machines/dj_station.dm +++ b/monkestation/code/modules/cassettes/machines/dj_station.dm @@ -1,6 +1,5 @@ GLOBAL_VAR(dj_broadcast) -GLOBAL_VAR(dj_booth) - +GLOBAL_DATUM(dj_booth, /obj/machinery/cassette/dj_station) /obj/item/clothing/ears //can we be used to listen to radio? @@ -8,42 +7,35 @@ GLOBAL_VAR(dj_booth) /obj/machinery/cassette/dj_station name = "Cassette Player" - desc = "Plays Space Music Board approved cassettes for anyone in the station to listen to " + desc = "Plays Space Music Board approved cassettes for anyone in the station to listen to." icon = 'monkestation/code/modules/cassettes/icons/radio_station.dmi' icon_state = "cassette_player" - active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION + use_power = NO_POWER_USE + + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + move_resist = MOVE_FORCE_OVERPOWERING - resistance_flags = INDESTRUCTIBLE anchored = TRUE density = TRUE - var/broadcasting = FALSE - var/obj/item/device/cassette_tape/inserted_tape - var/time_left = 0 - var/current_song_duration = 0 - var/list/people_with_signals = list() - var/list/active_listeners = list() - var/waiting_for_yield = FALSE - - //tape stuff goes here - var/pl_index = 0 - var/list/current_playlist = list() - var/list/current_namelist = list() + var/broadcasting = FALSE COOLDOWN_DECLARE(next_song_timer) /obj/machinery/cassette/dj_station/Initialize(mapload) . = ..() REGISTER_REQUIRED_MAP_ITEM(1, INFINITY) - GLOB.dj_booth = src register_context() + if(QDELETED(GLOB.dj_booth)) + GLOB.dj_booth = src /obj/machinery/cassette/dj_station/Destroy() - . = ..() - GLOB.dj_booth = null - STOP_PROCESSING(SSprocessing, src) + if(GLOB.dj_booth == src) + GLOB.dj_booth = null + return ..() +/* /obj/machinery/cassette/dj_station/add_context(atom/source, list/context, obj/item/held_item, mob/user) . = ..() if(inserted_tape) @@ -51,327 +43,4 @@ GLOBAL_VAR(dj_booth) if(!broadcasting) context[SCREENTIP_CONTEXT_LMB] = "Play Tape" return CONTEXTUAL_SCREENTIP_SET - -/obj/machinery/cassette/dj_station/examine(mob/user) - . = ..() - if(time_left > 0 || next_song_timer) - . += span_notice("It seems to be cooling down, you estimate it will take about [time_left ? DisplayTimeText(((time_left * 10) + 6000)) : DisplayTimeText(COOLDOWN_TIMELEFT(src, next_song_timer))].") - -/obj/machinery/cassette/dj_station/process(seconds_per_tick) - if(waiting_for_yield) - return - time_left -= round(seconds_per_tick) - if(time_left <= 0) - time_left = 0 - if(COOLDOWN_FINISHED(src, next_song_timer) && broadcasting) - COOLDOWN_START(src, next_song_timer, 10 MINUTES) - broadcasting = FALSE - -/obj/machinery/cassette/dj_station/attack_hand(mob/user) - . = ..() - if(!inserted_tape) - return - if((!COOLDOWN_FINISHED(src, next_song_timer)) && !broadcasting) - to_chat(user, span_notice("The [src] feels hot to the touch and needs time to cooldown.")) - to_chat(user, span_info("You estimate it will take about [time_left ? DisplayTimeText(((time_left * 10) + 6000)) : DisplayTimeText(COOLDOWN_TIMELEFT(src, next_song_timer))] to cool down.")) - return - message_admins("[src] started broadcasting [inserted_tape] interacted with by [user]") - logger.Log(LOG_CATEGORY_MUSIC, "[src] started broadcasting [inserted_tape]") - start_broadcast() - -/obj/machinery/cassette/dj_station/AltClick(mob/user) - . = ..() - if(!isliving(user) || !user.Adjacent(src)) - return - if(!inserted_tape) - return - if(broadcasting) - next_song() - -/obj/machinery/cassette/dj_station/CtrlClick(mob/user) - . = ..() - if(!inserted_tape || broadcasting) - return - if(Adjacent(user) && !issiliconoradminghost(user)) - if(!user.put_in_hands(inserted_tape)) - inserted_tape.forceMove(drop_location()) - else - inserted_tape.forceMove(drop_location()) - inserted_tape = null - time_left = 0 - current_song_duration = 0 - pl_index = 0 - current_playlist = list() - current_namelist = list() - stop_broadcast(TRUE) - -/obj/machinery/cassette/dj_station/attackby(obj/item/weapon, mob/user, params) - if(!istype(weapon, /obj/item/device/cassette_tape)) - return - var/obj/item/device/cassette_tape/attacked = weapon - if(!attacked.approved_tape) - to_chat(user, span_warning("The [src] smartly rejects the bootleg cassette tape")) - return - if(!inserted_tape) - insert_tape(attacked) - else - if(!broadcasting) - if(Adjacent(user) && !issiliconoradminghost(user)) - if(!user.put_in_hands(inserted_tape)) - inserted_tape.forceMove(drop_location()) - else - inserted_tape.forceMove(drop_location()) - inserted_tape = null - time_left = 0 - current_song_duration = 0 - pl_index = 0 - current_playlist = list() - current_namelist = list() - insert_tape(attacked) - if(broadcasting) - stop_broadcast(TRUE) - -/obj/machinery/cassette/dj_station/proc/insert_tape(obj/item/device/cassette_tape/CTape) - if(inserted_tape || !istype(CTape)) - return - - inserted_tape = CTape - CTape.forceMove(src) - - update_appearance() - pl_index = 1 - if(inserted_tape.songs["side1"] && inserted_tape.songs["side2"]) - var/list/list = inserted_tape.songs["[inserted_tape.flipped ? "side2" : "side1"]"] - for(var/song in list) - current_playlist += song - - var/list/name_list = inserted_tape.song_names["[inserted_tape.flipped ? "side2" : "side1"]"] - for(var/song in name_list) - current_namelist += song - -/obj/machinery/cassette/dj_station/proc/stop_broadcast(soft = FALSE) - STOP_PROCESSING(SSprocessing, src) - GLOB.dj_broadcast = FALSE - broadcasting = FALSE - message_admins("[src] has stopped broadcasting [inserted_tape].") - logger.Log(LOG_CATEGORY_MUSIC, "[src] has stopped broadcasting [inserted_tape]") - for(var/client/anything as anything in active_listeners) - if(!istype(anything)) - continue - anything.tgui_panel?.stop_music() - GLOB.youtube_exempt["dj-station"] -= anything - active_listeners = list() - - if(!soft) - for(var/mob/living/carbon/anything as anything in people_with_signals) - if(!istype(anything)) - continue - UnregisterSignal(anything, COMSIG_CARBON_UNEQUIP_EARS) - UnregisterSignal(anything, COMSIG_CARBON_EQUIP_EARS) - UnregisterSignal(anything, COMSIG_MOVABLE_Z_CHANGED) - people_with_signals = list() - -/obj/machinery/cassette/dj_station/proc/start_broadcast() - var/choice = tgui_input_list(usr, "Choose which song to play.", "[src]", current_namelist) - if(!choice) - return - var/list_index = current_namelist.Find(choice) - if(!list_index) - return - GLOB.dj_broadcast = TRUE - pl_index = list_index - - var/list/viable_z = SSmapping.levels_by_any_trait(list(ZTRAIT_STATION, ZTRAIT_MINING, ZTRAIT_CENTCOM, ZTRAIT_RESERVED)) - for(var/mob/person as anything in GLOB.player_list) - if(issilicon(person) || isobserver(person) || isaicamera(person) || isbot(person)) - active_listeners |= person.client - continue - if(iscarbon(person)) - var/mob/living/carbon/anything = person - if(!(anything in people_with_signals)) - if(!istype(anything)) - continue - - RegisterSignal(anything, COMSIG_CARBON_UNEQUIP_EARS, PROC_REF(stop_solo_broadcast)) - RegisterSignal(anything, COMSIG_CARBON_EQUIP_EARS, PROC_REF(check_solo_broadcast)) - RegisterSignal(anything, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(check_solo_broadcast)) - people_with_signals |= anything - - if(!(anything.client in active_listeners)) - if(!(anything.z in viable_z)) - continue - - if(!anything.client) - continue - - if(anything.client in GLOB.youtube_exempt["walkman"]) - continue - - var/obj/item/ear_slot = anything.get_item_by_slot(ITEM_SLOT_EARS) - if(istype(ear_slot, /obj/item/clothing/ears)) - var/obj/item/clothing/ears/worn - if(!worn || !worn?.radio_compat) - continue - else if(!istype(ear_slot, /obj/item/radio/headset)) - continue - - if(!anything.client.prefs?.read_preference(/datum/preference/toggle/hear_music)) - continue - - active_listeners |= anything.client - - if(!length(active_listeners)) - return - - start_playing(active_listeners) - START_PROCESSING(SSprocessing, src) - - -/obj/machinery/cassette/dj_station/proc/check_solo_broadcast(mob/living/carbon/source, obj/item/clothing/ears/ear_item) - SIGNAL_HANDLER - - if(!istype(source)) - return - - if(istype(ear_item, /obj/item/clothing/ears)) - var/obj/item/clothing/ears/worn - if(!worn || !worn?.radio_compat) - return - else if(!istype(ear_item, /obj/item/radio/headset)) - return - - var/list/viable_z = SSmapping.levels_by_any_trait(list(ZTRAIT_STATION, ZTRAIT_MINING, ZTRAIT_CENTCOM)) - if(!(source.z in viable_z) || !source.client) - return - - if(!source.client.prefs?.read_preference(/datum/preference/toggle/hear_music)) - return - - active_listeners |= source.client - GLOB.youtube_exempt["dj-station"] |= source.client - INVOKE_ASYNC(src, PROC_REF(start_playing),list(source.client)) - -/obj/machinery/cassette/dj_station/proc/stop_solo_broadcast(mob/living/carbon/source) - SIGNAL_HANDLER - - if(!source.client || !(source.client in active_listeners)) - return - - active_listeners -= source.client - GLOB.youtube_exempt["dj-station"] -= source.client - source.client.tgui_panel?.stop_music() - -/obj/machinery/cassette/dj_station/proc/start_playing(list/clients) - if(!inserted_tape) - if(broadcasting) - stop_broadcast(TRUE) - return - - waiting_for_yield = TRUE - if(findtext(current_playlist[pl_index], GLOB.is_http_protocol)) - ///invoking youtube-dl - var/ytdl = CONFIG_GET(string/invoke_youtubedl) - ///the input for ytdl handled by the song list - var/web_sound_input - ///the url for youtube-dl - var/web_sound_url = "" - ///all extra data from the youtube-dl really want the name - var/list/music_extra_data = list() - web_sound_input = trim(current_playlist[pl_index]) - if(!(web_sound_input in GLOB.parsed_audio)) - ///scrubbing the input before putting it in the shell - var/shell_scrubbed_input = shell_url_scrub(web_sound_input) - ///putting it in the shell - var/list/output = world.shelleo("[ytdl] --geo-bypass --format \"bestaudio\[ext=mp3]/best\[ext=mp4]\[height <= 360]/bestaudio\[ext=m4a]/bestaudio\[ext=aac]\" --dump-single-json --no-playlist --extractor-args \"youtube:lang=en\" -- \"[shell_scrubbed_input]\"") - ///any errors - var/errorlevel = output[SHELLEO_ERRORLEVEL] - ///the standard output - var/stdout = output[SHELLEO_STDOUT] - if(!errorlevel) - ///list for all the output data to go to - var/list/data - try - data = json_decode(stdout) - catch(var/exception/error) ///catch errors here - to_chat(src, "Youtube-dl JSON parsing FAILED:", confidential = TRUE) - to_chat(src, "[error]: [stdout]", confidential = TRUE) - return - - if (data["url"]) - web_sound_url = data["url"] - music_extra_data["start"] = data["start_time"] - music_extra_data["end"] = data["end_time"] - music_extra_data["link"] = data["webpage_url"] - music_extra_data["title"] = data["title"] - if(music_extra_data["start"]) - time_left = data["duration"] - music_extra_data["start"] - else - time_left = data["duration"] - - current_song_duration = data["duration"] - - GLOB.parsed_audio["[web_sound_input]"] = data - else - var/list/data = GLOB.parsed_audio["[web_sound_input]"] - web_sound_url = data["url"] - music_extra_data["start"] = data["start_time"] - music_extra_data["end"] = data["end_time"] - music_extra_data["link"] = data["webpage_url"] - music_extra_data["title"] = data["title"] - if(time_left <= 0) - if(music_extra_data["start"]) - time_left = data["duration"] - music_extra_data["start"] - else - time_left = data["duration"] - - current_song_duration = data["duration"] - music_extra_data["duration"] = data["duration"] - - if(time_left > 0) - music_extra_data["start"] = music_extra_data["duration"] - time_left - - for(var/client/anything as anything in clients) - if(!istype(anything)) - continue - anything.tgui_panel?.play_music(web_sound_url, music_extra_data) - GLOB.youtube_exempt["dj-station"] |= anything - broadcasting = TRUE - waiting_for_yield = FALSE - -/obj/machinery/cassette/dj_station/proc/add_new_player(mob/living/carbon/new_player) - if(!(new_player in people_with_signals)) - RegisterSignal(new_player, COMSIG_CARBON_UNEQUIP_EARS, PROC_REF(stop_solo_broadcast)) - RegisterSignal(new_player, COMSIG_CARBON_EQUIP_EARS, PROC_REF(check_solo_broadcast)) - RegisterSignal(new_player, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(check_solo_broadcast)) - people_with_signals |= new_player - - if(!broadcasting) - return - - var/obj/item/ear_slot = new_player.get_item_by_slot(ITEM_SLOT_EARS) - if(istype(ear_slot, /obj/item/clothing/ears)) - var/obj/item/clothing/ears/worn - if(!worn || !worn?.radio_compat) - return - else if(!istype(ear_slot, /obj/item/radio/headset)) - return - var/list/viable_z = SSmapping.levels_by_any_trait(list(ZTRAIT_STATION, ZTRAIT_MINING, ZTRAIT_CENTCOM)) - if(!(new_player.z in viable_z)) - return - - if(!(new_player.client in active_listeners)) - active_listeners |= new_player.client - start_playing(list(new_player.client)) - -/obj/machinery/cassette/dj_station/proc/next_song() - waiting_for_yield = TRUE - var/choice = tgui_input_number(usr, "Choose which song number to play.", "[src]", 1, length(current_playlist), 1) - if(!choice) - waiting_for_yield = FALSE - stop_broadcast() - return - GLOB.dj_broadcast = TRUE - pl_index = choice - - pl_index++ - start_playing(active_listeners) +*/ diff --git a/monkestation/code/modules/cassettes/machines/dj_station_old.dm b/monkestation/code/modules/cassettes/machines/dj_station_old.dm new file mode 100644 index 000000000000..f9b24f679fc8 --- /dev/null +++ b/monkestation/code/modules/cassettes/machines/dj_station_old.dm @@ -0,0 +1,377 @@ +GLOBAL_VAR(dj_broadcast) +GLOBAL_DATUM(dj_booth, /obj/machinery/cassette/dj_station) + +/obj/item/clothing/ears + //can we be used to listen to radio? + var/radio_compat = FALSE + +/obj/machinery/cassette/dj_station + name = "Cassette Player" + desc = "Plays Space Music Board approved cassettes for anyone in the station to listen to." + + icon = 'monkestation/code/modules/cassettes/icons/radio_station.dmi' + icon_state = "cassette_player" + + active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION + + resistance_flags = INDESTRUCTIBLE + anchored = TRUE + density = TRUE + var/broadcasting = FALSE + var/obj/item/cassette_tape/inserted_tape + var/time_left = 0 + var/current_song_duration = 0 + var/list/people_with_signals = list() + var/list/active_listeners = list() + var/waiting_for_yield = FALSE + + //tape stuff goes here + var/pl_index = 0 + var/list/current_playlist = list() + var/list/current_namelist = list() + + COOLDOWN_DECLARE(next_song_timer) + +/obj/machinery/cassette/dj_station/Initialize(mapload) + . = ..() + REGISTER_REQUIRED_MAP_ITEM(1, INFINITY) + register_context() + if(QDELETED(GLOB.dj_booth)) + GLOB.dj_booth = src + +/obj/machinery/cassette/dj_station/Destroy() + if(GLOB.dj_booth == src) + GLOB.dj_booth = null + return ..() + +/obj/machinery/cassette/dj_station/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = ..() + if(inserted_tape) + context[SCREENTIP_CONTEXT_CTRL_LMB] = "Eject Tape" + if(!broadcasting) + context[SCREENTIP_CONTEXT_LMB] = "Play Tape" + return CONTEXTUAL_SCREENTIP_SET + +/obj/machinery/cassette/dj_station/examine(mob/user) + . = ..() + if(time_left > 0 || next_song_timer) + . += span_notice("It seems to be cooling down, you estimate it will take about [time_left ? DisplayTimeText(((time_left * 10) + 6000)) : DisplayTimeText(COOLDOWN_TIMELEFT(src, next_song_timer))].") + +/obj/machinery/cassette/dj_station/process(seconds_per_tick) + if(waiting_for_yield) + return + time_left -= round(seconds_per_tick) + if(time_left <= 0) + time_left = 0 + if(COOLDOWN_FINISHED(src, next_song_timer) && broadcasting) + COOLDOWN_START(src, next_song_timer, 10 MINUTES) + broadcasting = FALSE + +/obj/machinery/cassette/dj_station/attack_hand(mob/user) + . = ..() + if(!inserted_tape) + return + if((!COOLDOWN_FINISHED(src, next_song_timer)) && !broadcasting) + to_chat(user, span_notice("The [src] feels hot to the touch and needs time to cooldown.")) + to_chat(user, span_info("You estimate it will take about [time_left ? DisplayTimeText(((time_left * 10) + 6000)) : DisplayTimeText(COOLDOWN_TIMELEFT(src, next_song_timer))] to cool down.")) + return + message_admins("[src] started broadcasting [inserted_tape] interacted with by [user]") + logger.Log(LOG_CATEGORY_MUSIC, "[src] started broadcasting [inserted_tape]") + start_broadcast() + +/obj/machinery/cassette/dj_station/AltClick(mob/user) + . = ..() + if(!isliving(user) || !user.Adjacent(src)) + return + if(!inserted_tape) + return + if(broadcasting) + next_song() + +/obj/machinery/cassette/dj_station/CtrlClick(mob/user) + . = ..() + if(!inserted_tape || broadcasting) + return + if(Adjacent(user) && !issiliconoradminghost(user)) + if(!user.put_in_hands(inserted_tape)) + inserted_tape.forceMove(drop_location()) + else + inserted_tape.forceMove(drop_location()) + inserted_tape = null + time_left = 0 + current_song_duration = 0 + pl_index = 0 + current_playlist = list() + current_namelist = list() + stop_broadcast(TRUE) + +/obj/machinery/cassette/dj_station/attackby(obj/item/weapon, mob/user, params) + if(!istype(weapon, /obj/item/cassette_tape)) + return + var/obj/item/cassette_tape/attacked = weapon + if(!attacked.approved_tape) + to_chat(user, span_warning("The [src] smartly rejects the bootleg cassette tape")) + return + if(!inserted_tape) + insert_tape(attacked) + else + if(!broadcasting) + if(Adjacent(user) && !issiliconoradminghost(user)) + if(!user.put_in_hands(inserted_tape)) + inserted_tape.forceMove(drop_location()) + else + inserted_tape.forceMove(drop_location()) + inserted_tape = null + time_left = 0 + current_song_duration = 0 + pl_index = 0 + current_playlist = list() + current_namelist = list() + insert_tape(attacked) + if(broadcasting) + stop_broadcast(TRUE) + +/obj/machinery/cassette/dj_station/proc/insert_tape(obj/item/cassette_tape/CTape) + if(inserted_tape || !istype(CTape)) + return + + inserted_tape = CTape + CTape.forceMove(src) + + update_appearance() + pl_index = 1 + if(inserted_tape.songs["side1"] && inserted_tape.songs["side2"]) + var/list/list = inserted_tape.songs["[inserted_tape.flipped ? "side2" : "side1"]"] + for(var/song in list) + current_playlist += song + + var/list/name_list = inserted_tape.song_names["[inserted_tape.flipped ? "side2" : "side1"]"] + for(var/song in name_list) + current_namelist += song + +/obj/machinery/cassette/dj_station/proc/stop_broadcast(soft = FALSE) + STOP_PROCESSING(SSprocessing, src) + GLOB.dj_broadcast = FALSE + broadcasting = FALSE + message_admins("[src] has stopped broadcasting [inserted_tape].") + logger.Log(LOG_CATEGORY_MUSIC, "[src] has stopped broadcasting [inserted_tape]") + for(var/client/anything as anything in active_listeners) + if(!istype(anything)) + continue + anything.tgui_panel?.stop_music() + GLOB.youtube_exempt["dj-station"] -= anything + active_listeners = list() + + if(!soft) + for(var/mob/living/carbon/anything as anything in people_with_signals) + if(!istype(anything)) + continue + UnregisterSignal(anything, COMSIG_CARBON_UNEQUIP_EARS) + UnregisterSignal(anything, COMSIG_CARBON_EQUIP_EARS) + UnregisterSignal(anything, COMSIG_MOVABLE_Z_CHANGED) + people_with_signals = list() + +/obj/machinery/cassette/dj_station/proc/start_broadcast() + var/choice = tgui_input_list(usr, "Choose which song to play.", "[src]", current_namelist) + if(!choice) + return + var/list_index = current_namelist.Find(choice) + if(!list_index) + return + GLOB.dj_broadcast = TRUE + pl_index = list_index + + var/list/viable_z = SSmapping.levels_by_any_trait(list(ZTRAIT_STATION, ZTRAIT_MINING, ZTRAIT_CENTCOM, ZTRAIT_RESERVED)) + for(var/mob/person as anything in GLOB.player_list) + if(issilicon(person) || isobserver(person) || isaicamera(person) || isbot(person)) + active_listeners |= person.client + continue + if(iscarbon(person)) + var/mob/living/carbon/anything = person + if(!(anything in people_with_signals)) + if(!istype(anything)) + continue + + RegisterSignal(anything, COMSIG_CARBON_UNEQUIP_EARS, PROC_REF(stop_solo_broadcast)) + RegisterSignal(anything, COMSIG_CARBON_EQUIP_EARS, PROC_REF(check_solo_broadcast)) + RegisterSignal(anything, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(check_solo_broadcast)) + people_with_signals |= anything + + if(!(anything.client in active_listeners)) + if(!(anything.z in viable_z)) + continue + + if(!anything.client) + continue + + if(anything.client in GLOB.youtube_exempt["walkman"]) + continue + + var/obj/item/ear_slot = anything.get_item_by_slot(ITEM_SLOT_EARS) + if(istype(ear_slot, /obj/item/clothing/ears)) + var/obj/item/clothing/ears/worn + if(!worn || !worn?.radio_compat) + continue + else if(!istype(ear_slot, /obj/item/radio/headset)) + continue + + if(!anything.client.prefs?.read_preference(/datum/preference/toggle/hear_music)) + continue + + active_listeners |= anything.client + + if(!length(active_listeners)) + return + + start_playing(active_listeners) + START_PROCESSING(SSprocessing, src) + + +/obj/machinery/cassette/dj_station/proc/check_solo_broadcast(mob/living/carbon/source, obj/item/clothing/ears/ear_item) + SIGNAL_HANDLER + + if(!istype(source)) + return + + if(istype(ear_item, /obj/item/clothing/ears)) + var/obj/item/clothing/ears/worn + if(!worn || !worn?.radio_compat) + return + else if(!istype(ear_item, /obj/item/radio/headset)) + return + + var/list/viable_z = SSmapping.levels_by_any_trait(list(ZTRAIT_STATION, ZTRAIT_MINING, ZTRAIT_CENTCOM)) + if(!(source.z in viable_z) || !source.client) + return + + if(!source.client.prefs?.read_preference(/datum/preference/toggle/hear_music)) + return + + active_listeners |= source.client + GLOB.youtube_exempt["dj-station"] |= source.client + INVOKE_ASYNC(src, PROC_REF(start_playing),list(source.client)) + +/obj/machinery/cassette/dj_station/proc/stop_solo_broadcast(mob/living/carbon/source) + SIGNAL_HANDLER + + if(!source.client || !(source.client in active_listeners)) + return + + active_listeners -= source.client + GLOB.youtube_exempt["dj-station"] -= source.client + source.client.tgui_panel?.stop_music() + +/obj/machinery/cassette/dj_station/proc/start_playing(list/clients) + if(!inserted_tape) + if(broadcasting) + stop_broadcast(TRUE) + return + + waiting_for_yield = TRUE + if(is_http_protocol(current_playlist[pl_index])) + ///invoking youtube-dl + var/ytdl = CONFIG_GET(string/invoke_youtubedl) + ///the input for ytdl handled by the song list + var/web_sound_input + ///the url for youtube-dl + var/web_sound_url = "" + ///all extra data from the youtube-dl really want the name + var/list/music_extra_data = list() + web_sound_input = trim(current_playlist[pl_index]) + if(!(web_sound_input in GLOB.parsed_audio)) + ///scrubbing the input before putting it in the shell + var/shell_scrubbed_input = shell_url_scrub(web_sound_input) + ///putting it in the shell + var/list/output = world.shelleo("[ytdl] --geo-bypass --format \"bestaudio\[ext=mp3]/best\[ext=mp4]\[height <= 360]/bestaudio\[ext=m4a]/bestaudio\[ext=aac]\" --dump-single-json --no-playlist --extractor-args \"youtube:lang=en\" -- \"[shell_scrubbed_input]\"") + ///any errors + var/errorlevel = output[SHELLEO_ERRORLEVEL] + ///the standard output + var/stdout = output[SHELLEO_STDOUT] + if(!errorlevel) + ///list for all the output data to go to + var/list/data + try + data = json_decode(stdout) + catch(var/exception/error) ///catch errors here + to_chat(src, "Youtube-dl JSON parsing FAILED:", confidential = TRUE) + to_chat(src, "[error]: [stdout]", confidential = TRUE) + return + + if (data["url"]) + web_sound_url = data["url"] + music_extra_data["start"] = data["start_time"] + music_extra_data["end"] = data["end_time"] + music_extra_data["link"] = data["webpage_url"] + music_extra_data["title"] = data["title"] + if(music_extra_data["start"]) + time_left = data["duration"] - music_extra_data["start"] + else + time_left = data["duration"] + + current_song_duration = data["duration"] + + GLOB.parsed_audio["[web_sound_input]"] = data + else + var/list/data = GLOB.parsed_audio["[web_sound_input]"] + web_sound_url = data["url"] + music_extra_data["start"] = data["start_time"] + music_extra_data["end"] = data["end_time"] + music_extra_data["link"] = data["webpage_url"] + music_extra_data["title"] = data["title"] + if(time_left <= 0) + if(music_extra_data["start"]) + time_left = data["duration"] - music_extra_data["start"] + else + time_left = data["duration"] + + current_song_duration = data["duration"] + music_extra_data["duration"] = data["duration"] + + if(time_left > 0) + music_extra_data["start"] = music_extra_data["duration"] - time_left + + for(var/client/anything as anything in clients) + if(!istype(anything)) + continue + anything.tgui_panel?.play_music(web_sound_url, music_extra_data) + GLOB.youtube_exempt["dj-station"] |= anything + broadcasting = TRUE + waiting_for_yield = FALSE + +/obj/machinery/cassette/dj_station/proc/add_new_player(mob/living/carbon/new_player) + if(!(new_player in people_with_signals)) + RegisterSignal(new_player, COMSIG_CARBON_UNEQUIP_EARS, PROC_REF(stop_solo_broadcast)) + RegisterSignal(new_player, COMSIG_CARBON_EQUIP_EARS, PROC_REF(check_solo_broadcast)) + RegisterSignal(new_player, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(check_solo_broadcast)) + people_with_signals |= new_player + + if(!broadcasting) + return + + var/obj/item/ear_slot = new_player.get_item_by_slot(ITEM_SLOT_EARS) + if(istype(ear_slot, /obj/item/clothing/ears)) + var/obj/item/clothing/ears/worn + if(!worn || !worn?.radio_compat) + return + else if(!istype(ear_slot, /obj/item/radio/headset)) + return + var/list/viable_z = SSmapping.levels_by_any_trait(list(ZTRAIT_STATION, ZTRAIT_MINING, ZTRAIT_CENTCOM)) + if(!(new_player.z in viable_z)) + return + + if(!(new_player.client in active_listeners)) + active_listeners |= new_player.client + start_playing(list(new_player.client)) + +/obj/machinery/cassette/dj_station/proc/next_song() + waiting_for_yield = TRUE + var/choice = tgui_input_number(usr, "Choose which song number to play.", "[src]", 1, length(current_playlist), 1) + if(!choice) + waiting_for_yield = FALSE + stop_broadcast() + return + GLOB.dj_broadcast = TRUE + pl_index = choice + + pl_index++ + start_playing(active_listeners) diff --git a/monkestation/code/modules/cassettes/machines/portable_mixer.dm b/monkestation/code/modules/cassettes/machines/portable_mixer.dm index 4f95b833c7da..a3f77b0639e4 100644 --- a/monkestation/code/modules/cassettes/machines/portable_mixer.dm +++ b/monkestation/code/modules/cassettes/machines/portable_mixer.dm @@ -1,3 +1,4 @@ +#warn TODO: cassette mixer /obj/item/device/cassette_deck name = "Dual Cassette Deck" desc = "A Dual Cassette Deck, popular for its ability to copy songs from a cassette. A relic of the old times" @@ -5,13 +6,13 @@ icon_state = "walkman" w_class = WEIGHT_CLASS_SMALL ///The cassette that is being copied from - var/obj/item/device/cassette_tape/send + var/obj/item/cassette_tape/send ///List of songs the sender has var/list/sender_list ///List of names the Sender has var/list/sender_names ///The cassette you are copying to - var/obj/item/device/cassette_tape/recieve + var/obj/item/cassette_tape/recieve ///List of songs the Reciever has var/list/reciever_list ///List of song names the Reciever has @@ -23,7 +24,7 @@ /obj/item/device/cassette_deck/AltClick(mob/user) if(recieve || send) - eject_tape(user) + //eject_tape(user) return return ..() @@ -32,15 +33,16 @@ removal = !removal /obj/item/device/cassette_deck/attackby(obj/item/cassette, mob/user) - if(!istype(cassette, /obj/item/device/cassette_tape)) + if(!istype(cassette, /obj/item/cassette_tape)) return if(!send || !recieve) - insert_tape(cassette) + //insert_tape(cassette) playsound(src,'sound/weapons/handcuffs.ogg',20,1) to_chat(user,("You insert \the [cassette] into \the [src]")) else to_chat(user,("Remove a tape first!")) +/* /obj/item/device/cassette_deck/attack_self(mob/user) . = ..() if(!recieve) @@ -76,7 +78,7 @@ reciever_list.Remove(reciever_list[num]) reciever_names.Remove(reciever_names[num]) -/obj/item/device/cassette_deck/proc/insert_tape(obj/item/device/cassette_tape/CTape) +/obj/item/device/cassette_deck/proc/insert_tape(obj/item/cassette_tape/CTape) if(send && recieve || !istype(CTape)) return @@ -113,3 +115,4 @@ send = null broke_approval = FALSE playsound(src,'sound/weapons/handcuffs.ogg',20,1) +*/ diff --git a/monkestation/code/modules/cassettes/machines/postbox.dm b/monkestation/code/modules/cassettes/machines/postbox.dm index 3b60a4326667..86d3767ed60b 100644 --- a/monkestation/code/modules/cassettes/machines/postbox.dm +++ b/monkestation/code/modules/cassettes/machines/postbox.dm @@ -1,3 +1,4 @@ +#warn TODO: cassette submission postbox /obj/machinery/cassette/mailbox name = "Space Board of Music Postbox" desc = "Has a slit specifically to fit cassettes into it." @@ -14,12 +15,12 @@ . = ..() REGISTER_REQUIRED_MAP_ITEM(1, INFINITY) - +/* /obj/machinery/cassette/mailbox/attackby(obj/item/weapon, mob/user, params) - if(!istype(weapon, /obj/item/device/cassette_tape) || !user.client) + if(!istype(weapon, /obj/item/cassette_tape) || !user.client) return - var/obj/item/device/cassette_tape/attacked_tape = weapon + var/obj/item/cassette_tape/attacked_tape = weapon var/list/admin_count = get_admin_counts(R_FUN) if(!length(admin_count["present"])) @@ -58,3 +59,4 @@ attacked_tape.moveToNullspace() submit_cassette_for_review(attacked_tape, user) return TRUE +*/ diff --git a/monkestation/code/modules/cassettes/machines/radio_mic.dm b/monkestation/code/modules/cassettes/machines/radio_mic.dm index 7336728ff584..7863adb076bc 100644 --- a/monkestation/code/modules/cassettes/machines/radio_mic.dm +++ b/monkestation/code/modules/cassettes/machines/radio_mic.dm @@ -26,6 +26,8 @@ /// overlay when speaking a message (is displayed simultaniously with speaker_active) overlay_mic_active = null + /// The proximity monitor used to allow people to hear DJ music while in hearing range. + var/datum/proximity_monitor/advanced/dj_music/music_field /obj/item/radio/radio_mic/Initialize(mapload) . = ..() @@ -40,6 +42,12 @@ set_broadcasting(TRUE) + music_field = new(src, isnull(listening_range) ? canhear_range : listening_range) + +/obj/item/radio/radio_mic/Destroy() + QDEL_NULL(music_field) + return ..() + /obj/item/radio/radio_mic/ui_interact(mob/user, datum/tgui/ui, datum/ui_state/state) return diff --git a/monkestation/code/modules/cassettes/machines/stationary_mixer.dm b/monkestation/code/modules/cassettes/machines/stationary_mixer.dm index 632e9426e8ae..ef901d9bcd6e 100644 --- a/monkestation/code/modules/cassettes/machines/stationary_mixer.dm +++ b/monkestation/code/modules/cassettes/machines/stationary_mixer.dm @@ -1,3 +1,4 @@ +#warn TODO: advanced cassette deck /obj/machinery/cassette/adv_cassette_deck name = "Advanced Cassette Deck" desc = "A more advanced less portable Cassette Deck. Useful for recording songs from our generation, or customizing the style of your cassettes." @@ -6,7 +7,7 @@ density = TRUE pass_flags = PASSTABLE ///cassette tape used in adding songs or customizing - var/obj/item/device/cassette_tape/tape + var/obj/item/cassette_tape/tape ///Selection used to remove songs var/selection @@ -20,7 +21,7 @@ return TRUE /obj/machinery/cassette/adv_cassette_deck/attackby(obj/item/cassette, mob/user) - if(!istype(cassette, /obj/item/device/cassette_tape)) + if(!istype(cassette, /obj/item/cassette_tape)) return ..() if(!tape) insert_tape(cassette) @@ -29,7 +30,7 @@ else to_chat(user,"Remove a tape first!") -/obj/machinery/cassette/adv_cassette_deck/proc/insert_tape(obj/item/device/cassette_tape/CTape) +/obj/machinery/cassette/adv_cassette_deck/proc/insert_tape(obj/item/cassette_tape/CTape) if(tape || !istype(CTape)) return tape = CTape @@ -57,6 +58,7 @@ ui = new(user, src, "CassetteDeck", name) ui.open() +/* /obj/machinery/cassette/adv_cassette_deck/ui_data(mob/user) ///all data for the tgui var/list/data = list() @@ -193,3 +195,4 @@ else tape.icon_state = design_path[design_names.Find(selection)] tape.side2_icon = design_path[design_names.Find(selection)] +*/ diff --git a/monkestation/code/modules/cassettes/random_cassette_selection.dm b/monkestation/code/modules/cassettes/random_cassette_selection.dm index 19b27f0fdd81..0cc292301f09 100644 --- a/monkestation/code/modules/cassettes/random_cassette_selection.dm +++ b/monkestation/code/modules/cassettes/random_cassette_selection.dm @@ -17,7 +17,7 @@ GLOBAL_LIST_INIT(approved_ids, initialize_approved_ids()) return list() return json_decode(file2text(ids_exist)) -/obj/item/device/cassette_tape/random +/obj/item/cassette_tape/random name = "Not Correctly Created Random Cassette" desc = "How did this happen?" random = TRUE diff --git a/monkestation/code/modules/cassettes/walkman/_walkmen.dm b/monkestation/code/modules/cassettes/walkman/_walkmen.dm index 6eee606c9ff9..cbf0c6c76f39 100644 --- a/monkestation/code/modules/cassettes/walkman/_walkmen.dm +++ b/monkestation/code/modules/cassettes/walkman/_walkmen.dm @@ -1,3 +1,4 @@ +#warn TODO: walkmen GLOBAL_LIST_INIT(parsed_audio, list()) GLOBAL_LIST_INIT(youtube_exempt, list( @@ -15,7 +16,7 @@ GLOBAL_LIST_INIT(youtube_exempt, list( w_class = WEIGHT_CLASS_SMALL actions_types = list(/datum/action/item_action/walkman/play_pause,/datum/action/item_action/walkman/next_song,/datum/action/item_action/walkman/restart_song) ///the cassette tape object - var/obj/item/device/cassette_tape/tape + var/obj/item/cassette_tape/tape ///if the walkman is paused or not var/paused = TRUE ///songs inside the current playlist @@ -43,6 +44,7 @@ GLOBAL_LIST_INIT(youtube_exempt, list( ///cooldown used by the next song to stop overlapping sounds between url based songs and normal ones COOLDOWN_DECLARE(next_song_use) +/* /obj/item/device/walkman/Initialize() . = ..() design = rand(1, 5) @@ -58,7 +60,7 @@ GLOBAL_LIST_INIT(youtube_exempt, list( . = ..() /obj/item/device/walkman/attackby(obj/item/cassette, mob/user) - if(!istype(cassette, /obj/item/device/cassette_tape)) + if(!istype(cassette, /obj/item/cassette_tape)) return if(!tape) insert_tape(cassette) @@ -142,7 +144,7 @@ GLOBAL_LIST_INIT(youtube_exempt, list( /obj/item/device/walkman/proc/play() if(!current_song) if(current_playlist.len > 0) - if(findtext(current_playlist[pl_index], GLOB.is_http_protocol)) + if(is_http_protocol(current_playlist[pl_index])) ///invoking youtube-dl var/ytdl = CONFIG_GET(string/invoke_youtubedl) ///the input for ytdl handled by the song list @@ -232,9 +234,9 @@ GLOBAL_LIST_INIT(youtube_exempt, list( /*Called when - *Arguments: obj/item/device/cassette_tape/CT -> the cassette in question that you are inserting into the walkman + *Arguments: obj/item/cassette_tape/CT -> the cassette in question that you are inserting into the walkman */ -/obj/item/device/walkman/proc/insert_tape(obj/item/device/cassette_tape/CTape) +/obj/item/device/walkman/proc/insert_tape(obj/item/cassette_tape/CTape) if(tape || !istype(CTape)) return @@ -285,7 +287,7 @@ GLOBAL_LIST_INIT(youtube_exempt, list( break_sound() pl_index = pl_index + 1 <= current_playlist.len ? (pl_index += 1) : 1 - link_play = findtext(current_playlist[pl_index], GLOB.is_http_protocol) ? TRUE : FALSE + link_play = is_http_protocol(current_playlist[pl_index]) if(!link_play) @@ -359,6 +361,7 @@ GLOBAL_LIST_INIT(youtube_exempt, list( return update_song(current_song, current_listener, 0) +*/ /* ACTION BUTTONS @@ -390,10 +393,12 @@ GLOBAL_LIST_INIT(youtube_exempt, list( ..() name = "Next song" +/* /datum/action/item_action/walkman/next_song/Trigger(trigger_flags) if(target) var/obj/item/device/walkman/walkM = target walkM.next_song(owner) +*/ /datum/action/item_action/walkman/restart_song button_icon_state = "walkman_restart" @@ -402,10 +407,13 @@ GLOBAL_LIST_INIT(youtube_exempt, list( ..() name = "Restart song" +/* /datum/action/item_action/walkman/restart_song/Trigger(trigger_flags) if(target) var/obj/item/device/walkman/walkM = target walkM.restart_song(owner) +*/ + #undef sound_to #undef NEXT_SONG_USE_TIMER diff --git a/monkestation/code/modules/mob/dead/observer/observer.dm b/monkestation/code/modules/mob/dead/observer/observer.dm new file mode 100644 index 000000000000..ab145f403285 --- /dev/null +++ b/monkestation/code/modules/mob/dead/observer/observer.dm @@ -0,0 +1,3 @@ +/mob/dead/observer/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_CAN_HEAR_MUSIC, INNATE_TRAIT) diff --git a/monkestation/code/modules/mob/living/silicon/silicon.dm b/monkestation/code/modules/mob/living/silicon/silicon.dm new file mode 100644 index 000000000000..6dab07329bf6 --- /dev/null +++ b/monkestation/code/modules/mob/living/silicon/silicon.dm @@ -0,0 +1,3 @@ +/mob/living/silicon/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_CAN_HEAR_MUSIC, INNATE_TRAIT) diff --git a/tgstation.dme b/tgstation.dme index 8dfe82c1869e..678f7535dba3 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -412,6 +412,7 @@ #include "code\__DEFINES\~monkestation\blueshift.dm" #include "code\__DEFINES\~monkestation\botany.dm" #include "code\__DEFINES\~monkestation\cargo.dm" +#include "code\__DEFINES\~monkestation\cassettes.dm" #include "code\__DEFINES\~monkestation\chat.dm" #include "code\__DEFINES\~monkestation\chewin.dm" #include "code\__DEFINES\~monkestation\clock_cult.dm" @@ -610,6 +611,7 @@ #include "code\__HELPERS\~monkestation-helpers\mobs.dm" #include "code\__HELPERS\~monkestation-helpers\records.dm" #include "code\__HELPERS\~monkestation-helpers\roundend.dm" +#include "code\__HELPERS\~monkestation-helpers\text.dm" #include "code\__HELPERS\~monkestation-helpers\time.dm" #include "code\__HELPERS\~monkestation-helpers\uwuify.dm" #include "code\__HELPERS\~monkestation-helpers\virology.dm" @@ -6170,6 +6172,7 @@ #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\implants\implant_misc.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" @@ -7122,7 +7125,10 @@ #include "monkestation\code\modules\cassettes\cassette_approval.dm" #include "monkestation\code\modules\cassettes\random_cassette_selection.dm" #include "monkestation\code\modules\cassettes\cassette_db\cassette_datum.dm" -#include "monkestation\code\modules\cassettes\cassette_db\subsystem.dm" +#include "monkestation\code\modules\cassettes\cassette_db\cassette_manager.dm" +#include "monkestation\code\modules\cassettes\dj\dj_music_field.dm" +#include "monkestation\code\modules\cassettes\dj\intercom.dm" +#include "monkestation\code\modules\cassettes\dj\mob_can_hear.dm" #include "monkestation\code\modules\cassettes\machines\cassette_rack.dm" #include "monkestation\code\modules\cassettes\machines\dj_station.dm" #include "monkestation\code\modules\cassettes\machines\portable_mixer.dm" @@ -7643,6 +7649,7 @@ #include "monkestation\code\modules\mob\dead\new_player\sprite_accessories\multi_part.dm" #include "monkestation\code\modules\mob\dead\new_player\sprite_accessories\sock_color.dm" #include "monkestation\code\modules\mob\dead\new_player\sprite_accessories\underwear.dm" +#include "monkestation\code\modules\mob\dead\observer\observer.dm" #include "monkestation\code\modules\mob\living\emote.dm" #include "monkestation\code\modules\mob\living\init_signals.dm" #include "monkestation\code\modules\mob\living\living.dm" @@ -7710,6 +7717,7 @@ #include "monkestation\code\modules\mob\living\carbon\human\species_type\tundra_moths\mothaccessories.dm" #include "monkestation\code\modules\mob\living\carbon\human\species_type\tundra_moths\tundramoths.dm" #include "monkestation\code\modules\mob\living\silicon\death.dm" +#include "monkestation\code\modules\mob\living\silicon\silicon.dm" #include "monkestation\code\modules\mob\living\simple_animal\megafauna\wendigo.dm" #include "monkestation\code\modules\mob\living\simple_animal\pets\bees.dm" #include "monkestation\code\modules\mob_spawn\ghost_roles\space_roles\oldchef.dm"