diff --git a/_maps/map_files/CogStation/CogStation.dmm b/_maps/map_files/CogStation/CogStation.dmm index 40a57135fd..3c64b9f6ad 100644 --- a/_maps/map_files/CogStation/CogStation.dmm +++ b/_maps/map_files/CogStation/CogStation.dmm @@ -2878,7 +2878,6 @@ /turf/closed/wall/r_wall, /area/security/processing) "agX" = ( -/obj/effect/mapping_helpers/airlock/cyclelink_helper, /obj/structure/cable{ icon_state = "1-2" }, @@ -4171,22 +4170,11 @@ /turf/open/floor/plasteel, /area/construction/secondary) "akg" = ( -/obj/machinery/power/solar_control{ - dir = 4; - id = "foreport"; - name = "Port Bow Solar Control" - }, -/obj/structure/sign/warning/electricshock{ - pixel_x = -32 - }, /obj/structure/cable{ - icon_state = "2-4" - }, -/obj/structure/cable{ - icon_state = "0-4" + icon_state = "1-2" }, /turf/open/floor/plating, -/area/maintenance/solars/port) +/area/maintenance/solars/starboard/fore) "akh" = ( /obj/effect/decal/cleanable/dirt, /turf/open/floor/plasteel, @@ -4196,14 +4184,16 @@ /turf/open/floor/plating, /area/construction/secondary) "akj" = ( -/obj/structure/chair{ - dir = 8 - }, /obj/structure/cable{ icon_state = "4-8" }, +/obj/machinery/door/airlock/external{ + name = "Starboard Bow Solar Exterior Airlock"; + req_access_txt = "10;13" + }, +/obj/machinery/door/firedoor, /turf/open/floor/plating, -/area/maintenance/solars/port) +/area/maintenance/solars/starboard/fore) "akk" = ( /obj/item/stack/tile/plasteel{ pixel_x = 10; @@ -4361,18 +4351,12 @@ /turf/open/floor/plasteel, /area/security/warden) "akz" = ( -/obj/machinery/light/small{ - dir = 1; - light_color = "#ffc1c1" +/obj/machinery/door/airlock/external, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 8 }, -/obj/machinery/power/terminal{ - dir = 4 - }, -/obj/structure/cable{ - icon_state = "0-8" - }, -/turf/open/floor/plating, -/area/maintenance/solars/port) +/turf/open/floor/plasteel, +/area/router/service) "akA" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/cable{ @@ -6331,16 +6315,16 @@ /turf/open/floor/plasteel, /area/security/brig) "aoW" = ( -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/external/glass{ - name = "Port Bow Solars External Access"; - req_access_txt = "10;13" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 8 +/obj/machinery/power/solar_control{ + dir = 4; + id = "foreport"; + name = "Port Bow Solar Control" }, /obj/structure/cable{ - icon_state = "4-8" + icon_state = "0-4" + }, +/obj/structure/cable{ + icon_state = "2-4" }, /turf/open/floor/plating, /area/maintenance/solars/port) @@ -7335,12 +7319,19 @@ /turf/open/floor/plasteel, /area/security/brig) "arr" = ( -/obj/structure/table, -/obj/machinery/cell_charger, -/obj/structure/sign/warning/vacuum/external{ - pixel_x = -32 +/obj/machinery/light/small{ + dir = 1; + light_color = "#ffc1c1" + }, +/obj/machinery/power/terminal{ + dir = 4 + }, +/obj/structure/cable{ + icon_state = "0-8" + }, +/obj/structure/chair{ + dir = 8 }, -/obj/item/stock_parts/cell/high/plus, /turf/open/floor/plating, /area/maintenance/solars/port) "ars" = ( @@ -7774,10 +7765,16 @@ /turf/open/floor/plasteel, /area/security/prison) "asl" = ( -/obj/machinery/portable_atmospherics/canister/oxygen, -/obj/machinery/camera{ - c_tag = "Port Bow Solar Maintenance"; - dir = 1 +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/machinery/door/firedoor, +/obj/machinery/door/airlock/external/glass{ + name = "Port Bow Solars External Access"; + req_access_txt = "10;13" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 8 }, /turf/open/floor/plating, /area/maintenance/solars/port) @@ -7991,12 +7988,18 @@ name = "Canister Storage" }) "asH" = ( -/obj/machinery/firealarm{ - dir = 1; - pixel_y = -26 +/obj/machinery/camera{ + c_tag = "Port Bow Solar Maintenance"; + dir = 1 }, -/obj/machinery/atmospherics/pipe/manifold/general/visible{ - dir = 8 +/obj/structure/table, +/obj/machinery/cell_charger, +/obj/item/stock_parts/cell/high/plus, +/obj/structure/sign/warning/vacuum/external{ + pixel_x = -32 + }, +/obj/structure/sign/warning/electricshock{ + pixel_y = -32 }, /turf/open/floor/plating, /area/maintenance/solars/port) @@ -8236,12 +8239,16 @@ /turf/open/floor/plasteel/cafeteria, /area/crew_quarters/kitchen) "atn" = ( -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ +/obj/machinery/firealarm{ + dir = 1; + pixel_y = -26 + }, +/obj/machinery/atmospherics/pipe/manifold/general/visible{ dir = 8 }, -/obj/machinery/door/airlock/external, -/turf/open/floor/plasteel, -/area/router/service) +/obj/machinery/portable_atmospherics/canister/oxygen, +/turf/open/floor/plating, +/area/maintenance/solars/port) "ato" = ( /obj/structure/disposalpipe/segment{ dir = 6 @@ -10451,13 +10458,11 @@ /turf/open/floor/circuit, /area/ai_monitored/turret_protected/ai) "aya" = ( -/obj/structure/disposalpipe/segment, -/obj/structure/cable{ - icon_state = "1-2" +/obj/machinery/atmospherics/pipe/simple/cyan/hidden{ + dir = 4 }, -/obj/machinery/camera{ - c_tag = "AI Core Exterior"; - dir = 1 +/obj/structure/cable{ + icon_state = "2-4" }, /turf/open/floor/plating/airless, /area/space/nearstation) @@ -15104,19 +15109,19 @@ }, /area/chapel/main) "aHj" = ( -/obj/machinery/door/firedoor, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 1 +/obj/effect/turf_decal/tile/yellow, +/obj/effect/turf_decal/tile/yellow{ + dir = 4 }, /obj/structure/cable{ - icon_state = "1-2" + icon_state = "4-8" }, -/obj/machinery/door/airlock/external{ - name = "Starboard Bow Solar Exterior Airlock"; - req_access_txt = "10;13" +/obj/machinery/airalarm{ + dir = 1; + pixel_y = -22 }, -/turf/open/floor/plating, -/area/maintenance/solars/starboard/fore) +/turf/open/floor/plasteel, +/area/tcommsat/computer) "aHk" = ( /obj/structure/chair{ dir = 4 @@ -15272,13 +15277,14 @@ /area/bridge) "aHE" = ( /obj/structure/cable{ - icon_state = "2-4" + icon_state = "4-8" }, -/obj/machinery/atmospherics/pipe/simple/cyan/hidden{ - dir = 4 +/obj/machinery/airalarm{ + dir = 1; + pixel_y = -22 }, -/turf/open/floor/plating/airless, -/area/space/nearstation) +/turf/open/floor/plating, +/area/tcommsat/computer) "aHF" = ( /turf/open/floor/plasteel/dark, /area/bridge) @@ -20986,19 +20992,18 @@ /turf/open/floor/plasteel, /area/security/brig) "aTh" = ( -/obj/effect/turf_decal/stripes/line, -/obj/machinery/light{ +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/machinery/door/airlock/external/glass{ + name = "Telecommunications External Access"; + req_access_txt = "61" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ dir = 8 }, -/obj/machinery/power/apc{ - name = "AI Upload Foyer APC"; - pixel_y = -24 - }, -/obj/structure/cable{ - icon_state = "0-4" - }, -/turf/open/floor/plasteel, -/area/ai_monitored/turret_protected/ai_upload_foyer) +/turf/open/floor/plating, +/area/tcommsat/computer) "aTi" = ( /obj/effect/turf_decal/tile/blue{ dir = 4 @@ -22804,14 +22809,14 @@ /turf/open/floor/wood, /area/library) "aXf" = ( -/obj/structure/window/reinforced/spawner/east, -/obj/structure/table/wood, +/obj/machinery/photocopier, /obj/machinery/light{ dir = 1; - light_color = "#c1caff" + light_color = "#e8eaff" }, -/turf/open/floor/carpet, -/area/library) +/obj/item/paper/fluff/cogstation/eva, +/turf/open/floor/plasteel/dark, +/area/security/checkpoint/customs) "aXg" = ( /obj/machinery/vending/coffee, /turf/open/floor/plasteel, @@ -23913,35 +23918,20 @@ /turf/closed/wall, /area/lawoffice) "aZz" = ( -/obj/structure/table/reinforced, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ +/obj/structure/lattice, +/obj/machinery/camera{ + c_tag = "Routing Depot - Fore Exterior"; dir = 4 }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/item/paper_bin, -/obj/item/pen, -/turf/open/floor/plasteel/dark, -/area/lawoffice) +/turf/open/space/basic, +/area/maintenance/department/eva) "aZA" = ( -/obj/structure/table/reinforced, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ +/obj/machinery/computer/card{ dir = 8 }, +/obj/item/paper/guides/cogstation/job_changes, /turf/open/floor/plasteel/dark, -/area/lawoffice) +/area/security/checkpoint/customs) "aZB" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -23961,7 +23951,9 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, -/obj/item/book/manual/wiki/security_space_law, +/obj/item/paper_bin, +/obj/item/pen, +/obj/structure/window/reinforced/spawner/north, /turf/open/floor/plasteel/dark, /area/lawoffice) "aZD" = ( @@ -24018,13 +24010,21 @@ /turf/open/floor/plasteel, /area/hallway/secondary/civilian) "aZI" = ( +/obj/structure/table/reinforced, +/obj/effect/turf_decal/tile/neutral, /obj/effect/turf_decal/tile/neutral{ dir = 4 }, /obj/effect/turf_decal/tile/neutral{ dir = 1 }, -/obj/effect/landmark/start/lawyer, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/machinery/door/window/northleft{ + name = "Law Office Counter"; + req_access_txt = "38" + }, /turf/open/floor/plasteel/dark, /area/lawoffice) "aZJ" = ( @@ -24426,13 +24426,21 @@ /turf/open/floor/wood, /area/library) "baz" = ( -/obj/machinery/photocopier, -/obj/machinery/light{ - dir = 1; - light_color = "#e8eaff" +/obj/structure/table/reinforced, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/item/book/manual/wiki/security_space_law, +/obj/structure/window/reinforced/spawner/north, /turf/open/floor/plasteel/dark, -/area/security/checkpoint/customs) +/area/lawoffice) "baA" = ( /obj/structure/filingcabinet/chestdrawer, /obj/item/folder/blue, @@ -25282,11 +25290,17 @@ /turf/closed/wall/r_wall, /area/science/mixing) "bcz" = ( -/obj/machinery/computer/card{ - dir = 8 +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/machinery/newscaster{ + pixel_x = -30 }, /turf/open/floor/plasteel/dark, -/area/security/checkpoint/customs) +/area/lawoffice) "bcA" = ( /obj/effect/turf_decal/tile/brown{ dir = 1 @@ -26131,9 +26145,21 @@ /turf/open/floor/plasteel, /area/quartermaster/office) "beC" = ( -/obj/machinery/recharge_station, -/turf/open/floor/plating, -/area/ai_monitored/turret_protected/ai_upload_foyer) +/obj/structure/rack, +/obj/item/storage/toolbox/mechanical, +/obj/item/radio/off, +/obj/effect/turf_decal/tile/yellow, +/obj/effect/turf_decal/tile/yellow{ + dir = 4 + }, +/obj/effect/turf_decal/tile/yellow{ + dir = 1 + }, +/obj/machinery/airalarm{ + pixel_y = 24 + }, +/turf/open/floor/plasteel, +/area/tcommsat/computer) "beD" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 10 @@ -26156,13 +26182,16 @@ /turf/open/floor/plasteel, /area/hallway/primary/central) "beG" = ( -/obj/structure/lattice, -/obj/machinery/camera{ - c_tag = "Routing Depot - Fore Exterior"; +/obj/effect/turf_decal/tile/neutral{ dir = 4 }, -/turf/open/space/basic, -/area/space/nearstation) +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/landmark/start/lawyer, +/obj/structure/chair/stool, +/turf/open/floor/plasteel/dark, +/area/lawoffice) "beH" = ( /obj/structure/window/reinforced/spawner/east, /obj/machinery/light{ @@ -30649,9 +30678,11 @@ /turf/open/floor/plasteel, /area/hallway/primary/central) "bnS" = ( -/obj/machinery/atmospherics/components/unary/outlet_injector/on, -/turf/open/floor/plating/airless, -/area/space/nearstation) +/obj/structure/cable{ + icon_state = "1-2" + }, +/turf/open/floor/plating, +/area/crew_quarters/fitness/cogpool) "bnT" = ( /obj/structure/disposalpipe/segment, /turf/closed/wall/r_wall, @@ -30768,17 +30799,17 @@ /turf/open/floor/plasteel, /area/ai_monitored/nuke_storage) "bof" = ( +/obj/structure/cable{ + icon_state = "1-2" + }, /obj/machinery/door/airlock/external/glass{ name = "Pool Exterior Access"; req_access_txt = "13" }, -/obj/structure/cable{ - icon_state = "1-2" - }, /obj/effect/mapping_helpers/airlock/cyclelink_helper{ dir = 1 }, -/turf/open/floor/plating, +/turf/open/space/basic, /area/crew_quarters/fitness/cogpool) "bog" = ( /obj/machinery/light{ @@ -32879,19 +32910,9 @@ /turf/open/floor/plasteel/stairs/left, /area/hydroponics) "bsx" = ( -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 1 - }, -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/door/airlock/external{ - name = "Recycler Exterior Access"; - req_one_access_txt = "13;50" - }, -/obj/machinery/atmospherics/pipe/simple/supplymain/hidden, -/turf/open/floor/plasteel, -/area/maintenance/disposal) +/obj/structure/lattice, +/turf/closed/wall/r_wall, +/area/crew_quarters/fitness/cogpool) "bsy" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/disposalpipe/segment, @@ -33297,21 +33318,9 @@ /turf/open/floor/plating, /area/maintenance/aft) "btw" = ( -/obj/structure/disposalpipe/segment, -/obj/structure/cable{ - icon_state = "2-4" - }, -/obj/structure/cable{ - icon_state = "2-8" - }, -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/light/small, -/turf/open/floor/plating{ - icon_state = "platingdmg2" - }, -/area/maintenance/department/eva) +/obj/machinery/atmospherics/components/unary/outlet_injector/on, +/turf/open/floor/plating/airless, +/area/router) "btx" = ( /obj/machinery/space_heater, /turf/open/floor/plating, @@ -34237,17 +34246,13 @@ /turf/open/floor/plasteel, /area/crew_quarters/bar) "bvM" = ( -/obj/machinery/door/airlock/external/glass{ - name = "External Construction Access"; - req_one_access_txt = "10;31" +/obj/structure/lattice, +/obj/machinery/camera{ + c_tag = "Routing Depot - Aft Exterior"; + pixel_x = 22 }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper, -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/supplymain/hidden, -/turf/open/floor/plasteel, -/area/quartermaster/warehouse) +/turf/open/space/basic, +/area/router) "bvN" = ( /obj/machinery/atmospherics/components/binary/valve/digital/on{ dir = 4 @@ -34971,21 +34976,30 @@ /turf/open/floor/plating, /area/ai_monitored/turret_protected/ai) "bxm" = ( -/obj/effect/turf_decal/tile/yellow{ - dir = 8 - }, -/turf/closed/wall/r_wall, -/area/tcommsat/computer) -"bxn" = ( -/obj/effect/turf_decal/tile/yellow, -/obj/effect/turf_decal/tile/yellow{ - dir = 4 +/obj/structure/disposalpipe/segment, +/obj/structure/cable{ + icon_state = "2-4" }, /obj/structure/cable{ - icon_state = "4-8" + icon_state = "2-8" }, -/turf/open/floor/plasteel, -/area/tcommsat/computer) +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/light/small, +/turf/open/floor/plating, +/area/maintenance/department/eva) +"bxn" = ( +/obj/structure/disposalpipe/segment, +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/camera{ + c_tag = "AI Core Exterior"; + dir = 1 + }, +/turf/open/floor/plating/airless, +/area/maintenance/department/eva) "bxo" = ( /obj/machinery/door/airlock/external{ name = "Telecommunications External Access"; @@ -35048,18 +35062,13 @@ /turf/open/floor/plating, /area/maintenance/disposal) "bxt" = ( -/obj/machinery/door/airlock/external/glass{ - name = "Telecommunications External Access"; - req_access_txt = "61" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 8 - }, -/obj/structure/cable{ - icon_state = "4-8" +/obj/effect/spawner/structure/window/reinforced, +/obj/structure/disposalpipe/segment, +/obj/machinery/atmospherics/pipe/simple/supplymain/hidden{ + dir = 4 }, /turf/open/floor/plating, -/area/tcommsat/computer) +/area/maintenance/disposal) "bxu" = ( /obj/structure/rack, /obj/item/circuitboard/machine/telecomms/broadcaster, @@ -35217,18 +35226,15 @@ /turf/open/floor/plasteel, /area/tcommsat/computer) "bxK" = ( -/obj/structure/rack, -/obj/item/storage/toolbox/mechanical, -/obj/item/radio/off, -/obj/effect/turf_decal/tile/yellow, -/obj/effect/turf_decal/tile/yellow{ +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/effect/turf_decal/delivery, +/obj/machinery/atmospherics/pipe/manifold/supplymain/hidden{ dir = 4 }, -/obj/effect/turf_decal/tile/yellow{ - dir = 1 - }, /turf/open/floor/plasteel, -/area/tcommsat/computer) +/area/maintenance/disposal) "bxL" = ( /obj/structure/disposalpipe/segment{ dir = 5 @@ -35905,11 +35911,11 @@ /turf/open/floor/plating, /area/maintenance/starboard/central) "bzp" = ( -/obj/structure/cable{ - icon_state = "4-8" - }, +/obj/structure/lattice/catwalk, +/obj/structure/disposalpipe/segment, +/obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, -/area/maintenance/solars/starboard/fore) +/area/maintenance/disposal) "bzq" = ( /obj/machinery/light{ dir = 8 @@ -37644,12 +37650,20 @@ /turf/open/floor/plasteel/showroomfloor, /area/medical/morgue) "bDb" = ( -/mob/living/simple_animal/mouse/white{ - desc = "Wubba lubba dub dub."; - name = "Rick" +/obj/structure/lattice/catwalk, +/obj/structure/cable{ + icon_state = "1-2" }, -/turf/open/floor/plasteel/showroomfloor, -/area/medical/morgue) +/obj/machinery/door/airlock/external{ + name = "Recycler Exterior Access"; + req_one_access_txt = "13;50" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/simple/supplymain/hidden, +/turf/open/floor/plasteel, +/area/maintenance/disposal) "bDc" = ( /obj/machinery/vending/security, /obj/structure/disposalpipe/segment, @@ -39183,23 +39197,9 @@ /turf/open/floor/plasteel/white, /area/medical/medbay/central) "bFY" = ( -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 1 - }, -/obj/structure/noticeboard{ - pixel_y = 28 - }, -/obj/machinery/light{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/simple/cyan/hidden{ - dir = 4 - }, -/turf/open/floor/plasteel/white, -/area/medical/medbay/central) +/obj/structure/lattice/catwalk, +/turf/closed/wall/r_wall, +/area/engine/atmos) "bFZ" = ( /obj/machinery/chem_heater, /turf/open/floor/plasteel/white, @@ -45857,11 +45857,11 @@ /area/engine/atmos) "bTJ" = ( /obj/structure/lattice, -/obj/machinery/atmospherics/pipe/simple/violet/hidden{ - dir = 8 - }, -/turf/open/space/basic, -/area/space/nearstation) +/obj/structure/disposalpipe/segment, +/obj/machinery/atmospherics/pipe/simple/orange/visible, +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/maintenance/disposal) "bTK" = ( /turf/open/floor/engine/co2, /area/engine/atmos) @@ -46099,17 +46099,12 @@ /turf/open/floor/plasteel, /area/ai_monitored/turret_protected/ai_upload_foyer) "bUj" = ( -/obj/structure/cable{ - icon_state = "1-8" +/obj/machinery/atmospherics/pipe/simple/violet/hidden{ + dir = 8 }, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/supplymain/hidden{ - dir = 9 - }, -/turf/open/floor/plasteel, -/area/ai_monitored/turret_protected/ai_upload_foyer) +/obj/structure/lattice/catwalk, +/turf/open/space/basic, +/area/space/nearstation) "bUk" = ( /obj/structure/closet/crate, /obj/effect/decal/cleanable/cobweb, @@ -46481,11 +46476,11 @@ /turf/open/floor/plasteel, /area/ai_monitored/turret_protected/ai_upload_foyer) "bUV" = ( -/obj/structure/lattice, /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/violet/hidden{ dir = 10 }, +/obj/structure/lattice/catwalk, /turf/open/space/basic, /area/space/nearstation) "bUW" = ( @@ -46837,14 +46832,9 @@ /turf/open/floor/plating, /area/ai_monitored/turret_protected/ai_upload_foyer) "bVD" = ( -/obj/structure/cable{ - icon_state = "1-8" - }, -/obj/machinery/atmospherics/pipe/simple/orange/hidden{ - dir = 8 - }, -/turf/open/floor/plating, -/area/ai_monitored/turret_protected/ai_upload_foyer) +/obj/structure/lattice, +/turf/open/space/basic, +/area/space) "bVE" = ( /obj/effect/landmark/start/atmospheric_technician, /obj/machinery/atmospherics/pipe/simple/orange/visible{ @@ -47270,13 +47260,16 @@ /turf/open/floor/engine/n2o, /area/engine/atmos) "bWq" = ( -/obj/structure/lattice, -/obj/machinery/camera{ - c_tag = "Routing Depot - Aft Exterior"; - pixel_x = 22 +/obj/effect/turf_decal/stripes/line{ + dir = 4 }, -/turf/open/space/basic, -/area/space/nearstation) +/obj/machinery/atmospherics/pipe/simple/cyan/hidden, +/obj/machinery/airalarm{ + dir = 8; + pixel_x = 23 + }, +/turf/open/floor/plasteel, +/area/hallway/primary/central) "bWr" = ( /obj/structure/closet/l3closet/scientist, /obj/effect/turf_decal/stripes/line, @@ -47442,17 +47435,18 @@ /turf/closed/wall/r_wall, /area/ai_monitored/turret_protected/ai_upload_foyer) "bWJ" = ( -/obj/effect/turf_decal/stripes/line, -/obj/structure/disposalpipe/segment, -/obj/structure/cable{ - icon_state = "2-4" - }, +/obj/structure/lattice/catwalk, /obj/structure/cable{ icon_state = "4-8" }, -/obj/machinery/atmospherics/pipe/simple/orange/hidden, -/turf/open/floor/plasteel, -/area/ai_monitored/turret_protected/ai_upload_foyer) +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/manifold/supplymain/visible{ + dir = 1 + }, +/turf/open/space/basic, +/area/space/nearstation) "bWK" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/atmospherics/pipe/simple/orange/visible, @@ -49080,14 +49074,15 @@ }, /area/library) "bZV" = ( +/obj/structure/window/reinforced/spawner/east, /obj/structure/table/wood, -/obj/structure/window/reinforced/spawner/north, -/obj/structure/cable{ - icon_state = "1-2" - }, -/turf/open/floor/carpet{ - icon_state = "carpetsymbol" +/obj/machinery/light{ + dir = 1; + light_color = "#c1caff" }, +/obj/item/paper_bin, +/obj/item/pen, +/turf/open/floor/carpet, /area/library) "bZW" = ( /obj/structure/sign/warning/fire, @@ -49805,18 +49800,24 @@ /turf/open/floor/plating, /area/ai_monitored/storage/eva) "cbI" = ( -/obj/structure/lattice/catwalk, /obj/structure/cable{ icon_state = "4-8" }, -/obj/machinery/atmospherics/pipe/manifold/supplymain/visible{ - dir = 1 - }, /obj/structure/disposalpipe/segment{ dir = 4 }, -/turf/open/space/basic, -/area/space/nearstation) +/obj/machinery/atmospherics/pipe/simple/supplymain/visible{ + dir = 4 + }, +/obj/machinery/door/airlock/external{ + name = "Atmospherics External Airlock"; + req_access_txt = "24" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/engine/atmos) "cbJ" = ( /obj/structure/table/reinforced, /obj/item/analyzer, @@ -49858,19 +49859,13 @@ /obj/structure/cable{ icon_state = "4-8" }, -/obj/machinery/door/airlock/external{ - name = "Atmospherics External Airlock"; - req_access_txt = "24" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 4 - }, /obj/machinery/atmospherics/pipe/simple/supplymain/hidden{ dir = 4 }, /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, /area/engine/atmos) "cbM" = ( @@ -51026,24 +51021,17 @@ /turf/closed/wall/r_wall, /area/medical/medbay/lobby) "cea" = ( -/obj/effect/turf_decal/delivery, -/obj/machinery/photocopier, -/obj/structure/disposalpipe/segment{ - dir = 4 +/obj/structure/table/wood, +/obj/structure/window/reinforced/spawner/north, +/obj/structure/cable{ + icon_state = "1-2" }, -/obj/machinery/requests_console{ - department = "Cargo Bay"; - departmentType = 2; - name = "Cargo RC"; - pixel_y = 30 +/obj/item/paper_bin, +/obj/item/pen, +/turf/open/floor/carpet{ + icon_state = "carpetsymbol" }, -/obj/machinery/camera{ - c_tag = "Supply - Delivery Office Aft"; - network = list("ss13","rd") - }, -/obj/item/paper/guides/cogstation/disposals, -/turf/open/floor/plasteel, -/area/quartermaster/sorting) +/area/library) "ceb" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/shutters/preopen{ @@ -55857,6 +55845,28 @@ }, /turf/open/floor/wood, /area/library) +"cnQ" = ( +/obj/structure/lattice, +/obj/structure/disposalpipe/segment, +/obj/machinery/atmospherics/pipe/simple/orange/hidden, +/obj/machinery/camera{ + c_tag = "Supply - Warehouse Exterior"; + dir = 1 + }, +/turf/open/space/basic, +/area/quartermaster/warehouse) +"cnR" = ( +/obj/structure/cable{ + icon_state = "1-8" + }, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/manifold/supplymain/hidden{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/ai_monitored/turret_protected/ai_upload_foyer) "cnS" = ( /obj/structure/cable{ icon_state = "0-2" @@ -56625,18 +56635,25 @@ /turf/open/floor/plasteel, /area/hallway/primary/central) "cpm" = ( -/obj/structure/chair/stool, -/obj/effect/turf_decal/tile/neutral{ +/obj/effect/turf_decal/delivery, +/obj/machinery/photocopier, +/obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 +/obj/machinery/requests_console{ + department = "Cargo Bay"; + departmentType = 2; + name = "Cargo RC"; + pixel_y = 30 }, -/obj/machinery/newscaster{ - pixel_x = -30 +/obj/machinery/camera{ + c_tag = "Supply - Delivery Office Aft"; + network = list("ss13","rd") }, -/turf/open/floor/plasteel/dark, -/area/lawoffice) +/obj/item/paper/guides/cogstation/disposals, +/obj/item/paper/fluff/cogstation/mulebot, +/turf/open/floor/plasteel, +/area/quartermaster/sorting) "cpn" = ( /obj/effect/turf_decal/tile/neutral, /obj/effect/turf_decal/tile/neutral{ @@ -58509,8 +58526,9 @@ req_access_txt = "5" }, /obj/item/clipboard, -/obj/item/clothing/glasses/hud/health, /obj/machinery/atmospherics/pipe/simple/orange/hidden, +/obj/item/paper/guides/cogstation/letter_med, +/obj/item/clothing/glasses/hud/health, /obj/item/pen, /obj/item/clothing/glasses/hud/health, /turf/open/floor/plasteel/white, @@ -61375,6 +61393,16 @@ /obj/machinery/atmospherics/pipe/simple/cyan/hidden, /turf/open/floor/plasteel, /area/quartermaster/miningoffice) +"cyd" = ( +/obj/structure/cable{ + icon_state = "1-8" + }, +/obj/machinery/atmospherics/pipe/simple/orange/hidden{ + dir = 8 + }, +/obj/machinery/atmospherics/components/unary/vent_pump/on, +/turf/open/floor/plating, +/area/ai_monitored/turret_protected/ai_upload_foyer) "cye" = ( /obj/structure/disposalpipe/segment{ dir = 5 @@ -65719,19 +65747,24 @@ /turf/open/floor/plasteel/white, /area/medical/medbay/central) "cFs" = ( -/obj/structure/table, -/obj/effect/turf_decal/tile/red{ +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ dir = 1 }, -/obj/effect/turf_decal/tile/red{ - dir = 8 +/obj/structure/noticeboard{ + pixel_y = 28 }, -/obj/machinery/atmospherics/pipe/simple/orange/hidden, -/obj/item/storage/backpack/duffelbag/med/surgery, +/obj/machinery/light{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/simple/cyan/hidden{ + dir = 4 + }, +/obj/item/paper/guides/cogstation/letter_med, /turf/open/floor/plasteel/white, -/area/medical/medbay/zone2{ - name = "Medbay Treatment Center" - }) +/area/medical/medbay/central) "cFt" = ( /obj/effect/turf_decal/tile/blue{ dir = 1 @@ -67921,6 +67954,11 @@ /obj/machinery/atmospherics/pipe/simple/supplymain/hidden, /turf/open/floor/plasteel/freezer, /area/crew_quarters/toilet/restrooms) +"cJg" = ( +/obj/structure/disposalpipe/segment, +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/quartermaster/warehouse) "cJh" = ( /obj/structure/table, /obj/structure/bedsheetbin/towel, @@ -68022,6 +68060,18 @@ /area/engine/engineering{ name = "Engine Room" }) +"cJq" = ( +/obj/machinery/atmospherics/pipe/simple/supplymain/visible, +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/door/airlock/external/glass{ + name = "External Construction Access"; + req_one_access_txt = "10;31" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper, +/turf/open/floor/plasteel, +/area/quartermaster/warehouse) "cJr" = ( /obj/structure/lattice/catwalk, /obj/machinery/atmospherics/pipe/simple/supplymain/visible{ @@ -68030,23 +68080,28 @@ /turf/open/space/basic, /area/space/nearstation) "cJs" = ( -/obj/structure/lattice/catwalk, /obj/structure/disposalpipe/segment, -/obj/machinery/atmospherics/pipe/simple/supplymain/visible{ - dir = 4 - }, -/turf/open/space/basic, -/area/space/nearstation) +/obj/machinery/atmospherics/pipe/simple/orange/hidden, +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/quartermaster/warehouse) "cJt" = ( -/obj/structure/lattice/catwalk, +/obj/effect/turf_decal/stripes/line, +/obj/machinery/light{ + dir = 8 + }, +/obj/machinery/power/apc{ + name = "AI Upload Foyer APC"; + pixel_y = -24 + }, /obj/structure/cable{ - icon_state = "1-2" + icon_state = "0-4" }, -/obj/machinery/atmospherics/pipe/manifold/supplymain/visible{ - dir = 4 +/obj/machinery/atmospherics/pipe/simple/supplymain/hidden{ + dir = 5 }, -/turf/open/space/basic, -/area/space/nearstation) +/turf/open/floor/plasteel, +/area/ai_monitored/turret_protected/ai_upload_foyer) "cJu" = ( /obj/machinery/atmospherics/components/unary/portables_connector/visible{ dir = 1 @@ -69993,6 +70048,21 @@ "cNB" = ( /turf/open/floor/plating, /area/quartermaster/miningdock/airless) +"cNC" = ( +/obj/effect/turf_decal/stripes/line, +/obj/structure/disposalpipe/segment, +/obj/structure/cable{ + icon_state = "2-4" + }, +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/orange/hidden, +/obj/machinery/atmospherics/pipe/simple/supplymain/hidden{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/ai_monitored/turret_protected/ai_upload_foyer) "cND" = ( /obj/effect/landmark/event_spawn, /turf/open/floor/plasteel, @@ -70935,15 +71005,25 @@ /turf/open/space/basic, /area/space/nearstation) "cPG" = ( -/obj/structure/lattice, -/obj/structure/disposalpipe/segment, -/obj/machinery/camera{ - c_tag = "Supply - Warehouse Exterior"; - dir = 1 +/obj/effect/turf_decal/stripes/line, +/obj/structure/cable{ + icon_state = "1-8" }, -/obj/machinery/atmospherics/pipe/simple/orange/hidden, -/turf/open/space/basic, -/area/space/nearstation) +/obj/machinery/turretid{ + control_area = "/area/science/server"; + icon_state = "control_stun"; + name = "Computer Core turret control"; + pixel_x = -3; + pixel_y = -23; + req_access_txt = "65" + }, +/obj/machinery/atmospherics/pipe/simple/supplymain/hidden{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/science/server{ + name = "Computer Core" + }) "cPH" = ( /obj/machinery/conveyor_switch{ id = "EngiCargoConveyer" @@ -71796,9 +71876,6 @@ /obj/item/paper/guides/jobs/medical/cloning{ pixel_x = -4 }, -/obj/item/paper/fluff/cogstation/cloner{ - pixel_x = 6 - }, /turf/open/floor/plasteel, /area/medical/medbay/central) "cRc" = ( @@ -71864,22 +71941,11 @@ /turf/open/floor/plasteel/white/corner, /area/engine/atmos) "cRh" = ( -/obj/effect/turf_decal/stripes/line, -/obj/structure/cable{ - icon_state = "1-8" +/obj/machinery/atmospherics/pipe/simple/supplymain/hidden{ + dir = 4 }, -/obj/machinery/turretid{ - control_area = "/area/science/server"; - icon_state = "control_stun"; - name = "Computer Core turret control"; - pixel_x = -3; - pixel_y = -23; - req_access_txt = "65" - }, -/turf/open/floor/plasteel, -/area/science/server{ - name = "Computer Core" - }) +/turf/closed/wall/r_wall, +/area/ai_monitored/turret_protected/ai_upload_foyer) "cRi" = ( /obj/effect/turf_decal/tile/purple{ dir = 1 @@ -72023,6 +72089,53 @@ }, /turf/open/floor/plasteel, /area/science/robotics/lab) +"cRs" = ( +/obj/machinery/recharge_station, +/obj/machinery/atmospherics/pipe/simple/supplymain/hidden{ + dir = 9 + }, +/obj/machinery/airalarm{ + dir = 4; + pixel_x = -22 + }, +/turf/open/floor/plating, +/area/ai_monitored/turret_protected/ai_upload_foyer) +"cRt" = ( +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/supplymain/hidden, +/obj/effect/turf_decal/delivery, +/turf/open/floor/plasteel, +/area/quartermaster/warehouse) +"cRu" = ( +/obj/effect/turf_decal/delivery, +/turf/open/floor/plasteel, +/area/science/mixing) +"cRv" = ( +/mob/living/simple_animal/opossum{ + desc = "Wubba lubba dub dub."; + name = "Rick" + }, +/turf/open/floor/plasteel/showroomfloor, +/area/medical/morgue) +"cRw" = ( +/obj/structure/table, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/simple/orange/hidden, +/obj/item/paper/guides/cogstation/letter_med{ + pixel_y = 4 + }, +/obj/item/storage/backpack/duffelbag/med/surgery, +/turf/open/floor/plasteel/white, +/area/medical/medbay/zone2{ + name = "Medbay Treatment Center" + }) "cVq" = ( /obj/machinery/atmospherics/pipe/simple/orange/visible{ dir = 4 @@ -72130,14 +72243,6 @@ }, /turf/open/floor/plasteel, /area/engine/atmos) -"frb" = ( -/obj/structure/lattice/catwalk, -/obj/machinery/atmospherics/pipe/simple/supplymain/visible, -/obj/structure/cable{ - icon_state = "1-2" - }, -/turf/open/space/basic, -/area/space/nearstation) "fti" = ( /obj/machinery/atmospherics/components/binary/pump, /obj/machinery/atmospherics/pipe/simple/orange/hidden{ @@ -84950,7 +85055,7 @@ akI aAJ cMp aYa -bxm +akI akI aNC bxv @@ -85206,7 +85311,7 @@ aLH akI aAK aRC -bxn +aHj akI bxA hiV @@ -85723,7 +85828,7 @@ aXm aXs bxC akI -bxK +beC bxY byh byb @@ -85977,7 +86082,7 @@ aaU aaU aEa cMK -bxt +aHE akI akI akI @@ -86233,9 +86338,9 @@ aaU aaa aaU aEa -aHE -bab -aaa +cMK +aTh +akI aaa aaU aaa @@ -86490,7 +86595,8 @@ aaU aaU aaU aEb -aPT +aya +bab aaU aaU aye @@ -86508,7 +86614,6 @@ aye aye aye aye -aye aaa aaa aaa @@ -90657,7 +90762,7 @@ aaa aaa aaa aaa -aaa +aWj aaU aaa aaU @@ -90913,9 +91018,9 @@ aaa aaa aaa aaa -aaa -aWj -aaU +bcw +aUZ +bcw aaa bcy cnU @@ -91171,7 +91276,7 @@ aaa aaa aaa bcw -aUZ +cRu bcw aaa bcy @@ -91193,7 +91298,7 @@ aaa aaa aaU aaa -bAb +brs bxT bFq bHY @@ -91363,10 +91468,10 @@ aaa aaa aaU abx -aqw -aoW -aqw -aqw +aiN +alu +aiN +abx aaU aaa aaa @@ -91620,9 +91725,9 @@ aaa aaa aaU abx -akg -apS -arr +aiN +asl +aiN abx aaU aaa @@ -91877,9 +91982,9 @@ aaa aaa aaU aiN -akj -anV -asl +aoW +apS +asH abx aaU aaU @@ -92134,9 +92239,9 @@ aaa aaa aaU aiN -akz +arr aDO -asH +atn awm axc awm @@ -95499,7 +95604,7 @@ aNL aNL aVl aYT -baz +aXf bFH aYT aaU @@ -97043,7 +97148,7 @@ aVL aYT baG bbM -bcz +aZA aYT bdy beM @@ -98094,7 +98199,7 @@ bgA bWf bXm bZQ -ccN +bWq ccN cei ccN @@ -99380,12 +99485,12 @@ bWh csG bQA aZy -aXf +bZV bZJ bZF cpu bax -bZV +cea bZX ccY cab @@ -99628,8 +99733,8 @@ bnz bpw bQx bll -aZz -cpm +aZC +bcz bad bah baR @@ -99885,8 +99990,8 @@ bnA bpy bQI bll -aZA aZI +beG bae chP baS @@ -100142,7 +100247,7 @@ bnD bpC bQx bll -aZC +baz aZN bCS bak @@ -102760,7 +102865,7 @@ caR czi cKj buS -bFY +cFs cEX bIU aJE @@ -103536,7 +103641,7 @@ cFe cFk cFm cFq -cFs +cRw cFz bqt blx @@ -104781,8 +104886,8 @@ bLz bNe ajO bQb -bUj -aTh +cnR +cJt bfh brb bzN @@ -105022,9 +105127,9 @@ bOV aRq cjD bnU -btw +bxm bns -aya +bxn avy alo awn @@ -105039,7 +105144,7 @@ bPp bPU bQi bUU -bWJ +cNC bWP cbV bzR @@ -105279,7 +105384,7 @@ aSt aSt aSt aSt -cpG +aWd aSt brs avv @@ -105296,7 +105401,7 @@ cRf bPV bQT bVq -cRh +cPG bfh cbY cdn @@ -105523,7 +105628,7 @@ axV aog aog aSH -beG +aZz aaU aaU aaU @@ -105535,9 +105640,9 @@ aRv aaU aaU aaU -aWj -aSq -aWj +aSt +cpG +aSt aaU avv ajO @@ -105553,7 +105658,7 @@ ajO ajO aRy bVC -aRy +cRh bfh bfh bBR @@ -105809,8 +105914,8 @@ bmH aYZ bht bcY -bVD -beC +cyd +cRs cgF bfh bBS @@ -106033,7 +106138,7 @@ aOJ aOJ bmF bnT -aRJ +bnT aRJ aRJ chm @@ -106289,13 +106394,13 @@ bfv bfv bkW bmJ +bnS bof aQJ aQJ aQJ aQJ aQJ -aQJ aRZ aaU aaa @@ -106357,7 +106462,7 @@ cyP byQ bzS czw -bDb +cRv bBu crX czI @@ -106547,7 +106652,7 @@ bjL blD bmK boi -aaU +bsx aaU aaU aaU @@ -106805,7 +106910,7 @@ aOJ aPN bes aaU -aye +bQe aye bQe aye @@ -107839,7 +107944,7 @@ aye aye aaU aWj -bnS +btw buq bwC bAr @@ -108619,7 +108724,7 @@ bMS bBv bXM bvB -bWq +bvM acY adz bsz @@ -109124,7 +109229,7 @@ aaa aaa aaU aWj -bnS +btw buq bwZ bBC @@ -113190,7 +113295,7 @@ abe aeP agX ajS -aHj +akg bvm ahP aif @@ -113448,7 +113553,7 @@ aaV ahP ahP ahP -bzp +akj ahP aif aiB @@ -115821,10 +115926,10 @@ bra bwW bwW aaU -bdR -cJr -bTJ -aaa +anr +aWj +bUj +bVD aaa cPz aaa @@ -116078,8 +116183,8 @@ brr bCr cwv bAQ -bAQ -cJs +bxt +bzp bUV caf bam @@ -116089,7 +116194,7 @@ bYl bam bam bam -bam +cJg bbX bbX bcn @@ -116335,8 +116440,8 @@ brP brX bsh bsu -bsx -cJt +bxK +bDb sVC cdp sVC @@ -116346,8 +116451,8 @@ bZp sVC sVC sVC -frb -bvM +cJq +cRt bvW bvX bwd @@ -116593,7 +116698,7 @@ bsi bsv bsy bsy -bsD +bTJ bsI bRv bsD @@ -116602,8 +116707,8 @@ bsD cPE cPF cPF -cPF -cPG +cnQ +cJs bBL bBL bCm @@ -118141,9 +118246,9 @@ bJv aaU cwJ aaU -cbG -aaU -aaU +bWJ +mIT +cJr aaU bbT bbT @@ -118397,10 +118502,10 @@ bPA wPS aaa cwI -aaa +cwA cbI -mIT -cJr +cwA +anr aaa aaU aaU @@ -118654,9 +118759,9 @@ bgM cwA cwA cwL -cww +cwA cbL -cww +cwA bdR mIT mIT @@ -118670,7 +118775,7 @@ bgk bbT bkd bbT -cea +cpm bVG cgn cje @@ -119876,7 +119981,7 @@ aaa aaa aaa arM -atn +atl arM aaU aaU @@ -120132,9 +120237,9 @@ aaa aaa aaa aaa -aaa -aWj -aaU +arM +akz +arM aaU aaU aaU @@ -120390,9 +120495,9 @@ aaa aaa aaa aaa -aaa -aaa -aaa +aWj +aaU +aaU aaa aaa aaU @@ -125338,11 +125443,11 @@ aWj cww cJm cww -aaU -aaa -aaa -aaa -aaa +aWj +aWj +aWj +aWj +aWj aaU aaU aaa @@ -125593,9 +125698,9 @@ bal aaU aWj cww -cJn +cJm cww -aaU +aWj bal bal bal @@ -125849,10 +125954,10 @@ aaa aaa aaU aWj +bFY +cJn +cww aWj -aWj -aaa -aaa aaa aaa aaa @@ -126105,11 +126210,11 @@ aaa aaa aaa aaU -aaa -aaa -aaa -aaa -aaa +aWj +aWj +aWj +aWj +aWj bal bal bal diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index fdc313c30a..6f0636d3cb 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -982,11 +982,11 @@ /area/security/prison) "aci" = ( /obj/structure/lattice/catwalk, -/obj/machinery/door/airlock/external{ - req_one_access_txt = "13,8" +/obj/structure/cable{ + icon_state = "0-2" }, /turf/open/space, -/area/maintenance/starboard/fore) +/area/solar/starboard/fore) "acj" = ( /obj/machinery/light/small{ dir = 1 @@ -3123,6 +3123,17 @@ }, /turf/open/floor/plasteel, /area/security/prison) +"afQ" = ( +/obj/machinery/door/airlock/external{ + name = "Solar Maintenance"; + req_access_txt = "10; 13" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper, +/obj/structure/cable{ + icon_state = "1-2" + }, +/turf/open/floor/plating, +/area/maintenance/solars/starboard/fore) "afR" = ( /obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ dir = 1 @@ -5480,15 +5491,17 @@ /turf/open/floor/plasteel/dark, /area/engine/gravity_generator) "akd" = ( -/turf/open/space, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/structure/cable{ + icon_state = "1-2" + }, +/turf/open/floor/plating, /area/maintenance/solars/starboard/fore) "ake" = ( -/obj/structure/cable{ - icon_state = "0-2" - }, -/obj/structure/lattice/catwalk, /turf/open/space, -/area/maintenance/solars/starboard/fore) +/area/space) "akf" = ( /obj/machinery/conveyor{ dir = 1; @@ -6294,11 +6307,6 @@ /obj/structure/cable{ icon_state = "1-2" }, -/obj/machinery/door/airlock/external{ - name = "Solar Maintenance"; - req_access_txt = "10; 13" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper, /turf/open/floor/plating, /area/maintenance/solars/starboard/fore) "alx" = ( @@ -6997,6 +7005,10 @@ /obj/structure/window/reinforced, /turf/open/floor/plasteel/dark, /area/crew_quarters/fitness/recreation) +"amB" = ( +/obj/effect/turf_decal/stripes/line, +/turf/open/floor/plating, +/area/maintenance/port) "amC" = ( /obj/structure/chair{ dir = 4 @@ -7014,6 +7026,17 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/closed/wall, /area/crew_quarters/fitness/recreation) +"amF" = ( +/obj/machinery/door/airlock/maintenance/abandoned{ + name = "Storage Room"; + req_access_txt = "12" + }, +/obj/structure/barricade/wooden, +/turf/open/floor/plating, +/area/crew_quarters/fitness/pool) +"amG" = ( +/turf/closed/wall, +/area/crew_quarters/fitness/pool) "amH" = ( /obj/machinery/door/airlock/external{ req_one_access_txt = "13,8" @@ -7086,14 +7109,9 @@ /turf/open/floor/plasteel/dark, /area/engine/gravity_generator) "amN" = ( -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, +/obj/effect/turf_decal/stripes/corner, /turf/open/floor/plating, -/area/maintenance/solars/starboard/fore) +/area/crew_quarters/fitness/pool) "amO" = ( /obj/machinery/light/small{ dir = 8 @@ -7158,6 +7176,13 @@ icon_state = "platingdmg2" }, /area/maintenance/port) +"amV" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/line, +/turf/open/floor/plating, +/area/crew_quarters/fitness/pool) "amW" = ( /obj/structure/table/reinforced, /obj/item/folder, @@ -7438,6 +7463,9 @@ /obj/item/paper, /turf/open/floor/plasteel, /area/security/main) +"anz" = ( +/turf/open/floor/plasteel, +/area/crew_quarters/fitness/pool) "anA" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /obj/structure/disposalpipe/segment, @@ -7898,6 +7926,10 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, /area/security/warden) +"aov" = ( +/obj/effect/turf_decal/stripes/line, +/turf/open/floor/plating, +/area/crew_quarters/fitness/pool) "aow" = ( /obj/machinery/door/firedoor, /obj/structure/cable/yellow{ @@ -8616,6 +8648,12 @@ }, /turf/open/floor/plasteel/showroomfloor, /area/security/warden) +"apP" = ( +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/turf/open/floor/plating, +/area/crew_quarters/fitness/pool) "apQ" = ( /obj/structure/reagent_dispensers/peppertank{ pixel_x = 32 @@ -8682,6 +8720,9 @@ /obj/item/assembly/flash/handheld, /turf/open/floor/plasteel, /area/security/main) +"apX" = ( +/turf/open/pool, +/area/crew_quarters/fitness/pool) "apY" = ( /obj/structure/table, /obj/item/folder/red, @@ -8747,6 +8788,12 @@ }, /turf/open/floor/plasteel/dark, /area/crew_quarters/fitness/recreation) +"aqe" = ( +/obj/machinery/pool/filter{ + pixel_y = 16 + }, +/turf/open/pool, +/area/crew_quarters/fitness/pool) "aqf" = ( /obj/structure/closet/lasertag/blue, /obj/effect/turf_decal/tile/neutral{ @@ -9248,6 +9295,12 @@ }, /turf/open/floor/plasteel/showroomfloor, /area/security/warden) +"arh" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/turf/open/floor/plating, +/area/crew_quarters/fitness/pool) "ari" = ( /obj/machinery/holopad, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, @@ -9478,6 +9531,14 @@ }, /turf/open/floor/plasteel, /area/crew_quarters/dorms) +"arF" = ( +/obj/structure/pool/ladder{ + dir = 8; + icon_state = "ladder"; + pixel_x = 4 + }, +/turf/open/pool, +/area/crew_quarters/fitness/pool) "arG" = ( /obj/structure/closet, /obj/item/storage/box/lights/mixed, @@ -10167,6 +10228,13 @@ /obj/structure/chair, /turf/open/floor/plasteel/grimy, /area/security/main) +"asN" = ( +/obj/machinery/light/built{ + icon_state = "tube-empty"; + dir = 8 + }, +/turf/open/floor/plasteel, +/area/crew_quarters/fitness/pool) "asO" = ( /obj/item/paper_bin{ pixel_x = -3; @@ -10466,6 +10534,10 @@ }, /turf/open/floor/plating, /area/maintenance/port/fore) +"atw" = ( +/obj/machinery/pool/drain, +/turf/open/pool, +/area/crew_quarters/fitness/pool) "atx" = ( /obj/structure/cable/yellow{ icon_state = "2-4" @@ -10536,6 +10608,12 @@ }, /turf/open/floor/plating, /area/maintenance/port/fore) +"atC" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/turf/open/floor/plating, +/area/crew_quarters/fitness/pool) "atD" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 @@ -10558,6 +10636,10 @@ }, /turf/open/floor/plating, /area/maintenance/port/fore) +"atF" = ( +/obj/machinery/pool/controller, +/turf/open/floor/plasteel, +/area/crew_quarters/fitness/pool) "atG" = ( /obj/structure/cable/yellow{ icon_state = "4-8" @@ -10601,6 +10683,23 @@ }, /turf/open/floor/plating, /area/maintenance/port/fore) +"atJ" = ( +/obj/structure/sign/poster/ripped{ + pixel_y = 32 + }, +/obj/structure/table, +/obj/structure/cable/yellow{ + icon_state = "0-2" + }, +/obj/item/light/tube, +/obj/machinery/power/apc{ + areastring = "/area/crew_quarters/fitness/pool"; + dir = 4; + name = "Pool APC"; + pixel_x = 26 + }, +/turf/open/floor/plasteel, +/area/crew_quarters/fitness/pool) "atK" = ( /obj/machinery/computer/prisoner/gulag_teleporter_computer{ dir = 1 @@ -11069,6 +11168,24 @@ }, /turf/open/floor/plating, /area/maintenance/port/fore) +"auQ" = ( +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/crew_quarters/fitness/pool) +"auR" = ( +/obj/structure/pool/Lboard, +/turf/open/pool, +/area/crew_quarters/fitness/pool) +"auS" = ( +/obj/structure/pool/Rboard, +/turf/open/floor/plasteel, +/area/crew_quarters/fitness/pool) +"auT" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/crew_quarters/fitness/pool) "auU" = ( /obj/structure/disposalpipe/segment{ dir = 6 @@ -11659,6 +11776,50 @@ }, /turf/open/floor/plating, /area/maintenance/port/fore) +"avU" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/structure/cable/yellow{ + icon_state = "1-4" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/turf/open/floor/plating, +/area/crew_quarters/fitness/pool) +"avV" = ( +/obj/structure/cable/yellow{ + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/machinery/door/airlock/maintenance/abandoned, +/obj/structure/barricade/wooden, +/turf/open/floor/plating, +/area/crew_quarters/fitness/pool) +"avW" = ( +/obj/structure/cable/yellow{ + icon_state = "1-2" + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/structure/cable/yellow{ + icon_state = "1-8" + }, +/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ + dir = 4 + }, +/turf/open/floor/plating, +/area/maintenance/port/aft) +"avX" = ( +/obj/structure/chair{ + dir = 8 + }, +/turf/open/floor/plasteel, +/area/crew_quarters/fitness/pool) "avY" = ( /obj/machinery/door/airlock/external{ name = "Labor Camp Shuttle Airlock"; @@ -11932,6 +12093,11 @@ }, /turf/open/floor/plasteel/dark, /area/security/brig) +"awA" = ( +/obj/structure/closet/athletic_mixed, +/obj/item/toy/poolnoodle/blue, +/turf/open/floor/plating, +/area/crew_quarters/fitness/pool) "awB" = ( /obj/structure/reagent_dispensers/watertank, /obj/item/storage/box/lights/mixed, @@ -12053,6 +12219,18 @@ /obj/effect/spawner/lootdrop/maintenance, /turf/open/floor/plating, /area/maintenance/port/fore) +"awN" = ( +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/turf/open/floor/plating, +/area/crew_quarters/fitness/pool) +"awO" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/turf/open/floor/plating, +/area/crew_quarters/fitness/pool) "awP" = ( /obj/item/clothing/gloves/color/rainbow, /obj/item/clothing/shoes/sneakers/rainbow, @@ -12105,6 +12283,25 @@ "awW" = ( /turf/closed/wall/r_wall, /area/security/nuke_storage) +"awX" = ( +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/turf/open/floor/plating, +/area/crew_quarters/fitness/pool) +"awY" = ( +/obj/machinery/door/airlock/external{ + name = "Solar Maintenance"; + req_access_txt = "10; 13" + }, +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 8 + }, +/turf/open/floor/plating, +/area/maintenance/solars/starboard/aft) "awZ" = ( /obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ dir = 8 @@ -54179,21 +54376,6 @@ /obj/structure/lattice/catwalk, /turf/open/space, /area/solar/port/aft) -"cek" = ( -/obj/machinery/door/airlock/external{ - req_access_txt = "13" - }, -/turf/open/floor/plating, -/area/maintenance/port/aft) -"cel" = ( -/obj/structure/sign/warning/vacuum/external{ - pixel_y = 32 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/turf/open/floor/plating, -/area/maintenance/port/aft) "cem" = ( /obj/structure/cable/yellow{ icon_state = "1-2" @@ -54885,16 +55067,6 @@ }, /turf/open/floor/plating, /area/maintenance/port/fore) -"cfB" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/obj/structure/cable/yellow{ - icon_state = "1-2" - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/turf/open/floor/plating, -/area/maintenance/port/aft) "cfC" = ( /obj/item/trash/pistachios, /obj/structure/closet, @@ -55509,13 +55681,6 @@ /obj/structure/reagent_dispensers/cooking_oil, /turf/open/floor/plasteel/showroomfloor, /area/crew_quarters/kitchen) -"cgG" = ( -/obj/structure/closet/emcloset/anchored, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/turf/open/floor/plating, -/area/maintenance/port/aft) "cgH" = ( /obj/structure/cable/yellow{ icon_state = "4-8" @@ -81287,19 +81452,6 @@ }, /turf/open/floor/plating, /area/maintenance/starboard/aft) -"gsT" = ( -/obj/machinery/door/airlock/external{ - name = "Solar Maintenance"; - req_access_txt = "10; 13" - }, -/obj/structure/cable{ - icon_state = "4-8" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 8 - }, -/turf/open/floor/plating, -/area/solar/starboard/aft) "gEk" = ( /obj/structure/cable/yellow{ icon_state = "2-8" @@ -97098,15 +97250,15 @@ bTp bUP bVS bXv -bYC -bYC -bYC -bYC -aaa -aaf -aaa -aaa -aaf +bTn +bTn +bTn +bTn +amG +auQ +auQ +amG +amG aaf aaf aaa @@ -97356,16 +97508,16 @@ bTn bTn bTn bTn +amN +anz +arh +asN +anz +arh +awN +amG aaa -aaf -aaa -aaa -aaf -aaf -aaf -aaf -aaa -aaa +lMJ aaa aaa aaa @@ -97609,20 +97761,20 @@ bzx alC mEe alK -aob -aob dix -alK -aaf -aaf -aaf -aaf -aaf -aaa -aaa -aaf -aaa +aob +amB +amF +amV +apX +apX +apX +apX +apX +awO +auQ aaa +lMJ aaa aaa aaa @@ -97869,17 +98021,17 @@ bTr bUQ bVT aob -alK -aaa -aaf -aaa -aaa -aaa -aaa -aaa -aaf -aaa +amG +anz +aqe +apX +atw +apX +apX +anz +auQ aaa +lMJ aaa aaa aaa @@ -98126,15 +98278,15 @@ alK bJs bVU bXx -alK -aaa -aaf -aaf -ack -aaf -aaf -aaf -aaf +amG +aov +apX +arF +apX +auR +apX +anz +auQ aaf aaf aaf @@ -98383,15 +98535,15 @@ alK bUR alK alK -alK -aaa -aaf -aaa -ack -aaf -aaa -aaa -aaa +amG +apP +anz +anz +atC +auS +atC +awX +amG aaa aaa aaa @@ -98640,15 +98792,15 @@ alK alK alK bXy -dux -dux -dux -dux -cek -dux -dux -dux -dux +amG +amG +amG +amG +atF +auT +avX +amG +amG ckN ckN ckN @@ -98900,11 +99052,11 @@ bXz bYE bYE cbp -dux -cel -bUU -cgG -dux +amG +atJ +avU +awA +amG cjp ckO cmg @@ -99157,11 +99309,11 @@ dux dux dux cbq -dux -dux -cek -dux -dux +amG +amG +avV +amG +amG cjq ckP ckS @@ -99416,7 +99568,7 @@ dux cbr bYE cem -cfB +avW cbp dux diE @@ -119752,7 +119904,7 @@ cJf dvY aaa dbN -gsT +awY dbN aaa aaa @@ -124021,7 +124173,7 @@ aaf aaa aaf dnh -aci +amH dnh ape dnS @@ -126848,7 +127000,7 @@ aaa aaf aaa aaa -akd +ake alv apm apm @@ -127103,7 +127255,7 @@ aaf aaa aaa aaf -akd +alv alv alv alv @@ -127359,10 +127511,10 @@ abR abR abR abR -abR -ake +aci +afQ +akd alw -amN sGh apo aqz @@ -127617,7 +127769,7 @@ aaf aaa aaa aaf -akd +alv alv alv alv @@ -127876,7 +128028,7 @@ aaa aaf aaa aaa -akd +ake alv apm apm diff --git a/code/__DEFINES/citadel_defines.dm b/code/__DEFINES/citadel_defines.dm index 585e8da7d7..6aca5cd998 100644 --- a/code/__DEFINES/citadel_defines.dm +++ b/code/__DEFINES/citadel_defines.dm @@ -99,7 +99,8 @@ #define NO_ASS_SLAP (1<<10) #define BIMBOFICATION (1<<11) #define NO_AUTO_WAG (1<<12) - +#define GENITAL_EXAMINE (1<<13) +#define VORE_EXAMINE (1<<14) #define TOGGLES_CITADEL 0 //belly sound pref things diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 3d15412efe..2273d34c82 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -2,19 +2,6 @@ #define COLOR_INPUT_DISABLED "#F0F0F0" #define COLOR_INPUT_ENABLED "#D3B5B5" - -#define COLOR_DARKMODE_INFO_BUTTONS_BG "#40628A" -#define COLOR_DARKMODE_ISSUE_BUTTON_BG "#A92C2C" -#define COLOR_DARKMODE_BACKGROUND "#272727" -#define COLOR_DARKMODE_DARKBACKGROUND "#242424" -#define COLOR_DARKMODE_TEXT "#E0E0E0" - -#define COLOR_WHITEMODE_INFO_BUTTONS_BG "#90B3DD" -#define COLOR_WHITEMODE_ISSUE_BUTTON_BG "#EF7F7F" -#define COLOR_WHITEMODE_BACKGROUND "#F0F0F0" -#define COLOR_WHITEMODE_DARKBACKGROUND "#E6E6E6" -#define COLOR_WHITEMODE_TEXT "#000000" - #define COLOR_FLOORTILE_GRAY "#8D8B8B" #define COLOR_ALMOST_BLACK "#333333" #define COLOR_BLACK "#000000" @@ -64,4 +51,4 @@ #define COLOR_ASSEMBLY_LBLUE "#5D99BE" #define COLOR_ASSEMBLY_BLUE "#38559E" #define COLOR_ASSEMBLY_PURPLE "#6F6192" -#define COLOR_ASSEMBLY_PINK "#ff4adc" \ No newline at end of file +#define COLOR_ASSEMBLY_PINK "#ff4adc" diff --git a/code/__DEFINES/configuration.dm b/code/__DEFINES/configuration.dm index a4bf69b2ad..e9881677ba 100644 --- a/code/__DEFINES/configuration.dm +++ b/code/__DEFINES/configuration.dm @@ -1,6 +1,7 @@ //config files #define CONFIG_GET(X) global.config.Get(/datum/config_entry/##X) #define CONFIG_SET(X, Y) global.config.Set(/datum/config_entry/##X, ##Y) +/// Gets the datum of the object, for when editing a const define. #define CONFIG_GET_ENTRY(X) global.config.GetEntryDatum(/datum/config_entry/##X) #define CONFIG_MAPS_FILE "maps.txt" diff --git a/code/__DEFINES/dye_keys.dm b/code/__DEFINES/dye_keys.dm index 133f9c47d3..a01dcacc18 100644 --- a/code/__DEFINES/dye_keys.dm +++ b/code/__DEFINES/dye_keys.dm @@ -4,6 +4,7 @@ #define DYE_REGISTRY_SNEAKERS "sneakers" #define DYE_REGISTRY_FANNYPACK "fannypack" #define DYE_REGISTRY_BEDSHEET "bedsheet" +#define DYE_LAWYER_SPECIAL "lawyer_special" #define DYE_RED "red" #define DYE_ORANGE "orange" @@ -16,6 +17,7 @@ #define DYE_RAINBOW "rainbow" #define DYE_MIME "mime" #define DYE_COSMIC "cosmic" +#define DYE_SYNDICATE "syndicate" #define DYE_QM "qm" #define DYE_LAW "law" #define DYE_CAPTAIN "captain" @@ -26,3 +28,5 @@ #define DYE_CMO "cmo" #define DYE_REDCOAT "redcoat" #define DYE_CLOWN "clown" +#define DYE_CHAP "chap" +#define DYE_CENTCOM "centcom" diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index 0e8e34d20e..df5b7d9f11 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -41,11 +41,13 @@ #define MC_HDD "HDD" #define MC_SDD "SDD" #define MC_CARD "CARD" +#define MC_CARD2 "CARD2" #define MC_NET "NET" #define MC_PRINT "PRINT" #define MC_CELL "CELL" #define MC_CHARGE "CHARGE" #define MC_AI "AI" +#define MC_SENSORS "SENSORS" //NTNet stuff, for modular computers // NTNet module-configuration values. Do not change these. If you need to add another use larger number (5..6..7 etc) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 927b661898..0704947dda 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -116,6 +116,7 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s #define CRAYON_FONT "Comic Sans MS" #define PRINTER_FONT "Times New Roman" #define SIGNFONT "Times New Roman" +#define CHARCOAL_FONT "Candara" #define RESIZE_DEFAULT_SIZE 1 @@ -318,9 +319,9 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S #define SHELTER_DEPLOY_ANCHORED_OBJECTS "anchored objects" //debug printing macros -#define debug_world(msg) if (GLOB.Debug2) to_chat(world, "DEBUG: [msg]") -#define debug_usr(msg) if (GLOB.Debug2&&usr) to_chat(usr, "DEBUG: [msg]") -#define debug_admins(msg) if (GLOB.Debug2) to_chat(GLOB.admins, "DEBUG: [msg]") +#define debug_world(msg) if (GLOB.Debug2) to_chat(world, "DEBUG: [msg]") +#define debug_usr(msg) if (GLOB.Debug2&&usr) to_chat(usr, "DEBUG: [msg]") +#define debug_admins(msg) if (GLOB.Debug2) to_chat(GLOB.admins, "DEBUG: [msg]") #define debug_world_log(msg) if (GLOB.Debug2) log_world("DEBUG: [msg]") #define INCREMENT_TALLY(L, stat) if(L[stat]){L[stat]++}else{L[stat] = 1} diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 6ddd269b4b..9c39446a67 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -64,7 +64,6 @@ #define DEFAULT_BODYPART_ICON 'icons/mob/human_parts.dmi' #define DEFAULT_BODYPART_ICON_ORGANIC 'icons/mob/human_parts_greyscale.dmi' #define DEFAULT_BODYPART_ICON_ROBOTIC 'icons/mob/augmentation/augments.dmi' -#define DEFAULT_BODYPART_ICON_CITADEL 'modular_citadel/icons/mob/mutant_bodyparts.dmi' #define MONKEY_BODYPART "monkey" #define ALIEN_BODYPART "alien" @@ -333,4 +332,4 @@ /// If you examine the same atom twice in this timeframe, we call examine_more() instead of examine() #define EXAMINE_MORE_TIME 1 SECONDS -#define SILENCE_RANGED_MESSAGE (1<<0) +#define SILENCE_RANGED_MESSAGE (1<<0) diff --git a/code/__DEFINES/rockpaperscissors.dm b/code/__DEFINES/rockpaperscissors.dm new file mode 100644 index 0000000000..77ba81938d --- /dev/null +++ b/code/__DEFINES/rockpaperscissors.dm @@ -0,0 +1,7 @@ +#define ROCKPAPERSCISSORS_RANGE 3 +#define ROCKPAPERSCISSORS_TIME_LIMIT 20 SECONDS + +#define ROCKPAPERSCISSORS_LOSE "lose" +#define ROCKPAPERSCISSORS_WIN "win" +#define ROCKPAPERSCISSORS_TIE "tie" +#define ROCKPAPERSCISSORS_NOT_DECIDED "not_decided" \ No newline at end of file diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index bfff1dc629..d6db35e68d 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -30,6 +30,8 @@ #define STATUS_EFFECT_FLESHMEND /datum/status_effect/fleshmend //Very fast healing; suppressed by fire, and heals less fire damage +#define STATUS_EFFECT_PANACEA /datum/status_effect/panacea //Anatomic panacea that directly heals, rather than injecting a small chemical cocktail + #define STATUS_EFFECT_EXERCISED /datum/status_effect/exercised //Prevents heart disease #define STATUS_EFFECT_HIPPOCRATIC_OATH /datum/status_effect/hippocraticOath //Gives you an aura of healing as well as regrowing the Rod of Asclepius if lost diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 81b5c5620f..37ca1c66dd 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -93,7 +93,6 @@ // If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child) #define FIRE_PRIORITY_VORE 5 -#define FIRE_PRIORITY_PING 10 #define FIRE_PRIORITY_IDLE_NPC 10 #define FIRE_PRIORITY_SERVER_MAINT 10 #define FIRE_PRIORITY_RESEARCH 10 diff --git a/code/__DEFINES/tgui.dm b/code/__DEFINES/tgui.dm index f5adeadade..467058f27a 100644 --- a/code/__DEFINES/tgui.dm +++ b/code/__DEFINES/tgui.dm @@ -26,3 +26,10 @@ #define TGUI_WINDOW_ID(index) "tgui-window-[index]" /// Get a pool index of the provided window id #define TGUI_WINDOW_INDEX(window_id) text2num(copytext(window_id, 13)) + +/// Creates a message packet for sending via output() +#define TGUI_CREATE_MESSAGE(type, payload) ( \ + url_encode(json_encode(list( \ + "type" = type, \ + "payload" = payload, \ + )))) \ No newline at end of file diff --git a/code/__HELPERS/_cit_helpers.dm b/code/__HELPERS/_cit_helpers.dm index 5e57dd5328..a5ffda36f9 100644 --- a/code/__HELPERS/_cit_helpers.dm +++ b/code/__HELPERS/_cit_helpers.dm @@ -17,7 +17,7 @@ var/matrix/mtrx=new() return mtrx.Scale(0.65) -proc/get_racelist(var/mob/user)//This proc returns a list of species that 'user' has available to them. It searches the list of ckeys attached to the 'whitelist' var for a species and also checks if they're an admin. +/proc/get_racelist(mob/user)//This proc returns a list of species that 'user' has available to them. It searches the list of ckeys attached to the 'whitelist' var for a species and also checks if they're an admin. for(var/spath in subtypesof(/datum/species)) var/datum/species/S = new spath() var/list/wlist = S.whitelist diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 3155fe2548..11332863f5 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -1103,15 +1103,26 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0 alpha += 25 obj_flags &= ~FROZEN +/// Save file used in icon2base64. Used for converting icons to base64. +GLOBAL_DATUM_INIT(dummySave, /savefile, new("tmp/dummySave.sav")) //Cache of icons for the browser output -//Converts an icon to base64. Operates by putting the icon in the iconCache savefile, -// exporting it as text, and then parsing the base64 from that. -// (This relies on byond automatically storing icons in savefiles as base64) -/proc/icon2base64(icon/icon, iconKey = "misc") + +/// Generate a filename for this asset +/// The same asset will always lead to the same asset name +/// (Generated names do not include file extention.) +/proc/generate_asset_name(file) + return "asset.[md5(fcopy_rsc(file))]" + +/** + * Converts an icon to base64. Operates by putting the icon in the iconCache savefile, + * exporting it as text, and then parsing the base64 from that. + * (This relies on byond automatically storing icons in savefiles as base64) + */ +/proc/icon2base64(icon/icon) if (!isicon(icon)) return FALSE - WRITE_FILE(GLOB.iconCache[iconKey], icon) - var/iconData = GLOB.iconCache.ExportText(iconKey) + WRITE_FILE(GLOB.dummySave["dummy"], icon) + var/iconData = GLOB.dummySave.ExportText("dummy") var/list/partial = splittext(iconData, "{") return replacetext(copytext_char(partial[2], 3, -5), "\n", "") @@ -1137,10 +1148,10 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0 if (isfile(thing)) //special snowflake var/name = sanitize_filename("[generate_asset_name(thing)].png") if(!SSassets.cache[name]) - register_asset(name, thing) + SSassets.transport.register_asset(name, thing) for (var/thing2 in targets) - send_asset(thing2, key) - return "" + SSassets.transport.send_assets(thing2, name) + return "" var/atom/A = thing if (isnull(dir)) dir = A.dir @@ -1162,11 +1173,11 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0 key = "[generate_asset_name(I)].png" if(!SSassets.cache[key]) - register_asset(key, I) + SSassets.transport.register_asset(key, I) for (var/thing2 in targets) - send_asset(thing2, key) + SSassets.transport.send_assets(thing2, key) - return "" + return "" /proc/icon2base64html(thing) if (!thing) diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 045ca2c519..6dcb5f2534 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -358,7 +358,7 @@ roundend_report.stylesheets = list() roundend_report.add_stylesheet("roundend", 'html/browser/roundend.css') roundend_report.add_stylesheet("font-awesome", 'html/font-awesome/css/all.min.css') - roundend_report.open(0) + roundend_report.open(FALSE) /datum/controller/subsystem/ticker/proc/personal_report(client/C, popcount) var/list/parts = list() diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index 7a18d2ce01..c4d105a3a6 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -620,6 +620,12 @@ else //regex everything else (works for /proc too) return lowertext(replacetext("[the_type]", "[type2parent(the_type)]/", "")) + +/// Return html to load a url. +/// for use inside of browse() calls to html assets that might be loaded on a cdn. +/proc/url2htmlloader(url) + return {""} + /proc/strtohex(str) if(!istext(str)||!str) return diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index 8225725ffd..71c2b2e13b 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -278,3 +278,8 @@ GLOBAL_LIST_INIT(all_mutant_parts, list("tail_lizard" = "Tail", "mam_tail" = "Ta GLOBAL_LIST_INIT(unlocked_mutant_parts, list("horns", "insect_fluff")) //parts in either of the above two lists that require a second option that allows them to be coloured GLOBAL_LIST_INIT(colored_mutant_parts, list("insect_wings" = "wings_color", "deco_wings" = "wings_color", "horns" = "horns_color")) + +//species ids that have greyscale sprites +GLOBAL_LIST_INIT(greyscale_limb_types, list("human","moth","lizard","pod","plant","jelly","slime","golem","lum","stargazer","mush","ethereal","snail","c_golem","b_golem","mammal","xeno","ipc","insect","synthliz","avian","aquatic")) + +//species ids that need snowflake coloring applied diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 4de7c88bf7..706a5ed955 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -13,6 +13,7 @@ GLOBAL_LIST_EMPTY(deliverybeacontags) //list of all tags associated with d GLOBAL_LIST_EMPTY(nuke_list) GLOBAL_LIST_EMPTY(alarmdisplay) //list of all machines or programs that can display station alerts GLOBAL_LIST_EMPTY(singularities) //list of all singularities on the station (actually technically all engines) +GLOBAL_LIST_EMPTY(grounding_rods) //list of all grounding rods on the station GLOBAL_LIST(chemical_reactions_list) //list of all /datum/chemical_reaction datums. Used during chemical reactions GLOBAL_LIST(chemical_reagents_list) //list of all /datum/reagent datums indexed by reagent id. Used by chemistry stuff @@ -41,3 +42,6 @@ GLOBAL_LIST_EMPTY(ai_status_displays) GLOBAL_LIST_EMPTY(mob_spawners) // All mob_spawn objects GLOBAL_LIST_EMPTY(alert_consoles) // Station alert consoles, /obj/machinery/computer/station_alert + +//list of everyone playing rock paper scissors +GLOBAL_LIST_EMPTY(rockpaperscissors_players) diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm index 1c6e96f9e4..81f01219b8 100644 --- a/code/controllers/configuration/configuration.dm +++ b/code/controllers/configuration/configuration.dm @@ -14,13 +14,15 @@ var/list/modes // allowed modes var/list/gamemode_cache var/list/votable_modes // votable modes - // var/list/ic_filter_regex var/list/storyteller_cache var/list/mode_names var/list/mode_reports var/list/mode_false_report_weight var/motd + // var/policy + + // var/static/regex/ic_filter_regex /datum/controller/configuration/proc/admin_reload() if(IsAdminAdvancedProcCall()) @@ -50,6 +52,11 @@ break loadmaplist(CONFIG_MAPS_FILE) LoadMOTD() + // LoadPolicy() + // LoadChatFilter() + + if (Master) + Master.OnConfigLoad() /datum/controller/configuration/proc/full_wipe() if(IsAdminAdvancedProcCall()) @@ -135,7 +142,7 @@ if(entry == "$include") if(!value) - log_config("LINE [linenumber]: Invalid $include directive: [value]") + log_config("LINE [linenumber]: Warning: Invalid $include directive: [value]") else LoadEntries(value, stack) ++. @@ -143,7 +150,7 @@ var/datum/config_entry/E = _entries[entry] if(!E) - log_config("LINE [linenumber]: Unknown setting: '[entry]'") + log_config("LINE [linenumber]: Unknown setting in configuration: '[entry]'") continue if(lockthis) @@ -153,9 +160,9 @@ var/datum/config_entry/new_ver = entries_by_type[E.deprecated_by] var/new_value = E.DeprecationUpdate(value) var/good_update = istext(new_value) - log_config("LINE [linenumber]: [entry] is deprecated and will be removed soon. Migrate to [new_ver.name]![good_update ? " Suggested new value is: [new_value]" : ""]") + log_config("LINE [linenumber]: Entry [entry] is deprecated and will be removed soon. Migrate to [new_ver.name]![good_update ? " Suggested new value is: [new_value]" : ""]") if(!warned_deprecated_configs) - addtimer(CALLBACK(GLOBAL_PROC, /proc/message_admins, "This server is using deprecated configuration settings. Please check the logs and update accordingly."), 0) + DelayedMessageAdmins("This server is using deprecated configuration settings. Please check the logs and update accordingly.") warned_deprecated_configs = TRUE if(good_update) value = new_value @@ -163,7 +170,7 @@ else warning("[new_ver.type] is deprecated but gave no proper return for DeprecationUpdate()") - var/validated = E.ValidateAndSet(value, TRUE) + var/validated = E.ValidateAndSet(value) if(!validated) log_config("LINE [linenumber]: Failed to validate setting \"[value]\" for [entry]") else @@ -195,13 +202,7 @@ statclick = new/obj/effect/statclick/debug(null, "Edit", src) stat("[name]:", statclick) -/datum/controller/configuration/proc/Get(entry_type) - var/datum/config_entry/E = GetEntryDatum(entry_type) - if((E.protection & CONFIG_ENTRY_HIDDEN) && IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Get" && GLOB.LastAdminCalledTargetRef == "[REF(src)]") - log_admin_private("Config access of [entry_type] attempted by [key_name(usr)]") - return - return E.config_entry_value - +/// Your typical GET but returns a config. /datum/controller/configuration/proc/GetEntryDatum(entry_type) var/datum/config_entry/E = entry_type var/entry_is_abstract = initial(E.abstract_type) == entry_type @@ -210,8 +211,24 @@ E = entries_by_type[entry_type] if(!E) CRASH("Missing config entry for [entry_type]!") + if((E.protection & CONFIG_ENTRY_HIDDEN) && IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Get" && GLOB.LastAdminCalledTargetRef == "[REF(src)]") + log_admin_private("Config access of [entry_type] attempted by [key_name(usr)]") + return return E +/datum/controller/configuration/proc/Get(entry_type) + var/datum/config_entry/E = entry_type + var/entry_is_abstract = initial(E.abstract_type) == entry_type + if(entry_is_abstract) + CRASH("Tried to retrieve an abstract config_entry: [entry_type]") + E = entries_by_type[entry_type] + if(!E) + CRASH("Missing config entry for [entry_type]!") + if((E.protection & CONFIG_ENTRY_HIDDEN) && IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Get" && GLOB.LastAdminCalledTargetRef == "[REF(src)]") + log_admin_private("Config access of [entry_type] attempted by [key_name(usr)]") + return + return E.config_entry_value + /datum/controller/configuration/proc/Set(entry_type, new_val) var/datum/config_entry/E = entry_type var/entry_is_abstract = initial(E.abstract_type) == entry_type @@ -236,7 +253,6 @@ for(var/T in gamemode_cache) // I wish I didn't have to instance the game modes in order to look up // their information, but it is the only way (at least that I know of). - // for future reference: just use initial() lol var/datum/game_mode/M = new T() if(M.config_tag) @@ -258,7 +274,37 @@ var/tm_info = GLOB.revdata.GetTestMergeInfo() if(motd || tm_info) motd = motd ? "[motd]
[tm_info]" : tm_info +/* +Policy file should be a json file with a single object. +Value is raw html. +Possible keywords : +Job titles / Assigned roles (ghost spawners for example) : Assistant , Captain , Ash Walker +Mob types : /mob/living/simple_animal/hostile/carp +Antagonist types : /datum/antagonist/highlander +Species types : /datum/species/lizard +special keywords defined in _DEFINES/admin.dm + +Example config: +{ + "Assistant" : "Don't kill everyone", + "/datum/antagonist/highlander" : "Kill everyone", + "Ash Walker" : "Kill all spacemans" +} + +*/ +/* +/datum/controller/configuration/proc/LoadPolicy() + policy = list() + var/rawpolicy = file2text("[directory]/policy.json") + if(rawpolicy) + var/parsed = safe_json_decode(rawpolicy) + if(!parsed) + log_config("JSON parsing failure for policy.json") + DelayedMessageAdmins("JSON parsing failure for policy.json") + else + policy = parsed +*/ /datum/controller/configuration/proc/loadmaplist(filename) log_config("Loading config file [filename]...") filename = "[directory]/[filename]" @@ -305,6 +351,8 @@ currentmap.voteweight = text2num(data) if ("default","defaultmap") defaultmap = currentmap + //if ("votable") + // currentmap.votable = TRUE if ("endmap") LAZYINITLIST(maplist) maplist[currentmap.map_name] = currentmap @@ -326,6 +374,7 @@ return new T return new /datum/game_mode/extended() +/// For dynamic. /datum/controller/configuration/proc/pick_storyteller(storyteller_name) for(var/T in storyteller_cache) var/datum/dynamic_storyteller/S = T @@ -334,6 +383,32 @@ return T return /datum/dynamic_storyteller/classic +/// Same with this +/datum/controller/configuration/proc/get_runnable_storytellers() + var/list/datum/dynamic_storyteller/runnable_storytellers = new + var/list/probabilities = Get(/datum/config_entry/keyed_list/storyteller_weight) + var/list/repeated_mode_adjust = Get(/datum/config_entry/number_list/repeated_mode_adjust) + var/list/min_player_counts = Get(/datum/config_entry/keyed_list/storyteller_min_players) + for(var/T in storyteller_cache) + var/datum/dynamic_storyteller/S = T + var/config_tag = initial(S.config_tag) + var/probability = (config_tag in probabilities) ? probabilities[config_tag] : initial(S.weight) + var/min_players = (config_tag in min_player_counts) ? min_player_counts[config_tag] : initial(S.min_players) + if(probability <= 0) + continue + if(length(GLOB.player_list) < min_players) + continue + if(SSpersistence.saved_storytellers.len == repeated_mode_adjust.len) + var/name = initial(S.name) + var/recent_round = min(SSpersistence.saved_storytellers.Find(name),3) + var/adjustment = 0 + while(recent_round) + adjustment += repeated_mode_adjust[recent_round] + recent_round = SSpersistence.saved_modes.Find(name,recent_round+1,0) + probability *= ((100-adjustment)/100) + runnable_storytellers[S] = probability + return runnable_storytellers + /datum/controller/configuration/proc/get_runnable_modes() var/list/datum/game_mode/runnable_modes = new var/list/probabilities = Get(/datum/config_entry/keyed_list/probability) @@ -367,32 +442,6 @@ runnable_modes[M] = final_weight return runnable_modes -/datum/controller/configuration/proc/get_runnable_storytellers() - var/list/datum/dynamic_storyteller/runnable_storytellers = new - var/list/probabilities = Get(/datum/config_entry/keyed_list/storyteller_weight) - var/list/repeated_mode_adjust = Get(/datum/config_entry/number_list/repeated_mode_adjust) - var/list/min_player_counts = Get(/datum/config_entry/keyed_list/storyteller_min_players) - for(var/T in storyteller_cache) - var/datum/dynamic_storyteller/S = T - var/config_tag = initial(S.config_tag) - var/probability = (config_tag in probabilities) ? probabilities[config_tag] : initial(S.weight) - var/min_players = (config_tag in min_player_counts) ? min_player_counts[config_tag] : initial(S.min_players) - if(probability <= 0) - continue - if(length(GLOB.player_list) < min_players) - continue - if(SSpersistence.saved_storytellers.len == repeated_mode_adjust.len) - var/name = initial(S.name) - var/recent_round = min(SSpersistence.saved_storytellers.Find(name),3) - var/adjustment = 0 - while(recent_round) - adjustment += repeated_mode_adjust[recent_round] - recent_round = SSpersistence.saved_modes.Find(name,recent_round+1,0) - probability *= ((100-adjustment)/100) - runnable_storytellers[S] = probability - return runnable_storytellers - - /datum/controller/configuration/proc/get_runnable_midround_modes(crew) var/list/datum/game_mode/runnable_modes = new var/list/probabilities = Get(/datum/config_entry/keyed_list/probability) @@ -418,7 +467,6 @@ /* /datum/controller/configuration/proc/LoadChatFilter() var/list/in_character_filter = list() - if(!fexists("[directory]/in_character_filter.txt")) return log_config("Loading config file in_character_filter.txt...") @@ -428,8 +476,8 @@ if(findtextEx(line,"#",1,2)) continue in_character_filter += REGEX_QUOTE(line) - ic_filter_regex = in_character_filter.len ? regex("\\b([jointext(in_character_filter, "|")])\\b", "i") : null - - syncChatRegexes() */ +//Message admins when you can. +/datum/controller/configuration/proc/DelayedMessageAdmins(text) + addtimer(CALLBACK(GLOBAL_PROC, /proc/message_admins, text), 0) diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 2d5e69f149..273ac7fd3e 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -368,6 +368,10 @@ /datum/config_entry/flag/allow_map_voting +/datum/config_entry/number/client_warn_version + config_entry_value = null + min_val = 500 + /datum/config_entry/number/client_warn_version config_entry_value = null min_val = 500 @@ -384,6 +388,10 @@ /datum/config_entry/string/client_error_message config_entry_value = "Your version of byond is too old, may have issues, and is blocked from accessing this server." +/datum/config_entry/number/client_error_build + config_entry_value = null + min_val = 0 + /datum/config_entry/number/minute_topic_limit config_entry_value = null min_val = 0 diff --git a/code/controllers/configuration/entries/resources.dm b/code/controllers/configuration/entries/resources.dm new file mode 100644 index 0000000000..c839ccc078 --- /dev/null +++ b/code/controllers/configuration/entries/resources.dm @@ -0,0 +1,30 @@ +/datum/config_entry/keyed_list/external_rsc_urls + key_mode = KEY_MODE_TEXT + value_mode = VALUE_MODE_FLAG + +/datum/config_entry/flag/asset_simple_preload + +/datum/config_entry/string/asset_transport +/datum/config_entry/string/asset_transport/ValidateAndSet(str_val) + return (lowertext(str_val) in list("simple", "webroot")) && ..(lowertext(str_val)) + +/datum/config_entry/string/asset_cdn_webroot + protection = CONFIG_ENTRY_LOCKED + +/datum/config_entry/string/asset_cdn_webroot/ValidateAndSet(str_var) + if (!str_var || trim(str_var) == "") + return FALSE + if (str_var && str_var[length(str_var)] != "/") + str_var += "/" + return ..(str_var) + +/datum/config_entry/string/asset_cdn_url + protection = CONFIG_ENTRY_LOCKED + default = null + +/datum/config_entry/string/asset_cdn_url/ValidateAndSet(str_var) + if (!str_var || trim(str_var) == "") + return FALSE + if (str_var && str_var[length(str_var)] != "/") + str_var += "/" + return ..(str_var) diff --git a/code/controllers/controller.dm b/code/controllers/controller.dm index 06547d120d..c9d5f1e565 100644 --- a/code/controllers/controller.dm +++ b/code/controllers/controller.dm @@ -16,4 +16,4 @@ /datum/controller/proc/Recover() -/datum/controller/proc/stat_entry() \ No newline at end of file +/datum/controller/proc/stat_entry() diff --git a/code/controllers/master.dm b/code/controllers/master.dm index cdbea1de85..8a381558a9 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -76,7 +76,11 @@ GLOBAL_REAL(Master, /datum/controller/master) = new // Highlander-style: there can only be one! Kill off the old and replace it with the new. if(!random_seed) - random_seed = (TEST_RUN_PARAMETER in world.params) ? 29051994 : rand(1, 1e9) + #ifdef UNIT_TESTS + random_seed = 29051994 + #else + random_seed = rand(1, 1e9) + #endif rand_seed(random_seed) var/list/_subsystems = list() @@ -184,9 +188,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if(delay) sleep(delay) - if(tgs_prime) - world.TgsInitializationComplete() - if(init_sss) init_subtypes(/datum/controller/subsystem, subsystems) @@ -219,6 +220,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new world.fps = CONFIG_GET(number/fps) var/initialized_tod = REALTIMEOFDAY + if(tgs_prime) + world.TgsInitializationComplete() + if(sleep_offline_after_initializations) world.sleep_offline = TRUE sleep(1) @@ -643,3 +647,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new processing = CONFIG_GET(number/mc_tick_rate/base_mc_tick_rate) else if (client_count > CONFIG_GET(number/mc_tick_rate/high_pop_mc_mode_amount)) processing = CONFIG_GET(number/mc_tick_rate/high_pop_mc_tick_rate) + +/datum/controller/master/proc/OnConfigLoad() + for (var/thing in subsystems) + var/datum/controller/subsystem/SS = thing + SS.OnConfigLoad() diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 4ee3a01956..ce582f52f9 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -1,39 +1,91 @@ +/** + * # Subsystem base class + * + * Defines a subsystem to be managed by the [Master Controller][/datum/controller/master] + * + * Simply define a child of this subsystem, using the [SUBSYSTEM_DEF] macro, and the MC will handle registration. + * Changing the name is required +**/ /datum/controller/subsystem // Metadata; you should define these. - name = "fire coderbus" //name of the subsystem - var/init_order = INIT_ORDER_DEFAULT //order of initialization. Higher numbers are initialized first, lower numbers later. Use defines in __DEFINES/subsystems.dm for easy understanding of order. - var/wait = 20 //time to wait (in deciseconds) between each call to fire(). Must be a positive integer. - var/priority = FIRE_PRIORITY_DEFAULT //When mutiple subsystems need to run in the same tick, higher priority subsystems will run first and be given a higher share of the tick before MC_TICK_CHECK triggers a sleep - var/flags = 0 //see MC.dm in __DEFINES Most flags must be set on world start to take full effect. (You can also restart the mc to force them to process again) + /// Name of the subsystem - you must change this + name = "fire coderbus" - var/initialized = FALSE //set to TRUE after it has been initialized, will obviously never be set if the subsystem doesn't initialize + /// Order of initialization. Higher numbers are initialized first, lower numbers later. Use or create defines such as [INIT_ORDER_DEFAULT] so we can see the order in one file. + var/init_order = INIT_ORDER_DEFAULT - //set to 0 to prevent fire() calls, mostly for admin use or subsystems that may be resumed later - // use the SS_NO_FIRE flag instead for systems that never fire to keep it from even being added to the list + /// Time to wait (in deciseconds) between each call to fire(). Must be a positive integer. + var/wait = 20 + + /// Priority Weight: When mutiple subsystems need to run in the same tick, higher priority subsystems will be given a higher share of the tick before MC_TICK_CHECK triggers a sleep, higher priority subsystems also run before lower priority subsystems + var/priority = FIRE_PRIORITY_DEFAULT + + /// [Subsystem Flags][SS_NO_INIT] to control binary behavior. Flags must be set at compile time or before preinit finishes to take full effect. (You can also restart the mc to force them to process again) + var/flags = 0 + + /// This var is set to TRUE after the subsystem has been initialized. + var/initialized = FALSE + + /// Set to 0 to prevent fire() calls, mostly for admin use or subsystems that may be resumed later + /// use the [SS_NO_FIRE] flag instead for systems that never fire to keep it from even being added to list that is checked every tick var/can_fire = TRUE - // Bookkeeping variables; probably shouldn't mess with these. - var/last_fire = 0 //last world.time we called fire() - var/next_fire = 0 //scheduled world.time for next fire() - var/cost = 0 //average time to execute - var/tick_usage = 0 //average tick usage - var/tick_overrun = 0 //average tick overrun - var/state = SS_IDLE //tracks the current state of the ss, running, paused, etc. - var/paused_ticks = 0 //ticks this ss is taking to run right now. - var/paused_tick_usage //total tick_usage of all of our runs while pausing this run - var/ticks = 1 //how many ticks does this ss take to run on avg. - var/times_fired = 0 //number of times we have called fire() - var/queued_time = 0 //time we entered the queue, (for timing and priority reasons) - var/queued_priority //we keep a running total to make the math easier, if priority changes mid-fire that would break our running total, so we store it here - //linked list stuff for the queue - var/datum/controller/subsystem/queue_next - var/datum/controller/subsystem/queue_prev - + ///Bitmap of what game states can this subsystem fire at. See [RUNLEVELS_DEFAULT] for more details. var/runlevels = RUNLEVELS_DEFAULT //points of the game at which the SS can fire - var/static/list/failure_strikes //How many times we suspect a subsystem type has crashed the MC, 3 strikes and you're out! + /* + * The following variables are managed by the MC and should not be modified directly. + */ + + /// Last world.time the subsystem completed a run (as in wasn't paused by [MC_TICK_CHECK]) + var/last_fire = 0 + + /// Scheduled world.time for next fire() + var/next_fire = 0 + + /// Running average of the amount of milliseconds it takes the subsystem to complete a run (including all resumes but not the time spent paused) + var/cost = 0 + + /// Running average of the amount of tick usage in percents of a tick it takes the subsystem to complete a run + var/tick_usage = 0 + + /// Running average of the amount of tick usage (in percents of a game tick) the subsystem has spent past its allocated time without pausing + var/tick_overrun = 0 + + /// Tracks the current execution state of the subsystem. Used to handle subsystems that sleep in fire so the mc doesn't run them again while they are sleeping + var/state = SS_IDLE + + /// Tracks how many fires the subsystem has consecutively paused on in the current run + var/paused_ticks = 0 + + /// Tracks how much of a tick the subsystem has consumed in the current run + var/paused_tick_usage + + /// Tracks how many fires the subsystem takes to complete a run on average. + var/ticks = 1 + + /// Tracks the amount of completed runs for the subsystem + var/times_fired = 0 + + /// Time the subsystem entered the queue, (for timing and priority reasons) + var/queued_time = 0 + + /// Priority at the time the subsystem entered the queue. Needed to avoid changes in priority (by admins and the like) from breaking things. + var/queued_priority + + /// How many times we suspect a subsystem type has crashed the MC, 3 strikes and you're out! + var/static/list/failure_strikes + + /// Next subsystem in the queue of subsystems to run this tick + var/datum/controller/subsystem/queue_next + /// Previous subsystem in the queue of subsystems to run this tick + var/datum/controller/subsystem/queue_prev + + //Do not blindly add vars here to the bottom, put it where it goes above + //If your var only has two values, put it in as a flag. + //Do not override ///datum/controller/subsystem/New() @@ -46,6 +98,7 @@ //This is used so the mc knows when the subsystem sleeps. do not override. /datum/controller/subsystem/proc/ignite(resumed = 0) + SHOULD_NOT_OVERRIDE(TRUE) set waitfor = 0 . = SS_SLEEPING fire(resumed) @@ -87,7 +140,7 @@ queue_node_flags = queue_node.flags if (queue_node_flags & SS_TICKER) - if (!(SS_flags & SS_TICKER)) + if ((SS_flags & (SS_TICKER|SS_BACKGROUND)) != SS_TICKER) continue if (queue_node_priority < SS_priority) break @@ -155,6 +208,9 @@ if(SS_SLEEPING) state = SS_PAUSING +/// Called after the config has been loaded or reloaded. +/datum/controller/subsystem/proc/OnConfigLoad() + /datum/controller/subsystem/proc/subsystem_log(msg) return log_subsystem(name, msg) diff --git a/code/controllers/subsystem/assets.dm b/code/controllers/subsystem/assets.dm index 7285298283..4f02d32ad0 100644 --- a/code/controllers/subsystem/assets.dm +++ b/code/controllers/subsystem/assets.dm @@ -4,6 +4,23 @@ SUBSYSTEM_DEF(assets) flags = SS_NO_FIRE var/list/cache = list() var/list/preload = list() + var/datum/asset_transport/transport = new() + +/datum/controller/subsystem/assets/OnConfigLoad() + var/newtransporttype = /datum/asset_transport + switch (CONFIG_GET(string/asset_transport)) + if ("webroot") + newtransporttype = /datum/asset_transport/webroot + + if (newtransporttype == transport.type) + return + + var/datum/asset_transport/newtransport = new newtransporttype () + if (newtransport.validate_config()) + transport = newtransport + transport.Load() + + /datum/controller/subsystem/assets/Initialize(timeofday) for(var/type in typesof(/datum/asset)) @@ -11,8 +28,6 @@ SUBSYSTEM_DEF(assets) if (type != initial(A._abstract)) get_asset_datum(type) - preload = cache.Copy() //don't preload assets generated during the round + transport.Initialize(cache) - for(var/client/C in GLOB.clients) - addtimer(CALLBACK(GLOBAL_PROC, .proc/getFilesSlow, C, preload, FALSE), 10) ..() diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm index ba2794d00f..a4f8dfdc5d 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -5,91 +5,35 @@ SUBSYSTEM_DEF(chat) priority = FIRE_PRIORITY_CHAT init_order = INIT_ORDER_CHAT - var/list/payload = list() - + var/list/payload_by_client = list() /datum/controller/subsystem/chat/fire() - for(var/i in payload) - var/client/C = i - C << output(payload[C], "browseroutput:output") - payload -= C - + for(var/key in payload_by_client) + var/client/client = key + var/payload = payload_by_client[key] + payload_by_client -= key + if(client) + // Send to tgchat + client.tgui_panel?.window.send_message("chat/message", payload) + // Send to old chat + for(var/msg in payload) + SEND_TEXT(client, msg["text"]) if(MC_TICK_CHECK) return - -/datum/controller/subsystem/chat/proc/queue(target, message, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = TRUE) - if(!target || !message) - return - - if(!istext(message)) - stack_trace("to_chat called with invalid input type") - return - - if(target == world) - target = GLOB.clients - - //Some macros remain in the string even after parsing and fuck up the eventual output - var/original_message = message - - //url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript. - //Do the double-encoding here to save nanoseconds - var/twiceEncoded - +/datum/controller/subsystem/chat/proc/queue(target, text, flags) if(islist(target)) - var/sanitized_message = FALSE - for(var/I in target) - var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible - - if(!C) - continue - - //Send it to the old style output window. - SEND_TEXT(C, original_message) - - if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file. - continue - - if(!sanitized_message) - message = replacetext(message, "\improper", "") - message = replacetext(message, "\proper", "") - if(handle_whitespace) - message = replacetext(message, "\n", "
") - message = replacetext(message, "\t", "[FOURSPACES][FOURSPACES]") - if (trailing_newline) - message += "
" - twiceEncoded = url_encode(url_encode(message)) - sanitized_message = TRUE - - if(!C.chatOutput.loaded) //Client still loading, put their messages in a queue - C.chatOutput.messageQueue += message - continue - - payload[C] += twiceEncoded - - else - var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible - - if(!C) - return - - //Send it to the old style output window. - SEND_TEXT(C, original_message) - - if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file. - return - - message = replacetext(message, "\improper", "") - message = replacetext(message, "\proper", "") - if(handle_whitespace) - message = replacetext(message, "\n", "
") - message = replacetext(message, "\t", "[FOURSPACES][FOURSPACES]") - if (trailing_newline) - message += "
" - twiceEncoded = url_encode(url_encode(message)) - - if(!C.chatOutput.loaded) //Client still loading, put their messages in a queue - C.chatOutput.messageQueue += message - return - - payload[C] += twiceEncoded + for(var/_target in target) + var/client/client = CLIENT_FROM_VAR(_target) + if(client) + LAZYADD(payload_by_client[client], list(list( + "text" = text, + "flags" = flags, + ))) + return + var/client/client = CLIENT_FROM_VAR(target) + if(client) + LAZYADD(payload_by_client[client], list(list( + "text" = text, + "flags" = flags, + ))) diff --git a/code/controllers/subsystem/ping.dm b/code/controllers/subsystem/ping.dm deleted file mode 100644 index c5c9bb3933..0000000000 --- a/code/controllers/subsystem/ping.dm +++ /dev/null @@ -1,33 +0,0 @@ -SUBSYSTEM_DEF(ping) - name = "Ping" - priority = FIRE_PRIORITY_PING - wait = 3 SECONDS - flags = SS_NO_INIT - runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME - - var/list/currentrun = list() - -/datum/controller/subsystem/ping/stat_entry() - ..("P:[GLOB.clients.len]") - - -/datum/controller/subsystem/ping/fire(resumed = 0) - if (!resumed) - src.currentrun = GLOB.clients.Copy() - - //cache for sanic speed (lists are references anyways) - var/list/currentrun = src.currentrun - - while (currentrun.len) - var/client/C = currentrun[currentrun.len] - currentrun.len-- - - if (!C || !C.chatOutput || !C.chatOutput.loaded) - if (MC_TICK_CHECK) - return - continue - - // softPang isn't handled anywhere but it'll always reset the opts.lastPang. - C.chatOutput.ehjax_send(data = C.is_afk(29) ? "softPang" : "pang") - if (MC_TICK_CHECK) - return diff --git a/code/controllers/subsystem/server_maint.dm b/code/controllers/subsystem/server_maint.dm index b77c78c4bb..2427fbd277 100644 --- a/code/controllers/subsystem/server_maint.dm +++ b/code/controllers/subsystem/server_maint.dm @@ -76,9 +76,7 @@ SUBSYSTEM_DEF(server_maint) if(!thing) continue var/client/C = thing - var/datum/chatOutput/co = C.chatOutput - if(co) - co.ehjax_send(data = "roundrestart") + C?.tgui_panel?.send_roundrestart() if(server) //if you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite C << link("byond://[server]") var/datum/tgs_version/tgsversion = world.TgsVersion() diff --git a/code/datums/accents.dm b/code/datums/accents.dm new file mode 100644 index 0000000000..edfdfb9fee --- /dev/null +++ b/code/datums/accents.dm @@ -0,0 +1,106 @@ +/datum/accent + +/datum/accent/proc/modify_speech(list/speech_args, datum/source, mob/living/carbon/owner) //transforms the message in some way + return speech_args + +/datum/accent/lizard/modify_speech(list/speech_args) + var/message = speech_args[SPEECH_MESSAGE] + var/static/regex/lizard_hiss = new("s+", "g") + var/static/regex/lizard_hiSS = new("S+", "g") + if(message[1] != "*") + message = lizard_hiss.Replace(message, "sss") + message = lizard_hiSS.Replace(message, "SSS") + speech_args[SPEECH_MESSAGE] = message + return speech_args + +/datum/accent/fly/modify_speech(list/speech_args) + var/message = speech_args[SPEECH_MESSAGE] + var/static/regex/fly_buzz = new("z+", "g") + var/static/regex/fly_buZZ = new("Z+", "g") + if(message[1] != "*") + message = fly_buzz.Replace(message, "zzz") + message = fly_buZZ.Replace(message, "ZZZ") + speech_args[SPEECH_MESSAGE] = message + return speech_args + +/datum/accent/abductor/modify_speech(list/speech_args, datum/source) + var/message = speech_args[SPEECH_MESSAGE] + var/mob/living/carbon/human/user = source + var/rendered = "[user.name]: [message]" + user.log_talk(message, LOG_SAY, tag="abductor") + for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) + var/obj/item/organ/tongue/T = H.getorganslot(ORGAN_SLOT_TONGUE) + if(!T || T.type != type) + continue + if(H.dna && H.dna.species.id == "abductor" && user.dna && user.dna.species.id == "abductor") + var/datum/antagonist/abductor/A = user.mind.has_antag_datum(/datum/antagonist/abductor) + if(!A || !(H.mind in A.team.members)) + continue + to_chat(H, rendered) + for(var/mob/M in GLOB.dead_mob_list) + var/link = FOLLOW_LINK(M, user) + to_chat(M, "[link] [rendered]") + speech_args[SPEECH_MESSAGE] = "" + return speech_args + +/datum/accent/zombie/modify_speech(list/speech_args) + var/message = speech_args[SPEECH_MESSAGE] + var/list/message_list = splittext(message, " ") + var/maxchanges = max(round(message_list.len / 1.5), 2) + + for(var/i = rand(maxchanges / 2, maxchanges), i > 0, i--) + var/insertpos = rand(1, message_list.len - 1) + var/inserttext = message_list[insertpos] + + if(!(copytext(inserttext, -3) == "..."))//3 == length("...") + message_list[insertpos] = inserttext + "..." + + if(prob(20) && message_list.len > 3) + message_list.Insert(insertpos, "[pick("BRAINS", "Brains", "Braaaiinnnsss", "BRAAAIIINNSSS")]...") + + speech_args[SPEECH_MESSAGE] = jointext(message_list, " ") + return speech_args + +/datum/accent/alien/modify_speech(list/speech_args, datum/source) + playsound(source, "hiss", 25, 1, 1) + return speech_args + +/datum/accent/fluffy/modify_speech(list/speech_args) + var/message = speech_args[SPEECH_MESSAGE] + if(message[1] != "*") + message = replacetext(message, "ne", "nye") + message = replacetext(message, "nu", "nyu") + message = replacetext(message, "na", "nya") + message = replacetext(message, "no", "nyo") + message = replacetext(message, "ove", "uv") + message = replacetext(message, "l", "w") + message = replacetext(message, "r", "w") + speech_args[SPEECH_MESSAGE] = lowertext(message) + return speech_args + +/datum/accent/bone + var/span_flag + +/datum/accent/bone/modify_speech(list/speech_args) + speech_args[SPEECH_SPANS] = span_flag + return speech_args + +//bone tongues either have the sans accent or the papyrus accent +/datum/accent/bone/sans + span_flag = SPAN_SANS + +/datum/accent/bone/papyrus + span_flag = SPAN_PAPYRUS + +/datum/accent/robot/modify_speech(list/speech_args) + speech_args[SPEECH_SPANS] = SPAN_ROBOT + +/datum/accent/dullahan/modify_speech(list/speech_args, datum/source, mob/living/carbon/owner) + if(owner) + if(isdullahan(owner)) + var/datum/species/dullahan/D = owner.dna.species + if(isobj(D.myhead.loc)) + var/obj/O = D.myhead.loc + O.say(speech_args[SPEECH_MESSAGE]) + speech_args[SPEECH_MESSAGE] = "" + return speech_args diff --git a/code/datums/browser.dm b/code/datums/browser.dm index 96b2a2a294..dbe60817bd 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -8,14 +8,14 @@ var/window_options = "can_close=1;can_minimize=1;can_maximize=0;can_resize=1;titlebar=1;" // window option is set using window_id var/stylesheets[0] var/scripts[0] - var/title_image var/head_elements var/body_elements var/head_content = "" var/content = "" + var/static/datum/asset/simple/namespaced/common/common_asset = get_asset_datum(/datum/asset/simple/namespaced/common) -/datum/browser/New(nuser, nwindow_id, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null) +/datum/browser/New(nuser, nwindow_id, ntitle = 0, nwidth = 0, nheight = 0, atom/nref = null) user = nuser window_id = nwindow_id @@ -27,7 +27,6 @@ height = nheight if (nref) ref = nref - add_stylesheet("common", 'html/browser/common.css') // this CSS sheet is common to all UIs /datum/browser/proc/add_head_content(nhead_content) head_content = nhead_content @@ -35,22 +34,21 @@ /datum/browser/proc/set_window_options(nwindow_options) window_options = nwindow_options -/datum/browser/proc/set_title_image(ntitle_image) - //title_image = ntitle_image - /datum/browser/proc/add_stylesheet(name, file) if(istype(name, /datum/asset/spritesheet)) var/datum/asset/spritesheet/sheet = name stylesheets["spritesheet_[sheet.name].css"] = "data/spritesheets/[sheet.name]" else var/asset_name = "[name].css" + stylesheets[asset_name] = file - if(!SSassets.cache[asset_name]) - register_asset(asset_name, file) + + if (!SSassets.cache[asset_name]) + SSassets.transport.register_asset(asset_name, file) /datum/browser/proc/add_script(name, file) scripts["[ckey(name)].js"] = file - register_asset("[ckey(name)].js", file) + SSassets.transport.register_asset("[ckey(name)].js", file) /datum/browser/proc/set_content(ncontent) content = ncontent @@ -60,15 +58,13 @@ /datum/browser/proc/get_header() var/file + head_content += "" for (file in stylesheets) - head_content += "" + head_content += "" + for (file in scripts) - head_content += "" - - var/title_attributes = "class='uiTitle'" - if (title_image) - title_attributes = "class='uiTitle icon' style='background-image: url([title_image]);'" + head_content += "" return {" @@ -79,7 +75,7 @@
- [title ? "
[title]
" : ""] + [title ? "
[title]
" : ""]
"} //" This is here because else the rest of the file looks like a string in notepad++. @@ -105,10 +101,11 @@ var/window_size = "" if(width && height) window_size = "size=[width]x[height];" + common_asset.send(user) if(stylesheets.len) - send_asset_list(user, stylesheets) + SSassets.transport.send_assets(user, stylesheets) if(scripts.len) - send_asset_list(user, scripts) + SSassets.transport.send_assets(user, scripts) user << browse(get_content(), "window=[window_id];[window_size][window_options]") if(use_onclose) setup_onclose() @@ -169,7 +166,7 @@ return Button3 //Same shit, but it returns the button number, could at some point support unlimited button amounts. -/proc/askuser(var/mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1, Timeout = 6000) +/proc/askuser(mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1, Timeout = 6000) if (!istype(User)) if (istype(User, /client/)) var/client/C = User @@ -188,7 +185,7 @@ var/selectedbutton = 0 var/stealfocus -/datum/browser/modal/New(nuser, nwindow_id, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null, StealFocus = 1, Timeout = 6000) +/datum/browser/modal/New(nuser, nwindow_id, ntitle = 0, nwidth = 0, nheight = 0, atom/nref = null, StealFocus = 1, Timeout = 6000) ..() stealfocus = StealFocus if (!StealFocus) diff --git a/code/datums/components/honkspam.dm b/code/datums/components/honkspam.dm new file mode 100644 index 0000000000..73b5e3335a --- /dev/null +++ b/code/datums/components/honkspam.dm @@ -0,0 +1,22 @@ +// This used to be in paper.dm, it was some snowflake code that was +// used ONLY on april's fool. I moved it to a component so it could be +// used in other places + +/datum/component/honkspam + dupe_mode = COMPONENT_DUPE_UNIQUE + var/spam_flag = FALSE + +/datum/component/honkspam/Initialize() + if(!isitem(parent)) + return COMPONENT_INCOMPATIBLE + RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, .proc/interact) + +/datum/component/honkspam/proc/reset_spamflag() + spam_flag = FALSE + +/datum/component/honkspam/proc/interact(mob/user) + if(!spam_flag) + spam_flag = TRUE + var/obj/item/parent_item = parent + playsound(parent_item.loc, 'sound/items/bikehorn.ogg', 50, TRUE) + addtimer(CALLBACK(src, .proc/reset_spamflag), 2 SECONDS) diff --git a/code/datums/components/label.dm b/code/datums/components/label.dm new file mode 100644 index 0000000000..c6d0c595eb --- /dev/null +++ b/code/datums/components/label.dm @@ -0,0 +1,87 @@ +/** + The label component. + + This component is used to manage labels applied by the hand labeler. + + Atoms can only have one instance of this component, and therefore only one label at a time. + This is to avoid having names like "Backpack (label1) (label2) (label3)". This is annoying and abnoxious to read. + + When a player clicks the atom with a hand labeler to apply a label, this component gets applied to it. + If the labeler is off, the component will be removed from it, and the label will be removed from its name. + */ +/datum/component/label + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + /// The name of the label the player is applying to the parent. + var/label_name + +/datum/component/label/Initialize(_label_name) + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + + label_name = _label_name + apply_label() + +/datum/component/label/RegisterWithParent() + RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/OnAttackby) + RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/Examine) + +/datum/component/label/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_PARENT_ATTACKBY, COMSIG_PARENT_EXAMINE)) + +/** + This proc will fire after the parent is hit by a hand labeler which is trying to apply another label. + Since the parent already has a label, it will remove the old one from the parent's name, and apply the new one. +*/ +/datum/component/label/InheritComponent(datum/component/label/new_comp , i_am_original, _label_name) + remove_label() + if(new_comp) + label_name = new_comp.label_name + else + label_name = _label_name + apply_label() + +/** + This proc will trigger when any object is used to attack the parent. + + If the attacking object is not a hand labeler, it will return. + If the attacking object is a hand labeler it will restore the name of the parent to what it was before this component was added to it, and the component will be deleted. + + Arguments: + * source: The parent. + * attacker: The object that is hitting the parent. + * user: The mob who is wielding the attacking object. +*/ +/datum/component/label/proc/OnAttackby(datum/source, obj/item/attacker, mob/user) + // If the attacking object is not a hand labeler or its mode is 1 (has a label ready to apply), return. + // The hand labeler should be off (mode is 0), in order to remove a label. + var/obj/item/hand_labeler/labeler = attacker + if(!istype(labeler) || labeler.mode) + return + + remove_label() + playsound(parent, 'sound/items/poster_ripped.ogg', 20, TRUE) + to_chat(user, "You remove the label from [parent].") + qdel(src) // Remove the component from the object. + +/** + This proc will trigger when someone examines the parent. + It will attach the text found in the body of the proc to the `examine_list` and display it to the player examining the parent. + + Arguments: + * source: The parent. + * user: The mob exmaining the parent. + * examine_list: The current list of text getting passed from the parent's normal examine() proc. +*/ +/datum/component/label/proc/Examine(datum/source, mob/user, list/examine_list) + examine_list += "It has a label with some words written on it. Use a hand labeler to remove it." + +/// Applies a label to the name of the parent in the format of: "parent_name (label)" +/datum/component/label/proc/apply_label() + var/atom/owner = parent + owner.name += " ([label_name])" + +/// Removes the label from the parent's name +/datum/component/label/proc/remove_label() + var/atom/owner = parent + owner.name = replacetext(owner.name, "([label_name])", "") // Remove the label text from the parent's name, wherever it's located. + owner.name = trim(owner.name) // Shave off any white space from the beginning or end of the parent's name. diff --git a/code/datums/components/ntnet_interface.dm b/code/datums/components/ntnet_interface.dm index 6159c7c2c4..06d69f0ce3 100644 --- a/code/datums/components/ntnet_interface.dm +++ b/code/datums/components/ntnet_interface.dm @@ -1,4 +1,4 @@ -//Thing meant for allowing datums and objects to access a NTnet network datum. +//Thing meant for allowing datums and objects to access an NTnet network datum. /datum/proc/ntnet_receive(datum/netdata/data) return diff --git a/code/datums/elements/decal.dm b/code/datums/elements/decal.dm index 50519b08e7..5ddd5bd03e 100644 --- a/code/datums/elements/decal.dm +++ b/code/datums/elements/decal.dm @@ -32,6 +32,7 @@ RegisterSignal(A, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_react) if(description) RegisterSignal(A, COMSIG_PARENT_EXAMINE, .proc/examine) + RegisterSignal(A, COMSIG_ATOM_UPDATE_OVERLAYS, .proc/apply_overlay, TRUE) num_decals_per_atom[A]++ apply(A) @@ -39,21 +40,20 @@ /datum/element/decal/Detach(datum/target) var/atom/A = target num_decals_per_atom[A]-- - apply(A, TRUE) if(!num_decals_per_atom[A]) UnregisterSignal(A, list(COMSIG_ATOM_DIR_CHANGE, COMSIG_COMPONENT_CLEAN_ACT, COMSIG_PARENT_EXAMINE, COMSIG_ATOM_UPDATE_OVERLAYS)) LAZYREMOVE(num_decals_per_atom, A) + apply(A) return ..() -/datum/element/decal/proc/apply(atom/target, removing = FALSE) - if(num_decals_per_atom[target] == 1 && !removing) - RegisterSignal(target, COMSIG_ATOM_UPDATE_OVERLAYS, .proc/apply_overlay, TRUE) +/datum/element/decal/proc/apply(atom/target) target.update_icon() if(isitem(target)) addtimer(CALLBACK(target, /obj/item/.proc/update_slot_icon), 0, TIMER_UNIQUE) /datum/element/decal/proc/apply_overlay(atom/source, list/overlay_list) - pic.dir = first_dir == NORTH ? source.dir : turn(first_dir, dir2angle(source.dir)) + if(first_dir) + pic.dir = turn(first_dir, -dir2angle(source.dir)) for(var/i in 1 to num_decals_per_atom[source]) overlay_list += pic diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index 6660bafcaf..e1147df225 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -102,8 +102,9 @@ /datum/emote/proc/can_run_emote(mob/user, status_check = TRUE, intentional = FALSE) . = TRUE - if(!is_type_in_typecache(user, mob_type_allowed_typecache)) - return FALSE + if(mob_type_allowed_typecache) //empty list = anyone can use it unless specifically blacklisted + if(!is_type_in_typecache(user, mob_type_allowed_typecache)) + return FALSE if(is_type_in_typecache(user, mob_type_blacklist_typecache)) return FALSE if(status_check && !is_type_in_typecache(user, mob_type_ignore_stat_typecache)) diff --git a/code/datums/explosion.dm b/code/datums/explosion.dm index dc9569a4d5..246226ceba 100644 --- a/code/datums/explosion.dm +++ b/code/datums/explosion.dm @@ -103,13 +103,21 @@ GLOBAL_LIST_EMPTY(explosions) // 3/7/14 will calculate to 80 + 35 var/far_dist = 0 - far_dist += heavy_impact_range * 5 + far_dist += heavy_impact_range * 15 // Large explosions carry further far_dist += devastation_range * 20 if(!silent) var/frequency = get_rand_frequency() var/sound/explosion_sound = sound(get_sfx("explosion")) var/sound/far_explosion_sound = sound('sound/effects/explosionfar.ogg') + var/sound/creaking_explosion_sound = sound(get_sfx("explosion_creaking")) + var/sound/hull_creaking_sound = sound(get_sfx("hull_creaking")) + var/sound/explosion_echo_sound = sound('sound/effects/explosion_distant.ogg') + var/on_station = SSmapping.level_trait(epicenter.z, ZTRAIT_STATION) + var/creaking_explosion = FALSE + + if(prob(devastation_range*30+heavy_impact_range*5) && on_station) // Huge explosions are near guaranteed to make the station creak and whine, smaller ones might. + creaking_explosion = TRUE // prob over 100 always returns true for(var/mob/M in GLOB.player_list) // Double check for client @@ -126,11 +134,29 @@ GLOBAL_LIST_EMPTY(explosions) shake_camera(M, 25, clamp(baseshakeamount, 0, 10)) // You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station. else if(dist <= far_dist) - var/far_volume = clamp(far_dist, 30, 50) // Volume is based on explosion size and dist - far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion - M.playsound_local(epicenter, null, far_volume, 1, frequency, falloff = 5, S = far_explosion_sound) - if(baseshakeamount > 0) + var/far_volume = clamp(far_dist/2, 40, 60) // Volume is based on explosion size and dist + if(creaking_explosion) + M.playsound_local(epicenter, null, far_volume, 1, frequency, S = creaking_explosion_sound, distance_multiplier = 0) + else if(prob(75)) + M.playsound_local(epicenter, null, far_volume, 1, frequency, S = far_explosion_sound, distance_multiplier = 0) // Far sound + else + M.playsound_local(epicenter, null, far_volume, 1, frequency, S = explosion_echo_sound, distance_multiplier = 0) // Echo sound + + if(baseshakeamount > 0 || devastation_range) + if(!baseshakeamount) // Devastating explosions rock the station and ground + baseshakeamount = devastation_range*3 shake_camera(M, 10, clamp(baseshakeamount*0.25, 0, 2.5)) + + else if(M.can_hear() && !isspaceturf(get_turf(M)) && heavy_impact_range) // Big enough explosions echo throughout the hull + var/echo_volume = 40 + if(devastation_range) + baseshakeamount = devastation_range + shake_camera(M, 10, clamp(baseshakeamount*0.25, 0, 2.5)) + echo_volume = 60 + M.playsound_local(epicenter, null, echo_volume, 1, frequency, S = explosion_echo_sound, distance_multiplier = 0) + + if(creaking_explosion) // 5 seconds after the bang, the station begins to creak + addtimer(CALLBACK(M, /mob/proc/playsound_local, epicenter, null, rand(25, 40), 1, frequency, null, null, FALSE, hull_creaking_sound, null, null, null, null, 0), 5 SECONDS) EX_PREPROCESS_CHECK_TICK //postpone processing for a bit diff --git a/code/datums/mutations/antenna.dm b/code/datums/mutations/antenna.dm index 978802fd80..ad08b8ebdc 100644 --- a/code/datums/mutations/antenna.dm +++ b/code/datums/mutations/antenna.dm @@ -90,6 +90,8 @@ to_chat(user, "You catch some drifting memories of their past conversations...") for(var/spoken_memory in recent_speech) to_chat(user, "[recent_speech[spoken_memory]]") + if(usr in GLOB.rockpaperscissors_players) + to_chat(user, "They're planning on playing [GLOB.rockpaperscissors_players[usr][1]]") if(iscarbon(M)) var/mob/living/carbon/human/H = M to_chat(user, "You find that their intent is to [H.a_intent]...") diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index 179ed765c5..ef3979d822 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -580,4 +580,63 @@ /datum/status_effect/regenerative_core/on_remove() . = ..() REMOVE_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, "regenerative_core") - owner.updatehealth() \ No newline at end of file + owner.updatehealth() + +/datum/status_effect/panacea + id = "Anatomic Panacea" + duration = 100 + tick_interval = 10 + alert_type = /obj/screen/alert/status_effect/panacea + +/obj/screen/alert/status_effect/panacea + name = "Panacea" + desc = "We purge the impurities from our body." + icon_state = "panacea" + +// Changeling's anatomic panacea now in buff form. Directly fixes issues instead of injecting chems +/datum/status_effect/panacea/tick() + var/mob/living/carbon/M = owner + + //Heal brain damage and toxyloss, alongside trauma + owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, -8) + owner.adjustToxLoss(-6, forced = TRUE) + M.cure_trauma_type(resilience = TRAUMA_RESILIENCE_BASIC) + //Purges 50 rads per tick + if(owner.radiation > 0) + owner.radiation -= min(owner.radiation, 50) + //Mutadone effects + owner.jitteriness = 0 + if(owner.has_dna()) + M.dna.remove_all_mutations(mutadone = TRUE) + if(!QDELETED(owner)) //We were a monkey, now a human + ..() + // Purges toxins + for(var/datum/reagent/toxin/R in owner.reagents.reagent_list) + owner.reagents.remove_reagent(R.type, 5) + //Antihol effects + M.reagents.remove_all_type(/datum/reagent/consumable/ethanol, 10, 0, 1) + M.drunkenness = max(M.drunkenness - 10, 0) + owner.dizziness = 0 + owner.drowsyness = 0 + owner.slurring = 0 + owner.confused = 0 + //Organ and disease cure moved from panacea.dm to buff proc + var/list/bad_organs = list( + owner.getorgan(/obj/item/organ/body_egg), + owner.getorgan(/obj/item/organ/zombie_infection)) + for(var/o in bad_organs) + var/obj/item/organ/O = o + if(!istype(O)) + continue + O.Remove() + if(iscarbon(owner)) + var/mob/living/carbon/C = owner + C.vomit(0, toxic = TRUE) + O.forceMove(get_turf(owner)) + if(isliving(owner)) + var/mob/living/L = owner + for(var/thing in L.diseases) + var/datum/disease/D = thing + if(D.severity == DISEASE_SEVERITY_POSITIVE) + continue + D.cure() diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 6a7671cd5b..b29584aa58 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -538,7 +538,7 @@ name = "Slaughter Demon" config_tag = "slaughter_demon" antag_flag = ROLE_ALIEN - enemy_roles = list("Security Officer","Shaft Miner","Head of Security","Captain","Janitor","AI","Cyborg") + enemy_roles = list("Security Officer","Shaft Miner","Head of Security","Captain","Janitor","AI","Cyborg","Bartender") required_enemies = list(3,2,2,2,2,1,1,1,1,0) required_candidates = 1 weight = 4 diff --git a/code/game/machinery/computer/arcade/battle.dm b/code/game/machinery/computer/arcade/battle.dm index fc99edd3eb..a6c98c6c9c 100644 --- a/code/game/machinery/computer/arcade/battle.dm +++ b/code/game/machinery/computer/arcade/battle.dm @@ -49,7 +49,6 @@ dat += "" var/datum/browser/popup = new(user, "arcade", "Space Villain 2000") popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) popup.open() /obj/machinery/computer/arcade/battle/Topic(href, href_list) diff --git a/code/game/machinery/computer/arcade/orion_trail.dm b/code/game/machinery/computer/arcade/orion_trail.dm index 8b81c69ed2..441010906c 100644 --- a/code/game/machinery/computer/arcade/orion_trail.dm +++ b/code/game/machinery/computer/arcade/orion_trail.dm @@ -160,7 +160,6 @@ dat += "

Close

" var/datum/browser/popup = new(user, "arcade", "The Orion Trail",400,700) popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) popup.open() return diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 74cceacd12..46d419a8fe 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -353,7 +353,6 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) dat = list("", header.Join(), body, "
") var/datum/browser/popup = new(user, "id_com", src.name, 900, 620) popup.set_content(dat.Join()) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.open() /obj/machinery/computer/card/Topic(href, href_list) diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 400ce041c7..2a05b359d8 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -276,7 +276,6 @@ var/datum/browser/popup = new(user, "cloning", "Cloning System Control") popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.open() /obj/machinery/computer/cloning/Topic(href, href_list) diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 6a99b248e3..3ef887b156 100755 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -1,3 +1,15 @@ +#define STATE_DEFAULT 1 +#define STATE_CALLSHUTTLE 2 +#define STATE_CANCELSHUTTLE 3 +#define STATE_MESSAGELIST 4 +#define STATE_VIEWMESSAGE 5 +#define STATE_DELMESSAGE 6 +#define STATE_STATUSDISPLAY 7 +#define STATE_ALERT_LEVEL 8 +#define STATE_CONFIRM_LEVEL 9 +#define STATE_TOGGLE_EMERGENCY 10 +#define STATE_PURCHASE 11 + // The communications computer /obj/machinery/computer/communications name = "communications console" @@ -6,6 +18,7 @@ icon_keyboard = "tech_key" req_access = list(ACCESS_HEADS) circuit = /obj/item/circuitboard/computer/communications + light_color = LIGHT_COLOR_BLUE var/auth_id = "Unknown" //Who is currently logged in? var/list/datum/comm_message/messages = list() var/datum/comm_message/currmsg @@ -16,22 +29,10 @@ var/ai_message_cooldown = 0 var/tmp_alertlevel = 0 var/static/security_level_cd // used to stop mass spam. - var/const/STATE_DEFAULT = 1 - var/const/STATE_CALLSHUTTLE = 2 - var/const/STATE_CANCELSHUTTLE = 3 - var/const/STATE_MESSAGELIST = 4 - var/const/STATE_VIEWMESSAGE = 5 - var/const/STATE_DELMESSAGE = 6 - var/const/STATE_STATUSDISPLAY = 7 - var/const/STATE_ALERT_LEVEL = 8 - var/const/STATE_CONFIRM_LEVEL = 9 - var/const/STATE_TOGGLE_EMERGENCY = 10 - var/const/STATE_PURCHASE = 11 var/stat_msg1 var/stat_msg2 - light_color = LIGHT_COLOR_BLUE /obj/machinery/computer/communications/proc/checkCCcooldown() var/obj/item/circuitboard/computer/communications/CM = circuit @@ -46,7 +47,7 @@ /obj/machinery/computer/communications/Topic(href, href_list) if(..()) return - if(!usr.canUseTopic(src)) + if(!usr.canUseTopic(src, !issilicon(usr))) return if(!is_station_level(z) && !is_reserved_level(z)) //Can only use in transit and on SS13 to_chat(usr, "Unable to establish a connection: \black You're too far away from the station!") @@ -132,15 +133,20 @@ if("crossserver") if(authenticated==2) + var/dest = href_list["cross_dest"] if(!checkCCcooldown()) to_chat(usr, "Arrays recycling. Please stand by.") playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE) return - var/input = stripped_multiline_input(usr, "Please choose a message to transmit to allied stations. Please be aware that this process is very expensive, and abuse will lead to... termination.", "Send a message to an allied station.", "") + var/warning = dest == "all" ? "Please choose a message to transmit to allied stations." : "Please choose a message to transmit to [dest] sector station." + var/input = stripped_multiline_input(usr, "[warning] Please be aware that this process is very expensive, and abuse will lead to... termination.", "Send a message to an allied station.", "") if(!input || !(usr in view(1,src)) || !checkCCcooldown()) return playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) - send2otherserver("[station_name()]", input,"Comms_Console") + if(dest == "all") + send2otherserver("[station_name()]", input,"Comms_Console") + else + send2otherserver("[station_name()]", input,"Comms_Console", list(dest)) minor_announce(input, title = "Outgoing message to allied station") usr.log_talk(input, LOG_SAY, tag="message to the other server") message_admins("[ADMIN_LOOKUPFLW(usr)] has sent a message to the other server.") @@ -156,12 +162,12 @@ var/datum/map_template/shuttle/S = locate(href_list["chosen_shuttle"]) in shuttles if(S && istype(S)) if(SSshuttle.emergency.mode != SHUTTLE_RECALL && SSshuttle.emergency.mode != SHUTTLE_IDLE) - to_chat(usr, "It's a bit late to buy a new shuttle, don't you think?") + to_chat(usr, "It's a bit late to buy a new shuttle, don't you think?") return if(SSshuttle.shuttle_purchased) - to_chat(usr, "A replacement shuttle has already been purchased.") + to_chat(usr, "A replacement shuttle has already been purchased.") else if(!S.prerequisites_met()) - to_chat(usr, "You have not met the requirements for purchasing this shuttle.") + to_chat(usr, "You have not met the requirements for purchasing this shuttle.") else var/points_to_check var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR) @@ -183,7 +189,7 @@ if("callshuttle") state = STATE_DEFAULT - if(authenticated) + if(authenticated && SSshuttle.canEvac(usr)) state = STATE_CALLSHUTTLE if("callshuttle2") if(authenticated) @@ -284,12 +290,12 @@ if("MessageCentCom") if(authenticated) if(!checkCCcooldown()) - to_chat(usr, "Arrays recycling. Please stand by.") + to_chat(usr, "Arrays recycling. Please stand by.") return var/input = stripped_input(usr, "Please choose a message to transmit to CentCom via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "Send a message to CentCom.", "") if(!input || !(usr in view(1,src)) || !checkCCcooldown()) return - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) CentCom_announce(input, usr) to_chat(usr, "Message transmitted to Central Command.") for(var/client/X in GLOB.admins) @@ -302,7 +308,7 @@ // OMG SYNDICATE ...LETTERHEAD if("MessageSyndicate") - if((authenticated==2) && (obj_flags & EMAGGED)) + if((authenticated) && (obj_flags & EMAGGED)) if(!checkCCcooldown()) to_chat(usr, "Arrays recycling. Please stand by.") playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE) @@ -332,7 +338,7 @@ if(!checkCCcooldown()) to_chat(usr, "Arrays recycling. Please stand by.") return - var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.","") + var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self-Destruct Code Request.","") if(!input || !(usr in view(1,src)) || !checkCCcooldown()) return Nuke_request(input, usr) @@ -347,7 +353,9 @@ aicurrmsg = null aistate = STATE_DEFAULT if("ai-callshuttle") - aistate = STATE_CALLSHUTTLE + aistate = STATE_DEFAULT + if(SSshuttle.canEvac(usr)) + aistate = STATE_CALLSHUTTLE if("ai-callshuttle2") SSshuttle.requestEvac(usr, href_list["call"]) aistate = STATE_DEFAULT @@ -460,9 +468,8 @@ var/datum/browser/popup = new(user, "communications", "Communications Console", 400, 500) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) - if(issilicon(user) || (hasSiliconAccessInArea(user) && !in_range(user,src))) + if(issilicon(user)) var/dat2 = interact_ai(user) // give the AI a different interact proc to limit its access if(dat2) dat += dat2 @@ -493,9 +500,15 @@ if (authenticated==2) dat += "

Captain Functions" dat += "
\[ Make a Captain's Announcement \]" - var/cross_servers_count = length(CONFIG_GET(keyed_list/cross_server)) - if(cross_servers_count) - dat += "
\[ Send a message to [cross_servers_count == 1 ? "an " : ""]allied station[cross_servers_count > 1 ? "s" : ""] \]" + var/list/cross_servers = CONFIG_GET(keyed_list/cross_server) + var/our_id = CONFIG_GET(string/cross_comms_name) + if(cross_servers.len) + for(var/server in cross_servers) + if(server == our_id) + continue + dat += "
\[ Send a message to station in [server] sector. \]" + if(cross_servers.len > 2) + dat += "
\[ Send a message to all allied stations \]" if(SSmapping.config.allow_custom_shuttles) dat += "
\[ Purchase Shuttle \]" dat += "
\[ Change Alert Level \]" @@ -721,8 +734,13 @@ to_chat(user, "Intercomms recharging. Please stand by.") return var/input = stripped_input(user, "Please choose a message to announce to the station crew.", "What?") - if(!input || !user.canUseTopic(src)) + if(!input || !user.canUseTopic(src, !issilicon(usr))) return + if(!(user.can_speak())) //No more cheating, mime/random mute guy! + input = "..." + to_chat(user, "You find yourself unable to speak.") + else + input = user.treat_message(input) //Adds slurs and so on. Someone should make this use languages too. SScommunications.make_announcement(user, is_silicon, input) deadchat_broadcast("[user.real_name] made an priority announcement from [get_area_name(usr, TRUE)].", user) @@ -771,3 +789,15 @@ content = new_content if(new_possible_answers) possible_answers = new_possible_answers + +#undef STATE_DEFAULT +#undef STATE_CALLSHUTTLE +#undef STATE_CANCELSHUTTLE +#undef STATE_MESSAGELIST +#undef STATE_VIEWMESSAGE +#undef STATE_DELMESSAGE +#undef STATE_STATUSDISPLAY +#undef STATE_ALERT_LEVEL +#undef STATE_CONFIRM_LEVEL +#undef STATE_TOGGLE_EMERGENCY +#undef STATE_PURCHASE diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm index 286c106788..3d212ec0cb 100644 --- a/code/game/machinery/computer/dna_console.dm +++ b/code/game/machinery/computer/dna_console.dm @@ -217,9 +217,6 @@ // already discovered mutations stored_research = SSresearch.science_tech -/obj/machinery/computer/scan_consolenew/examine(mob/user) - . = ..() - /obj/machinery/computer/scan_consolenew/ui_interact(mob/user, datum/tgui/ui) // Most of ui_interact is spent setting variables for passing to the tgui // interface. @@ -266,6 +263,10 @@ if(!ui) ui = new(user, src, "DnaConsole") ui.open() + +/obj/machinery/computer/scan_consolenew/ui_assets() + . = ..() || list() + . += get_asset_datum(/datum/asset/simple/genetics) /obj/machinery/computer/scan_consolenew/ui_data(mob/user) var/list/data = list() @@ -357,7 +358,7 @@ return data -/obj/machinery/computer/scan_consolenew/ui_act(action, var/list/params) +/obj/machinery/computer/scan_consolenew/ui_act(action, list/params) if(..()) return TRUE @@ -482,6 +483,7 @@ // Resolve mutation's BYOND path from the alias var/alias = params["alias"] var/path = GET_MUTATION_TYPE_FROM_ALIAS(alias) + // Make sure the occupant still has this mutation if(!(path in scanner_occupant.dna.mutation_index)) return diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index d8a5f856f1..4e99f67157 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -178,7 +178,6 @@ dat += "{Log In}" var/datum/browser/popup = new(user, "med_rec", "Medical Records Console", 600, 400) popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) popup.open() /obj/machinery/computer/med_data/Topic(href, href_list) diff --git a/code/game/machinery/computer/pod.dm b/code/game/machinery/computer/pod.dm index 53f7cb7e32..ca64d538b9 100644 --- a/code/game/machinery/computer/pod.dm +++ b/code/game/machinery/computer/pod.dm @@ -67,7 +67,6 @@ add_fingerprint(usr) var/datum/browser/popup = new(user, "computer", title, 400, 500) popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) popup.open() /obj/machinery/computer/pod/process() diff --git a/code/game/machinery/computer/prisoner/management.dm b/code/game/machinery/computer/prisoner/management.dm index 4b4b39740a..43aa978558 100644 --- a/code/game/machinery/computer/prisoner/management.dm +++ b/code/game/machinery/computer/prisoner/management.dm @@ -66,7 +66,6 @@ dat += "
{Log Out}" var/datum/browser/popup = new(user, "computer", "Prisoner Management Console", 400, 500) popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.open() return diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 5c280eeace..844f14465a 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -250,7 +250,6 @@ dat += "{Log In}" var/datum/browser/popup = new(user, "secure_rec", "Security Records Console", 600, 400) popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.open() return diff --git a/code/game/machinery/computer/telecrystalconsoles.dm b/code/game/machinery/computer/telecrystalconsoles.dm index 82e8f46ab6..87b7159125 100644 --- a/code/game/machinery/computer/telecrystalconsoles.dm +++ b/code/game/machinery/computer/telecrystalconsoles.dm @@ -103,7 +103,6 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E var/datum/browser/popup = new(user, "computer", "Telecrystal Upload/Receive Station", 700, 500) popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.open() /obj/machinery/computer/telecrystals/uplinker/Topic(href, href_list) @@ -185,7 +184,6 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E var/datum/browser/popup = new(user, "computer", "Team Telecrystal Management Console", 700, 500) popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.open() /obj/machinery/computer/telecrystals/boss/Topic(href, href_list) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 9330a30555..f7019e75ef 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -76,7 +76,6 @@ var/datum/browser/popup = new(user, "cryopod_console", "Cryogenic System Control") popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.open() /obj/machinery/computer/cryopod/Topic(href, href_list) @@ -308,7 +307,7 @@ var/mob/living/mob_occupant = occupant var/list/obj/item/cryo_items = list() - + investigate_log("Despawning [key_name(mob_occupant)].", INVESTIGATE_CRYOGENICS) //Handle Borg stuff first diff --git a/code/game/machinery/embedded_controller/access_controller.dm b/code/game/machinery/embedded_controller/access_controller.dm index f74de23171..8eebd779f4 100644 --- a/code/game/machinery/embedded_controller/access_controller.dm +++ b/code/game/machinery/embedded_controller/access_controller.dm @@ -260,7 +260,6 @@ /obj/machinery/doorButtons/airlock_controller/ui_interact(mob/user) var/datum/browser/popup = new(user, "computer", name) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.set_content(returnText()) popup.open() diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm index 6fd351bcff..5d0165189d 100644 --- a/code/game/machinery/embedded_controller/embedded_controller_base.dm +++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm @@ -29,7 +29,6 @@ . = ..() user.set_machine(src) var/datum/browser/popup = new(user, "computer", name) // Set up the popup browser window - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.set_content(return_text()) popup.open() diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index bf71786a05..ff6f96a29f 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -3,13 +3,14 @@ /obj/machinery/iv_drip name = "\improper IV drip" - desc = "An IV drip with an advanced infusion pump that can both drain blood into and inject liquids from attached containers. Blood packs are processed at an accelerated rate." + desc = "An IV drip with an advanced infusion pump that can both drain blood into and inject liquids from attached containers. Blood packs are processed at an accelerated rate. Alt-Click to change the transfer rate." icon = 'icons/obj/iv_drip.dmi' icon_state = "iv_drip" anchored = FALSE mouse_drag_pointer = MOUSE_ACTIVE_POINTER var/mob/living/carbon/attached var/mode = IV_INJECTING + var/dripfeed = FALSE var/obj/item/reagent_containers/beaker var/static/list/drip_containers = typecacheof(list(/obj/item/reagent_containers/blood, /obj/item/reagent_containers/food, @@ -132,9 +133,11 @@ if(mode) if(beaker.reagents.total_volume) var/transfer_amount = 5 + if (dripfeed) + transfer_amount = 1 if(istype(beaker, /obj/item/reagent_containers/blood)) // speed up transfer on blood packs - transfer_amount = 10 + transfer_amount *= 2 var/fraction = min(transfer_amount/beaker.reagents.total_volume, 1) //the fraction that is transfered of the total volume beaker.reagents.reaction(attached, INJECT, fraction, FALSE) //make reagents reacts, but don't spam messages beaker.reagents.trans_to(attached, transfer_amount) @@ -169,6 +172,16 @@ else toggle_mode() +/obj/machinery/iv_drip/AltClick(mob/living/user) + if(!user.canUseTopic(src, be_close=TRUE)) + return + if(dripfeed) + dripfeed = FALSE + to_chat(usr, "You loosen the valve to speed up the [src].") + else + dripfeed = TRUE + to_chat(usr, "You tighten the valve to slowly drip-feed the contents of [src].") + /obj/machinery/iv_drip/attack_robot(mob/user) if(Adjacent(user)) attack_hand(user) @@ -227,7 +240,7 @@ /obj/machinery/iv_drip/telescopic name = "telescopic IV drip" - desc = "An IV drip with an advanced infusion pump that can both drain blood into and inject liquids from attached containers. Blood packs are processed at an accelerated rate. This one is telescopic, and can be picked up and put down." + desc = "An IV drip with an advanced infusion pump that can both drain blood into and inject liquids from attached containers. Blood packs are processed at an accelerated rate. This one is telescopic, and can be picked up and put down.Alt-Click with a beaker attached to change the transfer rate." icon_state = "iv_drip" /obj/machinery/iv_drip/telescopic/update_icon_state() diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index e1f55da551..2915c2f34b 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -218,10 +218,10 @@ GLOBAL_LIST_EMPTY(allConsoles) dat += "Message Authentication

" dat += "Message for [dpt]: [message]

" dat += "
You may authenticate your message now by scanning your ID or your stamp

" - + dat += "Validated by: [msgVerified ? "[msgVerified]" : "Not Validated"]
" dat += "Stamped by: [msgStamped ? "[msgStamped]" : "Not Stamped"]

" - + dat += "Send Message

" dat += "<< Discard Message
" @@ -271,7 +271,6 @@ GLOBAL_LIST_EMPTY(allConsoles) var/datum/browser/popup = new(user, "req_console", "[department] Requests Console", 450, 440) popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.open() /obj/machinery/requests_console/Topic(href, href_list) @@ -279,7 +278,7 @@ GLOBAL_LIST_EMPTY(allConsoles) return usr.set_machine(src) add_fingerprint(usr) - + if(href_list["write"]) dpt = ckey(reject_bad_text(href_list["write"])) //write contains the string of the receiving department's name var/new_message = stripped_input(usr, "Write your message:", "Awaiting Input", "", MAX_MESSAGE_LEN) @@ -358,7 +357,7 @@ GLOBAL_LIST_EMPTY(allConsoles) workingServer = TRUE if(!workingServer) - screen = 7 + screen = 7 say("NOTICE: No server detected! Please contact your local engineering team.") updateUsrDialog() return @@ -539,7 +538,7 @@ GLOBAL_LIST_EMPTY(allConsoles) to_chat(user, "You are not authorized to send announcements!") updateUsrDialog() return - + if(istype(O, /obj/item/stamp)) if(screen == 9) var/obj/item/stamp/T = O diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm index 84b205b585..4740adaffb 100644 --- a/code/game/machinery/slotmachine.dm +++ b/code/game/machinery/slotmachine.dm @@ -132,7 +132,6 @@ var/datum/browser/popup = new(user, "slotmachine", "Slot Machine") popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) popup.open() /obj/machinery/computer/slot_machine/Topic(href, href_list) diff --git a/code/game/objects/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm index 25b262a146..b280851888 100644 --- a/code/game/objects/effects/decals/decal.dm +++ b/code/game/objects/effects/decals/decal.dm @@ -46,4 +46,4 @@ var/turf/T = loc if(!istype(T)) //you know this will happen somehow CRASH("Turf decal initialized in an object/nullspace") - T.AddElement(/datum/element/decal, icon, icon_state, turn(dir, -dir2angle(T.dir)), CLEAN_GOD, color, null, null, alpha) + T.AddElement(/datum/element/decal, icon, icon_state, dir, CLEAN_GOD, color, null, null, alpha) diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm index c89e318714..07ffe8896e 100644 --- a/code/game/objects/items/RCD.dm +++ b/code/game/objects/items/RCD.dm @@ -242,7 +242,6 @@ RLD var/datum/browser/popup = new(user, "rcd_access", "Access Control", 900, 500, src) popup.set_content(t1) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) popup.open() /obj/item/construction/rcd/Topic(href, href_list) diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index ec68cdb40c..10b2b68646 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -266,7 +266,7 @@ GLOBAL_LIST_EMPTY(PDAs) var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/simple/pda) assets.send(user) - var/datum/asset/spritesheet/emoji_s = get_asset_datum(/datum/asset/spritesheet/goonchat) + var/datum/asset/spritesheet/emoji_s = get_asset_datum(/datum/asset/spritesheet/chat) emoji_s.send(user) //Already sent by chat but no harm doing this user.set_machine(src) diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index 6eae9f127a..4ca0b86bc0 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -590,7 +590,7 @@ Code: var/static/list/emoji_icon_states var/static/emoji_table if(!emoji_table) - var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat) + var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/chat) var/list/collate = list("
") for(var/emoji in sortList(icon_states(icon('icons/emoji.dmi')))) var/tag = sheet.icon_tag("emoji-[emoji]") diff --git a/code/game/objects/items/devices/portable_chem_mixer.dm b/code/game/objects/items/devices/portable_chem_mixer.dm new file mode 100644 index 0000000000..25ecef5955 --- /dev/null +++ b/code/game/objects/items/devices/portable_chem_mixer.dm @@ -0,0 +1,207 @@ +/obj/item/storage/portable_chem_mixer + name = "Portable Chemical Mixer" + desc = "A portable device that dispenses and mixes chemicals. All necessary reagents need to be supplied with beakers. A label indicates that a screwdriver is required to open it for refills. This device can be worn on a belt. The letters 'S&T' are imprinted on the side." + icon = 'icons/obj/chemical.dmi' + icon_state = "portablechemicalmixer_open" + w_class = WEIGHT_CLASS_HUGE + slot_flags = ITEM_SLOT_BELT + custom_price = 2000 + custom_premium_price = 2000 + + var/obj/item/reagent_containers/beaker = null ///Creating an empty slot for a beaker that can be added to dispense into + var/amount = 30 ///The amount of reagent that is to be dispensed currently + + var/list/dispensable_reagents = list() ///List in which all currently dispensable reagents go + +/obj/item/storage/portable_chem_mixer/ComponentInitialize() + . = ..() + var/datum/component/storage/STR = GetComponent(/datum/component/storage) + STR.max_combined_w_class = 200 + STR.max_items = 50 + STR.insert_preposition = "in" + STR.can_hold = typecacheof(list( + /obj/item/reagent_containers/glass/beaker, + )) + +/obj/item/storage/portable_chem_mixer/Destroy() + QDEL_NULL(beaker) + return ..() + +/obj/item/storage/portable_chem_mixer/ex_act(severity, target) + if(severity < 3) + ..() + +/obj/item/storage/portable_chem_mixer/attackby(obj/item/I, mob/user, params) + var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED) + if (I.tool_behaviour == TOOL_SCREWDRIVER) + SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, !locked) + if (!locked) + update_contents() + if (locked) + replace_beaker(user) + update_icon() + I.play_tool_sound(src, 50) + return + + else if (istype(I, /obj/item/reagent_containers) && !(I.item_flags & ABSTRACT) && I.is_open_container() && locked) + var/obj/item/reagent_containers/B = I + . = TRUE //no afterattack + if(!user.transferItemToLoc(B, src)) + return + replace_beaker(user, B) + update_icon() + updateUsrDialog() + return + + return ..() + +/** + * Updates the contents of the portable chemical mixer + * + * A list of dispensable reagents is created by iterating through each source beaker in the portable chemical beaker and reading its contents + */ +/obj/item/storage/portable_chem_mixer/proc/update_contents() + dispensable_reagents.Cut() + + for (var/obj/item/reagent_containers/glass/beaker/B in contents) + var/key = B.reagents.get_master_reagent_id() + if (!(key in dispensable_reagents)) + dispensable_reagents[key] = list() + dispensable_reagents[key]["reagents"] = list() + dispensable_reagents[key]["reagents"] += B.reagents + + return + +/obj/item/storage/portable_chem_mixer/update_icon_state() + var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED) + if (!locked) + icon_state = "portablechemicalmixer_open" + else if (beaker) + icon_state = "portablechemicalmixer_full" + else + icon_state = "portablechemicalmixer_empty" + + +/obj/item/storage/portable_chem_mixer/AltClick(mob/living/user) + var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED) + if (!locked) + return ..() + if(!can_interact(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + return + replace_beaker(user) + update_icon() + +/** + * Replaces the beaker of the portable chemical mixer with another beaker, or simply adds the new beaker if none is in currently + * + * Checks if a valid user and a valid new beaker exist and attempts to replace the current beaker in the portable chemical mixer with the one in hand. Simply places the new beaker in if no beaker is currently loaded + * Arguments: + * * mob/living/user - The user who is trying to exchange beakers + * * obj/item/reagent_containers/new_beaker - The new beaker that the user wants to put into the device + */ +/obj/item/storage/portable_chem_mixer/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker) + if(!user) + return FALSE + if(beaker) + user.put_in_hands(beaker) + beaker = null + if(new_beaker) + beaker = new_beaker + return TRUE + +/obj/item/storage/portable_chem_mixer/attack_hand(mob/user) + if (loc != user) + return ..() + if(SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)) + ui_interact(user) + return + +/obj/item/storage/portable_chem_mixer/attack_self(mob/user) + if(loc == user) + var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED) + if (locked) + ui_interact(user) + return + else + to_chat(user, "The portable chemical mixer is currently open and its contents can be accessed.") + return + return + +/obj/item/storage/portable_chem_mixer/MouseDrop(obj/over_object) + . = ..() + if(ismob(loc)) + var/mob/M = loc + if(!M.incapacitated() && istype(over_object, /obj/screen/inventory/hand)) + var/obj/screen/inventory/hand/H = over_object + M.putItemFromInventoryInHandIfPossible(src, H.held_index) + +/obj/item/storage/portable_chem_mixer/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PortableChemMixer", name) + if(user.hallucinating()) + // to not ruin the immersion by constantly changing the fake chemicals + ui.set_autoupdate(FALSE) + ui.open() + +/obj/item/storage/portable_chem_mixer/ui_data(mob/user) + var/list/data = list() + data["amount"] = amount + data["isBeakerLoaded"] = beaker ? 1 : 0 + data["beakerCurrentVolume"] = beaker ? beaker.reagents.total_volume : null + data["beakerMaxVolume"] = beaker ? beaker.volume : null + data["beakerTransferAmounts"] = beaker ? beaker.possible_transfer_amounts : null + var/chemicals[0] + var/is_hallucinating = user.hallucinating() + if(user.hallucinating()) + is_hallucinating = TRUE + for(var/re in dispensable_reagents) + var/value = dispensable_reagents[re] + var/datum/reagent/temp = GLOB.chemical_reagents_list[re] + if(temp) + var/chemname = temp.name + var/total_volume = 0 + for (var/datum/reagents/rs in value["reagents"]) + total_volume += rs.total_volume + if(is_hallucinating && prob(5)) + chemname = "[pick_list_replacements("hallucination.json", "chemicals")]" + chemicals.Add(list(list("title" = chemname, "id" = ckey(temp.name), "volume" = total_volume ))) + data["chemicals"] = chemicals + var/beakerContents[0] + if(beaker) + for(var/datum/reagent/R in beaker.reagents.reagent_list) + beakerContents.Add(list(list("name" = R.name, "id" = ckey(R.name), "volume" = R.volume))) // list in a list because Byond merges the first list... + data["beakerContents"] = beakerContents + return data + +/obj/item/storage/portable_chem_mixer/ui_act(action, params) + if(..()) + return + switch(action) + if("amount") + var/target = text2num(params["target"]) + amount = target + . = TRUE + if("dispense") + var/reagent_name = params["reagent"] + var/datum/reagent/reagent = GLOB.name2reagent[reagent_name] + var/entry = dispensable_reagents[reagent] + if(beaker) + var/datum/reagents/R = beaker.reagents + var/actual = min(amount, 1000, R.maximum_volume - R.total_volume) + // todo: add check if we have enough reagent left + for (var/datum/reagents/source in entry["reagents"]) + var/to_transfer = min(source.total_volume, actual) + source.trans_to(beaker, to_transfer) + actual -= to_transfer + if (actual <= 0) + break + . = TRUE + if("remove") + var/amount = text2num(params["amount"]) + beaker.reagents.remove_all(amount) + . = TRUE + if("eject") + replace_beaker(usr) + update_icon() + . = TRUE diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm index e65325587c..c51edc8106 100644 --- a/code/game/objects/items/devices/radio/electropack.dm +++ b/code/game/objects/items/devices/radio/electropack.dm @@ -16,9 +16,6 @@ var/on = TRUE var/shock_cooldown = FALSE - var/ui_x = 260 - var/ui_y = 137 - /obj/item/electropack/suicide_act(mob/living/carbon/user) user.visible_message("[user] hooks [user.p_them()]self to the electropack and spams the trigger! It looks like [user.p_theyre()] trying to commit suicide!") return (FIRELOSS) @@ -201,17 +198,7 @@ else return ..() -/obj/item/electropack/shockcollar/ui_interact(mob/user) //note to src: use tgooey - var/dat = {" - -Frequency/Code for shock collar:
-Frequency: -[format_frequency(src.frequency)] -Set
-Code: -[src.code] -Set
-
"} - user << browse(dat, "window=radio") - onclose(user, "radio") - return +/obj/item/electropack/ui_act(action, params) + if(action == "power") // DO. NOT. + return FALSE + return ..() diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index bb1c63cf8d..3f2dc2506a 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -296,7 +296,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK w_class = WEIGHT_CLASS_BULKY - force = 9 + force = 7 //how much harm mode damage we do + var/stamina_damage_increment = 4 //how much extra damage do we do when in non-harm mode throwforce = 10 damtype = STAMINA attack_verb = list("whacked", "smacked", "struck") @@ -347,17 +348,19 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/melee/bokken/Initialize() . = ..() AddElement(/datum/element/sword_point) + if(!harm) //if initialised in non-harm mode, setup force accordingly + force = force + stamina_damage_increment /obj/item/melee/bokken/attack_self(mob/user) harm = !harm if(harm) - force -= 2 + force -= stamina_damage_increment damtype = BRUTE attack_verb = list("bashed", "smashed", "attacked") bare_wound_bonus = 15 // having your leg smacked by a wooden stick is probably not great for it if it's naked wound_bonus = 0 else - force += 2 + force += stamina_damage_increment damtype = STAMINA attack_verb = list("whacked", "smacked", "struck") bare_wound_bonus = 0 diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index f5003e035b..bcdf509fbe 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -143,10 +143,18 @@ var/amt = max(0, ((force - (move_resist * MOVE_FORCE_CRUSH_RATIO)) / (move_resist * MOVE_FORCE_CRUSH_RATIO)) * 10) take_damage(amt, BRUTE) +#define BLACKLISTED_OBJECTS list(/obj/machinery/power/apc, /obj/machinery/airalarm, /obj/machinery/power/smes, /obj/structure/cable) + /obj/attack_slime(mob/living/simple_animal/slime/user) if(!user.is_adult) return - attack_generic(user, rand(10, 15), "melee", 1) + if(src.type in BLACKLISTED_OBJECTS) + return + if(istype(src, /obj/machinery/atmospherics)) + return + attack_generic(user, rand(10, 15), BRUTE, "melee", 1) + +#undef BLACKLISTED_OBJECTS /obj/mech_melee_attack(obj/mecha/M) M.do_attack_animation(src) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 99b302e9ce..3abee4db33 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -304,18 +304,20 @@ /obj/proc/reskin_obj(mob/M) if(!LAZYLEN(unique_reskin)) return - var/dat = "Reskin options for [name]:\n" - for(var/V in unique_reskin) - var/output = icon2html(src, M, unique_reskin[V]) - dat += "[V]: [output]\n" - to_chat(M, dat) - - var/choice = input(M, always_reskinnable ? "Choose the a reskin for [src]" : "Warning, you can only reskin [src] once!","Reskin Object") as null|anything in unique_reskin - if(QDELETED(src) || !choice || (current_skin && !always_reskinnable) || M.incapacitated() || !in_range(M,src) || !unique_reskin[choice] || unique_reskin[choice] == current_skin) - return - current_skin = choice + var/list/skins = list() + for(var/S in unique_reskin) + skins[S] = image(icon = icon, icon_state = unique_reskin[S]) + var/choice = show_radial_menu(M, src, skins, custom_check = CALLBACK(src, .proc/check_skinnable, M), radius = 40, require_near = TRUE) + if(!choice) + return FALSE icon_state = unique_reskin[choice] - to_chat(M, "[src] is now skinned as '[choice]'.") + current_skin = choice + return + +/obj/proc/check_skinnable(/mob/M) + if(current_skin || !always_reskinnable) + return FALSE + return TRUE /obj/update_overlays() . = ..() diff --git a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm index beb14ff48c..d127a87b20 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm @@ -4,25 +4,6 @@ req_access = list(ACCESS_ALL_PERSONAL_LOCKERS) var/registered_name = null -/obj/structure/closet/secure_closet/personal/examine(mob/user) - . = ..() - if(registered_name) - . += "The display reads, \"Owned by [registered_name]\"." - -/obj/structure/closet/secure_closet/personal/check_access(obj/item/I) - . = ..() - if(!I || !istype(I)) - return - if(istype(I,/obj/item/modular_computer/tablet)) - var/obj/item/modular_computer/tablet/ourTablet = I - var/obj/item/computer_hardware/card_slot/card_slot = ourTablet.all_components[MC_CARD] - if(card_slot) - return registered_name == card_slot.stored_card.registered_name || registered_name == card_slot.stored_card2.registered_name - var/obj/item/card/id/ID = I.GetID() - if(ID && registered_name == ID.registered_name) - return TRUE - return FALSE - /obj/structure/closet/secure_closet/personal/PopulateContents() ..() if(prob(50)) @@ -54,15 +35,24 @@ /obj/structure/closet/secure_closet/personal/attackby(obj/item/W, mob/user, params) var/obj/item/card/id/I = W.GetID() - if(!I || !istype(I)) - return ..() - if(!can_lock(user, FALSE)) //Can't do anything if there isn't a lock! - return - if(I.registered_name && !registered_name) - to_chat(user, "You claim [src].") - registered_name = I.registered_name + if(istype(I)) + if(broken) + to_chat(user, "It appears to be broken.") + return + if(!I || !I.registered_name) + return + if(allowed(user) || !registered_name || (istype(I) && (registered_name == I.registered_name))) + //they can open all lockers, or nobody owns this, or they own this locker + locked = !locked + update_icon() + + if(!registered_name) + registered_name = I.registered_name + desc = "Owned by [I.registered_name]." + else + to_chat(user, "Access Denied.") else - ..() + return ..() /obj/structure/closet/secure_closet/personal/handle_lock_addition() //If lock construction is successful we don't care what access the electronics had, so we override it if(..()) diff --git a/code/game/sound.dm b/code/game/sound.dm index 0feb01c223..b012ba4e2a 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -130,6 +130,10 @@ /proc/get_sfx(soundin) if(istext(soundin)) switch(soundin) + if ("explosion_creaking") // from skyrat-ss13/skyrat13/pull/3295 + soundin = pick('sound/effects/explosioncreak1.ogg', 'sound/effects/explosioncreak2.ogg') + if ("hull_creaking") // from skyrat-ss13/skyrat13/pull/3295 + soundin = pick('sound/effects/creak1.ogg', 'sound/effects/creak2.ogg', 'sound/effects/creak3.ogg') if ("shatter") soundin = pick('sound/effects/glassbr1.ogg','sound/effects/glassbr2.ogg','sound/effects/glassbr3.ogg') if ("explosion") diff --git a/code/modules/NTNet/network.dm b/code/modules/NTNet/network.dm index d86ad792fe..2a2820d289 100644 --- a/code/modules/NTNet/network.dm +++ b/code/modules/NTNet/network.dm @@ -206,7 +206,7 @@ for(var/datum/ntnet_conversation/chan in chat_channels) if(chan.id == id) return chan - + // Resets the IDS alarm /datum/ntnet/proc/resetIDS() intrusion_detection_alarm = FALSE diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 8cfae3820a..8421d9955d 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -1,12 +1,12 @@ //////////////////////////////// /proc/message_admins(msg) - msg = "ADMIN LOG: [msg]" - to_chat(GLOB.admins, msg) + msg = "ADMIN LOG: [msg]" + to_chat(GLOB.admins, msg, confidential = TRUE) /proc/relay_msg_admins(msg) - msg = "RELAY: [msg]" - to_chat(GLOB.admins, msg) + msg = "RELAY: [msg]" + to_chat(GLOB.admins, msg, confidential = TRUE) ///////////////////////////////////////////////////////////////////////////////////////////////Panels @@ -22,7 +22,7 @@ log_admin("[key_name(usr)] checked the individual player panel for [key_name(M)][isobserver(usr)?"":" while in game"].") if(!M) - to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.") + to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.", confidential = TRUE) return var/body = "Options for [M.key]" @@ -65,6 +65,7 @@ body += "VV - " if(M.mind) body += "TP - " + // body += "SKILLS - " else body += "Init Mind - " if (iscyborg(M)) @@ -122,6 +123,7 @@ body += "Traitor panel | " body += "Narrate to | " body += "Subtle message | " + // body += "Play sound to | " body += "Language Menu" if (M.client) @@ -216,7 +218,7 @@ if (!istype(src, /datum/admins)) src = usr.client.holder if (!istype(src, /datum/admins)) - to_chat(usr, "Error: you are not an admin!") + to_chat(usr, "Error: you are not an admin!", confidential = TRUE) return var/dat dat = text("Admin Newscaster

Admin Newscaster Unit

") @@ -242,7 +244,7 @@ dat+="

The newscaster recognises you as:
[src.admin_signature]
" if(1) dat+= "Station Feed Channels
" - if( isemptylist(GLOB.news_network.network_channels) ) + if( !length(GLOB.news_network.network_channels) ) dat+="No active channels found..." else for(var/datum/news/feed_channel/CHANNEL in GLOB.news_network.network_channels) @@ -295,7 +297,7 @@ dat+="ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.
" dat+="No further feed story additions are allowed while the D-Notice is in effect.

" else - if( isemptylist(src.admincaster_feed_channel.messages) ) + if( !length(src.admincaster_feed_channel.messages) ) dat+="No feed messages found in channel...
" else var/i = 0 @@ -317,7 +319,7 @@ dat+="NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.
" dat+="Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it.
" dat+="
Select Feed channel to get Stories from:
" - if(isemptylist(GLOB.news_network.network_channels)) + if(!length(GLOB.news_network.network_channels)) dat+="No feed channels found active...
" else for(var/datum/news/feed_channel/CHANNEL in GLOB.news_network.network_channels) @@ -328,7 +330,7 @@ dat+="A D-Notice is to be bestowed upon the channel if the handling Authority deems it as harmful for the station's" dat+="morale, integrity or disciplinary behaviour. A D-Notice will render a channel unable to be updated by anyone, without deleting any feed" dat+="stories it might contain at the time. You can lift a D-Notice if you have the required access at any time.
" - if(isemptylist(GLOB.news_network.network_channels)) + if(!length(GLOB.news_network.network_channels)) dat+="No feed channels found active...
" else for(var/datum/news/feed_channel/CHANNEL in GLOB.news_network.network_channels) @@ -339,7 +341,7 @@ dat+="[src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.returnAuthor(-1)] \]
" dat+="[(src.admincaster_feed_channel.authorCensor) ? ("Undo Author censorship") : ("Censor channel Author")]
" - if( isemptylist(src.admincaster_feed_channel.messages) ) + if( !length(src.admincaster_feed_channel.messages) ) dat+="No feed messages found in channel...
" else for(var/datum/news/feed_message/MESSAGE in src.admincaster_feed_channel.messages) @@ -356,7 +358,7 @@ dat+="ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.
" dat+="No further feed story additions are allowed while the D-Notice is in effect.

" else - if( isemptylist(src.admincaster_feed_channel.messages) ) + if( !length(src.admincaster_feed_channel.messages) ) dat+="No feed messages found in channel...
" else for(var/datum/news/feed_message/MESSAGE in src.admincaster_feed_channel.messages) @@ -426,7 +428,6 @@ "} if(GLOB.master_mode == "secret") dat += "(Force Secret Mode)
" - if(GLOB.master_mode == "dynamic") if(SSticker.current_state <= GAME_STATE_PREGAME) dat += "(Force Roundstart Rulesets)
" @@ -449,7 +450,6 @@ dat += "
" if(SSticker.IsRoundInProgress()) dat += "(Game Mode Panel)
" - dat += {"
Create Object
@@ -461,7 +461,7 @@ if(marked_datum && istype(marked_datum, /atom)) dat += "Duplicate Marked Datum
" - usr << browse(dat, "window=admin2;size=210x200") + usr << browse(dat, "window=admin2;size=240x280") return /////////////////////////////////////////////////////////////////////////////////////////////////admins2.dm merge @@ -475,33 +475,42 @@ if (!usr.client.holder) return - var/list/options = list("Regular Restart", "Hard Restart (No Delay/Feeback Reason)", "Hardest Restart (No actions, just reboot)") + var/localhost_addresses = list("127.0.0.1", "::1") + var/list/options = list("Regular Restart", "Regular Restart (with delay)", "Hard Restart (No Delay/Feeback Reason)", "Hardest Restart (No actions, just reboot)") if(world.TgsAvailable()) options += "Server Restart (Kill and restart DD)"; - var/rebootconfirm if(SSticker.admin_delay_notice) - if(alert(usr, "Are you sure? An admin has already delayed the round end for the following reason: [SSticker.admin_delay_notice]", "Confirmation", "Yes", "No") == "Yes") - rebootconfirm = TRUE - else - rebootconfirm = TRUE - if(rebootconfirm) - var/result = input(usr, "Select reboot method", "World Reboot", options[1]) as null|anything in options - if(result) - SSblackbox.record_feedback("tally", "admin_verb", 1, "Reboot World") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - var/init_by = "Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]." - switch(result) - if("Regular Restart") - SSticker.Reboot(init_by, "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", 10) - if("Hard Restart (No Delay, No Feeback Reason)") - to_chat(world, "World reboot - [init_by]") - world.Reboot() - if("Hardest Restart (No actions, just reboot)") - to_chat(world, "Hard world reboot - [init_by]") - world.Reboot(fast_track = TRUE) - if("Server Restart (Kill and restart DD)") - to_chat(world, "Server restart - [init_by]") - world.TgsEndProcess() + if(alert(usr, "Are you sure? An admin has already delayed the round end for the following reason: [SSticker.admin_delay_notice]", "Confirmation", "Yes", "No") != "Yes") + return FALSE + + var/result = input(usr, "Select reboot method", "World Reboot", options[1]) as null|anything in options + if(result) + SSblackbox.record_feedback("tally", "admin_verb", 1, "Reboot World") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + var/init_by = "Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]." + switch(result) + if("Regular Restart") + if(!(isnull(usr.client.address) || (usr.client.address in localhost_addresses))) + if(alert("Are you sure you want to restart the server?","This server is live","Restart","Cancel") != "Restart") + return FALSE + SSticker.Reboot(init_by, "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", 10) + if("Regular Restart (with delay)") + var/delay = input("What delay should the restart have (in seconds)?", "Restart Delay", 5) as num|null + if(!delay) + return FALSE + if(!(isnull(usr.client.address) || (usr.client.address in localhost_addresses))) + if(alert("Are you sure you want to restart the server?","This server is live","Restart","Cancel") != "Restart") + return FALSE + SSticker.Reboot(init_by, "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", delay * 10) + if("Hard Restart (No Delay, No Feeback Reason)") + to_chat(world, "World reboot - [init_by]") + world.Reboot() + if("Hardest Restart (No actions, just reboot)") + to_chat(world, "Hard world reboot - [init_by]") + world.Reboot(fast_track = TRUE) + if("Server Restart (Kill and restart DD)") + to_chat(world, "Server restart - [init_by]") + world.TgsEndProcess() /datum/admins/proc/end_round() set category = "Server" @@ -529,7 +538,7 @@ if(message) if(!check_rights(R_SERVER,0)) message = adminscrub(message,500) - to_chat(world, "[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:\n \t [message]") + to_chat(world, "[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:\n \t [message]", confidential = TRUE) log_admin("Announce: [key_name(usr)] : [message]") SSblackbox.record_feedback("tally", "admin_verb", 1, "Announce") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -551,7 +560,7 @@ else message_admins("[key_name(usr)] set the admin notice.") log_admin("[key_name(usr)] set the admin notice:\n[new_admin_notice]") - to_chat(world, "Admin Notice:\n \t [new_admin_notice]") + to_chat(world, "Admin Notice:\n \t [new_admin_notice]", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Set Admin Notice") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! GLOB.admin_notice = new_admin_notice return @@ -598,20 +607,29 @@ set desc="Start the round RIGHT NOW" set name="Start Now" if(SSticker.current_state == GAME_STATE_PREGAME || SSticker.current_state == GAME_STATE_STARTUP) - SSticker.start_immediately = TRUE - log_admin("[usr.key] has started the game.") - var/msg = "" - if(SSticker.current_state == GAME_STATE_STARTUP) - msg = " (The server is still setting up, but the round will be \ - started as soon as possible.)" - message_admins("\ - [usr.key] has started the game.[msg]") - SSblackbox.record_feedback("tally", "admin_verb", 1, "Start Now") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return 1 + if(!SSticker.start_immediately) + var/localhost_addresses = list("127.0.0.1", "::1") + if(!(isnull(usr.client.address) || (usr.client.address in localhost_addresses))) + if(alert("Are you sure you want to start the round?","Start Now","Start Now","Cancel") != "Start Now") + return FALSE + SSticker.start_immediately = TRUE + log_admin("[usr.key] has started the game.") + var/msg = "" + if(SSticker.current_state == GAME_STATE_STARTUP) + msg = " (The server is still setting up, but the round will be \ + started as soon as possible.)" + message_admins("[usr.key] has started the game.[msg]") + SSblackbox.record_feedback("tally", "admin_verb", 1, "Start Now") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return TRUE + SSticker.start_immediately = FALSE + SSticker.SetTimeLeft(1800) + to_chat(world, "The game will start in 180 seconds.") + SEND_SOUND(world, sound(get_announcer_sound("attention"))) + message_admins("[usr.key] has cancelled immediate game start. Game will start in 180 seconds.") + log_admin("[usr.key] has cancelled immediate game start.") else to_chat(usr, "Error: Start Now: Game has already started.") - - return 0 + return FALSE /datum/admins/proc/toggleenter() set category = "Server" @@ -619,9 +637,9 @@ set name="Toggle Entering" GLOB.enter_allowed = !( GLOB.enter_allowed ) if (!( GLOB.enter_allowed )) - to_chat(world, "New players may no longer enter the game.") + to_chat(world, "New players may no longer enter the game.", confidential = TRUE) else - to_chat(world, "New players may now enter the game.") + to_chat(world, "New players may now enter the game.", confidential = TRUE) log_admin("[key_name(usr)] toggled new player game entering.") message_admins("[key_name_admin(usr)] toggled new player game entering.") world.update_status() @@ -634,9 +652,9 @@ var/alai = CONFIG_GET(flag/allow_ai) CONFIG_SET(flag/allow_ai, !alai) if (alai) - to_chat(world, "The AI job is no longer chooseable.") + to_chat(world, "The AI job is no longer chooseable.", confidential = TRUE) else - to_chat(world, "The AI job is chooseable now.") + to_chat(world, "The AI job is chooseable now.", confidential = TRUE) log_admin("[key_name(usr)] toggled AI allowed.") world.update_status() SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle AI", "[!alai ? "Disabled" : "Enabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -654,7 +672,7 @@ aiPlayer.end_multicam() log_admin("[key_name(usr)] toggled AI multicam.") world.update_status() - to_chat(GLOB.ai_list | GLOB.admins, "The AI [almcam ? "no longer" : "now"] has multicam.") + to_chat(GLOB.ai_list | GLOB.admins, "The AI [almcam ? "no longer" : "now"] has multicam.", confidential = TRUE) SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Multicam", "[!almcam ? "Disabled" : "Enabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /datum/admins/proc/toggleaban() @@ -664,9 +682,9 @@ var/new_nores = !CONFIG_GET(flag/norespawn) CONFIG_SET(flag/norespawn, new_nores) if (!new_nores) - to_chat(world, "You may now respawn.") + to_chat(world, "You may now respawn.", confidential = TRUE) else - to_chat(world, "You may no longer respawn :(") + to_chat(world, "You may no longer respawn :(", confidential = TRUE) message_admins("[key_name_admin(usr)] toggled respawn to [!new_nores ? "On" : "Off"].") log_admin("[key_name(usr)] toggled respawn to [!new_nores ? "On" : "Off"].") world.update_status() @@ -675,7 +693,7 @@ /datum/admins/proc/delay() set category = "Server" set desc="Delay the game start" - set name="Delay pre-game" + set name="Delay Pre-Game" var/newtime = input("Set a new time in seconds. Set -1 for indefinite delay.","Set Delay",round(SSticker.GetTimeLeft()/10)) as num|null if(SSticker.current_state > GAME_STATE_PREGAME) @@ -683,11 +701,12 @@ if(newtime) newtime = newtime*10 SSticker.SetTimeLeft(newtime) + SSticker.start_immediately = FALSE if(newtime < 0) - to_chat(world, "The game start has been delayed.") + to_chat(world, "The game start has been delayed.", confidential = TRUE) log_admin("[key_name(usr)] delayed the round start.") else - to_chat(world, "The game will start in [DisplayTimeText(newtime)].") + to_chat(world, "The game will start in [DisplayTimeText(newtime)].", confidential = TRUE) SEND_SOUND(world, sound(get_announcer_sound("attention"))) log_admin("[key_name(usr)] set the pre-game delay to [DisplayTimeText(newtime)].") SSblackbox.record_feedback("tally", "admin_verb", 1, "Delay Game Start") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -724,20 +743,28 @@ set desc = "(atom path) Spawn an atom" set name = "Spawn" - if(!check_rights(R_SPAWN)) + if(!check_rights(R_SPAWN) || !object) + return + + var/list/preparsed = splittext(object,":") + var/path = preparsed[1] + var/amount = 1 + if(preparsed.len > 1) + amount = clamp(text2num(preparsed[2]),1, 50) //50 at a time! + + var/chosen = pick_closest_path(path) + if(!chosen) return var/turf/T = get_turf(usr) - var/chosen = pick_closest_path(object) - if(!chosen) - return if(ispath(chosen, /turf)) T.ChangeTurf(chosen) else - var/atom/A = new chosen(T) - A.flags_1 |= ADMIN_SPAWNED_1 + for(var/i in 1 to amount) + var/atom/A = new chosen(T) + A.flags_1 |= ADMIN_SPAWNED_1 - log_admin("[key_name(usr)] spawned [chosen] at [AREACOORD(usr)]") + log_admin("[key_name(usr)] spawned [amount] x [chosen] at [AREACOORD(usr)]") SSblackbox.record_feedback("tally", "admin_verb", 1, "Spawn Atom") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /datum/admins/proc/podspawn_atom(object as text) @@ -782,20 +809,18 @@ log_admin("[key_name(usr)] spawned cargo pack [chosen] at [AREACOORD(usr)]") SSblackbox.record_feedback("tally", "admin_verb", 1, "Spawn Cargo") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/show_traitor_panel(mob/M in GLOB.mob_list) +/datum/admins/proc/show_traitor_panel(mob/target_mob in GLOB.mob_list) set category = "Admin" set desc = "Edit mobs's memory and role" set name = "Show Traitor Panel" - - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") + var/datum/mind/target_mind = target_mob.mind + if(!target_mind) + to_chat(usr, "This mob has no mind!", confidential = TRUE) return - if(!M.mind) - to_chat(usr, "This mob has no mind!") + if(!istype(target_mob) && !istype(target_mind)) + to_chat(usr, "This can only be used on instances of type /mob and /mind", confidential = TRUE) return - - M.mind.traitor_panel() + target_mind.traitor_panel() SSblackbox.record_feedback("tally", "admin_verb", 1, "Traitor Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -805,9 +830,9 @@ set name="Toggle tinted welding helmes" GLOB.tinted_weldhelh = !( GLOB.tinted_weldhelh ) if (GLOB.tinted_weldhelh) - to_chat(world, "The tinted_weldhelh has been enabled!") + to_chat(world, "The tinted_weldhelh has been enabled!", confidential = TRUE) else - to_chat(world, "The tinted_weldhelh has been disabled!") + to_chat(world, "The tinted_weldhelh has been disabled!", confidential = TRUE) log_admin("[key_name(usr)] toggled tinted_weldhelh.") message_admins("[key_name_admin(usr)] toggled tinted_weldhelh.") SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Tinted Welding Helmets", "[GLOB.tinted_weldhelh ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -819,9 +844,9 @@ var/new_guest_ban = !CONFIG_GET(flag/guest_ban) CONFIG_SET(flag/guest_ban, new_guest_ban) if (new_guest_ban) - to_chat(world, "Guests may no longer enter the game.") + to_chat(world, "Guests may no longer enter the game.", confidential = TRUE) else - to_chat(world, "Guests may now enter the game.") + to_chat(world, "Guests may now enter the game.", confidential = TRUE) log_admin("[key_name(usr)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed.") message_admins("[key_name_admin(usr)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed.") SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Guests", "[!new_guest_ban ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -832,37 +857,37 @@ var/mob/living/silicon/S = i ai_number++ if(isAI(S)) - to_chat(usr, "AI [key_name(S, usr)]'s laws:") + to_chat(usr, "AI [key_name(S, usr)]'s laws:", confidential = TRUE) else if(iscyborg(S)) var/mob/living/silicon/robot/R = S - to_chat(usr, "CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [key_name(R.connected_ai)])":"(Independent)"]: laws:") + to_chat(usr, "CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [key_name(R.connected_ai)])":"(Independent)"]: laws:", confidential = TRUE) else if (ispAI(S)) - to_chat(usr, "pAI [key_name(S, usr)]'s laws:") + to_chat(usr, "pAI [key_name(S, usr)]'s laws:", confidential = TRUE) else - to_chat(usr, "SOMETHING SILICON [key_name(S, usr)]'s laws:") + to_chat(usr, "SOMETHING SILICON [key_name(S, usr)]'s laws:", confidential = TRUE) if (S.laws == null) - to_chat(usr, "[key_name(S, usr)]'s laws are null?? Contact a coder.") + to_chat(usr, "[key_name(S, usr)]'s laws are null?? Contact a coder.", confidential = TRUE) else S.laws.show_laws(usr) if(!ai_number) - to_chat(usr, "No AIs located" ) + to_chat(usr, "No AIs located" , confidential = TRUE) /datum/admins/proc/output_all_devil_info() var/devil_number = 0 for(var/datum/mind/D in SSticker.mode.devils) devil_number++ var/datum/antagonist/devil/devil = D.has_antag_datum(/datum/antagonist/devil) - to_chat(usr, "Devil #[devil_number]:

" + devil.printdevilinfo()) + to_chat(usr, "Devil #[devil_number]:

" + devil.printdevilinfo(), confidential = TRUE) if(!devil_number) - to_chat(usr, "No Devils located" ) + to_chat(usr, "No Devils located" , confidential = TRUE) /datum/admins/proc/output_devil_info(mob/living/M) if(is_devil(M)) var/datum/antagonist/devil/devil = M.mind.has_antag_datum(/datum/antagonist/devil) - to_chat(usr, devil.printdevilinfo()) + to_chat(usr, devil.printdevilinfo(), confidential = TRUE) else - to_chat(usr, "[M] is not a devil.") + to_chat(usr, "[M] is not a devil.", confidential = TRUE) /datum/admins/proc/manage_free_slots() if(!check_rights()) @@ -965,21 +990,21 @@ if(kick_only_afk && !C.is_afk()) //Ignore clients who are not afk continue if(message) - to_chat(C, message) + to_chat(C, message, confidential = TRUE) kicked_client_names.Add("[C.key]") qdel(C) return kicked_client_names -//returns 1 to let the dragdrop code know we are trapping this event -//returns 0 if we don't plan to trap the event +//returns TRUE to let the dragdrop code know we are trapping this event +//returns FALSE if we don't plan to trap the event /datum/admins/proc/cmd_ghost_drag(mob/dead/observer/frommob, mob/tomob) //this is the exact two check rights checks required to edit a ckey with vv. if (!check_rights(R_VAREDIT,0) || !check_rights(R_SPAWN|R_DEBUG,0)) - return 0 + return FALSE if (!frommob.ckey) - return 0 + return FALSE var/question = "" if (tomob.ckey) @@ -988,12 +1013,18 @@ var/ask = alert(question, "Place ghost in control of mob?", "Yes", "No") if (ask != "Yes") - return 1 + return TRUE if (!frommob || !tomob) //make sure the mobs don't go away while we waited for a response - return 1 + return TRUE - tomob.ghostize(0) + // Disassociates observer mind from the body mind + if(tomob.client) + tomob.ghostize(FALSE) + else + for(var/mob/dead/observer/ghost in GLOB.dead_mob_list) + if(tomob.mind == ghost.mind) + ghost.mind = null message_admins("[key_name_admin(usr)] has put [frommob.key] in control of [tomob.name].") log_admin("[key_name(usr)] stuffed [frommob.key] into [tomob.name].") @@ -1002,7 +1033,7 @@ tomob.ckey = frommob.ckey qdel(frommob) - return 1 + return TRUE /client/proc/adminGreet(logout) if(SSticker.HasRoundStarted()) diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm index 99bb988be6..7e5c104d90 100644 --- a/code/modules/admin/admin_investigate.dm +++ b/code/modules/admin/admin_investigate.dm @@ -4,19 +4,39 @@ var/F = file("[GLOB.log_directory]/[subject].html") WRITE_FILE(F, "[TIME_STAMP("hh:mm:ss", FALSE)] [REF(src)] ([x],[y],[z]) || [src] [message]
") -/client/proc/investigate_show(subject in list("notes, memos, watchlist", INVESTIGATE_RCD, INVESTIGATE_RESEARCH, INVESTIGATE_EXONET, INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS, INVESTIGATE_RADIATION, INVESTIGATE_CIRCUIT, INVESTIGATE_NANITES, INVESTIGATE_CRYOGENICS) ) +/client/proc/investigate_show() set name = "Investigate" set category = "Admin" if(!holder) return - switch(subject) - if("notes, memos, watchlist") - if(!check_rights(R_ADMIN)) - return - browse_messages() + + var/list/investigates = list(INVESTIGATE_RCD, INVESTIGATE_RESEARCH, INVESTIGATE_EXONET, INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS, INVESTIGATE_RADIATION, INVESTIGATE_CIRCUIT, INVESTIGATE_NANITES, INVESTIGATE_CRYOGENICS) + + var/list/logs_present = list("notes, memos, watchlist") + var/list/logs_missing = list("---") + + for(var/subject in investigates) + var/temp_file = file("[GLOB.log_directory]/[subject].html") + if(fexists(temp_file)) + logs_present += subject else - var/F = file("[GLOB.log_directory]/[subject].html") - if(!fexists(F)) - to_chat(src, "No [subject] logfile was found.") - return - src << browse(F,"window=investigate[subject];size=800x300") + logs_missing += "[subject] (empty)" + + var/list/combined = sortList(logs_present) + sortList(logs_missing) + + var/selected = input("Investigate what?", "Investigate") as null|anything in combined + + if(!(selected in combined) || selected == "---") + return + + selected = replacetext(selected, " (empty)", "") + + if(selected == "notes, memos, watchlist" && check_rights(R_ADMIN)) + browse_messages() + return + + var/F = file("[GLOB.log_directory]/[selected].html") + if(!fexists(F)) + to_chat(src, "No [selected] logfile was found.", confidential = TRUE) + return + src << browse(F,"window=investigate[selected];size=800x300") diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm index 525e4e82c0..257c1d275a 100644 --- a/code/modules/admin/verbs/adminjump.dm +++ b/code/modules/admin/verbs/adminjump.dm @@ -3,7 +3,7 @@ set desc = "Area to jump to" set category = "Admin" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return if(!A) @@ -15,20 +15,22 @@ continue turfs.Add(T) - var/turf/T = safepick(turfs) - if(!T) - to_chat(src, "Nowhere to jump to!") + if(length(turfs)) + var/turf/T = pick(turfs) + usr.forceMove(T) + log_admin("[key_name(usr)] jumped to [AREACOORD(T)]") + message_admins("[key_name_admin(usr)] jumped to [AREACOORD(T)]") + SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Area") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + else + to_chat(src, "Nowhere to jump to!", confidential = TRUE) return - usr.forceMove(T) - log_admin("[key_name(usr)] jumped to [AREACOORD(A)]") - message_admins("[key_name_admin(usr)] jumped to [AREACOORD(A)]") - SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Area") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + /client/proc/jumptoturf(turf/T in world) set name = "Jump to Turf" set category = "Admin" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return log_admin("[key_name(usr)] jumped to [AREACOORD(T)]") @@ -42,7 +44,7 @@ set name = "Jump to Mob" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return log_admin("[key_name(usr)] jumped to [key_name(M)]") @@ -54,14 +56,14 @@ SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! A.forceMove(M.loc) else - to_chat(A, "This mob is not located in the game world.") + to_chat(A, "This mob is not located in the game world.", confidential = TRUE) /client/proc/jumptocoord(tx as num, ty as num, tz as num) set category = "Admin" set name = "Jump to Coordinate" if (!holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return if(src.mob) @@ -76,7 +78,7 @@ set name = "Jump to Key" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return var/list/keys = list() @@ -84,7 +86,7 @@ keys += M.client var/client/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys) if(!selection) - to_chat(src, "No keys found.") + to_chat(src, "No keys found.", confidential = TRUE) return var/mob/M = selection.mob log_admin("[key_name(usr)] jumped to [key_name(M)]") @@ -99,7 +101,7 @@ set name = "Get Mob" set desc = "Mob to teleport" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return var/atom/loc = get_turf(usr) @@ -116,7 +118,7 @@ set desc = "Key to teleport" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return var/list/keys = list() @@ -142,16 +144,17 @@ set category = "Admin" set name = "Send Mob" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return var/area/A = input(usr, "Pick an area.", "Pick an area") in GLOB.sortedAreas|null if(A && istype(A)) - if(M.forceMove(safepick(get_area_turfs(A)))) + var/list/turfs = get_area_turfs(A) + if(length(turfs) && M.forceMove(pick(turfs))) - log_admin("[key_name(usr)] teleported [key_name(M)] to [AREACOORD(A)]") - var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)] to [AREACOORD(A)]" + log_admin("[key_name(usr)] teleported [key_name(M)] to [AREACOORD(M)]") + var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)] to [AREACOORD(M)]" message_admins(msg) admin_ticket_log(M, msg) else - to_chat(src, "Failed to move mob to a valid location.") + to_chat(src, "Failed to move mob to a valid location.", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Send Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index b7f05740a3..2931c52a5c 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -1,12 +1,11 @@ -#define IRCREPLYCOUNT 2 - +#define EXTERNALREPLYCOUNT 2 //allows right clicking mobs to send an admin PM to their client, forwards the selected mob's client to cmd_admin_pm /client/proc/cmd_admin_pm_context(mob/M in GLOB.mob_list) set category = null set name = "Admin PM Mob" if(!holder) - to_chat(src, "Error: Admin-PM-Context: Only administrators may use this command.") + to_chat(src, "Error: Admin-PM-Context: Only administrators may use this command.", confidential = TRUE) return if( !ismob(M) || !M.client ) return @@ -18,7 +17,7 @@ set category = "Admin" set name = "Admin PM" if(!holder) - to_chat(src, "Error: Admin-PM-Panel: Only administrators may use this command.") + to_chat(src, "Error: Admin-PM-Panel: Only administrators may use this command.", confidential = TRUE) return var/list/client/targets[0] for(var/client/T) @@ -37,7 +36,7 @@ /client/proc/cmd_ahelp_reply(whom) if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).") + to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).", confidential = TRUE) return var/client/C if(istext(whom)) @@ -48,45 +47,61 @@ C = whom if(!C) if(holder) - to_chat(src, "Error: Admin-PM: Client not found.") + to_chat(src, "Error: Admin-PM: Client not found.", confidential = TRUE) return var/datum/admin_help/AH = C.current_ticket if(AH) - message_admins("[key_name_admin(src)] has started replying to [key_name(C, 0, 0)]'s admin help.") + message_admins("[key_name_admin(src)] has started replying to [key_name_admin(C, 0, 0)]'s admin help.") var/msg = input(src,"Message:", "Private message to [C.holder?.fakekey ? "an Administrator" : key_name(C, 0, 0)].") as message|null if (!msg) - message_admins("[key_name_admin(src)] has cancelled their reply to [key_name(C, 0, 0)]'s admin help.") + message_admins("[key_name_admin(src)] has cancelled their reply to [key_name_admin(C, 0, 0)]'s admin help.") return + if(!C) //We lost the client during input, disconnected or relogged. + if(GLOB.directory[AH.initiator_ckey]) // Client has reconnected, lets try to recover + whom = GLOB.directory[AH.initiator_ckey] + else + to_chat(src, "Error: Admin-PM: Client not found.", confidential = TRUE) + to_chat(src, "Message not sent:
[msg]", confidential = TRUE) + AH.AddInteraction("No client found, message not sent:
[msg]") + return cmd_admin_pm(whom, msg) //takes input from cmd_admin_pm_context, cmd_admin_pm_panel or /client/Topic and sends them a PM. //Fetching a message if needed. src is the sender and C is the target client /client/proc/cmd_admin_pm(whom, msg) if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).") + to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).", confidential = TRUE) return if(!holder && !current_ticket) //no ticket? https://www.youtube.com/watch?v=iHSPf6x1Fdo - to_chat(src, "You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.") - to_chat(src, "Message: [msg]") + to_chat(src, "You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.", confidential = TRUE) + to_chat(src, "Message: [msg]", confidential = TRUE) return var/client/recipient - var/irc = 0 + var/recipient_ckey // Stored in case client is deleted between this and after the message is input + var/datum/admin_help/recipient_ticket // Stored in case client is deleted between this and after the message is input + var/external = 0 if(istext(whom)) if(whom[1] == "@") whom = findStealthKey(whom) if(whom == "IRCKEY") - irc = 1 + external = 1 else recipient = GLOB.directory[whom] else if(istype(whom, /client)) recipient = whom + if(!recipient) + to_chat(src, "Error: Admin-PM: Client not found.", confidential = TRUE) + return - if(irc) + recipient_ckey = recipient.ckey + recipient_ticket = recipient.current_ticket + + if(external) if(!ircreplyamount) //to prevent people from spamming irc/discord return if(!msg) @@ -95,21 +110,11 @@ if(!msg) return if(holder) - to_chat(src, "Error: Use the admin IRC channel, nerd.") + to_chat(src, "Error: Use the admin IRC/Discord channel, nerd.", confidential = TRUE) return else - if(!recipient) - if(holder) - to_chat(src, "Error: Admin-PM: Client not found.") - if(msg) - to_chat(src, msg) - return - else if(msg) // you want to continue if there's no message instead of returning now - current_ticket.MessageNoRecipient(msg) - return - //get message text, limit it's length.and clean/escape html if(!msg) msg = input(src,"Message:", "Private message to [recipient.holder?.fakekey ? "an Administrator" : key_name(recipient, 0, 0)].") as message|null @@ -117,22 +122,30 @@ if(!msg) return - if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).") - return - - if(!recipient) + if(!recipient) + if(GLOB.directory[recipient_ckey]) // Client has reconnected, lets try to recover + recipient = GLOB.directory[recipient_ckey] + else if(holder) - to_chat(src, "Error: Admin-PM: Client not found.") + to_chat(src, "Error: Admin-PM: Client not found.", confidential = TRUE) + to_chat(src, "Message not sent:
[msg]", confidential = TRUE) + if(recipient_ticket) + recipient_ticket.AddInteraction("No client found, message not sent:
[msg]") + return else current_ticket.MessageNoRecipient(msg) - return + return + + + if(prefs.muted & MUTE_ADMINHELP) + to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).", confidential = TRUE) + return if (src.handle_spam_prevention(msg,MUTE_ADMINHELP)) return //clean the message if it's not sent by a high-rank admin - if(!check_rights(R_SERVER|R_DEBUG,0)||irc)//no sending html to the poor bots + if(!check_rights(R_SERVER|R_DEBUG,0)||external)//no sending html to the poor bots msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN)) if(!msg) return @@ -144,28 +157,33 @@ var/keywordparsedmsg = keywords_lookup(msg) - if(irc) - to_chat(src, "PM to-Admins: [rawmsg]") - var/datum/admin_help/AH = admin_ticket_log(src, "Reply PM from-[key_name(src, TRUE, TRUE)] to IRC: [keywordparsedmsg]") + if(external) + to_chat(src, "PM to-Admins: [rawmsg]", confidential = TRUE) + var/datum/admin_help/AH = admin_ticket_log(src, "Reply PM from-[key_name(src, TRUE, TRUE)] to External: [keywordparsedmsg]") ircreplyamount-- send2irc("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg) + else - if(recipient.holder) - if(holder) //both are admins - to_chat(recipient, "Admin PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]") - to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [keywordparsedmsg]") + var/badmin = FALSE //Lets figure out if an admin is getting bwoinked. + if(holder && recipient.holder && !current_ticket) //Both are admins, and this is not a reply to our own ticket. + badmin = TRUE + if(recipient.holder && !badmin) + if(holder) + to_chat(recipient, "Admin PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]", confidential = TRUE) + to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [keywordparsedmsg]", confidential = TRUE) //omg this is dumb, just fill in both their tickets var/interaction_message = "PM from-[key_name(src, recipient, 1)] to-[key_name(recipient, src, 1)]: [keywordparsedmsg]" admin_ticket_log(src, interaction_message) if(recipient != src) //reeee admin_ticket_log(recipient, interaction_message) - + // SSblackbox.LogAhelp(current_ticket.id, "Reply", msg, recipient.ckey, src.ckey) else //recipient is an admin but sender is not var/replymsg = "Reply PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]" admin_ticket_log(src, "[replymsg]") - to_chat(recipient, "[replymsg]") - to_chat(src, "PM to-Admins: [msg]") + to_chat(recipient, "[replymsg]", confidential = TRUE) + to_chat(src, "PM to-Admins: [msg]", confidential = TRUE) + // SSblackbox.LogAhelp(current_ticket.id, "Reply", msg, recipient.ckey, src.ckey) //play the receiving admin the adminhelp sound (if they have them enabled) if(recipient.prefs.toggles & SOUND_ADMINHELP) @@ -173,78 +191,88 @@ else if(holder) //sender is an admin but recipient is not. Do BIG RED TEXT + //var/already_logged = FALSE if(!recipient.current_ticket) new /datum/admin_help(msg, recipient, TRUE) + //already_logged = TRUE + // SSblackbox.LogAhelp(recipient.current_ticket.id, "Ticket Opened", msg, recipient.ckey, src.ckey) - to_chat(recipient, "-- Administrator private message --") - to_chat(recipient, "Admin PM from-[key_name(src, recipient, 0)]: [msg]") - to_chat(recipient, "Click on the administrator's name to reply.") - to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [msg]") + to_chat(recipient, "-- Administrator private message --", confidential = TRUE) + to_chat(recipient, "Admin PM from-[key_name(src, recipient, 0)]: [msg]", confidential = TRUE) + to_chat(recipient, "Click on the administrator's name to reply.", confidential = TRUE) + to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [msg]", confidential = TRUE) admin_ticket_log(recipient, "PM From [key_name_admin(src)]: [keywordparsedmsg]") + // if(!already_logged) //Reply to an existing ticket + // SSblackbox.LogAhelp(recipient.current_ticket.id, "Reply", msg, recipient.ckey, src.ckey) + + //always play non-admin recipients the adminhelp sound SEND_SOUND(recipient, sound('sound/effects/adminhelp.ogg')) //AdminPM popup for ApocStation and anybody else who wants to use it. Set it with POPUP_ADMIN_PM in config.txt ~Carn if(CONFIG_GET(flag/popup_admin_pm)) - spawn() //so we don't hold the caller proc up. Please functionalize this - var/sender = src - var/sendername = key - var/reply = input(recipient, msg,"Admin PM from-[sendername]", "") as message|null //show message and await a reply - if(recipient && reply) - if(sender) - recipient.cmd_admin_pm(sender,reply) //sender is still about, let's reply to them - else - adminhelp(reply) //sender has left, adminhelp instead - return + INVOKE_ASYNC(src, .proc/popup_admin_pm, recipient, msg) else //neither are admins - to_chat(src, "Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.") + to_chat(src, "Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.", confidential = TRUE) return - if(irc) - log_admin_private("PM: [key_name(src)]->IRC: [rawmsg]") + if(external) + log_admin_private("PM: [key_name(src)]->External: [rawmsg]") for(var/client/X in GLOB.admins) - to_chat(X, "PM: [key_name(src, X, 0)]->IRC: [keywordparsedmsg]") + to_chat(X, "PM: [key_name(src, X, 0)]->External: [keywordparsedmsg]", confidential = TRUE) else window_flash(recipient, ignorepref = TRUE) log_admin_private("PM: [key_name(src)]->[key_name(recipient)]: [rawmsg]") //we don't use message_admins here because the sender/receiver might get it too for(var/client/X in GLOB.admins) if(X.key!=key && X.key!=recipient.key) //check client/X is an admin and isn't the sender or recipient - to_chat(X, "PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]: [keywordparsedmsg]" ) + to_chat(X, "PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]: [keywordparsedmsg]" , confidential = TRUE) + +/client/proc/popup_admin_pm(client/recipient, msg) + var/sender = src + var/sendername = key + var/reply = input(recipient, msg,"Admin PM from-[sendername]", "") as message|null //show message and await a reply + if(recipient && reply) + if(sender) + recipient.cmd_admin_pm(sender,reply) //sender is still about, let's reply to them + else + adminhelp(reply) //sender has left, adminhelp instead - -#define IRC_AHELP_USAGE "Usage: ticket " /proc/IrcPm(target,msg,sender) + return TgsPm(target,msg,sender) //compatability moment. + +#define TGS_AHELP_USAGE "Usage: ticket " +/proc/TgsPm(target,msg,sender) target = ckey(target) var/client/C = GLOB.directory[target] var/datum/admin_help/ticket = C ? C.current_ticket : GLOB.ahelp_tickets.CKey2ActiveTicket(target) var/compliant_msg = trim(lowertext(msg)) - var/irc_tagged = "[sender](IRC)" + var/tgs_tagged = "[sender](TGS/External)" var/list/splits = splittext(compliant_msg, " ") if(splits.len && splits[1] == "ticket") if(splits.len < 2) - return IRC_AHELP_USAGE + return TGS_AHELP_USAGE switch(splits[2]) if("close") if(ticket) - ticket.Close(irc_tagged) + ticket.Close(tgs_tagged) return "Ticket #[ticket.id] successfully closed" if("resolve") if(ticket) - ticket.Resolve(irc_tagged) + ticket.Resolve(tgs_tagged) return "Ticket #[ticket.id] successfully resolved" if("icissue") if(ticket) - ticket.ICIssue(irc_tagged) + ticket.ICIssue(tgs_tagged) return "Ticket #[ticket.id] successfully marked as IC issue" if("reject") if(ticket) - ticket.Reject(irc_tagged) + ticket.Reject(tgs_tagged) return "Ticket #[ticket.id] successfully rejected" if("reopen") if(ticket) @@ -253,7 +281,7 @@ if(!isnull(fail)) fail = text2num(splits[3]) if(isnull(fail)) - return "Error: No/Invalid ticket id specified. [IRC_AHELP_USAGE]" + return "Error: No/Invalid ticket id specified. [TGS_AHELP_USAGE]" var/datum/admin_help/AH = GLOB.ahelp_tickets.TicketByID(fail) if(!AH) return "Error: Ticket #[fail] not found" @@ -275,41 +303,42 @@ . += "#[AH.id]" return else - return IRC_AHELP_USAGE + return TGS_AHELP_USAGE return "Error: Ticket could not be found" var/static/stealthkey - var/adminname = CONFIG_GET(flag/show_irc_name) ? irc_tagged : "Administrator" + var/adminname = CONFIG_GET(flag/show_irc_name) ? tgs_tagged : "Administrator" if(!C) return "Error: No client" if(!stealthkey) - stealthkey = GenIrcStealthKey() + stealthkey = GenTgsStealthKey() msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN)) if(!msg) return "Error: No message" - message_admins("IRC message from [sender] to [key_name_admin(C)] : [msg]") - log_admin_private("IRC PM: [sender] -> [key_name(C)] : [msg]") + message_admins("External message from [sender] to [key_name_admin(C)] : [msg]") + log_admin_private("External PM: [sender] -> [key_name(C)] : [msg]") msg = emoji_parse(msg) - to_chat(C, "-- Administrator private message --") - to_chat(C, "Admin PM from-[adminname]: [msg]") - to_chat(C, "Click on the administrator's name to reply.") + to_chat(C, "-- Administrator private message --", confidential = TRUE) + to_chat(C, "Admin PM from-[adminname]: [msg]", confidential = TRUE) + to_chat(C, "Click on the administrator's name to reply.", confidential = TRUE) - admin_ticket_log(C, "PM From [irc_tagged]: [msg]") + admin_ticket_log(C, "PM From [tgs_tagged]: [msg]") window_flash(C, ignorepref = TRUE) //always play non-admin recipients the adminhelp sound SEND_SOUND(C, 'sound/effects/adminhelp.ogg') - C.ircreplyamount = IRCREPLYCOUNT + // C.externalreplyamount = EXTERNALREPLYCOUNT + C.ircreplyamount = EXTERNALREPLYCOUNT return "Message Successful" -/proc/GenIrcStealthKey() +/proc/GenTgsStealthKey() var/num = (rand(0,1000)) var/i = 0 while(i == 0) @@ -322,4 +351,4 @@ GLOB.stealthminID["IRCKEY"] = stealth return stealth -#undef IRCREPLYCOUNT +#undef EXTERNALREPLYCOUNT diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 9081357ef6..66653020d4 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -13,7 +13,7 @@ msg = keywords_lookup(msg) msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [msg]" - to_chat(GLOB.admins, msg) + to_chat(GLOB.admins, msg, confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Asay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/bluespacearty.dm b/code/modules/admin/verbs/bluespacearty.dm index 2910d6dd85..23fff5ece9 100644 --- a/code/modules/admin/verbs/bluespacearty.dm +++ b/code/modules/admin/verbs/bluespacearty.dm @@ -5,7 +5,7 @@ var/mob/living/target = M if(!isliving(target)) - to_chat(usr, "This can only be used on instances of type /mob/living") + to_chat(usr, "This can only be used on instances of type /mob/living", confidential = TRUE) return explosion(target.loc, 0, 0, 0, 0) diff --git a/code/modules/admin/verbs/borgpanel.dm b/code/modules/admin/verbs/borgpanel.dm index 35f4ddb3e5..2dd5bcb806 100644 --- a/code/modules/admin/verbs/borgpanel.dm +++ b/code/modules/admin/verbs/borgpanel.dm @@ -7,9 +7,9 @@ return if (!istype(borgo, /mob/living/silicon/robot)) - borgo = input("Select a borg", "Select a borg", null, null) as null|anything in GLOB.silicon_mobs + borgo = input("Select a borg", "Select a borg", null, null) as null|anything in sortNames(GLOB.silicon_mobs) if (!istype(borgo, /mob/living/silicon/robot)) - to_chat(usr, "Borg is required for borgpanel") + to_chat(usr, "Borg is required for borgpanel", confidential = TRUE) var/datum/borgpanel/borgpanel = new(usr, borgo) @@ -25,18 +25,18 @@ if(!istype(to_borg)) qdel(src) CRASH("Borg panel is only available for borgs") - user = CLIENT_FROM_VAR(to_user) - if (!user) CRASH("Borg panel attempted to open to a mob without a client") - borg = to_borg -/datum/borgpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/datum/borgpanel/ui_state(mob/user) + return GLOB.admin_state + +/datum/borgpanel/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "BorgPanel", "Borg Panel", 700, 700, master_ui, state) + ui = new(user, src, "BorgPanel") ui.open() /datum/borgpanel/ui_data(mob/user) @@ -53,13 +53,13 @@ .["upgrades"] = list() for (var/upgradetype in subtypesof(/obj/item/borg/upgrade)-/obj/item/borg/upgrade/hypospray) //hypospray is a dummy parent for hypospray upgrades var/obj/item/borg/upgrade/upgrade = upgradetype - if (initial(upgrade.module_type) && !istype(borg.module, initial(upgrade.module_type))) // Upgrade requires a different module + if (initial(upgrade.module_type) && !is_type_in_list(borg.module, initial(upgrade.module_type))) // Upgrade requires a different module continue var/installed = FALSE if (locate(upgradetype) in borg) installed = TRUE .["upgrades"] += list(list("name" = initial(upgrade.name), "installed" = installed, "type" = upgradetype)) - .["laws"] = borg.laws ? borg.laws.get_law_list(include_zeroth = TRUE) : list() + .["laws"] = borg.laws ? borg.laws.get_law_list(include_zeroth = TRUE, render_html = FALSE) : list() .["channels"] = list() for (var/k in GLOB.radiochannels) if (k == RADIO_CHANNEL_COMMON) diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index 4df6f22686..66acc0f667 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -2,16 +2,16 @@ set category = "Special Verbs" set name = "Dsay" set hidden = 1 - if(!src.holder) - to_chat(src, "Only administrators may use this command.") + if(!holder) + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return - if(!src.mob) + if(!mob) return if(prefs.muted & MUTE_DEADCHAT) - to_chat(src, "You cannot send DSAY messages (muted).") + to_chat(src, "You cannot send DSAY messages (muted).", confidential = TRUE) return - if (src.handle_spam_prevention(msg,MUTE_DEADCHAT)) + if (handle_spam_prevention(msg,MUTE_DEADCHAT)) return msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN) @@ -23,14 +23,25 @@ var/rendered = "DEAD: [uppertext(holder.rank)]([src.holder.fakekey ? pick(nicknames) : src.key]) says, \"[emoji_parse(msg)]\"" + // var/rank_name = holder.rank + // var/admin_name = key + // if(holder.fakekey) + // rank_name = pick(strings("admin_nicknames.json", "ranks", "config")) please use this soon. + // admin_name = pick(strings("admin_nicknames.json", "names", "config")) + // var/rendered = "DEAD: [rank_name]([admin_name]) says, \"[emoji_parse(msg)]\"" + for (var/mob/M in GLOB.player_list) if(isnewplayer(M)) continue - if (M.stat == DEAD || (M.client && M.client.holder && (M.client.prefs.chat_toggles & CHAT_DEAD))) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above - to_chat(M, rendered) + if (M.stat == DEAD || (M.client.holder && (M.client.prefs.chat_toggles & CHAT_DEAD))) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above + to_chat(M, rendered, confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Dsay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/get_dead_say() - var/msg = input(src, null, "dsay \"text\"") as text + var/msg = input(src, null, "dsay \"text\"") as text|null + + if (isnull(msg)) + return + dsay(msg) diff --git a/code/modules/admin/verbs/getlogs.dm b/code/modules/admin/verbs/getlogs.dm index 21a722d32f..4cbb0214f7 100644 --- a/code/modules/admin/verbs/getlogs.dm +++ b/code/modules/admin/verbs/getlogs.dm @@ -31,5 +31,5 @@ src << ftp(file(path)) else return - to_chat(src, "Attempting to send [path], this may take a fair few minutes if the file is very large.") - return \ No newline at end of file + to_chat(src, "Attempting to send [path], this may take a fair few minutes if the file is very large.", confidential = TRUE) + return diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index e7b9342645..fc2ba55f54 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -34,9 +34,7 @@ for(var/mob/M in GLOB.player_list) if(M.client.prefs.toggles & SOUND_MIDI) - var/user_vol = M.client.chatOutput.adminMusicVolume - if(user_vol) - admin_sound.volume = vol * (user_vol / 100) + admin_sound.volume = vol * M.client.admin_music_volume SEND_SOUND(M, admin_sound) admin_sound.volume = vol @@ -69,7 +67,7 @@ if(istext(web_sound_input)) var/web_sound_url = "" var/stop_web_sounds = FALSE - var/pitch + var/list/music_extra_data = list() if(length(web_sound_input)) web_sound_input = trim(web_sound_input) @@ -97,11 +95,10 @@ var/webpage_url = title if (data["webpage_url"]) webpage_url = "[title]" - - var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num - if(!freq) - freq = 1 - pitch = freq + 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"] var/res = alert(usr, "Show the title of and link to this song to the players?\n[title]",, "No", "Yes", "Cancel") switch(res) @@ -130,11 +127,11 @@ for(var/m in GLOB.player_list) var/mob/M = m var/client/C = M.client - if((C.prefs.toggles & SOUND_MIDI) && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded) + if(C.prefs.toggles & SOUND_MIDI) if(!stop_web_sounds) - C.chatOutput.sendMusic(web_sound_url, pitch) + C.tgui_panel?.play_music(web_sound_url, music_extra_data) else - C.chatOutput.stopMusic() + C.tgui_panel?.stop_music() SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Internet Sound") @@ -144,7 +141,7 @@ if(!check_rights(R_SOUNDS)) return - var/web_sound_input = input("Enter content stream URL (fetch this from local youtube-dl!)", "Play Internet Sound via direct URL") as text|null + var/web_sound_input = input("Enter content stream URL (must be a direct link)", "Play Internet Sound via direct URL") as text|null if(istext(web_sound_input)) if(!length(web_sound_input)) log_admin("[key_name(src)] stopped web sound") @@ -152,34 +149,37 @@ var/mob/M for(var/i in GLOB.player_list) M = i - M?.client?.chatOutput?.stopMusic() + M?.client?.tgui_panel?.stop_music() return - else - if(web_sound_input && !findtext(web_sound_input, GLOB.is_http_protocol)) - to_chat(src, "BLOCKED: Content URL not using http(s) protocol") - return - var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num - if(isnull(freq)) - return - if(!freq) - freq = 1 - SSblackbox.record_feedback("nested tally", "played_url", 1, list("[ckey]", "[web_sound_input]")) - var/logstr = "[key_name(src)] played web sound at freq [freq]: [web_sound_input]" - log_admin(logstr) - message_admins(logstr) - var/mob/M - var/client/C - var/datum/chatOutput/O - for(var/i in GLOB.player_list) - M = i - C = M.client - if(!(C?.prefs?.toggles & SOUND_MIDI)) - continue - O = C.chatOutput - if(!O || O.broken || !O.loaded) - continue - O.sendMusic(web_sound_input, freq) - SSblackbox.record_feedback("tally", "admin_verb", 1, "Manual Play Internet Sound") + + var/list/music_extra_data = list() + web_sound_input = trim(web_sound_input) + if(web_sound_input && (findtext(web_sound_input, ":") && !findtext(web_sound_input, GLOB.is_http_protocol))) + to_chat(src, "Non-http(s) URIs are not allowed.", confidential = TRUE) + return + + var/list/explode = splittext(web_sound_input, "/") //if url=="https://fixthisshit.com/pogchamp.ogg"then title="pogchamp.ogg" + var/title = "[explode[explode.len]]" + + if(!findtext(title, ".mp3") && !findtext(title, ".mp4")) // IE sucks. + to_chat(src, "The format is not .mp3/.mp4, IE 8 and above can only support the .mp3/.mp4 format, the music might not play.", confidential = TRUE) + + if(length(title) > 50) //kev no. + title = "Unknown.mp3" + + music_extra_data["title"] = title + + SSblackbox.record_feedback("nested tally", "played_url", 1, list("[ckey]", "[web_sound_input]")) + log_admin("[key_name(src)] played web sound: [web_sound_input]") + message_admins("[key_name(src)] played web sound: [web_sound_input]") + + for(var/m in GLOB.player_list) + var/mob/M = m + var/client/C = M.client + if(C.prefs.toggles & SOUND_MIDI) + C.tgui_panel?.play_music(web_sound_input, music_extra_data) + + SSblackbox.record_feedback("tally", "admin_verb", 1, "Manual Play Internet Sound") /client/proc/set_round_end_sound(S as sound) set category = "Fun" @@ -193,42 +193,6 @@ message_admins("[key_name_admin(src)] set the round end sound to [S]") SSblackbox.record_feedback("tally", "admin_verb", 1, "Set Round End Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/proc/play_web_sound_manual() - set category = "Fun" - set name = "Manual Play Internet Sound" - if(!check_rights(R_SOUNDS)) - return - - var/web_sound_input = input("Enter youtube-dl fetched content URL (supported sites only, leave blank to stop playing)", "Send youtube-dl media link") as text|null - if(!istext(web_sound_input)) - return - web_sound_input = trim(web_sound_input) - if(!length(web_sound_input)) - log_admin("[key_name(src)] stopped web sound") - message_admins("[key_name(src)] stopped web sound") - for(var/m in GLOB.player_list) - var/mob/M = m - var/client/C = M.client - if((C.prefs.toggles & SOUND_MIDI) && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded) - C.chatOutput.stopMusic() - return - var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num - if(!freq) - return - if(web_sound_input && !findtext(web_sound_input, GLOB.is_http_protocol)) - to_chat(src, "BLOCKED: Content URL not using http(s) protocol") - to_chat(src, "The media provider returned a content URL that isn't using the HTTP or HTTPS protocol") - return - - SSblackbox.record_feedback("nested tally", "played_url_manual", 1, list("[ckey]", "[web_sound_input]")) - log_admin("[key_name(src)] manually played web sound: [web_sound_input]") - message_admins("[key_name(src)] manually played web sound: HREF") - for(var/m in GLOB.player_list) - var/mob/M = m - var/client/C = M.client - if((C.prefs.toggles & SOUND_MIDI) && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded) - C.chatOutput.sendMusic(web_sound_input, freq) - /client/proc/stop_sounds() set category = "Debug" set name = "Stop All Playing Sounds" @@ -238,9 +202,7 @@ log_admin("[key_name(src)] stopped all currently playing sounds.") message_admins("[key_name_admin(src)] stopped all currently playing sounds.") for(var/mob/M in GLOB.player_list) - if(M.client) - SEND_SOUND(M, sound(null)) - var/client/C = M.client - if(C && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded) - C.chatOutput.stopMusic() + SEND_SOUND(M, sound(null)) + var/client/C = M.client + C?.tgui_panel?.stop_music() SSblackbox.record_feedback("tally", "admin_verb", 1, "Stop All Playing Sounds") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/view_variables/mass_edit_variables.dm b/code/modules/admin/view_variables/mass_edit_variables.dm index 4e78e1aed7..a498868436 100644 --- a/code/modules/admin/view_variables/mass_edit_variables.dm +++ b/code/modules/admin/view_variables/mass_edit_variables.dm @@ -38,7 +38,7 @@ var/var_value = O.vars[variable] if(variable in GLOB.VVckey_edit) - to_chat(src, "It's forbidden to mass-modify ckeys. It'll crash everyone's client you dummy.") + to_chat(src, "It's forbidden to mass-modify ckeys. It'll crash everyone's client you dummy.", confidential = TRUE) return if(variable in GLOB.VVlocked) if(!check_rights(R_DEBUG)) @@ -56,11 +56,11 @@ default = vv_get_class(variable, var_value) if(isnull(default)) - to_chat(src, "Unable to determine variable type.") + to_chat(src, "Unable to determine variable type.", confidential = TRUE) else - to_chat(src, "Variable appears to be [uppertext(default)].") + to_chat(src, "Variable appears to be [uppertext(default)].", confidential = TRUE) - to_chat(src, "Variable contains: [var_value]") + to_chat(src, "Variable contains: [var_value]", confidential = TRUE) if(default == VV_NUM) var/dir_text = "" @@ -75,7 +75,7 @@ dir_text += "WEST" if(dir_text) - to_chat(src, "If a direction, direction is: [dir_text]") + to_chat(src, "If a direction, direction is: [dir_text]", confidential = TRUE) var/value = vv_get_value(default_class = default) var/new_value = value["value"] @@ -97,9 +97,9 @@ switch(class) if(VV_RESTORE_DEFAULT) - to_chat(src, "Finding items...") + to_chat(src, "Finding items...", confidential = TRUE) var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") + to_chat(src, "Changing [items.len] items...", confidential = TRUE) for(var/thing in items) if (!thing) continue @@ -123,9 +123,9 @@ for(var/V in varsvars) new_value = replacetext(new_value,"\[[V]]","[O.vars[V]]") - to_chat(src, "Finding items...") + to_chat(src, "Finding items...", confidential = TRUE) var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") + to_chat(src, "Changing [items.len] items...", confidential = TRUE) for(var/thing in items) if (!thing) continue @@ -151,9 +151,9 @@ many = FALSE var/type = value["type"] - to_chat(src, "Finding items...") + to_chat(src, "Finding items...", confidential = TRUE) var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") + to_chat(src, "Changing [items.len] items...", confidential = TRUE) for(var/thing in items) if (!thing) continue @@ -169,9 +169,9 @@ CHECK_TICK else - to_chat(src, "Finding items...") + to_chat(src, "Finding items...", confidential = TRUE) var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") + to_chat(src, "Changing [items.len] items...", confidential = TRUE) for(var/thing in items) if (!thing) continue @@ -185,20 +185,20 @@ var/count = rejected+accepted if (!count) - to_chat(src, "No objects found") + to_chat(src, "No objects found", confidential = TRUE) return if (!accepted) - to_chat(src, "Every object rejected your edit") + to_chat(src, "Every object rejected your edit", confidential = TRUE) return if (rejected) - to_chat(src, "[rejected] out of [count] objects rejected your edit") + to_chat(src, "[rejected] out of [count] objects rejected your edit", confidential = TRUE) log_world("### MassVarEdit by [src]: [O.type] (A/R [accepted]/[rejected]) [variable]=[html_encode("[O.vars[variable]]")]([list2params(value)])") log_admin("[key_name(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") message_admins("[key_name_admin(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") //not using global lists as vv is a debug function and debug functions should rely on as less things as possible. -/proc/get_all_of_type(var/T, subtypes = TRUE) +/proc/get_all_of_type(T, subtypes = TRUE) var/list/typecache = list() typecache[T] = 1 if (subtypes) @@ -253,7 +253,7 @@ CHECK_TICK else if (ispath(T, /client)) - for(var/client/thing in world) + for(var/client/thing in GLOB.clients) if (typecache[thing.type]) . += thing CHECK_TICK diff --git a/code/modules/admin/view_variables/modify_variables.dm b/code/modules/admin/view_variables/modify_variables.dm index 5f80b3a5e4..a8871a6467 100644 --- a/code/modules/admin/view_variables/modify_variables.dm +++ b/code/modules/admin/view_variables/modify_variables.dm @@ -17,7 +17,7 @@ GLOBAL_PROTECT(VVpixelmovement) //FALSE = no subtypes, strict exact type pathing (or the type doesn't have subtypes) //TRUE = Yes subtypes //NULL = User cancelled at the prompt or invalid type given -/client/proc/vv_subtype_prompt(var/type) +/client/proc/vv_subtype_prompt(type) if (!ispath(type)) return var/list/subtypes = subtypesof(type) @@ -102,7 +102,7 @@ GLOBAL_PROTECT(VVpixelmovement) L[var_value] = mod_list_add_ass(O) //hehe if (O) if (O.vv_edit_var(objectvar, L) == FALSE) - to_chat(src, "Your edit was rejected by the object.") + to_chat(src, "Your edit was rejected by the object.", confidential = TRUE) return log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]") log_admin("[key_name(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]") @@ -112,7 +112,7 @@ GLOBAL_PROTECT(VVpixelmovement) if(!check_rights(R_VAREDIT)) return if(!istype(L, /list)) - to_chat(src, "Not a List.") + to_chat(src, "Not a List.", confidential = TRUE) return if(L.len > 1000) @@ -121,7 +121,6 @@ GLOBAL_PROTECT(VVpixelmovement) return var/is_normal_list = IS_NORMAL_LIST(L) - var/list/names = list() for (var/i in 1 to L.len) var/key = L[i] @@ -145,7 +144,7 @@ GLOBAL_PROTECT(VVpixelmovement) L = L.Copy() listclearnulls(L) if (!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") + to_chat(src, "Your edit was rejected by the object.", confidential = TRUE) return log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS") log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR NULLS") @@ -155,7 +154,7 @@ GLOBAL_PROTECT(VVpixelmovement) if(variable == "(CLEAR DUPES)") L = uniqueList(L) if (!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") + to_chat(src, "Your edit was rejected by the object.", confidential = TRUE) return log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES") log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR DUPES") @@ -165,7 +164,7 @@ GLOBAL_PROTECT(VVpixelmovement) if(variable == "(SHUFFLE)") L = shuffle(L) if (!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") + to_chat(src, "Your edit was rejected by the object.", confidential = TRUE) return log_world("### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE") log_admin("[key_name(src)] modified [original_name]'s [objectvar]: SHUFFLE") @@ -202,9 +201,9 @@ GLOBAL_PROTECT(VVpixelmovement) default = vv_get_class(objectvar, variable) - to_chat(src, "Variable appears to be [uppertext(default)].") + to_chat(src, "Variable appears to be [uppertext(default)].", confidential = TRUE) - to_chat(src, "Variable contains: [variable]") + to_chat(src, "Variable contains: [variable]", confidential = TRUE) if(default == VV_NUM) var/dir_text = "" @@ -220,7 +219,7 @@ GLOBAL_PROTECT(VVpixelmovement) dir_text += "WEST" if(dir_text) - to_chat(usr, "If a direction, direction is: [dir_text]") + to_chat(usr, "If a direction, direction is: [dir_text]", confidential = TRUE) var/original_var = variable @@ -248,7 +247,7 @@ GLOBAL_PROTECT(VVpixelmovement) L.Cut(index, index+1) if (O) if (O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") + to_chat(src, "Your edit was rejected by the object.", confidential = TRUE) return log_world("### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]") log_admin("[key_name(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]") @@ -260,6 +259,7 @@ GLOBAL_PROTECT(VVpixelmovement) for(var/V in varsvars) new_var = replacetext(new_var,"\[[V]]","[O.vars[V]]") + if(is_normal_list) if(assoc) L[assoc_key] = new_var @@ -269,7 +269,7 @@ GLOBAL_PROTECT(VVpixelmovement) L[new_var] = old_assoc_value if (O) if (O.vv_edit_var(objectvar, L) == FALSE) - to_chat(src, "Your edit was rejected by the object.") + to_chat(src, "Your edit was rejected by the object.", confidential = TRUE) return log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]") log_admin("[key_name(src)] modified [original_name]'s [objectvar]: [original_var]=[new_var]") @@ -297,7 +297,7 @@ GLOBAL_PROTECT(VVpixelmovement) if(param_var_name) if(!(param_var_name in O.vars)) - to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])") + to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])", confidential = TRUE) return variable = param_var_name @@ -322,11 +322,11 @@ GLOBAL_PROTECT(VVpixelmovement) var/default = vv_get_class(variable, var_value) if(isnull(default)) - to_chat(src, "Unable to determine variable type.") + to_chat(src, "Unable to determine variable type.", confidential = TRUE) else - to_chat(src, "Variable appears to be [uppertext(default)].") + to_chat(src, "Variable appears to be [uppertext(default)].", confidential = TRUE) - to_chat(src, "Variable contains: [var_value]") + to_chat(src, "Variable contains: [var_value]", confidential = TRUE) if(default == VV_NUM) var/dir_text = "" @@ -341,7 +341,7 @@ GLOBAL_PROTECT(VVpixelmovement) dir_text += "WEST" if(dir_text) - to_chat(src, "If a direction, direction is: [dir_text]") + to_chat(src, "If a direction, direction is: [dir_text]", confidential = TRUE) if(autodetect_class && default != VV_NULL) if (default == VV_TEXT) @@ -378,7 +378,7 @@ GLOBAL_PROTECT(VVpixelmovement) if (O.vv_edit_var(variable, var_new) == FALSE) - to_chat(src, "Your edit was rejected by the object.") + to_chat(src, "Your edit was rejected by the object.", confidential = TRUE) return vv_update_display(O, "varedited", VV_MSG_EDITED) log_world("### VarEdit by [key_name(src)]: [O.type] [variable]=[var_value] => [var_new]") diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm index 626e75fa10..9f61781f01 100644 --- a/code/modules/admin/view_variables/topic.dm +++ b/code/modules/admin/view_variables/topic.dm @@ -25,7 +25,7 @@ var/mob/M = locate(href_list["rename"]) in GLOB.mob_list if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") + to_chat(usr, "This can only be used on instances of type /mob", confidential = TRUE) return var/new_name = stripped_input(usr,"What would you like to name this mob?","Input a name",M.real_name,MAX_NAME_LEN) @@ -43,7 +43,7 @@ var/atom/A = locate(href_list["rotatedatum"]) if(!istype(A)) - to_chat(usr, "This can only be done to instances of type /atom") + to_chat(usr, "This can only be done to instances of type /atom", confidential = TRUE) return switch(href_list["rotatedir"]) @@ -60,13 +60,13 @@ var/mob/living/carbon/monkey/Mo = locate(href_list["makehuman"]) in GLOB.mob_list if(!istype(Mo)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/monkey") + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/monkey", confidential = TRUE) return if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return if(!Mo) - to_chat(usr, "Mob doesn't exist anymore") + to_chat(usr, "Mob doesn't exist anymore", confidential = TRUE) return holder.Topic(href, list("humanone"=href_list["makehuman"])) @@ -80,10 +80,13 @@ var/Text = href_list["adjustDamage"] - var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num + var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num|null + + if (isnull(amount)) + return if(!L) - to_chat(usr, "Mob doesn't exist anymore") + to_chat(usr, "Mob doesn't exist anymore", confidential = TRUE) return var/newamt @@ -110,7 +113,7 @@ L.adjustStaminaLoss(amount) newamt = L.getStaminaLoss() else - to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]") + to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]", confidential = TRUE) return if(amount != 0) @@ -124,5 +127,5 @@ //Finally, refresh if something modified the list. if(href_list["datumrefresh"]) var/datum/DAT = locate(href_list["datumrefresh"]) - if(istype(DAT, /datum) || istype(DAT, /client)) + if(istype(DAT, /datum) || istype(DAT, /client) || islist(DAT)) debug_variables(DAT) diff --git a/code/modules/admin/view_variables/topic_basic.dm b/code/modules/admin/view_variables/topic_basic.dm index 9ee7103562..b5d7632515 100644 --- a/code/modules/admin/view_variables/topic_basic.dm +++ b/code/modules/admin/view_variables/topic_basic.dm @@ -34,11 +34,11 @@ if (!C) return if(!target) - to_chat(usr, "The object you tried to expose to [C] no longer exists (nulled or hard-deled)") + to_chat(usr, "The object you tried to expose to [C] no longer exists (nulled or hard-deled)", confidential = TRUE) return message_admins("[key_name_admin(usr)] Showed [key_name_admin(C)] a VV window") log_admin("Admin [key_name(usr)] Showed [key_name(C)] a VV window of a [target]") - to_chat(C, "[holder.fakekey ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window") + to_chat(C, "[holder.fakekey ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window", confidential = TRUE) C.debug_variables(target) if(check_rights(R_DEBUG)) if(href_list[VV_HK_DELETE]) @@ -46,31 +46,33 @@ if (isturf(src)) // show the turf that took its place usr.client.debug_variables(src) return - #ifdef REFERENCE_TRACKING - if(href_list[VV_HK_VIEW_REFERENCES]) - var/datum/D = locate(href_list[VV_HK_TARGET]) - if(!D) - to_chat(usr, "Unable to locate item.") + + #ifdef REFERENCE_TRACKING //people with debug can only access this putnam! + if(href_list[VV_HK_VIEW_REFERENCES]) + var/datum/D = locate(href_list[VV_HK_TARGET]) + if(!D) + to_chat(usr, "Unable to locate item.") + return + usr.client.holder.view_refs(target) return - usr.client.holder.view_refs(target) - return - #endif + #endif + if(href_list[VV_HK_MARK]) usr.client.mark_datum(target) if(href_list[VV_HK_ADDCOMPONENT]) if(!check_rights(NONE)) return var/list/names = list() - var/list/componentsubtypes = subtypesof(/datum/component) + var/list/componentsubtypes = sortList(subtypesof(/datum/component), /proc/cmp_typepaths_asc) names += "---Components---" names += componentsubtypes names += "---Elements---" - names += subtypesof(/datum/element) + names += sortList(subtypesof(/datum/element), /proc/cmp_typepaths_asc) var/result = input(usr, "Choose a component/element to add","better know what ur fuckin doin pal") as null|anything in names if(!usr || !result || result == "---Components---" || result == "---Elements---") return if(QDELETED(src)) - to_chat(usr, "That thing doesn't exist anymore!") + to_chat(usr, "That thing doesn't exist anymore!", confidential = TRUE) return var/list/lst = get_callproc_args() if(!lst) @@ -83,7 +85,7 @@ else datumname = "element" target._AddElement(lst) - log_admin("[key_name(usr)] has added [result] [datumname] to [key_name(src)].") - message_admins("[key_name_admin(usr)] has added [result] [datumname] to [key_name_admin(src)].") + log_admin("[key_name(usr)] has added [result] [datumname] to [key_name(target)].") + message_admins("[key_name_admin(usr)] has added [result] [datumname] to [key_name_admin(target)].") if(href_list[VV_HK_CALLPROC]) usr.client.callproc_datum(target) diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm index a4dff725f7..3851008d86 100644 --- a/code/modules/admin/view_variables/view_variables.dm +++ b/code/modules/admin/view_variables/view_variables.dm @@ -1,4 +1,3 @@ - /client/proc/debug_variables(datum/D in world) set category = "Debug" set name = "View Variables" @@ -6,7 +5,7 @@ var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. if(!usr.client || !usr.client.holder) //This is usr because admins can call the proc on other clients, even if they're not admins, to show them VVs. - to_chat(usr, "You need to be an administrator to access this.") + to_chat(usr, "You need to be an administrator to access this.", confidential = TRUE) return if(!D) @@ -26,7 +25,6 @@ if(istype(D, /atom)) sprite = getFlatIcon(D) - hash = md5(sprite) if(sprite) hash = md5(sprite) src << browse_rsc(sprite, "vv[hash].png") @@ -97,7 +95,7 @@ [title] - + - \ No newline at end of file + diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index ae48fddfb4..d5e1e91478 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -75,11 +75,12 @@ var/inprefs = FALSE var/list/topiclimiter + + ///Used for limiting the rate of clicks sends by the client to avoid abuse var/list/clicklimiter - var/datum/chatOutput/chatOutput - - var/list/credits //lazy list of all credit object bound to this client + ///lazy list of all credit object bound to this client + var/list/credits var/datum/player_details/player_details //these persist between logins/logouts during the same round. diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index e7aa447840..72250fa8f2 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -20,7 +20,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( When somebody clicks a link in game, this Topic is called first. It does the stuff in this proc and then is redirected to the Topic() proc for the src=[0xWhatever] (if specified in the link). ie locate(hsrc).Topic() + Such links can be spoofed. + Because of this certain things MUST be considered whenever adding a Topic() for something: - Can it be fed harmful values which could cause runtimes? - Is the Topic call an admin-only thing? @@ -38,7 +40,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( var/asset_cache_job if(href_list["asset_cache_confirm_arrival"]) asset_cache_job = asset_cache_confirm_arrival(href_list["asset_cache_confirm_arrival"]) - if(!asset_cache_job) + if (!asset_cache_job) return // Rate limiting @@ -100,7 +102,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( return // Tgui Topic middleware - if(!tgui_Topic(href_list)) + if(tgui_Topic(href_list)) return // Admin PM @@ -108,10 +110,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( cmd_admin_pm(href_list["priv_msg"],null) return - // CITADEL Start - Mentor PM + // Mentor PM (cit.) if (citadel_client_procs(href_list)) return - // CITADEL End switch(href_list["_src_"]) if("holder") @@ -119,7 +120,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if("usr") hsrc = mob if("mentor") // CITADEL - hsrc = mentor_datum // CITADEL END + hsrc = mentor_datum if("prefs") if (inprefs) return @@ -129,8 +130,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( return if("vars") return view_var_Topic(href,href_list,hsrc) - if("chat") - return chatOutput.Topic(href, href_list) switch(href_list["action"]) if("openLink") @@ -147,7 +146,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( to_chat(src, "Become a BYOND member to access member-perks and features, as well as support the engine that makes this game possible. Only 10 bucks for 3 months! Click Here to find out more.") return 0 return 1 - /* * Call back proc that should be checked in all paths where a client can send messages * @@ -210,14 +208,10 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( /////////// //CONNECT// /////////// -#if (PRELOAD_RSC == 0) -GLOBAL_LIST_EMPTY(external_rsc_urls) -#endif /client/New(TopicData) - world.SetConfig("APP/admin", ckey, "role=admin") //CITADEL EDIT - Allows admins to reboot in OOM situations + world.SetConfig("APP/admin", ckey, "role=admin") var/tdata = TopicData //save this for later use - chatOutput = new /datum/chatOutput(src) TopicData = null //Prevent calls to client.Topic from connect if(connection != "seeker" && connection != "web")//Invalid connection type. @@ -226,6 +220,9 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) GLOB.clients += src GLOB.directory[ckey] = src + // Instantiate tgui panel + tgui_panel = new(src) + GLOB.ahelp_tickets.ClientLogin(src) var/connecting_admin = FALSE //because de-admined admins connecting should be treated like admins. //Admin Authorisation @@ -266,7 +263,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) new /datum/admins(localhost_rank, ckey, 1, 1) //preferences datum - also holds some persistent data for the client (because we may as well keep these datums to a minimum) prefs = GLOB.preferences_datums[ckey] - if(prefs) prefs.parent = src else @@ -276,7 +272,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) prefs.last_ip = address //these are gonna be used for banning prefs.last_id = computer_id //these are gonna be used for banning - fps = prefs.clientfps + fps = prefs.clientfps //(prefs.clientfps < 0) ? RECOMMENDED_FPS : prefs.clientfps if(fexists(roundend_report_file())) verbs += /client/proc/show_previous_roundend_report @@ -301,22 +297,26 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) alert_mob_dupe_login = TRUE if(matches) if(C) - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(C)].") - log_access("Notice: [key_name(src)] has the same [matches] as [key_name(C)].") + message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(C)].") + log_admin_private("Notice: [key_name(src)] has the same [matches] as [key_name(C)].") else - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(C)] (no longer logged in). ") - log_access("Notice: [key_name(src)] has the same [matches] as [key_name(C)] (no longer logged in).") + message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(C)] (no longer logged in). ") + log_admin_private("Notice: [key_name(src)] has the same [matches] as [key_name(C)] (no longer logged in).") if(GLOB.player_details[ckey]) player_details = GLOB.player_details[ckey] player_details.byond_version = full_version else - player_details = new + player_details = new(ckey) player_details.byond_version = full_version GLOB.player_details[ckey] = player_details . = ..() //calls mob.Login() + // if (length(GLOB.stickybanadminexemptions)) + // GLOB.stickybanadminexemptions -= ckey + // if (!length(GLOB.stickybanadminexemptions)) + // restore_stickybans() if (byond_version >= 512) if (!byond_build || byond_build < 1386) @@ -336,7 +336,12 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) qdel(src) return - chatOutput.start() // Starts the chat + // if(SSinput.initialized) placed here on tg. + // set_macros() + // update_movement_keys() + + // Initialize tgui panel + tgui_panel.initialize() if(alert_mob_dupe_login) spawn() @@ -347,12 +352,13 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) connection_timeofday = world.timeofday winset(src, null, "command=\".configure graphics-hwmode on\"") var/cev = CONFIG_GET(number/client_error_version) + var/ceb = CONFIG_GET(number/client_error_build) var/cwv = CONFIG_GET(number/client_warn_version) - if (byond_version < cev) //Out of date client. + if (byond_version < cev || (byond_version == cev && byond_build < ceb)) //Out of date client. to_chat(src, "Your version of BYOND is too old:") to_chat(src, CONFIG_GET(string/client_error_message)) - to_chat(src, "Your version: [byond_version]") - to_chat(src, "Required version: [cev] or later") + to_chat(src, "Your version: [byond_version].[byond_build]") + to_chat(src, "Required version: [cev].[ceb] or later") to_chat(src, "Visit BYOND's website to get the latest version of BYOND.") if (connecting_admin) to_chat(src, "Because you are an admin, you are being allowed to walk past this limitation, But it is still STRONGLY suggested you upgrade") @@ -464,6 +470,10 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) if (menuitem) menuitem.Load_checked(src) + // view_size = new(src, getScreenSize(prefs.widescreenpref)) + // view_size.resetFormat() + // view_size.setZoomMode() + // fit_viewport() Master.UpdateTickRate() /client/proc/ensure_keys_set() @@ -477,13 +487,17 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) /client/Del() if(!gc_destroyed) - Destroy() + Destroy() //Clean up signals and timers. return ..() /client/Destroy() + GLOB.clients -= src + GLOB.directory -= ckey + log_access("Logout: [key_name(src)]") + GLOB.ahelp_tickets.ClientLogout(src) + // SSserver_maint.UpdateHubStatus() if(credits) QDEL_LIST(credits) - log_access("Logout: [key_name(src)]") if(holder) adminGreet(1) holder.owner = null @@ -505,16 +519,13 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) ) send2irc("Server", "[cheesy_message] (No admins online)") - - GLOB.ahelp_tickets.ClientLogout(src) - GLOB.directory -= ckey - GLOB.clients -= src QDEL_LIST_ASSOC_VAL(char_render_holders) if(movingmob != null) movingmob.client_mobs_in_contents -= mob UNSETEMPTY(movingmob.client_mobs_in_contents) + // seen_messages = null Master.UpdateTickRate() - . = ..() + . = ..() //Even though we're going to be hard deleted there are still some things that want to know the destroy is happening return QDEL_HINT_HARDDEL_NOW /client/proc/set_client_age_from_db(connectiontopic) @@ -616,6 +627,9 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) var/datum/DBQuery/query_log_connection = SSdbcore.NewQuery("INSERT INTO `[format_table_name("connection_log")]` (`id`,`datetime`,`server_ip`,`server_port`,`round_id`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')),'[world.port]','[GLOB.round_id]','[sql_ckey]',INET_ATON('[sql_ip]'),'[sql_computerid]')") query_log_connection.Execute() qdel(query_log_connection) + + // SSserver_maint.UpdateHubStatus() + if(new_player) player_age = -1 . = player_age @@ -857,40 +871,31 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) return inactivity return FALSE -//send resources to the client. It's here in its own proc so we can move it around easiliy if need be +/// Send resources to the client. +/// Sends both game resources and browser assets. /client/proc/send_resources() #if (PRELOAD_RSC == 0) var/static/next_external_rsc = 0 - if(GLOB.external_rsc_urls && GLOB.external_rsc_urls.len) - next_external_rsc = WRAP(next_external_rsc+1, 1, GLOB.external_rsc_urls.len+1) - preload_rsc = GLOB.external_rsc_urls[next_external_rsc] + var/list/external_rsc_urls = CONFIG_GET(keyed_list/external_rsc_urls) + if(length(external_rsc_urls)) + next_external_rsc = WRAP(next_external_rsc+1, 1, external_rsc_urls.len+1) + preload_rsc = external_rsc_urls[next_external_rsc] #endif - //get the common files - getFiles( - 'html/search.js', - 'html/panels.css', - 'html/browser/common.css', - 'html/browser/scannernew.css', - 'html/browser/playeroptions.css', - ) + spawn (10) //removing this spawn causes all clients to not get verbs. //load info on what assets the client has src << browse('code/modules/asset_cache/validate_assets.html', "window=asset_cache_browser") //Precache the client with all other assets slowly, so as to not block other browse() calls - getFilesSlow(src, SSassets.preload, register_asset = FALSE) - addtimer(CALLBACK(GLOBAL_PROC, /proc/getFilesSlow, src, SSassets.preload, FALSE), 5 SECONDS) + if (CONFIG_GET(flag/asset_simple_preload)) + addtimer(CALLBACK(SSassets.transport, /datum/asset_transport.proc/send_assets_slow, src, SSassets.transport.preload), 5 SECONDS) #if (PRELOAD_RSC == 0) for (var/name in GLOB.vox_sounds) var/file = GLOB.vox_sounds[name] Export("##action=load_rsc", file) stoplag() - for (var/name in GLOB.vox_sounds_male) - var/file = GLOB.vox_sounds_male[name] - Export("##action=load_rsc", file) - stoplag() #endif diff --git a/code/modules/client/darkmode.dm b/code/modules/client/darkmode.dm deleted file mode 100644 index f806e5c964..0000000000 --- a/code/modules/client/darkmode.dm +++ /dev/null @@ -1,65 +0,0 @@ -//Darkmode preference by Kmc2000// - -/* -This lets you switch chat themes by using winset and CSS loading, you must relog to see this change (or rebuild your browseroutput datum) -Things to note: -If you change ANYTHING in interface/skin.dmf you need to change it here: -Format: -winset(src, "window as appears in skin.dmf after elem", "var to change = desired value") -How this works: -I've added a function to browseroutput.js which registers a cookie for darkmode and swaps the chat accordingly. You can find the button to do this under the "cog" icon next to the ping button (top right of chat) -This then swaps the window theme automatically -Thanks to spacemaniac and mcdonald for help with the JS side of this. -*/ - -/client/proc/force_white_theme() //There's no way round it. We're essentially changing the skin by hand. It's painful but it works, and is the way Lummox suggested. - //Main windows - winset(src, "infowindow", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "info", "background-color = [COLOR_WHITEMODE_BACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "browseroutput", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "outputwindow", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "mainwindow", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND]") - winset(src, "split", "background-color = [COLOR_WHITEMODE_BACKGROUND]") - //Buttons - winset(src, "changelog", "background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "rules", "background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "wiki", "background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "forum", "background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "github", "background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "report-issue", "background-color = [COLOR_WHITEMODE_ISSUE_BUTTON_BG];text-color = [COLOR_WHITEMODE_TEXT]") - //Status and verb tabs - winset(src, "output", "background-color = [COLOR_WHITEMODE_BACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "statwindow", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "stat", "background-color = [COLOR_WHITEMODE_BACKGROUND];tab-background-color = [COLOR_WHITEMODE_DARKBACKGROUND];\ - text-color = [COLOR_WHITEMODE_TEXT];tab-text-color = [COLOR_WHITEMODE_TEXT];\ - prefix-color = [COLOR_WHITEMODE_TEXT];suffix-color = [COLOR_WHITEMODE_TEXT]") - //Etc. - winset(src, "say", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "asset_cache_browser", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "tooltip", "background-color = [COLOR_WHITEMODE_BACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]") - -/client/proc/force_dark_theme() //Inversely, if theyre using white theme and want to swap to the superior dark theme, let's get WINSET() ing - //Main windows - winset(src, "infowindow", "background-color = [COLOR_DARKMODE_DARKBACKGROUND];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "info", "background-color = [COLOR_DARKMODE_BACKGROUND];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "browseroutput", "background-color = [COLOR_DARKMODE_BACKGROUND];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "outputwindow", "background-color = [COLOR_DARKMODE_BACKGROUND];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "mainwindow", "background-color = [COLOR_DARKMODE_DARKBACKGROUND]") - winset(src, "split", "background-color = [COLOR_DARKMODE_BACKGROUND]") - //Buttons - winset(src, "changelog", "background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "rules", "background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "wiki", "background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "forum", "background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "github", "background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "report-issue", "background-color = [COLOR_DARKMODE_ISSUE_BUTTON_BG];text-color = [COLOR_DARKMODE_TEXT]") - //Status and verb tabs - winset(src, "output", "background-color = [COLOR_DARKMODE_BACKGROUND];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "statwindow", "background-color = [COLOR_DARKMODE_DARKBACKGROUND];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "stat", "background-color = [COLOR_DARKMODE_DARKBACKGROUND];tab-background-color = [COLOR_DARKMODE_BACKGROUND];\ - text-color = [COLOR_DARKMODE_TEXT];tab-text-color = [COLOR_DARKMODE_TEXT];\ - prefix-color = [COLOR_DARKMODE_TEXT];suffix-color = [COLOR_DARKMODE_TEXT]") - //Etc. - winset(src, "say", "background-color = [COLOR_DARKMODE_BACKGROUND];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "asset_cache_browser", "background-color = [COLOR_DARKMODE_BACKGROUND];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "tooltip", "background-color = [COLOR_DARKMODE_BACKGROUND];text-color = [COLOR_DARKMODE_TEXT]") \ No newline at end of file diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index ae2dfee7a7..b077a0c8db 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -246,6 +246,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) /// Which of the 5 persistent scar slots we randomly roll to load for this round, if enabled. Actually rolled in [/datum/preferences/proc/load_character(slot)] var/scars_index = 1 + var/chosen_limb_id //body sprite selected to load for the users limbs, null means default, is sanitized when loaded + /datum/preferences/New(client/C) parent = C @@ -521,6 +523,12 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "" mutant_category = 0 + if(length(pref_species.allowed_limb_ids)) + if(!chosen_limb_id || !(chosen_limb_id in pref_species.allowed_limb_ids)) + chosen_limb_id = pref_species.id + dat += "

Body sprite

" + dat += "[chosen_limb_id]" + if(mutant_category) dat += "" mutant_category = 0 @@ -849,6 +857,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "
" dat += "

Fetish content prefs

" dat += "Arousal:[arousable == TRUE ? "Enabled" : "Disabled"]
" + dat += "Genital examine text:[(cit_toggles & GENITAL_EXAMINE) ? "Enabled" : "Disabled"]
" + dat += "Vore examine text:[(cit_toggles & VORE_EXAMINE) ? "Enabled" : "Disabled"]
" dat += "Voracious MediHound sleepers: [(cit_toggles & MEDIHOUND_SLEEPER) ? "Yes" : "No"]
" dat += "Hear Vore Sounds: [(cit_toggles & EATING_NOISES) ? "Yes" : "No"]
" dat += "Hear Vore Digestion Sounds: [(cit_toggles & DIGESTION_NOISES) ? "Yes" : "No"]
" @@ -2117,6 +2127,11 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/selected_custom_speech_verb = input(user, "Choose your desired speech verb (none means your species speech verb)", "Character Preference") as null|anything in GLOB.speech_verbs if(selected_custom_speech_verb) custom_speech_verb = selected_custom_speech_verb + + if("bodysprite") + var/selected_body_sprite = input(user, "Choose your desired body sprite", "Character Preference") as null|anything in pref_species.allowed_limb_ids + if(selected_body_sprite) + chosen_limb_id = selected_body_sprite //this gets sanitized before loading else switch(href_list["preference"]) //CITADEL PREFERENCES EDIT - I can't figure out how to modularize these, so they have to go here. :c -Pooj @@ -2340,6 +2355,13 @@ GLOBAL_LIST_EMPTY(preferences_datums) parent.mob.hud_used.update_parallax_pref(parent.mob) // Citadel edit - Prefs don't work outside of this. :c + + if("genital_examine") + cit_toggles ^= GENITAL_EXAMINE + + if("vore_examine") + cit_toggles ^= VORE_EXAMINE + if("hound_sleeper") cit_toggles ^= MEDIHOUND_SLEEPER @@ -2508,6 +2530,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) character.dna.features = features.Copy() character.set_species(chosen_species, icon_update = FALSE, pref_load = TRUE) + if(chosen_limb_id && (chosen_limb_id in character.dna.species.allowed_limb_ids)) + character.dna.species.mutant_bodyparts["limbs_id"] = chosen_limb_id character.dna.real_name = character.real_name character.dna.nameless = character.nameless character.dna.custom_species = character.custom_species diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index a8183e4940..55a594cf21 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -514,6 +514,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["scars3"] >> scars_list["3"] S["scars4"] >> scars_list["4"] S["scars5"] >> scars_list["5"] + S["chosen_limb_id"] >> chosen_limb_id //Custom names @@ -844,6 +845,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["feature_ooc_notes"], features["ooc_notes"]) + WRITE_FILE(S["chosen_limb_id"], chosen_limb_id) + //Custom names for(var/custom_name_id in GLOB.preferences_custom_names) var/savefile_slot_name = custom_name_id + "_name" //TODO remove this diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm index 951e36f9a6..e16a491c61 100644 --- a/code/modules/client/preferences_toggles.dm +++ b/code/modules/client/preferences_toggles.dm @@ -145,8 +145,7 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, togglemidis)() to_chat(usr, "You will no longer hear sounds uploaded by admins") usr.stop_sound_channel(CHANNEL_ADMIN) var/client/C = usr.client - if(C && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded) - C.chatOutput.stopMusic() + C?.tgui_panel?.stop_music() SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Hearing Midis", "[usr.client.prefs.toggles & SOUND_MIDI ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /datum/verbs/menu/Settings/Sound/togglemidis/Get_checked(client/C) return C.prefs.toggles & SOUND_MIDI @@ -234,8 +233,7 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggleprayersounds)() set desc = "Stop Current Sounds" SEND_SOUND(usr, sound(null)) var/client/C = usr.client - if(C && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded) - C.chatOutput.stopMusic() + C?.tgui_panel?.stop_music() SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Stop Self Sounds")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index fa1145ecea..99703482b9 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -158,88 +158,6 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") else to_chat(src, "There are no admin notices at the moment.") -/client/verb/fix_chat() - set name = "Fix chat" - set category = "OOC" - if (!chatOutput || !istype(chatOutput)) - var/action = alert(src, "Invalid Chat Output data found!\nRecreate data?", "Wot?", "Recreate Chat Output data", "Cancel") - if (action != "Recreate Chat Output data") - return - chatOutput = new /datum/chatOutput(src) - chatOutput.start() - action = alert(src, "Goon chat reloading, wait a bit and tell me if it's fixed", "", "Fixed", "Nope") - if (action == "Fixed") - log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by re-creating the chatOutput datum") - else - chatOutput.load() - action = alert(src, "How about now? (give it a moment (it may also try to load twice))", "", "Yes", "No") - if (action == "Yes") - log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by re-creating the chatOutput datum and forcing a load()") - else - action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat") - if (action == "Switch to old chat") - winset(src, "output", "is-visible=true;is-disabled=false") - winset(src, "browseroutput", "is-visible=false") - log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window after recreating the chatOutput and forcing a load()") - - else if (chatOutput.loaded) - var/action = alert(src, "ChatOutput seems to be loaded\nDo you want me to force a reload, wiping the chat log or just refresh the chat window because it broke/went away?", "Hmmm", "Force Reload", "Refresh", "Cancel") - switch (action) - if ("Force Reload") - chatOutput.loaded = FALSE - chatOutput.start() //this is likely to fail since it asks , but we should try it anyways so we know. - action = alert(src, "Goon chat reloading, wait a bit and tell me if it's fixed", "", "Fixed", "Nope") - if (action == "Fixed") - log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a start()") - else - chatOutput.load() - action = alert(src, "How about now? (give it a moment (it may also try to load twice))", "", "Yes", "No") - if (action == "Yes") - log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a load()") - else - action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat") - if (action == "Switch to old chat") - winset(src, "output", "is-visible=true;is-disabled=false") - winset(src, "browseroutput", "is-visible=false") - log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window forcing a start() and forcing a load()") - - if ("Refresh") - chatOutput.showChat() - action = alert(src, "Goon chat refreshing, wait a bit and tell me if it's fixed", "", "Fixed", "Nope, force a reload") - if (action == "Fixed") - log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a show()") - else - chatOutput.loaded = FALSE - chatOutput.load() - action = alert(src, "How about now? (give it a moment)", "", "Yes", "No") - if (action == "Yes") - log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a load()") - else - action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat") - if (action == "Switch to old chat") - winset(src, "output", "is-visible=true;is-disabled=false") - winset(src, "browseroutput", "is-visible=false") - log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window forcing a show() and forcing a load()") - return - - else - chatOutput.start() - var/action = alert(src, "Manually loading Chat, wait a bit and tell me if it's fixed", "", "Fixed", "Nope") - if (action == "Fixed") - log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by manually calling start()") - else - chatOutput.load() - alert(src, "How about now? (give it a moment (it may also try to load twice))", "", "Yes", "No") - if (action == "Yes") - log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by manually calling start() and forcing a load()") - else - action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat") - if (action == "Switch to old chat") - winset(src, "output", list2params(list("on-show" = "", "is-disabled" = "false", "is-visible" = "true"))) - winset(src, "browseroutput", "is-disabled=true;is-visible=false") - log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window after manually calling start() and forcing a load()") - - /client/verb/motd() set name = "MOTD" diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index cf7dbc7462..6732f1c86c 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -80,7 +80,9 @@ item_state = "hostrench" flags_inv = 0 strip_delay = 80 - unique_reskin = list("Coat" = "hostrench", "Cloak" = "trenchcloak") + unique_reskin = list("Coat" = "hostrench", + "Cloak" = "trenchcloak" + ) /obj/item/clothing/suit/armor/vest/warden name = "warden's jacket" diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index 27fb0cc00d..85c1d0e114 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -118,7 +118,6 @@ icon_state = "plasmaman" item_state = "plasmaman" armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 0, "fire" = 95, "acid" = 95) - slowdown = 1 body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS mutantrace_variation = USE_TAUR_CLIP_MASK can_adjust = FALSE diff --git a/code/modules/emoji/emoji_parse.dm b/code/modules/emoji/emoji_parse.dm index 3fd83899c9..64c07f5a38 100644 --- a/code/modules/emoji/emoji_parse.dm +++ b/code/modules/emoji/emoji_parse.dm @@ -17,7 +17,7 @@ if(search) emoji = lowertext(copytext(text, pos + length(text[pos]), search)) var/isthisapath = (emoji[1] == "/") && text2path(emoji) - var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat) + var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/chat) var/tag = sheet.icon_tag("emoji-[emoji]") if(tag) parsed += "[tag]" //evil way of enforcing 16x16 diff --git a/code/modules/goonchat/browserOutput.dm b/code/modules/goonchat/browserOutput.dm deleted file mode 100644 index ce27dccb74..0000000000 --- a/code/modules/goonchat/browserOutput.dm +++ /dev/null @@ -1,341 +0,0 @@ -/********************************* -For the main html chat area -*********************************/ - -/// Should match the value set in the browser js -#define MAX_COOKIE_LENGTH 5 - -//Precaching a bunch of shit. Someone ship this out of here -GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of icons for the browser output - -//lazy renaming to chat_output, instead renamed to old chatOutput -/** - * The chatOutput datum exists to handle the goonchat browser. - * On client, created on Client/New() - */ -/datum/chatOutput - /// The client that owns us. - var/client/owner - /// How many times client data has been checked - var/total_checks = 0 - /// When to next clear the client data checks counter - var/next_time_to_clear = 0 - /// Has the client loaded the browser output area? - var/loaded = FALSE - /// If they haven't loaded chat, this is where messages will go until they do - var/list/messageQueue - var/cookieSent = FALSE // Has the client sent a cookie for analysis - var/broken = FALSE - var/list/connectionHistory //Contains the connection history passed from chat cookie - var/adminMusicVolume = 25 //This is for the Play Global Sound verb - -/datum/chatOutput/New(client/C) - owner = C - messageQueue = list() - connectionHistory = list() - -/** - * start: Tries to load the chat browser - * Aborts if a problem is encountered. - * Async because this is called from Client/New. - */ -/datum/chatOutput/proc/start() - set waitfor = FALSE - //Check for existing chat - if(!owner) - return FALSE - - if(!winexists(owner, "browseroutput")) // Oh goddamnit. - broken = TRUE - message_admins("Couldn't start chat for [key_name_admin(owner)]!") - . = FALSE - alert(owner.mob, "Updated chat window does not exist. If you are using a custom skin file please allow the game to update.") - return - - if(winget(owner, "browseroutput", "is-visible") == "true") //Already setup - doneLoading() - - else //Not setup - load() - - return TRUE - -/// Loads goonchat and sends assets. -/datum/chatOutput/proc/load() - set waitfor = FALSE - if(!owner) - return - - var/datum/asset/stuff = get_asset_datum(/datum/asset/group/goonchat) - stuff.send(owner) - - owner << browse(file('code/modules/goonchat/browserassets/html/browserOutput.html'), "window=browseroutput") - -/// Interprets input from the client. Will send data back if required. -/datum/chatOutput/Topic(href, list/href_list) - if(usr.client != owner) - return TRUE - - // Build arguments. - // Arguments are in the form "param[paramname]=thing" - var/list/params = list() - for(var/key in href_list) - if(length_char(key) > 7 && findtext(key, "param")) // 7 is the amount of characters in the basic param key template. - var/param_name = copytext_char(key, 7, -1) - var/item = href_list[key] - - params[param_name] = item - - var/data // Data to be sent back to the chat. - switch(href_list["proc"]) - if("doneLoading") - data = doneLoading(arglist(params)) - - if("debug") - data = debug(arglist(params)) - - if("ping") - data = ping(arglist(params)) - - if("analyzeClientData") - data = analyzeClientData(arglist(params)) - - if("setMusicVolume") - data = setMusicVolume(arglist(params)) - if("colorPresetPost") //User just swapped color presets in their goonchat preferences. Do we do anything else? - switch(href_list["preset"]) - if("light") - owner.force_white_theme() - if("dark" || "normal") - owner.force_dark_theme() - // if("swaptodarkmode") - // swaptodarkmode() - // if("swaptolightmode") - // swaptolightmode() - - if(data) - ehjax_send(data = data) - - -/// Called on chat output done-loading by JS. -/datum/chatOutput/proc/doneLoading() - if(loaded) - return - - testing("Chat loaded for [owner.ckey]") - loaded = TRUE - showChat() - - - for(var/message in messageQueue) - // whitespace has already been handled by the original to_chat - to_chat(owner, message, handle_whitespace=FALSE) - - messageQueue = null - sendClientData() - - syncRegex() - - //do not convert to to_chat() - SEND_TEXT(owner, "Failed to load fancy chat, reverting to old chat. Certain features won't work.") - -/// Hides the standard output and makes the browser visible. -/datum/chatOutput/proc/showChat() - winset(owner, "output", "is-visible=false") - winset(owner, "browseroutput", "is-disabled=false;is-visible=true") - -/// Calls syncRegex on all currently owned chatOutput datums -/proc/syncChatRegexes() - for (var/user in GLOB.clients) - var/client/C = user - var/datum/chatOutput/Cchat = C.chatOutput - if (Cchat && !Cchat.broken && Cchat.loaded) - Cchat.syncRegex() - -/// Used to dynamically add regexes to the browser output. Currently only used by the IC filter. -/datum/chatOutput/proc/syncRegex() - var/list/regexes = list() - /* - if (config.ic_filter_regex) - regexes["show_filtered_ic_chat"] = list( - config.ic_filter_regex.name, - "ig", - "$1" - ) - */ - if (regexes.len) - ehjax_send(data = list("syncRegex" = regexes)) - -/// Sends json encoded data to the browser. -/datum/chatOutput/proc/ehjax_send(client/C = owner, window = "browseroutput", data) - if(islist(data)) - data = json_encode(data) - C << output("[data]", "[window]:ehjaxCallback") - -/** - * Sends music data to the browser. If enabled by the browser, it will start playing. - * Arguments: - * music must be a https adress. - * extra_data is a list. The keys "pitch", "start" and "end" are used. - ** "pitch" determines the playback rate - ** "start" determines the start time of the sound - ** "end" determines when the musics stops playing - */ -/datum/chatOutput/proc/sendMusic(music, pitch, list/extra_data) //someone remove pitch - if(!findtext(music, GLOB.is_http_protocol)) - return - var/list/music_data = list("adminMusic" = url_encode(url_encode(music))) - - if(extra_data?.len) - music_data["musicRate"] = extra_data["pitch"] || pitch - music_data["musicSeek"] = extra_data["start"] - music_data["musicHalt"] = extra_data["end"] - - ehjax_send(data = music_data) - -/// Stops music playing throw the browser. -/datum/chatOutput/proc/stopMusic() - ehjax_send(data = "stopMusic") - -/// Setter for adminMusicVolume. Sanitizes the value to between 0 and 100. -/datum/chatOutput/proc/setMusicVolume(volume = "") - if(volume) - adminMusicVolume = clamp(text2num(volume), 0, 100) - -/// Sends client connection details to the chat to handle and save -/datum/chatOutput/proc/sendClientData() - //Get dem deets - var/list/deets = list("clientData" = list()) - deets["clientData"]["ckey"] = owner.ckey - deets["clientData"]["ip"] = owner.address - deets["clientData"]["compid"] = owner.computer_id - var/data = json_encode(deets) - ehjax_send(data = data) - -/// Called by client, sent data to investigate (cookie history so far) -/datum/chatOutput/proc/analyzeClientData(cookie = "") - //Spam check - if(world.time > next_time_to_clear) - next_time_to_clear = world.time + (3 SECONDS) - total_checks = 0 - - total_checks += 1 - - if(total_checks > SPAM_TRIGGER_AUTOMUTE) - message_admins("[key_name(owner)] kicked for goonchat topic spam") - qdel(owner) - return - - if(!cookie) - return - - if(cookie != "none") - var/list/connData = json_decode(cookie) - if (connData && islist(connData) && connData.len > 0 && connData["connData"]) - connectionHistory = connData["connData"] //lol fuck - var/list/found = new() - - if(connectionHistory.len > MAX_COOKIE_LENGTH) - message_admins("[key_name(src.owner)] was kicked for an invalid ban cookie)") - qdel(owner) - return - - for(var/i in connectionHistory.len to 1 step -1) - if(QDELETED(owner)) - //he got cleaned up before we were done - return - var/list/row = src.connectionHistory[i] - if (!row || row.len < 3 || (!row["ckey"] || !row["compid"] || !row["ip"])) //Passed malformed history object - return - if (world.IsBanned(row["ckey"], row["ip"], row["compid"], real_bans_only=TRUE)) - found = row - break - CHECK_TICK - - //Uh oh this fucker has a history of playing on a banned account!! - if (found.len > 0) - message_admins("[key_name(src.owner)] has a cookie from a banned account! (Matched: [found["ckey"]], [found["ip"]], [found["compid"]])") - log_admin_private("[key_name(owner)] has a cookie from a banned account! (Matched: [found["ckey"]], [found["ip"]], [found["compid"]])") - - cookieSent = TRUE - -/// Called by js client every 60 seconds -/datum/chatOutput/proc/ping() - return "pong" - -/// Called by js client on js error -/datum/chatOutput/proc/debug(error) - log_world("\[[time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")]\] Client: [(src.owner.key ? src.owner.key : src.owner)] triggered JS error: [error]") - -/// Global chat proc. to_chat_immediate will circumvent SSchat and send data as soon as possible. -/proc/to_chat_immediate(target, message, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = FALSE) - if(!target || !message) - return - - if(target == world) - target = GLOB.clients - - var/original_message = message - if(handle_whitespace) - message = replacetext(message, "\n", "
") - message = replacetext(message, "\t", "[FOURSPACES][FOURSPACES]") //EIGHT SPACES IN TOTAL!! - if(trailing_newline) - message += "
" - - if(islist(target)) - // Do the double-encoding outside the loop to save nanoseconds - var/twiceEncoded = url_encode(url_encode(message)) - for(var/I in target) - var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible - - if (!C) - continue - - //Send it to the old style output window. - SEND_TEXT(C, original_message) - - if(!C.chatOutput || C.chatOutput.broken) // A player who hasn't updated his skin file. - continue - - if(!C.chatOutput.loaded) - //Client still loading, put their messages in a queue - C.chatOutput.messageQueue += message - continue - - C << output(twiceEncoded, "browseroutput:output") - else - var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible - - if (!C) - return - - //Send it to the old style output window. - SEND_TEXT(C, original_message) - - if(!C.chatOutput || C.chatOutput.broken) // A player who hasn't updated his skin file. - return - - if(!C.chatOutput.loaded) - //Client still loading, put their messages in a queue - C.chatOutput.messageQueue += message - return - - // url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript. - C << output(url_encode(url_encode(message)), "browseroutput:output") - -/// Sends a text message to the target. -/proc/to_chat(target, message, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = FALSE) - if(Master.current_runlevel == RUNLEVEL_INIT || !SSchat?.initialized) - to_chat_immediate(target, message, handle_whitespace, trailing_newline, confidential) - return - SSchat.queue(target, message, handle_whitespace, trailing_newline, confidential) - -/// Dark mode light mode stuff. Yell at KMC if this breaks! (See darkmode.dm for documentation) -/datum/chatOutput/proc/swaptolightmode() - owner.force_white_theme() - -/// Light mode stuff. (See darkmode.dm for documentation) -/datum/chatOutput/proc/swaptodarkmode() - owner.force_dark_theme() - -#undef MAX_COOKIE_LENGTH diff --git a/code/modules/goonchat/browserassets/css/browserOutput.css b/code/modules/goonchat/browserassets/css/browserOutput.css deleted file mode 100644 index 2669a3634a..0000000000 --- a/code/modules/goonchat/browserassets/css/browserOutput.css +++ /dev/null @@ -1,464 +0,0 @@ -/***************************************** -* -* GLOBAL STYLES -* -******************************************/ -html, body { - padding: 0; - margin: 0; - height: 100%; - color: #000000; -} -body { - background: #E0E0E0; /*CIT CHANGE - darkens chatbox a lil*/ - font-family: Verdana, sans-serif; - font-size: 13px; - line-height: 1.2; - overflow-x: hidden; - overflow-y: scroll; - word-wrap: break-word; -} - -em { - font-style: normal; - font-weight: bold; -} - -img { - margin: 0; - padding: 0; - line-height: 1; - -ms-interpolation-mode: nearest-neighbor; - image-rendering: pixelated; -} -img.icon { - height: 1em; - min-height: 16px; - width: auto; - vertical-align: bottom; -} - - -.r:before { /* "repeated" badge class for combined messages */ - content: 'x'; -} -.r { - display: inline-block; - min-width: 0.5em; - font-size: 0.7em; - padding: 0.2em 0.3em; - line-height: 1; - color: white; - text-align: center; - white-space: nowrap; - vertical-align: middle; - background-color: crimson; - border-radius: 10px; -} - -a {color: #0000ff;} -a.visited {color: #ff00ff;} -a:visited {color: #ff00ff;} -a.popt {text-decoration: none;} - -/***************************************** -* -* OUTPUT NOT RELATED TO ACTUAL MESSAGES -* -******************************************/ -#loading { - position: fixed; - width: 300px; - height: 150px; - text-align: center; - left: 50%; - top: 50%; - margin: -75px 0 0 -150px; -} -#loading i {display: block; padding-bottom: 3px;} - -#messages { - font-size: 13px; - padding: 3px; - margin: 0; - word-wrap: break-word; -} -#newMessages { - position: fixed; - display: block; - bottom: 0; - right: 0; - padding: 8px; - background: #d0d0d0; - text-decoration: none; - font-variant: small-caps; - font-size: 1.1em; - font-weight: bold; - color: #333; -} -#newMessages:hover {background: #ccc;} -#newMessages i {vertical-align: middle; padding-left: 3px;} -#ping { - position: fixed; - top: 0; - right: 135px; - width: 45px; - background: #d0d0d0; - height: 30px; - padding: 8px 0 2px 0; -} -#ping i {display: block; text-align: center;} -#ping .ms { - display: block; - text-align: center; - font-size: 8pt; - padding-top: 2px; -} -#userBar { - position: fixed; - top: 0; - right: 0; -} -#userBar .subCell { - background: #d0d0d0; - height: 30px; - padding: 5px 0; - display: block; - color: #333; - text-decoration: none; - line-height: 28px; - border-top: 1px solid #b4b4b4; -} -#userBar .subCell:hover {background: #ccc;} -#userBar .toggle { - width: 45px; - background: #ccc; - border-top: 0; - float: right; - text-align: center; -} -#userBar .sub {clear: both; display: none; width: 180px;} -#userBar .sub.scroll {overflow-y: scroll;} -#userBar .sub.subCell {padding: 3px 0 3px 8px; line-height: 30px; font-size: 0.9em; clear: both;} -#userBar .sub span { - display: block; - line-height: 30px; - float: left; -} -#userBar .sub i { - display: block; - padding: 0 5px; - font-size: 1.1em; - width: 22px; - text-align: center; - line-height: 30px; - float: right; -} -#userBar .sub input { - position: absolute; - padding: 7px 5px; - width: 121px; - line-height: 30px; - float: left; -} -#userBar .topCell {border-top: 0;} - -/* POPUPS */ -.popup { - position: fixed; - top: 50%; - left: 50%; - background: #d0d0d0; -} -.popup .close { - position: absolute; - background: #aaa; - top: 0; - right: 0; - color: #333; - text-decoration: none; - z-index: 2; - padding: 0 10px; - height: 30px; - line-height: 30px; -} -.popup .close:hover {background: #999;} -.popup .head { - background: #999; - color: #d0d0d0; - padding: 0 10px; - height: 30px; - line-height: 30px; - text-transform: uppercase; - font-size: 0.9em; - font-weight: bold; - border-bottom: 2px solid green; -} -.popup input {border: 1px solid #999; background: #fff; margin: 0; padding: 5px; outline: none; color: #333;} -.popup input[type=text]:hover, .popup input[type=text]:active, .popup input[type=text]:focus {border-color: green;} -.popup input[type=submit] {padding: 5px 10px; background: #999; color: #d0d0d0; text-transform: uppercase; font-size: 0.9em; font-weight: bold;} -.popup input[type=submit]:hover, .popup input[type=submit]:focus, .popup input[type=submit]:active {background: #aaa; cursor: pointer;} - -.changeFont {padding: 10px;} -.changeFont a {display: block; text-decoration: none; padding: 3px; color: #333;} -.changeFont a:hover {background: #ccc;} - -.highlightPopup {padding: 10px; text-align: center;} -.highlightPopup input[type=text] {display: block; width: 215px; text-align: left; margin-top: 5px;} -.highlightPopup input.highlightColor {background-color: #FFFF00;} -.highlightPopup input.highlightTermSubmit {margin-top: 5px;} - -/* ADMIN CONTEXT MENU */ -.contextMenu { - background-color: #d0d0d0; - position: fixed; - margin: 2px; - width: 150px; -} -.contextMenu a { - display: block; - padding: 2px 5px; - text-decoration: none; - color: #333; -} - -.contextMenu a:hover { - background-color: #ccc; -} - -/* ADMIN FILTER MESSAGES MENU */ -.filterMessages {padding: 5px;} -.filterMessages div {padding: 2px 0;} -.filterMessages input {} -.filterMessages label {} - -.icon-stack {height: 1em; line-height: 1em; width: 1em; vertical-align: middle; margin-top: -2px;} - - -/***************************************** -* -* OUTPUT ACTUALLY RELATED TO MESSAGES -* -******************************************/ - -/* MOTD */ -.motd {color: #638500; font-family: Verdana, sans-serif;} -.motd h1, .motd h2, .motd h3, .motd h4, .motd h5, .motd h6 {color: #638500; text-decoration: underline;} -.motd a, .motd a:link, .motd a:visited, .motd a:active, .motd a:hover {color: #638500;} - -/* ADD HERE FOR BOLD */ -.bold, .name, .prefix, .ooc, .looc, .adminooc, .admin, .medal, .yell {font-weight: bold;} - -/* ADD HERE FOR ITALIC */ -.italic, .italics, .emote {font-style: italic;} - -/* OUTPUT COLORS */ -.highlight {background: yellow;} - -h1, h2, h3, h4, h5, h6 {color: #0000ff;font-family: Georgia, Verdana, sans-serif;} -h1.alert, h2.alert {color: #000000;} - -em {font-style: normal; font-weight: bold;} - -.ooc {color: #002eb8; font-weight: bold;} -.looc {color: #6699CC; font-weight: bold;} -.antagooc {color: #b8002e; font-weight: bold;} -.adminobserverooc {color: #0099cc; font-weight: bold;} -.adminooc {color: #700038; font-weight: bold;} - -.adminsay {color: #FF4500} -.admin {color: #386aff; font-weight: bold;} - -.name { font-weight: bold;} - -.say {} -.deadsay {color: #5c00e6;} -.binarysay {color: #20c20e; background-color: #000000; display: block;} -.binarysay a {color: #00ff00;} -.binarysay a:active, .binarysay a:visited {color: #88ff88;} -.radio {color: #008000;} -.sciradio {color: #993399;} -.comradio {color: #948f02;} -.secradio {color: #a30000;} -.medradio {color: #337296;} -.engradio {color: #fb5613;} -.suppradio {color: #a8732b;} -.servradio {color: #6eaa2c;} -.syndradio {color: #6d3f40;} -.centcomradio {color: #686868;} -.aiprivradio {color: #ff00ff;} -.redteamradio {color: #ff0000;} -.blueteamradio {color: #0000ff;} - -.yell { font-weight: bold;} - -.alert {color: #ff0000;} -h1.alert, h2.alert {color: #000000;} - -.emote { font-style: italic;} -.selecteddna {color: #ffffff; background-color: #001B1B} - -.attack {color: #ff0000;} -.disarm {color: #990000;} -.passive {color: #660000;} - -.userdanger {color: #ff0000; font-weight: bold; font-size: 185%;} -.bolddanger {color: #c51e1e;font-weight: bold;} -.danger {color: #ff0000;} -.tinydanger {color: #c51e1e; font-size: 85%;} -.smalldanger {color: #c51e1e; font-size: 90%;} -.warning {color: #ff0000; font-style: italic;} -.alertwarning {color: #FF0000; font-weight: bold} -.boldwarning {color: #ff0000; font-style: italic; font-weight: bold} -.announce {color: #228b22; font-weight: bold;} -.boldannounce {color: #ff0000; font-weight: bold;} -.greenannounce {color: #00ff00; font-weight: bold;} -.rose {color: #ff5050;} -.info {color: #0000CC;} -.notice {color: #000099;} -.tinynotice {color: #6685f5; font-style: italic; font-size: 85%;} -.smallnotice {color: #6685f5; font-size: 90%;} -.smallnoticeital {color: #6685f5; font-style: italic; font-size: 90%;} -.boldnotice {color: #000099; font-weight: bold;} -.adminnotice {color: #0000ff;} -.adminhelp {color: #ff0000; font-weight: bold;} -.unconscious {color: #0000ff; font-weight: bold;} -.suicide {color: #ff5050; font-style: italic;} -.green {color: #03ff39;} -.red {color: #FF0000;} -.pink {color: #FF69Bf;} -.blue {color: #0000FF;} -.nicegreen {color: #14a833;} -.userlove {color: #FF1493; font-style: italic; font-weight: bold; text-shadow: 0 0 6px #ff6dbc;} -.love {color: #ff006a; font-style: italic; text-shadow: 0 0 6px #ff6d6d;} -.shadowling {color: #3b2769;} -.cult {color: #960000;} - -.cultitalic {color: #960000; font-style: italic;} -.cultbold {color: #960000; font-style: italic; font-weight: bold;} -.cultboldtalic {color: #960000; font-weight: bold; font-size: 185%;} - -.cultlarge {color: #960000; font-weight: bold; font-size: 185%;} -.narsie {color: #960000; font-weight: bold; font-size: 925%;} -.narsiesmall {color: #960000; font-weight: bold; font-size: 370%;} -.colossus {color: #7F282A; font-size: 310%;} -.hierophant {color: #660099; font-weight: bold; font-style: italic;} -.hierophant_warning {color: #660099; font-style: italic;} -.purple {color: #5e2d79;} -.holoparasite {color: #35333a;} - -.revennotice {color: #1d2953;} -.revenboldnotice {color: #1d2953; font-weight: bold;} -.revenbignotice {color: #1d2953; font-weight: bold; font-size: 185%;} -.revenminor {color: #823abb} -.revenwarning {color: #760fbb; font-style: italic;} -.revendanger {color: #760fbb; font-weight: bold; font-size: 185%;} -.umbra {color: #5000A0;} -.umbra_emphasis {color: #5000A0; font-weight: bold; font-style: italic;} -.umbra_large {color: #5000A0; font-size: 185%; font-weight: bold; font-style: italic;} - -.deconversion_message {color: #5000A0; font-size: 185%; font-style: italic;} - -.brass {color: #BE8700;} -.heavy_brass {color: #BE8700; font-weight: bold; font-style: italic;} -.large_brass {color: #BE8700; font-size: 185%;} -.big_brass {color: #BE8700; font-size: 185%; font-weight: bold; font-style: italic;} -.ratvar {color: #BE8700; font-size: 370%; font-weight: bold; font-style: italic;} -.alloy {color: #42474D;} -.heavy_alloy {color: #42474D; font-weight: bold; font-style: italic;} -.nezbere_large {color: #42474D; font-size: 185%; font-weight: bold; font-style: italic;} -.nezbere {color: #42474D; font-weight: bold; font-style: italic;} -.nezbere_small {color: #42474D;} -.sevtug_large {color: #AF0AAF; font-size: 185%; font-weight: bold; font-style: italic;} -.sevtug {color: #AF0AAF; font-weight: bold; font-style: italic;} -.sevtug_small {color: #AF0AAF;} -.inathneq_large {color: #1E8CE1; font-size: 185%; font-weight: bold; font-style: italic;} -.inathneq {color: #1E8CE1; font-weight: bold; font-style: italic;} -.inathneq_small {color: #1E8CE1;} -.nzcrentr_large {color: #DAAA18; font-size: 185%; font-weight: bold; font-style: italic;} -.nzcrentr {color: #DAAA18; font-weight: bold; font-style: italic;} -.nzcrentr_small {color: #DAAA18;} -.neovgre_large {color: #6E001A; font-size: 185%; font-weight: bold; font-style: italic;} -.neovgre {color: #6E001A; font-weight: bold; font-style: italic;} -.neovgre_small {color: #6E001A;} - -.newscaster {color: #800000;} -.ghostalert {color: #5c00e6; font-style: italic; font-weight: bold;} - -.alien {color: #543354;} -.noticealien {color: #00c000;} -.alertalien {color: #00c000; font-weight: bold;} -.changeling {color: #800080; font-style: italic;} - -.spider {color: #4d004d; font-weight: bold; font-size: 185%;} - -.interface {color: #330033;} - -.sans {font-family: "Comic Sans MS", cursive, sans-serif;} -.papyrus {font-family: "Papyrus", cursive, sans-serif;} -.robot {font-family: "Courier New", cursive, sans-serif;} - -.command_headset {font-weight: bold; font-size: 160%;} -.small {font-size: 60%;} -.big {font-size: 185%;} -.reallybig {font-size: 245%;} -.extremelybig {font-size: 310%;} -.greentext {color: #00FF00; font-size: 185%;} -.redtext {color: #FF0000; font-size: 185%;} -.clown {color: #FF69Bf; font-size: 160%; font-family: "Comic Sans MS", cursive, sans-serif; font-weight: bold;} -.his_grace {color: #15D512; font-family: "Courier New", cursive, sans-serif; font-style: italic;} -.spooky {color: #FF6100;} -.velvet {color: #660015; font-weight: bold; animation: velvet 5000ms infinite;} - -.lethal {color: #bf3d3d; font-weight: bold;} -.stun {color: #0f81bc; font-weight: bold;} -.ion {color: #d084d6; font-weight: bold;} -.xray {color: #32c025; font-weight: bold;} - -@keyframes velvet { - 0% { color: #400020; } - 40% { color: #FF0000; } - 50% { color: #FF8888; } - 60% { color: #FF0000; } - 100% { color: #400020; } -} - -.hypnophrase {color: #202020; font-weight: bold; animation: hypnocolor 1500ms infinite;} -@keyframes hypnocolor { - 0% { color: #202020; } - 25% { color: #4b02ac; } - 50% { color: #9f41f1; } - 75% { color: #541c9c; } - 100% { color: #7adbf3; } -} - -.phobia {color: #dd0000; font-weight: bold; animation: phobia 750ms infinite;} -@keyframes phobia { - 0% { color: #f75a5a; } - 50% { color: #dd0000; } - 100% { color: #f75a5a; } -} - - -.icon {height: 1em; width: auto;} - -.memo {color: #638500; text-align: center;} -.memoedit {text-align: center; font-size: 125%;} -.abductor {color: #800080; font-style: italic;} -.mind_control {color: #A00D6F; font-size: 100%; font-weight: bold; font-style: italic;} -.slime {color: #00CED1;} -.drone {color: #848482;} -.monkey {color: #975032;} -.swarmer {color: #2C75FF;} -.resonate {color: #298F85;} - -.monkeyhive {color: #774704;} -.monkeylead {color: #774704; font-size: 125%;} - -.connectionClosed, .fatalError {background: red; color: white; padding: 5px;} -.connectionClosed.restored {background: green;} -.internal.boldnshit {color: #000099; font-weight: bold;} - -/* HELPER CLASSES */ -.text-normal {font-weight: normal; font-style: normal;} -.hidden {display: none; visibility: hidden;} diff --git a/code/modules/goonchat/browserassets/css/browserOutput_dark.css b/code/modules/goonchat/browserassets/css/browserOutput_dark.css deleted file mode 100644 index 3377bcf72c..0000000000 --- a/code/modules/goonchat/browserassets/css/browserOutput_dark.css +++ /dev/null @@ -1,159 +0,0 @@ -html, body {color: #E0E0E0;} -body { - background: #171717; - font-color: #E0E0E0; - scrollbar-face-color:#1A1A1A; - scrollbar-track-color:#171717; - scrollbar-highlight-color:#171717; -} - -a {color: #397ea5;} -a.visited {color: #7c00e6;} -a:visited {color: #7c00e6;} - -#newMessages { - background: #242424; - color: #E0E0E0; -} -#newMessages:hover {background: #272727;} - -#ping {background: #272727;} - -#userBar .subCell { - background: #272727; - color: #E0E0E0; - border-top: 1px solid #171717; -} -#userBar .subCell:hover {background: #272727;} -#userBar .toggle {background: #272727;} - -/* MOTD */ -.motd {color: #E0E0E0;} -.motd h1, .motd h2, .motd h3, .motd h4, .motd h5, .motd h6 {color: #E0E0E0;} -.motd a, .motd a:link, .motd a:visited, .motd a:active, .motd a:hover {color: #E0E0E0;} - -h1, h2, h3, h4, h5, h6 {color: #E0E0E0;} -h1.alert, h2.alert {color: #E0E0E0;} - -.ooc {color: #cca300;} -.looc {color: #d8b555;} -.antagooc {color: #ce254f;} -.adminobserverooc {color: #0099cc;} -.adminooc {color: #3d5bc3;} - -.admin {color: #5975da;} - -.deadsay {color: #e2c1ff;} -.radio {color: #1ecc43;} -.sciradio {color: #c68cfa;} -.comradio {color: #5177ff;} -.secradio {color: #dd3535;} -.medradio {color: #57b8f0;} -.engradio {color: #f37746;} -.suppradio {color: #b88646;} -.servradio {color: #6ca729;} -.syndradio {color: #8f4a4b;} -.centcomradio {color: #2681a5;} -.aiprivradio {color: #d65d95;} -.redteamradio {color: #ff4444;} -.blueteamradio {color: #3434fd;} - -.alert {color: #d82020;} -h1.alert, h2.alert {color: #99aab5;} - -.attack {color: #e01c1c;} -.disarm {color: #b42525;} -.passive {color: #a00f0f;} - -.userdanger {color: #c51e1e;} -.danger {color: #c51e1e;} -.warning {color: #c51e1e;} -.alertwarning {color: #c51e1e;} -.boldwarning {color: #c51e1e;} -.announce {color: #c51e1e;} -.boldannounce {color: #c51e1e;} -.greenannounce {color: #059223;} -.info {color: #6685f5;} -.notice {color: #6685f5;} -.boldnotice {color: #6685f5;} -.adminnotice {color: #6685f5;} -.adminhelp {color: #ff0000;} -.unconscious {color: #E0E0E0;} -.red {color: #FF0000;} -.pink {color: #ff70c1;} -.blue {color: #215cff;} -.green {color: #059223;} -.nicegreen {color: #059223;} -.userlove {color: #ff42a6; text-shadow: 0 0 6px #82365e;} -.love {color: #ff4591; text-shadow: 0 0 6px #994449;} -.shadowling {color: #8e8a99;} -.cult {color: #aa1c1c;} - -.cultitalic {color: #aa1c1c;} -.cultbold {color: #aa1c1c;} -.cultboldtalic {color: #aa1c1c;} - -.cultlarge {color: #aa1c1c;} -.narsie {color: #aa1c1c;} -.narsiesmall {color: #aa1c1c;} -.hierophant {color: #b441ee;} -.hierophant_warning {color: #c56bf1;} -.purple {color: #9956d3;} -.holoparasite {color: #88809c;} - -.revennotice {color: #3645aa;} -.revenboldnotice {color: #3645aa;} -.revenbignotice {color: #3645aa;} -.revenminor {color: #823ddd;} -.revenwarning {color: #8911d9;} -.revendanger {color: #8911d9;} -.umbra {color: #7c00e6;} -.umbra_emphasis {color: #7c00e6;} -.umbra_large {color: #7c00e6;} - -.deconversion_message {color: #a947ff;} - -.alloy {color: #545b64;} -.heavy_alloy {color: #545b64;} -.nezbere_large {color: #545b64;} -.nezbere {color: #545b64;} -.nezbere_small {color: #545b64;} -.inathneq_large {color: #1d7dc7;} -.inathneq {color: #1d7dc7;} -.inathneq_small {color: #1d7dc7;} -.neovgre_large {color: #7c0622;} -.neovgre {color: #7c0622;} -.neovgre_small {color: #7c0622;} - -.newscaster {color: #c05d5d;} -.ghostalert {color: #6600ff;} - -.alien {color: #855d85;} -.noticealien {color: #059223;} -.alertalien {color: #059223;} -.changeling {color: #059223;} - -.spider {color: #8800ff;} - -.interface {color: #750e75;} - -.greentext {color: #059223;} -.redtext {color: #c51e1e;} -.clown {color: #ff70c1;} -.velvet {color: #660015;} -@keyframes velvet { - 0% { color: #890020; } - 40% { color: #c51e1e; } - 50% { color: #FF8888; } - 60% { color: #c51e1e; } - 100% { color: #890020; } -} - -.abductor {color: #c204c2;} -.mind_control {color: #df3da9;} -.drone {color: #979795;} - -.monkeyhive {color: #a56408;} -.monkeylead {color: #af6805;} - -.internal.boldnshit {color: #3d5bc3;} diff --git a/code/modules/goonchat/browserassets/css/browserOutput_light.css b/code/modules/goonchat/browserassets/css/browserOutput_light.css deleted file mode 100644 index b7036c74d8..0000000000 --- a/code/modules/goonchat/browserassets/css/browserOutput_light.css +++ /dev/null @@ -1,14 +0,0 @@ -body {background: #F1F1F1;} - -#newMessages {background: #ddd;} -#ping {background: #ddd;} - -#userBar .subCell {background: #ddd;} - -/* POPUPS */ -.popup {background: #ddd;} -.popup .head {color: #ddd;} -.popup input[type=submit] {color: #ddd;} - -/* ADMIN CONTEXT MENU */ -.contextMenu {background-color: #ddd;} diff --git a/code/modules/goonchat/browserassets/html/browserOutput.html b/code/modules/goonchat/browserassets/html/browserOutput.html deleted file mode 100644 index ce51cd8de8..0000000000 --- a/code/modules/goonchat/browserassets/html/browserOutput.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - Chat - - - - - - - - - - -
- -
- Loading...

- If this takes longer than 30 seconds, it will automatically reload a maximum of 5 times.
- If it still doesn't work, use the bug report button at the top right of the window. -
-
-
- -
- - - - - diff --git a/code/modules/goonchat/browserassets/js/browserOutput.js b/code/modules/goonchat/browserassets/js/browserOutput.js deleted file mode 100644 index ac30076de4..0000000000 --- a/code/modules/goonchat/browserassets/js/browserOutput.js +++ /dev/null @@ -1,1104 +0,0 @@ - -/***************************************** -* -* FUNCTION AND VAR DECLARATIONS -* -******************************************/ - -//DEBUG STUFF -var escaper = encodeURIComponent || escape; -var decoder = decodeURIComponent || unescape; -window.onerror = function(msg, url, line, col, error) { - if (document.location.href.indexOf("proc=debug") <= 0) { - var extra = !col ? '' : ' | column: ' + col; - extra += !error ? '' : ' | error: ' + error; - extra += !navigator.userAgent ? '' : ' | user agent: ' + navigator.userAgent; - var debugLine = 'Error: ' + msg + ' | url: ' + url + ' | line: ' + line + extra; - window.location = '?_src_=chat&proc=debug¶m[error]='+escaper(debugLine); - } - return true; -}; - -//Globals -window.status = 'Output'; -var $messages, $subOptions, $subAudio, $selectedSub, $contextMenu, $filterMessages, $last_message; -var opts = { - //General - 'messageCount': 0, //A count...of messages... - 'messageLimit': 2053, //A limit...for the messages... - 'scrollSnapTolerance': 10, //If within x pixels of bottom - 'clickTolerance': 10, //Keep focus if outside x pixels of mousedown position on mouseup - 'imageRetryDelay': 50, //how long between attempts to reload images (in ms) - 'imageRetryLimit': 50, //how many attempts should we make? - 'popups': 0, //Amount of popups opened ever - 'wasd': false, //Is the user in wasd mode? - 'priorChatHeight': 0, //Thing for height-resizing detection - 'restarting': false, //Is the round restarting? - 'colorPreset': 0, // index in the color presets list. - //'darkmode':false, //Are we using darkmode? If not WHY ARE YOU LIVING IN 2009??? <- /tg/ take on darktheme - - //Options menu - 'selectedSubLoop': null, //Contains the interval loop for closing the selected sub menu - 'suppressSubClose': false, //Whether or not we should be hiding the selected sub menu - 'highlightTerms': [], - 'highlightLimit': 5, - 'highlightColor': '#FFFF00', //The color of the highlighted message - 'pingDisabled': false, //Has the user disabled the ping counter - - //Ping display - 'lastPang': 0, //Timestamp of the last response from the server. - 'pangLimit': 35000, - 'pingTime': 0, //Timestamp of when ping sent - 'pongTime': 0, //Timestamp of when ping received - 'noResponse': false, //Tracks the state of the previous ping request - 'noResponseCount': 0, //How many failed pings? - - //Clicks - 'mouseDownX': null, - 'mouseDownY': null, - 'preventFocus': false, //Prevents switching focus to the game window - - //Client Connection Data - 'clientDataLimit': 5, - 'clientData': [], - - //Admin music volume update - 'volumeUpdateDelay': 5000, //Time from when the volume updates to data being sent to the server - 'volumeUpdating': false, //True if volume update function set to fire - 'updatedVolume': 0, //The volume level that is sent to the server - 'musicStartAt': 0, //The position the music starts playing - 'musicEndAt': 0, //The position the music... stops playing... if null, doesn't apply (so the music runs through) - - 'defaultMusicVolume': 25, - - 'messageCombining': true, - -}; -var replaceRegexes = {}; - -// Array of names for chat display color presets. CIT SPECIFIC. -// If not set to normal, a CSS file `browserOutput_${name}.css` will be added to the head. -var colorPresets = [ - 'normal', - 'light', - 'dark' -] - -function clamp(val, min, max) { - return Math.max(min, Math.min(val, max)) -} - -//Polyfill for fucking date now because of course IE8 and below don't support it -if (!Date.now) { - Date.now = function now() { - return new Date().getTime(); - }; -} -//Polyfill for trim() (IE8 and below) -if (typeof String.prototype.trim !== 'function') { - String.prototype.trim = function () { - return this.replace(/^\s+|\s+$/g, ''); - }; -} - -// CIT SPECIFIC. -function updateColorPreset() { - var el = $("#colorPresetLink")[0]; - el.href = "browserOutput_"+colorPresets[opts.colorPreset]+".css"; - runByond('?_src_=chat&proc=colorPresetPost&preset='+colorPresets[opts.colorPreset]); -} - -// Linkify the contents of a node, within its parent. -function linkify(parent, insertBefore, text) { - var start = 0; - var match; - var regex = /(?:(?:https?:\/\/)|(?:www\.))(?:[^ ]*?\.[^ ]*?)+[-A-Za-z0-9+&@#\/%?=~_|$!:,.;()]+/ig; - while ((match = regex.exec(text)) !== null) { - // add the unmatched text - parent.insertBefore(document.createTextNode(text.substring(start, match.index)), insertBefore); - - var href = match[0]; - if (!/^https?:\/\//i.test(match[0])) { - href = "http://" + match[0]; - } - - // add the link - var link = document.createElement("a"); - link.href = href; - link.textContent = match[0]; - parent.insertBefore(link, insertBefore); - - start = regex.lastIndex; - } - if (start !== 0) { - // add the remaining text and remove the original text node - parent.insertBefore(document.createTextNode(text.substring(start)), insertBefore); - parent.removeChild(insertBefore); - } -} - -// Recursively linkify the children of a given node. -function linkify_node(node) { - var children = node.childNodes; - // work backwards to avoid the risk of looping forever on our own output - for (var i = children.length - 1; i >= 0; --i) { - var child = children[i]; - if (child.nodeType == Node.TEXT_NODE) { - // text is to be linkified - linkify(node, child, child.textContent); - } else if (child.nodeName != "A" && child.nodeName != "a") { - // do not linkify existing links - linkify_node(child); - } - } -} - -//Shit fucking piece of crap that doesn't work god fuckin damn it -function linkify_fallback(text) { - var rex = /((?:'+$0+''; - } - else { - return $1 ? $0: ''+$0+''; - } - }); -} - -function byondDecode(message) { - // Basically we url_encode twice server side so we can manually read the encoded version and actually do UTF-8. - // The replace for + is because FOR SOME REASON, BYOND replaces spaces with a + instead of %20, and a plus with %2b. - // Marvelous. - message = message.replace(/\+/g, "%20"); - try { - // This is a workaround for the above not always working when BYOND's shitty url encoding breaks. (byond bug id:2399401) - if (decodeURIComponent) { - message = decodeURIComponent(message); - } else { - throw new Error("Easiest way to trigger the fallback") - } - } catch (err) { - message = unescape(message); - } - return message; -} - -function replaceRegex() { - var selectedRegex = replaceRegexes[$(this).attr('replaceRegex')]; - if (selectedRegex) { - var replacedText = $(this).html().replace(selectedRegex[0], selectedRegex[1]); - $(this).html(replacedText); - } - $(this).removeAttr('replaceRegex'); -} - -// Get a highlight markup span -function createHighlightMarkup() { - var extra = ''; - if (opts.highlightColor) { - extra += ' style="background-color: ' + opts.highlightColor + '"'; - } - return ''; -} - -// Get all child text nodes that match a regex pattern -function getTextNodes(elem, pattern) { - var result = $([]); - $(elem).contents().each(function(idx, child) { - if (child.nodeType === 3 && /\S/.test(child.nodeValue) && pattern.test(child.nodeValue)) { - result = result.add(child); - } - else { - result = result.add(getTextNodes(child, pattern)); - } - }); - return result; -} - -// Highlight all text terms matching the registered regex patterns -function highlightTerms(el) { - var pattern = new RegExp("(" + opts.highlightTerms.join('|') + ")", 'gi'); - var nodes = getTextNodes(el, pattern); - - nodes.each(function (idx, node) { - var content = $(node).text(); - var parent = $(node).parent(); - var pre = $(node.previousSibling); - $(node).remove(); - content.split(pattern).forEach(function (chunk) { - // Get our highlighted span/text node - var toInsert = null; - if (pattern.test(chunk)) { - var tmpElem = $(createHighlightMarkup()); - tmpElem.text(chunk); - toInsert = tmpElem; - } - else { - toInsert = document.createTextNode(chunk); - } - - // Insert back into our element - if (pre.length == 0) { - var result = parent.prepend(toInsert); - pre = $(result[0].firstChild); - } - else { - pre.after(toInsert); - pre = $(pre[0].nextSibling); - } - }); - }); -} - -function iconError(E) { - var that = this; - setTimeout(function() { - var attempts = $(that).data('reload_attempts'); - if (typeof attempts === 'undefined' || !attempts) { - attempts = 1; - } - if (attempts > opts.imageRetryLimit) - return; - var src = that.src; - that.src = null; - that.src = src+'#'+attempts; - $(that).data('reload_attempts', ++attempts); - }, opts.imageRetryDelay); -} - -//Send a message to the client -function output(message, flag) { - if (typeof message === 'undefined') { - return; - } - if (typeof flag === 'undefined') { - flag = ''; - } - - if (flag !== 'internal') - opts.lastPang = Date.now(); - - message = byondDecode(message).trim(); - - //The behemoth of filter-code (for Admin message filters) - //Note: This is proooobably hella inefficient - var filteredOut = false; - if (opts.hasOwnProperty('showMessagesFilters') && !opts.showMessagesFilters['All'].show) { - //Get this filter type (defined by class on message) - var messageHtml = $.parseHTML(message), - messageClasses; - if (opts.hasOwnProperty('filterHideAll') && opts.filterHideAll) { - var internal = false; - messageClasses = (!!$(messageHtml).attr('class') ? $(messageHtml).attr('class').split(/\s+/) : false); - if (messageClasses) { - for (var i = 0; i < messageClasses.length; i++) { //Every class - if (messageClasses[i] == 'internal') { - internal = true; - break; - } - } - } - if (!internal) { - filteredOut = 'All'; - } - } else { - //If the element or it's child have any classes - if (!!$(messageHtml).attr('class') || !!$(messageHtml).children().attr('class')) { - messageClasses = $(messageHtml).attr('class').split(/\s+/); - if (!!$(messageHtml).children().attr('class')) { - messageClasses = messageClasses.concat($(messageHtml).children().attr('class').split(/\s+/)); - } - var tempCount = 0; - for (var i = 0; i < messageClasses.length; i++) { //Every class - var thisClass = messageClasses[i]; - $.each(opts.showMessagesFilters, function(key, val) { //Every filter - if (key !== 'All' && val.show === false && typeof val.match != 'undefined') { - for (var i = 0; i < val.match.length; i++) { - var matchClass = val.match[i]; - if (matchClass == thisClass) { - filteredOut = key; - break; - } - } - } - if (filteredOut) return false; - }); - if (filteredOut) break; - tempCount++; - } - } else { - if (!opts.showMessagesFilters['Misc'].show) { - filteredOut = 'Misc'; - } - } - } - } - - //Stuff we do along with appending a message - var atBottom = false; - if (!filteredOut) { - var bodyHeight = $('body').height(); - var messagesHeight = $messages.outerHeight(); - var scrollPos = $('body,html').scrollTop(); - - //Should we snap the output to the bottom? - if (bodyHeight + scrollPos >= messagesHeight - opts.scrollSnapTolerance) { - atBottom = true; - if ($('#newMessages').length) { - $('#newMessages').remove(); - } - //If not, put the new messages box in - } else { - if ($('#newMessages').length) { - var messages = $('#newMessages .number').text(); - messages = parseInt(messages); - messages++; - $('#newMessages .number').text(messages); - if (messages == 2) { - $('#newMessages .messageWord').append('s'); - } - } else { - $messages.after('1 new message '); - } - } - } - - opts.messageCount++; - - //Pop the top message off if history limit reached - if (opts.messageCount >= opts.messageLimit) { - $messages.children('div.entry:first-child').remove(); - opts.messageCount--; //I guess the count should only ever equal the limit - } - - // Create the element - if combining is off, we use it, and if it's on, we - // might discard it bug need to check its text content. Some messages vary - // only in HTML markup, have the same text content, and should combine. - var entry = document.createElement('div'); - entry.innerHTML = message; - var trimmed_message = entry.textContent || entry.innerText || ""; - - var handled = false; - if (opts.messageCombining) { - var lastmessages = $messages.children('div.entry:last-child').last(); - if (lastmessages.length && $last_message && $last_message == trimmed_message) { - var badge = lastmessages.children('.r').last(); - if (badge.length) { - badge = badge.detach(); - badge.text(parseInt(badge.text()) + 1); - } else { - badge = $('', {'class': 'r', 'text': 2}); - } - lastmessages.html(message); - lastmessages.find('[replaceRegex]').each(replaceRegex); - lastmessages.append(badge); - badge.animate({ - "font-size": "0.9em" - }, 100, function() { - badge.animate({ - "font-size": "0.7em" - }, 100); - }); - opts.messageCount--; - handled = true; - } - } - - if (!handled) { - //Actually append the message - entry.className = 'entry'; - - if (filteredOut) { - entry.className += ' hidden'; - entry.setAttribute('data-filter', filteredOut); - } - - $(entry).find('[replaceRegex]').each(replaceRegex); - - $last_message = trimmed_message; - $messages[0].appendChild(entry); - $(entry).find("img.icon").error(iconError); - - var to_linkify = $(entry).find(".linkify"); - if (typeof Node === 'undefined') { - // Linkify fallback for old IE - for(var i = 0; i < to_linkify.length; ++i) { - to_linkify[i].innerHTML = linkify_fallback(to_linkify[i].innerHTML); - } - } else { - // Linkify for modern IE versions - for(var i = 0; i < to_linkify.length; ++i) { - linkify_node(to_linkify[i]); - } - } - - //Actually do the snap - //Stuff we can do after the message shows can go here, in the interests of responsiveness - if (opts.highlightTerms && opts.highlightTerms.length > 0) { - highlightTerms($(entry)); - } - } - - if (!filteredOut && atBottom) { - $('body,html').scrollTop($messages.outerHeight()); - } -} - -function internalOutput(message, flag) -{ - output(escaper(message), flag) -} - -//Runs a route within byond, client or server side. Consider this "ehjax" for byond. -function runByond(uri) { - window.location = uri; -} - -function setCookie(cname, cvalue, exdays) { - cvalue = escaper(cvalue); - var d = new Date(); - d.setTime(d.getTime() + (exdays*24*60*60*1000)); - var expires = 'expires='+d.toUTCString(); - document.cookie = cname + '=' + cvalue + '; ' + expires + "; path=/"; -} - -function getCookie(cname) { - var name = cname + '='; - var ca = document.cookie.split(';'); - for(var i=0; i < ca.length; i++) { - var c = ca[i]; - while (c.charAt(0)==' ') c = c.substring(1); - if (c.indexOf(name) === 0) { - return decoder(c.substring(name.length,c.length)); - } - } - return ''; -} - -function rgbToHex(R,G,B) {return toHex(R)+toHex(G)+toHex(B);} -function toHex(n) { - n = parseInt(n,10); - if (isNaN(n)) return "00"; - n = Math.max(0,Math.min(n,255)); - return "0123456789ABCDEF".charAt((n-n%16)/16) + "0123456789ABCDEF".charAt(n%16); -} - -/* -function swap() { //Swap to darkmode - if (opts.darkmode){ - document.getElementById("sheetofstyles").href = "browserOutput_white.css"; - opts.darkmode = false; - runByond('?_src_=chat&proc=swaptolightmode'); - } else { - document.getElementById("sheetofstyles").href = "browserOutput.css"; - opts.darkmode = true; - runByond('?_src_=chat&proc=swaptodarkmode'); - } - setCookie('darkmode', (opts.darkmode ? 'true' : 'false'), 365); -} -*/ -function handleClientData(ckey, ip, compid) { - //byond sends player info to here - var currentData = {'ckey': ckey, 'ip': ip, 'compid': compid}; - if (opts.clientData && !$.isEmptyObject(opts.clientData)) { - runByond('?_src_=chat&proc=analyzeClientData¶m[cookie]='+JSON.stringify({'connData': opts.clientData})); - - for (var i = 0; i < opts.clientData.length; i++) { - var saved = opts.clientData[i]; - if (currentData.ckey == saved.ckey && currentData.ip == saved.ip && currentData.compid == saved.compid) { - return; //Record already exists - } - } - //Lets make sure we obey our limit (can connect from server with higher limit) - while (opts.clientData.length >= opts.clientDataLimit) { - opts.clientData.shift(); - } - } else { - runByond('?_src_=chat&proc=analyzeClientData¶m[cookie]=none'); - } - - //Update the cookie with current details - opts.clientData.push(currentData); - setCookie('connData', JSON.stringify(opts.clientData), 365); -} - -//Server calls this on ehjax response -//Or, y'know, whenever really -function ehjaxCallback(data) { - opts.lastPang = Date.now(); - if (data == 'softPang') { - return; - } else if (data == 'pang') { - opts.pingCounter = 0; //reset - opts.pingTime = Date.now(); - runByond('?_src_=chat&proc=ping'); - - } else if (data == 'pong') { - if (opts.pingDisabled) {return;} - opts.pongTime = Date.now(); - var pingDuration = Math.ceil((opts.pongTime - opts.pingTime) / 2); - $('#pingMs').text(pingDuration+'ms'); - pingDuration = Math.min(pingDuration, 255); - var red = pingDuration; - var green = 255 - pingDuration; - var blue = 0; - var hex = rgbToHex(red, green, blue); - $('#pingDot').css('color', '#'+hex); - - } else if (data == 'roundrestart') { - opts.restarting = true; - internalOutput('
The connection has been closed because the server is restarting. Please wait while you automatically reconnect.
', 'internal'); - } else if (data == 'stopMusic') { - $('#adminMusic').prop('src', ''); - } else { - //Oh we're actually being sent data instead of an instruction - var dataJ; - try { - dataJ = $.parseJSON(data); - } catch (e) { - //But...incorrect :sadtrombone: - window.onerror('JSON: '+e+'. '+data, 'browserOutput.html', 327); - return; - } - data = dataJ; - - if (data.clientData) { - if (opts.restarting) { - opts.restarting = false; - $('.connectionClosed.restarting:not(.restored)').addClass('restored').text('The round restarted and you successfully reconnected!'); - } - if (!data.clientData.ckey && !data.clientData.ip && !data.clientData.compid) { - //TODO: Call shutdown perhaps - return; - } else { - handleClientData(data.clientData.ckey, data.clientData.ip, data.clientData.compid); - } - sendVolumeUpdate(); - } else if (data.adminMusic) { - if (typeof data.adminMusic === 'string') { - var adminMusic = byondDecode(data.adminMusic); - var bindLoadedData = false; - adminMusic = adminMusic.match(/https?:\/\/\S+/) || ''; - if (data.musicRate) { - var newRate = Number(data.musicRate); - if(newRate) { - $('#adminMusic').prop('defaultPlaybackRate', newRate); - } - } else { - $('#adminMusic').prop('defaultPlaybackRate', 1.0); - } - if (data.musicSeek) { - opts.musicStartAt = Number(data.musicSeek) || 0; - bindLoadedData = true; - } else { - opts.musicStartAt = 0; - } - if (data.musicHalt) { - opts.musicEndAt = Number(data.musicHalt) || null; - bindLoadedData = true; - } - if (bindLoadedData) { - $('#adminMusic').one('loadeddata', adminMusicLoadedData); - } - $('#adminMusic').prop('src', adminMusic); - $('#adminMusic').trigger("play"); - } - } else if (data.syncRegex) { - for (var i in data.syncRegex) { - - var regexData = data.syncRegex[i]; - var regexName = regexData[0]; - var regexFlags = regexData[1]; - var regexReplaced = regexData[2]; - - replaceRegexes[i] = [new RegExp(regexName, regexFlags), regexReplaced]; - } - } - } -} - -function createPopup(contents, width) { - opts.popups++; - $('body').append(''); - - //Attach close popup event - var $popup = $('#popup'+opts.popups); - var height = $popup.outerHeight(); - $popup.css({'height': height+'px', 'margin': '-'+(height/2)+'px 0 0 -'+(width/2)+'px'}); - - $popup.on('click', '.close', function(e) { - e.preventDefault(); - $popup.remove(); - }); -} - -function toggleWasd(state) { - opts.wasd = (state == 'on' ? true : false); -} - -function sendVolumeUpdate() { - opts.volumeUpdating = false; - if(opts.updatedVolume) { - runByond('?_src_=chat&proc=setMusicVolume¶m[volume]='+opts.updatedVolume); - } -} - -function adminMusicEndCheck(event) { - if (opts.musicEndAt) { - if ($('#adminMusic').prop('currentTime') >= opts.musicEndAt) { - $('#adminMusic').off(event); - $('#adminMusic').trigger('pause'); - $('#adminMusic').prop('src', ''); - } - } else { - $('#adminMusic').off(event); - } -} - -function adminMusicLoadedData(event) { - if (opts.musicStartAt && ($('#adminMusic').prop('duration') === Infinity || (opts.musicStartAt <= $('#adminMusic').prop('duration'))) ) { - $('#adminMusic').prop('currentTime', opts.musicStartAt); - } - if (opts.musicEndAt) { - $('#adminMusic').on('timeupdate', adminMusicEndCheck); - } -} - -function subSlideUp() { - $(this).removeClass('scroll'); - $(this).css('height', ''); -} - -function startSubLoop() { - if (opts.selectedSubLoop) { - clearInterval(opts.selectedSubLoop); - } - return setInterval(function() { - if (!opts.suppressSubClose && $selectedSub.is(':visible')) { - $selectedSub.slideUp('fast', subSlideUp); - clearInterval(opts.selectedSubLoop); - } - }, 5000); //every 5 seconds -} - -function handleToggleClick($sub, $toggle) { - if ($selectedSub !== $sub && $selectedSub.is(':visible')) { - $selectedSub.slideUp('fast', subSlideUp); - } - $selectedSub = $sub - if ($selectedSub.is(':visible')) { - $selectedSub.slideUp('fast', subSlideUp); - clearInterval(opts.selectedSubLoop); - } else { - $selectedSub.slideDown('fast', function() { - var windowHeight = $(window).height(); - var toggleHeight = $toggle.outerHeight(); - var priorSubHeight = $selectedSub.outerHeight(); - var newSubHeight = windowHeight - toggleHeight; - $(this).height(newSubHeight); - if (priorSubHeight > (windowHeight - toggleHeight)) { - $(this).addClass('scroll'); - } - }); - opts.selectedSubLoop = startSubLoop(); - } -} - -/***************************************** -* -* DOM READY -* -******************************************/ - -if (typeof $ === 'undefined') { - var div = document.getElementById('loading').childNodes[1]; - div += '

ERROR: Jquery did not load.'; -} - -$(function() { - $messages = $('#messages'); - $subOptions = $('#subOptions'); - $subAudio = $('#subAudio'); - $selectedSub = $subOptions; - - //Hey look it's a controller loop! - setInterval(function() { - if (opts.lastPang + opts.pangLimit < Date.now() && !opts.restarting) { //Every pingLimit - if (!opts.noResponse) { //Only actually append a message if the previous ping didn't also fail (to prevent spam) - opts.noResponse = true; - opts.noResponseCount++; - internalOutput('
You are either AFK, experiencing lag or the connection has closed.
', 'internal'); - } - } else if (opts.noResponse) { //Previous ping attempt failed ohno - $('.connectionClosed[data-count="'+opts.noResponseCount+'"]:not(.restored)').addClass('restored').text('Your connection has been restored (probably)!'); - opts.noResponse = false; - } - }, 2000); //2 seconds - - - /***************************************** - * - * LOAD SAVED CONFIG - * - ******************************************/ - var savedConfig = { - fontsize: getCookie('fontsize'), //no need for compatabiliy, cookie name is the same - lineheight: getCookie('lineheight'), - 'spingDisabled': getCookie('pingdisabled'), - 'shighlightTerms': getCookie('highlightterms'), - 'shighlightColor': getCookie('highlightcolor'), - 'smusicVolume': getCookie('musicVolume'), - 'smessagecombining': getCookie('messagecombining'), - 'sdarkmode': getCookie('darkmode'), - 'scolorPreset': getCookie('colorpreset'), - }; - - if (savedConfig.fontsize) { - $messages.css('font-size', savedConfig.fontsize); - internalOutput('Loaded font size setting of: '+savedConfig.fontsize+'', 'internal'); - } - if (savedConfig.lineheight) { - $("body").css('line-height', savedConfig.lineheight); - internalOutput('Loaded line height setting of: '+savedConfig.lineheight+'', 'internal'); - } - // if(savedConfig.sdarkmode == 'true'){ - // swap(); - // } - if (savedConfig.scolorPreset) { - opts.colorPreset = Number(savedConfig.scolorPreset); - updateColorPreset(); - internalOutput('Loaded color preset of: '+colorPresets[opts.colorPreset]+'', 'internal'); - } - if (savedConfig.spingDisabled) { - if (savedConfig.spingDisabled == 'true') { - opts.pingDisabled = true; - $('#ping').hide(); - } - internalOutput('Loaded ping display of: '+(opts.pingDisabled ? 'hidden' : 'visible')+'', 'internal'); - } - if (savedConfig.shighlightTerms) { - var savedTerms = $.parseJSON(savedConfig.shighlightTerms).filter(function (entry) { - return entry !== null && /\S/.test(entry); - }); - var actualTerms = savedTerms.length != 0 ? savedTerms.join(', ') : null; - if (actualTerms) { - internalOutput('Loaded highlight strings of: ' + actualTerms+'', 'internal'); - opts.highlightTerms = savedTerms; - } - } - if (savedConfig.shighlightColor) { - opts.highlightColor = savedConfig.shighlightColor; - internalOutput('Loaded highlight color of: '+savedConfig.shighlightColor+'', 'internal'); - } - if (savedConfig.smusicVolume) { - var newVolume = clamp(savedConfig.smusicVolume, 0, 100); - $('#adminMusic').prop('volume', newVolume / 100); - $('#musicVolume').val(newVolume); - opts.updatedVolume = newVolume; - sendVolumeUpdate(); - internalOutput('Loaded music volume of: '+savedConfig.smusicVolume+'', 'internal'); - } - else{ - $('#adminMusic').prop('volume', opts.defaultMusicVolume / 100); - } - - if (savedConfig.smessagecombining) { - if (savedConfig.smessagecombining == 'false') { - opts.messageCombining = false; - } else { - opts.messageCombining = true; - } - } - (function() { - var dataCookie = getCookie('connData'); - if (dataCookie) { - var dataJ; - try { - dataJ = $.parseJSON(dataCookie); - } catch (e) { - window.onerror('JSON '+e+'. '+dataCookie, 'browserOutput.html', 434); - return; - } - opts.clientData = dataJ; - } - })(); - - - /***************************************** - * - * BASE CHAT OUTPUT EVENTS - * - ******************************************/ - - $('body').on('click', 'a', function(e) { - e.preventDefault(); - }); - - $('body').on('mousedown', function(e) { - var $target = $(e.target); - - if ($contextMenu && opts.hasOwnProperty('contextMenuTarget') && opts.contextMenuTarget) { - hideContextMenu(); - return false; - } - - if ($target.is('a') || $target.parent('a').length || $target.is('input') || $target.is('textarea')) { - opts.preventFocus = true; - } else { - opts.preventFocus = false; - opts.mouseDownX = e.pageX; - opts.mouseDownY = e.pageY; - } - }); - - $messages.on('mousedown', function(e) { - if ($selectedSub && $selectedSub.is(':visible')) { - $selectedSub.slideUp('fast', subSlideUp); - clearInterval(opts.selectedSubLoop); - } - }); - - $('body').on('mouseup', function(e) { - if (!opts.preventFocus && - (e.pageX >= opts.mouseDownX - opts.clickTolerance && e.pageX <= opts.mouseDownX + opts.clickTolerance) && - (e.pageY >= opts.mouseDownY - opts.clickTolerance && e.pageY <= opts.mouseDownY + opts.clickTolerance) - ) { - opts.mouseDownX = null; - opts.mouseDownY = null; - runByond('byond://winset?mapwindow.map.focus=true'); - } - }); - - $messages.on('click', 'a', function(e) { - var href = $(this).attr('href'); - $(this).addClass('visited'); - if (href[0] == '?' || (href.length >= 8 && href.substring(0,8) == 'byond://')) { - runByond(href); - } else { - href = escaper(href); - runByond('?action=openLink&link='+href); - } - runByond('byond://winset?mapwindow.map.focus=true'); - }); - - $('body').on('keydown', function(e) { - if (e.target.nodeName == 'INPUT' || e.target.nodeName == 'TEXTAREA') { - return; - } - if (e.ctrlKey || e.altKey || e.shiftKey) { //Band-aid "fix" for allowing ctrl+c copy paste etc. Needs a proper fix. - return; - } - runByond('byond://winset?mapwindow.map.focus=true'); - }); - - //Mildly hacky fix for scroll issues on mob change (interface gets resized sometimes, messing up snap-scroll) - $(window).on('resize', function(e) { - if ($(this).height() !== opts.priorChatHeight) { - $('body,html').scrollTop($messages.outerHeight()); - opts.priorChatHeight = $(this).height(); - } - }); - - - /***************************************** - * - * OPTIONS INTERFACE EVENTS - * - ******************************************/ - - $('body').on('click', '#newMessages', function(e) { - var messagesHeight = $messages.outerHeight(); - $('body,html').scrollTop(messagesHeight); - $('#newMessages').remove(); - runByond('byond://winset?mapwindow.map.focus=true'); - }); - - $('#toggleOptions').click(function(e) { - handleToggleClick($subOptions, $(this)); - }); - // $('#darkmodetoggle').click(function(e) { - // swap(); - // }); - $('#toggleAudio').click(function(e) { - handleToggleClick($subAudio, $(this)); - }); - - $('.sub, .toggle').mouseenter(function() { - opts.suppressSubClose = true; - }); - - $('.sub, .toggle').mouseleave(function() { - opts.suppressSubClose = false; - }); - - $('#decreaseFont').click(function(e) { - savedConfig.fontsize = Math.max(parseInt(savedConfig.fontsize || 13) - 1, 1) + 'px'; - $messages.css({'font-size': savedConfig.fontsize}); - setCookie('fontsize', savedConfig.fontsize, 365); - internalOutput('Font size set to '+savedConfig.fontsize+'', 'internal'); - }); - - $('#increaseFont').click(function(e) { - savedConfig.fontsize = (parseInt(savedConfig.fontsize || 13) + 1) + 'px'; - $messages.css({'font-size': savedConfig.fontsize}); - setCookie('fontsize', savedConfig.fontsize, 365); - internalOutput('Font size set to '+savedConfig.fontsize+'', 'internal'); - }); - - $('#decreaseLineHeight').click(function(e) { - savedConfig.lineheight = Math.max(parseFloat(savedConfig.lineheight || 1.2) - 0.1, 0.1).toFixed(1); - $("body").css({'line-height': savedConfig.lineheight}); - setCookie('lineheight', savedConfig.lineheight, 365); - internalOutput('Line height set to '+savedConfig.lineheight+'', 'internal'); - }); - - $('#increaseLineHeight').click(function(e) { - savedConfig.lineheight = (parseFloat(savedConfig.lineheight || 1.2) + 0.1).toFixed(1); - $("body").css({'line-height': savedConfig.lineheight}); - setCookie('lineheight', savedConfig.lineheight, 365); - internalOutput('Line height set to '+savedConfig.lineheight+'', 'internal'); - }); - - $('#togglePing').click(function(e) { - if (opts.pingDisabled) { - $('#ping').slideDown('fast'); - opts.pingDisabled = false; - } else { - $('#ping').slideUp('fast'); - opts.pingDisabled = true; - } - setCookie('pingdisabled', (opts.pingDisabled ? 'true' : 'false'), 365); - }); - - $('#saveLog').click(function(e) { - // Requires IE 10+ to issue download commands. Just opening a popup - // window will cause Ctrl+S to save a blank page, ignoring innerHTML. - if (!window.Blob) { - output('This function is only supported on IE 10 and up. Upgrade if possible.', 'internal'); - return; - } - - $.ajax({ - type: 'GET', - url: 'browserOutput.css', // browserOutput_white.css - success: function(styleData) { - var blob = new Blob(['Chat Log', $messages.html(), '']); - - var fname = 'SS13 Chat Log'; - var date = new Date(), month = date.getMonth(), day = date.getDay(), hours = date.getHours(), mins = date.getMinutes(), secs = date.getSeconds(); - fname += ' ' + date.getFullYear() + '-' + (month < 10 ? '0' : '') + month + '-' + (day < 10 ? '0' : '') + day; - fname += ' ' + (hours < 10 ? '0' : '') + hours + (mins < 10 ? '0' : '') + mins + (secs < 10 ? '0' : '') + secs; - fname += '.html'; - - window.navigator.msSaveBlob(blob, fname); - } - }); - }); - - $('#highlightTerm').click(function(e) { - if ($('.popup .highlightTerm').is(':visible')) {return;} - var termInputs = ''; - for (var i = 0; i < opts.highlightLimit; i++) { - termInputs += '
'; - } - var popupContent = '
String Highlighting
' + - '
' + - '
Choose up to '+opts.highlightLimit+' strings that will highlight the line when they appear in chat.
' + - '
' + - termInputs + - '
' + - '
' + - '
' + - '
'; - createPopup(popupContent, 250); - }); - - $('body').on('keyup', '#highlightColor', function() { - var color = $('#highlightColor').val(); - color = color.trim(); - if (!color || color.charAt(0) != '#') return; - $('#highlightColor').css('background-color', color); - }); - - $('body').on('submit', '#highlightTermForm', function(e) { - e.preventDefault(); - - opts.highlightTerms = []; - for (var count = 0; count < opts.highlightLimit; count++) { - var term = $('#highlightTermInput'+count).val(); - if (term !== null && /\S/.test(term)) { - opts.highlightTerms.push(term.trim().toLowerCase()); - } - } - - var color = $('#highlightColor').val(); - color = color.trim(); - if (color == '' || color.charAt(0) != '#') { - opts.highlightColor = '#FFFF00'; - } else { - opts.highlightColor = color; - } - var $popup = $('#highlightPopup').closest('.popup'); - $popup.remove(); - - setCookie('highlightterms', JSON.stringify(opts.highlightTerms), 365); - setCookie('highlightcolor', opts.highlightColor, 365); - }); - - $('#clearMessages').click(function() { - $messages.empty(); - opts.messageCount = 0; - }); - - $('#changeColorPreset').click(function() { //CIT SPECIFIC - opts.colorPreset = (opts.colorPreset+1) % colorPresets.length; - updateColorPreset(); - setCookie('colorpreset', opts.colorPreset, 365); - internalOutput('Changed color preset to: '+colorPresets[opts.colorPreset]); - }); - - $('#musicVolumeSpan').hover(function() { - $('#musicVolumeText').addClass('hidden'); - $('#musicVolume').removeClass('hidden'); - }, function() { - $('#musicVolume').addClass('hidden'); - $('#musicVolumeText').removeClass('hidden'); - }); - - $('#musicVolume').change(function() { - var newVolume = $('#musicVolume').val(); - newVolume = clamp(newVolume, 0, 100); - $('#adminMusic').prop('volume', newVolume / 100); - setCookie('musicVolume', newVolume, 365); - opts.updatedVolume = newVolume; - if(!opts.volumeUpdating) { - setTimeout(sendVolumeUpdate, opts.volumeUpdateDelay); - opts.volumeUpdating = true; - } - }); - - $('#toggleCombine').click(function(e) { - opts.messageCombining = !opts.messageCombining; - setCookie('messagecombining', (opts.messageCombining ? 'true' : 'false'), 365); - }); - - $('img.icon').error(iconError); - - - - - /***************************************** - * - * KICK EVERYTHING OFF - * - ******************************************/ - - runByond('?_src_=chat&proc=doneLoading'); - if ($('#loading').is(':visible')) { - $('#loading').remove(); - } - $('#userBar').show(); - opts.priorChatHeight = $(window).height(); -}); diff --git a/code/modules/goonchat/browserassets/js/json2.min.js b/code/modules/goonchat/browserassets/js/json2.min.js deleted file mode 100644 index d867407f26..0000000000 --- a/code/modules/goonchat/browserassets/js/json2.min.js +++ /dev/null @@ -1 +0,0 @@ -"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(t){return 10>t?"0"+t:t}function this_value(){return this.valueOf()}function quote(t){return rx_escapable.lastIndex=0,rx_escapable.test(t)?'"'+t.replace(rx_escapable,function(t){var e=meta[t];return"string"==typeof e?e:"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+t+'"'}function str(t,e){var r,n,o,u,f,a=gap,i=e[t];switch(i&&"object"==typeof i&&"function"==typeof i.toJSON&&(i=i.toJSON(t)),"function"==typeof rep&&(i=rep.call(e,t,i)),typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";if(gap+=indent,f=[],"[object Array]"===Object.prototype.toString.apply(i)){for(u=i.length,r=0;u>r;r+=1)f[r]=str(r,i)||"null";return o=0===f.length?"[]":gap?"[\n"+gap+f.join(",\n"+gap)+"\n"+a+"]":"["+f.join(",")+"]",gap=a,o}if(rep&&"object"==typeof rep)for(u=rep.length,r=0;u>r;r+=1)"string"==typeof rep[r]&&(n=rep[r],o=str(n,i),o&&f.push(quote(n)+(gap?": ":":")+o));else for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(o=str(n,i),o&&f.push(quote(n)+(gap?": ":":")+o));return o=0===f.length?"{}":gap?"{\n"+gap+f.join(",\n"+gap)+"\n"+a+"}":"{"+f.join(",")+"}",gap=a,o}}var rx_one=/^[\],:{}\s]*$/,rx_two=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,rx_three=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,rx_four=/(?:^|:|,)(?:\s*\[)+/g,rx_escapable=/[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,rx_dangerous=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},Boolean.prototype.toJSON=this_value,Number.prototype.toJSON=this_value,String.prototype.toJSON=this_value);var gap,indent,meta,rep;"function"!=typeof JSON.stringify&&(meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=function(t,e,r){var n;if(gap="",indent="","number"==typeof r)for(n=0;r>n;n+=1)indent+=" ";else"string"==typeof r&&(indent=r);if(rep=e,e&&"function"!=typeof e&&("object"!=typeof e||"number"!=typeof e.length))throw new Error("JSON.stringify");return str("",{"":t})}),"function"!=typeof JSON.parse&&(JSON.parse=function(text,reviver){function walk(t,e){var r,n,o=t[e];if(o&&"object"==typeof o)for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(n=walk(o,r),void 0!==n?o[r]=n:delete o[r]);return reviver.call(t,e,o)}var j;if(text=String(text),rx_dangerous.lastIndex=0,rx_dangerous.test(text)&&(text=text.replace(rx_dangerous,function(t){return"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})),rx_one.test(text.replace(rx_two,"@").replace(rx_three,"]").replace(rx_four,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}(); \ No newline at end of file diff --git a/code/modules/holiday/halloween/jacqueen.dm b/code/modules/holiday/halloween/jacqueen.dm index 4561e0ae3e..573b04a296 100644 --- a/code/modules/holiday/halloween/jacqueen.dm +++ b/code/modules/holiday/halloween/jacqueen.dm @@ -334,8 +334,7 @@ to_chat(C, " You feel an overwhelming desire to [message]") if(2) visible_message("[src] waves their arms around, \"If only you had a better upbringing, your ears are now full of my singing!\"") - var/client/C2 = C.client - C2.chatOutput.sendMusic("https://puu.sh/ExBbv.mp4", 1)//I hope this works! + C.client.tgui_panel?.play_music("https://puu.sh/ExBbv.mp4") if(3) visible_message("[src] waves their arms around, \"You're cute little bumpkin, On your head is a pumpkin!\"") if(C.head) diff --git a/code/modules/instruments/songs/editor.dm b/code/modules/instruments/songs/editor.dm index 8c5171667a..e385eed142 100644 --- a/code/modules/instruments/songs/editor.dm +++ b/code/modules/instruments/songs/editor.dm @@ -82,7 +82,6 @@ var/datum/browser/popup = new(user, "instrument", parent?.name || "instrument", 700, 500) popup.set_content(dat.Join("")) - popup.set_title_image(user.browse_rsc_icon(parent.icon, parent.icon_state)) popup.open() /datum/song/proc/ParseSong(text) diff --git a/code/modules/language/language.dm b/code/modules/language/language.dm index 536bf06e8d..42b439ba03 100644 --- a/code/modules/language/language.dm +++ b/code/modules/language/language.dm @@ -35,7 +35,7 @@ return TRUE /datum/language/proc/get_icon() - var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat) + var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/chat) return sheet.icon_tag("language-[icon_state]") /datum/language/proc/get_random_name(gender, name_count=2, syllable_count=4, syllable_divisor=2) diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index f777246453..3953f5e28c 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -66,7 +66,6 @@ dat += "\[Go Back\]
" var/datum/browser/popup = new(user, "publiclibrary", name, 600, 400) popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.open() /obj/machinery/computer/libraryconsole/Topic(href, href_list) @@ -314,7 +313,6 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums var/datum/browser/popup = new(user, "library", name, 600, 400) popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.open() /obj/machinery/computer/libraryconsole/bookmanagement/proc/findscanner(viewrange) @@ -537,7 +535,6 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums dat += "
" var/datum/browser/popup = new(user, "scanner", name, 600, 400) popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.open() /obj/machinery/libraryscanner/Topic(href, href_list) diff --git a/code/modules/mapping/minimaps.dm b/code/modules/mapping/minimaps.dm index 7a62e9ab44..e0eb174cb3 100644 --- a/code/modules/mapping/minimaps.dm +++ b/code/modules/mapping/minimaps.dm @@ -4,7 +4,7 @@ // The map icons var/icon/map_icon var/icon/meta_icon - + var/list/color_area_names = list() var/minx @@ -33,7 +33,7 @@ meta_icon = new('html/blank.png') map_icon.Scale(x2 - x1 + 1, y2 - y1 + 1) // arrays start at 1 meta_icon.Scale(x2 - x1 + 1, y2 - y1 + 1) - + var/list/area_to_color = list() for(var/turf/T in block(locate(x1, y1, z_level), locate(x2, y2, z_level))) var/area/A = T.loc @@ -44,7 +44,7 @@ crop_x2 = max(crop_x2, T.x) crop_y1 = min(crop_y1, T.y) crop_y2 = max(crop_y2, T.y) - + var/meta_color = area_to_color[A] if(!meta_color) meta_color = rgb(rand(0, 255), rand(0, 255), rand(0, 255)) // technically conflicts could happen but it's like very unlikely and it's not that big of a deal if one happens @@ -73,13 +73,15 @@ overlay_icon = new(map_icon) overlay_icon.Scale(16, 16) //we're done baking, now we ship it. - register_asset("minimap-[id].png", map_icon) - register_asset("minimap-[id]-meta.png", meta_icon) + if (!SSassets.cache["minimap-[id].png"]) + SSassets.transport.register_asset("minimap-[id].png", map_icon) + if (!SSassets.cache["minimap-[id]-meta.png"]) + SSassets.transport.register_asset("minimap-[id]-meta.png", meta_icon) /datum/minimap/proc/send(mob/user) if(!id) CRASH("ERROR: send called, but the minimap id is null/missing. ID: [id]") - send_asset_list(user, list("minimap-[id].png" = map_icon, "minimap-[id]-meta.png" = meta_icon)) + SSassets.transport.send_assets(user, list("minimap-[id].png" = map_icon, "minimap-[id]-meta.png" = meta_icon)) /datum/minimap_group var/list/minimaps = list() @@ -100,15 +102,17 @@ var/list/datas = list() var/list/info = list() - + for(var/i in 1 to length(minimaps))// OLD: for(var/i in 1 to length(minimaps)) var/datum/minimap/M = minimaps[i] + var/map_name = "minimap-[M.id].png" + var/meta_name = "minimap-[M.id]-meta.png" M.send(user) info += {"
- - + +
@@ -183,6 +187,4 @@ var/datum/browser/popup = new(user, "minimap_[id]", name, 500, 700) popup.add_head_content(headerJS) //set the head popup.set_content(info) - var/datum/minimap/MICO = minimaps[1] - popup.set_title_image(MICO.overlay_icon) popup.open(FALSE) diff --git a/code/modules/mining/aux_base.dm b/code/modules/mining/aux_base.dm index 006065d048..b10177ebaf 100644 --- a/code/modules/mining/aux_base.dm +++ b/code/modules/mining/aux_base.dm @@ -70,7 +70,6 @@ interface with the mining shuttle at the landing site if a mobile beacon is also var/datum/browser/popup = new(user, "computer", "base management", 550, 300) //width, height popup.set_content("
[dat]
") - popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state)) popup.open() diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 40c3a781fc..0b535ef33e 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -206,6 +206,8 @@ new /obj/item/reagent_containers/glass/bottle/ichor/red(src) new /obj/item/reagent_containers/glass/bottle/ichor/blue(src) new /obj/item/reagent_containers/glass/bottle/ichor/green(src) + if(32) + new /obj/item/disk/tech_disk/illegal(src) //KA modkit design discs /obj/item/disk/design_disk/modkit_disc diff --git a/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm b/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm index 899bf91081..d1f7f15ac9 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm @@ -14,14 +14,14 @@ name = "Standard" icon_state = "standard" -/datum/sprite_accessory/xeno_dorsal/royal - name = "Royal" - icon_state = "royal" - /datum/sprite_accessory/xeno_dorsal/down name = "Dorsal Down" icon_state = "down" +/datum/sprite_accessory/xeno_dorsal/royal + name = "Royal" + icon_state = "royal" + /****************************************** ************* Xeno Tails ****************** *******************************************/ @@ -57,14 +57,14 @@ name = "Standard" icon_state = "standard" -/datum/sprite_accessory/xeno_head/royal - name = "royal" - icon_state = "royal" - /datum/sprite_accessory/xeno_head/hollywood name = "hollywood" icon_state = "hollywood" +/datum/sprite_accessory/xeno_head/royal + name = "royal" + icon_state = "royal" + /datum/sprite_accessory/xeno_head/warrior name = "warrior" icon_state = "warrior" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/body_markings.dm b/code/modules/mob/dead/new_player/sprite_accessories/body_markings.dm index a04838a360..5286acb33a 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/body_markings.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/body_markings.dm @@ -16,6 +16,12 @@ icon_state = "dtiger" gender_specific = 1 +/datum/sprite_accessory/body_markings/guilmon + name = "Guilmon" + icon_state = "guilmon" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' + /datum/sprite_accessory/body_markings/ltiger name = "Light Tiger Body" icon_state = "ltiger" @@ -49,11 +55,6 @@ icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' relevant_layers = null -/datum/sprite_accessory/mam_body_markings/plain - name = "Plain" - icon_state = "plain" - icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' - /datum/sprite_accessory/mam_body_markings/redpanda name = "Redpanda" icon_state = "redpanda" @@ -77,14 +78,14 @@ icon_state = "bellyslim" icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' -/datum/sprite_accessory/mam_body_markings/corgi - name = "Corgi" - icon_state = "corgi" - /datum/sprite_accessory/mam_body_markings/cow name = "Bovine" icon_state = "bovine" +/datum/sprite_accessory/mam_body_markings/corgi + name = "Corgi" + icon_state = "corgi" + /datum/sprite_accessory/mam_body_markings/corvid name = "Corvid" icon_state = "corvid" @@ -139,15 +140,19 @@ name = "Hyena" icon_state = "hyena" -/datum/sprite_accessory/mam_body_markings/lab - name = "Lab" - icon_state = "lab" - /datum/sprite_accessory/mam_body_markings/insect name = "Insect" icon_state = "insect" icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' +/datum/sprite_accessory/mam_body_markings/lab + name = "Lab" + icon_state = "lab" + +/datum/sprite_accessory/mam_body_markings/orca + name = "Orca" + icon_state = "orca" + /datum/sprite_accessory/mam_body_markings/otie name = "Otie" icon_state = "otie" @@ -156,14 +161,15 @@ name = "Otter" icon_state = "otter" -/datum/sprite_accessory/mam_body_markings/orca - name = "Orca" - icon_state = "orca" - /datum/sprite_accessory/mam_body_markings/panther name = "Panther" icon_state = "panther" +/datum/sprite_accessory/mam_body_markings/plain + name = "Plain" + icon_state = "plain" + icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' + /datum/sprite_accessory/mam_body_markings/possum name = "Possum" icon_state = "possum" @@ -172,6 +178,10 @@ name = "Raccoon" icon_state = "raccoon" +/datum/sprite_accessory/mam_body_markings/sergal + name = "Sergal" + icon_state = "sergal" + /datum/sprite_accessory/mam_body_markings/pede name = "Scolipede" icon_state = "scolipede" @@ -181,18 +191,14 @@ name = "Shark" icon_state = "shark" -/datum/sprite_accessory/mam_body_markings/skunk - name = "Skunk" - icon_state = "skunk" - -/datum/sprite_accessory/mam_body_markings/sergal - name = "Sergal" - icon_state = "sergal" - /datum/sprite_accessory/mam_body_markings/shepherd name = "Shepherd" icon_state = "shepherd" +/datum/sprite_accessory/mam_body_markings/skunk + name = "Skunk" + icon_state = "skunk" + /datum/sprite_accessory/mam_body_markings/tajaran name = "Tajaran" icon_state = "tajaran" @@ -232,75 +238,10 @@ icon_state = "none" relevant_layers = null -/datum/sprite_accessory/insect_fluff/plain - name = "Plain" - icon_state = "plain" - -/datum/sprite_accessory/insect_fluff/reddish - name = "Reddish" - icon_state = "redish" - -/datum/sprite_accessory/insect_fluff/royal - name = "Royal" - icon_state = "royal" - -/datum/sprite_accessory/insect_fluff/gothic - name = "Gothic" - icon_state = "gothic" - -/datum/sprite_accessory/insect_fluff/lovers - name = "Lovers" - icon_state = "lovers" - -/datum/sprite_accessory/insect_fluff/whitefly - name = "White Fly" - icon_state = "whitefly" - /datum/sprite_accessory/insect_fluff/punished name = "Burnt Off" icon_state = "punished" -/datum/sprite_accessory/insect_fluff/firewatch - name = "Firewatch" - icon_state = "firewatch" - -/datum/sprite_accessory/insect_fluff/deathhead - name = "Deathshead" - icon_state = "deathhead" - -/datum/sprite_accessory/insect_fluff/poison - name = "Poison" - icon_state = "poison" - -/datum/sprite_accessory/insect_fluff/ragged - name = "Ragged" - icon_state = "ragged" - -/datum/sprite_accessory/insect_fluff/moonfly - name = "Moon Fly" - icon_state = "moonfly" - -/datum/sprite_accessory/insect_fluff/snow - name = "Snow" - icon_state = "snow" - -/datum/sprite_accessory/insect_fluff/oakworm - name = "Oak Worm" - icon_state = "oakworm" - -/datum/sprite_accessory/insect_fluff/jungle - name = "Jungle" - icon_state = "jungle" - -/datum/sprite_accessory/insect_fluff/witchwing - name = "Witch Wing" - icon_state = "witchwing" - -/datum/sprite_accessory/insect_fluff/colored - name = "Colored (Hair)" - icon_state = "snow" - color_src = HAIR - /datum/sprite_accessory/insect_fluff/colored1 name = "Colored (Primary)" icon_state = "snow" @@ -314,4 +255,69 @@ /datum/sprite_accessory/insect_fluff/colored3 name = "Colored (Tertiary)" icon_state = "snow" - color_src = MUTCOLORS3 \ No newline at end of file + color_src = MUTCOLORS3 + +/datum/sprite_accessory/insect_fluff/colored + name = "Colored (Hair)" + icon_state = "snow" + color_src = HAIR + +/datum/sprite_accessory/insect_fluff/deathhead + name = "Deathshead" + icon_state = "deathhead" + +/datum/sprite_accessory/insect_fluff/firewatch + name = "Firewatch" + icon_state = "firewatch" + +/datum/sprite_accessory/insect_fluff/gothic + name = "Gothic" + icon_state = "gothic" + +/datum/sprite_accessory/insect_fluff/jungle + name = "Jungle" + icon_state = "jungle" + +/datum/sprite_accessory/insect_fluff/lovers + name = "Lovers" + icon_state = "lovers" + +/datum/sprite_accessory/insect_fluff/moonfly + name = "Moon Fly" + icon_state = "moonfly" + +/datum/sprite_accessory/insect_fluff/oakworm + name = "Oak Worm" + icon_state = "oakworm" + +/datum/sprite_accessory/insect_fluff/plain + name = "Plain" + icon_state = "plain" + +/datum/sprite_accessory/insect_fluff/poison + name = "Poison" + icon_state = "poison" + +/datum/sprite_accessory/insect_fluff/ragged + name = "Ragged" + icon_state = "ragged" + +/datum/sprite_accessory/insect_fluff/reddish + name = "Reddish" + icon_state = "redish" + +/datum/sprite_accessory/insect_fluff/royal + name = "Royal" + icon_state = "royal" + +/datum/sprite_accessory/insect_fluff/snow + name = "Snow" + icon_state = "snow" + +/datum/sprite_accessory/insect_fluff/whitefly + name = "White Fly" + icon_state = "whitefly" + +/datum/sprite_accessory/insect_fluff/witchwing + name = "Witch Wing" + icon_state = "witchwing" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/ears.dm b/code/modules/mob/dead/new_player/sprite_accessories/ears.dm index 1e6c797364..7515ce560a 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/ears.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/ears.dm @@ -41,7 +41,7 @@ extra = TRUE extra_color_src = NONE -/datum/sprite_accessory/ears/human/bigwolfdark +/datum/sprite_accessory/ears/human/bigwolfdark //ignore alphabetical sort here for ease-of-use name = "Dark Big Wolf" icon_state = "bigwolfdark" icon = 'modular_citadel/icons/mob/mam_ears.dmi' @@ -55,6 +55,12 @@ extra = TRUE extra_color_src = NONE +/datum/sprite_accessory/ears/bunny + name = "Bunny" + icon_state = "bunny" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + /datum/sprite_accessory/ears/cat name = "Cat" icon_state = "cat" @@ -74,6 +80,12 @@ icon = 'modular_citadel/icons/mob/mam_ears.dmi' color_src = MUTCOLORS3 +/datum/sprite_accessory/ears/lab + name = "Dog, Floppy" + icon_state = "lab" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + /datum/sprite_accessory/ears/human/eevee name = "Eevee" icon_state = "eevee" @@ -115,12 +127,6 @@ icon_state = "jellyfish" color_src = HAIR -/datum/sprite_accessory/ears/lab - name = "Dog, Floppy" - icon_state = "lab" - color_src = MATRIXED - icon = 'modular_citadel/icons/mob/mam_ears.dmi' - /datum/sprite_accessory/ears/murid name = "Murid" icon_state = "murid" @@ -133,18 +139,18 @@ color_src = MATRIXED icon = 'modular_citadel/icons/mob/mam_ears.dmi' -/datum/sprite_accessory/ears/human/pede - name = "Scolipede" - icon_state = "pede" - icon = 'modular_citadel/icons/mob/mam_ears.dmi' - color_src = MATRIXED - /datum/sprite_accessory/ears/human/rabbit name = "Rabbit" icon_state = "rabbit" color_src = MATRIXED icon = 'modular_citadel/icons/mob/mam_ears.dmi' +/datum/sprite_accessory/ears/human/pede + name = "Scolipede" + icon_state = "pede" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + /datum/sprite_accessory/ears/human/sergal name = "Sergal" icon_state = "sergal" @@ -169,12 +175,6 @@ color_src = MATRIXED icon = 'modular_citadel/icons/mob/mam_ears.dmi' -/datum/sprite_accessory/ears/bunny - name = "Bunny" - icon_state = "bunny" - color_src = MATRIXED - icon = 'modular_citadel/icons/mob/mam_ears.dmi' - /****************************************** *************** Furry Ears **************** *******************************************/ @@ -216,7 +216,7 @@ extra = TRUE extra_color_src = NONE -/datum/sprite_accessory/ears/mam_ears/bigwolfdark +/datum/sprite_accessory/ears/mam_ears/bigwolfdark //alphabetical sort ignored here for ease-of-use name = "Dark Big Wolf" icon_state = "bigwolfdark" @@ -226,6 +226,10 @@ extra = TRUE extra_color_src = NONE +/datum/sprite_accessory/ears/mam_ears/bunny + name = "Bunny" + icon_state = "bunny" + /datum/sprite_accessory/ears/mam_ears/cat name = "Cat" icon_state = "cat" @@ -256,13 +260,11 @@ name = "Eevee" icon_state = "eevee" - /datum/sprite_accessory/ears/mam_ears/elf name = "Elf" icon_state = "elf" color_src = MUTCOLORS3 - /datum/sprite_accessory/ears/mam_ears/elephant name = "Elephant" icon_state = "elephant" @@ -283,15 +285,15 @@ name = "Husky" icon_state = "wolf" -/datum/sprite_accessory/ears/mam_ears/kangaroo - name = "kangaroo" - icon_state = "kangaroo" - /datum/sprite_accessory/ears/mam_ears/jellyfish name = "Jellyfish" icon_state = "jellyfish" color_src = HAIR +/datum/sprite_accessory/ears/mam_ears/kangaroo + name = "kangaroo" + icon_state = "kangaroo" + /datum/sprite_accessory/ears/mam_ears/lab name = "Dog, Long" icon_state = "lab" @@ -304,18 +306,14 @@ name = "Otusian" icon_state = "otie" -/datum/sprite_accessory/ears/mam_ears/squirrel - name = "Squirrel" - icon_state = "squirrel" +/datum/sprite_accessory/ears/mam_ears/rabbit + name = "Rabbit" + icon_state = "rabbit" /datum/sprite_accessory/ears/mam_ears/pede name = "Scolipede" icon_state = "pede" -/datum/sprite_accessory/ears/mam_ears/rabbit - name = "Rabbit" - icon_state = "rabbit" - /datum/sprite_accessory/ears/mam_ears/sergal name = "Sergal" icon_state = "sergal" @@ -324,10 +322,10 @@ name = "skunk" icon_state = "skunk" +/datum/sprite_accessory/ears/mam_ears/squirrel + name = "Squirrel" + icon_state = "squirrel" + /datum/sprite_accessory/ears/mam_ears/wolf name = "Wolf" icon_state = "wolf" - -/datum/sprite_accessory/ears/mam_ears/bunny - name = "Bunny" - icon_state = "bunny" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/frills.dm b/code/modules/mob/dead/new_player/sprite_accessories/frills.dm index 18a76df116..49013161a9 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/frills.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/frills.dm @@ -11,6 +11,10 @@ icon_state = "none" relevant_layers = null +/datum/sprite_accessory/frills/aquatic + name = "Aquatic" + icon_state = "aqua" + /datum/sprite_accessory/frills/simple name = "Simple" icon_state = "simple" @@ -18,7 +22,3 @@ /datum/sprite_accessory/frills/short name = "Short" icon_state = "short" - -/datum/sprite_accessory/frills/aquatic - name = "Aquatic" - icon_state = "aqua" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/hair_face.dm b/code/modules/mob/dead/new_player/sprite_accessories/hair_face.dm index 34988f5656..a07fdaa5d3 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/hair_face.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/hair_face.dm @@ -6,6 +6,10 @@ gender = MALE // barf (unless you're a dorf, dorfs dig chix w/ beards :P) // please make sure they're sorted alphabetically and categorized +/datum/sprite_accessory/facial_hair/shaved //this is exempt from the alphabetical sort + name = "Shaved" + icon_state = null + gender = NEUTER /datum/sprite_accessory/facial_hair/threeoclock name = "Beard (3 o\'Clock)" @@ -135,11 +139,6 @@ name = "Mutton Chops with Moustache" icon_state = "facial_muttonmus" -/datum/sprite_accessory/facial_hair/shaved - name = "Shaved" - icon_state = null - gender = NEUTER - /datum/sprite_accessory/facial_hair/sideburn name = "Sideburns" icon_state = "facial_sideburns" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm b/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm index 138704067d..8e4e6ad617 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm @@ -9,6 +9,10 @@ // try to spell // you do not need to define _s or _l sub-states, game automatically does this for you +/datum/sprite_accessory/hair/bald //this is exempt from the alphabetical sort + name = "Bald" + icon_state = "bald" + /datum/sprite_accessory/hair/afro name = "Afro" icon_state = "hair_afro" @@ -25,10 +29,6 @@ name = "Ahoge" icon_state = "hair_antenna" -/datum/sprite_accessory/hair/bald - name = "Bald" - icon_state = "bald" - /datum/sprite_accessory/hair/balding name = "Balding Hair" icon_state = "hair_e" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/horns.dm b/code/modules/mob/dead/new_player/sprite_accessories/horns.dm index 3a65eaebfa..aff342c7a6 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/horns.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/horns.dm @@ -12,22 +12,10 @@ icon_state = "none" relevant_layers = null -/datum/sprite_accessory/horns/simple - name = "Simple" - icon_state = "simple" - -/datum/sprite_accessory/horns/short - name = "Short" - icon_state = "short" - /datum/sprite_accessory/horns/curled name = "Curled" icon_state = "curled" -/datum/sprite_accessory/horns/ram - name = "Ram" - icon_state = "ram" - /datum/sprite_accessory/horns/angler name = "Angeler" icon_state = "angler" @@ -40,3 +28,15 @@ /datum/sprite_accessory/horns/guilmon name = "Guilmon" icon_state = "guilmon" + +/datum/sprite_accessory/horns/ram + name = "Ram" + icon_state = "ram" + +/datum/sprite_accessory/horns/simple + name = "Simple" + icon_state = "simple" + +/datum/sprite_accessory/horns/short + name = "Short" + icon_state = "short" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/ipc_synths.dm b/code/modules/mob/dead/new_player/sprite_accessories/ipc_synths.dm index 110ac69201..6019245b80 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/ipc_synths.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/ipc_synths.dm @@ -11,96 +11,90 @@ name = "Blank" icon_state = "blank" -/datum/sprite_accessory/screen/pink - name = "Pink" - icon_state = "pink" - -/datum/sprite_accessory/screen/green - name = "Green" - icon_state = "green" - -/datum/sprite_accessory/screen/red - name = "Red" - icon_state = "red" - /datum/sprite_accessory/screen/blue name = "Blue" icon_state = "blue" -/datum/sprite_accessory/screen/yellow - name = "Yellow" - icon_state = "yellow" - -/datum/sprite_accessory/screen/shower - name = "Shower" - icon_state = "shower" - -/datum/sprite_accessory/screen/nature - name = "Nature" - icon_state = "nature" - -/datum/sprite_accessory/screen/eight - name = "Eight" - icon_state = "eight" - -/datum/sprite_accessory/screen/goggles - name = "Goggles" - icon_state = "goggles" - -/datum/sprite_accessory/screen/heart - name = "Heart" - icon_state = "heart" - -/datum/sprite_accessory/screen/monoeye - name = "Mono eye" - icon_state = "monoeye" - /datum/sprite_accessory/screen/breakout name = "Breakout" icon_state = "breakout" -/datum/sprite_accessory/screen/purple - name = "Purple" - icon_state = "purple" - -/datum/sprite_accessory/screen/scroll - name = "Scroll" - icon_state = "scroll" +/datum/sprite_accessory/screen/bsod + name = "BSOD" + icon_state = "bsod" /datum/sprite_accessory/screen/console name = "Console" icon_state = "console" -/datum/sprite_accessory/screen/rgb - name = "RGB" - icon_state = "rgb" +/datum/sprite_accessory/screen/eight + name = "Eight" + icon_state = "eight" + +/datum/sprite_accessory/screen/eyes + name = "Eyes" + icon_state = "eyes" + +/datum/sprite_accessory/screen/ecgwave + name = "ECG wave" + icon_state = "ecgwave" + +/datum/sprite_accessory/screen/green + name = "Green" + icon_state = "green" + +/datum/sprite_accessory/screen/goggles + name = "Goggles" + icon_state = "goggles" /datum/sprite_accessory/screen/golglider name = "Gol Glider" icon_state = "golglider" +/datum/sprite_accessory/screen/heart + name = "Heart" + icon_state = "heart" + +/datum/sprite_accessory/screen/pink + name = "Pink" + icon_state = "pink" + +/datum/sprite_accessory/screen/red + name = "Red" + icon_state = "red" + +/datum/sprite_accessory/screen/monoeye + name = "Mono eye" + icon_state = "monoeye" + +/datum/sprite_accessory/screen/nature + name = "Nature" + icon_state = "nature" + +/datum/sprite_accessory/screen/purple + name = "Purple" + icon_state = "purple" + /datum/sprite_accessory/screen/rainbow name = "Rainbow" icon_state = "rainbow" -/datum/sprite_accessory/screen/sunburst - name = "Sunburst" - icon_state = "sunburst" - -/datum/sprite_accessory/screen/static - name = "Static" - icon_state = "static" - -//Oracle Station sprites - -/datum/sprite_accessory/screen/bsod - name = "BSOD" - icon_state = "bsod" - /datum/sprite_accessory/screen/redtext name = "Red Text" icon_state = "retext" +/datum/sprite_accessory/screen/rgb + name = "RGB" + icon_state = "rgb" + +/datum/sprite_accessory/screen/scroll + name = "Scroll" + icon_state = "scroll" + +/datum/sprite_accessory/screen/shower + name = "Shower" + icon_state = "shower" + /datum/sprite_accessory/screen/sinewave name = "Sine wave" icon_state = "sinewave" @@ -109,22 +103,25 @@ name = "Square wave" icon_state = "squarwave" -/datum/sprite_accessory/screen/ecgwave - name = "ECG wave" - icon_state = "ecgwave" +/datum/sprite_accessory/screen/stars + name = "Stars" + icon_state = "stars" -/datum/sprite_accessory/screen/eyes - name = "Eyes" - icon_state = "eyes" +/datum/sprite_accessory/screen/static + name = "Static" + icon_state = "static" + +/datum/sprite_accessory/screen/sunburst + name = "Sunburst" + icon_state = "sunburst" /datum/sprite_accessory/screen/textdrop name = "Text drop" icon_state = "textdrop" -/datum/sprite_accessory/screen/stars - name = "Stars" - icon_state = "stars" - +/datum/sprite_accessory/screen/yellow + name = "Yellow" + icon_state = "yellow" /****************************************** ************** IPC Antennas *************** @@ -145,14 +142,6 @@ name = "Angled Antennae" icon_state = "antennae" -/datum/sprite_accessory/antenna/tvantennae - name = "TV Antennae" - icon_state = "tvantennae" - -/datum/sprite_accessory/antenna/cyberhead - name = "Cyberhead" - icon_state = "cyberhead" - /datum/sprite_accessory/antenna/antlers name = "Antlers" icon_state = "antlers" @@ -160,3 +149,11 @@ /datum/sprite_accessory/antenna/crowned name = "Crowned" icon_state = "crowned" + +/datum/sprite_accessory/antenna/cyberhead + name = "Cyberhead" + icon_state = "cyberhead" + +/datum/sprite_accessory/antenna/tvantennae + name = "TV Antennae" + icon_state = "tvantennae" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/legs_and_taurs.dm b/code/modules/mob/dead/new_player/sprite_accessories/legs_and_taurs.dm index d41f5ace22..6f7b955d8f 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/legs_and_taurs.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/legs_and_taurs.dm @@ -49,6 +49,13 @@ relevant_layers = null hide_legs = FALSE +/datum/sprite_accessory/taur/canine + name = "Canine" + icon_state = "canine" + taur_mode = STYLE_PAW_TAURIC + color_src = MUTCOLORS + extra = TRUE + /datum/sprite_accessory/taur/cow name = "Cow" icon_state = "cow" @@ -95,6 +102,13 @@ color_src = MUTCOLORS extra = TRUE +/datum/sprite_accessory/taur/feline + name = "Feline" + icon_state = "feline" + taur_mode = STYLE_PAW_TAURIC + color_src = MUTCOLORS + extra = TRUE + /datum/sprite_accessory/taur/horse name = "Horse" icon_state = "horse" @@ -126,17 +140,3 @@ taur_mode = STYLE_SNEK_TAURIC color_src = MUTCOLORS hide_legs = USE_SNEK_CLIP_MASK - -/datum/sprite_accessory/taur/canine - name = "Canine" - icon_state = "canine" - taur_mode = STYLE_PAW_TAURIC - color_src = MUTCOLORS - extra = TRUE - -/datum/sprite_accessory/taur/feline - name = "Feline" - icon_state = "feline" - taur_mode = STYLE_PAW_TAURIC - color_src = MUTCOLORS - extra = TRUE diff --git a/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm b/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm index f514ef54db..99d2c67cc9 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm @@ -7,14 +7,19 @@ var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD) return ((H.wear_mask && (H.wear_mask.flags_inv & HIDESNOUT)) || (H.head && (H.head.flags_inv & HIDESNOUT)) || !HD || HD.status == BODYPART_ROBOTIC) -/datum/sprite_accessory/snouts/sharp - name = "Sharp" - icon_state = "sharp" +/datum/sprite_accessory/snout/guilmon + name = "Guilmon" + icon_state = "guilmon" + color_src = MATRIXED /datum/sprite_accessory/snouts/round name = "Round" icon_state = "round" +/datum/sprite_accessory/snouts/sharp + name = "Sharp" + icon_state = "sharp" + /datum/sprite_accessory/snouts/sharplight name = "Sharp + Light" icon_state = "sharplight" @@ -23,11 +28,6 @@ name = "Round + Light" icon_state = "roundlight" -/datum/sprite_accessory/snout/guilmon - name = "Guilmon" - icon_state = "guilmon" - color_src = MATRIXED - //christ this was a mistake, but it's here just in case someone wants to selectively fix -- Pooj /************* Lizard compatable snoots *********** /datum/sprite_accessory/snouts/bird @@ -192,11 +192,19 @@ extra = TRUE extra_color_src = MUTCOLORS3 -/datum/sprite_accessory/snouts/mam_snouts/skulldog - name = "Skulldog" - icon_state = "skulldog" +/datum/sprite_accessory/snouts/mam_snouts/husky + name = "Husky" + icon_state = "husky" + +/datum/sprite_accessory/snouts/mam_snouts/rhino + name = "Horn" + icon_state = "rhino" extra = TRUE - extra_color_src = MATRIXED + extra = MUTCOLORS3 + +/datum/sprite_accessory/snouts/mam_snouts/rodent + name = "Rodent" + icon_state = "rodent" /datum/sprite_accessory/snouts/mam_snouts/lcanid name = "Mammal, Long" @@ -226,32 +234,20 @@ name = "Mammal, Thick ALT" icon_state = "wolfalt" -/datum/sprite_accessory/snouts/mam_snouts/redpanda - name = "WahCoon" - icon_state = "wah" - -/datum/sprite_accessory/snouts/mam_snouts/redpandaalt - name = "WahCoon ALT" - icon_state = "wahalt" - -/datum/sprite_accessory/snouts/mam_snouts/rhino - name = "Horn" - icon_state = "rhino" - extra = TRUE - extra = MUTCOLORS3 - -/datum/sprite_accessory/snouts/mam_snouts/rodent - name = "Rodent" - icon_state = "rodent" - -/datum/sprite_accessory/snouts/mam_snouts/husky - name = "Husky" - icon_state = "husky" - /datum/sprite_accessory/snouts/mam_snouts/otie name = "Otie" icon_state = "otie" +/datum/sprite_accessory/snouts/mam_snouts/round + name = "Round" + icon_state = "round" + color_src = MUTCOLORS + +/datum/sprite_accessory/snouts/mam_snouts/roundlight + name = "Round + Light" + icon_state = "roundlight" + color_src = MUTCOLORS + /datum/sprite_accessory/snouts/mam_snouts/pede name = "Scolipede" icon_state = "pede" @@ -268,30 +264,33 @@ name = "hShark" icon_state = "hshark" -/datum/sprite_accessory/snouts/mam_snouts/toucan - name = "Toucan" - icon_state = "toucan" - /datum/sprite_accessory/snouts/mam_snouts/sharp name = "Sharp" icon_state = "sharp" color_src = MUTCOLORS -/datum/sprite_accessory/snouts/mam_snouts/round - name = "Round" - icon_state = "round" - color_src = MUTCOLORS - /datum/sprite_accessory/snouts/mam_snouts/sharplight name = "Sharp + Light" icon_state = "sharplight" color_src = MUTCOLORS -/datum/sprite_accessory/snouts/mam_snouts/roundlight - name = "Round + Light" - icon_state = "roundlight" - color_src = MUTCOLORS +/datum/sprite_accessory/snouts/mam_snouts/skulldog + name = "Skulldog" + icon_state = "skulldog" + extra = TRUE + extra_color_src = MATRIXED +/datum/sprite_accessory/snouts/mam_snouts/toucan + name = "Toucan" + icon_state = "toucan" + +/datum/sprite_accessory/snouts/mam_snouts/redpanda + name = "WahCoon" + icon_state = "wah" + +/datum/sprite_accessory/snouts/mam_snouts/redpandaalt + name = "WahCoon ALT" + icon_state = "wahalt" /****************************************** **************** Snouts ******************* @@ -318,6 +317,16 @@ extra = TRUE extra_color_src = MUTCOLORS3 +/datum/sprite_accessory/snouts/mam_snouts/frhino + name = "Horn (Top)" + icon_state = "frhino" + extra = TRUE + extra = MUTCOLORS3 + +/datum/sprite_accessory/snouts/mam_snouts/fhusky + name = "Husky (Top)" + icon_state = "fhusky" + /datum/sprite_accessory/snouts/mam_snouts/flcanid name = "Mammal, Long (Top)" icon_state = "flcanid" @@ -346,27 +355,23 @@ name = "Mammal, Thick ALT (Top)" icon_state = "fwolfalt" -/datum/sprite_accessory/snouts/mam_snouts/fredpanda - name = "WahCoon (Top)" - icon_state = "fwah" - -/datum/sprite_accessory/snouts/mam_snouts/frhino - name = "Horn (Top)" - icon_state = "frhino" - extra = TRUE - extra = MUTCOLORS3 +/datum/sprite_accessory/snouts/mam_snouts/fotie + name = "Otie (Top)" + icon_state = "fotie" /datum/sprite_accessory/snouts/mam_snouts/frodent name = "Rodent (Top)" icon_state = "frodent" -/datum/sprite_accessory/snouts/mam_snouts/fhusky - name = "Husky (Top)" - icon_state = "fhusky" +/datum/sprite_accessory/snouts/mam_snouts/fround + name = "Round (Top)" + icon_state = "fround" + color_src = MUTCOLORS -/datum/sprite_accessory/snouts/mam_snouts/fotie - name = "Otie (Top)" - icon_state = "fotie" +/datum/sprite_accessory/snouts/mam_snouts/froundlight + name = "Round + Light (Top)" + icon_state = "froundlight" + color_src = MUTCOLORS /datum/sprite_accessory/snouts/mam_snouts/fpede name = "Scolipede (Top)" @@ -380,26 +385,20 @@ name = "Shark (Top)" icon_state = "fshark" -/datum/sprite_accessory/snouts/mam_snouts/ftoucan - name = "Toucan (Top)" - icon_state = "ftoucan" - /datum/sprite_accessory/snouts/mam_snouts/fsharp name = "Sharp (Top)" icon_state = "fsharp" color_src = MUTCOLORS -/datum/sprite_accessory/snouts/mam_snouts/fround - name = "Round (Top)" - icon_state = "fround" - color_src = MUTCOLORS - /datum/sprite_accessory/snouts/mam_snouts/fsharplight name = "Sharp + Light (Top)" icon_state = "fsharplight" color_src = MUTCOLORS -/datum/sprite_accessory/snouts/mam_snouts/froundlight - name = "Round + Light (Top)" - icon_state = "froundlight" - color_src = MUTCOLORS +/datum/sprite_accessory/snouts/mam_snouts/ftoucan + name = "Toucan (Top)" + icon_state = "ftoucan" + +/datum/sprite_accessory/snouts/mam_snouts/fredpanda + name = "WahCoon (Top)" + icon_state = "fwah" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/socks.dm b/code/modules/mob/dead/new_player/sprite_accessories/socks.dm index 19ec677a72..ffb808eede 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/socks.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/socks.dm @@ -20,6 +20,10 @@ name = "Knee-high - Bee" icon_state = "bee_knee" +/datum/sprite_accessory/underwear/socks/christmas_knee + name = "Knee-High - Christmas" + icon_state = "christmas_knee" + /datum/sprite_accessory/underwear/socks/commie_knee name = "Knee-High - Commie" icon_state = "commie_knee" @@ -32,6 +36,14 @@ name = "Knee-high - Rainbow" icon_state = "rainbow_knee" +/datum/sprite_accessory/underwear/socks/candycaner_knee + name = "Knee-High - Red Candy Cane" + icon_state = "candycaner_knee" + +/datum/sprite_accessory/underwear/socks/candycaneg_knee //ignore alphabetisation for ease of use in scenarios like this + name = "Knee-High - Green Candy Cane" + icon_state = "candycaneg_knee" + /datum/sprite_accessory/underwear/socks/striped_knee name = "Knee-high - Striped" icon_state = "striped_knee" @@ -46,18 +58,6 @@ name = "Knee-High - UK" icon_state = "uk_knee" -/datum/sprite_accessory/underwear/socks/christmas_knee - name = "Knee-High - Christmas" - icon_state = "christmas_knee" - -/datum/sprite_accessory/underwear/socks/candycaner_knee - name = "Knee-High - Red Candy Cane" - icon_state = "candycaner_knee" - -/datum/sprite_accessory/underwear/socks/candycaneg_knee - name = "Knee-High - Green Candy Cane" - icon_state = "candycaneg_knee" - /datum/sprite_accessory/underwear/socks/socks_norm name = "Normal" icon_state = "socks_norm" @@ -129,22 +129,34 @@ name = "Thigh-high - Bee" icon_state = "bee_thigh" +/datum/sprite_accessory/underwear/socks/christmas_thigh + name = "Thigh-high - Christmas" + icon_state = "christmas_thigh" + /datum/sprite_accessory/underwear/socks/commie_thigh name = "Thigh-high - Commie" icon_state = "commie_thigh" -/datum/sprite_accessory/underwear/socks/usa_thigh - name = "Thigh-high - Freedom" - icon_state = "assblastusa_thigh" - /datum/sprite_accessory/underwear/socks/fishnet name = "Thigh-high - Fishnet" icon_state = "fishnet" +/datum/sprite_accessory/underwear/socks/usa_thigh + name = "Thigh-high - Freedom" + icon_state = "assblastusa_thigh" + /datum/sprite_accessory/underwear/socks/rainbow_thigh name = "Thigh-high - Rainbow" icon_state = "rainbow_thigh" +/datum/sprite_accessory/underwear/socks/candycaner_thigh + name = "Thigh-high - Red Candy Cane" + icon_state = "candycaner_thigh" + +/datum/sprite_accessory/underwear/socks/candycaneg_thigh + name = "Thigh-high - Green Candy Cane" + icon_state = "candycaneg_thigh" + /datum/sprite_accessory/underwear/socks/striped_thigh name = "Thigh-high - Striped" icon_state = "striped_thigh" @@ -157,16 +169,4 @@ /datum/sprite_accessory/underwear/socks/uk_thigh name = "Thigh-high - UK" - icon_state = "uk_thigh" - -/datum/sprite_accessory/underwear/socks/christmas_thigh - name = "Thigh-high - Christmas" - icon_state = "christmas_thigh" - -/datum/sprite_accessory/underwear/socks/candycaner_thigh - name = "Thigh-high - Red Candy Cane" - icon_state = "candycaner_thigh" - -/datum/sprite_accessory/underwear/socks/candycaneg_thigh - name = "Thigh-high - Green Candy Cane" - icon_state = "candycaneg_thigh" \ No newline at end of file + icon_state = "uk_thigh" \ No newline at end of file diff --git a/code/modules/mob/dead/new_player/sprite_accessories/spines.dm b/code/modules/mob/dead/new_player/sprite_accessories/spines.dm index 83415ed375..5d7207c934 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/spines.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/spines.dm @@ -21,21 +21,13 @@ name = "None" icon_state = "none" -/datum/sprite_accessory/spines/short - name = "Short" - icon_state = "short" +/datum/sprite_accessory/spines/aqautic + name = "Aquatic" + icon_state = "aqua" -/datum/sprite_accessory/spines_animated/short - name = "Short" - icon_state = "short" - -/datum/sprite_accessory/spines/shortmeme - name = "Short + Membrane" - icon_state = "shortmeme" - -/datum/sprite_accessory/spines_animated/shortmeme - name = "Short + Membrane" - icon_state = "shortmeme" +/datum/sprite_accessory/spines_animated/aqautic + name = "Aquatic" + icon_state = "aqua" /datum/sprite_accessory/spines/long name = "Long" @@ -53,10 +45,18 @@ name = "Long + Membrane" icon_state = "longmeme" -/datum/sprite_accessory/spines/aqautic - name = "Aquatic" - icon_state = "aqua" +/datum/sprite_accessory/spines/short + name = "Short" + icon_state = "short" -/datum/sprite_accessory/spines_animated/aqautic - name = "Aquatic" - icon_state = "aqua" +/datum/sprite_accessory/spines_animated/short + name = "Short" + icon_state = "short" + +/datum/sprite_accessory/spines/shortmeme + name = "Short + Membrane" + icon_state = "shortmeme" + +/datum/sprite_accessory/spines_animated/shortmeme + name = "Short + Membrane" + icon_state = "shortmeme" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/synthliz.dm b/code/modules/mob/dead/new_player/sprite_accessories/synthliz.dm index 16531d8f06..9addd15dca 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/synthliz.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/synthliz.dm @@ -25,12 +25,6 @@ icon_state = "synthliz_tertunder" //Synth body markings -/datum/sprite_accessory/mam_body_markings/synthliz - recommended_species = list("synthliz") - icon = 'modular_citadel/icons/mob/synthliz_body_markings.dmi' - name = "Synthetic Lizard - Plates" - icon_state = "synthlizscutes" - /datum/sprite_accessory/mam_body_markings/synthliz/synthliz_pecs icon = 'modular_citadel/icons/mob/synthliz_body_markings.dmi' name = "Synthetic Lizard - Pecs" @@ -41,6 +35,12 @@ name = "Synthetic Lizard - Pecs Light" icon_state = "synthlizpecslight" +/datum/sprite_accessory/mam_body_markings/synthliz + recommended_species = list("synthliz") + icon = 'modular_citadel/icons/mob/synthliz_body_markings.dmi' + name = "Synthetic Lizard - Plates" + icon_state = "synthlizscutes" + //Synth tails /datum/sprite_accessory/tails/mam_tails/synthliz recommended_species = list("synthliz") @@ -70,17 +70,17 @@ name = "Synthetic Lizard - Curled" icon_state = "synth_curled" -/datum/sprite_accessory/antenna/synthliz/synthliz_thick +/datum/sprite_accessory/antenna/synthliz/synth_horns icon = 'modular_citadel/icons/mob/synthliz_antennas.dmi' color_src = MUTCOLORS - name = "Synthetic Lizard - Thick" - icon_state = "synth_thick" + name = "Synthetic Lizard - Horns" + icon_state = "synth_horns" -/datum/sprite_accessory/antenna/synthliz/synth_thicklight +/datum/sprite_accessory/antenna/synthliz/synth_hornslight icon = 'modular_citadel/icons/mob/synthliz_antennas.dmi' color_src = MATRIXED - name = "Synthetic Lizard - Thick Light" - icon_state = "synth_thicklight" + name = "Synthetic Lizard - Horns Light" + icon_state = "synth_hornslight" /datum/sprite_accessory/antenna/synthliz/synth_short icon = 'modular_citadel/icons/mob/synthliz_antennas.dmi' @@ -100,17 +100,17 @@ name = "Synthetic Lizard - Sharp Light" icon_state = "synth_sharplight" -/datum/sprite_accessory/antenna/synthliz/synth_horns +/datum/sprite_accessory/antenna/synthliz/synthliz_thick icon = 'modular_citadel/icons/mob/synthliz_antennas.dmi' color_src = MUTCOLORS - name = "Synthetic Lizard - Horns" - icon_state = "synth_horns" + name = "Synthetic Lizard - Thick" + icon_state = "synth_thick" -/datum/sprite_accessory/antenna/synthliz/synth_hornslight +/datum/sprite_accessory/antenna/synthliz/synth_thicklight icon = 'modular_citadel/icons/mob/synthliz_antennas.dmi' color_src = MATRIXED - name = "Synthetic Lizard - Horns Light" - icon_state = "synth_hornslight" + name = "Synthetic Lizard - Thick Light" + icon_state = "synth_thicklight" //Synth Taurs (Ported from Virgo) /datum/sprite_accessory/taur/synthliz diff --git a/code/modules/mob/dead/new_player/sprite_accessories/tails.dm b/code/modules/mob/dead/new_player/sprite_accessories/tails.dm index 812e0c052c..d9e2de1525 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/tails.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/tails.dm @@ -18,38 +18,7 @@ /datum/sprite_accessory/tails_animated/lizard/is_not_visible(var/mob/living/carbon/human/H, var/tauric) return (((H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR)) || tauric) || H.dna.species.mutant_bodyparts["tail_lizard"]) -/datum/sprite_accessory/tails/lizard/smooth - name = "Smooth" - icon_state = "smooth" - -/datum/sprite_accessory/tails_animated/lizard/smooth - name = "Smooth" - icon_state = "smooth" - -/datum/sprite_accessory/tails/lizard/dtiger - name = "Dark Tiger" - icon_state = "dtiger" - -/datum/sprite_accessory/tails_animated/lizard/dtiger - name = "Dark Tiger" - icon_state = "dtiger" - -/datum/sprite_accessory/tails/lizard/ltiger - name = "Light Tiger" - icon_state = "ltiger" - -/datum/sprite_accessory/tails_animated/lizard/ltiger - name = "Light Tiger" - icon_state = "ltiger" - -/datum/sprite_accessory/tails/lizard/spikes - name = "Spikes" - icon_state = "spikes" - -/datum/sprite_accessory/tails_animated/lizard/spikes - name = "Spikes" - icon_state = "spikes" - +//this goes first regardless of alphabetical order /datum/sprite_accessory/tails/lizard/none name = "None" icon_state = "None" @@ -72,11 +41,13 @@ color_src = MATRIXED icon = 'modular_citadel/icons/mob/mam_tails.dmi' -/datum/sprite_accessory/body_markings/guilmon - name = "Guilmon" - icon_state = "guilmon" - color_src = MATRIXED - icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' +/datum/sprite_accessory/tails/lizard/dtiger + name = "Dark Tiger" + icon_state = "dtiger" + +/datum/sprite_accessory/tails_animated/lizard/dtiger + name = "Dark Tiger" + icon_state = "dtiger" /datum/sprite_accessory/tails/lizard/guilmon name = "Guilmon" @@ -90,6 +61,30 @@ color_src = MATRIXED icon = 'modular_citadel/icons/mob/mam_tails.dmi' +/datum/sprite_accessory/tails/lizard/ltiger + name = "Light Tiger" + icon_state = "ltiger" + +/datum/sprite_accessory/tails_animated/lizard/ltiger + name = "Light Tiger" + icon_state = "ltiger" + +/datum/sprite_accessory/tails/lizard/smooth + name = "Smooth" + icon_state = "smooth" + +/datum/sprite_accessory/tails_animated/lizard/smooth + name = "Smooth" + icon_state = "smooth" + +/datum/sprite_accessory/tails/lizard/spikes + name = "Spikes" + icon_state = "spikes" + +/datum/sprite_accessory/tails_animated/lizard/spikes + name = "Spikes" + icon_state = "spikes" + /****************************************** ************** Human Tails **************** *******************************************/ @@ -107,18 +102,6 @@ /datum/sprite_accessory/tails_animated/human/is_not_visible(var/mob/living/carbon/human/H, var/tauric) return (((H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR)) || tauric)|| H.dna.species.mutant_bodyparts["tail_human"]) -/datum/sprite_accessory/tails/human/ailurus - name = "Red Panda" - icon_state = "wah" - icon = 'modular_citadel/icons/mob/mam_tails.dmi' - color_src = MATRIXED - -/datum/sprite_accessory/tails_animated/human/ailurus - name = "Red Panda" - icon_state = "wah" - icon = 'modular_citadel/icons/mob/mam_tails.dmi' - color_src = MATRIXED - /datum/sprite_accessory/tails/human/axolotl name = "Axolotl" icon_state = "axolotl" @@ -199,6 +182,14 @@ icon = 'modular_citadel/icons/mob/mam_tails.dmi' color_src = MATRIXED +/datum/sprite_accessory/tails/human/corvid + name = "Corvid" + icon_state = "crow" + +/datum/sprite_accessory/tails_animated/human/corvid + name = "Corvid" + icon_state = "crow" + /datum/sprite_accessory/tails/human/cow name = "Cow" icon_state = "cow" @@ -211,13 +202,25 @@ icon = 'modular_citadel/icons/mob/mam_tails.dmi' color_src = MATRIXED -/datum/sprite_accessory/tails/human/corvid - name = "Corvid" - icon_state = "crow" +/datum/sprite_accessory/tails/human/dtiger + name = "Dark Tiger" + icon_state = "dtiger" -/datum/sprite_accessory/tails_animated/human/corvid - name = "Corvid" - icon_state = "crow" +/datum/sprite_accessory/tails_animated/human/dtiger + name = "Dark Tiger" + icon_state = "dtiger" + +/datum/sprite_accessory/tails/human/datashark + name = "datashark" + icon_state = "datashark" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/human/datashark + name = "datashark" + icon_state = "datashark" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' /datum/sprite_accessory/tails/human/eevee name = "Eevee" @@ -298,7 +301,7 @@ color_src = MATRIXED /datum/sprite_accessory/tails_animated/human/insect - name = "insect" + name = "Insect" icon_state = "insect" icon = 'modular_citadel/icons/mob/mam_tails.dmi' color_src = MATRIXED @@ -315,6 +318,14 @@ color_src = MATRIXED icon = 'modular_citadel/icons/mob/mam_tails.dmi' +/datum/sprite_accessory/tails/human/ltiger + name = "Light Tiger" + icon_state = "ltiger" + +/datum/sprite_accessory/tails_animated/human/ltiger + name = "Light Tiger" + icon_state = "ltiger" + /datum/sprite_accessory/tails/human/murid name = "Murid" icon_state = "murid" @@ -327,18 +338,6 @@ color_src = MATRIXED icon = 'modular_citadel/icons/mob/mam_tails.dmi' -/datum/sprite_accessory/tails/human/otie - name = "Otusian" - icon_state = "otie" - color_src = MATRIXED - icon = 'modular_citadel/icons/mob/mam_tails.dmi' - -/datum/sprite_accessory/tails_animated/human/otie - name = "Otusian" - icon_state = "otie" - color_src = MATRIXED - icon = 'modular_citadel/icons/mob/mam_tails.dmi' - /datum/sprite_accessory/tails/orca name = "Orca" icon_state = "orca" @@ -351,15 +350,15 @@ color_src = MATRIXED icon = 'modular_citadel/icons/mob/mam_tails.dmi' -/datum/sprite_accessory/tails/human/pede - name = "Scolipede" - icon_state = "pede" +/datum/sprite_accessory/tails/human/otie + name = "Otusian" + icon_state = "otie" color_src = MATRIXED icon = 'modular_citadel/icons/mob/mam_tails.dmi' -/datum/sprite_accessory/tails_animated/human/pede - name = "Scolipede" - icon_state = "pede" +/datum/sprite_accessory/tails_animated/human/otie + name = "Otusian" + icon_state = "otie" color_src = MATRIXED icon = 'modular_citadel/icons/mob/mam_tails.dmi' @@ -375,6 +374,30 @@ color_src = MATRIXED icon = 'modular_citadel/icons/mob/mam_tails.dmi' +/datum/sprite_accessory/tails/human/ailurus + name = "Red Panda" + icon_state = "wah" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails_animated/human/ailurus + name = "Red Panda" + icon_state = "wah" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails/human/pede + name = "Scolipede" + icon_state = "pede" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/human/pede + name = "Scolipede" + icon_state = "pede" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + /datum/sprite_accessory/tails/human/sergal name = "Sergal" icon_state = "sergal" @@ -387,6 +410,18 @@ color_src = MATRIXED icon = 'modular_citadel/icons/mob/mam_tails.dmi' +/datum/sprite_accessory/tails/human/shark + name = "Shark" + icon_state = "shark" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/human/shark + name = "Shark" + icon_state = "shark" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + /datum/sprite_accessory/tails/human/skunk name = "skunk" icon_state = "skunk" @@ -415,30 +450,6 @@ name = "Spikes" icon_state = "spikes" -/datum/sprite_accessory/tails/human/shark - name = "Shark" - icon_state = "shark" - color_src = MATRIXED - icon = 'modular_citadel/icons/mob/mam_tails.dmi' - -/datum/sprite_accessory/tails_animated/human/shark - name = "Shark" - icon_state = "shark" - color_src = MATRIXED - icon = 'modular_citadel/icons/mob/mam_tails.dmi' - -/datum/sprite_accessory/tails/human/datashark - name = "datashark" - icon_state = "datashark" - color_src = MATRIXED - icon = 'modular_citadel/icons/mob/mam_tails.dmi' - -/datum/sprite_accessory/tails_animated/human/datashark - name = "datashark" - icon_state = "datashark" - color_src = MATRIXED - icon = 'modular_citadel/icons/mob/mam_tails.dmi' - /datum/sprite_accessory/tails/human/straighttail name = "Straight Tail" icon_state = "straighttail" @@ -495,22 +506,6 @@ color_src = MATRIXED icon = 'modular_citadel/icons/mob/mam_tails.dmi' -/datum/sprite_accessory/tails/human/dtiger - name = "Dark Tiger" - icon_state = "dtiger" - -/datum/sprite_accessory/tails_animated/human/dtiger - name = "Dark Tiger" - icon_state = "dtiger" - -/datum/sprite_accessory/tails/human/ltiger - name = "Light Tiger" - icon_state = "ltiger" - -/datum/sprite_accessory/tails_animated/human/ltiger - name = "Light Tiger" - icon_state = "ltiger" - /datum/sprite_accessory/tails/human/wolf name = "Wolf" icon_state = "wolf" @@ -554,16 +549,6 @@ icon_state = "none" relevant_layers = null -/datum/sprite_accessory/tails/mam_tails/ailurus - name = "Red Panda" - icon_state = "wah" - extra = TRUE - -/datum/sprite_accessory/tails_animated/mam_tails_animated/ailurus - name = "Red Panda" - icon_state = "wah" - extra = TRUE - /datum/sprite_accessory/tails/mam_tails/axolotl name = "Axolotl" icon_state = "axolotl" @@ -638,6 +623,18 @@ name = "Cow" icon_state = "cow" +/datum/sprite_accessory/tails/mam_tails/dtiger + name = "Dark Tiger" + icon_state = "dtiger" + color_src = MUTCOLORS + icon = 'icons/mob/mutant_bodyparts.dmi' + +/datum/sprite_accessory/tails_animated/mam_tails_animated/dtiger + name = "Dark Tiger" + icon_state = "dtiger" + color_src = MUTCOLORS + icon = 'icons/mob/mutant_bodyparts.dmi' + /datum/sprite_accessory/tails/mam_tails/eevee name = "Eevee" icon_state = "eevee" @@ -728,6 +725,18 @@ datum/sprite_accessory/tails/mam_tails/insect name = "Lab" icon_state = "lab" +/datum/sprite_accessory/tails/mam_tails/ltiger + name = "Light Tiger" + icon_state = "ltiger" + color_src = MUTCOLORS + icon = 'icons/mob/mutant_bodyparts.dmi' + +/datum/sprite_accessory/tails_animated/mam_tails_animated/ltiger + name = "Light Tiger" + icon_state = "ltiger" + color_src = MUTCOLORS + icon = 'icons/mob/mutant_bodyparts.dmi' + /datum/sprite_accessory/tails/mam_tails/murid name = "Murid" icon_state = "murid" @@ -736,14 +745,6 @@ datum/sprite_accessory/tails/mam_tails/insect name = "Murid" icon_state = "murid" -/datum/sprite_accessory/tails/mam_tails/otie - name = "Otusian" - icon_state = "otie" - -/datum/sprite_accessory/tails_animated/mam_tails_animated/otie - name = "Otusian" - icon_state = "otie" - /datum/sprite_accessory/tails/mam_tails/orca name = "Orca" icon_state = "orca" @@ -752,13 +753,13 @@ datum/sprite_accessory/tails/mam_tails/insect name = "Orca" icon_state = "orca" -/datum/sprite_accessory/tails/mam_tails/pede - name = "Scolipede" - icon_state = "pede" +/datum/sprite_accessory/tails/mam_tails/otie + name = "Otusian" + icon_state = "otie" -/datum/sprite_accessory/tails_animated/mam_tails_animated/pede - name = "Scolipede" - icon_state = "pede" +/datum/sprite_accessory/tails_animated/mam_tails_animated/otie + name = "Otusian" + icon_state = "otie" /datum/sprite_accessory/tails/mam_tails/rabbit name = "Rabbit" @@ -768,6 +769,24 @@ datum/sprite_accessory/tails/mam_tails/insect name = "Rabbit" icon_state = "rabbit" +/datum/sprite_accessory/tails/mam_tails/ailurus + name = "Red Panda" + icon_state = "wah" + extra = TRUE + +/datum/sprite_accessory/tails_animated/mam_tails_animated/ailurus + name = "Red Panda" + icon_state = "wah" + extra = TRUE + +/datum/sprite_accessory/tails/mam_tails/pede + name = "Scolipede" + icon_state = "pede" + +/datum/sprite_accessory/tails_animated/mam_tails_animated/pede + name = "Scolipede" + icon_state = "pede" + /datum/sprite_accessory/tails/mam_tails/sergal name = "Sergal" icon_state = "sergal" @@ -776,6 +795,22 @@ datum/sprite_accessory/tails/mam_tails/insect name = "Sergal" icon_state = "sergal" +/datum/sprite_accessory/tails/mam_tails/shark + name = "Shark" + icon_state = "shark" + +/datum/sprite_accessory/tails_animated/mam_tails_animated/shark + name = "Shark" + icon_state = "shark" + +/datum/sprite_accessory/tails/mam_tails/shepherd + name = "Shepherd" + icon_state = "shepherd" + +/datum/sprite_accessory/tails_animated/mam_tails_animated/shepherd + name = "Shepherd" + icon_state = "shepherd" + /datum/sprite_accessory/tails/mam_tails/skunk name = "Skunk" icon_state = "skunk" @@ -808,22 +843,6 @@ datum/sprite_accessory/tails/mam_tails/insect color_src = MUTCOLORS icon = 'icons/mob/mutant_bodyparts.dmi' -/datum/sprite_accessory/tails/mam_tails/shark - name = "Shark" - icon_state = "shark" - -/datum/sprite_accessory/tails_animated/mam_tails_animated/shark - name = "Shark" - icon_state = "shark" - -/datum/sprite_accessory/tails/mam_tails/shepherd - name = "Shepherd" - icon_state = "shepherd" - -/datum/sprite_accessory/tails_animated/mam_tails_animated/shepherd - name = "Shepherd" - icon_state = "shepherd" - /datum/sprite_accessory/tails/mam_tails/straighttail name = "Straight Tail" icon_state = "straighttail" @@ -864,30 +883,6 @@ datum/sprite_accessory/tails/mam_tails/insect name = "Tiger" icon_state = "tiger" -/datum/sprite_accessory/tails/mam_tails/dtiger - name = "Dark Tiger" - icon_state = "dtiger" - color_src = MUTCOLORS - icon = 'icons/mob/mutant_bodyparts.dmi' - -/datum/sprite_accessory/tails_animated/mam_tails_animated/dtiger - name = "Dark Tiger" - icon_state = "dtiger" - color_src = MUTCOLORS - icon = 'icons/mob/mutant_bodyparts.dmi' - -/datum/sprite_accessory/tails/mam_tails/ltiger - name = "Light Tiger" - icon_state = "ltiger" - color_src = MUTCOLORS - icon = 'icons/mob/mutant_bodyparts.dmi' - -/datum/sprite_accessory/tails_animated/mam_tails_animated/ltiger - name = "Light Tiger" - icon_state = "ltiger" - color_src = MUTCOLORS - icon = 'icons/mob/mutant_bodyparts.dmi' - /datum/sprite_accessory/tails/mam_tails/wolf name = "Wolf" icon_state = "wolf" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/undershirt.dm b/code/modules/mob/dead/new_player/sprite_accessories/undershirt.dm index b8282931d2..1be02c207e 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/undershirt.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/undershirt.dm @@ -12,6 +12,38 @@ // please make sure they're sorted alphabetically and categorized +/datum/sprite_accessory/underwear/top/cowboyshirt + name = "Cowboy Shirt Black" + icon_state = "cowboyshirt" + +/datum/sprite_accessory/underwear/top/cowboyshirt/red + name = "Cowboy Shirt Red" + icon_state = "cowboyshirt_red" + +/datum/sprite_accessory/underwear/top/cowboyshirt/navy + name = "Cowboy Shirt Navy" + icon_state = "cowboyshirt_navy" + +/datum/sprite_accessory/underwear/top/cowboyshirt/white + name = "Cowboy Shirt White" + icon_state = "cowboyshirt_white" + +/datum/sprite_accessory/underwear/top/cowboyshirt/s + name = "Cowboy Shirt Shortsleeved Black" + icon_state = "cowboyshirt_s" + +/datum/sprite_accessory/underwear/top/cowboyshirt/red/s + name = "Cowboy Shirt Shortsleeved Red" + icon_state = "cowboyshirt_reds" + +/datum/sprite_accessory/underwear/top/cowboyshirt/navy/s + name = "Cowboy Shirt Shortsleeved Navy" + icon_state = "cowboyshirt_navys" + +/datum/sprite_accessory/underwear/top/cowboyshirt/white/s + name = "Cowboy Shirt Shortsleeved White" + icon_state = "cowboyshirt_whites" + /datum/sprite_accessory/underwear/top/longjon name = "Long John Shirt" icon_state = "ljont" @@ -30,36 +62,6 @@ icon_state = "undershirt" has_color = TRUE -/datum/sprite_accessory/underwear/top/bowlingw - name = "Shirt - Bowling" - icon_state = "bowlingw" - has_color = TRUE - -/datum/sprite_accessory/underwear/top/bowling - name = "Shirt, Bowling - Red" - icon_state = "bowling" - -/datum/sprite_accessory/underwear/top/bowlingp - name = "Shirt, Bowling - Pink" - icon_state = "bowlingp" - -/datum/sprite_accessory/underwear/top/bowlinga - name = "Shirt, Bowling - Aqua" - icon_state = "bowlinga" - -/datum/sprite_accessory/underwear/top/bluejersey - name = "Shirt, Jersey - Blue" - icon_state = "shirt_bluejersey" - -/datum/sprite_accessory/underwear/top/redjersey - name = "Shirt, Jersey - Red" - icon_state = "shirt_redjersey" - -/datum/sprite_accessory/underwear/top/polo - name = "Shirt - Polo" - icon_state = "polo" - has_color = TRUE - /datum/sprite_accessory/underwear/top/alienshirt name = "Shirt - Alien" icon_state = "shirt_alien" @@ -72,6 +74,23 @@ name = "Shirt - Bee" icon_state = "bee_shirt" +/datum/sprite_accessory/underwear/top/bowlingw + name = "Shirt - Bowling" + icon_state = "bowlingw" + has_color = TRUE + +/datum/sprite_accessory/underwear/top/bowlinga + name = "Shirt, Bowling - Aqua" + icon_state = "bowlinga" + +/datum/sprite_accessory/underwear/top/bowling + name = "Shirt, Bowling - Red" + icon_state = "bowling" + +/datum/sprite_accessory/underwear/top/bowlingp + name = "Shirt, Bowling - Pink" + icon_state = "bowlingp" + /datum/sprite_accessory/underwear/top/clownshirt name = "Shirt - Clown" icon_state = "shirt_clown" @@ -88,6 +107,14 @@ name = "Shirt - I Love NT" icon_state = "ilovent" +/datum/sprite_accessory/underwear/top/bluejersey + name = "Shirt, Jersey - Blue" + icon_state = "shirt_bluejersey" + +/datum/sprite_accessory/underwear/top/redjersey + name = "Shirt, Jersey - Red" + icon_state = "shirt_redjersey" + /datum/sprite_accessory/underwear/top/lover name = "Shirt - Lover" icon_state = "lover" @@ -112,6 +139,11 @@ name = "Shirt - Pogoman" icon_state = "pogoman" +/datum/sprite_accessory/underwear/top/polo + name = "Shirt - Polo" + icon_state = "polo" + has_color = TRUE + /datum/sprite_accessory/underwear/top/question name = "Shirt - Question" icon_state = "shirt_question" @@ -120,6 +152,23 @@ name = "Shirt - Skull" icon_state = "shirt_skull" +/datum/sprite_accessory/underwear/top/shortsleeve + name = "Shirt - Short Sleeved" + icon_state = "shortsleeve" + has_color = TRUE + +/datum/sprite_accessory/underwear/top/blueshirtsport + name = "Shirt, Sports - Blue" + icon_state = "blueshirtsport" + +/datum/sprite_accessory/underwear/top/greenshirtsport + name = "Shirt, Sports - Green" + icon_state = "greenshirtsport" + +/datum/sprite_accessory/underwear/top/redshirtsport + name = "Shirt, Sports - Red" + icon_state = "redshirtsport" + /datum/sprite_accessory/underwear/top/ss13 name = "Shirt - SS13" icon_state = "shirt_ss13" @@ -141,27 +190,6 @@ name = "Shirt - USA" icon_state = "shirt_assblastusa" -/datum/sprite_accessory/underwear/top/shortsleeve - name = "Shirt - Short Sleeved" - icon_state = "shortsleeve" - has_color = TRUE - -/datum/sprite_accessory/underwear/top/blueshirtsport - name = "Shirt, Sports - Blue" - icon_state = "blueshirtsport" - -/datum/sprite_accessory/underwear/top/greenshirtsport - name = "Shirt, Sports - Green" - icon_state = "greenshirtsport" - -/datum/sprite_accessory/underwear/top/redshirtsport - name = "Shirt, Sports - Red" - icon_state = "redshirtsport" - -/datum/sprite_accessory/underwear/top/tankfire - name = "Tank Top - Fire" - icon_state = "tank_fire" - /datum/sprite_accessory/underwear/top/tanktop name = "Tank Top" icon_state = "tanktop" @@ -172,6 +200,10 @@ icon_state = "tanktop_alt" has_color = TRUE +/datum/sprite_accessory/underwear/top/tankfire + name = "Tank Top - Fire" + icon_state = "tank_fire" + /datum/sprite_accessory/underwear/top/tanktop_midriff name = "Tank Top - Midriff" icon_state = "tank_midriff" @@ -192,6 +224,8 @@ name = "Tank top - Sun" icon_state = "tank_sun" +//feminine accessories from here on + /datum/sprite_accessory/underwear/top/babydoll name = "Baby-Doll" icon_state = "babydoll" @@ -210,15 +244,25 @@ has_color = TRUE gender = FEMALE -/datum/sprite_accessory/underwear/top/bra_thin - name = "Bra - Thin" - icon_state = "bra_thin" - has_color = TRUE +/datum/sprite_accessory/underwear/top/bra_beekini + name = "Bra - Bee-kini" + icon_state = "bra_bee-kini" gender = FEMALE -/datum/sprite_accessory/underwear/top/bra_kinky - name = "Bra - Kinky Black" - icon_state = "bra_kinky" +/datum/sprite_accessory/underwear/top/bra_binder + name = "Bra (binder)" + icon_state = "bra_binder" + has_color = TRUE + +/datum/sprite_accessory/underwear/top/bra_binder_strapless + name = "Bra (binder, strapless)" + icon_state = "bra_binder_strapless" + has_color = TRUE + + +/datum/sprite_accessory/underwear/top/bra_commie + name = "Bra - Commie" + icon_state = "bra_commie" gender = FEMALE /datum/sprite_accessory/underwear/top/bra_freedom @@ -226,33 +270,17 @@ icon_state = "bra_assblastusa" gender = FEMALE -/datum/sprite_accessory/underwear/top/bra_commie - name = "Bra - Commie" - icon_state = "bra_commie" - gender = FEMALE - -/datum/sprite_accessory/underwear/top/bra_beekini - name = "Bra - Bee-kini" - icon_state = "bra_bee-kini" - gender = FEMALE - -/datum/sprite_accessory/underwear/top/bra_uk - name = "Bra - UK" - icon_state = "bra_uk" - gender = FEMALE - -/datum/sprite_accessory/underwear/top/bra_neko - name = "Bra - Neko" - icon_state = "bra_neko" - has_color = TRUE - gender = FEMALE - /datum/sprite_accessory/underwear/top/halterneck_bra name = "Bra - Halterneck" icon_state = "halterneck_bra" has_color = TRUE gender = FEMALE +/datum/sprite_accessory/underwear/top/bra_kinky + name = "Bra - Kinky Black" + icon_state = "bra_kinky" + gender = FEMALE + /datum/sprite_accessory/underwear/top/sports_bra name = "Bra, Sports" icon_state = "sports_bra" @@ -283,9 +311,21 @@ has_color = TRUE gender = FEMALE -/datum/sprite_accessory/underwear/top/fishnet_sleeves - name = "Fishnet - sleeves" - icon_state = "fishnet_sleeves" +/datum/sprite_accessory/underwear/top/bra_thin + name = "Bra - Thin" + icon_state = "bra_thin" + has_color = TRUE + gender = FEMALE + +/datum/sprite_accessory/underwear/top/bra_neko + name = "Bra - Neko" + icon_state = "bra_neko" + has_color = TRUE + gender = FEMALE + +/datum/sprite_accessory/underwear/top/bra_uk + name = "Bra - UK" + icon_state = "bra_uk" gender = FEMALE /datum/sprite_accessory/underwear/top/fishnet_gloves @@ -293,6 +333,11 @@ icon_state = "fishnet_gloves" gender = FEMALE +/datum/sprite_accessory/underwear/top/fishnet_sleeves + name = "Fishnet - sleeves" + icon_state = "fishnet_sleeves" + gender = FEMALE + /datum/sprite_accessory/underwear/top/fishnet_base name = "Fishnet - top" icon_state = "fishnet_body" @@ -315,45 +360,3 @@ icon_state = "tubetop" has_color = TRUE gender = FEMALE - -/datum/sprite_accessory/underwear/top/cowboyshirt - name = "Cowboy Shirt Black" - icon_state = "cowboyshirt" - -/datum/sprite_accessory/underwear/top/cowboyshirt/s - name = "Cowboy Shirt Shortsleeved Black" - icon_state = "cowboyshirt_s" - -/datum/sprite_accessory/underwear/top/cowboyshirt/white - name = "Cowboy Shirt White" - icon_state = "cowboyshirt_white" - -/datum/sprite_accessory/underwear/top/cowboyshirt/white/s - name = "Cowboy Shirt Shortsleeved White" - icon_state = "cowboyshirt_whites" - -/datum/sprite_accessory/underwear/top/cowboyshirt/navy - name = "Cowboy Shirt Navy" - icon_state = "cowboyshirt_navy" - -/datum/sprite_accessory/underwear/top/cowboyshirt/navy/s - name = "Cowboy Shirt Shortsleeved Navy" - icon_state = "cowboyshirt_navys" - -/datum/sprite_accessory/underwear/top/cowboyshirt/red - name = "Cowboy Shirt Red" - icon_state = "cowboyshirt_red" - -/datum/sprite_accessory/underwear/top/cowboyshirt/red/s - name = "Cowboy Shirt Shortsleeved Red" - icon_state = "cowboyshirt_reds" - -/datum/sprite_accessory/underwear/top/bra_binder - name = "Bra (binder)" - icon_state = "bra_binder" - has_color = TRUE - -/datum/sprite_accessory/underwear/top/bra_binder_strapless - name = "Bra (binder, strapless)" - icon_state = "bra_binder_strapless" - has_color = TRUE diff --git a/code/modules/mob/dead/new_player/sprite_accessories/underwear.dm b/code/modules/mob/dead/new_player/sprite_accessories/underwear.dm index 58d5e1ba88..edfeba79f1 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/underwear.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/underwear.dm @@ -10,18 +10,6 @@ icon_state = null covers_groin = FALSE -/datum/sprite_accessory/underwear/bottom/mankini - name = "Mankini" - icon_state = "mankini" - has_color = TRUE - gender = MALE - -/datum/sprite_accessory/underwear/bottom/male_kinky - name = "Jockstrap" - icon_state = "jockstrap" - has_color = TRUE - gender = MALE - /datum/sprite_accessory/underwear/bottom/briefs name = "Briefs" icon_state = "briefs" @@ -77,6 +65,26 @@ has_digitigrade = TRUE has_color = TRUE +/datum/sprite_accessory/underwear/bottom/male_kinky + name = "Jockstrap" + icon_state = "jockstrap" + has_color = TRUE + gender = MALE + +/datum/sprite_accessory/underwear/bottom/longjon + name = "Long John Bottoms" + icon_state = "ljonb" + has_digitigrade = TRUE + has_color = TRUE + +/datum/sprite_accessory/underwear/bottom/mankini + name = "Mankini" + icon_state = "mankini" + has_color = TRUE + gender = MALE + +//feminine underwear from here on + /datum/sprite_accessory/underwear/bottom/panties name = "Panties" icon_state = "panties" @@ -89,11 +97,6 @@ has_color = TRUE gender = FEMALE -/datum/sprite_accessory/underwear/bottom/fishnet_lower - name = "Panties - Fishnet" - icon_state = "fishnet_lower" - gender = FEMALE - /datum/sprite_accessory/underwear/bottom/female_beekini name = "Panties - Bee-kini" icon_state = "panties_bee-kini" @@ -104,6 +107,11 @@ icon_state = "panties_commie" gender = FEMALE +/datum/sprite_accessory/underwear/bottom/fishnet_lower + name = "Panties - Fishnet" + icon_state = "fishnet_lower" + gender = FEMALE + /datum/sprite_accessory/underwear/bottom/female_usastripe name = "Panties - Freedom" icon_state = "panties_assblastusa" @@ -114,11 +122,6 @@ icon_state = "panties_kinky" gender = FEMALE -/datum/sprite_accessory/underwear/bottom/panties_uk - name = "Panties - UK" - icon_state = "panties_uk" - gender = FEMALE - /datum/sprite_accessory/underwear/bottom/panties_neko name = "Panties - Neko" icon_state = "panties_neko" @@ -149,17 +152,10 @@ has_color = TRUE gender = FEMALE -/datum/sprite_accessory/underwear/bottom/longjon - name = "Long John Bottoms" - icon_state = "ljonb" - has_digitigrade = TRUE - has_color = TRUE - -/datum/sprite_accessory/underwear/bottom/swimsuit_red - name = "Swimsuit, One Piece - Red" - icon_state = "swimming_red" +/datum/sprite_accessory/underwear/bottom/panties_uk + name = "Panties - UK" + icon_state = "panties_uk" gender = FEMALE - covers_chest = TRUE /datum/sprite_accessory/underwear/bottom/swimsuit name = "Swimsuit, One Piece - Black" @@ -173,6 +169,12 @@ gender = FEMALE covers_chest = TRUE +/datum/sprite_accessory/underwear/bottom/swimsuit_red + name = "Swimsuit, One Piece - Red" + icon_state = "swimming_red" + gender = FEMALE + covers_chest = TRUE + /datum/sprite_accessory/underwear/bottom/thong name = "Thong" icon_state = "thong" @@ -184,5 +186,3 @@ icon_state = "thong_babydoll" has_color = TRUE gender = FEMALE - - diff --git a/code/modules/mob/dead/new_player/sprite_accessories/wings.dm b/code/modules/mob/dead/new_player/sprite_accessories/wings.dm index fb03df88a1..fb71bb483d 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/wings.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/wings.dm @@ -58,6 +58,10 @@ dimension_y = 34 relevant_layers = list(BODY_BEHIND_LAYER, BODY_ADJ_LAYER, BODY_FRONT_LAYER) +/datum/sprite_accessory/deco_wings/atlas + name = "Atlas" + icon_state = "atlas" + /datum/sprite_accessory/deco_wings/bat name = "Bat" icon_state = "bat" @@ -66,6 +70,10 @@ name = "Bee" icon_state = "bee" +/datum/sprite_accessory/deco_wings/deathhead + name = "Deathshead" + icon_state = "deathhead" + /datum/sprite_accessory/deco_wings/fairy name = "Fairy" icon_state = "fairy" @@ -74,14 +82,6 @@ name = "Feathery" icon_state = "feathery" -/datum/sprite_accessory/deco_wings/atlas - name = "Atlas" - icon_state = "atlas" - -/datum/sprite_accessory/deco_wings/deathhead - name = "Deathshead" - icon_state = "deathhead" - /datum/sprite_accessory/deco_wings/firewatch name = "Firewatch" icon_state = "firewatch" @@ -150,6 +150,10 @@ icon_state = "none" relevant_layers = null +/datum/sprite_accessory/insect_wings/atlas + name = "Atlas" + icon_state = "atlas" + /datum/sprite_accessory/insect_wings/bat name = "Bat" icon_state = "bat" @@ -158,6 +162,10 @@ name = "Bee" icon_state = "bee" +/datum/sprite_accessory/insect_wings/deathhead + name = "Deathshead" + icon_state = "deathhead" + /datum/sprite_accessory/insect_wings/fairy name = "Fairy" icon_state = "fairy" @@ -166,14 +174,6 @@ name = "Feathery" icon_state = "feathery" -/datum/sprite_accessory/insect_wings/atlas - name = "Atlas" - icon_state = "atlas" - -/datum/sprite_accessory/insect_wings/deathhead - name = "Deathshead" - icon_state = "deathhead" - /datum/sprite_accessory/insect_wings/firewatch name = "Firewatch" icon_state = "firewatch" @@ -182,6 +182,10 @@ name = "Gothic" icon_state = "gothic" +/datum/sprite_accessory/insect_wings/jungle + name = "Jungle" + icon_state = "jungle" + /datum/sprite_accessory/insect_wings/lovers name = "Lovers" icon_state = "lovers" @@ -198,6 +202,10 @@ name = "Moon Fly" icon_state = "moonfly" +/datum/sprite_accessory/insect_wings/oakworm + name = "Oak Worm" + icon_state = "oakworm" + /datum/sprite_accessory/insect_wings/plain name = "Plain" icon_state = "plain" @@ -230,14 +238,6 @@ name = "White Fly" icon_state = "whitefly" -/datum/sprite_accessory/insect_wings/oakworm - name = "Oak Worm" - icon_state = "oakworm" - -/datum/sprite_accessory/insect_wings/jungle - name = "Jungle" - icon_state = "jungle" - /datum/sprite_accessory/insect_wings/witchwing name = "Witch Wing" icon_state = "witchwing" diff --git a/code/modules/mob/dead/observer/orbit.dm b/code/modules/mob/dead/observer/orbit.dm index b81172afad..3aa5f8e302 100644 --- a/code/modules/mob/dead/observer/orbit.dm +++ b/code/modules/mob/dead/observer/orbit.dm @@ -80,3 +80,8 @@ data["npcs"] = npcs return data + +/datum/orbit_menu/ui_assets() + . = ..() || list() + . += get_asset_datum(/datum/asset/simple/orbit) + diff --git a/code/modules/mob/living/brain/emote.dm b/code/modules/mob/living/brain/emote.dm index 540c7b3949..e1cb1a29cc 100644 --- a/code/modules/mob/living/brain/emote.dm +++ b/code/modules/mob/living/brain/emote.dm @@ -1,13 +1,3 @@ -/datum/emote/brain - mob_type_allowed_typecache = list(/mob/living/brain) - mob_type_blacklist_typecache = list() - -/datum/emote/brain/can_run_emote(mob/user, status_check = TRUE) - . = ..() - var/mob/living/brain/B = user - if(!istype(B) || (!(B.container && istype(B.container, /obj/item/mmi)))) - return FALSE - /datum/emote/brain/alarm key = "alarm" message = "sounds an alarm." diff --git a/code/modules/mob/living/carbon/alien/emote.dm b/code/modules/mob/living/carbon/alien/emote.dm index 0ab448d2c5..963981b51e 100644 --- a/code/modules/mob/living/carbon/alien/emote.dm +++ b/code/modules/mob/living/carbon/alien/emote.dm @@ -1,6 +1,3 @@ -/datum/emote/living/alien - mob_type_allowed_typecache = list(/mob/living/carbon/alien) - /datum/emote/living/alien/gnarl key = "gnarl" key_third_person = "gnarls" diff --git a/code/modules/mob/living/carbon/emote.dm b/code/modules/mob/living/carbon/emote.dm index e3512e3929..49501e0315 100644 --- a/code/modules/mob/living/carbon/emote.dm +++ b/code/modules/mob/living/carbon/emote.dm @@ -1,6 +1,3 @@ -/datum/emote/living/carbon - mob_type_allowed_typecache = list(/mob/living/carbon) - /datum/emote/living/carbon/airguitar key = "airguitar" message = "is strumming the air and headbanging like a safari chimp." @@ -22,7 +19,6 @@ muzzle_ignore = TRUE restraint_check = TRUE emote_type = EMOTE_AUDIBLE - mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/carbon/clap/run_emote(mob/living/user, params) . = ..() @@ -41,7 +37,6 @@ key = "gnarl" key_third_person = "gnarls" message = "gnarls and shows its teeth..." - mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) /datum/emote/living/carbon/moan key = "moan" @@ -55,27 +50,23 @@ key = "roll" key_third_person = "rolls" message = "rolls." - mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) restraint_check = TRUE /datum/emote/living/carbon/scratch key = "scratch" key_third_person = "scratches" message = "scratches." - mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) restraint_check = TRUE /datum/emote/living/carbon/screech key = "screech" key_third_person = "screeches" message = "screeches." - mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) /datum/emote/living/carbon/sign key = "sign" key_third_person = "signs" message_param = "signs the number %t." - mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) restraint_check = TRUE /datum/emote/living/carbon/sign/select_param(mob/user, params) @@ -87,13 +78,11 @@ key = "signal" key_third_person = "signals" message_param = "raises %t fingers." - mob_type_allowed_typecache = list(/mob/living/carbon/human) restraint_check = TRUE /datum/emote/living/carbon/tail key = "tail" message = "waves their tail." - mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) /datum/emote/living/carbon/wink key = "wink" diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 1bfb72a415..837b4f73cf 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -1,6 +1,3 @@ -/datum/emote/living/carbon/human - mob_type_allowed_typecache = list(/mob/living/carbon/human) - /datum/emote/living/carbon/human/cry key = "cry" key_third_person = "cries" @@ -192,3 +189,71 @@ key_third_person = "chimes" message = "chimes." sound = 'sound/machines/chime.ogg' + +//rock paper scissors emote handling +/mob/living/carbon/human/proc/beginRockPaperScissors(var/chosen_move) + GLOB.rockpaperscissors_players[src] = list(chosen_move, ROCKPAPERSCISSORS_NOT_DECIDED) + do_after_advanced(src, ROCKPAPERSCISSORS_TIME_LIMIT, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT|DO_AFTER_NO_PROGRESSBAR|DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER, CALLBACK(src, .proc/rockpaperscissors_tick)) + var/new_entry = GLOB.rockpaperscissors_players[src] + if(new_entry[2] == ROCKPAPERSCISSORS_NOT_DECIDED) + to_chat(src, "You put your hand back down.") + GLOB.rockpaperscissors_players -= src + +/mob/living/carbon/human/proc/rockpaperscissors_tick() //called every cycle of the progress bar for rock paper scissors while waiting for an opponent + var/mob/living/carbon/human/opponent + for(var/mob/living/carbon/human/potential_opponent in (GLOB.rockpaperscissors_players - src)) //dont play against yourself + if(get_dist(src, potential_opponent) <= ROCKPAPERSCISSORS_RANGE) + opponent = potential_opponent + break + if(opponent) + //we found an opponent before they found us + var/move_to_number = list("rock" = 0, "paper" = 1, "scissors" = 2) + var/our_move = move_to_number[GLOB.rockpaperscissors_players[src][1]] + var/their_move = move_to_number[GLOB.rockpaperscissors_players[opponent][1]] + var/result_us = ROCKPAPERSCISSORS_WIN + var/result_them = ROCKPAPERSCISSORS_LOSE + if(our_move == their_move) + result_us = ROCKPAPERSCISSORS_TIE + result_them = ROCKPAPERSCISSORS_TIE + else + if(((our_move + 1) % 3) == their_move) + result_us = ROCKPAPERSCISSORS_LOSE + result_them = ROCKPAPERSCISSORS_WIN + //we decided our results so set them in the list + GLOB.rockpaperscissors_players[src][2] = result_us + GLOB.rockpaperscissors_players[opponent][2] = result_them + + //show what happened + src.visible_message("[src] makes [GLOB.rockpaperscissors_players[src][1]] with their hand!") + opponent.visible_message("[opponent] makes [GLOB.rockpaperscissors_players[opponent][1]] with their hands!") + switch(result_us) + if(ROCKPAPERSCISSORS_TIE) + src.visible_message("It was a tie!") + if(ROCKPAPERSCISSORS_WIN) + src.visible_message("[src] wins!") + if(ROCKPAPERSCISSORS_LOSE) + src.visible_message("[opponent] wins!") + + //make the progress bar end so that each player can handle the result + return DO_AFTER_STOP + + //no opponent was found, so keep searching + return DO_AFTER_PROCEED + +//the actual emotes +/datum/emote/living/carbon/human/rockpaperscissors + message = "is attempting to play rock paper scissors!" + +/datum/emote/living/carbon/human/rockpaperscissors/rock + key = "rock" + +/datum/emote/living/carbon/human/rockpaperscissors/paper + key = "paper" + +/datum/emote/living/carbon/human/rockpaperscissors/scissors + key = "scissors" + +/datum/emote/living/carbon/human/rockpaperscissors/run_emote(mob/living/carbon/human/user, params) + if(!(user in GLOB.rockpaperscissors_players)) //no using the emote again while already playing! + . = ..() + user.beginRockPaperScissors(key) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 65b2931e08..16470d81ce 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -113,14 +113,14 @@ . += effects_exam //CIT CHANGES START HERE - adds genital details to examine text - if(LAZYLEN(internal_organs)) + if(LAZYLEN(internal_organs) && CHECK_BITFIELD(user.client?.prefs.cit_toggles, GENITAL_EXAMINE)) for(var/obj/item/organ/genital/dicc in internal_organs) if(istype(dicc) && dicc.is_exposed()) . += "[dicc.desc]" - - var/cursed_stuff = attempt_vr(src,"examine_bellies",args) //vore Code - if(cursed_stuff) - . += cursed_stuff + if(CHECK_BITFIELD(user.client?.prefs.cit_toggles, VORE_EXAMINE)) + var/cursed_stuff = attempt_vr(src,"examine_bellies",args) //vore Code + if(cursed_stuff) + . += cursed_stuff //END OF CIT CHANGES //Jitters diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index af95b9e1b7..5f30d902b1 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -75,12 +75,8 @@ . = pda.owner else if(istype(tablet)) var/obj/item/computer_hardware/card_slot/card_slot = tablet.all_components[MC_CARD] - if(card_slot && (card_slot.stored_card2 || card_slot.stored_card)) - if(card_slot.stored_card2) //The second card is the one used for authorization in the ID changing program, so we prioritize it here for consistency - . = card_slot.stored_card2.registered_name - else - if(card_slot.stored_card) - . = card_slot.stored_card.registered_name + if(card_slot?.stored_card) + . = card_slot.stored_card.registered_name if(!.) . = if_no_id //to prevent null-names making the mob unclickable return diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 230a634e0e..ed509f900a 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -114,6 +114,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) /// Our default override for typing indicator state var/typing_indicator_state + //the ids you can use for your species, if empty, it means default only and not changeable + var/list/allowed_limb_ids + /////////// // PROCS // /////////// @@ -121,7 +124,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) /datum/species/New() if(!limbs_id) //if we havent set a limbs id to use, just use our own id - limbs_id = id + mutant_bodyparts["limbs_id"] = id //done this way to be non-intrusive to the existing system + else + mutant_bodyparts["limbs_id"] = limbs_id ..() //update our mutant bodyparts to include unlocked ones diff --git a/code/modules/mob/living/carbon/human/species_types/bugmen.dm b/code/modules/mob/living/carbon/human/species_types/bugmen.dm index 2a955e28a4..16b371c772 100644 --- a/code/modules/mob/living/carbon/human/species_types/bugmen.dm +++ b/code/modules/mob/living/carbon/human/species_types/bugmen.dm @@ -13,10 +13,11 @@ meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/insect liked_food = MEAT | FRUIT disliked_food = TOXIC - icon_limbs = DEFAULT_BODYPART_ICON_CITADEL exotic_bloodtype = "BUG" exotic_blood_color = BLOOD_COLOR_BUG tail_type = "mam_tail" wagging_type = "mam_waggingtail" - species_type = "insect" \ No newline at end of file + species_type = "insect" + + allowed_limb_ids = list("insect","apid","moth","moth_not_greyscale") diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm index d8dfe63b35..f3b499093b 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -83,17 +83,7 @@ /obj/item/organ/tongue/dullahan zone = "abstract" - modifies_speech = TRUE - -/obj/item/organ/tongue/dullahan/handle_speech(datum/source, list/speech_args) - if(ishuman(owner)) - var/mob/living/carbon/human/H = owner - if(isdullahan(H)) - var/datum/species/dullahan/D = H.dna.species - if(isobj(D.myhead.loc)) - var/obj/O = D.myhead.loc - O.say(speech_args[SPEECH_MESSAGE]) - speech_args[SPEECH_MESSAGE] = "" + accents = list(/datum/accent/dullahan) /obj/item/organ/ears/dullahan zone = "abstract" diff --git a/code/modules/mob/living/carbon/human/species_types/furrypeople.dm b/code/modules/mob/living/carbon/human/species_types/furrypeople.dm index 7706d4a9d5..534536d6e7 100644 --- a/code/modules/mob/living/carbon/human/species_types/furrypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/furrypeople.dm @@ -2,7 +2,6 @@ name = "Anthromorph" id = "mammal" default_color = "4B4B4B" - icon_limbs = DEFAULT_BODYPART_ICON_CITADEL species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAIR,HORNCOLOR,WINGCOLOR,HAS_FLESH,HAS_BONE) inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BEAST mutant_bodyparts = list("mcolor" = "FFFFFF","mcolor2" = "FFFFFF","mcolor3" = "FFFFFF", "mam_snouts" = "Husky", "mam_tail" = "Husky", "mam_ears" = "Husky", "deco_wings" = "None", @@ -17,3 +16,5 @@ tail_type = "mam_tail" wagging_type = "mam_waggingtail" species_type = "furry" + + allowed_limb_ids = list("mammal","aquatic","avian") \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species_types/ipc.dm b/code/modules/mob/living/carbon/human/species_types/ipc.dm index 3aef14c256..806782a8b2 100644 --- a/code/modules/mob/living/carbon/human/species_types/ipc.dm +++ b/code/modules/mob/living/carbon/human/species_types/ipc.dm @@ -3,7 +3,6 @@ id = "ipc" say_mod = "beeps" default_color = "00FF00" - icon_limbs = DEFAULT_BODYPART_ICON_CITADEL blacklisted = 0 sexes = 0 species_traits = list(MUTCOLORS,NOEYES,NOTRANSSTING,HAS_FLESH,HAS_BONE) diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm index f5a2f807e4..0f62953e6c 100644 --- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm @@ -21,6 +21,8 @@ species_type = "plant" + allowed_limb_ids = list("pod","mush") + /datum/species/pod/on_species_gain(mob/living/carbon/C, datum/species/old_species) . = ..() C.faction |= "plants" diff --git a/code/modules/mob/living/carbon/human/species_types/synthliz.dm b/code/modules/mob/living/carbon/human/species_types/synthliz.dm index 173411eaa1..70afa2f476 100644 --- a/code/modules/mob/living/carbon/human/species_types/synthliz.dm +++ b/code/modules/mob/living/carbon/human/species_types/synthliz.dm @@ -1,7 +1,6 @@ /datum/species/synthliz name = "Synthetic Lizardperson" id = "synthliz" - icon_limbs = DEFAULT_BODYPART_ICON_CITADEL say_mod = "beeps" default_color = "00FF00" species_traits = list(MUTCOLORS,NOTRANSSTING,EYECOLOR,LIPS,HAIR,HAS_FLESH,HAS_BONE) diff --git a/code/modules/mob/living/carbon/human/species_types/synths.dm b/code/modules/mob/living/carbon/human/species_types/synths.dm index c10521dfd9..3d55ce1027 100644 --- a/code/modules/mob/living/carbon/human/species_types/synths.dm +++ b/code/modules/mob/living/carbon/human/species_types/synths.dm @@ -61,7 +61,7 @@ mutant_organs = S.mutant_organs.Copy() nojumpsuit = S.nojumpsuit no_equip = S.no_equip.Copy() - limbs_id = S.limbs_id + limbs_id = S.mutant_bodyparts["limbs_id"] use_skintones = S.use_skintones fixed_mut_color = S.fixed_mut_color hair_color = S.hair_color diff --git a/code/modules/mob/living/carbon/human/species_types/xeno.dm b/code/modules/mob/living/carbon/human/species_types/xeno.dm index db34d1ae45..ddd1c86f0a 100644 --- a/code/modules/mob/living/carbon/human/species_types/xeno.dm +++ b/code/modules/mob/living/carbon/human/species_types/xeno.dm @@ -4,7 +4,6 @@ id = "xeno" say_mod = "hisses" default_color = "00FF00" - icon_limbs = DEFAULT_BODYPART_ICON_CITADEL species_traits = list(MUTCOLORS,EYECOLOR,LIPS,CAN_SCAR) mutant_bodyparts = list("xenotail"="Xenomorph Tail","xenohead"="Standard","xenodorsal"="Standard", "mam_body_markings" = "Xeno","mcolor" = "0F0","mcolor2" = "0F0","mcolor3" = "0F0","taur" = "None", "legs" = "Digitigrade") attack_verb = "slash" diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index e399ddf872..9b39438563 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -660,7 +660,7 @@ use_mob_overlay_icon: if FALSE, it will always use the default_icon_file even if //produces a key based on the human's limbs /mob/living/carbon/human/generate_icon_render_key() - . = "[dna.species.limbs_id]" + . = "[dna.species.mutant_bodyparts["limbs_id"]]" if(dna.check_mutation(HULK)) . += "-coloured-hulk" diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index a735baceae..55764d8d50 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -1,9 +1,3 @@ - -/* EMOTE DATUMS */ -/datum/emote/living - mob_type_allowed_typecache = /mob/living - mob_type_blacklist_typecache = list(/mob/living/simple_animal/slime, /mob/living/brain) - /datum/emote/living/blush key = "blush" key_third_person = "blushes" diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm index c0fca997f5..c532e44ff5 100644 --- a/code/modules/mob/living/silicon/robot/emote.dm +++ b/code/modules/mob/living/silicon/robot/emote.dm @@ -5,7 +5,7 @@ /datum/emote/sound/silicon mob_type_allowed_typecache = list(/mob/living/silicon, /mob/living/carbon/human) emote_type = EMOTE_AUDIBLE - var/unrestricted = FALSE + var/unrestricted = TRUE /datum/emote/sound/silicon/run_emote(mob/user, params) if(!unrestricted && !(issilicon(user) || isipcperson(user))) diff --git a/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm b/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm index 100db06174..8fb531f1fb 100644 --- a/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm +++ b/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm @@ -1,6 +1,5 @@ /datum/emote/sound/gorilla mob_type_allowed_typecache = /mob/living/simple_animal/hostile/gorilla - mob_type_blacklist_typecache = list() /datum/emote/sound/gorilla/ooga key = "ooga" @@ -8,4 +7,3 @@ message = "oogas." message_param = "oogas at %t." sound = 'sound/creatures/gorilla.ogg' - diff --git a/code/modules/mob/living/simple_animal/slime/emote.dm b/code/modules/mob/living/simple_animal/slime/emote.dm index 070cd48a70..9440caf3fc 100644 --- a/code/modules/mob/living/simple_animal/slime/emote.dm +++ b/code/modules/mob/living/simple_animal/slime/emote.dm @@ -1,6 +1,5 @@ /datum/emote/slime mob_type_allowed_typecache = /mob/living/simple_animal/slime - mob_type_blacklist_typecache = list() /datum/emote/slime/bounce key = "bounce" diff --git a/code/modules/modular_computers/computers/_modular_computer_shared.dm b/code/modules/modular_computers/computers/_modular_computer_shared.dm index 8ca93e8347..9dde9a0c49 100644 --- a/code/modules/modular_computers/computers/_modular_computer_shared.dm +++ b/code/modules/modular_computers/computers/_modular_computer_shared.dm @@ -44,18 +44,20 @@ . += "It has a slot installed for an intelliCard." var/obj/item/computer_hardware/card_slot/card_slot = get_modular_computer_part(MC_CARD) + var/obj/item/computer_hardware/card_slot/card_slot2 = get_modular_computer_part(MC_CARD2) + var/multiple_slots = istype(card_slot) && istype(card_slot2) if(card_slot) - if(card_slot.stored_card || card_slot.stored_card2) + if(card_slot?.stored_card || card_slot2?.stored_card) var/obj/item/card/id/first_ID = card_slot.stored_card - var/obj/item/card/id/second_ID = card_slot.stored_card2 + var/obj/item/card/id/second_ID = card_slot2.stored_card var/multiple_cards = istype(first_ID) && istype(second_ID) if(user_is_adjacent) - . += "It has two slots for identification cards installed[multiple_cards ? " which contain [first_ID] and [second_ID]" : ", one of which contains [first_ID ? first_ID : second_ID]"]." + . += "It has [multiple_slots ? "two slots" : "a slot"] for identification cards installed[multiple_cards ? " which contain [first_ID] and [second_ID]" : ", one of which contains [first_ID ? first_ID : second_ID]"]." else - . += "It has two slots for identification cards installed, [multiple_cards ? "both of which appear" : "and one of them appears"] to be occupied." + . += "It has [multiple_slots ? "two slots" : "a slot"] for identification cards installed, [multiple_cards ? "both of which appear" : "and one of them appears"] to be occupied." . += "Alt-click [src] to eject the identification card[multiple_cards ? "s":""]." else - . += "It has two slots installed for identification cards." + . += "It has [multiple_slots ? "two slots" : "a slot"] installed for identification cards." var/obj/item/computer_hardware/printer/printer_slot = get_modular_computer_part(MC_PRINT) if(printer_slot) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 67c04de13f..63cb1cc5fa 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -4,6 +4,12 @@ /obj/item/modular_computer name = "modular microcomputer" desc = "A small portable microcomputer." + icon = 'icons/obj/computer.dmi' + icon_state = "laptop-open" + var/light_on = FALSE + integrity_failure = 0.5 + max_integrity = 100 + armor = list("melee" = 0, "bullet" = 20, "laser" = 20, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 0, "acid" = 0) var/enabled = 0 // Whether the computer is turned on. var/screen_on = 1 // Whether the computer is active/opened/it's screen is on. @@ -22,8 +28,6 @@ // must have it's own DMI file. Icon states must be called exactly the same in all files, but may look differently // If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example. - icon = 'icons/obj/computer.dmi' - icon_state = "laptop-open" var/icon_state_unpowered = null // Icon state when the computer is turned off. var/icon_state_powered = null // Icon state when the computer is turned on. var/icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen. @@ -31,20 +35,16 @@ var/max_hardware_size = 0 // Maximal hardware w_class. Tablets/PDAs have 1, laptops 2, consoles 4. var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer. - integrity_failure = 0.5 - max_integrity = 100 - armor = list("melee" = 0, "bullet" = 20, "laser" = 20, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 0, "acid" = 0) - - // Important hardware (must be installed for computer to work) - - // Optional hardware (improves functionality, but is not critical for computer to work) - - var/list/all_components = list() // List of "connection ports" in this computer and the components with which they are plugged + /// List of "connection ports" in this computer and the components with which they are plugged + var/list/all_components = list() + /// Lazy List of extra hardware slots that can be used modularly. + var/list/expansion_bays + /// Number of total expansion bays this computer has available. + var/max_bays = 0 var/list/idle_threads // Idle programs on background. They still receive process calls but can't be interacted with. var/obj/physical = null // Object that represents our computer. It's used for Adjacent() and UI visibility checks. var/has_light = FALSE //If the computer has a flashlight/LED light/what-have-you installed - var/light_on = FALSE //If that light is enabled var/comp_light_luminosity = 3 //The brightness of that light var/comp_light_color //The color of that light @@ -71,80 +71,15 @@ physical = null return ..() - -/obj/item/modular_computer/proc/add_verb(var/path) - switch(path) - if(MC_CARD) - verbs += /obj/item/modular_computer/proc/eject_id - if(MC_SDD) - verbs += /obj/item/modular_computer/proc/eject_disk - if(MC_AI) - verbs += /obj/item/modular_computer/proc/eject_card - -/obj/item/modular_computer/proc/remove_verb(path) - switch(path) - if(MC_CARD) - verbs -= /obj/item/modular_computer/proc/eject_id - if(MC_SDD) - verbs -= /obj/item/modular_computer/proc/eject_disk - if(MC_AI) - verbs -= /obj/item/modular_computer/proc/eject_card - -// Eject ID card from computer, if it has ID slot with card inside. -/obj/item/modular_computer/proc/eject_id() - set name = "Eject ID" - set category = "Object" - set src in view(1) - - if(issilicon(usr)) - return - var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] - if(usr.canUseTopic(src, BE_CLOSE)) - card_slot.try_eject(null, usr) - -// Eject ID card from computer, if it has ID slot with card inside. -/obj/item/modular_computer/proc/eject_card() - set name = "Eject Intellicard" - set category = "Object" - - if(issilicon(usr)) - return - var/obj/item/computer_hardware/ai_slot/ai_slot = all_components[MC_AI] - if(usr.canUseTopic(src, BE_CLOSE)) - ai_slot.try_eject(null, usr,1) - - -// Eject ID card from computer, if it has ID slot with card inside. -/obj/item/modular_computer/proc/eject_disk() - set name = "Eject Data Disk" - set category = "Object" - - if(issilicon(usr)) - return - - if(usr.canUseTopic(src, BE_CLOSE)) - var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD] - if(uninstall_component(portable_drive, usr)) - portable_drive.verb_pickup() - /obj/item/modular_computer/AltClick(mob/user) ..() if(issilicon(user)) return if(user.canUseTopic(src, BE_CLOSE)) + var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2] var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] - var/obj/item/computer_hardware/ai_slot/ai_slot = all_components[MC_AI] - var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD] - if(portable_drive) - if(uninstall_component(portable_drive, user)) - portable_drive.verb_pickup() - else - if(card_slot && card_slot.try_eject(null, user)) - return - if(ai_slot) - ai_slot.try_eject(null, user) - + return (card_slot2?.try_eject(user) || card_slot?.try_eject(user)) //Try the secondary one first. // Gets IDs/access levels from card slot. Would be useful when/if PDAs would become modular PCs. /obj/item/modular_computer/GetAccess() @@ -160,19 +95,25 @@ return ..() /obj/item/modular_computer/RemoveID() + var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2] var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] - if(!card_slot) - return - return card_slot.RemoveID() + return (card_slot2?.try_eject() || card_slot?.try_eject()) //Try the secondary one first. /obj/item/modular_computer/InsertID(obj/item/inserting_item) var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] - if(!card_slot) + var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2] + if(!(card_slot || card_slot2)) + //to_chat(user, "There isn't anywhere you can fit a card into on this computer.") return FALSE + var/obj/item/card/inserting_id = inserting_item.RemoveID() if(!inserting_id) return FALSE - return card_slot.try_insert(inserting_id) + + if((card_slot?.try_insert(inserting_id)) || (card_slot2?.try_insert(inserting_id))) + return TRUE + //to_chat(user, "This computer doesn't have an open card slot.") + return FALSE /obj/item/modular_computer/MouseDrop(obj/over_object, src_location, over_location) var/mob/M = usr diff --git a/code/modules/modular_computers/computers/item/computer_components.dm b/code/modules/modular_computers/computers/item/computer_components.dm index 3c94a66384..8668b279cf 100644 --- a/code/modules/modular_computers/computers/item/computer_components.dm +++ b/code/modules/modular_computers/computers/item/computer_components.dm @@ -6,6 +6,14 @@ to_chat(user, "This component is too large for \the [src]!") return FALSE + if(H.expansion_hw) + if(LAZYLEN(expansion_bays) >= max_bays) + to_chat(user, "All of the computer's expansion bays are filled.") + return FALSE + if(LAZYACCESS(expansion_bays, H.device_type)) + to_chat(user, "The computer immediately ejects /the [H] and flashes an error: \"Hardware Address Conflict\".") + return FALSE + if(all_components[H.device_type]) to_chat(user, "This computer's hardware slot is already occupied by \the [all_components[H.device_type]].") return FALSE @@ -20,6 +28,8 @@ if(user && !user.transferItemToLoc(H, src)) return FALSE + if(H.expansion_hw) + LAZYSET(expansion_bays, H.device_type, H) all_components[H.device_type] = H to_chat(user, "You install \the [H] into \the [src].") @@ -32,7 +42,9 @@ /obj/item/modular_computer/proc/uninstall_component(obj/item/computer_hardware/H, mob/living/user = null) if(H.holder != src) // Not our component at all. return FALSE + if(H.expansion_hw) + LAZYREMOVE(expansion_bays, H.device_type) all_components.Remove(H.device_type) to_chat(user, "You remove \the [H] from \the [src].") @@ -43,6 +55,7 @@ if(enabled && !use_power()) shutdown_computer() update_icon() + return TRUE // Checks all hardware pieces to determine if name matches, if yes, returns the hardware piece, otherwise returns null diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm index fd017e2b0f..4a985b93c1 100644 --- a/code/modules/modular_computers/computers/item/computer_ui.dm +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -44,6 +44,33 @@ /obj/item/modular_computer/ui_data(mob/user) var/list/data = get_header_data() data["device_theme"] = device_theme + + data["login"] = list() + var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD] + if(cardholder) + var/obj/item/card/id/stored_card = cardholder.GetID() + if(stored_card) + var/stored_name = stored_card.registered_name + var/stored_title = stored_card.assignment + if(!stored_name) + stored_name = "Unknown" + if(!stored_title) + stored_title = "Unknown" + data["login"] = list( + IDName = stored_name, + IDJob = stored_title, + ) + + data["removable_media"] = list() + if(all_components[MC_SDD]) + data["removable_media"] += "removable storage disk" + var/obj/item/computer_hardware/ai_slot/intelliholder = all_components[MC_AI] + if(intelliholder?.stored_card) + data["removable_media"] += "intelliCard" + var/obj/item/computer_hardware/card_slot/secondarycardholder = all_components[MC_CARD2] + if(secondarycardholder?.stored_card) + data["removable_media"] += "secondary RFID card" + data["programs"] = list() var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] for(var/datum/computer_file/program/P in hard_drive.stored_files) @@ -157,6 +184,36 @@ light_color = new_color update_light() return TRUE + + if("PC_Eject_Disk") + var/param = params["name"] + var/mob/user = usr + switch(param) + if("removable storage disk") + var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD] + if(!portable_drive) + return + if(uninstall_component(portable_drive, usr)) + user.put_in_hands(portable_drive) + playsound(src, 'sound/machines/card_slide.ogg', 50) + if("intelliCard") + var/obj/item/computer_hardware/ai_slot/intelliholder = all_components[MC_AI] + if(!intelliholder) + return + if(intelliholder.try_eject(user)) + playsound(src, 'sound/machines/card_slide.ogg', 50) + if("ID") + var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD] + if(!cardholder) + return + cardholder.try_eject(user) + if("secondary RFID card") + var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD2] + if(!cardholder) + return + cardholder.try_eject(user) + + else return diff --git a/code/modules/modular_computers/computers/item/laptop.dm b/code/modules/modular_computers/computers/item/laptop.dm index 5927d57a0b..ef83140a8f 100644 --- a/code/modules/modular_computers/computers/item/laptop.dm +++ b/code/modules/modular_computers/computers/item/laptop.dm @@ -12,6 +12,7 @@ hardware_flag = PROGRAM_LAPTOP max_hardware_size = 2 w_class = WEIGHT_CLASS_NORMAL + max_bays = 4 // No running around with open laptops in hands. item_flags = SLOWS_WHILE_IN_HAND diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm index c79d7a9361..0d7b567877 100644 --- a/code/modules/modular_computers/computers/item/processor.dm +++ b/code/modules/modular_computers/computers/item/processor.dm @@ -8,6 +8,7 @@ icon_state_unpowered = null icon_state_menu = null hardware_flag = 0 + max_bays = 4 var/obj/machinery/modular_computer/machinery_computer = null @@ -18,7 +19,7 @@ machinery_computer = null . = ..() -/obj/item/modular_computer/processor/New(comp) //intentional new probably +/obj/item/modular_computer/processor/New(comp) ..() STOP_PROCESSING(SSobj, src) // Processed by its machine @@ -56,23 +57,5 @@ machinery_computer.update_icon() return -/obj/item/modular_computer/processor/add_verb(path) - switch(path) - if(MC_CARD) - machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_id - if(MC_SDD) - machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_disk - if(MC_AI) - machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_card - -/obj/item/modular_computer/processor/remove_verb(path) - switch(path) - if(MC_CARD) - machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_id - if(MC_SDD) - machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_disk - if(MC_AI) - machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_card - /obj/item/modular_computer/processor/attack_ghost(mob/user) ui_interact(user) diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm index 41a256467f..67e8118c7a 100644 --- a/code/modules/modular_computers/computers/item/tablet.dm +++ b/code/modules/modular_computers/computers/item/tablet.dm @@ -5,10 +5,11 @@ icon_state_unpowered = "tablet" icon_state_powered = "tablet" icon_state_menu = "menu" - //worn_icon_state = "tablet" + // worn_icon_state = "tablet" hardware_flag = PROGRAM_TABLET max_hardware_size = 1 w_class = WEIGHT_CLASS_SMALL + max_bays = 3 steel_sheet_cost = 1 slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT has_light = TRUE //LED flashlight! diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm index 7cca8ea5b4..8ac669d2bf 100644 --- a/code/modules/modular_computers/computers/item/tablet_presets.dm +++ b/code/modules/modular_computers/computers/item/tablet_presets.dm @@ -26,11 +26,21 @@ install_component(new /obj/item/computer_hardware/processor_unit/small) install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer)) install_component(hard_drive) + install_component(new /obj/item/computer_hardware/card_slot) install_component(new /obj/item/computer_hardware/network_card) install_component(new /obj/item/computer_hardware/printer/mini) hard_drive.store_file(new /datum/computer_file/program/bounty) //hard_drive.store_file(new /datum/computer_file/program/shipping) +/obj/item/modular_computer/tablet/preset/advanced/atmos/Initialize() //This will be defunct and will be replaced when NtOS PDAs are done + . = ..() + install_component(new /obj/item/computer_hardware/sensorpackage) + +/obj/item/modular_computer/tablet/preset/advanced/command/Initialize() + . = ..() + install_component(new /obj/item/computer_hardware/sensorpackage) + install_component(new /obj/item/computer_hardware/card_slot/secondary) + /// Given by the syndicate as part of the contract uplink bundle - loads in the Contractor Uplink. /obj/item/modular_computer/tablet/syndicate_contract_uplink/preset/uplink/Initialize() . = ..() diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm index 9d29b23e76..12b2f6d25a 100644 --- a/code/modules/modular_computers/computers/machinery/console_presets.dm +++ b/code/modules/modular_computers/computers/machinery/console_presets.dm @@ -1,6 +1,6 @@ /obj/machinery/modular_computer/console/preset // Can be changed to give devices specific hardware - var/_has_id_slot = FALSE + var/_has_second_id_slot = FALSE var/_has_printer = FALSE var/_has_battery = FALSE var/_has_ai = FALSE @@ -11,8 +11,9 @@ return cpu.install_component(new /obj/item/computer_hardware/processor_unit) - if(_has_id_slot) - cpu.install_component(new /obj/item/computer_hardware/card_slot) + cpu.install_component(new /obj/item/computer_hardware/card_slot) + if(_has_second_id_slot) + cpu.install_component(new /obj/item/computer_hardware/card_slot/secondary) if(_has_printer) cpu.install_component(new /obj/item/computer_hardware/printer) if(_has_battery) @@ -59,7 +60,7 @@ console_department = "Command" name = "command console" desc = "A stationary computer. This one comes preloaded with command programs." - _has_id_slot = TRUE + _has_second_id_slot = TRUE _has_printer = TRUE /obj/machinery/modular_computer/console/preset/command/install_programs() @@ -73,7 +74,7 @@ console_department = "Identification" name = "identification console" desc = "A stationary computer. This one comes preloaded with identification modification programs." - _has_id_slot = TRUE + _has_second_id_slot = TRUE _has_printer = TRUE /obj/machinery/modular_computer/console/preset/id/install_programs() diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm index 6f016ad147..0e6f4d161a 100644 --- a/code/modules/modular_computers/computers/machinery/modular_computer.dm +++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm @@ -75,30 +75,6 @@ add_overlay("bsod") add_overlay("broken") -// Eject ID card from computer, if it has ID slot with card inside. -/obj/machinery/modular_computer/proc/eject_id() - set name = "Eject ID" - set category = "Object" - - if(cpu) - cpu.eject_id() - -// Eject ID card from computer, if it has ID slot with card inside. -/obj/machinery/modular_computer/proc/eject_disk() - set name = "Eject Data Disk" - set category = "Object" - - if(cpu) - cpu.eject_disk() - -/obj/machinery/modular_computer/proc/eject_card() - set name = "Eject Intellicard" - set category = "Object" - set src in view(1) - - if(cpu) - cpu.eject_card() - /obj/machinery/modular_computer/AltClick(mob/user) if(cpu) cpu.AltClick(user) @@ -136,7 +112,7 @@ return . = ..() -/obj/machinery/modular_computer/attackby(var/obj/item/W as obj, mob/user) +/obj/machinery/modular_computer/attackby(obj/item/W as obj, mob/user) if(cpu && !(flags_1 & NODECONSTRUCT_1)) return cpu.attackby(W, user) return ..() @@ -169,5 +145,4 @@ // "Brute" damage mostly damages the casing. /obj/machinery/modular_computer/bullet_act(obj/item/projectile/Proj) if(cpu) - return cpu.bullet_act(Proj) - return ..() + cpu.bullet_act(Proj) diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 12e5ef6e95..6d6a48d567 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -25,8 +25,6 @@ var/ntnet_status = 1 /// Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL var/usage_flags = PROGRAM_ALL - /// Optional string that describes what NTNet server/system this program connects to. Used in default logging. - var/network_destination = null /// Whether the program can be downloaded from NTNet. Set to 0 to disable. var/available_on_ntnet = 1 /// Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to 1 to enable. @@ -82,10 +80,18 @@ /datum/computer_file/program/proc/process_tick() return 1 -// Check if the user can run program. Only humans can operate computer. Automatically called in run_program() -// User has to wear their ID for ID Scan to work. -// Can also be called manually, with optional parameter being access_to_check to scan the user's ID -/datum/computer_file/program/proc/can_run(mob/user, loud = FALSE, access_to_check, transfer = FALSE) +/** + *Check if the user can run program. Only humans can operate computer. Automatically called in run_program() + *ID must be inserted into a card slot to be read. If the program is not currently installed (as is the case when + *NT Software Hub is checking available software), a list can be given to be used instead. + *Arguments: + *user is a ref of the mob using the device. + *loud is a bool deciding if this proc should use to_chats + *access_to_check is an access level that will be checked against the ID + *transfer, if TRUE and access_to_check is null, will tell this proc to use the program's transfer_access in place of access_to_check + *access can contain a list of access numbers to check against. If access is not empty, it will be used istead of checking any inserted ID. +*/ +/datum/computer_file/program/proc/can_run(mob/user, loud = FALSE, access_to_check, transfer = FALSE, var/list/access) // Defaults to required_access if(!access_to_check) if(transfer && transfer_access) @@ -104,29 +110,24 @@ if(issilicon(user)) return TRUE - if(ishuman(user)) + if(!length(access)) var/obj/item/card/id/D var/obj/item/computer_hardware/card_slot/card_slot - if(computer && card_slot) + if(computer) card_slot = computer.all_components[MC_CARD] - D = card_slot.GetID() - var/mob/living/carbon/human/h = user - var/obj/item/card/id/I = h.get_idcard(TRUE) + D = card_slot?.GetID() - if(!I && !D) + if(!D) if(loud) to_chat(user, "\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning.") return FALSE + access = D.GetAccess() - if(I) - if(access_to_check in I.GetAccess()) - return TRUE - else if(D) - if(access_to_check in D.GetAccess()) - return TRUE - if(loud) - to_chat(user, "\The [computer] flashes an \"Access Denied\" warning.") - return 0 + if(access_to_check in access) + return TRUE + if(loud) + to_chat(user, "\The [computer] flashes an \"Access Denied\" warning.") + return FALSE // This attempts to retrieve header data for UIs. If implementing completely new device of different type than existing ones // always include the device here in this proc. This proc basically relays the request to whatever is running the program. @@ -139,8 +140,12 @@ // When implementing new program based device, use this to run the program. /datum/computer_file/program/proc/run_program(mob/living/user) if(can_run(user, 1)) - if(requires_ntnet && network_destination) - generate_network_log("Connection opened to [network_destination].") + if(requires_ntnet) + var/obj/item/card/id/ID + var/obj/item/computer_hardware/card_slot/card_holder = computer.all_components[MC_CARD] + if(card_holder) + ID = card_holder.GetID() + generate_network_log("Connection opened -- Program ID: [filename] User:[ID?"[ID.registered_name]":"None"]") program_state = PROGRAM_STATE_ACTIVE return 1 return 0 @@ -162,8 +167,12 @@ // Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client. /datum/computer_file/program/proc/kill_program(forced = FALSE) program_state = PROGRAM_STATE_KILLED - if(network_destination) - generate_network_log("Connection to [network_destination] closed.") + if(requires_ntnet) + var/obj/item/card/id/ID + var/obj/item/computer_hardware/card_slot/card_holder = computer.all_components[MC_CARD] + if(card_holder) + ID = card_holder.GetID() + generate_network_log("Connection closed -- Program ID: [filename] User:[ID?"[ID.registered_name]":"None"]") return 1 /datum/computer_file/program/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/modules/modular_computers/file_system/program_events.dm b/code/modules/modular_computers/file_system/program_events.dm index 3c1daa5af3..1cb74a227b 100644 --- a/code/modules/modular_computers/file_system/program_events.dm +++ b/code/modules/modular_computers/file_system/program_events.dm @@ -2,7 +2,7 @@ // Always include a parent call when overriding an event. // Called when the ID card is removed from computer. ID is removed AFTER this proc. -/datum/computer_file/program/proc/event_idremoved(background, slot) +/datum/computer_file/program/proc/event_idremoved(background) return // Called when the computer fails due to power loss. Override when program wants to specifically react to power loss. diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm index 364ad79737..7ae6dd203a 100644 --- a/code/modules/modular_computers/file_system/programs/airestorer.dm +++ b/code/modules/modular_computers/file_system/programs/airestorer.dm @@ -1,8 +1,8 @@ /datum/computer_file/program/aidiag filename = "aidiag" - filedesc = "AI Integrity Restorer" + filedesc = "NT FRK" program_icon_state = "generic" - extended_desc = "This program is capable of reconstructing damaged AI systems. Requires direct AI connection via intellicard slot." + extended_desc = "Firmware Restoration Kit, capable of reconstructing damaged AI systems. Requires direct AI connection via intellicard slot." size = 12 requires_ntnet = FALSE usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP @@ -48,7 +48,7 @@ if(computer.all_components[MC_AI]) var/obj/item/computer_hardware/ai_slot/ai_slot = computer.all_components[MC_AI] if(ai_slot && ai_slot.stored_card) - ai_slot.try_eject(0,usr) + ai_slot.try_eject(usr) return TRUE /datum/computer_file/program/aidiag/process_tick() @@ -71,14 +71,19 @@ ai_slot.locked = FALSE restoring = FALSE return - ai_slot.locked =TRUE + ai_slot.locked = TRUE A.adjustOxyLoss(-5, 0)//, FALSE) A.adjustFireLoss(-5, 0)//, FALSE) A.adjustToxLoss(-5, 0) A.adjustBruteLoss(-5, 0) + + // Please don't forget to update health, otherwise the below if statements will probably always fail. A.updatehealth() + if(A.health >= 0 && A.stat == DEAD) A.revive(full_heal = FALSE, admin_revive = FALSE) + cardhold.update_icon() + // Finished restoring if(A.health >= 100) ai_slot.locked = FALSE diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/alarm.dm index 577fad83d0..55dea600e3 100644 --- a/code/modules/modular_computers/file_system/programs/alarm.dm +++ b/code/modules/modular_computers/file_system/programs/alarm.dm @@ -1,11 +1,10 @@ /datum/computer_file/program/alarm_monitor filename = "alarmmonitor" - filedesc = "Alarm Monitor" + filedesc = "Canary" ui_header = "alarm_green.gif" program_icon_state = "alert-green" - extended_desc = "This program provides visual interface for station's alarm system." + extended_desc = "This program provides visual interface for a station's alarm system." requires_ntnet = 1 - network_destination = "alarm monitoring network" size = 5 tgui_id = "NtosStationAlertConsole" var/has_alert = 0 diff --git a/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm b/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm index 3accb8e02d..aa361d4544 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm @@ -14,7 +14,7 @@ var/assigned = FALSE var/first_load = TRUE -/datum/computer_file/program/contract_uplink/run_program(var/mob/living/user) +/datum/computer_file/program/contract_uplink/run_program(mob/living/user) . = ..(user) /datum/computer_file/program/contract_uplink/ui_act(action, params) diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm index 2ba3d69fe6..4f1c488b9e 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm @@ -10,7 +10,7 @@ tgui_id = "NtosRevelation" var/armed = 0 -/datum/computer_file/program/revelation/run_program(var/mob/living/user) +/datum/computer_file/program/revelation/run_program(mob/living/user) . = ..(user) if(armed) activate() diff --git a/code/modules/modular_computers/file_system/programs/arcade.dm b/code/modules/modular_computers/file_system/programs/arcade.dm index 2503073f9a..87a3f1ec94 100644 --- a/code/modules/modular_computers/file_system/programs/arcade.dm +++ b/code/modules/modular_computers/file_system/programs/arcade.dm @@ -1,10 +1,9 @@ /datum/computer_file/program/arcade - filename = "arcade" - filedesc = "Nanotrasen Micro Arcade" + filename = "dsarcade" + filedesc = "Donksoft Micro Arcade" program_icon_state = "arcade" extended_desc = "This port of the classic game 'Outbomb Cuban Pete', redesigned to run on tablets, with thrilling graphics and chilling storytelling." requires_ntnet = FALSE - network_destination = "arcade network" size = 6 tgui_id = "NtosArcade" @@ -25,7 +24,7 @@ /datum/computer_file/program/arcade/proc/game_check(mob/user) sleep(5) - //user?.mind?.adjust_experience(/datum/skill/gaming, 1) No gaming(TM) Yet + // user?.mind?.adjust_experience(/datum/skill/gaming, 1) if(boss_hp <= 0) heads_up = "You have crushed [boss_name]! Rejoice!" playsound(computer.loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3, falloff = 10) @@ -34,7 +33,7 @@ if(istype(computer)) computer.update_icon() ticket_count += 1 - //user?.mind?.adjust_experience(/datum/skill/gaming, 50) + // user?.mind?.adjust_experience(/datum/skill/gaming, 50) sleep(10) else if(player_hp <= 0 || player_mp <= 0) heads_up = "You have been defeated... how will the station survive?" @@ -43,7 +42,7 @@ program_icon_state = "arcade_off" if(istype(computer)) computer.update_icon() - //user?.mind?.adjust_experience(/datum/skill/gaming, 10) + // user?.mind?.adjust_experience(/datum/skill/gaming, 10) sleep(10) /datum/computer_file/program/arcade/proc/enemy_check(mob/user) @@ -98,8 +97,8 @@ if(computer) printer = computer.all_components[MC_PRINT] - //var/gamerSkillLevel = usr.mind?.get_skill_level(/datum/skill/gaming) - //var/gamerSkill = usr.mind?.get_skill_modifier(/datum/skill/gaming, SKILL_RANDS_MODIFIER) + // var/gamerSkillLevel = usr.mind?.get_skill_level(/datum/skill/gaming) + // var/gamerSkill = usr.mind?.get_skill_modifier(/datum/skill/gaming, SKILL_RANDS_MODIFIER) switch(action) if("Attack") var/attackamt = 0 //Spam prevention. @@ -119,8 +118,8 @@ if(pause_state == FALSE) healamt = rand(6,8)// + rand(0, gamerSkill) var/maxPointCost = 3 - //if(gamerSkillLevel >= SKILL_LEVEL_JOURNEYMAN) - // maxPointCost = 2 + // if(gamerSkillLevel >= SKILL_LEVEL_JOURNEYMAN) + // maxPointCost = 2 healcost = rand(1, maxPointCost) pause_state = TRUE heads_up = "You heal for [healamt] damage." diff --git a/code/modules/modular_computers/file_system/programs/atmosscan.dm b/code/modules/modular_computers/file_system/programs/atmosscan.dm index 2df751bebd..c4b9951838 100644 --- a/code/modules/modular_computers/file_system/programs/atmosscan.dm +++ b/code/modules/modular_computers/file_system/programs/atmosscan.dm @@ -1,17 +1,25 @@ /datum/computer_file/program/atmosscan filename = "atmosscan" - filedesc = "Atmospheric Scanner" + filedesc = "AtmoZphere" program_icon_state = "air" extended_desc = "A small built-in sensor reads out the atmospheric conditions around the device." - network_destination = "atmos scan" size = 4 tgui_id = "NtosAtmos" +/datum/computer_file/program/atmosscan/run_program(mob/living/user) + . = ..() + if (!.) + return + if(!computer?.get_modular_computer_part(MC_SENSORS)) //Giving a clue to users why the program is spitting out zeros. + to_chat(user, "\The [computer] flashes an error: \"hardware\\sensorpackage\\startup.bin -- file not found\".") + + /datum/computer_file/program/atmosscan/ui_data(mob/user) var/list/data = get_header_data() var/list/airlist = list() var/turf/T = get_turf(ui_host()) - if(T) + var/obj/item/computer_hardware/sensorpackage/sensors = computer?.get_modular_computer_part(MC_SENSORS) + if(T && sensors?.check_functionality()) var/datum/gas_mixture/environment = T.return_air() var/list/env_gases = environment.get_gases() var/pressure = environment.return_pressure() @@ -24,6 +32,10 @@ if(gas_level > 0) airlist += list(list("name" = "[GLOB.meta_gas_names[id]]", "percentage" = round(gas_level*100, 0.01))) data["AirData"] = airlist + else + data["AirPressure"] = 0 + data["AirTemp"] = 0 + data["AirData"] = list(list()) return data /datum/computer_file/program/atmosscan/ui_act(action, list/params) diff --git a/code/modules/modular_computers/file_system/programs/borg_monitor.dm b/code/modules/modular_computers/file_system/programs/borg_monitor.dm index c2160a0e92..13caab27ef 100644 --- a/code/modules/modular_computers/file_system/programs/borg_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm @@ -1,12 +1,11 @@ /datum/computer_file/program/borg_monitor - filename = "cyborgmonitor" - filedesc = "Cyborg Remote Monitoring" + filename = "siliconnect" + filedesc = "SiliConnect" ui_header = "borg_mon.gif" program_icon_state = "generic" extended_desc = "This program allows for remote monitoring of station cyborgs." requires_ntnet = TRUE transfer_access = ACCESS_ROBOTICS - network_destination = "cyborg remote monitoring" size = 5 tgui_id = "NtosCyborgRemoteMonitor" @@ -32,7 +31,7 @@ var/list/cyborg_data = list( name = R.name, - locked_down = R.lockcharge, + locked_down = R.locked_down, status = R.stat, shell_discon = shell, charge = R.cell ? round(R.cell.percent()) : null, @@ -81,8 +80,8 @@ return ID.registered_name /datum/computer_file/program/borg_monitor/syndicate - filename = "scyborgmonitor" - filedesc = "Mission-Specific Cyborg Remote Monitoring" + filename = "roboverlord" + filedesc = "Roboverlord" ui_header = "borg_mon.gif" program_icon_state = "generic" extended_desc = "This program allows for remote monitoring of mission-assigned cyborgs." @@ -90,7 +89,6 @@ available_on_ntnet = FALSE available_on_syndinet = TRUE transfer_access = null - network_destination = "cyborg remote monitoring" tgui_id = "NtosCyborgRemoteMonitorSyndicate" /datum/computer_file/program/borg_monitor/syndicate/evaluate_borg(mob/living/silicon/robot/R) diff --git a/code/modules/modular_computers/file_system/programs/bounty_board.dm b/code/modules/modular_computers/file_system/programs/bounty_board.dm index 46fde84f65..2e7d3cc87f 100644 --- a/code/modules/modular_computers/file_system/programs/bounty_board.dm +++ b/code/modules/modular_computers/file_system/programs/bounty_board.dm @@ -4,7 +4,6 @@ program_icon_state = "bountyboard" extended_desc = "A multi-platform network for placing requests across the station, with payment across the network being possible.." requires_ntnet = TRUE - network_destination = "bounty board interface" size = 10 tgui_id = "NtosRequestKiosk" ///Reference to the currently logged in user. diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm index 842d6e2588..6d45914add 100644 --- a/code/modules/modular_computers/file_system/programs/card.dm +++ b/code/modules/modular_computers/file_system/programs/card.dm @@ -7,8 +7,8 @@ #define CARDCON_DEPARTMENT_COMMAND "Command" /datum/computer_file/program/card_mod - filename = "cardmod" - filedesc = "ID Card Modification" + filename = "plexagonidwriter" + filedesc = "Plexagon Access Management" program_icon_state = "id" extended_desc = "Program for programming employee ID cards to access parts of the station." transfer_access = ACCESS_HEADS @@ -98,17 +98,19 @@ return TRUE var/obj/item/computer_hardware/card_slot/card_slot + var/obj/item/computer_hardware/card_slot/card_slot2 var/obj/item/computer_hardware/printer/printer if(computer) card_slot = computer.all_components[MC_CARD] + card_slot2 = computer.all_components[MC_CARD2] printer = computer.all_components[MC_PRINT] - if(!card_slot) + if(!card_slot || !card_slot2) return var/mob/user = usr - var/obj/item/card/id/user_id_card = user.get_idcard(FALSE) + var/obj/item/card/id/user_id_card = card_slot.stored_card - var/obj/item/card/id/id_card = card_slot.stored_card + var/obj/item/card/id/target_id_card = card_slot2.stored_card switch(action) if("PRG_authenticate") @@ -129,14 +131,14 @@ return var/contents = {"

Access Report

Prepared By: [user_id_card && user_id_card.registered_name ? user_id_card.registered_name : "Unknown"]
- For: [id_card.registered_name ? id_card.registered_name : "Unregistered"]
+ For: [target_id_card.registered_name ? target_id_card.registered_name : "Unregistered"]

- Assignment: [id_card.assignment]
+ Assignment: [target_id_card.assignment]
Access:
"} var/known_access_rights = get_all_accesses() - for(var/A in id_card.access) + for(var/A in target_id_card.access) if(A in known_access_rights) contents += " [get_access_desc(A)]" @@ -148,43 +150,40 @@ computer.visible_message("\The [computer] prints out a paper.") return TRUE if("PRG_eject") - if(!computer || !card_slot) + if(!computer || !card_slot2) return - if(id_card) - GLOB.data_core.manifest_modify(id_card.registered_name, id_card.assignment) - card_slot.try_eject(TRUE, user) + if(target_id_card) + GLOB.data_core.manifest_modify(target_id_card.registered_name, target_id_card.assignment) + return card_slot2.try_eject(user) else var/obj/item/I = user.get_active_held_item() if(istype(I, /obj/item/card/id)) - if(!user.transferItemToLoc(I, computer)) - return - card_slot.stored_card = I - playsound(computer, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) - return TRUE + return card_slot2.try_insert(I) + return FALSE if("PRG_terminate") if(!computer || !authenticated) return if(minor) - if(!(id_card.assignment in head_subordinates) && id_card.assignment != "Assistant") + if(!(target_id_card.assignment in head_subordinates) && target_id_card.assignment != "Assistant") return - id_card.access -= get_all_centcom_access() + get_all_accesses() - id_card.assignment = "Unassigned" - id_card.update_label() + target_id_card.access -= get_all_centcom_access() + get_all_accesses() + target_id_card.assignment = "Unassigned" + target_id_card.update_label() playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE) return TRUE if("PRG_edit") - if(!computer || !authenticated || !id_card) + if(!computer || !authenticated || !target_id_card) return var/new_name = params["name"] if(!new_name) return - id_card.registered_name = new_name - id_card.update_label() + target_id_card.registered_name = new_name + target_id_card.update_label() playsound(computer, "terminal_type", 50, FALSE) return TRUE if("PRG_assign") - if(!computer || !authenticated || !id_card) + if(!computer || !authenticated || !target_id_card) return var/target = params["assign_target"] if(!target) @@ -193,8 +192,8 @@ if(target == "Custom") var/custom_name = params["custom_name"] if(custom_name) - id_card.assignment = custom_name - id_card.update_label() + target_id_card.assignment = custom_name + target_id_card.update_label() else if(minor && !(target in head_subordinates)) return @@ -212,10 +211,10 @@ to_chat(user, "No class exists for this job: [target]") return new_access = job.get_access() - id_card.access -= get_all_centcom_access() + get_all_accesses() - id_card.access |= new_access - id_card.assignment = target - id_card.update_label() + target_id_card.access -= get_all_centcom_access() + get_all_accesses() + target_id_card.access |= new_access + target_id_card.assignment = target + target_id_card.update_label() playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) return TRUE if("PRG_access") @@ -223,22 +222,22 @@ return var/access_type = text2num(params["access_target"]) if(access_type in (is_centcom ? get_all_centcom_access() : get_all_accesses())) - if(access_type in id_card.access) - id_card.access -= access_type + if(access_type in target_id_card.access) + target_id_card.access -= access_type else - id_card.access |= access_type + target_id_card.access |= access_type playsound(computer, "terminal_type", 50, FALSE) return TRUE if("PRG_grantall") if(!computer || !authenticated || minor) return - id_card.access |= (is_centcom ? get_all_centcom_access() : get_all_accesses()) + target_id_card.access |= (is_centcom ? get_all_centcom_access() : get_all_accesses()) playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) return TRUE if("PRG_denyall") if(!computer || !authenticated || minor) return - id_card.access.Cut() + target_id_card.access.Cut() playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE) return TRUE if("PRG_grantregion") @@ -247,7 +246,7 @@ var/region = text2num(params["region"]) if(isnull(region)) return - id_card.access |= get_region_accesses(region) + target_id_card.access |= get_region_accesses(region) playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) return TRUE if("PRG_denyregion") @@ -256,7 +255,7 @@ var/region = text2num(params["region"]) if(isnull(region)) return - id_card.access -= get_region_accesses(region) + target_id_card.access -= get_region_accesses(region) playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE) return TRUE @@ -321,17 +320,17 @@ /datum/computer_file/program/card_mod/ui_data(mob/user) var/list/data = get_header_data() - var/obj/item/computer_hardware/card_slot/card_slot + var/obj/item/computer_hardware/card_slot/card_slot2 var/obj/item/computer_hardware/printer/printer if(computer) - card_slot = computer.all_components[MC_CARD] + card_slot2 = computer.all_components[MC_CARD2] printer = computer.all_components[MC_PRINT] data["station_name"] = station_name() if(computer) - data["have_id_slot"] = !!card_slot + data["have_id_slot"] = !!(card_slot2) data["have_printer"] = !!printer else data["have_id_slot"] = FALSE @@ -340,7 +339,7 @@ data["authenticated"] = authenticated if(computer) - var/obj/item/card/id/id_card = card_slot.stored_card + var/obj/item/card/id/id_card = card_slot2.stored_card data["has_id"] = !!id_card data["id_name"] = id_card ? id_card.name : "-----" if(id_card) diff --git a/code/modules/modular_computers/file_system/programs/cargobounty.dm b/code/modules/modular_computers/file_system/programs/cargobounty.dm index d9bc65c98d..74ac44ade3 100644 --- a/code/modules/modular_computers/file_system/programs/cargobounty.dm +++ b/code/modules/modular_computers/file_system/programs/cargobounty.dm @@ -5,7 +5,6 @@ extended_desc = "A basic interface for supply personnel to check and claim bounties." requires_ntnet = TRUE transfer_access = ACCESS_CARGO - network_destination = "cargo claims interface" size = 10 tgui_id = "NtosBountyConsole" ///cooldown var for printing paper sheets. diff --git a/code/modules/modular_computers/file_system/programs/cargoship.dm b/code/modules/modular_computers/file_system/programs/cargoship.dm index 3ba08a3719..db8d6d9f82 100644 --- a/code/modules/modular_computers/file_system/programs/cargoship.dm +++ b/code/modules/modular_computers/file_system/programs/cargoship.dm @@ -1,9 +1,8 @@ /datum/computer_file/program/shipping filename = "shipping" - filedesc = "Nanotrasen Shipping Scanner" + filedesc = "GrandArk Exporter" program_icon_state = "shipping" extended_desc = "A combination printer/scanner app that enables modular computers to print barcodes for easy scanning and shipping." - network_destination = "ship scanner" size = 6 tgui_id = "NtosShipping" ///Account used for creating barcodes. diff --git a/code/modules/modular_computers/file_system/programs/crewmanifest.dm b/code/modules/modular_computers/file_system/programs/crewmanifest.dm index a1503ce3a8..4f2688d8f1 100644 --- a/code/modules/modular_computers/file_system/programs/crewmanifest.dm +++ b/code/modules/modular_computers/file_system/programs/crewmanifest.dm @@ -1,10 +1,10 @@ /datum/computer_file/program/crew_manifest - filename = "crewmani" - filedesc = "Crew Manifest" + filename = "plexagoncrew" + filedesc = "Plexagon Crew List" program_icon_state = "id" extended_desc = "Program for viewing and printing the current crew manifest" transfer_access = ACCESS_HEADS - requires_ntnet = FALSE + requires_ntnet = TRUE size = 4 tgui_id = "NtosCrewManifest" diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm index bccc6e4dbe..b88b793b66 100644 --- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm +++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm @@ -1,10 +1,10 @@ /datum/computer_file/program/job_management - filename = "job_manage" - filedesc = "Job Manager" + filename = "plexagoncore" + filedesc = "Plexagon HR Core" program_icon_state = "id" extended_desc = "Program for viewing and changing job slot avalibility." transfer_access = ACCESS_HEADS - requires_ntnet = 0 + requires_ntnet = TRUE size = 4 tgui_id = "NtosJobManager" diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm index 6401d6207f..8fbcfd0b01 100644 --- a/code/modules/modular_computers/file_system/programs/ntdownloader.dm +++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm @@ -1,14 +1,14 @@ /datum/computer_file/program/ntnetdownload - filename = "ntndownloader" - filedesc = "Software Download Tool" + filename = "ntsoftwarehub" + filedesc = "NT Software Hub" program_icon_state = "generic" extended_desc = "This program allows downloads of software from official NT repositories" - unsendable = 1 - undeletable = 1 + unsendable = TRUE + undeletable = TRUE size = 4 - requires_ntnet = 1 + requires_ntnet = TRUE requires_ntnet_feature = NTNET_SOFTWAREDOWNLOAD - available_on_ntnet = 0 + available_on_ntnet = FALSE ui_header = "downloader_finished.gif" tgui_id = "NtosNetDownloader" @@ -125,6 +125,8 @@ if(!istype(my_computer)) return + var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] + var/list/access = card_slot?.GetAccess() var/list/data = get_header_data() @@ -146,7 +148,7 @@ for(var/A in main_repo) var/datum/computer_file/program/P = A // Only those programs our user can run will show in the list - if(!P.can_run(user,transfer = 1) || hard_drive.find_file_by_name(P.filename)) + if(!P.can_run(user,transfer = 1, access = access) || hard_drive.find_file_by_name(P.filename)) continue all_entries.Add(list(list( "filename" = P.filename, diff --git a/code/modules/modular_computers/file_system/programs/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/ntmonitor.dm index 7d6d89f32c..bbbde14780 100644 --- a/code/modules/modular_computers/file_system/programs/ntmonitor.dm +++ b/code/modules/modular_computers/file_system/programs/ntmonitor.dm @@ -1,6 +1,6 @@ /datum/computer_file/program/ntnetmonitor - filename = "ntmonitor" - filedesc = "NTNet Diagnostics and Monitoring" + filename = "wirecarp" + filedesc = "WireCarp" //wireshark. program_icon_state = "comm_monitor" extended_desc = "This program monitors stationwide NTNet network, provides access to logging systems, and allows for configuration changes" size = 12 diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm index df9b02d8ec..f03ff3f8fd 100644 --- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm +++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm @@ -6,7 +6,6 @@ size = 8 requires_ntnet = 1 requires_ntnet_feature = NTNET_COMMUNICATION - network_destination = "NTNRC server" ui_header = "ntnrc_idle.gif" available_on_ntnet = 1 tgui_id = "NtosNetChat" diff --git a/code/modules/modular_computers/file_system/programs/powermonitor.dm b/code/modules/modular_computers/file_system/programs/powermonitor.dm index bd11474858..e87a731a40 100644 --- a/code/modules/modular_computers/file_system/programs/powermonitor.dm +++ b/code/modules/modular_computers/file_system/programs/powermonitor.dm @@ -1,15 +1,14 @@ //normal computer version is located in code\modules\power\monitor.dm, /obj/machinery/computer/monitor /datum/computer_file/program/power_monitor - filename = "powermonitor" - filedesc = "Power Monitor" + filename = "ampcheck" + filedesc = "AmpCheck" program_icon_state = "power_monitor" extended_desc = "This program connects to sensors around the station to provide information about electrical systems" ui_header = "power_norm.gif" transfer_access = ACCESS_ENGINE usage_flags = PROGRAM_CONSOLE requires_ntnet = 0 - network_destination = "power monitoring system" size = 9 tgui_id = "NtosPowerMonitor" diff --git a/code/modules/modular_computers/file_system/programs/radar.dm b/code/modules/modular_computers/file_system/programs/radar.dm index 9b0e09ef99..216365d6ea 100644 --- a/code/modules/modular_computers/file_system/programs/radar.dm +++ b/code/modules/modular_computers/file_system/programs/radar.dm @@ -7,7 +7,6 @@ transfer_access = null available_on_ntnet = FALSE usage_flags = PROGRAM_LAPTOP | PROGRAM_TABLET - network_destination = "tracking program" size = 5 tgui_id = "NtosRadar" ///List of trackable entities. Updated by the scan() proc. @@ -207,7 +206,7 @@ ///A program that tracks crew members via suit sensors /datum/computer_file/program/radar/lifeline - filename = "Lifeline" + filename = "lifeline" filedesc = "Lifeline" extended_desc = "This program allows for tracking of crew members via their suit sensors." requires_ntnet = TRUE @@ -252,9 +251,9 @@ //Nuke Disk Finder App// //////////////////////// -///A program that tracks crew members via suit sensors +///A program that tracks nukes and nuclear accessories /datum/computer_file/program/radar/fission360 - filename = "Fission360" + filename = "fission360" filedesc = "Fission360" program_icon_state = "radarsyndicate" extended_desc = "This program allows for tracking of nuclear authorization disks and warheads." @@ -276,8 +275,6 @@ objects = list() for(var/i in GLOB.nuke_list) var/obj/machinery/nuclearbomb/nuke = i - if(!trackable(nuke)) - continue var/list/nukeinfo = list( ref = REF(nuke), @@ -285,9 +282,8 @@ ) objects += list(nukeinfo) var/obj/item/disk/nuclear/disk = locate() in GLOB.poi_list - if(trackable(disk)) - var/list/nukeinfo = list( - ref = REF(disk), - name = disk.name, - ) - objects += list(nukeinfo) + var/list/nukeinfo = list( + ref = REF(disk), + name = "Nuke Auth. Disk", + ) + objects += list(nukeinfo) diff --git a/code/modules/modular_computers/file_system/programs/robocontrol.dm b/code/modules/modular_computers/file_system/programs/robocontrol.dm index 8644ce09b4..c0b82b9c95 100644 --- a/code/modules/modular_computers/file_system/programs/robocontrol.dm +++ b/code/modules/modular_computers/file_system/programs/robocontrol.dm @@ -1,12 +1,11 @@ /datum/computer_file/program/robocontrol - filename = "robocontrol" - filedesc = "Bot Remote Controller" + filename = "botkeeper" + filedesc = "Botkeeper" program_icon_state = "robot" extended_desc = "A remote controller used for giving basic commands to non-sentient robots." transfer_access = ACCESS_ROBOTICS requires_ntnet = TRUE - network_destination = "robotics control network" size = 12 tgui_id = "NtosRoboControl" ///Number of simple robots on-station. @@ -78,7 +77,7 @@ return if(id_card) GLOB.data_core.manifest_modify(id_card.registered_name, id_card.assignment) - card_slot.try_eject(TRUE, current_user) + card_slot.try_eject(current_user) else playsound(get_turf(ui_host()) , 'sound/machines/buzz-sigh.ogg', 25, FALSE) return diff --git a/code/modules/modular_computers/file_system/programs/sm_monitor.dm b/code/modules/modular_computers/file_system/programs/sm_monitor.dm index 32ad102871..e4cf590930 100644 --- a/code/modules/modular_computers/file_system/programs/sm_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/sm_monitor.dm @@ -1,12 +1,11 @@ /datum/computer_file/program/supermatter_monitor - filename = "smmonitor" - filedesc = "Supermatter Monitoring" + filename = "ntcims" + filedesc = "NT CIMS" ui_header = "smmon_0.gif" program_icon_state = "smmon_0" - extended_desc = "This program connects to specially calibrated supermatter sensors to provide information on the status of supermatter-based engines." + extended_desc = "Crystal Integrity Monitoring System, connects to specially calibrated supermatter sensors to provide information on the status of supermatter-based engines." requires_ntnet = TRUE transfer_access = ACCESS_CONSTRUCTION - network_destination = "supermatter monitoring system" size = 5 tgui_id = "NtosSupermatterMonitor" var/last_status = SUPERMATTER_INACTIVE diff --git a/code/modules/modular_computers/hardware/_hardware.dm b/code/modules/modular_computers/hardware/_hardware.dm index b33442f99b..81555340b2 100644 --- a/code/modules/modular_computers/hardware/_hardware.dm +++ b/code/modules/modular_computers/hardware/_hardware.dm @@ -10,9 +10,11 @@ // Computer that holds this hardware, if any. var/power_usage = 0 // If the hardware uses extra power, change this. - var/enabled = 1 // If the hardware is turned off set this to 0. - var/critical = 0 // Prevent disabling for important component, like the CPU. - var/can_install = 1 // Prevents direct installation of removable media. + var/enabled = TRUE // If the hardware is turned off set this to 0. + var/critical = FALSE // Prevent disabling for important component, like the CPU. + var/can_install = TRUE // Prevents direct installation of removable media. + var/expansion_hw = FALSE // Hardware that fits into expansion bays. + var/removable = TRUE // Whether the hardware is removable or not. var/damage = 0 // Current damage level var/max_damage = 100 // Maximal damage level. var/damage_malfunction = 20 // "Malfunction" threshold. When damage exceeds this value the hardware piece will semi-randomly fail and do !!FUN!! things @@ -20,7 +22,7 @@ var/malfunction_probability = 10// Chance of malfunction when the component is damaged var/device_type -/obj/item/computer_hardware/New(var/obj/L) +/obj/item/computer_hardware/New(obj/L) ..() pixel_x = rand(-8, 8) pixel_y = rand(-8, 8) @@ -56,7 +58,7 @@ return TRUE // Called on multitool click, prints diagnostic information to the user. -/obj/item/computer_hardware/proc/diagnostics(var/mob/user) +/obj/item/computer_hardware/proc/diagnostics(mob/user) to_chat(user, "Hardware Integrity Test... (Corruption: [damage]/[max_damage]) [damage > damage_failure ? "FAIL" : damage > damage_malfunction ? "WARN" : "PASS"]") // Handles damage checks @@ -73,7 +75,7 @@ return TRUE // Good to go. -/obj/item/computer_hardware/examine(var/mob/user) +/obj/item/computer_hardware/examine(mob/user) . = ..() if(damage > damage_failure) . += "It seems to be severely damaged!" diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm index 0ad157afcb..c874d786a0 100644 --- a/code/modules/modular_computers/hardware/ai_slot.dm +++ b/code/modules/modular_computers/hardware/ai_slot.dm @@ -5,6 +5,7 @@ icon_state = "card_mini" w_class = WEIGHT_CLASS_SMALL device_type = MC_AI + expansion_hw = TRUE var/obj/item/aicard/stored_card = null var/locked = FALSE @@ -19,12 +20,6 @@ if(stored_card) . += "There appears to be an intelliCard loaded. There appears to be a pinhole protecting a manual eject button. A screwdriver could probably press it." -/obj/item/computer_hardware/ai_slot/on_install(obj/item/modular_computer/M, mob/living/user = null) - M.add_verb(device_type) - -/obj/item/computer_hardware/ai_slot/on_remove(obj/item/modular_computer/M, mob/living/user = null) - M.remove_verb(device_type) - /obj/item/computer_hardware/ai_slot/try_insert(obj/item/I, mob/living/user = null) if(!holder) return FALSE @@ -44,7 +39,7 @@ return TRUE -/obj/item/computer_hardware/ai_slot/try_eject(slot=0,mob/living/user = null,forced = 0) +/obj/item/computer_hardware/ai_slot/try_eject(mob/living/user = null,forced = FALSE) if(!stored_card) to_chat(user, "There is no card in \the [src].") return FALSE diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm index 6e3193abfd..0668248315 100644 --- a/code/modules/modular_computers/hardware/battery_module.dm +++ b/code/modules/modular_computers/hardware/battery_module.dm @@ -21,7 +21,7 @@ /obj/item/computer_hardware/battery/handle_atom_del(atom/A) if(A == battery) - try_eject(0, null, TRUE) + try_eject(forced = TRUE) . = ..() /obj/item/computer_hardware/battery/try_insert(obj/item/I, mob/living/user = null) @@ -48,7 +48,7 @@ return TRUE -/obj/item/computer_hardware/battery/try_eject(slot=0, mob/living/user = null, forced = 0) +/obj/item/computer_hardware/battery/try_eject(mob/living/user = null, forced = FALSE) if(!battery) to_chat(user, "There is no power cell connected to \the [src].") return FALSE diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm index 18b423a42e..c243bf7db1 100644 --- a/code/modules/modular_computers/hardware/card_slot.dm +++ b/code/modules/modular_computers/hardware/card_slot.dm @@ -7,13 +7,10 @@ device_type = MC_CARD var/obj/item/card/id/stored_card = null - var/obj/item/card/id/stored_card2 = null /obj/item/computer_hardware/card_slot/handle_atom_del(atom/A) if(A == stored_card) - try_eject(1, null, TRUE) - if(A == stored_card2) - try_eject(2, null, TRUE) + try_eject(null, TRUE) . = ..() /obj/item/computer_hardware/card_slot/Destroy() @@ -21,37 +18,25 @@ return ..() /obj/item/computer_hardware/card_slot/GetAccess() - if(stored_card && stored_card2) // Best of both worlds - return (stored_card.GetAccess() | stored_card2.GetAccess()) - else if(stored_card) - return stored_card.GetAccess() - else if(stored_card2) - return stored_card2.GetAccess() - return ..() + var/list/total_access + if(stored_card) + total_access = stored_card.GetAccess() + var/obj/item/computer_hardware/card_slot/card_slot2 = holder?.all_components[MC_CARD2] //Best of both worlds + if(card_slot2?.stored_card) + total_access |= card_slot2.stored_card.GetAccess() + return total_access /obj/item/computer_hardware/card_slot/GetID() if(stored_card) return stored_card - else if(stored_card2) - return stored_card2 return ..() /obj/item/computer_hardware/card_slot/RemoveID() if(stored_card) . = stored_card - if(!try_eject(1)) + if(!try_eject()) return null return - if(stored_card2) - . = stored_card2 - if(!try_eject(2)) - return null - -/obj/item/computer_hardware/card_slot/on_install(obj/item/modular_computer/M, mob/living/user = null) - M.add_verb(device_type) - -/obj/item/computer_hardware/card_slot/on_remove(obj/item/modular_computer/M, mob/living/user = null) - M.remove_verb(device_type) /obj/item/computer_hardware/card_slot/try_insert(obj/item/I, mob/living/user = null) if(!holder) @@ -60,8 +45,7 @@ if(!istype(I, /obj/item/card/id)) return FALSE - if(stored_card && stored_card2) - to_chat(user, "You try to insert \the [I] into \the [src], but its slots are occupied.") + if(stored_card) return FALSE if(user) if(!user.transferItemToLoc(I, src)) @@ -69,11 +53,8 @@ else I.forceMove(src) - if(!stored_card) - stored_card = I - else - stored_card2 = I - to_chat(user, "You insert \the [I] into \the [src].") + stored_card = I + to_chat(user, "You insert \the [I] into \the [expansion_hw ? "secondary":"primary"] [src].") playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) if(ishuman(user)) var/mob/living/carbon/human/H = user @@ -82,53 +63,58 @@ return TRUE -/obj/item/computer_hardware/card_slot/try_eject(slot=0, mob/living/user = null, forced = 0) - if(!stored_card && !stored_card2) +/obj/item/computer_hardware/card_slot/try_eject(mob/living/user = null, forced = FALSE) + if(!stored_card) to_chat(user, "There are no cards in \the [src].") return FALSE - var/ejected = 0 - if(stored_card && (!slot || slot == 1)) - if(user) - user.put_in_hands(stored_card) - else - stored_card.forceMove(drop_location()) - stored_card = null - ejected++ + if(user) + user.put_in_hands(stored_card) + else + stored_card.forceMove(drop_location()) + stored_card = null - if(stored_card2 && (!slot || slot == 2)) - if(user) - user.put_in_hands(stored_card2) - else - stored_card2.forceMove(drop_location()) - stored_card2 = null - ejected++ + if(holder) + if(holder.active_program) + holder.active_program.event_idremoved(0) - if(ejected) - if(holder) - if(holder.active_program) - holder.active_program.event_idremoved(0, slot) - - for(var/I in holder.idle_threads) - var/datum/computer_file/program/P = I - P.event_idremoved(1, slot) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.sec_hud_set_ID() - to_chat(user, "You remove the card[ejected>1 ? "s" : ""] from \the [src].") - playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) - return TRUE - return FALSE + for(var/p in holder.idle_threads) + var/datum/computer_file/program/computer_program = p + computer_program.event_idremoved(1) + if(ishuman(user)) + var/mob/living/carbon/human/human_user = user + human_user.sec_hud_set_ID() + to_chat(user, "You remove the card from \the [src].") + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + return TRUE /obj/item/computer_hardware/card_slot/attackby(obj/item/I, mob/living/user) if(..()) return if(I.tool_behaviour == TOOL_SCREWDRIVER) - to_chat(user, "You press down on the manual eject button with \the [I].") - try_eject(0,user) - return + if(stored_card) + to_chat(user, "You press down on the manual eject button with \the [I].") + try_eject(user) + return + swap_slot() + to_chat(user, "You adjust the connecter to fit into [expansion_hw ? "an expansion bay" : "the primary ID bay"].") + +/** + *Swaps the card_slot hardware between using the dedicated card slot bay on a computer, and using an expansion bay. +*/ +/obj/item/computer_hardware/card_slot/proc/swap_slot() + expansion_hw = !expansion_hw + if(expansion_hw) + device_type = MC_CARD2 + else + device_type = MC_CARD /obj/item/computer_hardware/card_slot/examine(mob/user) . = ..() - if(stored_card || stored_card2) + . += "The connector is set to fit into [expansion_hw ? "an expansion bay" : "a computer's primary ID bay"], but can be adjusted with a screwdriver." + if(stored_card) . += "There appears to be something loaded in the card slots." + +/obj/item/computer_hardware/card_slot/secondary + device_type = MC_CARD2 + expansion_hw = TRUE diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm index b8b9624388..e5c133de20 100644 --- a/code/modules/modular_computers/hardware/hard_drive.dm +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -22,14 +22,14 @@ . = ..() . += "It has [max_capacity] GQ of storage capacity." -/obj/item/computer_hardware/hard_drive/diagnostics(var/mob/user) +/obj/item/computer_hardware/hard_drive/diagnostics(mob/user) ..() // 999 is a byond limit that is in place. It's unlikely someone will reach that many files anyway, since you would sooner run out of space. to_chat(user, "NT-NFS File Table Status: [stored_files.len]/999") to_chat(user, "Storage capacity: [used_capacity]/[max_capacity]GQ") // Use this proc to add file to the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks. -/obj/item/computer_hardware/hard_drive/proc/store_file(var/datum/computer_file/F) +/obj/item/computer_hardware/hard_drive/proc/store_file(datum/computer_file/F) if(!F || !istype(F)) return 0 @@ -52,7 +52,7 @@ return 1 // Use this proc to remove file from the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks. -/obj/item/computer_hardware/hard_drive/proc/remove_file(var/datum/computer_file/F) +/obj/item/computer_hardware/hard_drive/proc/remove_file(datum/computer_file/F) if(!F || !istype(F)) return 0 @@ -78,7 +78,7 @@ used_capacity = total_size // Checks whether file can be stored on the hard drive. We can only store unique files, so this checks whether we wouldn't get a duplicity by adding a file. -/obj/item/computer_hardware/hard_drive/proc/can_store_file(var/datum/computer_file/F) +/obj/item/computer_hardware/hard_drive/proc/can_store_file(datum/computer_file/F) if(!F || !istype(F)) return 0 @@ -101,7 +101,7 @@ // Tries to find the file by filename. Returns null on failure -/obj/item/computer_hardware/hard_drive/proc/find_file_by_name(var/filename) +/obj/item/computer_hardware/hard_drive/proc/find_file_by_name(filename) if(!check_functionality()) return null diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm index fe1b1879cb..04bf494fe4 100644 --- a/code/modules/modular_computers/hardware/network_card.dm +++ b/code/modules/modular_computers/hardware/network_card.dm @@ -11,7 +11,7 @@ device_type = MC_NET var/static/ntnet_card_uid = 1 -/obj/item/computer_hardware/network_card/diagnostics(var/mob/user) +/obj/item/computer_hardware/network_card/diagnostics(mob/user) ..() to_chat(user, "NIX Unique ID: [identification_id]") to_chat(user, "NIX User Tag: [identification_string]") @@ -22,7 +22,7 @@ if(ethernet) to_chat(user, "OpenEth (Physical Connection) - Physical network connection port") -/obj/item/computer_hardware/network_card/New(var/l) +/obj/item/computer_hardware/network_card/New(l) ..() identification_id = ntnet_card_uid++ @@ -31,7 +31,7 @@ return "[identification_string] (NID [identification_id])" // 0 - No signal, 1 - Low signal, 2 - High signal. 3 - Wired Connection -/obj/item/computer_hardware/network_card/proc/get_signal(var/specific_action = 0) +/obj/item/computer_hardware/network_card/proc/get_signal(specific_action = 0) if(!holder) // Hardware is not installed in anything. No signal. How did this even get called? return 0 diff --git a/code/modules/modular_computers/hardware/portable_disk.dm b/code/modules/modular_computers/hardware/portable_disk.dm index b5a957be04..89b0382e86 100644 --- a/code/modules/modular_computers/hardware/portable_disk.dm +++ b/code/modules/modular_computers/hardware/portable_disk.dm @@ -8,12 +8,8 @@ max_capacity = 16 device_type = MC_SDD -/obj/item/computer_hardware/hard_drive/portable/on_install(obj/item/modular_computer/M, mob/living/user = null) - M.add_verb(device_type) - -/obj/item/computer_hardware/hard_drive/portable/on_remove(obj/item/modular_computer/M, mob/living/user = null) - ..() - M.remove_verb(device_type) +/obj/item/computer_hardware/hard_drive/portable/on_remove(obj/item/modular_computer/MC, mob/user) + return //this is a floppy disk, let's not shut the computer down when it gets pulled out. /obj/item/computer_hardware/hard_drive/portable/install_default_programs() return // Empty by default diff --git a/code/modules/modular_computers/hardware/printer.dm b/code/modules/modular_computers/hardware/printer.dm index ebe40c1922..3bd5946435 100644 --- a/code/modules/modular_computers/hardware/printer.dm +++ b/code/modules/modular_computers/hardware/printer.dm @@ -5,6 +5,7 @@ icon_state = "printer" w_class = WEIGHT_CLASS_NORMAL device_type = MC_PRINT + expansion_hw = TRUE var/stored_paper = 20 var/max_paper = 30 diff --git a/code/modules/modular_computers/hardware/sensor_package.dm b/code/modules/modular_computers/hardware/sensor_package.dm new file mode 100644 index 0000000000..c0363bc809 --- /dev/null +++ b/code/modules/modular_computers/hardware/sensor_package.dm @@ -0,0 +1,8 @@ +//This item doesn't do much on its own, but is required by apps such as AtmoZphere. +/obj/item/computer_hardware/sensorpackage + name = "sensor package" + desc = "An integrated sensor package allowing a computer to take readings from the environment. Required by certain programs." + icon_state = "servo" + w_class = WEIGHT_CLASS_TINY + device_type = MC_SENSORS + expansion_hw = TRUE diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm index a8d30bad21..83bb057d66 100644 --- a/code/modules/modular_computers/laptop_vendor.dm +++ b/code/modules/modular_computers/laptop_vendor.dm @@ -52,6 +52,7 @@ var/obj/item/computer_hardware/battery/battery_module = null if(fabricate) fabricated_laptop = new /obj/item/modular_computer/laptop/buildable(src) + fabricated_laptop.install_component(new /obj/item/computer_hardware/card_slot) fabricated_laptop.install_component(new /obj/item/computer_hardware/battery) battery_module = fabricated_laptop.all_components[MC_CELL] total_price = 99 @@ -107,7 +108,7 @@ if(dev_card) total_price += 199 if(fabricate) - fabricated_laptop.install_component(new /obj/item/computer_hardware/card_slot) + fabricated_laptop.install_component(new /obj/item/computer_hardware/card_slot/secondary) return total_price else if(devtype == 2) // Tablet, more expensive, not everyone could probably afford this. @@ -116,6 +117,7 @@ fabricated_tablet = new(src) fabricated_tablet.install_component(new /obj/item/computer_hardware/battery) fabricated_tablet.install_component(new /obj/item/computer_hardware/processor_unit/small) + fabricated_tablet.install_component(new/obj/item/computer_hardware/card_slot) battery_module = fabricated_tablet.all_components[MC_CELL] total_price = 199 switch(dev_battery) @@ -154,11 +156,11 @@ if(dev_printer) total_price += 99 if(fabricate) - fabricated_tablet.install_component(new/obj/item/computer_hardware/printer) + fabricated_tablet.install_component(new/obj/item/computer_hardware/printer/mini) if(dev_card) total_price += 199 if(fabricate) - fabricated_tablet.install_component(new/obj/item/computer_hardware/card_slot) + fabricated_tablet.install_component(new/obj/item/computer_hardware/card_slot/secondary) return total_price return FALSE @@ -257,7 +259,7 @@ say("Insufficient credits on card to purchase!") return credits += target_credits - say("[target_credits] cr has been deposited from your account.") + say("[target_credits] cr have been withdrawn from your account.") return return ..() diff --git a/code/modules/newscaster/newscaster_machine.dm b/code/modules/newscaster/newscaster_machine.dm index 470b34e82c..7e515bf0f5 100644 --- a/code/modules/newscaster/newscaster_machine.dm +++ b/code/modules/newscaster/newscaster_machine.dm @@ -340,7 +340,6 @@ GLOBAL_LIST_EMPTY(allCasters) dat+="Return" var/datum/browser/popup = new(human_or_robot_user, "newscaster_main", "Newscaster Unit #[unit_no]", 400, 600) popup.set_content(dat) - popup.set_title_image(human_or_robot_user.browse_rsc_icon(icon, icon_state)) popup.open() /obj/machinery/newscaster/Topic(href, href_list) diff --git a/code/modules/paperwork/carbonpaper.dm b/code/modules/paperwork/carbonpaper.dm new file mode 100644 index 0000000000..dc8f172069 --- /dev/null +++ b/code/modules/paperwork/carbonpaper.dm @@ -0,0 +1,45 @@ +/obj/item/paper/carbon + name = "sheet of carbon" + icon_state = "paper_stack" + item_state = "paper" + // inhand_icon_state = "paper" + show_written_words = FALSE + var/copied = FALSE + var/iscopy = FALSE + +/obj/item/paper/carbon/update_icon_state() + if(iscopy) + icon_state = "cpaper" + else if(copied) + icon_state = "paper" + else + icon_state = "paper_stack" + if(info) + icon_state = "[icon_state]_words" + +/obj/item/paper/carbon/proc/removecopy(mob/living/user) + if(!copied) + var/obj/item/paper/carbon/C = src + var/copycontents = C.info + var/obj/item/paper/carbon/Copy = new /obj/item/paper/carbon(user.loc) + + if(info) + copycontents = replacetext(copycontents, "" + Copy.name = "Copy - [C.name]" + to_chat(user, "You tear off the carbon-copy!") + C.copied = TRUE + Copy.iscopy = TRUE + Copy.update_icon_state() + C.update_icon_state() + user.put_in_hands(Copy) + else + to_chat(user, "There are no more carbon copies attached to this paper!") + +/obj/item/paper/carbon/on_attack_hand(mob/living/user) + if(loc == user && user.is_holding(src)) + removecopy(user) + return + return ..() diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm index 1a93661c76..5b576a2438 100644 --- a/code/modules/paperwork/clipboard.dm +++ b/code/modules/paperwork/clipboard.dm @@ -3,6 +3,8 @@ icon = 'icons/obj/bureaucracy.dmi' icon_state = "clipboard" item_state = "clipboard" + // inhand_icon_state = "clipboard" + // worn_icon_state = "clipboard" throwforce = 0 w_class = WEIGHT_CLASS_SMALL throw_speed = 3 @@ -34,7 +36,6 @@ . += "clipboard_pen" . += "clipboard_over" - /obj/item/clipboard/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/paper)) if(!user.transferItemToLoc(W, src)) @@ -92,14 +93,14 @@ to_chat(usr, "You slot [W] into [src].") if(href_list["write"]) - var/obj/item/P = locate(href_list["write"]) - if(istype(P) && P.loc == src) + var/obj/item/P = locate(href_list["write"]) in src + if(istype(P)) if(usr.get_active_held_item()) P.attackby(usr.get_active_held_item(), usr) if(href_list["remove"]) - var/obj/item/P = locate(href_list["remove"]) - if(istype(P) && P.loc == src) + var/obj/item/P = locate(href_list["remove"]) in src + if(istype(P)) P.forceMove(usr.loc) usr.put_in_hands(P) if(P == toppaper) @@ -111,13 +112,13 @@ toppaper = null if(href_list["read"]) - var/obj/item/paper/P = locate(href_list["read"]) - if(istype(P) && P.loc == src) + var/obj/item/paper/P = locate(href_list["read"]) in src + if(istype(P)) usr.examinate(P) if(href_list["top"]) - var/obj/item/P = locate(href_list["top"]) - if(istype(P) && P.loc == src) + var/obj/item/P = locate(href_list["top"]) in src + if(istype(P)) toppaper = P to_chat(usr, "You move [P.name] to the top.") diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm index b8f2c95762..390cd0cf83 100644 --- a/code/modules/paperwork/filingcabinet.dm +++ b/code/modules/paperwork/filingcabinet.dm @@ -35,7 +35,7 @@ . = ..() if(mapload) for(var/obj/item/I in loc) - if(istype(I, /obj/item/paper) || istype(I, /obj/item/folder) || istype(I, /obj/item/photo)) + if(I.w_class < WEIGHT_CLASS_NORMAL) //there probably shouldn't be anything placed ontop of filing cabinets in a map that isn't meant to go in them I.forceMove(src) /obj/structure/filingcabinet/deconstruct(disassembled = TRUE) @@ -46,7 +46,12 @@ qdel(src) /obj/structure/filingcabinet/attackby(obj/item/P, mob/user, params) - if(istype(P, /obj/item/paper) || istype(P, /obj/item/folder) || istype(P, /obj/item/photo) || istype(P, /obj/item/documents)) + if(P.tool_behaviour == TOOL_WRENCH && user.a_intent != INTENT_HELP) + to_chat(user, "You begin to [anchored ? "unwrench" : "wrench"] [src].") + if(P.use_tool(src, user, 20, volume=50)) + to_chat(user, "You successfully [anchored ? "unwrench" : "wrench"] [src].") + anchored = !anchored + else if(P.w_class < WEIGHT_CLASS_NORMAL) if(!user.transferItemToLoc(P, src)) return to_chat(user, "You put [P] in [src].") @@ -54,11 +59,6 @@ sleep(5) icon_state = initial(icon_state) updateUsrDialog() - else if(istype(P, /obj/item/wrench)) - to_chat(user, "You begin to [anchored ? "unwrench" : "wrench"] [src].") - if(P.use_tool(src, user, 20, volume=50)) - to_chat(user, "You successfully [anchored ? "unwrench" : "wrench"] [src].") - anchored = !anchored else if(user.a_intent != INTENT_HARM) to_chat(user, "You can't put [P] in [src]!") else @@ -67,9 +67,6 @@ /obj/structure/filingcabinet/ui_interact(mob/user) . = ..() - if(isobserver(user)) - return - if(contents.len <= 0) to_chat(user, "[src] is empty.") return @@ -100,16 +97,17 @@ to_chat(user, "You find nothing in [src].") /obj/structure/filingcabinet/Topic(href, href_list) + if(!usr.canUseTopic(src, BE_CLOSE, ismonkey(usr))) + return if(href_list["retrieve"]) usr << browse("", "window=filingcabinet") // Close the menu - var/obj/item/P = locate(href_list["retrieve"])//contents[retrieveindex] - if(istype(P) && P.loc == src && in_range(src, usr)) + var/obj/item/P = locate(href_list["retrieve"]) in src //contents[retrieveindex] + if(istype(P) && in_range(src, usr)) usr.put_in_hands(P) updateUsrDialog() icon_state = "[initial(icon_state)]-open" - sleep(5) - icon_state = initial(icon_state) + addtimer(VARSET_CALLBACK(src, icon_state, initial(icon_state)), 5) /* @@ -170,6 +168,7 @@ virgin = 0 //tabbing here is correct- it's possible for people to try and use it //before the records have been generated, so we do this inside the loop. +//ATTACK HAND IGNORING PARENT RETURN VALUE /obj/structure/filingcabinet/medical/on_attack_hand() populate() . = ..() diff --git a/code/modules/paperwork/folders.dm b/code/modules/paperwork/folders.dm index c32afab342..305099d115 100644 --- a/code/modules/paperwork/folders.dm +++ b/code/modules/paperwork/folders.dm @@ -33,7 +33,10 @@ if(contents.len) . += "folder_paper" + /obj/item/folder/attackby(obj/item/W, mob/user, params) + if(burn_paper_product_attackby_check(W, user)) + return if(istype(W, /obj/item/paper) || istype(W, /obj/item/photo) || istype(W, /obj/item/documents)) if(!user.transferItemToLoc(W, src)) return @@ -43,11 +46,14 @@ if(!user.is_literate()) to_chat(user, "You scribble illegibly on the cover of [src]!") return + var/inputvalue = stripped_input(user, "What would you like to label the folder?", "Folder Labelling", "", MAX_NAME_LEN) + if(!inputvalue) return + if(user.canUseTopic(src, BE_CLOSE)) - name = "folder - '[inputvalue]'" + name = "folder[(inputvalue ? " - '[inputvalue]'" : null)]" /obj/item/folder/Destroy() @@ -76,14 +82,14 @@ if(usr.contents.Find(src)) if(href_list["remove"]) - var/obj/item/I = locate(href_list["remove"]) - if(istype(I) && I.loc == src) + var/obj/item/I = locate(href_list["remove"]) in src + if(istype(I)) I.forceMove(usr.loc) usr.put_in_hands(I) if(href_list["read"]) - var/obj/item/I = locate(href_list["read"]) - if(istype(I) && I.loc == src) + var/obj/item/I = locate(href_list["read"]) in src + if(istype(I)) usr.examinate(I) //Update everything diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm index d054e5ff12..da9fdc4ca4 100644 --- a/code/modules/paperwork/handlabeler.dm +++ b/code/modules/paperwork/handlabeler.dm @@ -1,9 +1,10 @@ /obj/item/hand_labeler name = "hand labeler" - desc = "A combined label printer and applicator in a portable device, designed to be easy to operate and use." + desc = "A combined label printer, applicator, and remover, all in a single portable device. Designed to be easy to operate and use." icon = 'icons/obj/bureaucracy.dmi' icon_state = "labeler0" item_state = "flight" + // inhand_icon_state = "flight" var/label = null var/labels_left = 30 var/mode = 0 @@ -55,9 +56,10 @@ to_chat(user, "You can't label creatures!") // use a collar return - user.visible_message("[user] labels [A] as [label].", \ - "You label [A] as [label].") - A.name = "[A.name] ([label])" + user.visible_message("[user] labels [A] with \"[label]\".", \ + "You label [A] with \"[label]\".") + A.AddComponent(/datum/component/label, label) + // playsound(A, 'sound/items/handling/component_pickup.ogg', 20, TRUE) labels_left-- @@ -90,7 +92,9 @@ name = "cyborg-hand labeler" /obj/item/hand_labeler/borg/afterattack(atom/A, mob/user, proximity) - . = ..(A, user, proximity) + . = ..() + if(!proximity) + return if(!iscyborg(user)) return @@ -114,6 +118,7 @@ desc = "A roll of paper. Use it on a hand labeler to refill it." icon_state = "labeler_refill" item_state = "electropack" + // inhand_icon_state = "electropack" lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' w_class = WEIGHT_CLASS_TINY diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 5d842ef11a..20ec678e45 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -11,41 +11,6 @@ #define MODE_WRITING 1 #define MODE_STAMPING 2 -/** - * This is a custom ui state. All it really does is keep track of pen - * being used and if they are editing it or not. This way we can keep - * the data with the ui rather than on the paper - */ -/datum/ui_state/default/paper_state - /// What edit mode we are in and who is - /// writing on it right now - var/edit_mode = MODE_READING - /// Setup for writing to a sheet - var/pen_color = "black" - var/pen_font = "" - var/is_crayon = FALSE - /// Setup for stamping a sheet - // Why not the stamp obj? I have no idea - // what happens to states out of scope so - // don't want to put instances in this - var/stamp_icon_state = "" - var/stamp_name = "" - var/stamp_class = "" - -/datum/ui_state/default/paper_state/proc/copy_from(datum/ui_state/default/paper_state/from) - switch(from.edit_mode) - if(MODE_READING) - edit_mode = MODE_READING - if(MODE_WRITING) - edit_mode = MODE_WRITING - pen_color = from.pen_color - pen_font = from.pen_font - is_crayon = from.is_crayon - if(MODE_STAMPING) - edit_mode = MODE_STAMPING - stamp_icon_state = from.stamp_icon_state - stamp_class = from.stamp_class - stamp_name = from.stamp_name /** * Paper is now using markdown (like in github pull notes) for ALL rendering @@ -58,6 +23,9 @@ icon = 'icons/obj/bureaucracy.dmi' icon_state = "paper" item_state = "paper" + // inhand_icon_state = "paper" + // worn_icon_state = "paper" + // custom_fire_overlay = "paper_onfire_overlay" throwforce = 0 w_class = WEIGHT_CLASS_TINY throw_range = 1 @@ -80,34 +48,19 @@ var/list/stamps /// Positioning for the stamp in tgui var/list/stamped /// Overlay info - /// This REALLY should be a componenet. Basicly used during, april fools - /// to honk at you - var/rigged = 0 - var/spam_flag = 0 - var/contact_poison // Reagent ID to transfer on contact var/contact_poison_volume = 0 - // Ok, so WHY are we caching the ui's? - // Since we are not using autoupdate we - // need some way to update the ui's of - // other people looking at it and if - // its been updated. Yes yes, lame - // but canot be helped. However by - // doing it this way, we can see - // live updates and have multipule - // people look at it - var/list/viewing_ui = list() - /// When the sheet can be "filled out" /// This is an associated list var/list/form_fields = list() var/field_counter = 1 /obj/item/paper/Destroy() - close_all_ui() stamps = null stamped = null + form_fields = null + stamped = null . = ..() /** @@ -162,7 +115,7 @@ set category = "Object" set src in usr - if(usr.incapacitated() || !usr.is_literate()) + if(!usr.can_read(src) || usr.incapacitated(TRUE, TRUE) || (isobserver(usr) && !IsAdminGhost(usr))) return if(ishuman(usr)) var/mob/living/carbon/human/H = usr @@ -180,18 +133,6 @@ user.visible_message("[user] scratches a grid on [user.p_their()] wrist with the paper! It looks like [user.p_theyre()] trying to commit sudoku...") return (BRUTELOSS) -/// ONLY USED FOR APRIL FOOLS -/obj/item/paper/proc/reset_spamflag() - spam_flag = FALSE - -/obj/item/paper/attack_self(mob/user) - if(rigged && (SSevents.holidays && SSevents.holidays[APRIL_FOOLS])) - if(!spam_flag) - spam_flag = TRUE - playsound(loc, 'sound/items/bikehorn.ogg', 50, TRUE) - addtimer(CALLBACK(src, .proc/reset_spamflag), 20) - . = ..() - /obj/item/paper/proc/clearpaper() info = "" stamps = null @@ -199,28 +140,39 @@ cut_overlays() update_icon_state() -/obj/item/paper/examine_more(mob/user) - ui_interact(user) - return list("You try to read [src]...") +/obj/item/paper/examine(mob/user) + . = ..() + if(!in_range(user, src) && !isobserver(user)) + . += "You're too far away to read it!" + return + if(user.can_read(src)) + ui_interact(user) + return + . += "You cannot read it!" + +/obj/item/paper/ui_status(mob/user,/datum/ui_state/state) + // Are we on fire? Hard ot read if so + if(resistance_flags & ON_FIRE) + return UI_CLOSE + if(!in_range(user,src)) + return UI_CLOSE + if(user.incapacitated(TRUE, TRUE) || (isobserver(user) && !IsAdminGhost(user))) + return UI_UPDATE + // Even harder to read if your blind...braile? humm + // .. or if you cannot read + if(!user.can_read(src)) + return UI_CLOSE + if(in_contents_of(/obj/machinery/door/airlock)) + return UI_INTERACTIVE + return ..() + + /obj/item/paper/can_interact(mob/user) - if(!..()) - return FALSE - // Are we on fire? Hard ot read if so - if(resistance_flags & ON_FIRE) - return FALSE - // Even harder to read if your blind...braile? humm - if(user.is_blind()) - return FALSE - // checks if the user can read. - return user.can_read(src) + if(in_contents_of(/obj/machinery/door/airlock)) + return TRUE + return ..() -/** - * This creates the ui, since we are using a custom state but not much else - * just makes it easyer to make it. - */ -/obj/item/paper/proc/create_ui(mob/user, datum/ui_state/default/paper_state/state) - ui_interact(user, state = state) /obj/item/proc/burn_paper_product_attackby_check(obj/item/I, mob/living/user, bypass_clumsy) var/ignition_message = I.ignition_effect(src, user) @@ -244,49 +196,22 @@ /obj/item/paper/attackby(obj/item/P, mob/living/user, params) if(burn_paper_product_attackby_check(P, user)) - close_all_ui() + SStgui.close_uis(src) return if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) if(length(info) >= MAX_PAPER_LENGTH) // Sheet must have less than 1000 charaters to_chat(user, "This sheet of paper is full!") return - - var/datum/ui_state/default/paper_state/state = new - state.edit_mode = MODE_WRITING - // should a crayon be in the same subtype as a pen? How about a brush or charcoal? - // TODO: Convert all writing stuff to one type, /obj/item/art_tool maybe? - state.is_crayon = istype(P, /obj/item/toy/crayon); - if(state.is_crayon) - var/obj/item/toy/crayon/PEN = P - state.pen_font = CRAYON_FONT - state.pen_color = PEN.paint_color - else - var/obj/item/pen/PEN = P - state.pen_font = PEN.font - state.pen_color = PEN.colour - - create_ui(user, state) + ui_interact(user) return else if(istype(P, /obj/item/stamp)) - - var/datum/ui_state/default/paper_state/state = new - state.edit_mode = MODE_STAMPING // we are read only becausse the sheet is full - state.stamp_icon_state = P.icon_state - state.stamp_name = P.name - - var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/simple/paper) - state.stamp_class = sheet.icon_class_name(P.icon_state) - to_chat(user, "You ready your stamp over the paper! ") - - create_ui(user, state) + ui_interact(user) return /// Normaly you just stamp, you don't need to read the thing else // cut paper? the sky is the limit! - var/datum/ui_state/default/paper_state/state = new - state.edit_mode = MODE_READING - create_ui(user, state) // The other ui will be created with just read mode outside of this + ui_interact(user) // The other ui will be created with just read mode outside of this return ..() @@ -301,68 +226,64 @@ get_asset_datum(/datum/asset/spritesheet/simple/paper), ) -/obj/item/paper/ui_interact(mob/user, datum/tgui/ui, - datum/ui_state/default/paper_state/state) - // Update the state - ui = ui || SStgui.get_open_ui(user, src) - if(ui && state) - var/datum/ui_state/default/paper_state/current_state = ui.state - current_state.copy_from(state) +/obj/item/paper/ui_interact(mob/user, datum/tgui/ui) // Update the UI ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "PaperSheet", name) - state = new - ui.set_state(state) - ui.set_autoupdate(FALSE) - viewing_ui[user] = ui ui.open() -/obj/item/paper/ui_close(mob/user) - /// close the editing window and change the mode - viewing_ui[user] = null - . = ..() -// Again, we have to do this as autoupdate is off -/obj/item/paper/proc/update_all_ui() - for(var/datum/tgui/ui in viewing_ui) - ui.process(force = TRUE) +/obj/item/paper/ui_static_data(mob/user) + . = list() + .["text"] = info + .["max_length"] = MAX_PAPER_LENGTH + .["paper_color"] = !color || color == "white" ? "#FFFFFF" : color // color might not be set + .["paper_state"] = icon_state /// TODO: show the sheet will bloodied or crinkling? + .["stamps"] = stamps + -// Again, we have to do this as autoupdate is off -/obj/item/paper/proc/close_all_ui() - for(var/datum/tgui/ui in viewing_ui) - ui.close() - viewing_ui = list() /obj/item/paper/ui_data(mob/user) var/list/data = list() - - var/datum/tgui/ui = viewing_ui[user] - var/datum/ui_state/default/paper_state/state = ui.state - - // Should all this go in static data and just do a forced update? - data["text"] = info - data["max_length"] = MAX_PAPER_LENGTH - data["paper_state"] = icon_state /// TODO: show the sheet will bloodied or crinkling? - data["paper_color"] = !color || color == "white" ? "#FFFFFF" : color // color might not be set - data["stamps"] = stamps - - data["edit_mode"] = state.edit_mode - data["edit_usr"] = "[ui.user]"; - - // pen info for editing - data["is_crayon"] = state.is_crayon - data["pen_font"] = state.pen_font - data["pen_color"] = state.pen_color - // stamping info for..stamping - data["stamp_class"] = state.stamp_class - + var/obj/O = user.get_active_held_item() + if(istype(O, /obj/item/toy/crayon)) + var/obj/item/toy/crayon/PEN = O + data["pen_font"] = CRAYON_FONT + data["pen_color"] = PEN.paint_color + data["edit_mode"] = MODE_WRITING + data["is_crayon"] = TRUE + data["stamp_class"] = "FAKE" + data["stamp_icon_state"] = "FAKE" + else if(istype(O, /obj/item/pen)) + var/obj/item/pen/PEN = O + data["pen_font"] = PEN.font + data["pen_color"] = PEN.colour + data["edit_mode"] = MODE_WRITING + data["is_crayon"] = FALSE + data["stamp_class"] = "FAKE" + data["stamp_icon_state"] = "FAKE" + else if(istype(O, /obj/item/stamp)) + var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/simple/paper) + data["stamp_icon_state"] = O.icon_state + data["stamp_class"] = sheet.icon_class_name(O.icon_state) + data["edit_mode"] = MODE_STAMPING + data["pen_font"] = "FAKE" + data["pen_color"] = "FAKE" + data["is_crayon"] = FALSE + else + data["edit_mode"] = MODE_READING + data["pen_font"] = "FAKE" + data["pen_color"] = "FAKE" + data["is_crayon"] = FALSE + data["stamp_icon_state"] = "FAKE" + data["stamp_class"] = "FAKE" data["field_counter"] = field_counter data["form_fields"] = form_fields return data -/obj/item/paper/ui_act(action, params, datum/tgui/ui, datum/ui_state/default/paper_state/state) +/obj/item/paper/ui_act(action, params,datum/tgui/ui) if(..()) return switch(action) @@ -370,34 +291,33 @@ var/stamp_x = text2num(params["x"]) var/stamp_y = text2num(params["y"]) var/stamp_r = text2num(params["r"]) // rotation in degrees - + var/stamp_icon_state = params["stamp_icon_state"] + var/stamp_class = params["stamp_class"] if (isnull(stamps)) - stamps = new/list() + stamps = list() if(stamps.len < MAX_PAPER_STAMPS) // I hate byond when dealing with freaking lists - stamps += list(list(state.stamp_class, stamp_x, stamp_y,stamp_r)) /// WHHHHY + stamps[++stamps.len] = list(stamp_class, stamp_x, stamp_y, stamp_r) /// WHHHHY /// This does the overlay stuff if (isnull(stamped)) - stamped = new/list() + stamped = list() if(stamped.len < MAX_PAPER_STAMPS_OVERLAYS) - var/mutable_appearance/stampoverlay = mutable_appearance('icons/obj/bureaucracy.dmi', "paper_[state.stamp_icon_state]") + var/mutable_appearance/stampoverlay = mutable_appearance('icons/obj/bureaucracy.dmi', "paper_[stamp_icon_state]") stampoverlay.pixel_x = rand(-2, 2) stampoverlay.pixel_y = rand(-3, 2) add_overlay(stampoverlay) - LAZYADD(stamped, state.stamp_icon_state) + LAZYADD(stamped, stamp_icon_state) - ui.user.visible_message("[ui.user] stamps [src] with [state.stamp_name]!", "You stamp [src] with [state.stamp_name]!") + update_static_data(usr,ui) + ui.user.visible_message("[ui.user] stamps [src] with [stamp_class]!", "You stamp [src] with [stamp_class]!") else to_chat(usr, pick("You try to stamp but you miss!", "There is no where else you can stamp!")) - - update_all_ui() . = TRUE if("save") var/in_paper = params["text"] var/paper_len = length(in_paper) - var/list/fields = params["form_fields"] field_counter = params["field_counter"] ? text2num(params["field_counter"]) : field_counter if(paper_len > MAX_PAPER_LENGTH) @@ -413,14 +333,10 @@ if(info != in_paper) to_chat(ui.user, "You have added to your paper masterpiece!"); info = in_paper - - for(var/key in fields) - form_fields[key] = fields[key]; + update_static_data(usr,ui) - update_all_ui() update_icon() - . = TRUE /** diff --git a/code/modules/paperwork/paper_cutter.dm b/code/modules/paperwork/paper_cutter.dm index 3937720f74..a3b9b23141 100644 --- a/code/modules/paperwork/paper_cutter.dm +++ b/code/modules/paperwork/paper_cutter.dm @@ -26,11 +26,11 @@ var/obj/item/bodypart/BP = C.get_bodypart(BODY_ZONE_HEAD) if(BP) BP.drop_limb() - playsound(loc,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1) + playsound(loc, pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg'),50, TRUE, -1) return (BRUTELOSS) else user.visible_message("[user] repeatedly bashes [src.name] against [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide!") - playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1) + playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1) return (BRUTELOSS) @@ -42,11 +42,12 @@ if(storedpaper) . += "paper" + /obj/item/papercutter/attackby(obj/item/P, mob/user, params) if(istype(P, /obj/item/paper) && !storedpaper) if(!user.transferItemToLoc(P, src)) return - playsound(loc, "pageturn", 60, 1) + playsound(loc, "pageturn", 60, TRUE) to_chat(user, "You place [P] in [src].") storedpaper = P update_icon() @@ -59,17 +60,17 @@ storedcutter = P update_icon() return - if(istype(P, /obj/item/screwdriver) && storedcutter) + if(P.tool_behaviour == TOOL_SCREWDRIVER && storedcutter) P.play_tool_sound(src) to_chat(user, "[storedcutter] has been [cuttersecured ? "unsecured" : "secured"].") cuttersecured = !cuttersecured return ..() -/obj/item/papercutter/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) +/obj/item/papercutter/on_attack_hand(mob/user) add_fingerprint(user) if(!storedcutter) - to_chat(user, "The cutting blade is gone! You can't use [src] now.") + to_chat(user, "The cutting blade is gone! You can't use [src] now.") return if(!cuttersecured) @@ -79,7 +80,7 @@ update_icon() if(storedpaper) - playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1) + playsound(src.loc, 'sound/weapons/slash.ogg', 50, TRUE) to_chat(user, "You neatly cut [storedpaper].") storedpaper = null qdel(storedpaper) @@ -88,6 +89,7 @@ update_icon() /obj/item/papercutter/MouseDrop(atom/over_object) + . = ..() var/mob/M = usr if(M.incapacitated() || !Adjacent(M)) return @@ -98,10 +100,6 @@ else if(istype(over_object, /obj/screen/inventory/hand)) var/obj/screen/inventory/hand/H = over_object M.putItemFromInventoryInHandIfPossible(src, H.held_index) - - else - . = ..() - add_fingerprint(M) /obj/item/paperslip @@ -112,6 +110,12 @@ resistance_flags = FLAMMABLE max_integrity = 50 +/obj/item/paperslip/attackby(obj/item/I, mob/living/user, params) + if(burn_paper_product_attackby_check(I, user)) + return + return ..() + + /obj/item/paperslip/Initialize() . = ..() pixel_x = rand(-5, 5) @@ -124,5 +128,6 @@ icon = 'icons/obj/bureaucracy.dmi' icon_state = "cutterblade" item_state = "knife" + // inhand_icon_state = "knife" lefthand_file = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/kitchen_righthand.dmi' diff --git a/code/modules/paperwork/paper_premade.dm b/code/modules/paperwork/paper_premade.dm index 414026924a..5d6d3d45e8 100644 --- a/code/modules/paperwork/paper_premade.dm +++ b/code/modules/paperwork/paper_premade.dm @@ -4,53 +4,22 @@ /obj/item/paper/fluff/sop name = "paper- 'Standard Operating Procedure'" - info = {" -Alert Levels: -* Blue - Emergency - * Caused by fire - * Caused by manual interaction - * Action: Close all fire doors. These can only be opened by resetting the alarm -* Red- Ejection/Self Destruct - * Caused by module operating computer. - * Action: After the specified time the module will eject completely. -Engine Maintenance Instructions: -1. Shut off ignition systems: -2. Activate internal power -3. Activate orbital balance matrix -4. Remove volatile liquids from area -5. Wear a fire suit -6. After Decontaminate Visit medical examiner -Toxin Laboratory Procedure: -1. Wear a gas mask regardless -2. Get an oxygen tank. -3. Activate internal atmosphere -4. After Decontaminate Visit medical examiner -Disaster Procedure: -Fire: -1. Activate sector fire alarm. -2. Move to a safe area. -3. Get a fire suit -* After: - 1. Assess Damage - 2. Repair damages - 3. If needed, Evacuate -Meteor Shower: -1. Activate fire alarm -2. Move to the back of ship -* After - 1. Repair damage - 2. If needed, Evacuate -Accidental Reentry: -1. Activate fire alarms in front of ship. -2. Move volatile matter to a fire proof area! -3. Get a fire suit. -4. Stay secure until an emergency ship arrives. -5. If ship does not arrive-Evacuate to a nearby safe area! -"}; + info = "Alert Levels:
\nBlue- Emergency
\n\t1. Caused by fire
\n\t2. Caused by manual interaction
\n\tAction:
\n\t\tClose all fire doors. These can only be opened by resetting the alarm
\nRed- Ejection/Self-Destruct
\n\t1. Caused by module operating computer.
\n\tAction:
\n\t\tAfter the specified time the module will eject completely.
\n
\nEngine Maintenance Instructions:
\n\tShut off ignition systems:
\n\tActivate internal power
\n\tActivate orbital balance matrix
\n\tRemove volatile liquids from area
\n\tWear a fire suit
\n
\n\tAfter
\n\t\tDecontaminate
\n\t\tVisit medical examiner
\n
\nToxin Laboratory Procedure:
\n\tWear a gas mask regardless
\n\tGet an oxygen tank.
\n\tActivate internal atmosphere
\n
\n\tAfter
\n\t\tDecontaminate
\n\t\tVisit medical examiner
\n
\nDisaster Procedure:
\n\tFire:
\n\t\tActivate sector fire alarm.
\n\t\tMove to a safe area.
\n\t\tGet a fire suit
\n\t\tAfter:
\n\t\t\tAssess Damage
\n\t\t\tRepair damages
\n\t\t\tIf needed, Evacuate
\n\tMeteor Shower:
\n\t\tActivate fire alarm
\n\t\tMove to the back of ship
\n\t\tAfter
\n\t\t\tRepair damage
\n\t\t\tIf needed, Evacuate
\n\tAccidental Reentry:
\n\t\tActivate fire alarms in front of ship.
\n\t\tMove volatile matter to a fire proof area!
\n\t\tGet a fire suit.
\n\t\tStay secure until an emergency ship arrives.
\n
\n\t\tIf ship does not arrive-
\n\t\t\tEvacuate to a nearby safe area!" /obj/item/paper/fluff/shuttles/daniel info = "i love daniel
daniel is my best friend

you are tearing me apart elise" +/obj/item/paper/fluff/jobs/prisoner/letter + name = "letter from home" + info = {"Dearest sweetheart, +
It is truly saddening you must spend your time locked up in an awful prison on that dangerous station. I have spoken to your lawyer who will attempt to appeal to the judge so your sentence may hopefully be reduced. +
Regardless, I just want you to understand that all of us out here still love you, and want to see you released safely some day! I know that prison can be a very vicious place, so please promise us you'll avoid getting into any fights or trouble, okay? +
We all care for your safety deeply, and could not live with ourselves if you ended up getting hurt. We've scheduled a visit to see you, and with any luck, hopefully our request will be granted soon. +
Anyways, please do your best to make it by in that place, and never forget we'll be always here for you, no matter if we're separated. +
+
Please stay safe, +
-Love, Your Dearest"} + //////////// Job guides n' fluff @@ -90,13 +59,46 @@ Accidental Reentry: name = "paper- 'Chemical Information'" info = "Known Onboard Toxins:
\n\tGrade A Semi-Liquid Plasma:
\n\t\tHighly poisonous. You cannot sustain concentrations above 15 units.
\n\t\tA gas mask fails to filter plasma after 50 units.
\n\t\tWill attempt to diffuse like a gas.
\n\t\tFiltered by scrubbers.
\n\t\tThere is a bottled version which is very different
\n\t\t\tfrom the version found in canisters!
\n
\n\t\tWARNING: Highly Flammable. Keep away from heat sources
\n\t\texcept in an enclosed fire area!
\n\t\tWARNING: It is a crime to use this without authorization.
\nKnown Onboard Anti-Toxin:
\n\tAnti-Toxin Type 01P: Works against Grade A Plasma.
\n\t\tBest if injected directly into bloodstream.
\n\t\tA full injection is in every regular Med-Kit.
\n\t\tSpecial toxin Kits hold around 7.
\n
\nKnown Onboard Chemicals (other):
\n\tRejuvenation T#001:
\n\t\tEven 1 unit injected directly into the bloodstream
\n\t\t\twill cure unconscious and sleep toxins.
\n\t\tIf administered to a dying patient it will prevent
\n\t\t\tfurther damage for about units*3 seconds.
\n\t\t\tit will not cure them or allow them to be cured.
\n\t\tIt can be administered to a non-dying patient
\n\t\t\tbut the chemicals disappear just as fast.
\n\tMorphine T#054:
\n\t\t5 units will induce precisely 1 minute of sleep.
\n\t\t\tThe effect are cumulative.
\n\t\tWARNING: It is a crime to use this without authorization" +/obj/item/paper/fluff/jobs/medical/hippocratic + name = "paper- 'Hippocratic Oath'" + info = {"I swear to fulfill, to the best of my ability and judgment, this covenant: +
+ I will respect the hard-won scientific gains of those physicians in whose steps I walk, + and gladly share such knowledge as is mine with those who are to follow. +
+ I will apply, for the benefit of the sick, all measures that are required, + avoiding those twin traps of overtreatment and therapeutic nihilism. +
+ I will remember that there is art to medicine as well as science, + and that warmth, sympathy, and understanding may outweigh the surgeon's knife or the chemist's drug. +
+ I will not be ashamed to say "I know not," + nor will I fail to call in my colleagues when the skills of another are needed for a patient's recovery. +
+ I will respect the privacy of my patients, for their problems are not disclosed to me that the world may know. Most especially must I tread with care in matters of life and death. + If it is given me to save a life, all thanks. But it may also be within my power to take a life; + this awesome responsibility must be faced with great humbleness and awareness of my own frailty. Above all, I must not play at God. +
+ I will remember that I do not treat a fever chart, a cancerous growth, but a sick human being, whose illness may affect the person's family and economic stability. + My responsibility includes these related problems, if I am to care adequately for the sick. +
+ I will prevent disease whenever I can, for prevention is preferable to cure. +
+ I will remember that I remain a member of society, with special obligations to all my fellow human beings, + those sound of mind and body as well as the infirm. +
+ If I do not violate this oath, may I enjoy life and art, respected while I live and remembered with affection thereafter. + May I always act so as to preserve the finest traditions of my calling and may I long experience the joy of healing those who seek my help. +
"} + /* * Stations */ +////////// cogstation. /obj/item/paper/guides/cogstation/job_changes name = "MEMO: Job Changes" - info = "To ensure minimal employee downtime, please take note of the following changes to select professions that CogStation specifically requires:
\n
\n- Scientists are to have access to chemistry in order to reach the MedSci router.
\n
\n- Chemists should at the very least be provided with an encryption key for the Science channel, if not basic access to the Research department at large.
\n- Roboticists are to have basic Medical and Morgue access.
\n- Engineers and Atmospheric Technicians are to have Warehouse and Mining access.
\n- The Cook should not have Morgue access
\n- The Clown and Mime are to have Maintenance access. This is necessary due to the location of their offices.
\n
\nGenerated by Organic Resources Bot #2053" + info = "To ensure minimal employee downtime, please take note of the following changes to select professions that CogStation specifically requires:
\n
\n- Engineers and Atmospheric Technicians are to have Warehouse and Mining access.
\n- The Cook should not have Morgue access.
\n- The Clown and Mime are to have Maintenance access. This is necessary due to the location of their offices.
\n
\nGenerated by Organic Resources Bot #2053" /obj/item/paper/guides/cogstation/letter_sec name = "To future Security personnel" @@ -104,7 +106,7 @@ Accidental Reentry: /obj/item/paper/guides/cogstation/disposals name = "Regarding the disposal system:" - info = "As you might have noticed, this station has far more disposal pipes than you may expect from your average Nanotrasen research facility. Part of the reason for this is specialization - mail, trash, even corpses have their own disposal systems. Unfortunately, the convenient color-coding was lost in translocation and we've had to compensate by marking the area around each bin.
\n
\n- WHITE/GRAY STRIPES is for DELIVERIES.
\n- RED STRIPES is for CORPSES.
\n- EVERYTHING ELSE is for TRASH,
barring a few exceptions that should be labeled as such.
\n
\nIdeally the station won't sustain any heavy structural damage during your time here but if it does, or someone decides to tamper with/sabotage this system, you'll be forgiven if you can't put it back together perfectly.
\n
\n-C. Donnelly
Architectural Analyst" + info = "As you might have noticed, this station has far more disposal pipes than you may expect from your average Nanotrasen research facility. Part of the reason for this is specialization - mail, trash, even corpses have their own disposal systems. Unfortunately, the convenient color-coding was lost in translocation and we've had to compensate by marking the area around each bin.
\n
\n- WHITE/GRAY STRIPES are for DELIVERIES.
\n- RED STRIPES are for CORPSES.
\n- EVERYTHING ELSE is for TRASH,
barring a few exceptions that should be labeled as such.
\n
\nIdeally the station won't sustain any heavy structural damage during your time here but if it does, or someone decides to tamper with/sabotage this system, you'll be forgiven if you can't put it back together perfectly.
\n
\n-C. Donnelly
Architectural Analyst" /obj/item/paper/guides/cogstation/janitor name = "a quick tip" @@ -128,7 +130,7 @@ Accidental Reentry: /obj/item/paper/guides/cogstation/letter_eng name = "To future Engineering staff:" - info = "I'm not gonna sugarcoat this. Compared to other departments, you might have your work cut out for you. CogStation is an entirely different beast than your standard Box, but everyone's still gonna expect you to keep the place running.
\n
\n If there's any good news, it's your time to shine if you know how to run a thermo-electric generator. That's what this station runs on, and that isn't likely to change anytime soon. If it's absolutely critical you might be able to run a singularity or tesla engine east of mining, but it won't have any sort of shielding out there.
\n
\nYou still have three solar arrays to work with, two of them being on each end of the starboard side. The port side array will need you to either get access from a head of staff or security, unless you want to spacewalk around the whole station. Don't be afraid to ask the latter - they're there for you, after all. As for other utilities the air system is a bit different than you'd expect, but fortunately you should have the atmos techs this station needed a long time ago. The disposal network is significantly more complicated, yet more capable. I've already elaborated on it, so I'll let you find and read my write-up for that. As for the routing system, it's just begging to get hit by a stray meteor so consider other utilities a higher priority.
\n
\nGood luck. You're gonna need it.
\n
\n-C. Donnelly
Architectural Analyst" + info = "I'm not gonna sugarcoat this. Compared to other departments, you might have your work cut out for you. CogStation is an entirely different beast than your standard Box, but everyone's still gonna expect you to keep the place running.
\n
\n If there's any good news, it's your time to shine if you know how to run a thermo-electric generator. That's what this station runs on, and that isn't likely to change anytime soon. If it's absolutely critical you might be able to run a singularity or tesla engine east of mining, but it won't have any sort of protection out there.
\n
\nYou still have three solar arrays to work with, two of them being on each end of the starboard side. The port side array will need you to either get access from a head of staff or security, unless you want to spacewalk around the whole station. Don't be afraid to ask the latter - they're there for you, after all. As for other utilities the air system is a bit different than you'd expect, but fortunately you should have the atmos techs this station needed a long time ago. The disposal network is significantly more complicated, yet more capable. I've already elaborated on it, so I'll let you find and read my write-up for that. As for the routing system, it's just begging to get hit by a stray meteor so consider other utilities a higher priority.
\n
\nGood luck. You're gonna need it.
\n
\n-C. Donnelly
Architectural Analyst" /obj/item/paper/guides/cogstation/letter_atmos name = "To future Atmospheric Technicians:" @@ -140,15 +142,15 @@ Accidental Reentry: /obj/item/paper/guides/cogstation/letter_hos name = "To the future HoS" - info = "I'm gonna be rather disappointed if CentCom doesn't brief you about this station, but if they don't I wrote up another letter for your department that should cover it pretty well. Make sure your officers read it if they aren't up to speed.
\n
\nSomething you in particular should know is that if someone's getting to be too much to handle, the boys and I have constructed a 'discount transfer centre' just behind the router. Use it only as a last resort - the walls may be reinforced but they're still thin, and you'll have big trouble on your hands if the AI or any cyborgs find out about it.
\n
-LC" + info = "I'm gonna be rather disappointed if Central Command doesn't brief you about this station, but if they don't I wrote up another letter for your department that should cover it pretty well. Make sure your officers read it if they aren't up to speed.
\n
\nSomething you in particular should know is that if someone's getting to be too much to handle, the boys and I have constructed a 'discount transfer centre' just behind the router. Use it only as a last resort - the walls may be reinforced but they're still thin, and you'll have big trouble on your hands if the AI or any cyborgs find out about it.
\n
-LC" /obj/item/paper/guides/cogstation/letter_supp name = "To future Supply Staff:" - info = "Cargo, move freight. Miners, don't die. Your jobs are pretty straightforward, which is likely why they originally fell under Engineering on this station as opposed to their own department. Although we've considerably readjusted this part of the station to accommodate you, there are potential differences you should be aware of.
\n
\nEngineeringwill have access to some of your department, namely the warehouse and mining dock. Mining operations on this station were originally asteroid-based, hence the catwalk into the great beyond. Although you won't need to worry about being space-worthy due to a newly installed shuttle dock, they might need to get out there.
\n
\nYou'll have all your usual means of shipping out goods, but the disposal network is more complex with a separate line for mail and trash. I've left another note that explains this in detail, but know trash is the janitor's responsibility, not yours.
\n
\nThe biggest difference has to be this station's router system, which allows departments to ship goods between themselves. Even if the belts aren't working properly they'll still have their own request consoles, so you'll want to check for orders regularly.
\n
\n-C. Donnelly
\nArchitectural Analyst" + info = "Cargo, move freight. Miners, don't die. Your jobs are pretty straightforward, which is likely why they originally fell under Engineering on this station as opposed to their own department. Although we've considerably readjusted this part of the station to accommodate you, there are potential differences you should be aware of.
\n
\nEngineeringwill have access to some of your department, namely the warehouse and mining dock. Mining operations on this station were originally asteroid-based, hence the catwalk into the great beyond. Although you won't need to worry about being space-worthy due to a newly installed shuttle dock, they might need to get out there.
\n
\nYou'll have all your usual means of shipping out goods, but the disposal network is more complex with a separate line for mail and trash. It also isn't fully space-proofed, meanin it may not be the best choice for livestock, monkey cubes, or clowns. I've left another note that explains this in detail, but know trash is the janitor's responsibility, not yours.
\n
\nThe biggest difference has to be this station's router system, which allows departments to ship goods between themselves. Even if the belts aren't working properly they'll still have their own request consoles, so you'll want to check for orders regularly.
\n
\n-C. Donnelly
\nArchitectural Analyst" -/obj/item/paper/fluff/cogstation/sleepers - name = "Re: Sleepers?" - info = "Yes, the sleepers are meant to be publicly accessible. Policies in this station's original location encouraged crew to visit the clinic or treat themselves when it came to minor injuries.
\n
\nThis is no excuse for you not to do your jobs. You may wish to keep an eye on the sleepers as to ensure they're being used responsibly. Remember, allowing an overdose to happen under your watch isn't much different from administering that overdose yourself.
\n
\n- Dr. Halley" +/obj/item/paper/guides/cogstation/letter_med + name = "Re: Future Medical Staff" + info = "With this station nearing approval for regular use, I've been told to consolidate anything noteworthy about its general medical department into a single document. As you may be able to guess, this is that document.
\n
\n- First, you should know a medical clinic is present in the civilian (starboard bow) wing. If you have personnel to spare, it's recommended you have someone staff it - that way people with minor injuries can report there instead of clogging up the research wing.
\n
\n- Despite recent renovations to bring this station in line with regional policy, you'll still find the robotics lab directly adjacent to your department. I advise you take full advantage of this, whether it's requesting prosthetics in advance or harvesting organs from those who have undergone more...permanent procedures.
\n
\n- Lastly, please make a habit of checking the morgue on a regular basis. Thanks to the Corpse Disposal Network (or CDN for short), you may find the station's deceased delivered directly to you. Some may be employees capable of being revived - more information can be found in the morgue itself.
\n
\n- Dr. Halley" /obj/item/paper/fluff/cogstation/cloner name = "Re: Issue with the cloner?" @@ -172,7 +174,7 @@ Accidental Reentry: /obj/item/paper/fluff/cogstation/letter_chap name = "A message from the DHDA" - info = "Regardless of what the name leads you to believe, CogStation is neither Ratvarian in origin nor designed by members of this so-called 'clock cult'. Despite a potential common enemy and instances of exhibiting peaceful behavior, their beliefs have been labeled 'Heretical' by the Department of Higher-Dimensional Affairs and following them is grounds for immediate termination.
\nAs the station's designated Chaplain, it is advised you correct anyone who claims this station and/or its designers are Ratvarian. While they are most likely misinformed or 'joking around', untruths gain credibility the more they are repeated.
\n
\nSoulstone Obelisk
\n
\nDepartment of Higher-Dimensional Affairs" + info = "Regardless of what the name leads you to believe, CogStation is neither Ratvarian in origin nor designed by members of this so-called 'clock cult'. Despite a potential common enemy and instances of exhibiting peaceful behavior, their beliefs have been labeled 'Heretical' by the Department of Higher-Dimensional Affairs and following them is grounds for immediate termination.
\nAs the station's designated Chaplain, it is advised you correct anyone who claims this station and/or its designers are Ratvarian. While they are most likely misinformed or 'joking around', untruths gain credibility the more they are repeated.
\n
\nSoulstone Obelisk
\n
\nDepartment of Higher-Dimensional Affairs" /obj/item/paper/fluff/cogstation/cluwne name = "Mysterious Note" @@ -184,7 +186,7 @@ Accidental Reentry: /obj/item/paper/fluff/cogstation/eva name = "MEMO: Spacesuits" - info = "As a Head of Personnel, you may be familiar with crew members requesting EVA access, particularly when there is an absence of credible threats on the station. While it is your decision to grant or deny access unless overriden by your superior(s), you should be aware of an abundance of spacesuits on this station. While intended for emergencies, these suits are cheaper to replace. You may find it beneficial to direct aspiring 'space explorers' towards finding one of these suits instead, although it is advised you order replacement suits in advance through the cargo department.
\n
\n-Generated by Organic Resources Bot #2053" + info = "As a Head of Personnel, you may be familiar with crew members requesting EVA access, particularly when there is an absence of credible threats on the station. While it is your decision to grant or deny access unless overriden by your superior(s), you should be aware of an abundance of spacesuits on this station. While intended for emergencies, these suits are cheaper to replace. You may find it beneficial to direct aspiring 'space explorers' towards finding one of these suits instead, although it is advised you order replacement suits in advance through the cargo department.
\n
\nGenerated by Organic Resources Bot #2053" /obj/item/paper/fluff/cogstation/chemists name = "Re: Scientists?!" @@ -214,9 +216,9 @@ Accidental Reentry: name = "ROUTER STATUS: LIMITED" info = "Currently, this router cannot receive deliveries from the Airbridge, MedSci, Security, or Service Routers. Cargo and the recycler are the only points currently accepting deliveries from here, although manual input from the routing depot is currently required.
\n
\n-C. Donnelly
\n
\nArchitectural Analyst" -/obj/item/paper/fluff/cogstation/router_cargo - name = "ROUTER STATUS: VERY LIMITED" - info = "Currently, this router cannot receive deliveries from the Airbridge, MedSci, Security, or Service Routers. It is not yet capable of making deliveries, beyond sending items to the recycler.
\n
\n-C. Donnelly
\n
\nArchitectural Analyst" +/obj/item/paper/fluff/cogstation/mulebot + name = "MEMO: MULEbots" + info = "As you may know, MULEbots have been coded to minimize travel distance for maximum efficiency. In the case of this station, that may include travelling through depressurized areas exposed to space. Please bear this in mind before using them to transport living tissue.
\n
\nGenerated by Organic Resources Bot #2053" /////////// CentCom diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm index 28cb5ffae9..b8bbd0a30e 100644 --- a/code/modules/paperwork/paperbin.dm +++ b/code/modules/paperwork/paperbin.dm @@ -4,6 +4,7 @@ icon = 'icons/obj/bureaucracy.dmi' icon_state = "paper_bin1" item_state = "sheet-metal" + // inhand_icon_state = "sheet-metal" lefthand_file = 'icons/mob/inhands/misc/sheets_lefthand.dmi' righthand_file = 'icons/mob/inhands/misc/sheets_righthand.dmi' throwforce = 0 @@ -43,6 +44,7 @@ ..() /obj/item/paper_bin/MouseDrop(atom/over_object) + . = ..() var/mob/living/M = usr if(!istype(M) || M.incapacitated() || !Adjacent(M)) return @@ -54,17 +56,18 @@ var/obj/screen/inventory/hand/H = over_object M.putItemFromInventoryInHandIfPossible(src, H.held_index) - else - . = ..() - add_fingerprint(M) /obj/item/paper_bin/attack_paw(mob/user) return attack_hand(user) -/obj/item/paper_bin/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) - if(user.lying) - return +//ATTACK HAND IGNORING PARENT RETURN VALUE +/obj/item/paper_bin/on_attack_hand(mob/user) + if(isliving(user)) + var/mob/living/L = user + if(!(L.mobility_flags & MOBILITY_PICKUP)) + return + // user.changeNext_move(CLICK_CD_MELEE) if(bin_pen) var/obj/item/pen/P = bin_pen P.add_fingerprint(user) @@ -85,8 +88,8 @@ P = new papertype(src) if(SSevents.holidays && SSevents.holidays[APRIL_FOOLS]) if(prob(30)) - P.info = "*HONK HONK HONK HONK HONK HONK HONK
HOOOOOOOOOOOOOOOOOOOOOONK*\n*APRIL FOOLS*\n" - P.rigged = 1 + P.info = "HONK HONK HONK HONK HONK HONK HONK
HOOOOOOOOOOOOOOOOOOOOOONK
APRIL FOOLS
" + P.AddComponent(/datum/component/honkspam) P.add_fingerprint(user) P.forceMove(user.loc) @@ -148,8 +151,7 @@ papertype = /obj/item/paper/natural resistance_flags = FLAMMABLE -/obj/item/paper_bin/bundlenatural/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) - ..() +/obj/item/paper_bin/bundlenatural/on_attack_hand(mob/user) if(total_paper < 1) qdel(src) @@ -173,3 +175,9 @@ qdel(src) else ..() + +/obj/item/paper_bin/carbon + name = "carbon paper bin" + desc = "Contains all the paper you'll ever need, in duplicate!" + icon_state = "paper_bin_carbon" + papertype = /obj/item/paper/carbon diff --git a/code/modules/paperwork/paperplane.dm b/code/modules/paperwork/paperplane.dm index c6a1ee1389..53fe886d65 100644 --- a/code/modules/paperwork/paperplane.dm +++ b/code/modules/paperwork/paperplane.dm @@ -1,9 +1,9 @@ - /obj/item/paperplane name = "paper plane" desc = "Paper, folded in the shape of a plane." icon = 'icons/obj/bureaucracy.dmi' icon_state = "paperplane" + // custom_fire_overlay = "paperplane_onfire" throw_range = 7 throw_speed = 1 throwforce = 0 @@ -11,7 +11,7 @@ resistance_flags = FLAMMABLE max_integrity = 50 - var/hit_probability = 2//% + var/hit_probability = 2 //% var/obj/item/paper/internalPaper /obj/item/paperplane/origami @@ -41,6 +41,13 @@ qdel(src) return ..() +/obj/item/paperplane/Exited(atom/movable/AM, atom/newLoc) + . = ..() + if (AM == internalPaper) + internalPaper = null + if(!QDELETED(src)) + qdel(src) + /obj/item/paperplane/Destroy() QDEL_NULL(internalPaper) return ..() @@ -71,31 +78,19 @@ user.put_in_hands(internal_paper_tmp) /obj/item/paperplane/attackby(obj/item/P, mob/living/carbon/human/user, params) - ..() + if(burn_paper_product_attackby_check(P, user)) + return if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) - to_chat(user, "You should unfold [src] before changing it.") + to_chat(user, "You should unfold [src] before changing it!") return else if(istype(P, /obj/item/stamp)) //we don't randomize stamps on a paperplane internalPaper.attackby(P, user) //spoofed attack to update internal paper. update_icon() + add_fingerprint(user) + return - else if(P.get_temperature()) - if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(10)) - user.visible_message("[user] accidentally ignites [user.p_them()]self!", \ - "You miss [src] and accidentally light yourself on fire!") - user.dropItemToGround(P) - user.adjust_fire_stacks(1) - user.IgniteMob() - return - - if(!(in_range(user, src))) //to prevent issues as a result of telepathically lighting a paper - return - user.dropItemToGround(src) - user.visible_message("[user] lights [src] ablaze with [P]!", "You light [src] on fire!") - fire_act() - - add_fingerprint(user) + return ..() /obj/item/paperplane/throw_at(atom/target, range, speed, mob/thrower, spin=FALSE, diagonals_first = FALSE, datum/callback/callback) @@ -112,26 +107,28 @@ if(..() || !ishuman(hit_atom))//if the plane is caught or it hits a nonhuman return var/mob/living/carbon/human/H = hit_atom + var/obj/item/organ/eyes/eyes = H.getorganslot(ORGAN_SLOT_EYES) if(prob(hit_probability)) if(H.is_eyes_covered()) return - var/obj/item/organ/eyes/eyes = H.getorganslot(ORGAN_SLOT_EYES) - visible_message("\The [src] hits [H] in the eye!") + visible_message("\The [src] hits [H] in the eye[eyes ? "" : " socket"]!") H.adjust_blurriness(6) - if(eyes) - eyes.applyOrganDamage(rand(6,8)) + eyes?.applyOrganDamage(rand(6,8)) H.DefaultCombatKnockdown(40) H.emote("scream") - /obj/item/paper/examine(mob/user) . = ..() . += "Alt-click [src] to fold it into a paper plane." /obj/item/paper/AltClick(mob/living/carbon/user, obj/item/I) - . = ..() - if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user), NO_TK)) + if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user))) return + if(istype(src, /obj/item/paper/carbon)) + var/obj/item/paper/carbon/Carbon = src + if(!Carbon.iscopy && !Carbon.copied) + to_chat(user, "Take off the carbon copy first.") + return to_chat(user, "You fold [src] into the shape of a plane!") user.temporarilyRemoveItemFromInventory(src) var/obj/item/paperplane/plane_type = /obj/item/paperplane @@ -142,4 +139,3 @@ I = new plane_type(user, src) user.put_in_hands(I) - return TRUE diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index 91b8a6719b..13890ae69e 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -16,6 +16,8 @@ icon = 'icons/obj/bureaucracy.dmi' icon_state = "pen" item_state = "pen" + // inhand_icon_state = "pen" + // worn_icon_state = "pen" slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_EARS throwforce = 0 w_class = WEIGHT_CLASS_TINY @@ -76,6 +78,22 @@ icon_state = "pen-fountain" font = FOUNTAIN_PEN_FONT +/obj/item/pen/charcoal + name = "charcoal stylus" + desc = "It's just a wooden stick with some compressed ash on the end. At least it can write." + icon_state = "pen-charcoal" + colour = "dimgray" + font = CHARCOAL_FONT + custom_materials = null + grind_results = list(/datum/reagent/ash = 5, /datum/reagent/cellulose = 10) + +/datum/crafting_recipe/charcoal_stylus + name = "Charcoal Stylus" + result = /obj/item/pen/charcoal + reqs = list(/obj/item/stack/sheet/mineral/wood = 1, /datum/reagent/ash = 30) + time = 30 + category = CAT_PRIMAL + /obj/item/pen/fountain/captain name = "captain's fountain pen" desc = "It's an expensive Oak fountain pen. The nib is quite sharp." @@ -93,6 +111,7 @@ "Black and Silver" = "pen-fountain-b", "Command Blue" = "pen-fountain-cb" ) + embedding = list("embed_chance" = 75) /obj/item/pen/fountain/captain/Initialize() . = ..() @@ -139,20 +158,18 @@ if(QDELETED(O) || !user.canUseTopic(O, BE_CLOSE)) return if(oldname == input) - to_chat(user, "You changed \the [O.name] to... well... \the [O.name].") + to_chat(user, "You changed \the [O.name] to... well... \the [O.name].") else O.name = input - to_chat(user, "\The [oldname] has been successfully been renamed to \the [input].") + to_chat(user, "\The [oldname] has been successfully been renamed to \the [input].") O.renamedByPlayer = TRUE - log_game("[user] [key_name(user)] has renamed [O] to [input]") if(penchoice == "Change description") - var/input = stripped_input(user,"Describe \the [O.name] here", ,"", 2048) + var/input = stripped_input(user,"Describe \the [O.name] here", ,"", 100) if(QDELETED(O) || !user.canUseTopic(O, BE_CLOSE)) return O.desc = input - to_chat(user, "You have successfully changed \the [O.name]'s description.") - log_game("[user] [key_name(user)] has changed [O]'s description to to [input]") + to_chat(user, "You have successfully changed \the [O.name]'s description.") /* * Sleepypens @@ -181,9 +198,10 @@ */ /obj/item/pen/edagger attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") //these wont show up if the pen is off + // attack_verb_continuous = list("slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") //these won't show up if the pen is off + // attack_verb_simple = list("slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut") sharpness = SHARP_EDGED var/on = FALSE - embedding = list(embed_chance = EMBED_CHANCE) /obj/item/pen/edagger/ComponentInitialize() . = ..() @@ -193,29 +211,38 @@ /obj/item/pen/edagger/get_sharpness() return on * sharpness +/obj/item/pen/edagger/suicide_act(mob/user) + . = BRUTELOSS + if(on) + user.visible_message("[user] forcefully rams the pen into their mouth!") + else + user.visible_message("[user] is holding a pen up to their mouth! It looks like [user.p_theyre()] trying to commit suicide!") + attack_self(user) + /obj/item/pen/edagger/attack_self(mob/living/user) if(on) on = FALSE force = initial(force) + throw_speed = initial(throw_speed) w_class = initial(w_class) name = initial(name) hitsound = initial(hitsound) - embedding = null + embedding = list(embed_chance = EMBED_CHANCE) throwforce = initial(throwforce) - playsound(user, 'sound/weapons/saberoff.ogg', 5, 1) + playsound(user, 'sound/weapons/saberoff.ogg', 5, TRUE) to_chat(user, "[src] can now be concealed.") - updateEmbedding() else on = TRUE force = 18 + throw_speed = 4 w_class = WEIGHT_CLASS_NORMAL name = "energy dagger" hitsound = 'sound/weapons/blade1.ogg' - embedding = list(embed_chance = 100, fall_chance = 0) //rule of cool + embedding = list(embed_chance = 100) //rule of cool throwforce = 35 - playsound(user, 'sound/weapons/saberon.ogg', 5, 1) + playsound(user, 'sound/weapons/saberon.ogg', 5, TRUE) to_chat(user, "[src] is now active.") - updateEmbedding() + updateEmbedding() update_icon() /obj/item/pen/edagger/update_icon_state() @@ -235,6 +262,8 @@ icon = 'icons/obj/bureaucracy.dmi' icon_state = "digging_pen" item_state = "pen" + // inhand_icon_state = "pen" + // worn_icon_state = "pen" force = 3 w_class = WEIGHT_CLASS_TINY custom_materials = list(/datum/material/iron=10, /datum/material/diamond=100, /datum/material/titanium = 10) diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index 0b1f3bb01d..38029b5d4c 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -1,12 +1,20 @@ -/* Photocopiers! - * Contains: - * Photocopier - * Toner Cartridge - */ -/* - * Photocopier - */ +/// For use with the `color_mode` var. Photos will be printed in greyscale while the var has this value. +#define PHOTO_GREYSCALE "Greyscale" +/// For use with the `color_mode` var. Photos will be printed in full color while the var has this value. +#define PHOTO_COLOR "Color" + +/// How much toner is used for making a copy of a paper. +#define PAPER_TONER_USE 0.125 +/// How much toner is used for making a copy of a photo. +#define PHOTO_TONER_USE 0.625 +/// How much toner is used for making a copy of a document. +#define DOCUMENT_TONER_USE 0.75 +/// How much toner is used for making a copy of an ass. +#define ASS_TONER_USE 0.625 +/// The maximum amount of copies you can make with one press of the copy button. +#define MAX_COPIES_AT_ONCE 10 + /obj/machinery/photocopier name = "photocopier" desc = "Used to copy important documents and anatomy studies." @@ -19,183 +27,316 @@ power_channel = EQUIP max_integrity = 300 integrity_failure = 0.33 - var/obj/item/paper/copy = null //what's in the copier! - var/obj/item/photo/photocopy = null - var/obj/item/documents/doccopy = null - var/copies = 1 //how many copies to print! - var/toner = 40 //how much toner is left! woooooo~ - var/maxcopies = 10 //how many copies can be copied at once- idea shamelessly stolen from bs12's copier! - var/greytoggle = "Greyscale" - var/mob/living/ass //i can't believe i didn't write a stupid-ass comment about this var when i first coded asscopy. + /// A reference to an `/obj/item/paper` inside the copier, if one is inserted. Otherwise null. + var/obj/item/paper/paper_copy + /// A reference to an `/obj/item/photo` inside the copier, if one is inserted. Otherwise null. + var/obj/item/photo/photo_copy + /// A reference to an `/obj/item/documents` inside the copier, if one is inserted. Otherwise null. + var/obj/item/documents/document_copy + /// A reference to a mob on top of the photocopier trying to copy their ass. Null if there is no mob. + var/mob/living/ass + /// A reference to the toner cartridge that's inserted into the copier. Null if there is no cartridge. + var/obj/item/toner/toner_cartridge + /// How many copies will be printed with one click of the "copy" button. + var/num_copies = 1 + /// Used with photos. Determines if the copied photo will be in greyscale or color. + var/color_mode = PHOTO_COLOR + /// Indicates whether the printer is currently busy copying or not. var/busy = FALSE -/obj/machinery/photocopier/ui_interact(mob/user) +/obj/machinery/photocopier/Initialize() . = ..() - var/list/dat = list("Photocopier

") - if(copy || photocopy || doccopy || (ass && (ass.loc == src.loc))) - dat += "Remove Paper
" - if(toner) - dat += "Copy
" - dat += "Printing: [copies] copies." - dat += "- " - dat += "+

" - if(photocopy) - dat += "Printing in [greytoggle]

" - else if(toner) - dat += "Please insert paper to copy.

" - if(isAI(user)) - dat += "Print photo from database

" - dat += "Current toner level: [toner]" - if(!toner) - dat +="
Please insert a new toner cartridge!" - user << browse(dat.Join(""), "window=copier") - onclose(user, "copier") + //AddComponent(/datum/component/payment, 5, SSeconomy.get_dep_account(ACCOUNT_CIV), PAYMENT_CLINICAL) + toner_cartridge = new(src) -/obj/machinery/photocopier/Topic(href, href_list) +/obj/machinery/photocopier/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Photocopier") + ui.open() + +/obj/machinery/photocopier/ui_data(mob/user) + var/list/data = list() + data["has_item"] = !copier_empty() + data["num_copies"] = num_copies + + if(photo_copy) + data["is_photo"] = TRUE + data["color_mode"] = color_mode + + if(isAI(user)) + data["isAI"] = TRUE + data["can_AI_print"] = toner_cartridge ? toner_cartridge.charges >= PHOTO_TONER_USE : FALSE + else + data["isAI"] = FALSE + + if(toner_cartridge) + data["has_toner"] = TRUE + data["current_toner"] = toner_cartridge.charges + data["max_toner"] = toner_cartridge.max_charges + data["has_enough_toner"] = has_enough_toner() + else + data["has_toner"] = FALSE + data["has_enough_toner"] = FALSE + + return data + +/obj/machinery/photocopier/ui_act(action, list/params) if(..()) return - if(href_list["copy"]) - if(copy) - for(var/i = 0, i < copies, i++) - if(toner > 0 && !busy && copy) - var/copy_as_paper = 1 - if(istype(copy, /obj/item/paper/contract/employment)) - var/obj/item/paper/contract/employment/E = copy - var/obj/item/paper/contract/employment/C = new /obj/item/paper/contract/employment (loc, E.target.current) - if(C) - copy_as_paper = 0 - if(copy_as_paper) - var/obj/item/paper/c = new /obj/item/paper (loc) - if(length(copy.info) > 0) //Only print and add content if the copied doc has words on it - if(toner > 10) //lots of toner, make it dark - c.info = "" - else //no toner? shitty copies for you! - c.info = "" - var/copied = copy.info - copied = replacetext(copied, "" - c.name = copy.name - c.update_icon() - c.stamps = copy.stamps - if(copy.stamped) - c.stamped = copy.stamped.Copy() - c.copy_overlays(copy, TRUE) - toner-- - busy = TRUE - addtimer(CALLBACK(src, .proc/reset_busy), 1.5 SECONDS) - else - break - updateUsrDialog() - else if(photocopy) - for(var/i = 0, i < copies, i++) - if(toner >= 5 && !busy && photocopy) //Was set to = 0, but if there was say 3 toner left and this ran, you would get -2 which would be weird for ink - new /obj/item/photo (loc, photocopy.picture.Copy(greytoggle == "Greyscale"? TRUE : FALSE)) - busy = TRUE - addtimer(CALLBACK(src, .proc/reset_busy), 1.5 SECONDS) - else - break - else if(doccopy) - for(var/i = 0, i < copies, i++) - if(toner > 5 && !busy && doccopy) - new /obj/item/documents/photocopy(loc, doccopy) - toner-= 6 // the sprite shows 6 papers, yes I checked - busy = TRUE - addtimer(CALLBACK(src, .proc/reset_busy), 1.5 SECONDS) - else - break - updateUsrDialog() - else if(ass) //ASS COPY. By Miauw - for(var/i = 0, i < copies, i++) - var/icon/temp_img - if(ishuman(ass) && (ass.get_item_by_slot(ITEM_SLOT_ICLOTHING) || ass.get_item_by_slot(ITEM_SLOT_OCLOTHING))) - to_chat(usr, "You feel kind of silly, copying [ass == usr ? "your" : ass][ass == usr ? "" : "\'s"] ass with [ass == usr ? "your" : "[ass.p_their()]"] clothes on." ) - break - else if(toner >= 5 && !busy && check_ass()) //You have to be sitting on the copier and either be a xeno or a human without clothes on. - if(isalienadult(ass) || istype(ass, /mob/living/simple_animal/hostile/alien)) //Xenos have their own asses, thanks to Pybro. - temp_img = icon('icons/ass/assalien.png') - else if(ishuman(ass)) //Suit checks are in check_ass - temp_img = icon(ass.gender == FEMALE ? 'icons/ass/assfemale.png' : 'icons/ass/assmale.png') - else if(isdrone(ass)) //Drones are hot - temp_img = icon('icons/ass/assdrone.png') - else - break - busy = TRUE - sleep(15) - var/obj/item/photo/p = new /obj/item/photo (loc) - var/datum/picture/toEmbed = new(name = "[ass]'s Ass", desc = "You see [ass]'s ass on the photo.", image = temp_img) - p.pixel_x = rand(-10, 10) - p.pixel_y = rand(-10, 10) - toEmbed.psize_x = 128 - toEmbed.psize_y = 128 - p.set_picture(toEmbed, TRUE, TRUE) - toner -= 5 - busy = FALSE - else - break - updateUsrDialog() - else if(href_list["remove"]) - if(copy) - remove_photocopy(copy, usr) - copy = null - else if(photocopy) - remove_photocopy(photocopy, usr) - photocopy = null - else if(doccopy) - remove_photocopy(doccopy, usr) - doccopy = null - else if(check_ass()) - to_chat(ass, "You feel a slight pressure on your ass.") - updateUsrDialog() - else if(href_list["min"]) - if(copies > 1) - copies-- - updateUsrDialog() - else if(href_list["add"]) - if(copies < maxcopies) - copies++ - updateUsrDialog() - else if(href_list["aipic"]) - if(!isAI(usr)) - return - if(toner >= 5 && !busy) + + switch(action) + // Copying paper, photos, documents and asses. + if("make_copy") + if(busy) + to_chat(usr, "[src] is currently busy copying something. Please wait until it is finished.") + return FALSE + if(paper_copy) + if(!length(paper_copy.info)) + to_chat(usr, "An error message flashes across [src]'s screen: \"The supplied paper is blank. Aborting.\"") + return FALSE + // Basic paper + if(istype(paper_copy, /obj/item/paper)) + do_copy_loop(CALLBACK(src, .proc/make_paper_copy), usr) + return TRUE + // Devil contract paper. + if(istype(paper_copy, /obj/item/paper/contract/employment)) + do_copy_loop(CALLBACK(src, .proc/make_devil_paper_copy), usr) + return TRUE + // Copying photo. + if(photo_copy) + do_copy_loop(CALLBACK(src, .proc/make_photo_copy), usr) + return TRUE + // Copying Documents. + if(document_copy) + do_copy_loop(CALLBACK(src, .proc/make_document_copy), usr) + return TRUE + // ASS COPY. By Miauw + if(ass) + do_copy_loop(CALLBACK(src, .proc/make_ass_copy), usr) + return TRUE + + // Remove the paper/photo/document from the photocopier. + if("remove") + if(paper_copy) + remove_photocopy(paper_copy, usr) + paper_copy = null + else if(photo_copy) + remove_photocopy(photo_copy, usr) + photo_copy = null + else if(document_copy) + remove_photocopy(document_copy, usr) + document_copy = null + else if(check_ass()) + to_chat(ass, "You feel a slight pressure on your ass.") + return TRUE + + // AI printing photos from their saved images. + if("ai_photo") + if(busy) + to_chat(usr, "[src] is currently busy copying something. Please wait until it is finished.") + return FALSE var/mob/living/silicon/ai/tempAI = usr - if(tempAI.aicamera.stored.len == 0) - to_chat(usr, "No images saved") + if(!length(tempAI.aicamera.stored)) + to_chat(usr, "No images saved.") return var/datum/picture/selection = tempAI.aicamera.selectpicture(usr) - var/obj/item/photo/photo = new(loc, selection) - photo.pixel_x = rand(-10, 10) - photo.pixel_y = rand(-10, 10) - toner -= 5 //AI prints color pictures only, thus they can do it more efficiently - busy = TRUE - addtimer(CALLBACK(src, .proc/reset_busy), 1.5 SECONDS) - updateUsrDialog() - else if(href_list["colortoggle"]) - if(greytoggle == "Greyscale") - greytoggle = "Color" - else - greytoggle = "Greyscale" - updateUsrDialog() + var/obj/item/photo/photo = new(loc, selection) // AI prints color photos only. + give_pixel_offset(photo) + toner_cartridge.charges -= PHOTO_TONER_USE + return TRUE + // Switch between greyscale and color photos + if("color_mode") + if(params["mode"] in list(PHOTO_GREYSCALE, PHOTO_COLOR)) + color_mode = params["mode"] + return TRUE + + // Remove the toner cartridge from the copier. + if("remove_toner") + if(issilicon(usr) || (ishuman(usr) && !usr.put_in_hands(toner_cartridge))) + toner_cartridge.forceMove(drop_location()) + toner_cartridge = null + return TRUE + + // Set the number of copies to be printed with 1 click of the "copy" button. + if("set_copies") + num_copies = clamp(text2num(params["num_copies"]), 1, MAX_COPIES_AT_ONCE) + return TRUE + +/** + * Determines if the photocopier has enough toner to create `num_copies` amount of copies of the currently inserted item. + */ +/obj/machinery/photocopier/proc/has_enough_toner() + if(paper_copy) + return toner_cartridge.charges >= (PAPER_TONER_USE * num_copies) + else if(document_copy) + return toner_cartridge.charges >= (DOCUMENT_TONER_USE * num_copies) + else if(photo_copy) + return toner_cartridge.charges >= (PHOTO_TONER_USE * num_copies) + else if(ass) + return toner_cartridge.charges >= (ASS_TONER_USE * num_copies) + return FALSE + +/** + * Will invoke the passed in `copy_cb` callback in 1 second intervals, and charge the user 5 credits for each copy made. + * + * Arguments: + * * copy_cb - a callback for which proc to call. Should only be one of the `make_x_copy()` procs, such as `make_paper_copy()`. + * * user - the mob who clicked copy. + */ +/obj/machinery/photocopier/proc/do_copy_loop(datum/callback/copy_cb, mob/user) + busy = TRUE + var/i + for(i in 1 to num_copies) + //if(attempt_charge(src, user) & COMPONENT_OBJ_CANCEL_CHARGE) + // break + addtimer(copy_cb, i SECONDS) + addtimer(CALLBACK(src, .proc/reset_busy), i SECONDS) + +/** + * Sets busy to `FALSE`. Created as a proc so it can be used in callbacks. + */ /obj/machinery/photocopier/proc/reset_busy() busy = FALSE - updateUsrDialog() -/obj/machinery/photocopier/proc/do_insertion(obj/item/O, mob/user) - O.forceMove(src) - to_chat(user, "You insert [O] into [src].") +/** + * Gives items a random x and y pixel offset, between -10 and 10 for each. + * + * This is done that when someone prints multiple papers, we dont have them all appear to be stacked in the same exact location. + * + * Arguments: + * * copied_item - The paper, document, or photo that was just spawned on top of the printer. + */ +/obj/machinery/photocopier/proc/give_pixel_offset(obj/item/copied_item) + copied_item.pixel_x = rand(-10, 10) + copied_item.pixel_y = rand(-10, 10) + +/** + * Handles the copying of devil contract paper. Transfers all the text, stamps and so on from the old paper, to the copy. + * + * Checks first if `paper_copy` exists. Since this proc is called from a timer, it's possible that it was removed. + * Does not check if it has enough toner because devil contracts cost no toner to print. + */ +/obj/machinery/photocopier/proc/make_devil_paper_copy() + if(!paper_copy) + return + var/obj/item/paper/contract/employment/E = paper_copy + var/obj/item/paper/contract/employment/C = new(loc, E.target.current) + give_pixel_offset(C) + +/** + * Handles the copying of paper. Transfers all the text, stamps and so on from the old paper, to the copy. + * + * Checks first if `paper_copy` exists. Since this proc is called from a timer, it's possible that it was removed. + */ +/obj/machinery/photocopier/proc/make_paper_copy() + if(!paper_copy) + return + var/obj/item/paper/copied_paper = new(loc) + give_pixel_offset(copied_paper) + if(toner_cartridge.charges > 10) // Lots of toner, make it dark. + copied_paper.info = "" + else // No toner? shitty copies for you! + copied_paper.info = "" + + var/copied_info = paper_copy.info + copied_info = replacetext(copied_info, "" + copied_paper.name = paper_copy.name + copied_paper.update_icon() + copied_paper.stamps = paper_copy.stamps + if(paper_copy.stamped) + copied_paper.stamped = paper_copy.stamped.Copy() + copied_paper.copy_overlays(paper_copy, TRUE) + toner_cartridge.charges -= PAPER_TONER_USE + +/** + * Handles the copying of photos, which can be printed in either color or greyscale. + * + * Checks first if `photo_copy` exists. Since this proc is called from a timer, it's possible that it was removed. + */ +/obj/machinery/photocopier/proc/make_photo_copy() + if(!photo_copy) + return + var/obj/item/photo/copied_pic = new(loc, photo_copy.picture.Copy(color_mode == PHOTO_GREYSCALE ? TRUE : FALSE)) + give_pixel_offset(copied_pic) + toner_cartridge.charges -= PHOTO_TONER_USE + +/** + * Handles the copying of documents. + * + * Checks first if `document_copy` exists. Since this proc is called from a timer, it's possible that it was removed. + */ +/obj/machinery/photocopier/proc/make_document_copy() + if(!document_copy) + return + var/obj/item/documents/photocopy/copied_doc = new(loc, document_copy) + give_pixel_offset(copied_doc) + toner_cartridge.charges -= DOCUMENT_TONER_USE + +/** + * Handles the copying of an ass photo. + * + * Calls `check_ass()` first to make sure that `ass` exists, among other conditions. Since this proc is called from a timer, it's possible that it was removed. + * Additionally checks that the mob has their clothes off. + */ +/obj/machinery/photocopier/proc/make_ass_copy() + if(!check_ass()) + return + if(ishuman(ass)) //(ass.get_item_by_slot(ITEM_SLOT_ICLOTHING) || ass.get_item_by_slot(ITEM_SLOT_OCLOTHING))) + var/mob/living/carbon/C = ass //have to typecast to this, is_groin_exposed is carbon level + if(C.is_groin_exposed()) + to_chat(usr, "You feel kind of silly, copying [ass == usr ? "your" : ass][ass == usr ? "" : "\'s"] ass with [ass == usr ? "your" : "[ass.p_their()]"] clothes on." ) + return + + var/icon/temp_img + if(isalienadult(ass) || istype(ass, /mob/living/simple_animal/hostile/alien)) //Xenos have their own asses, thanks to Pybro. + temp_img = icon('icons/ass/assalien.png') + else if(ishuman(ass)) //Suit checks are after check_ass + temp_img = icon(ass.gender == FEMALE ? 'icons/ass/assfemale.png' : 'icons/ass/assmale.png') + else if(isdrone(ass)) //Drones are hot + temp_img = icon('icons/ass/assdrone.png') + + var/obj/item/photo/copied_ass = new /obj/item/photo(loc) + var/datum/picture/toEmbed = new(name = "[ass]'s Ass", desc = "You see [ass]'s ass on the photo.", image = temp_img) + give_pixel_offset(copied_ass) + toEmbed.psize_x = 128 + toEmbed.psize_y = 128 + copied_ass.set_picture(toEmbed, TRUE, TRUE) + toner_cartridge.charges -= ASS_TONER_USE + +/** + * Inserts the item into the copier. Called in `attackby()` after a human mob clicked on the copier with a paper, photo, or document. + * + * Arugments: + * * object - the object that got inserted. + * * user - the mob that inserted the object. + */ +/obj/machinery/photocopier/proc/do_insertion(obj/item/object, mob/user) + object.forceMove(src) + to_chat(user, "You insert [object] into [src].") flick("photocopier1", src) - updateUsrDialog() -/obj/machinery/photocopier/proc/remove_photocopy(obj/item/O, mob/user) +/** + * Called when someone hits the "remove item" button on the copier UI. + * + * If the user is a silicon, it drops the object at the location of the copier. If the user is not a silicon, it tries to put the object in their hands first. + * Sets `busy` to `FALSE` because if the inserted item is removed, the copier should halt copying. + * + * Arguments: + * * object - the item we're trying to remove. + * * user - the user removing the item. + */ +/obj/machinery/photocopier/proc/remove_photocopy(obj/item/object, mob/user) if(!issilicon(user)) //surprised this check didn't exist before, putting stuff in AI's hand is bad - O.forceMove(user.loc) - user.put_in_hands(O) + object.forceMove(user.loc) + user.put_in_hands(object) else - O.forceMove(drop_location()) - to_chat(user, "You take [O] out of [src].") + object.forceMove(drop_location()) + to_chat(user, "You take [object] out of [src]. [busy ? "The [src] comes to a halt." : ""]") /obj/machinery/photocopier/attackby(obj/item/O, mob/user, params) if(default_unfasten_wrench(user, O)) @@ -210,7 +351,7 @@ else if(!user.temporarilyRemoveItemFromInventory(O)) return - copy = O + paper_copy = O do_insertion(O, user) else to_chat(user, "There is already something in [src]!") @@ -219,7 +360,7 @@ if(copier_empty()) if(!user.temporarilyRemoveItemFromInventory(O)) return - photocopy = O + photo_copy = O do_insertion(O, user) else to_chat(user, "There is already something in [src]!") @@ -228,38 +369,35 @@ if(copier_empty()) if(!user.temporarilyRemoveItemFromInventory(O)) return - doccopy = O + document_copy = O do_insertion(O, user) else to_chat(user, "There is already something in [src]!") else if(istype(O, /obj/item/toner)) - if(toner <= 0) - if(!user.temporarilyRemoveItemFromInventory(O)) - return - qdel(O) - toner = 40 - to_chat(user, "You insert [O] into [src].") - updateUsrDialog() - else - to_chat(user, "This cartridge is not yet ready for replacement! Use up the rest of the toner.") + if(toner_cartridge) + to_chat(user, "[src] already has a toner cartridge inserted. Remove that one first.") + return + O.forceMove(src) + toner_cartridge = O + to_chat(user, "You insert [O] into [src].") else if(istype(O, /obj/item/areaeditor/blueprints)) - to_chat(user, "The Blueprint is too large to put into the copier. You need to find something else to record the document") + to_chat(user, "The Blueprint is too large to put into the copier. You need to find something else to record the document.") else return ..() /obj/machinery/photocopier/obj_break(damage_flag) . = ..() - if(. && toner > 0) + if(. && toner_cartridge.charges) new /obj/effect/decal/cleanable/oil(get_turf(src)) - toner = 0 + toner_cartridge.charges = 0 /obj/machinery/photocopier/MouseDrop_T(mob/target, mob/user) check_ass() //Just to make sure that you can re-drag somebody onto it after they moved off. - if (!istype(target) || target.anchored || target.buckled || !Adjacent(target) || !user.canUseTopic(src, BE_CLOSE) || target == ass || copier_blocked()) + if(!istype(target) || target.anchored || target.buckled || !Adjacent(target) || !user.canUseTopic(src, BE_CLOSE) || target == ass || copier_blocked()) return - src.add_fingerprint(user) + add_fingerprint(user) if(target == user) user.visible_message("[user] starts climbing onto the photocopier!", "You start climbing onto the photocopier...") else @@ -277,49 +415,63 @@ target.forceMove(drop_location()) ass = target - if(photocopy) - photocopy.forceMove(drop_location()) - visible_message("[photocopy] is shoved out of the way by [ass]!") - photocopy = null + if(photo_copy) + photo_copy.forceMove(drop_location()) + visible_message("[photo_copy] is shoved out of the way by [ass]!") + photo_copy = null - else if(copy) - copy.forceMove(drop_location()) - visible_message("[copy] is shoved out of the way by [ass]!") - copy = null - updateUsrDialog() + else if(paper_copy) + paper_copy.forceMove(drop_location()) + visible_message("[paper_copy] is shoved out of the way by [ass]!") + paper_copy = null + else if(document_copy) + document_copy.forceMove(drop_location()) + visible_message("[document_copy] is shoved out of the way by [ass]!") + document_copy = null + +/obj/machinery/photocopier/Exited(atom/movable/AM, atom/newloc) + check_ass() // There was potentially a person sitting on the copier, check if they're still there. + return ..() + +/** + * Checks the living mob `ass` exists and its location is the same as the photocopier. + * + * Returns FALSE if `ass` doesn't exist or is not at the copier's location. Returns TRUE otherwise. + */ /obj/machinery/photocopier/proc/check_ass() //I'm not sure wether I made this proc because it's good form or because of the name. if(!ass) - return 0 - if(ass.loc != src.loc) + return FALSE + if(ass.loc != loc) ass = null - updateUsrDialog() - return 0 - else if(ishuman(ass)) - if(!ass.get_item_by_slot(ITEM_SLOT_ICLOTHING) && !ass.get_item_by_slot(ITEM_SLOT_OCLOTHING)) - return 1 - else - return 0 - else - return 1 + return FALSE + return TRUE +/** + * Checks if the copier is deleted, or has something dense at its location. Called in `MouseDrop_T()` + */ /obj/machinery/photocopier/proc/copier_blocked() if(QDELETED(src)) return if(loc.density) - return 1 + return TRUE for(var/atom/movable/AM in loc) if(AM == src) continue if(AM.density) - return 1 - return 0 + return TRUE + return FALSE +/** + * Checks if there is an item inserted into the copier or a mob sitting on top of it. + * + * Return `FALSE` is the copier has something inside of it. Returns `TRUE` if it doesn't. + */ /obj/machinery/photocopier/proc/copier_empty() - if(copy || photocopy || check_ass()) - return 0 + if(paper_copy || photo_copy || document_copy || check_ass()) + return FALSE else - return 1 + return TRUE /* * Toner cartridge @@ -343,3 +495,11 @@ desc = "Why would ANYONE need THIS MUCH TONER?" charges = 200 max_charges = 200 + +#undef PHOTO_GREYSCALE +#undef PHOTO_COLOR +#undef PAPER_TONER_USE +#undef PHOTO_TONER_USE +#undef DOCUMENT_TONER_USE +#undef ASS_TONER_USE +#undef MAX_COPIES_AT_ONCE diff --git a/code/modules/paperwork/stamps.dm b/code/modules/paperwork/stamps.dm index 241dde13a8..104f70bfdf 100644 --- a/code/modules/paperwork/stamps.dm +++ b/code/modules/paperwork/stamps.dm @@ -4,6 +4,7 @@ icon = 'icons/obj/bureaucracy.dmi' icon_state = "stamp-ok" item_state = "stamp" + // inhand_icon_state = "stamp" throwforce = 0 w_class = WEIGHT_CLASS_TINY throw_speed = 3 @@ -11,6 +12,8 @@ custom_materials = list(/datum/material/iron=60) pressure_resistance = 2 attack_verb = list("stamped") + // attack_verb_continuous = list("stamps") + // attack_verb_simple = list("stamp") /obj/item/stamp/suicide_act(mob/user) user.visible_message("[user] stamps 'VOID' on [user.p_their()] forehead, then promptly falls over, dead.") @@ -66,5 +69,25 @@ icon_state = "stamp-clown" dye_color = DYE_CLOWN +/obj/item/stamp/mime + name = "mime's rubber stamp" + icon_state = "stamp-mime" + dye_color = DYE_MIME + +/obj/item/stamp/chap + name = "chaplain's rubber stamp" + icon_state = "stamp-chap" + dye_color = DYE_CHAP + +/obj/item/stamp/centcom + name = "CentCom rubber stamp" + icon_state = "stamp-centcom" + dye_color = DYE_CENTCOM + +/obj/item/stamp/syndicate + name = "Syndicate rubber stamp" + icon_state = "stamp-syndicate" + dye_color = DYE_SYNDICATE + /obj/item/stamp/attack_paw(mob/user) return attack_hand(user) diff --git a/code/modules/paperwork/ticketmachine.dm b/code/modules/paperwork/ticketmachine.dm new file mode 100644 index 0000000000..e46ed64971 --- /dev/null +++ b/code/modules/paperwork/ticketmachine.dm @@ -0,0 +1,231 @@ +//Bureaucracy machine! +//Simply set this up in the hopline and you can serve people based on ticket numbers + +/obj/machinery/ticket_machine + name = "ticket machine" + icon = 'icons/obj/bureaucracy.dmi' + icon_state = "ticketmachine" + desc = "A marvel of bureaucratic engineering encased in an efficient plastic shell. It can be refilled with a hand labeler refill roll and linked to buttons with a multitool." + density = FALSE + maptext_height = 26 + maptext_width = 32 + maptext_x = 7 + maptext_y = 10 + layer = HIGH_OBJ_LAYER + var/ticket_number = 0 //Increment the ticket number whenever the HOP presses his button + var/current_number = 0 //What ticket number are we currently serving? + var/max_number = 100 //At this point, you need to refill it. + var/cooldown = 50 + var/ready = TRUE + var/id = "ticket_machine_default" //For buttons + var/list/ticket_holders = list() + var/list/obj/item/ticket_machine_ticket/tickets = list() + +/obj/machinery/ticket_machine/multitool_act(mob/living/user, obj/item/I) + if(!multitool_check_buffer(user, I)) //make sure it has a data buffer + return + var/obj/item/multitool/M = I + M.buffer = src + to_chat(user, "You store linkage information in [I]'s buffer.") + return TRUE + +/obj/machinery/ticket_machine/emag_act(mob/user) //Emag the ticket machine to dispense burning tickets, as well as randomize its number to destroy the HoP's mind. + if(obj_flags & EMAGGED) + return + to_chat(user, "You overload [src]'s bureaucratic logic circuitry to its MAXIMUM setting.") + ticket_number = rand(0,max_number) + current_number = ticket_number + obj_flags |= EMAGGED + if(tickets.len) + for(var/obj/item/ticket_machine_ticket/ticket in tickets) + ticket.audible_message("\the [ticket] disperses!") + qdel(ticket) + tickets.Cut() + update_icon() + +/obj/machinery/ticket_machine/Initialize() + . = ..() + update_icon() + +/obj/machinery/ticket_machine/proc/increment() + if(current_number > ticket_number) + return + if(current_number && !(obj_flags & EMAGGED) && tickets[current_number]) + tickets[current_number].audible_message("\the [tickets[current_number]] disperses!") + qdel(tickets[current_number]) + if(current_number < ticket_number) + current_number ++ //Increment the one we're serving. + playsound(src, 'sound/misc/announce_dig.ogg', 50, FALSE) + say("Now serving ticket #[current_number]!") + if(!(obj_flags & EMAGGED) && tickets[current_number]) + tickets[current_number].audible_message("\the [tickets[current_number]] vibrates!") + update_icon() //Update our icon here rather than when they take a ticket to show the current ticket number being served + +/obj/machinery/button/ticket_machine + name = "increment ticket counter" + desc = "Use this button after you've served someone to tell the next person to come forward." + device_type = /obj/item/assembly/control/ticket_machine + req_access = list() + id = "ticket_machine_default" + +/obj/machinery/button/ticket_machine/Initialize() + . = ..() + if(device) + var/obj/item/assembly/control/ticket_machine/ours = device + ours.id = id + +/obj/machinery/button/ticket_machine/multitool_act(mob/living/user, obj/item/I) + . = ..() + if(I.tool_behaviour == TOOL_MULTITOOL) + var/obj/item/multitool/M = I + if(M.buffer && !istype(M.buffer, /obj/machinery/ticket_machine)) + return + var/obj/item/assembly/control/ticket_machine/controller = device + controller.linked = M.buffer + id = null + controller.id = null + to_chat(user, "You've linked [src] to [controller.linked].") + +/obj/item/assembly/control/ticket_machine + name = "ticket machine controller" + desc = "A remote controller for the HoP's ticket machine." + var/obj/machinery/ticket_machine/linked //To whom are we linked? + +/obj/item/assembly/control/ticket_machine/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/item/assembly/control/ticket_machine/LateInitialize() + find_machine() + +/obj/item/assembly/control/ticket_machine/proc/find_machine() //Locate the one to which we're linked + for(var/obj/machinery/ticket_machine/ticketsplease in GLOB.machines) + if(ticketsplease.id == id) + linked = ticketsplease + if(linked) + return TRUE + else + return FALSE + +/obj/item/assembly/control/ticket_machine/activate() + if(cooldown) + return + if(!linked) + return + cooldown = TRUE + linked.increment() + addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 10) + +/obj/machinery/ticket_machine/update_icon() + switch(ticket_number) //Gives you an idea of how many tickets are left + if(0 to 49) + icon_state = "ticketmachine_100" + if(50 to 99) + icon_state = "ticketmachine_50" + if(100) + icon_state = "ticketmachine_0" + handle_maptext() + +/obj/machinery/ticket_machine/proc/handle_maptext() + switch(ticket_number) //This is here to handle maptext offsets so that the numbers align. + if(0 to 9) + maptext_x = 13 + if(10 to 99) + maptext_x = 10 + if(100) + maptext_x = 8 + maptext = "[current_number]" //Finally, apply the maptext + +/obj/machinery/ticket_machine/attackby(obj/item/I, mob/user, params) + ..() + if(istype(I, /obj/item/hand_labeler_refill)) + if(!(ticket_number >= max_number)) + to_chat(user, "[src] refuses [I]! There [max_number-ticket_number==1 ? "is" : "are"] still [max_number-ticket_number] ticket\s left!") + return + to_chat(user, "You start to refill [src]'s ticket holder (doing this will reset its ticket count!).") + if(do_after(user, 30, target = src)) + to_chat(user, "You insert [I] into [src] as it whirs nondescriptly.") + qdel(I) + ticket_number = 0 + current_number = 0 + if(tickets.len) + for(var/obj/item/ticket_machine_ticket/ticket in tickets) + ticket.audible_message("\the [ticket] disperses!") + qdel(ticket) + tickets.Cut() + max_number = initial(max_number) + update_icon() + return + +/obj/machinery/ticket_machine/proc/reset_cooldown() + ready = TRUE + +/obj/machinery/ticket_machine/attack_hand(mob/living/carbon/user) + . = ..() + if(!ready) + to_chat(user,"You press the button, but nothing happens...") + return + if(ticket_number >= max_number) + to_chat(user,"Ticket supply depleted, please refill this unit with a hand labeller refill cartridge!") + return + if((user in ticket_holders) && !(obj_flags & EMAGGED)) + to_chat(user, "You already have a ticket!") + return + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 100, FALSE) + ticket_number ++ + to_chat(user, "You take a ticket from [src], looks like you're ticket number #[ticket_number]...") + var/obj/item/ticket_machine_ticket/theirticket = new /obj/item/ticket_machine_ticket(get_turf(src)) + theirticket.name = "Ticket #[ticket_number]" + theirticket.maptext = "[ticket_number]" + theirticket.saved_maptext = "[ticket_number]" + theirticket.ticket_number = ticket_number + theirticket.source = src + theirticket.owner = user + user.put_in_hands(theirticket) + ticket_holders += user + tickets += theirticket + if(obj_flags & EMAGGED) //Emag the machine to destroy the HOP's life. + ready = FALSE + addtimer(CALLBACK(src, .proc/reset_cooldown), cooldown)//Small cooldown to prevent piles of flaming tickets + theirticket.fire_act() + user.dropItemToGround(theirticket) + user.adjust_fire_stacks(1) + user.IgniteMob() + return + +/obj/item/ticket_machine_ticket + name = "Ticket" + desc = "A ticket which shows your place in the Head of Personnel's line. Made from Nanotrasen patented NanoPaper®. Though solid, its form seems to shimmer slightly. Feels (and burns) just like the real thing." + icon = 'icons/obj/bureaucracy.dmi' + icon_state = "ticket" + maptext_x = 7 + maptext_y = 10 + w_class = WEIGHT_CLASS_TINY + resistance_flags = FLAMMABLE + max_integrity = 50 + var/saved_maptext = null + var/mob/living/carbon/owner + var/obj/machinery/ticket_machine/source + var/ticket_number + +/obj/item/ticket_machine_ticket/attack_hand(mob/user) + . = ..() + maptext = saved_maptext //For some reason, storage code removes all maptext off objs, this stops its number from being wiped off when taken out of storage. + +/obj/item/ticket_machine_ticket/attackby(obj/item/P, mob/living/carbon/human/user, params) //Stolen from papercode + if(burn_paper_product_attackby_check(P, user)) + return + + return ..() + +/obj/item/paper/extinguish() + ..() + update_icon() + +/obj/item/ticket_machine_ticket/Destroy() + if(owner && source) + source.ticket_holders -= owner + source.tickets[ticket_number] = null + owner = null + source = null + return ..() diff --git a/code/modules/photography/_pictures.dm b/code/modules/photography/_pictures.dm index f667e18f47..6f85cdb3a6 100644 --- a/code/modules/photography/_pictures.dm +++ b/code/modules/photography/_pictures.dm @@ -30,16 +30,16 @@ if(autogenerate_icon && !picture_icon && picture_image) regenerate_small_icon() -/datum/picture/proc/get_small_icon() +/datum/picture/proc/get_small_icon(iconstate) if(!picture_icon) - regenerate_small_icon() + regenerate_small_icon(iconstate) return picture_icon -/datum/picture/proc/regenerate_small_icon() +/datum/picture/proc/regenerate_small_icon(iconstate) if(!picture_image) return var/icon/small_img = icon(picture_image) - var/icon/ic = icon('icons/obj/items_and_weapons.dmi', "photo") + var/icon/ic = icon('icons/obj/items_and_weapons.dmi', iconstate ? iconstate :"photo") small_img.Scale(8, 8) ic.Blend(small_img,ICON_OVERLAY, 13, 13) picture_icon = ic diff --git a/code/modules/photography/photos/photo.dm b/code/modules/photography/photos/photo.dm index 5c78709e9b..c363c549de 100644 --- a/code/modules/photography/photos/photo.dm +++ b/code/modules/photography/photos/photo.dm @@ -35,23 +35,23 @@ /obj/item/photo/update_icon_state() if(!istype(picture) || !picture.picture_image) return - var/icon/I = picture.get_small_icon() + var/icon/I = picture.get_small_icon(initial(icon_state)) if(I) icon = I /obj/item/photo/suicide_act(mob/living/carbon/user) user.visible_message("[user] is taking one last look at \the [src]! It looks like [user.p_theyre()] giving in to death!")//when you wanna look at photo of waifu one last time before you die... if (user.gender == MALE) - playsound(user, 'sound/voice/human/manlaugh1.ogg', 50, 1)//EVERY TIME I DO IT MAKES ME LAUGH + playsound(user, 'sound/voice/human/manlaugh1.ogg', 50, TRUE)//EVERY TIME I DO IT MAKES ME LAUGH else if (user.gender == FEMALE) - playsound(user, 'sound/voice/human/womanlaugh.ogg', 50, 1) + playsound(user, 'sound/voice/human/womanlaugh.ogg', 50, TRUE) return OXYLOSS /obj/item/photo/attack_self(mob/user) user.examinate(src) /obj/item/photo/attackby(obj/item/P, mob/user, params) - if(try_burn(P, user)) + if(burn_paper_product_attackby_check(P, user)) return if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) if(!user.is_literate()) @@ -60,31 +60,13 @@ var/txt = stripped_input(user, "What would you like to write on the back?", "Photo Writing", "", 128) if(txt && user.canUseTopic(src, BE_CLOSE)) scribble = txt - ..() - -/obj/item/photo/proc/try_burn(obj/item/I, mob/living/user) - var/ignition_message = I.ignition_effect(src, user) - if(!ignition_message) - return - . = TRUE - if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(10) && Adjacent(user)) - user.visible_message("[user] accidentally ignites [user.p_them()]self!", \ - "You miss [src] and accidentally light yourself on fire!") - if(user.is_holding(I)) //checking if they're holding it in case TK is involved - user.dropItemToGround(I) - user.adjust_fire_stacks(1) - user.IgniteMob() - return - - if(user.is_holding(src)) //no TK shit here. - user.dropItemToGround(src) - user.visible_message(ignition_message) - add_fingerprint(user) - fire_act(I.get_temperature()) + else + return ..() /obj/item/photo/examine(mob/user) . = ..() - if(in_range(src, user)) + + if(in_range(src, user) || isobserver(user)) show(user) else . += "You need to get closer to get a good look at this photo!" diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index 54b1362518..93fd9fe3d9 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -66,7 +66,7 @@ var/energy_transfer = delta_temperature*hot_air_heat_capacity*cold_air_heat_capacity/(hot_air_heat_capacity+cold_air_heat_capacity) var/heat = energy_transfer*(1-efficiency) - lastgen += LOGISTIC_FUNCTION(1000000,0.0034,delta_temperature,2000) + lastgen += LOGISTIC_FUNCTION(1250000,0.0001,delta_temperature,50000) hot_air.set_temperature(hot_air.return_temperature() - energy_transfer/hot_air_heat_capacity) cold_air.set_temperature(cold_air.return_temperature() + heat/cold_air_heat_capacity) @@ -139,7 +139,6 @@ . = ..() var/datum/browser/popup = new(user, "teg", "Thermo-Electric Generator", 460, 300) popup.set_content(get_menu()) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.open() /obj/machinery/power/generator/Topic(href, href_list) diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm index eb80548441..be24ea0fef 100644 --- a/code/modules/power/tesla/coil.dm +++ b/code/modules/power/tesla/coil.dm @@ -160,6 +160,14 @@ buckle_lying = FALSE buckle_requires_restraints = TRUE +/obj/machinery/power/grounding_rod/Initialize() + GLOB.grounding_rods |= src + return ..() + +/obj/machinery/power/grounding_rod/Destroy() + GLOB.grounding_rods.Remove(src) + return ..() + /obj/machinery/power/grounding_rod/default_unfasten_wrench(mob/user, obj/item/I, time = 20) . = ..() if(. == SUCCESSFUL_UNFASTEN) diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index 06e5d27f6d..89c5aa3316 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -31,6 +31,7 @@ var/produced_power var/energy_to_raise = 32 var/energy_to_lower = -20 + var/obj/machinery/power/grounding_rod/rodtarget /obj/singularity/energy_ball/Initialize(mapload, starting_energy = 50, is_miniball = FALSE) miniball = is_miniball @@ -60,11 +61,12 @@ return //don't annnounce miniballs ..() - /obj/singularity/energy_ball/process() if(!orbiting) handle_energy() + determine_containment() + move_the_basket_ball(4 + orbiting_balls.len * 1.5) playsound(src.loc, 'sound/magic/lightningbolt.ogg', 100, TRUE, extrarange = 30) @@ -87,12 +89,19 @@ if(orbiting_balls.len) . += "There are [orbiting_balls.len] mini-balls orbiting it." - /obj/singularity/energy_ball/proc/move_the_basket_ball(var/move_amount) //we face the last thing we zapped, so this lets us favor that direction a bit var/move_bias = pick(GLOB.alldirs) + var/move_dir + for(var/rod in GLOB.grounding_rods) // grounding rods pull the tesla ball, picks the nearest one + if(!rodtarget || get_dist(src,rod)= energy_to_raise) @@ -121,6 +144,10 @@ else if(orbiting_balls.len) dissipate() //sing code has a much better system. + if(energy<=0) + investigate_log("fizzled.", INVESTIGATE_SINGULO) + qdel(src) + /obj/singularity/energy_ball/proc/new_mini_ball() if(!loc) return @@ -134,7 +161,6 @@ EB.orbit(src, orbitsize, pick(FALSE, TRUE), rand(10, 25), pick(3, 4, 5, 6, 36)) - /obj/singularity/energy_ball/Bump(atom/A) dust_mobs(A) @@ -166,7 +192,6 @@ if (!QDELETED(src)) qdel(src) - /obj/singularity/energy_ball/proc/dust_mobs(atom/A) if(isliving(A)) var/mob/living/L = A @@ -329,6 +354,12 @@ else power = closest_atom.zap_act(power, zap_flags, shocked_targets) + + var/obj/singularity/energy_ball/tesla = source + if(istype(tesla)) + if(istype(closest_atom,/obj/machinery/power/grounding_rod) && tesla.energy>13 && !tesla.contained) + qdel(closest_atom) // each rod deletes two miniballs, + tesla.energy = round(tesla.energy/1.5625) // if there are no miniballs the rod stays and continues to pull the ball in if(prob(20))//I know I know tesla_zap(closest_atom, next_range, power * 0.5, zap_flags, shocked_targets) tesla_zap(closest_atom, next_range, power * 0.5, zap_flags, shocked_targets) diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 17dcfa96e6..0a587e8eba 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -268,7 +268,7 @@ for(var/i = ratio, i >= 1, i--) charge_overlay.pixel_x = ammo_x_offset * (i - 1) charge_overlay.pixel_y = ammo_y_offset * (i - 1) - . += charge_overlay + . += new /mutable_appearance(charge_overlay) else . += "[icon_state]_charge[ratio]" diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm index e1433eb64e..3675efa283 100644 --- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm @@ -329,7 +329,7 @@ color = "#101000" // rgb: 16, 16, 0 nutriment_factor = 0 taste_description = "sweet red tea" - glass_icon_state = "teaglass" + glass_icon_state = "tea_red" glass_name = "glass of red tea" glass_desc = "A piping hot tea that helps with the digestion of food." @@ -340,6 +340,7 @@ M.drowsyness = max(0,M.drowsyness-1) M.jitteriness = max(0,M.jitteriness-3) M.adjust_bodytemperature(23 * TEMPERATURE_DAMAGE_COEFFICIENT, 0, BODYTEMP_NORMAL) + ..() . = 1 /datum/reagent/consumable/tea/green @@ -348,7 +349,7 @@ color = "#101000" // rgb: 16, 16, 0 nutriment_factor = 0 taste_description = "tart green tea" - glass_icon_state = "teaglass" + glass_icon_state = "tea_green" glass_name = "glass of tea" glass_desc = "A calming glass of green tea to help get you through the day." @@ -358,6 +359,7 @@ M.drowsyness = max(0,M.drowsyness-1) M.jitteriness = max(0,M.jitteriness-3) M.adjust_bodytemperature(15 * TEMPERATURE_DAMAGE_COEFFICIENT, 0, BODYTEMP_NORMAL) + ..() . = 1 /datum/reagent/consumable/tea/forest @@ -367,7 +369,7 @@ nutriment_factor = 0 quality = DRINK_NICE taste_description = "sweet tea" - glass_icon_state = "teaglass" + glass_icon_state = "tea_forest" glass_name = "glass of forest tea" glass_desc = "A lovely glass of tea and honey." @@ -379,6 +381,7 @@ M.drowsyness = max(0,M.drowsyness-1) M.jitteriness = max(0,M.jitteriness-3) M.adjust_bodytemperature(15 * TEMPERATURE_DAMAGE_COEFFICIENT, 0, BODYTEMP_NORMAL) + ..() . = 1 /datum/reagent/consumable/tea/mush @@ -388,7 +391,7 @@ nutriment_factor = 0 quality = DRINK_NICE taste_description = "fungal infections" - glass_icon_state = "teaglass" + glass_icon_state = "tea_mush" glass_name = "glass of mush tea" glass_desc = "A cold merky brown tea." @@ -398,6 +401,7 @@ M.Dizzy(10) if(prob(10)) M.disgust = 0 + ..() . = 1 /datum/reagent/consumable/lemonade diff --git a/code/modules/research/designs/computer_part_designs.dm b/code/modules/research/designs/computer_part_designs.dm index ab487b0aaa..db37b13e87 100644 --- a/code/modules/research/designs/computer_part_designs.dm +++ b/code/modules/research/designs/computer_part_designs.dm @@ -143,7 +143,7 @@ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING // APC Link -/datum/design/APClink +/datum/design/apc_link name = "Area Power Connector" id = "APClink" build_type = PROTOLATHE @@ -230,7 +230,7 @@ name = "Photonic Processor Board" id = "pcpu_normal" build_type = IMPRINTER - materials = list(/datum/material/glass= 6400, /datum/material/gold = 2000) + materials = list(/datum/material/glass = 6400, /datum/material/gold = 2000) build_path = /obj/item/computer_hardware/processor_unit/photonic category = list("Computer Parts") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING @@ -243,3 +243,12 @@ build_path = /obj/item/computer_hardware/processor_unit/photonic/small category = list("Computer Parts") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/sensorpackage + name = "Sensor Package" + id = "sensorpackage" + build_type = PROTOLATHE + materials = list(/datum/material/iron = 200, /datum/material/glass = 100, /datum/material/gold = 50, /datum/material/silver = 50) + build_path = /obj/item/computer_hardware/sensorpackage + category = list("Computer Parts") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 0a9fce2e67..7013412e6d 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -354,6 +354,16 @@ category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL +/datum/design/portable_chem_mixer + name = "Portable Chemical Mixer" + desc = "A portable device that dispenses and mixes chemicals. Reagents have to be supplied with beakers." + id = "portable_chem_mixer" + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + materials = list(/datum/material/plastic = 5000, /datum/material/iron = 10000, /datum/material/glass = 3000) + build_path = /obj/item/storage/portable_chem_mixer + category = list("Equipment") + ///////////////////////////////////////// //////////Cybernetic Implants//////////// ///////////////////////////////////////// diff --git a/code/modules/research/techweb/nodes/computer_hud_nodes.dm b/code/modules/research/techweb/nodes/computer_hud_nodes.dm index 2835edc52b..2db03861e9 100644 --- a/code/modules/research/techweb/nodes/computer_hud_nodes.dm +++ b/code/modules/research/techweb/nodes/computer_hud_nodes.dm @@ -13,10 +13,10 @@ display_name = "Computer Hardware" description = "How computer hardware are made." prereq_ids = list("comptech") - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1250) //they are really kinda shitty + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000) //they are really shitty design_ids = list("hdd_basic", "hdd_advanced", "hdd_super", "hdd_cluster", "ssd_small", "ssd_micro", "netcard_basic", "netcard_advanced", "netcard_wired", "portadrive_basic", "portadrive_advanced", "portadrive_super", "cardslot", "aislot", "miniprinter", "APClink", "bat_control", "bat_normal", "bat_advanced", - "bat_super", "bat_micro", "bat_nano", "cpu_normal", "pcpu_normal", "cpu_small", "pcpu_small") + "bat_super", "bat_micro", "bat_nano", "cpu_normal", "pcpu_normal", "cpu_small", "pcpu_small", "sensorpackage") /datum/techweb_node/comp_recordkeeping id = "comp_recordkeeping" diff --git a/code/modules/shuttle/computer.dm b/code/modules/shuttle/computer.dm index 682a7fa14b..cee83c5b23 100644 --- a/code/modules/shuttle/computer.dm +++ b/code/modules/shuttle/computer.dm @@ -33,7 +33,6 @@ var/datum/browser/popup = new(user, "computer", M ? M.name : "shuttle", 300, 200) popup.set_content("
[dat]
") - popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state)) popup.open() /obj/machinery/computer/shuttle/Topic(href, href_list) diff --git a/code/modules/shuttle/custom_shuttle.dm b/code/modules/shuttle/custom_shuttle.dm index c71b6ba9a8..8047e972db 100644 --- a/code/modules/shuttle/custom_shuttle.dm +++ b/code/modules/shuttle/custom_shuttle.dm @@ -68,7 +68,6 @@ popup = new(user, "computer", M ? M.name : "shuttle", 350, 450) popup.set_content("
[dat]
") - popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state)) popup.open() /obj/machinery/computer/custom_shuttle/Topic(href, href_list) diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm index 472734b74b..9ebcfe91d6 100644 --- a/code/modules/station_goals/bsa.dm +++ b/code/modules/station_goals/bsa.dm @@ -31,6 +31,7 @@ anchored = TRUE /obj/machinery/bsa/wrench_act(mob/living/user, obj/item/I) + ..() default_unfasten_wrench(user, I, 10) return TRUE @@ -142,17 +143,17 @@ /obj/machinery/bsa/full/proc/get_front_turf() switch(dir) if(WEST) - return locate(x - 6,y,z) + return locate(x - 7,y,z) if(EAST) - return locate(x + 4,y,z) + return locate(x + 7,y,z) return get_turf(src) /obj/machinery/bsa/full/proc/get_back_turf() switch(dir) if(WEST) - return locate(x + 4,y,z) + return locate(x + 5,y,z) if(EAST) - return locate(x - 6,y,z) + return locate(x - 5,y,z) return get_turf(src) /obj/machinery/bsa/full/proc/get_target_turf() @@ -169,11 +170,12 @@ switch(cannon_direction) if(WEST) setDir(WEST) - pixel_x = -192 top_layer.icon_state = "top_west" icon_state = "cannon_west" if(EAST) setDir(EAST) + pixel_x = -128 + bound_x = -128 top_layer.icon_state = "top_east" icon_state = "cannon_east" add_overlay(top_layer) @@ -211,6 +213,7 @@ message_admins("[ADMIN_LOOKUPFLW(user)] has launched an artillery strike targeting [ADMIN_VERBOSEJMP(bullseye)] but it was blocked by [blocker] at [ADMIN_VERBOSEJMP(target)].") log_game("[key_name(user)] has launched an artillery strike targeting [AREACOORD(bullseye)] but it was blocked by [blocker] at [AREACOORD(target)].") + /obj/machinery/bsa/full/proc/reload() ready = FALSE use_power(power_used_per_shot) @@ -293,15 +296,16 @@ /obj/machinery/computer/bsa_control/proc/get_target_name() if(istype(target, /area)) return get_area_name(target, TRUE) - else if(istype(target, /obj/item/gps)) - var/obj/item/gps/G = target + else if(istype(target, /datum/component/gps)) + var/datum/component/gps/G = target return G.gpstag /obj/machinery/computer/bsa_control/proc/get_impact_turf() if(istype(target, /area)) return pick(get_area_turfs(target)) - else if(istype(target, /obj/item/gps)) - return get_turf(target) + else if(istype(target, /datum/component/gps)) + var/datum/component/gps/G = target + return get_turf(G.parent) /obj/machinery/computer/bsa_control/proc/fire(mob/user) if(cannon.stat) diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 3d88282937..e90f86bf1c 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -598,7 +598,7 @@ var/datum/species/S = H.dna.species base_bp_icon = S?.icon_limbs || DEFAULT_BODYPART_ICON - species_id = S.limbs_id + species_id = S.mutant_bodyparts["limbs_id"] species_flags_list = H.dna.species.species_traits //body marking memes @@ -613,7 +613,6 @@ if(S.use_skintones) skin_tone = H.skin_tone - base_bp_icon = (base_bp_icon == DEFAULT_BODYPART_ICON) ? DEFAULT_BODYPART_ICON_ORGANIC : base_bp_icon else skin_tone = "" @@ -626,13 +625,9 @@ species_color = S.fixed_mut_color else species_color = H.dna.features["mcolor"] - base_bp_icon = (base_bp_icon == DEFAULT_BODYPART_ICON) ? DEFAULT_BODYPART_ICON_ORGANIC : base_bp_icon else species_color = "" - if(base_bp_icon != DEFAULT_BODYPART_ICON) - color_src = mut_colors ? MUTCOLORS : ((H.dna.skin_tone_override && S.use_skintones == USE_SKINTONES_GRAYSCALE_CUSTOM) ? CUSTOM_SKINTONE : SKINTONE) - if(S.mutant_bodyparts["legs"]) if(body_zone == BODY_ZONE_L_LEG || body_zone == BODY_ZONE_R_LEG) if(DIGITIGRADE in S.species_traits) @@ -652,11 +647,16 @@ body_markings = "plain" aux_marking = "plain" markings_color = list(colorlist) - else body_markings = null aux_marking = null + if(species_id in GLOB.greyscale_limb_types) //should they have greyscales? + base_bp_icon = DEFAULT_BODYPART_ICON_ORGANIC + + if(base_bp_icon != DEFAULT_BODYPART_ICON) + color_src = mut_colors ? MUTCOLORS : ((H.dna.skin_tone_override && S.use_skintones == USE_SKINTONES_GRAYSCALE_CUSTOM) ? CUSTOM_SKINTONE : SKINTONE) + if(!dropping_limb && H.dna.check_mutation(HULK)) mutation_color = "00aa00" else diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm index 7090ab62e2..33d143d400 100644 --- a/code/modules/surgery/organs/tongue.dm +++ b/code/modules/surgery/organs/tongue.dm @@ -11,7 +11,8 @@ var/say_mod = null var/taste_sensitivity = 15 // lower is more sensitive. maxHealth = TONGUE_MAX_HEALTH - var/modifies_speech = FALSE + var/list/initial_accents //the ones the tongue starts with, not what it currently has + var/list/accents //done in order of priority (please always apply abductor accent and stuttering last) var/static/list/languages_possible_base = typecacheof(list( /datum/language/common, /datum/language/draconic, @@ -37,9 +38,12 @@ now_failing = "Your [name] feels like it's about to fall out!." now_fixed = "The excruciating pain of your [name] has subsided." languages_possible = languages_possible_base + for(var/accent in initial_accents) + initial_accents += new accent -/obj/item/organ/tongue/proc/handle_speech(datum/source, list/speech_args) - return +/obj/item/organ/tongue/proc/handle_speech(datum/source, list/speech_args) //this wont proc unless there's initial_accents + for(var/datum/accent/speech_modifier in initial_accents) + speech_args = speech_modifier.modify_speech(speech_args, source, owner) /obj/item/organ/tongue/applyOrganDamage(d, maximum = maxHealth) . = ..() @@ -52,7 +56,7 @@ ..() if(say_mod && M.dna && M.dna.species) M.dna.species.say_mod = say_mod - if (modifies_speech) + if(initial_accents) RegisterSignal(M, COMSIG_MOB_SAY, .proc/handle_speech) M.UnregisterSignal(M, COMSIG_MOB_SAY) @@ -74,16 +78,7 @@ say_mod = "hisses" taste_sensitivity = 10 // combined nose + tongue, extra sensitive maxHealth = 40 //extra sensitivity means tongue is more susceptible to damage - modifies_speech = TRUE - -/obj/item/organ/tongue/lizard/handle_speech(datum/source, list/speech_args) - var/static/regex/lizard_hiss = new("s+", "g") - var/static/regex/lizard_hiSS = new("S+", "g") - var/message = speech_args[SPEECH_MESSAGE] - if(message[1] != "*") - message = lizard_hiss.Replace(message, "sss") - message = lizard_hiSS.Replace(message, "SSS") - speech_args[SPEECH_MESSAGE] = message + initial_accents = list(/datum/accent/lizard) /obj/item/organ/tongue/fly name = "proboscis" @@ -92,16 +87,7 @@ say_mod = "buzzes" taste_sensitivity = 25 // you eat vomit, this is a mercy maxHealth = 80 //years of eatting trash has made your tongue strong - modifies_speech = TRUE - -/obj/item/organ/tongue/fly/handle_speech(datum/source, list/speech_args) - var/static/regex/fly_buzz = new("z+", "g") - var/static/regex/fly_buZZ = new("Z+", "g") - var/message = speech_args[SPEECH_MESSAGE] - if(message[1] != "*") - message = fly_buzz.Replace(message, "zzz") - message = fly_buZZ.Replace(message, "ZZZ") - speech_args[SPEECH_MESSAGE] = message + initial_accents = list(/datum/accent/fly) /obj/item/organ/tongue/abductor name = "superlingual matrix" @@ -110,7 +96,7 @@ say_mod = "gibbers" taste_sensitivity = 101 // ayys cannot taste anything. maxHealth = 120 //Ayys probe a lot - modifies_speech = TRUE + initial_accents = list(/datum/accent/abductor) var/mothership /obj/item/organ/tongue/abductor/attack_self(mob/living/carbon/human/H) @@ -138,26 +124,6 @@ else . += "It is attuned to [mothership]." -/obj/item/organ/tongue/abductor/handle_speech(datum/source, list/speech_args) - //Hacks - var/message = speech_args[SPEECH_MESSAGE] - var/mob/living/carbon/human/user = source - var/rendered = "[user.name]: [message]" - user.log_talk(message, LOG_SAY, tag="abductor") - for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) - var/obj/item/organ/tongue/T = H.getorganslot(ORGAN_SLOT_TONGUE) - if(!T || T.type != type) - continue - if(H.dna && H.dna.species.id == "abductor" && user.dna && user.dna.species.id == "abductor") - var/datum/antagonist/abductor/A = user.mind.has_antag_datum(/datum/antagonist/abductor) - if(!A || !(H.mind in A.team.members)) - continue - to_chat(H, rendered) - for(var/mob/M in GLOB.dead_mob_list) - var/link = FOLLOW_LINK(M, user) - to_chat(M, "[link] [rendered]") - speech_args[SPEECH_MESSAGE] = "" - /obj/item/organ/tongue/zombie name = "rotting tongue" desc = "Between the decay and the fact that it's just lying there you doubt a tongue has ever seemed less sexy." @@ -165,23 +131,7 @@ say_mod = "moans" taste_sensitivity = 32 maxHealth = 65 //Stop! It's already dead...! - modifies_speech = TRUE - -/obj/item/organ/tongue/zombie/handle_speech(datum/source, list/speech_args) - var/list/message_list = splittext(speech_args[SPEECH_MESSAGE], " ") - var/maxchanges = max(round(message_list.len / 1.5), 2) - - for(var/i = rand(maxchanges / 2, maxchanges), i > 0, i--) - var/insertpos = rand(1, message_list.len - 1) - var/inserttext = message_list[insertpos] - - if(!(copytext(inserttext, -3) == "..."))//3 == length("...") - message_list[insertpos] = inserttext + "..." - - if(prob(20) && message_list.len > 3) - message_list.Insert(insertpos, "[pick("BRAINS", "Brains", "Braaaiinnnsss", "BRAAAIIINNSSS")]...") - - speech_args[SPEECH_MESSAGE] = jointext(message_list, " ") + initial_accents = list(/datum/accent/zombie) /obj/item/organ/tongue/alien name = "alien tongue" @@ -190,7 +140,7 @@ say_mod = "hisses" taste_sensitivity = 10 // LIZARDS ARE ALIENS CONFIRMED maxHealth = 500 //They've a little mouth for a tongue, so it's pretty rhobust - modifies_speech = TRUE // not really, they just hiss + initial_accents = list(/datum/accent/alien) var/static/list/languages_possible_alien = typecacheof(list( /datum/language/xenocommon, /datum/language/common, @@ -202,9 +152,6 @@ . = ..() languages_possible = languages_possible_alien -/obj/item/organ/tongue/alien/handle_speech(datum/source, list/speech_args) - playsound(owner, "hiss", 25, 1, 1) - /obj/item/organ/tongue/bone name = "bone \"tongue\"" desc = "Apparently skeletons alter the sounds they produce through oscillation of their teeth, hence their characteristic rattling." @@ -214,14 +161,13 @@ attack_verb = list("bitten", "chattered", "chomped", "enamelled", "boned") taste_sensitivity = 101 // skeletons cannot taste anything maxHealth = 75 //Take brute damage instead - modifies_speech = TRUE var/chattering = FALSE var/phomeme_type = "sans" - var/list/phomeme_types = list("sans", "papyrus") + var/list/phomeme_types = list(/datum/accent/bone/sans, /datum/accent/bone/papyrus) /obj/item/organ/tongue/bone/Initialize() . = ..() - phomeme_type = pick(phomeme_types) + initial_accents += pick(phomeme_types) /obj/item/organ/tongue/bone/applyOrganDamage(var/d, var/maximum = maxHealth) if(d < 0) @@ -234,19 +180,14 @@ return /obj/item/organ/tongue/bone/handle_speech(datum/source, list/speech_args) - if (chattering) + if(chattering) chatter(speech_args[SPEECH_MESSAGE], phomeme_type, source) - switch(phomeme_type) - if("sans") - speech_args[SPEECH_SPANS] |= SPAN_SANS - if("papyrus") - speech_args[SPEECH_SPANS] |= SPAN_PAPYRUS + ..() /obj/item/organ/tongue/bone/plasmaman name = "plasma bone \"tongue\"" desc = "Like animated skeletons, Plasmamen vibrate their teeth in order to produce speech." icon_state = "tongueplasma" - modifies_speech = FALSE /obj/item/organ/tongue/robot name = "robotic voicebox" @@ -256,7 +197,7 @@ icon_state = "tonguerobot" say_mod = "states" attack_verb = list("beeped", "booped") - modifies_speech = TRUE + initial_accents = list(/datum/accent/robot) taste_sensitivity = 25 // not as good as an organic tongue maxHealth = 100 //RoboTongue! var/electronics_magic = TRUE @@ -265,7 +206,7 @@ return ..() || electronics_magic /obj/item/organ/tongue/robot/handle_speech(datum/source, list/speech_args) - speech_args[SPEECH_SPANS] |= SPAN_ROBOT + ..() /obj/item/organ/tongue/fluffy name = "fluffy tongue" @@ -273,20 +214,7 @@ icon_state = "tonguefluffy" taste_sensitivity = 10 // extra sensitive and inquisitive uwu maxHealth = 35 //Sensitive tongue! - modifies_speech = TRUE - -/obj/item/organ/tongue/fluffy/handle_speech(datum/source, list/speech_args) - var/message = speech_args[SPEECH_MESSAGE] - if(message[1] != "*") - message = replacetext(message, "ne", "nye") - message = replacetext(message, "nu", "nyu") - message = replacetext(message, "na", "nya") - message = replacetext(message, "no", "nyo") - message = replacetext(message, "ove", "uv") - message = replacetext(message, "l", "w") - message = replacetext(message, "r", "w") - message = lowertext(message) - speech_args[SPEECH_MESSAGE] = message + initial_accents = list(/datum/accent/fluffy) /obj/item/organ/tongue/cybernetic name = "cybernetic tongue" @@ -305,6 +233,7 @@ /obj/item/organ/tongue/cybernetic/handle_speech(datum/source, list/speech_args) speech_args[SPEECH_SPANS] |= SPAN_ROBOT + ..() /obj/item/organ/tongue/robot/ipc name = "positronic voicebox" diff --git a/code/modules/tgui/external.dm b/code/modules/tgui/external.dm index 46b324e151..c4515b8a76 100644 --- a/code/modules/tgui/external.dm +++ b/code/modules/tgui/external.dm @@ -130,6 +130,13 @@ */ /client/var/list/tgui_windows = list() +/** + * global + * + * TRUE if cache was reloaded by tgui dev server at least once. + */ +/client/var/tgui_cache_reloaded = FALSE + /** * public * @@ -159,16 +166,29 @@ /** * Middleware for /client/Topic. * - * return bool Whether the topic is passed (TRUE), or cancelled (FALSE). + * return bool If TRUE, prevents propagation of the topic call. */ /proc/tgui_Topic(href_list) // Skip non-tgui topics if(!href_list["tgui"]) - return TRUE + return FALSE var/type = href_list["type"] // Unconditionally collect tgui logs if(type == "log") log_tgui(usr, href_list["message"]) + // Reload all tgui windows + if(type == "cacheReloaded") + if(!check_rights(R_ADMIN) || usr.client.tgui_cache_reloaded) + return TRUE + // Mark as reloaded + usr.client.tgui_cache_reloaded = TRUE + // Notify windows + var/list/windows = usr.client.tgui_windows + for(var/window_id in windows) + var/datum/tgui_window/window = windows[window_id] + if (window.status == TGUI_WINDOW_READY) + window.on_message(type, null, href_list) + return TRUE // Locate window var/window_id = href_list["window_id"] var/datum/tgui_window/window @@ -177,7 +197,7 @@ if(!window) log_tgui(usr, "Error: Couldn't find the window datum, force closing.") SStgui.force_close_window(usr, window_id) - return FALSE + return TRUE // Decode payload var/payload if(href_list["payload"]) @@ -185,4 +205,4 @@ // Pass message to window if(window) window.on_message(type, payload, href_list) - return FALSE + return TRUE diff --git a/code/modules/tgui/states/debug.dm b/code/modules/tgui/states/debug.dm new file mode 100644 index 0000000000..6c600b38ce --- /dev/null +++ b/code/modules/tgui/states/debug.dm @@ -0,0 +1,6 @@ +GLOBAL_DATUM_INIT(debug_state, /datum/ui_state/debug_state, new) + +/datum/ui_state/debug_state/can_use_topic(src_object, mob/user) + if(check_rights_for(user.client, R_DEBUG)) + return UI_INTERACTIVE + return UI_CLOSE diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm index d0d5ff8ebb..b3b07eb178 100644 --- a/code/modules/tgui/tgui.dm +++ b/code/modules/tgui/tgui.dm @@ -80,14 +80,20 @@ opened_at = world.time window.acquire_lock(src) if(!window.is_ready()) - window.initialize(inline_assets = list( - get_asset_datum(/datum/asset/simple/tgui), - )) + window.initialize( + fancy = user.client.prefs.tgui_fancy, + inline_assets = list( + get_asset_datum(/datum/asset/simple/tgui_common), + get_asset_datum(/datum/asset/simple/tgui), + )) else window.send_message("ping") - window.send_asset(get_asset_datum(/datum/asset/simple/fontawesome)) + var/flush_queue = window.send_asset(get_asset_datum( + /datum/asset/simple/namespaced/fontawesome)) for(var/datum/asset/asset in src_object.ui_assets(user)) - window.send_asset(asset) + flush_queue |= window.send_asset(asset) + if (flush_queue) + user.client.browse_queue_flush() window.send_message("update", get_payload( with_data = TRUE, with_static_data = TRUE)) @@ -143,11 +149,13 @@ * Makes an asset available to use in tgui. * * required asset datum/asset + * + * return bool - true if an asset was actually sent */ /datum/tgui/proc/send_asset(datum/asset/asset) if(!window) CRASH("send_asset() can only be called after open().") - window.send_asset(asset) + return window.send_asset(asset) /** * public @@ -199,13 +207,17 @@ "key" = window_key, "size" = window_size, "fancy" = user.client.prefs.tgui_fancy, - "locked" = user.client.prefs.tgui_lock + "locked" = user.client.prefs.tgui_lock, + ), + "client" = list( + "ckey" = user.client.ckey, + "address" = user.client.address, + "computer_id" = user.client.computer_id, ), "user" = list( "name" = "[user]", - "ckey" = "[user.ckey]", - "observer" = isobserver(user) - ) + "observer" = isobserver(user), + ), ) var/data = custom_data || with_data && src_object.ui_data(user) if(data) diff --git a/code/modules/tgui/tgui_window.dm b/code/modules/tgui/tgui_window.dm index 3f271163c9..b511fe4057 100644 --- a/code/modules/tgui/tgui_window.dm +++ b/code/modules/tgui/tgui_window.dm @@ -8,12 +8,18 @@ var/client/client var/pooled var/pool_index + var/is_browser = FALSE var/status = TGUI_WINDOW_CLOSED var/locked = FALSE var/datum/tgui/locked_by + var/datum/subscriber_object + var/subscriber_delegate var/fatally_errored = FALSE var/message_queue var/sent_assets = list() + // Vars passed to initialize proc (and saved for later) + var/inline_assets + var/fancy /** * public @@ -26,9 +32,9 @@ /datum/tgui_window/New(client/client, id, pooled = FALSE) src.id = id src.client = client + src.client.tgui_windows[id] = src src.pooled = pooled if(pooled) - client.tgui_windows[id] = src src.pool_index = TGUI_WINDOW_INDEX(id) /** @@ -39,18 +45,24 @@ * will be put into the queue until the window finishes loading. * * optional inline_assets list List of assets to inline into the html. + * optional inline_html string Custom HTML to inject. + * optional fancy bool If TRUE, will hide the window titlebar. */ -/datum/tgui_window/proc/initialize(inline_assets = list()) +/datum/tgui_window/proc/initialize( + inline_assets = list(), + inline_html = "", + fancy = FALSE) log_tgui(client, "[id]/initialize") if(!client) return + src.inline_assets = inline_assets + src.fancy = fancy status = TGUI_WINDOW_LOADING fatally_errored = FALSE - message_queue = null // Build window options var/options = "file=[id].html;can_minimize=0;auto_format=0;" // Remove titlebar and resize handles for a fancy window - if(client.prefs.tgui_fancy) + if(fancy) options += "titlebar=0;can_resize=0;" else options += "titlebar=1;can_resize=1;" @@ -69,13 +81,17 @@ inline_styles += "\n" else if(copytext(name, -3) == ".js") inline_scripts += "\n" - asset.send() + asset.send(client) html = replacetextEx(html, "\n", inline_styles) html = replacetextEx(html, "\n", inline_scripts) + // Inject custom HTML + html = replacetextEx(html, "\n", inline_html) // Open the window client << browse(html, "window=[id];[options]") // Instruct the client to signal UI when the window is closed. winset(client, id, "on-close=\"uiclose [id]\"") + // Detect whether the control is a browser + is_browser = winexists(client, id) == "BROWSER" /** * public @@ -107,8 +123,8 @@ * Acquire the window lock. Pool will not be able to provide this window * to other UIs for the duration of the lock. * - * Can be given an optional tgui datum, which will hook its on_message - * callback into the message stream. + * Can be given an optional tgui datum, which will be automatically + * subscribed to incoming messages via the on_message proc. * * optional ui /datum/tgui */ @@ -117,6 +133,8 @@ locked_by = ui /** + * public + * * Release the window lock. */ /datum/tgui_window/proc/release_lock() @@ -126,6 +144,28 @@ locked = FALSE locked_by = null +/** + * public + * + * Subscribes the datum to consume window messages on a specified proc. + * + * Note, that this supports only one subscriber, because code for that + * is simpler and therefore faster. If necessary, this can be rewritten + * to support multiple subscribers. + */ +/datum/tgui_window/proc/subscribe(datum/object, delegate) + subscriber_object = object + subscriber_delegate = delegate + +/** + * public + * + * Unsubscribes the datum. Do not forget to call this when cleaning up. + */ +/datum/tgui_window/proc/unsubscribe(datum/object) + subscriber_object = null + subscriber_delegate = null + /** * public * @@ -159,25 +199,40 @@ * required payload list Message payload * optional force bool Send regardless of the ready status. */ -/datum/tgui_window/proc/send_message(type, list/payload, force) +/datum/tgui_window/proc/send_message(type, payload, force) if(!client) return - var/message = json_encode(list( - "type" = type, - "payload" = payload, - )) - // Strip #255/improper. - message = replacetext(message, "\proper", "") - message = replacetext(message, "\improper", "") - // Pack for sending via output() - message = url_encode(message) + var/message = TGUI_CREATE_MESSAGE(type, payload) // Place into queue if window is still loading if(!force && status != TGUI_WINDOW_READY) if(!message_queue) message_queue = list() message_queue += list(message) return - client << output(message, "[id].browser:update") + client << output(message, is_browser \ + ? "[id]:update" \ + : "[id].browser:update") + +/** + * public + * + * Sends a raw payload to tgui window. + * + * required message string JSON+urlencoded blob to send. + * optional force bool Send regardless of the ready status. + */ +/datum/tgui_window/proc/send_raw_message(message, force) + if(!client) + return + // Place into queue if window is still loading + if(!force && status != TGUI_WINDOW_READY) + if(!message_queue) + message_queue = list() + message_queue += list(message) + return + client << output(message, is_browser \ + ? "[id]:update" \ + : "[id].browser:update") /** * public @@ -185,16 +240,18 @@ * Makes an asset available to use in tgui. * * required asset datum/asset + * + * return bool - TRUE if any assets had to be sent to the client */ /datum/tgui_window/proc/send_asset(datum/asset/asset) if(!client || !asset) return + sent_assets |= list(asset) + . = asset.send(client) if(istype(asset, /datum/asset/spritesheet)) var/datum/asset/spritesheet/spritesheet = asset send_message("asset/stylesheet", spritesheet.css_filename()) send_message("asset/mappings", asset.get_url_mappings()) - sent_assets += list(asset) - asset.send(client) /** * private @@ -205,7 +262,9 @@ if(!client || !message_queue) return for(var/message in message_queue) - client << output(message, "[id].browser:update") + client << output(message, is_browser \ + ? "[id]:update" \ + : "[id].browser:update") message_queue = null /** @@ -213,26 +272,45 @@ * * Callback for handling incoming tgui messages. */ -/datum/tgui_window/proc/on_message(type, list/payload, list/href_list) - switch(type) - if("ready") - // Status can be READY if user has refreshed the window. - if(status == TGUI_WINDOW_READY) - // Resend the assets - for(var/asset in sent_assets) - send_asset(asset) - status = TGUI_WINDOW_READY - if("log") - if(href_list["fatal"]) - fatally_errored = TRUE +/datum/tgui_window/proc/on_message(type, payload, href_list) + // Status can be READY if user has refreshed the window. + if(type == "ready" && status == TGUI_WINDOW_READY) + // Resend the assets + for(var/asset in sent_assets) + send_asset(asset) + // Mark this window as fatally errored which prevents it from + // being suspended. + if(type == "log" && href_list["fatal"]) + fatally_errored = TRUE + // Mark window as ready since we received this message from somewhere + if(status != TGUI_WINDOW_READY) + status = TGUI_WINDOW_READY + flush_message_queue() // Pass message to UI that requested the lock if(locked && locked_by) - locked_by.on_message(type, payload, href_list) - flush_message_queue() - return + var/prevent_default = locked_by.on_message(type, payload, href_list) + if(prevent_default) + return + // Pass message to the subscriber + else if(subscriber_object) + var/prevent_default = call( + subscriber_object, + subscriber_delegate)(type, payload, href_list) + if(prevent_default) + return // If not locked, handle these message types switch(type) + if("ping") + send_message("pingReply", payload) if("suspend") close(can_be_suspended = TRUE) if("close") close(can_be_suspended = FALSE) + if("openLink") + client << link(href_list["url"]) + if("cacheReloaded") + // Reinitialize + initialize(inline_assets = inline_assets, fancy = fancy) + // Resend the assets + for(var/asset in sent_assets) + send_asset(asset) diff --git a/code/modules/tgui_panel/audio.dm b/code/modules/tgui_panel/audio.dm new file mode 100644 index 0000000000..e62c4b5bc1 --- /dev/null +++ b/code/modules/tgui_panel/audio.dm @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT + */ + +/// Admin music volume, from 0 to 1. +/client/var/admin_music_volume = 1 + +/** + * public + * + * Sends music data to the browser. + * + * Optional settings: + * - pitch: the playback rate + * - start: the start time of the sound + * - end: when the musics stops playing + * + * required url string Must be an https URL. + * 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)) + return + var/list/payload = list() + if(length(extra_data) > 0) + for(var/key in extra_data) + payload[key] = extra_data[key] + payload["url"] = url + window.send_message("audio/playMusic", payload) + +/** + * public + * + * Stops playing music through the browser. + */ +/datum/tgui_panel/proc/stop_music() + if(!is_ready()) + return + window.send_message("audio/stopMusic") diff --git a/code/modules/tgui_panel/external.dm b/code/modules/tgui_panel/external.dm new file mode 100644 index 0000000000..57c89dc194 --- /dev/null +++ b/code/modules/tgui_panel/external.dm @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT + */ + +/client/var/datum/tgui_panel/tgui_panel + +/** + * tgui panel / chat troubleshooting verb + */ +/client/verb/fix_chat() + set name = "Fix chat" + set category = "OOC" + var/action + log_tgui(src, "tgui_panel: Started fix_chat.") + // Not initialized + if(!tgui_panel || !istype(tgui_panel)) + log_tgui(src, "tgui_panel: datum is missing") + action = alert(src, "tgui panel was not initialized!\nSet it up again?", "", "OK", "Cancel") + if(action != "OK") + return + tgui_panel = new(src) + tgui_panel.initialize() + action = alert(src, "Wait a bit and tell me if it's fixed", "", "Fixed", "Nope") + if(action == "Fixed") + log_tgui(src, "tgui_panel: Fixed by calling 'new' + 'initialize'") + return + // Not ready + if(!tgui_panel?.is_ready()) + log_tgui(src, "tgui_panel: not ready") + action = alert(src, "tgui panel looks like it's waiting for something.\nSend it a ping?", "", "OK", "Cancel") + if(action != "OK") + return + tgui_panel.window.send_message("ping", force = TRUE) + action = alert(src, "Wait a bit and tell me if it's fixed", "", "Fixed", "Nope") + if(action == "Fixed") + log_tgui(src, "tgui_panel: Fixed by sending a ping") + return + // Catch all solution + action = alert(src, "Looks like tgui panel was already setup, but we can always try again.\nSet it up again?", "", "OK", "Cancel") + if(action != "OK") + return + tgui_panel.initialize(force = TRUE) + action = alert(src, "Wait a bit and tell me if it's fixed", "", "Fixed", "Nope") + if(action == "Fixed") + log_tgui(src, "tgui_panel: Fixed by calling 'initialize'") + return + // Failed to fix + action = alert(src, "Welp, I'm all out of ideas. Try closing BYOND and reconnecting.\nWe could also disable tgui_panel and re-enable the old UI", "", "Thanks anyways", "Switch to old UI") + if (action == "Switch to old UI") + winset(src, "output", "on-show=&is-disabled=0&is-visible=1") + winset(src, "browseroutput", "is-disabled=1;is-visible=0") + log_tgui(src, "tgui_panel: Failed to fix.") diff --git a/code/modules/tgui_panel/telemetry.dm b/code/modules/tgui_panel/telemetry.dm new file mode 100644 index 0000000000..79087d8500 --- /dev/null +++ b/code/modules/tgui_panel/telemetry.dm @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT + */ + +/** + * Maximum number of connection records allowed to analyze. + * Should match the value set in the browser. + */ +#define TGUI_TELEMETRY_MAX_CONNECTIONS 5 + +/** + * Maximum time allocated for sending a telemetry packet. + */ +#define TGUI_TELEMETRY_RESPONSE_WINDOW 30 SECONDS + +/// Time of telemetry request +/datum/tgui_panel/var/telemetry_requested_at +/// Time of telemetry analysis completion +/datum/tgui_panel/var/telemetry_analyzed_at +/// List of previous client connections +/datum/tgui_panel/var/list/telemetry_connections + +/** + * private + * + * Requests some telemetry from the client. + */ +/datum/tgui_panel/proc/request_telemetry() + telemetry_requested_at = world.time + telemetry_analyzed_at = null + window.send_message("telemetry/request", list( + "limits" = list( + "connections" = TGUI_TELEMETRY_MAX_CONNECTIONS, + ), + )) + +/** + * private + * + * Analyzes a telemetry packet. + * + * Is currently only useful for detecting ban evasion attempts. + */ +/datum/tgui_panel/proc/analyze_telemetry(payload) + if(world.time > telemetry_requested_at + TGUI_TELEMETRY_RESPONSE_WINDOW) + message_admins("[key_name(client)] sent telemetry outside of the allocated time window.") + return + if(telemetry_analyzed_at) + message_admins("[key_name(client)] sent telemetry more than once.") + return + telemetry_analyzed_at = world.time + if(!payload) + return + telemetry_connections = payload["connections"] + var/len = length(telemetry_connections) + if(len == 0) + return + if(len > TGUI_TELEMETRY_MAX_CONNECTIONS) + message_admins("[key_name(client)] was kicked for sending a huge telemetry payload") + qdel(client) + return + var/list/found + for(var/i in 1 to len) + if(QDELETED(client)) + // He got cleaned up before we were done + return + var/list/row = telemetry_connections[i] + // Check for a malformed history object + if (!row || row.len < 3 || (!row["ckey"] || !row["address"] || !row["computer_id"])) + return + if (world.IsBanned(row["ckey"], row["address"], row["computer_id"], real_bans_only = TRUE)) + found = row + break + CHECK_TICK + // This fucker has a history of playing on a banned account. + if(found) + var/msg = "[key_name(client)] has a banned account in connection history! (Matched: [found["ckey"]], [found["address"]], [found["computer_id"]])" + message_admins(msg) + log_admin_private(msg) diff --git a/code/modules/tgui_panel/tgui_panel.dm b/code/modules/tgui_panel/tgui_panel.dm new file mode 100644 index 0000000000..b983484046 --- /dev/null +++ b/code/modules/tgui_panel/tgui_panel.dm @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT + */ + +/** + * tgui_panel datum + * Hosts tgchat and other nice features. + */ +/datum/tgui_panel + var/client/client + var/datum/tgui_window/window + var/broken = FALSE + var/initialized_at + +/datum/tgui_panel/New(client/client) + src.client = client + window = new(client, "browseroutput") + window.subscribe(src, .proc/on_message) + +/datum/tgui_panel/Del() + window.unsubscribe(src) + window.close() + return ..() + +/** + * public + * + * TRUE if panel is initialized and ready to receive messages. + */ +/datum/tgui_panel/proc/is_ready() + return !broken && window.is_ready() + +/** + * public + * + * Initializes tgui panel. + */ +/datum/tgui_panel/proc/initialize(force = FALSE) + initialized_at = world.time + // Perform a clean initialization + window.initialize(inline_assets = list( + get_asset_datum(/datum/asset/simple/tgui_common), + get_asset_datum(/datum/asset/simple/tgui_panel), + )) + window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/fontawesome)) + window.send_asset(get_asset_datum(/datum/asset/spritesheet/chat)) + request_telemetry() + addtimer(CALLBACK(src, .proc/on_initialize_timed_out), 2 SECONDS) + +/** + * private + * + * Called when initialization has timed out. + */ +/datum/tgui_panel/proc/on_initialize_timed_out() + // Currently does nothing but sending a message to old chat. + SEND_TEXT(client, "Failed to load fancy chat, reverting to old chat. Certain features won't work.") + +/** + * private + * + * Callback for handling incoming tgui messages. + */ +/datum/tgui_panel/proc/on_message(type, payload) + if(type == "ready") + broken = FALSE + window.send_message("update", list( + "config" = list( + "client" = list( + "ckey" = client.ckey, + "address" = client.address, + "computer_id" = client.computer_id, + ), + "window" = list( + "fancy" = FALSE, + "locked" = FALSE, + ), + ), + )) + return TRUE + if(type == "audio/setAdminMusicVolume") + client.admin_music_volume = payload["volume"] + return TRUE + if(type == "telemetry") + analyze_telemetry(payload) + return TRUE + +/** + * public + * + * Sends a round restart notification. + */ +/datum/tgui_panel/proc/send_roundrestart() + window.send_message("roundrestart") diff --git a/code/modules/tgui_panel/to_chat.dm b/code/modules/tgui_panel/to_chat.dm new file mode 100644 index 0000000000..aad27d4872 --- /dev/null +++ b/code/modules/tgui_panel/to_chat.dm @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT + */ + +/** + * global + * + * Circumvents the message queue and sends the message + * to the recipient (target) as soon as possible. + */ +/proc/to_chat_immediate( + target, + text, + handle_whitespace = TRUE, + trailing_newline = TRUE, + confidential = FALSE) + if(!target || !text) + return + if(target == world) + target = GLOB.clients + var/flags = handle_whitespace \ + | trailing_newline << 1 \ + | confidential << 2 + var/message = TGUI_CREATE_MESSAGE("chat/message", list( + "text" = text, + "flags" = flags, + )) + if(islist(target)) + for(var/_target in target) + var/client/client = CLIENT_FROM_VAR(_target) + if(client) + // Send to tgchat + client.tgui_panel?.window.send_raw_message(message) + // Send to old chat + SEND_TEXT(client, text) + return + var/client/client = CLIENT_FROM_VAR(target) + if(client) + // Send to tgchat + client.tgui_panel?.window.send_raw_message(message) + // Send to old chat + SEND_TEXT(client, text) + +/** + * global + * + * Sends the message to the recipient (target). + */ +/proc/to_chat( + target, + text, + handle_whitespace = TRUE, + trailing_newline = TRUE, + confidential = FALSE) + if(Master.current_runlevel == RUNLEVEL_INIT || !SSchat?.initialized) + to_chat_immediate( + target, + text, + handle_whitespace, + trailing_newline, + confidential) + return + if(!target || !text) + return + if(target == world) + target = GLOB.clients + var/flags = handle_whitespace \ + | trailing_newline << 1 \ + | confidential << 2 + SSchat.queue(target, text, flags) diff --git a/code/modules/uplink/uplink_items/uplink_clothing.dm b/code/modules/uplink/uplink_items/uplink_clothing.dm index c26a9ae1f0..745eddcc07 100644 --- a/code/modules/uplink/uplink_items/uplink_clothing.dm +++ b/code/modules/uplink/uplink_items/uplink_clothing.dm @@ -90,6 +90,7 @@ desc = "Through bluespace magic stolen from an organisation that hoards technology, these boots simply allow you to slip through the atoms that make up anything, but only while walking, for safety reasons. As well as this, they unfortunately cause minor breath loss as the majority of atoms in your lungs are sucked out into any solid object you walk through." item = /obj/item/clothing/shoes/wallwalkers cost = 6 + exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/device_tools/guerillagloves name = "Guerilla Gloves" diff --git a/code/modules/uplink/uplink_items/uplink_dangerous.dm b/code/modules/uplink/uplink_items/uplink_dangerous.dm index a43e3fc2ce..58cb43996d 100644 --- a/code/modules/uplink/uplink_items/uplink_dangerous.dm +++ b/code/modules/uplink/uplink_items/uplink_dangerous.dm @@ -177,6 +177,7 @@ organic host as a home base and source of fuel. Holoparasites come in various types and share damage with their host." item = /obj/item/storage/box/syndie_kit/guardian cost = 15 + limited_stock = 1 // you can only have one holopara apparently? refundable = TRUE cant_discount = TRUE surplus = 0 diff --git a/code/modules/uplink/uplink_items/uplink_explosives.dm b/code/modules/uplink/uplink_items/uplink_explosives.dm index 6cc1c9fe05..f44966fb3b 100644 --- a/code/modules/uplink/uplink_items/uplink_explosives.dm +++ b/code/modules/uplink/uplink_items/uplink_explosives.dm @@ -80,6 +80,7 @@ item = /obj/item/cartridge/virus/syndicate cost = 5 restricted = TRUE + limited_stock = 1 /datum/uplink_item/explosives/emp name = "EMP Grenades and Implanter Kit" diff --git a/config/config.txt b/config/config.txt index 46f9a0cdc4..a550e6bf8a 100644 --- a/config/config.txt +++ b/config/config.txt @@ -4,6 +4,8 @@ $include game_options.txt $include dbconfig.txt $include comms.txt $include antag_rep.txt +$include resources.txt +# Cit-specific imports $include donator_groupings.txt $include dynamic_config.txt $include plushies/defines.txt @@ -431,6 +433,8 @@ AUTOADMIN_RANK Game Master #CLIENT_WARN_MESSAGE Byond released 511 as the stable release. You can set the framerate your client runs at, which makes the game feel very different and cool. Shortly after its release we will end up using 511 client features and you will be forced to update. CLIENT_ERROR_VERSION 511 CLIENT_ERROR_MESSAGE Your version of byond is not supported. Please upgrade. +## The minimum build needed for joining the server, if using 512, a good minimum build would be 1421 as that disables the Middle Mouse Button exploit. +CLIENT_ERROR_BUILD 1421 ## TOPIC RATE LIMITING ## This allows you to limit how many topic calls (clicking on an interface window) the client can do in any given game second and/or game minute. diff --git a/config/policy.json b/config/policy.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/config/policy.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/config/resources.txt b/config/resources.txt new file mode 100644 index 0000000000..909bef0809 --- /dev/null +++ b/config/resources.txt @@ -0,0 +1,38 @@ +# External resources +# Set this to the location of a .zip with the server's .rsc inside of it. +# If you set this mutiple times, the server will rotate between the links. +# To use this, the compile option PRELOAD_RSC must be set to 0 to keep byond from preloading resources + +EXTERNAL_RSC_URLS http://tgstation13.download/byond/tgstationv2.zip + + +######################## +# Browser Asset Config # +######################## +# Browser assets are any file included in interfaces. css, images, javascript, etc. +# This handles configuring how we get these to the player so interfaces can access them. + +# Asset Transport +# The normal way of getting assets to clients is to use the internal byond system. This can be slow and delay the opening of interface windows. It also doesn't allow the internal IE windows byond uses to cache anything. +# You can instead have the server save them to a website via a folder within the game server that the web server can read. This could be a simple webserver or something backed by a CDN. +# Valid values: simple, webroot. Simple is the default. +#ASSET_TRANSPORT webroot + + +# Simple asset transport configurable values. + +# Uncomment this to have the server passively send all browser assets to each client in the background. (instead of waiting for them to be needed) +# This should be uncommented in production and commented in development +#ASSET_SIMPLE_PRELOAD + + +# Webroot asset transport configurable values. + +# Local folder to save assets to. +# Assets will be saved in the format of asset.MD5HASH.EXT or in namespaces/hash/ as ASSET_FILE_NAME or asset.MD5HASH.EXT +#ASSET_CDN_WEBROOT data/asset-store/ + +# URL the folder from above can be accessed from. +# for best results the webserver powering this should return a long cache validity time, as all assets sent via this transport use hash based urls +# if you want to test this locally, you simpily run the `localhost-asset-webroot-server.py` python3 script to host assets stored in `data/asset-store/` via http://localhost:58715/ +#ASSET_CDN_URL http://localhost:58715/ diff --git a/html/panels.css b/html/admin/panels.css similarity index 97% rename from html/panels.css rename to html/admin/panels.css index 1f969c690e..22373ee0ca 100644 --- a/html/panels.css +++ b/html/admin/panels.css @@ -1,10 +1,10 @@ -body {padding:0px;margin:0px;} -#top {position:fixed;top:5px;left:10%;width:80%;text-align:center;background-color:#fff;border:2px solid #ccc;} -#main {position:relative;top:10px;left:3%;width:96%;text-align:center;z-index:0;} -#searchable {table-layout:fixed;width:100%;text-align:center;"#f4f4f4";} -tr.norm {background-color:#f4f4f4;} -tr.title {background-color:#ccc;} -tr.alt {background-color:#e7e7e7;} -.small {font-size:80%;} -a {text-decoration:none;} -a:hover {color:#d3d;} +body {padding:0px;margin:0px;} +#top {position:fixed;top:5px;left:10%;width:80%;text-align:center;background-color:#fff;border:2px solid #ccc;} +#main {position:relative;top:10px;left:3%;width:96%;text-align:center;z-index:0;} +#searchable {table-layout:fixed;width:100%;text-align:center;"#f4f4f4";} +tr.norm {background-color:#f4f4f4;} +tr.title {background-color:#ccc;} +tr.alt {background-color:#e7e7e7;} +.small {font-size:80%;} +a {text-decoration:none;} +a:hover {color:#d3d;} diff --git a/html/search.js b/html/admin/search.js similarity index 100% rename from html/search.js rename to html/admin/search.js diff --git a/html/changelog.html b/html/changelog.html index 870578cae5..573d4c5dd8 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -50,6 +50,46 @@ -->
+

30 August 2020

+

raspy-on-osu updated:

+
    +
  • new explosion echoes
  • +
  • explosion echo range
  • +
  • 5 new explosion related sounds
  • +
+ +

28 August 2020

+

EmeraldSundisk updated:

+
    +
  • Adds more paper to the library
  • +
  • The law office now has a desk window
  • +
  • Expands most of CogStation's exterior airlocks. Slightly adjusts surrounding areas to accommodate this.
  • +
  • Updates some of CogStation's paperwork
  • +
  • The rat in the morgue turned themselves into a possum. Funniest shit I've ever seen.
  • +
  • Adjusts some area designations so cameras should receive power properly
  • +
  • Cleans up an errant decal
  • +
+

Hatterhat updated:

+
    +
  • Traitor holoparasites can now only be bought once, because apparently you can only have one active holopara.
  • +
  • PDA bombs can now only be bought once per uplink.
  • +
+

lolman360 updated:

+
    +
  • atmos = radiation = chemistry.
  • +
+

shellspeed1 updated:

+
    +
  • Adds slow mode for iv drips
  • +
+

timothyteakettle updated:

+
    +
  • an ancient game over a thousand years old has re-emerged among crewmembers - rock paper scissors
  • +
  • customization features appear in alphabetical order where necessary
  • +
  • bokken do two more stamina damage now
  • +
  • you can now choose a body sprite as an anthromorph or anthromorphic insect, and can choose from aquatic/avian and apid respectively (and obviously back to the defaults too)
  • +
+

27 August 2020

silicons updated:

    @@ -1395,77 +1435,6 @@
    • slimes no longer have white blood by default
    - -

    28 June 2020

    -

    Detective-Google updated:

    -
      -
    • cog is less the suck
    • -
    • piggybacking is no longer absolutely inferior
    • -
    -

    Ghommie updated:

    -
      -
    • Fixing windows interaction with spraycans.
    • -
    • Fixing kinetic accelerator guns not working well with gun circuitries.
    • -
    • Fixing Zoomba borgs lights overlays.
    • -
    • Fixing the "absorb another ling" and "absorb the most dna" objectives rolling when no other changeling is around.
    • -
    • Clarified a pet peeve about the spread infestation ability.
    • -
    • BEPIS nodes won't show up anymore in the expert mode ui of the r&d console anymore (good thing they weren't researchable).
    • -
    • Hopefully fixing sound loop edge cases.
    • -
    • Fixing pAI radios being permanently disabled by EMPs at times.
    • -
    • Windoors can now be obscured with spraycans just like windows.
    • -
    -

    Ghommie porting PRs by Qustinnus/Floyd, Willow, cacogen, nemvar, Ghilker and EOBGames (Inept) updated:

    -
      -
    • Fixes a material duplication bug.
    • -
    • unique combinations of custom_materials lists are now shared between objects
    • -
    • meat material. yes.
    • -
    • materials can now be used to build walls/floors. meat house
    • -
    • edible component now does not try to attack if you eat something with it
    • -
    • Texture support for mat datums with thanks to 4DPlanner!
    • -
    • you no longer hit yourself with organs when eating
    • -
    • A whole bunch of materials are now datumised! Check out bronze, runed metal, sand, sandstone, snow, paper, cardboard, bone and bamboo. Oh, and pizza. Yes, pizza.
    • -
    • Buffs material floor tiles' throwforces from 1 to 10 (same as iron) to better showcase the effect of different materials (e.g. meat vs. titanium)
    • -
    • Radioactive items no longer output a single . when examined at a distance
    • -
    -

    MrJWhit updated:

    -
      -
    • Removed air alarm in Snow Snaxi in Tcomms Sat
    • -
    • Removed trash bins in genetics and mining
    • -
    • Gives cargo techs a cargolathe
    • -
    -

    Putnam3145 updated:

    -
      -
    • lost my mind just a couple of times
    • -
    -

    b1tt3r1n0 updated:

    -
      -
    • pouches, again, and and material pouches.
    • -
    -

    timothyteakettle updated:

    -
      -
    • support for custom blood colours implemented, slimes blood colour now equivalent to their body colour
    • -
    - -

    27 June 2020

    -

    Detective-Google updated:

    -
      -
    • Lying down is better
    • -
    -

    timothyteakettle updated:

    -
      -
    • felinids now nya when tabled
    • -
    - -

    26 June 2020

    -

    Ghommie updated:

    -
      -
    • Snore spam.
    • -
    • Hostile mobs shouldn't hit their original spawner structures or thoses of the same faction.
    • -
    -

    silicons updated:

    -
      -
    • soap cleans blood again
    • -
GoonStation 13 Development Team diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index fd9523fb15..0276508e57 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -27100,3 +27100,35 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. timothyteakettle: - tweak: changing your character's gender won't randomize its hairstyle and facial hairstyle now +2020-08-28: + EmeraldSundisk: + - rscadd: Adds more paper to the library + - rscadd: The law office now has a desk window + - tweak: Expands most of CogStation's exterior airlocks. Slightly adjusts surrounding + areas to accommodate this. + - tweak: Updates some of CogStation's paperwork + - tweak: The rat in the morgue turned themselves into a possum. Funniest shit I've + ever seen. + - bugfix: Adjusts some area designations so cameras should receive power properly + - bugfix: Cleans up an errant decal + Hatterhat: + - tweak: Traitor holoparasites can now only be bought once, because apparently you + can only have one active holopara. + - balance: PDA bombs can now only be bought once per uplink. + lolman360: + - rscadd: atmos = radiation = chemistry. + shellspeed1: + - rscadd: Adds slow mode for iv drips + timothyteakettle: + - rscadd: an ancient game over a thousand years old has re-emerged among crewmembers + - rock paper scissors + - tweak: customization features appear in alphabetical order where necessary + - tweak: bokken do two more stamina damage now + - rscadd: you can now choose a body sprite as an anthromorph or anthromorphic insect, + and can choose from aquatic/avian and apid respectively (and obviously back + to the defaults too) +2020-08-30: + raspy-on-osu: + - rscadd: new explosion echoes + - tweak: explosion echo range + - soundadd: 5 new explosion related sounds diff --git a/html/changelogs/AutoChangeLog-pr-12957.yml b/html/changelogs/AutoChangeLog-pr-12957.yml new file mode 100644 index 0000000000..84e733b162 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12957.yml @@ -0,0 +1,4 @@ +author: "kappa-sama" +delete-after: True +changes: + - balance: "miners can no longer acquire funny antag item" diff --git a/html/changelogs/AutoChangeLog-pr-13024.yml b/html/changelogs/AutoChangeLog-pr-13024.yml new file mode 100644 index 0000000000..1992b4dfeb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13024.yml @@ -0,0 +1,4 @@ +author: "zeroisthebiggay" +delete-after: True +changes: + - rscdel: "waffleco" diff --git a/html/changelogs/AutoChangeLog-pr-13125.yml b/html/changelogs/AutoChangeLog-pr-13125.yml new file mode 100644 index 0000000000..4fd3e478e9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13125.yml @@ -0,0 +1,4 @@ +author: "Chiirno" +delete-after: True +changes: + - bugfix: "Moves pill_bottles/dice to box/dice on CogStation." diff --git a/html/changelogs/AutoChangeLog-pr-13173.yml b/html/changelogs/AutoChangeLog-pr-13173.yml new file mode 100644 index 0000000000..5a41fdaa19 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13173.yml @@ -0,0 +1,4 @@ +author: "LetterN" +delete-after: True +changes: + - rscadd: "tgchat" diff --git a/html/changelogs/AutoChangeLog-pr-13184.yml b/html/changelogs/AutoChangeLog-pr-13184.yml new file mode 100644 index 0000000000..85660e2fcc --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13184.yml @@ -0,0 +1,4 @@ +author: "timothyteakettle" +delete-after: True +changes: + - imagedel: "the drake credit and pickle credit sprites have been removed" diff --git a/html/changelogs/AutoChangeLog-pr-13192.yml b/html/changelogs/AutoChangeLog-pr-13192.yml new file mode 100644 index 0000000000..857d3ca1ea --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13192.yml @@ -0,0 +1,5 @@ +author: "Putnam3145" +delete-after: True +changes: + - balance: "Buffed slaughter demon: gets stronger as it eats people" + - balance: "Nerfed slaughter demon: no longer permanently round-removes all who are eaten by it, instead releasing their now-heartless bodies" diff --git a/html/changelogs/AutoChangeLog-pr-13229.yml b/html/changelogs/AutoChangeLog-pr-13229.yml new file mode 100644 index 0000000000..1cc156e675 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13229.yml @@ -0,0 +1,5 @@ +author: "Sonic121x" +delete-after: True +changes: + - bugfix: "Fix the four type of new tea that will stuck inside your vein." + - rscadd: "drinking glass sprite for those tea." diff --git a/html/changelogs/AutoChangeLog-pr-13230.yml b/html/changelogs/AutoChangeLog-pr-13230.yml new file mode 100644 index 0000000000..36addf5717 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13230.yml @@ -0,0 +1,4 @@ +author: "Detective-Google" +delete-after: True +changes: + - rscadd: "object reskins now use very nice and cool radials" diff --git a/html/changelogs/AutoChangeLog-pr-13263.yml b/html/changelogs/AutoChangeLog-pr-13263.yml new file mode 100644 index 0000000000..460d06ae50 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13263.yml @@ -0,0 +1,6 @@ +author: "raspy-on-osu" +delete-after: True +changes: + - tweak: "tesla movement priorities" + - rscadd: "tesla counterplay" + - rscadd: "tesla containment check (containment variable now usable)" diff --git a/html/changelogs/AutoChangeLog-pr-13266.yml b/html/changelogs/AutoChangeLog-pr-13266.yml new file mode 100644 index 0000000000..4ecbd44647 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13266.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "e-gun overlays and some floor decals should have been fixed." diff --git a/html/changelogs/AutoChangeLog-pr-13267.yml b/html/changelogs/AutoChangeLog-pr-13267.yml new file mode 100644 index 0000000000..9017747c84 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13267.yml @@ -0,0 +1,4 @@ +author: "Arturlang" +delete-after: True +changes: + - tweak: "Slimes can now damage structures, don't leave them unfed!" diff --git a/html/changelogs/AutoChangeLog-pr-13268.yml b/html/changelogs/AutoChangeLog-pr-13268.yml new file mode 100644 index 0000000000..941ff7ac5a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13268.yml @@ -0,0 +1,4 @@ +author: "timothyteakettle" +delete-after: True +changes: + - refactor: "tongue speech handling is now done by accent datums" diff --git a/html/changelogs/AutoChangeLog-pr-13269.yml b/html/changelogs/AutoChangeLog-pr-13269.yml new file mode 100644 index 0000000000..152245e430 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13269.yml @@ -0,0 +1,6 @@ +author: "EmeraldSundisk" +delete-after: True +changes: + - rscadd: "Adds a pool to MetaStation" + - tweak: "Slight readjustments to the surrounding area" + - bugfix: "Fixes a handful of external airlocks" diff --git a/html/changelogs/AutoChangeLog-pr-13272.yml b/html/changelogs/AutoChangeLog-pr-13272.yml new file mode 100644 index 0000000000..9b0f64d7a0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13272.yml @@ -0,0 +1,4 @@ +author: "Detective-Google" +delete-after: True +changes: + - tweak: "plasmamen have no more slowdown" diff --git a/html/changelogs/AutoChangeLog-pr-13276.yml b/html/changelogs/AutoChangeLog-pr-13276.yml new file mode 100644 index 0000000000..48ad4ed26c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13276.yml @@ -0,0 +1,4 @@ +author: "timothyteakettle" +delete-after: True +changes: + - tweak: "buzz, buzz2 and ping are now all unrestricted emotes and can be used by anyone" diff --git a/html/changelogs/AutoChangeLog-pr-13277.yml b/html/changelogs/AutoChangeLog-pr-13277.yml new file mode 100644 index 0000000000..d6d0d89ed6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13277.yml @@ -0,0 +1,6 @@ +author: "Lynxless" +delete-after: True +changes: + - tweak: "Changed anatomic panacea into a direct buff, instead of a chem injection" + - balance: "Changed the values of anatomic panacea" + - imageadd: "Added a new icon for panacea's buff alert" diff --git a/html/changelogs/AutoChangeLog-pr-13280.yml b/html/changelogs/AutoChangeLog-pr-13280.yml new file mode 100644 index 0000000000..e69aefd961 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13280.yml @@ -0,0 +1,4 @@ +author: "raspy-on-osu" +delete-after: True +changes: + - tweak: "TEG power output" diff --git a/html/changelogs/AutoChangeLog-pr-13281.yml b/html/changelogs/AutoChangeLog-pr-13281.yml new file mode 100644 index 0000000000..b39f9b66e0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13281.yml @@ -0,0 +1,4 @@ +author: "Putnam3145" +delete-after: True +changes: + - tweak: "Pref for genital/vore examine text" diff --git a/html/changelogs/AutoChangeLog-pr-13290.yml b/html/changelogs/AutoChangeLog-pr-13290.yml new file mode 100644 index 0000000000..c2faacf790 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13290.yml @@ -0,0 +1,5 @@ +author: "ForrestWick" +delete-after: True +changes: + - balance: "removes wall walking boots from nukie uplink" + - tweak: "removes wall walking boots from nukie uplink" diff --git a/code/modules/goonchat/browserassets/js/jquery.min.js b/html/jquery.min.js similarity index 100% rename from code/modules/goonchat/browserassets/js/jquery.min.js rename to html/jquery.min.js diff --git a/icons/mob/32x64.dmi b/icons/mob/32x64.dmi index cddf9599b4..79a4c8a7fd 100644 Binary files a/icons/mob/32x64.dmi and b/icons/mob/32x64.dmi differ diff --git a/icons/mob/cyborg/drakemech.dmi b/icons/mob/cyborg/drakemech.dmi index 6a4845d983..a9c47b2b78 100644 Binary files a/icons/mob/cyborg/drakemech.dmi and b/icons/mob/cyborg/drakemech.dmi differ diff --git a/icons/mob/human_parts.dmi b/icons/mob/human_parts.dmi index 916686e63f..60978d55a2 100644 Binary files a/icons/mob/human_parts.dmi and b/icons/mob/human_parts.dmi differ diff --git a/icons/mob/human_parts_greyscale.dmi b/icons/mob/human_parts_greyscale.dmi index 989ec8049c..794074bfe4 100644 Binary files a/icons/mob/human_parts_greyscale.dmi and b/icons/mob/human_parts_greyscale.dmi differ diff --git a/icons/mob/screen_alert.dmi b/icons/mob/screen_alert.dmi index 30c23601a5..4ced910193 100644 Binary files a/icons/mob/screen_alert.dmi and b/icons/mob/screen_alert.dmi differ diff --git a/icons/mob/wings.dmi b/icons/mob/wings.dmi index ace37b1f17..4523403344 100644 Binary files a/icons/mob/wings.dmi and b/icons/mob/wings.dmi differ diff --git a/icons/obj/bureaucracy.dmi b/icons/obj/bureaucracy.dmi index 95ae326f1e..b778a9d1a2 100644 Binary files a/icons/obj/bureaucracy.dmi and b/icons/obj/bureaucracy.dmi differ diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi index cd12f4a457..14eb973e25 100644 Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ diff --git a/icons/obj/crates.dmi b/icons/obj/crates.dmi index a1bc443efb..9b42188f5e 100644 Binary files a/icons/obj/crates.dmi and b/icons/obj/crates.dmi differ diff --git a/icons/obj/custom.dmi b/icons/obj/custom.dmi index d0be1113ee..9067a4beca 100644 Binary files a/icons/obj/custom.dmi and b/icons/obj/custom.dmi differ diff --git a/icons/obj/drinks.dmi b/icons/obj/drinks.dmi index 5962e7522d..008618199a 100644 Binary files a/icons/obj/drinks.dmi and b/icons/obj/drinks.dmi differ diff --git a/icons/stamp_icons/font.png b/icons/stamp_icons/font.png new file mode 100644 index 0000000000..4297937696 Binary files /dev/null and b/icons/stamp_icons/font.png differ diff --git a/icons/stamp_icons/large_stamp-cap.png b/icons/stamp_icons/large_stamp-cap.png index 7f7ce460a4..19883563b2 100644 Binary files a/icons/stamp_icons/large_stamp-cap.png and b/icons/stamp_icons/large_stamp-cap.png differ diff --git a/icons/stamp_icons/large_stamp-ce.png b/icons/stamp_icons/large_stamp-ce.png index 189db310cf..d07f70c4d5 100644 Binary files a/icons/stamp_icons/large_stamp-ce.png and b/icons/stamp_icons/large_stamp-ce.png differ diff --git a/icons/stamp_icons/large_stamp-centcom.png b/icons/stamp_icons/large_stamp-centcom.png new file mode 100644 index 0000000000..6250cbff88 Binary files /dev/null and b/icons/stamp_icons/large_stamp-centcom.png differ diff --git a/icons/stamp_icons/large_stamp-chap.png b/icons/stamp_icons/large_stamp-chap.png new file mode 100644 index 0000000000..2586c13bad Binary files /dev/null and b/icons/stamp_icons/large_stamp-chap.png differ diff --git a/icons/stamp_icons/large_stamp-clown.png b/icons/stamp_icons/large_stamp-clown.png index 5abbff7113..d606870507 100644 Binary files a/icons/stamp_icons/large_stamp-clown.png and b/icons/stamp_icons/large_stamp-clown.png differ diff --git a/icons/stamp_icons/large_stamp-cmo.png b/icons/stamp_icons/large_stamp-cmo.png index eb004be621..bac4c7b2f9 100644 Binary files a/icons/stamp_icons/large_stamp-cmo.png and b/icons/stamp_icons/large_stamp-cmo.png differ diff --git a/icons/stamp_icons/large_stamp-deny.png b/icons/stamp_icons/large_stamp-deny.png index 7c216fce48..1c7f416597 100644 Binary files a/icons/stamp_icons/large_stamp-deny.png and b/icons/stamp_icons/large_stamp-deny.png differ diff --git a/icons/stamp_icons/large_stamp-hop.png b/icons/stamp_icons/large_stamp-hop.png index 3f9aa4a76f..1447d58a87 100644 Binary files a/icons/stamp_icons/large_stamp-hop.png and b/icons/stamp_icons/large_stamp-hop.png differ diff --git a/icons/stamp_icons/large_stamp-hos.png b/icons/stamp_icons/large_stamp-hos.png index 67b69d7503..bd59590975 100644 Binary files a/icons/stamp_icons/large_stamp-hos.png and b/icons/stamp_icons/large_stamp-hos.png differ diff --git a/icons/stamp_icons/large_stamp-law.png b/icons/stamp_icons/large_stamp-law.png index d6d77eee9b..aee5aa3fbc 100644 Binary files a/icons/stamp_icons/large_stamp-law.png and b/icons/stamp_icons/large_stamp-law.png differ diff --git a/icons/stamp_icons/large_stamp-mime.png b/icons/stamp_icons/large_stamp-mime.png new file mode 100644 index 0000000000..c9a0143439 Binary files /dev/null and b/icons/stamp_icons/large_stamp-mime.png differ diff --git a/icons/stamp_icons/large_stamp-ok.png b/icons/stamp_icons/large_stamp-ok.png index e568ae0921..b40d3e3f1b 100644 Binary files a/icons/stamp_icons/large_stamp-ok.png and b/icons/stamp_icons/large_stamp-ok.png differ diff --git a/icons/stamp_icons/large_stamp-qm.png b/icons/stamp_icons/large_stamp-qm.png index ea863078b4..4ba31b3741 100644 Binary files a/icons/stamp_icons/large_stamp-qm.png and b/icons/stamp_icons/large_stamp-qm.png differ diff --git a/icons/stamp_icons/large_stamp-rd.png b/icons/stamp_icons/large_stamp-rd.png index 35479e805f..a9e7ad040b 100644 Binary files a/icons/stamp_icons/large_stamp-rd.png and b/icons/stamp_icons/large_stamp-rd.png differ diff --git a/icons/stamp_icons/large_stamp-syndicate.png b/icons/stamp_icons/large_stamp-syndicate.png new file mode 100644 index 0000000000..68f17d8f5e Binary files /dev/null and b/icons/stamp_icons/large_stamp-syndicate.png differ diff --git a/interface/interface.dm b/interface/interface.dm index 2b33ebb5e8..2aece7cb6c 100644 --- a/interface/interface.dm +++ b/interface/interface.dm @@ -71,7 +71,7 @@ return var/static/issue_template = file2text(".github/ISSUE_TEMPLATE.md") var/servername = CONFIG_GET(string/servername) - var/url_params = "Reporting client version: [byond_version]\n\n[issue_template]" + var/url_params = "Reporting client version: [byond_version].[byond_build]\n\n[issue_template]" if(GLOB.round_id || servername) url_params = "Issue reported from [GLOB.round_id ? " Round ID: [GLOB.round_id][servername ? " ([servername])" : ""]" : servername]\n\n[url_params]" DIRECT_OUTPUT(src, link("[githuburl]/issues/new?body=[url_encode(url_params)]")) @@ -79,151 +79,13 @@ to_chat(src, "The Github URL is not set in the server configuration.") return -/client/verb/hotkeys_help() - set name = "hotkeys-help" - set category = "OOC" - - var/adminhotkeys = {" -Admin: -\tF3 = asay -\tF5 = Aghost (admin-ghost) -\tF6 = player-panel -\tF7 = Buildmode -\tF8 = Invisimin -\tCtrl+F8 = Stealthmin -"} - - mob.hotkey_help() - - if(holder) - to_chat(src, adminhotkeys) - /client/verb/changelog() set name = "Changelog" set category = "OOC" - var/datum/asset/changelog = get_asset_datum(/datum/asset/simple/changelog) + var/datum/asset/simple/namespaced/changelog = get_asset_datum(/datum/asset/simple/namespaced/changelog) changelog.send(src) - src << browse('html/changelog.html', "window=changes;size=675x650") + src << browse(changelog.get_htmlloader("changelog.html"), "window=changes;size=675x650") if(prefs.lastchangelog != GLOB.changelog_hash) prefs.lastchangelog = GLOB.changelog_hash prefs.save_preferences() winset(src, "infowindow.changelog", "font-style=;") - - -/mob/proc/hotkey_help() - var/hotkey_mode = {" -Hotkey-Mode: (hotkey-mode must be on) -\tTAB = toggle hotkey-mode -\ta = left -\ts = down -\td = right -\tw = up -\tq = drop -\te = equip -\tr = throw -\tm = me -\tt = say -\to = OOC -\tb = resist -\tv = rest -\th = stop pulling -\tx = swap-hand -\tz = activate held object (or y) -\tShift+e = Put held item into belt or take out most recent item added to belt. -\tShift+b = Put held item into backpack or take out most recent item added to backpack. -\tf = cycle-intents-left -\tg = cycle-intents-right -\t1 = help-intent -\t2 = disarm-intent -\t3 = grab-intent -\t4 = harm-intent -\tNumpad = Body target selection (Press 8 repeatedly for Head->Eyes->Mouth) -\tAlt(HOLD) = Alter movement intent -"} - - var/other = {" -Any-Mode: (hotkey doesn't need to be on) -\tCtrl+a = left -\tCtrl+s = down -\tCtrl+d = right -\tCtrl+w = up -\tCtrl+q = drop -\tCtrl+e = equip -\tCtrl+r = throw -\tCtrl+b = resist -\tCtrl+h = stop pulling -\tCtrl+o = OOC -\tCtrl+x = swap-hand -\tCtrl+z = activate held object (or Ctrl+y) -\tCtrl+f = cycle-intents-left -\tCtrl+g = cycle-intents-right -\tCtrl+1 = help-intent -\tCtrl+2 = disarm-intent -\tCtrl+3 = grab-intent -\tCtrl+4 = harm-intent -\tCtrl+'+/-' OR -\tShift+Mousewheel = Ghost zoom in/out -\tDEL = stop pulling -\tINS = cycle-intents-right -\tHOME = drop -\tPGUP = swap-hand -\tPGDN = activate held object -\tEND = throw -\tCtrl+Numpad = Body target selection (Press 8 repeatedly for Head->Eyes->Mouth) -"} - - to_chat(src, hotkey_mode) - to_chat(src, other) - -/mob/living/silicon/robot/hotkey_help() - //h = talk-wheel has a nonsense tag in it because \th is an escape sequence in BYOND. - var/hotkey_mode = {" -Hotkey-Mode: (hotkey-mode must be on) -\tTAB = toggle hotkey-mode -\ta = left -\ts = down -\td = right -\tw = up -\tq = unequip active module -\tv = rest -\th = stop pulling -\tm = me -\tt = say -\to = OOC -\tx = cycle active modules -\tb = resist -\tz = activate held object (or y) -\tf = cycle-intents-left -\tg = cycle-intents-right -\t1 = activate module 1 -\t2 = activate module 2 -\t3 = activate module 3 -\t4 = toggle intents -"} - - var/other = {" -Any-Mode: (hotkey doesn't need to be on) -\tCtrl+a = left -\tCtrl+s = down -\tCtrl+d = right -\tCtrl+w = up -\tCtrl+q = unequip active module -\tCtrl+x = cycle active modules -\tCtrl+b = resist -\tCtrl+h = stop pulling -\tCtrl+o = OOC -\tCtrl+z = activate held object (or Ctrl+y) -\tCtrl+f = cycle-intents-left -\tCtrl+g = cycle-intents-right -\tCtrl+1 = activate module 1 -\tCtrl+2 = activate module 2 -\tCtrl+3 = activate module 3 -\tCtrl+4 = toggle intents -\tDEL = stop pulling -\tINS = toggle intents -\tPGUP = cycle active modules -\tPGDN = activate held object -"} - - to_chat(src, hotkey_mode) - to_chat(src, other) diff --git a/interface/menu.dm b/interface/menu.dm index 3c881656da..976abb16b3 100644 --- a/interface/menu.dm +++ b/interface/menu.dm @@ -63,7 +63,6 @@ GLOBAL_LIST_EMPTY(menulist) return M.Set_checked(src, verbpath) - /datum/verbs/menu/Icon/Load_checked(client/C) //So we can be lazy, we invoke the "checked" menu item on menu load. var/procpath/verbpath = Get_checked(C) if (!verbpath || !(verbpath in typesof("[type]/verb"))) @@ -115,4 +114,3 @@ GLOBAL_LIST_EMPTY(menulist) /datum/verbs/menu/Icon/Scaling/verb/BL() set name = "@.winset \"mapwindow.map.zoom-mode=blur\"" set desc = "Bilinear" - diff --git a/interface/skin.dmf b/interface/skin.dmf index cf49f0f30f..d7e461eadd 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -1,320 +1,290 @@ -macro "default" - - -menu "menu" - elem - name = "&File" - command = "" - saved-params = "is-checked" - elem - name = "&Quick screenshot\tF2" - command = ".screenshot auto" - category = "&File" - saved-params = "is-checked" - elem - name = "&Save screenshot as...\tShift+F2" - command = ".screenshot" - category = "&File" - saved-params = "is-checked" - elem - name = "" - command = "" - category = "&File" - saved-params = "is-checked" - elem "reconnectbutton" - name = "&Reconnect" - command = ".reconnect" - category = "&File" - saved-params = "is-checked" - elem - name = "&Quit\tAlt-F4" - command = ".quit" - category = "&File" - saved-params = "is-checked" - elem - name = "&Help" - command = "" - saved-params = "is-checked" - elem - name = "&Admin Help\tF1" - command = "adminhelp" - category = "&Help" - saved-params = "is-checked" - elem - name = "&Hotkeys" - command = "hotkeys-help" - category = "&Help" - saved-params = "is-checked" - - -window "mainwindow" - elem "mainwindow" - type = MAIN - pos = 0,0 - size = 640x440 - anchor1 = none - anchor2 = none - background-color = #242424 - is-default = true - saved-params = "pos;size;is-minimized;is-maximized" - icon = 'icons\\ss13_64.png' - macro = "default" - menu = "menu" - elem "split" - type = CHILD - pos = 0,0 - size = 637x440 - anchor1 = 0,0 - anchor2 = 100,100 - background-color = #272727 - saved-params = "splitter" - left = "mapwindow" - right = "infowindow" - is-vert = true - splitter = 75 - elem "asset_cache_browser" - type = BROWSER - pos = 0,0 - size = 200x200 - anchor1 = none - anchor2 = none - background-color = #272727 - is-visible = false - auto-format = false - saved-params = "" - elem "tooltip" - type = BROWSER - pos = 0,0 - size = 999x999 - anchor1 = none - anchor2 = none - background-color = #272727 - is-visible = false - saved-params = "" - -window "mapwindow" - elem "mapwindow" - type = MAIN - pos = 418,0 - size = 1024x1024 - anchor1 = none - anchor2 = none - background-color = none - saved-params = "pos;size;is-minimized;is-maximized" - statusbar = false - is-pane = true - outer-size = 684x617 - inner-size = 662x561 - elem "map" - type = MAP - pos = 0,0 - size = 1024x1024 - anchor1 = 0,0 - anchor2 = 100,100 - font-family = "Arial" - font-size = 7 - is-default = true - saved-params = "icon-size" - zoom-mode = distort - style = ".center { text-align: center; }\n.maptext { font-family: 'Small Fonts'; font-size: 7px; -dm-text-outline: 1px black; color: white; line-height: 1.1; }\n.command_headset { font-weight: bold;\tfont-size: 8px; } .small { font-size: 6px; }\n.big { font-size: 8px; }\n.reallybig { font-size: 8px; }\n.extremelybig { font-size: 8px; }\n.greentext { color: #00ff00; font-size: 7px; }\n.redtext { color: #ff0000; font-size: 7px; }\n.clown { color: #ff69bf; font-size: 7px; font-weight: bold; }\n.his_grace { color: #15d512; }\n.hypnophrase { color: #0d0d0d; font-weight: bold; }\n.yell { font-weight: bold; }\n.italics { font-size: 6px; }" - -window "infowindow" - elem "infowindow" - type = MAIN - pos = 0,0 - size = 640x480 - anchor1 = none - anchor2 = none - background-color = #242424 - saved-params = "pos;size;is-minimized;is-maximized" - is-pane = true - elem "info" - type = CHILD - pos = 0,30 - size = 640x445 - anchor1 = 0,0 - anchor2 = 100,100 - background-color = #272727 - saved-params = "splitter" - left = "statwindow" - right = "outputwindow" - is-vert = false - elem "changelog" - type = BUTTON - pos = 16,5 - size = 104x20 - anchor1 = 3,0 - anchor2 = 19,0 - text-color = #e0e0e0 - background-color = #40628a - saved-params = "is-checked" - text = "Changelog" - command = "changelog" - elem "rules" - type = BUTTON - pos = 120,5 - size = 100x20 - anchor1 = 19,0 - anchor2 = 34,0 - text-color = #e0e0e0 - background-color = #40628a - saved-params = "is-checked" - text = "Rules" - command = "rules" - elem "wiki" - type = BUTTON - pos = 220,5 - size = 100x20 - anchor1 = 34,0 - anchor2 = 50,0 - text-color = #e0e0e0 - background-color = #40628a - saved-params = "is-checked" - text = "Wiki" - command = "wiki" - elem "forum" - type = BUTTON - pos = 320,5 - size = 100x20 - anchor1 = 50,0 - anchor2 = 66,0 - text-color = #e0e0e0 - background-color = #40628a - saved-params = "is-checked" - text = "Forum" - command = "forum" - elem "github" - type = BUTTON - pos = 420,5 - size = 100x20 - anchor1 = 66,0 - anchor2 = 81,0 - text-color = #e0e0e0 - background-color = #40628a - saved-params = "is-checked" - text = "GitHub" - command = "github" - elem "report-issue" - type = BUTTON - pos = 520,5 - size = 100x20 - anchor1 = 81,0 - anchor2 = 97,0 - font-size = 8 - text-color = #e0e0e0 - background-color = #a92c2c - saved-params = "is-checked" - text = "Report Issue" - command = "report-issue" - -window "outputwindow" - elem "outputwindow" - type = MAIN - pos = 0,0 - size = 640x480 - anchor1 = none - anchor2 = none - background-color = #272727 - saved-params = "pos;size;is-minimized;is-maximized" - titlebar = false - statusbar = false - can-close = false - can-minimize = false - can-resize = false - is-pane = true - elem "input" - type = INPUT - pos = 2,460 - size = 595x20 - anchor1 = 0,100 - anchor2 = 100,100 - background-color = #d3b5b5 - is-default = true - border = sunken - saved-params = "command" - elem "say" - type = BUTTON - pos = 600,460 - size = 37x20 - anchor1 = 100,100 - anchor2 = none - text-color = #e0e0e0 - background-color = #272727 - saved-params = "is-checked" - text = "Chat" - command = ".winset \"say.is-checked=true ? input.command=\"!say \\\"\" : input.command=\"" - is-flat = true - button-type = pushbox - elem "browseroutput" - type = BROWSER - pos = 0,0 - size = 640x456 - anchor1 = 0,0 - anchor2 = 100,100 - background-color = #272727 - is-visible = false - is-disabled = true - saved-params = "" - auto-format = false - elem "output" - type = OUTPUT - pos = 0,0 - size = 640x456 - anchor1 = 0,0 - anchor2 = 100,100 - text-color = #e0e0e0 - background-color = #272727 - is-default = true - saved-params = "" - -window "statwindow" - elem "statwindow" - type = MAIN - pos = 281,0 - size = 640x480 - anchor1 = none - anchor2 = none - background-color = #242424 - saved-params = "pos;size;is-minimized;is-maximized" - is-pane = true - elem "stat" - type = INFO - pos = 0,0 - size = 640x480 - anchor1 = 0,0 - anchor2 = 100,100 - text-color = #e0e0e0 - background-color = #272727 - is-default = true - saved-params = "" - tab-text-color = #e0e0e0 - tab-background-color = #242424 - prefix-color = #e0e0e0 - suffix-color = #e0e0e0 - -window "preferences_window" - elem "preferences_window" - type = MAIN - pos = 372,0 - size = 1280x1000 - anchor1 = none - anchor2 = none - is-visible = false - saved-params = "pos;size;is-minimized;is-maximized" - statusbar = false - elem "preferences_browser" - type = BROWSER - pos = 0,0 - size = 960x1000 - anchor1 = 0,0 - anchor2 = 75,100 - saved-params = "" - elem "character_preview_map" - type = MAP - pos = 960,0 - size = 320x1000 - anchor1 = 75,0 - anchor2 = 100,100 - right-click = true - saved-params = "zoom;letterbox;zoom-mode" - +macro "default" + + +menu "menu" + elem + name = "&File" + command = "" + saved-params = "is-checked" + elem + name = "&Quick screenshot\tF2" + command = ".screenshot auto" + category = "&File" + saved-params = "is-checked" + elem + name = "&Save screenshot as...\tShift+F2" + command = ".screenshot" + category = "&File" + saved-params = "is-checked" + elem + name = "" + command = "" + category = "&File" + saved-params = "is-checked" + elem "reconnectbutton" + name = "&Reconnect" + command = ".reconnect" + category = "&File" + saved-params = "is-checked" + elem + name = "&Quit\tAlt-F4" + command = ".quit" + category = "&File" + saved-params = "is-checked" + elem + name = "&Help" + command = "" + saved-params = "is-checked" + elem + name = "&Admin Help\tF1" + command = "adminhelp" + category = "&Help" + saved-params = "is-checked" + elem + name = "&Hotkeys" + command = "hotkeys-help" + category = "&Help" + saved-params = "is-checked" + +window "mainwindow" + elem "mainwindow" + type = MAIN + pos = 0,0 + size = 640x440 + anchor1 = none + anchor2 = none + is-default = true + saved-params = "pos;size;is-minimized;is-maximized" + icon = 'icons\\ss13_64.png' + macro = "default" + menu = "menu" + elem "split" + type = CHILD + pos = 3,0 + size = 637x440 + anchor1 = 0,0 + anchor2 = 100,100 + saved-params = "splitter" + left = "mapwindow" + right = "infowindow" + is-vert = true + elem "asset_cache_browser" + type = BROWSER + pos = 0,0 + size = 200x200 + anchor1 = none + anchor2 = none + is-visible = false + auto-format = false + saved-params = "" + elem "tooltip" + type = BROWSER + pos = 0,0 + size = 999x999 + anchor1 = none + anchor2 = none + is-visible = false + saved-params = "" + +window "mapwindow" + elem "mapwindow" + type = MAIN + pos = 0,0 + size = 1024x1024 + anchor1 = none + anchor2 = none + saved-params = "pos;size;is-minimized;is-maximized" + is-pane = true + elem "map" + type = MAP + pos = 0,0 + size = 1024x1024 + anchor1 = 0,0 + anchor2 = 100,100 + font-family = "Arial" + font-size = 7 + text-color = none + is-default = true + style=".center { text-align: center; } .maptext { font-family: 'Small Fonts'; font-size: 7px; -dm-text-outline: 1px black; color: white; line-height: 1.1; } .command_headset { font-weight: bold; font-size: 8px; } .small { font-size: 6px; } .big { font-size: 8px; } .reallybig { font-size: 8px; } .extremelybig { font-size: 8px; } .greentext { color: #00FF00; font-size: 7px; } .redtext { color: #FF0000; font-size: 7px; } .clown { color: #FF69Bf; font-size: 7px; font-weight: bold; } .his_grace { color: #15D512; } .hypnophrase { color: #0d0d0d; font-weight: bold; } .yell { font-weight: bold; } .italics { font-size: 6px; }" + +window "infowindow" + elem "infowindow" + type = MAIN + pos = 0,0 + size = 640x480 + anchor1 = none + anchor2 = none + saved-params = "pos;size;is-minimized;is-maximized" + is-pane = true + elem "info" + type = CHILD + pos = 0,30 + size = 640x445 + anchor1 = 0,0 + anchor2 = 100,100 + saved-params = "splitter" + left = "statwindow" + right = "outputwindow" + is-vert = false + elem "changelog" + type = BUTTON + pos = 16,5 + size = 104x20 + anchor1 = 3,0 + anchor2 = 19,0 + saved-params = "is-checked" + text = "Changelog" + command = "changelog" + elem "rules" + type = BUTTON + pos = 120,5 + size = 100x20 + anchor1 = 19,0 + anchor2 = 34,0 + saved-params = "is-checked" + text = "Rules" + command = "rules" + elem "wiki" + type = BUTTON + pos = 220,5 + size = 100x20 + anchor1 = 34,0 + anchor2 = 50,0 + saved-params = "is-checked" + text = "Wiki" + command = "wiki" + elem "forum" + type = BUTTON + pos = 320,5 + size = 100x20 + anchor1 = 50,0 + anchor2 = 66,0 + saved-params = "is-checked" + text = "Forum" + command = "forum" + elem "github" + type = BUTTON + pos = 420,5 + size = 100x20 + anchor1 = 66,0 + anchor2 = 81,0 + saved-params = "is-checked" + text = "Github" + command = "github" + elem "report-issue" + type = BUTTON + pos = 520,5 + size = 100x20 + anchor1 = 81,0 + anchor2 = 97,0 + saved-params = "is-checked" + text = "Report Issue" + command = "report-issue" + +window "outputwindow" + elem "outputwindow" + type = MAIN + pos = 0,0 + size = 640x480 + anchor1 = none + anchor2 = none + saved-params = "pos;size;is-minimized;is-maximized" + is-pane = true + elem "input" + type = INPUT + pos = 2,460 + size = 595x20 + anchor1 = 0,100 + anchor2 = 100,100 + background-color = #d3b5b5 + is-default = true + border = sunken + saved-params = "command" + elem "saybutton" + type = BUTTON + pos = 600,460 + size = 37x20 + anchor1 = 100,100 + anchor2 = none + saved-params = "is-checked" + text = "Chat" + command = ".winset \"saybutton.is-checked=true ? input.command=\"!say \\\"\" : input.command=\"" + is-flat = true + button-type = pushbox + elem "browseroutput" + type = BROWSER + pos = 0,0 + size = 640x456 + anchor1 = 0,0 + anchor2 = 100,100 + background-color = #ffffff + is-visible = false + is-disabled = true + saved-params = "" + auto-format = false + elem "output" + type = OUTPUT + pos = 0,0 + size = 640x456 + anchor1 = 0,0 + anchor2 = 100,100 + is-default = true + saved-params = "" + +window "popupwindow" + elem "popupwindow" + type = MAIN + pos = 281,0 + size = 120x120 + anchor1 = none + anchor2 = none + background-color = none + is-visible = false + saved-params = "pos;size;is-minimized;is-maximized" + statusbar = false + can-resize = false + +window "preferences_window" + elem "preferences_window" + type = MAIN + pos = 281,0 + size = 1280x1000 + anchor1 = none + anchor2 = none + is-visible = false + saved-params = "pos;size;is-minimized;is-maximized" + statusbar = false + elem "preferences_browser" + type = BROWSER + pos = 0,0 + size = 960x1000 + anchor1 = 0,0 + anchor2 = 75,100 + saved-params = "" + elem "character_preview_map" + type = MAP + pos = 960,0 + size = 320x1000 + anchor1 = 75,0 + anchor2 = 100,100 + right-click = true + saved-params = "zoom;letterbox;zoom-mode" + +window "statwindow" + elem "statwindow" + type = MAIN + pos = 281,0 + size = 640x480 + anchor1 = none + anchor2 = none + saved-params = "pos;size;is-minimized;is-maximized" + is-pane = true + elem "stat" + type = INFO + pos = 0,0 + size = 640x480 + anchor1 = 0,0 + anchor2 = 100,100 + is-default = true + saved-params = "" + diff --git a/interface/stylesheet.dm b/interface/stylesheet.dm index 9c35ddb75f..f375fea88f 100644 --- a/interface/stylesheet.dm +++ b/interface/stylesheet.dm @@ -2,7 +2,7 @@ /// !!!!!!!!!!HEY LISTEN!!!!!!!!!!!!!!!!!!!!!!!! /// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// If you modify this file you ALSO need to modify code/modules/goonchat/browserAssets/browserOutput.css +// If you modify this file you ALSO need to modify code/modules/goonchat/browserAssets/browserOutput.css and browserOutput_white.css // BUT you have to use PX font sizes with are on a x8 scale of these font sizes // Sample font-size: DM: 8 CSS: 64px @@ -26,18 +26,24 @@ em {font-style: normal; font-weight: bold;} .prefix { font-weight: bold;} .ooc { font-weight: bold;} +.looc {color: #6699CC; font-weight: bold;} +.antagooc {color: #b8002e; font-weight: bold;} .adminobserverooc {color: #0099cc; font-weight: bold;} .adminooc {color: #700038; font-weight: bold;} .adminobserver {color: #996600; font-weight: bold;} +.adminsay {color: #FF4500; font-weight: bold;} .admin {color: #386aff; font-weight: bold;} +.mentorsay {color: #E236D8; font-weight: bold;} +.mentorsay_admin {color: #8A2BE2; font-weight: bold;} + .name { font-weight: bold;} .say {} .deadsay {color: #5c00e6;} -.binarysay {color: #20c20e; background-color: #000000; display: block;} -.binarysay a {color: #00ff00;} +.binarysay {color: #20c20e; background-color: #000000; display: block;} +.binarysay a {color: #00ff00;} .binarysay a:active, .binarysay a:visited {color: #88ff88;} .radio {color: #008000;} .sciradio {color: #993399;} @@ -50,8 +56,8 @@ em {font-style: normal; font-weight: bold;} .syndradio {color: #6d3f40;} .centcomradio {color: #686868;} .aiprivradio {color: #ff00ff;} -.redteamradio {color: #ff0000;} -.blueteamradio {color: #0000ff;} +.redteamradio {color: #ff0000;} +.blueteamradio {color: #0000ff;} .yell { font-weight: bold;} @@ -65,13 +71,13 @@ h1.alert, h2.alert {color: #000000;} .disarm {color: #990000;} .passive {color: #660000;} -.userdanger {color: #ff0000; font-weight: bold; font-size: 3;} +.userdanger {color: #ff0000; font-weight: bold; font-size: 3;} .danger {color: #ff0000; font-weight: bold;} .tinydanger {color: #ff0000; font-size: 85%;} .smalldanger {color: #ff0000; font-size: 90%;} .warning {color: #ff0000; font-style: italic;} .boldwarning {color: #ff0000; font-style: italic; font-weight: bold} -.announce {color: #228b22; font-weight: bold;} +.announce {color: #228b22; font-weight: bold;} .boldannounce {color: #ff0000; font-weight: bold;} .greenannounce {color: #00ff00; font-weight: bold;} .rose {color: #ff5050;} @@ -81,29 +87,31 @@ h1.alert, h2.alert {color: #000000;} .smallnotice {color: #000099; font-size: 90%;} .smallnoticeital {color: #000099; font-style: italic; font-size: 90%;} .boldnotice {color: #000099; font-weight: bold;} +.hear {color: #000099; font-style: italic;} .adminnotice {color: #0000ff;} -.adminhelp {color: #ff0000; font-weight: bold;} +.adminhelp {color: #ff0000; font-weight: bold;} .unconscious {color: #0000ff; font-weight: bold;} .suicide {color: #ff5050; font-style: italic;} .green {color: #03ff39;} -.nicegreen {color: #14a833;} +.nicegreen {color: #14a833;} .shadowling {color: #3b2769;} .cult {color: #960000;} -.cultlarge {color: #960000; font-weight: bold; font-size: 3;} -.narsie {color: #960000; font-weight: bold; font-size: 15;} -.narsiesmall {color: #960000; font-weight: bold; font-size: 6;} -.colossus {color: #7F282A; font-size: 5;} -.hierophant {color: #660099; font-weight: bold; font-style: italic;} -.hierophant_warning {color: #660099; font-style: italic;} +.cultlarge {color: #960000; font-weight: bold; font-size: 3;} +.narsie {color: #960000; font-weight: bold; font-size: 15;} +.narsiesmall {color: #960000; font-weight: bold; font-size: 6;} +.colossus {color: #7F282A; font-size: 5;} +.hierophant {color: #660099; font-weight: bold; font-style: italic;} +.hierophant_warning {color: #660099; font-style: italic;} .purple {color: #5e2d79;} .holoparasite {color: #35333a;} +.bounty {color: #ab6613; font-style: italic;} .revennotice {color: #1d2953;} .revenboldnotice {color: #1d2953; font-weight: bold;} -.revenbignotice {color: #1d2953; font-weight: bold; font-size: 3;} +.revenbignotice {color: #1d2953; font-weight: bold; font-size: 3;} .revenminor {color: #823abb} .revenwarning {color: #760fbb; font-style: italic;} -.revendanger {color: #760fbb; font-weight: bold; font-size: 3;} +.revendanger {color: #760fbb; font-weight: bold; font-size: 3;} .umbra {color: #5000A0;} .umbra_emphasis {color: #5000A0; font-weight: bold; font-style: italic;} .umbra_large {color: #5000A0; font-size: 3; font-weight: bold; font-style: italic;} @@ -133,8 +141,7 @@ h1.alert, h2.alert {color: #000000;} .neovgre {color: #6E001A; font-weight: bold; font-style: italic;} .neovgre_small {color: #6E001A;} -.newscaster {color: #800000;} -.ghostalert {color: #5c00e6; font-style: italic; font-weight: bold;} +.ghostalert {color: #5c00e6; font-style: italic; font-weight: bold;} .alien {color: #543354;} .noticealien {color: #00c000;} @@ -149,7 +156,7 @@ h1.alert, h2.alert {color: #000000;} .papyrus {font-family: "Papyrus", cursive, sans-serif;} .robot {font-family: "Courier New", cursive, sans-serif;} -.command_headset {font-weight: bold; font-size: 3;} +.command_headset {font-weight: bold; font-size: 3;} .small {font-size: 1;} .big {font-size: 3;} .reallybig {font-size: 4;} @@ -157,9 +164,9 @@ h1.alert, h2.alert {color: #000000;} .greentext {color: #00FF00; font-size: 3;} .redtext {color: #FF0000; font-size: 3;} .yellowtext {color: #FFCC00; font-size: 3;} -.clown {color: #FF69Bf; font-size: 3; font-family: "Comic Sans MS", cursive, sans-serif; font-weight: bold;} -.his_grace {color: #15D512; font-family: "Courier New", cursive, sans-serif; font-style: italic;} -.spooky {color: #FF9100;} +.clown {color: #FF69Bf; font-size: 3; font-family: "Comic Sans MS", cursive, sans-serif; font-weight: bold;} +.singing {font-family: "Trebuchet MS", cursive, sans-serif; font-style: italic;} +.his_grace {color: #15D512; font-family: "Courier New", cursive, sans-serif; font-style: italic;} .velvet {color: #660015; font-weight: bold; animation: velvet 5000ms infinite;} @keyframes velvet { 0% { color: #400020; } @@ -169,28 +176,29 @@ h1.alert, h2.alert {color: #000000;} 100% { color: #400020; } } -.hypnophrase {color: #3bb5d3; font-weight: bold; animation: hypnocolor 1500ms infinite;} -@keyframes hypnocolor { - 0% { color: #0d0d0d; } - 25% { color: #410194; } - 50% { color: #7f17d8; } - 75% { color: #410194; } - 100% { color: #3bb5d3; } +.hypnophrase {color: #3bb5d3; font-weight: bold; animation: hypnocolor 1500ms infinite; animation-direction: alternate;} + @keyframes hypnocolor { + 0% {color: #0d0d0d;} + 25% {color: #410194;} + 50% {color: #7f17d8;} + 75% {color: #410194;} + 100% {color: #3bb5d3;} } -.phobia {color: #dd0000; font-weight: bold; animation: phobia 750ms infinite;} + +.phobia {color: #dd0000; font-weight: bold; animation: phobia 750ms infinite;} @keyframes phobia { - 0% { color: #0d0d0d; } - 50% { color: #dd0000; } - 100% { color: #0d0d0d; } + 0% {color: #0d0d0d;} + 50% {color: #dd0000;} + 100% {color: #0d0d0d;} } -.icon {height: 1em; width: auto;} +.icon {height: 1em; width: auto;} .memo {color: #638500; text-align: center;} .memoedit {text-align: center; font-size: 2;} -.abductor {color: #800080; font-style: italic;} -.mind_control {color: #A00D6F; font-size: 3; font-weight: bold; font-style: italic;} +.abductor {color: #800080; font-style: italic;} +.mind_control {color: #A00D6F; font-size: 3; font-weight: bold; font-style: italic;} .slime {color: #00CED1;} .drone {color: #848482;} .monkey {color: #975032;} diff --git a/modular_citadel/code/modules/client/loadout/__donator.dm b/modular_citadel/code/modules/client/loadout/__donator.dm index 06783df03d..478fb41a23 100644 --- a/modular_citadel/code/modules/client/loadout/__donator.dm +++ b/modular_citadel/code/modules/client/loadout/__donator.dm @@ -40,12 +40,6 @@ path = /obj/item/bikehorn/airhorn ckeywhitelist = list("kevinz000") -/datum/gear/donator/cebusoap - name = "Cebutris' soap" - slot = SLOT_IN_BACKPACK - path = /obj/item/custom/ceb_soap - ckeywhitelist = list("cebutris") - /datum/gear/donator/kiaracloak name = "Kiara's cloak" slot = SLOT_NECK diff --git a/modular_citadel/code/modules/custom_loadout/custom_items.dm b/modular_citadel/code/modules/custom_loadout/custom_items.dm index bd377d6081..374312a26a 100644 --- a/modular_citadel/code/modules/custom_loadout/custom_items.dm +++ b/modular_citadel/code/modules/custom_loadout/custom_items.dm @@ -1,26 +1,8 @@ - //For custom items. // Unless there's a digitigrade version make sure you add mutantrace_variation = NONE to all clothing/under and shoes - Pooj // Digitigrade stuff is uniform_digi.dmi and digishoes.dmi in icons/mob -/obj/item/custom/ceb_soap - name = "Cebutris' Soap" - desc = "A generic bar of soap that doesn't really seem to work right." - gender = PLURAL - icon = 'icons/obj/custom.dmi' - icon_state = "cebu" - w_class = WEIGHT_CLASS_TINY - item_flags = NOBLUDGEON - -/obj/item/soap/cebu //real versions, for admin shenanigans. Adminspawn only - desc = "A bright blue bar of soap that smells of wolves" - icon = 'icons/obj/custom.dmi' - icon_state = "cebu" - -/obj/item/soap/cebu/fast //speedyquick cleaning version. Still not as fast as Syndiesoap. Adminspawn only. - cleanspeed = 15 - /obj/item/clothing/neck/cloak/inferno name = "Kiara's Cloak" desc = "The design on this seems a little too familiar." @@ -316,7 +298,7 @@ /obj/item/clothing/neck/petcollar/naomi name = "worn pet collar" - desc = "a pet collar that looks well used." + desc = "A pet collar that looks well used." /obj/item/clothing/neck/cloak/green name = "Generic Green Cloak" diff --git a/modular_citadel/code/modules/mentor/mentorsay.dm b/modular_citadel/code/modules/mentor/mentorsay.dm index c13e3c6ef3..dc1281abe0 100644 --- a/modular_citadel/code/modules/mentor/mentorsay.dm +++ b/modular_citadel/code/modules/mentor/mentorsay.dm @@ -13,7 +13,7 @@ log_mentor("MSAY: [key_name(src)] : [msg]") if(check_rights_for(src, R_ADMIN,0)) - msg = "MENTOR: [key_name(src, 0, 0)]: [msg]" + msg = "MENTOR: [key_name(src, 0, 0)]: [msg]" else - msg = "MENTOR: [key_name(src, 0, 0)]: [msg]" - to_chat(GLOB.admins | GLOB.mentors, msg) \ No newline at end of file + msg = "MENTOR: [key_name(src, 0, 0)]: [msg]" + to_chat(GLOB.admins | GLOB.mentors, msg, confidential = TRUE) diff --git a/modular_citadel/code/modules/mob/cit_emotes.dm b/modular_citadel/code/modules/mob/cit_emotes.dm index e58c6bda30..3b442af1c7 100644 --- a/modular_citadel/code/modules/mob/cit_emotes.dm +++ b/modular_citadel/code/modules/mob/cit_emotes.dm @@ -3,10 +3,6 @@ /mob var/nextsoundemote = 1 -//Disables the custom emote blacklist from TG that normally applies to slimes. -/datum/emote/living/custom - mob_type_blacklist_typecache = list(/mob/living/brain) - /datum/emote/living/insult key = "insult" key_third_person = "insults" @@ -74,7 +70,6 @@ emote_type = EMOTE_AUDIBLE muzzle_ignore = TRUE restraint_check = TRUE - mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/snap/run_emote(mob/living/user, params) if(!(. = ..())) @@ -91,7 +86,6 @@ emote_type = EMOTE_AUDIBLE muzzle_ignore = TRUE restraint_check = TRUE - mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/snap2/run_emote(mob/living/user, params) if(!(. = ..())) @@ -108,7 +102,6 @@ emote_type = EMOTE_AUDIBLE muzzle_ignore = TRUE restraint_check = TRUE - mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/snap3/run_emote(mob/living/user, params) if(!(. = ..())) @@ -125,7 +118,6 @@ emote_type = EMOTE_AUDIBLE muzzle_ignore = FALSE restraint_check = FALSE - mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/awoo/run_emote(mob/living/user, params) if(!(. = ..())) @@ -142,7 +134,6 @@ emote_type = EMOTE_AUDIBLE muzzle_ignore = FALSE restraint_check = FALSE - mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/nya/run_emote(mob/living/user, params) if(!(. = ..())) @@ -159,7 +150,6 @@ emote_type = EMOTE_AUDIBLE muzzle_ignore = FALSE restraint_check = FALSE - mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/weh/run_emote(mob/living/user, params) if(!(. = ..())) @@ -176,7 +166,6 @@ emote_type = EMOTE_AUDIBLE muzzle_ignore = FALSE restraint_check = FALSE - mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/peep/run_emote(mob/living/user, params) if(!(. = ..())) @@ -200,7 +189,6 @@ emote_type = EMOTE_AUDIBLE muzzle_ignore = FALSE restraint_check = FALSE - mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/mothsqueak/run_emote(mob/living/user, params) if(!(. = ..())) @@ -217,7 +205,6 @@ emote_type = EMOTE_AUDIBLE muzzle_ignore = FALSE restraint_check = FALSE - mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/merp/run_emote(mob/living/user, params) if(!(. = ..())) @@ -234,7 +221,6 @@ emote_type = EMOTE_AUDIBLE muzzle_ignore = FALSE restraint_check = FALSE - mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/bark/run_emote(mob/living/user, params) if(!(. = ..())) @@ -252,7 +238,6 @@ emote_type = EMOTE_AUDIBLE muzzle_ignore = FALSE restraint_check = FALSE - mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/squish/run_emote(mob/living/user, params) if(!(. = ..())) diff --git a/modular_citadel/icons/mob/citadel_refs/furry_parts_greyscale.dmi b/modular_citadel/icons/mob/citadel_refs/furry_parts_greyscale.dmi deleted file mode 100644 index c8d5ceb0a6..0000000000 Binary files a/modular_citadel/icons/mob/citadel_refs/furry_parts_greyscale.dmi and /dev/null differ diff --git a/modular_citadel/icons/mob/mutant_bodyparts.dmi b/modular_citadel/icons/mob/mutant_bodyparts.dmi deleted file mode 100644 index 6098dd3567..0000000000 Binary files a/modular_citadel/icons/mob/mutant_bodyparts.dmi and /dev/null differ diff --git a/sound/effects/creak1.ogg b/sound/effects/creak1.ogg new file mode 100644 index 0000000000..0cad4802ff Binary files /dev/null and b/sound/effects/creak1.ogg differ diff --git a/sound/effects/creak2.ogg b/sound/effects/creak2.ogg new file mode 100644 index 0000000000..707bf39e33 Binary files /dev/null and b/sound/effects/creak2.ogg differ diff --git a/sound/effects/creak3.ogg b/sound/effects/creak3.ogg new file mode 100644 index 0000000000..88ff37a339 Binary files /dev/null and b/sound/effects/creak3.ogg differ diff --git a/sound/effects/explosioncreak1.ogg b/sound/effects/explosioncreak1.ogg new file mode 100644 index 0000000000..474f5febb5 Binary files /dev/null and b/sound/effects/explosioncreak1.ogg differ diff --git a/sound/effects/explosioncreak2.ogg b/sound/effects/explosioncreak2.ogg new file mode 100644 index 0000000000..75d323eb06 Binary files /dev/null and b/sound/effects/explosioncreak2.ogg differ diff --git a/sound/machines/card_slide.ogg b/sound/machines/card_slide.ogg new file mode 100644 index 0000000000..f97ed15fbf Binary files /dev/null and b/sound/machines/card_slide.ogg differ diff --git a/sound/machines/twobeep_high.ogg b/sound/machines/twobeep_high.ogg new file mode 100644 index 0000000000..b97b39a4f0 Binary files /dev/null and b/sound/machines/twobeep_high.ogg differ diff --git a/strings/traumas.json b/strings/traumas.json index 58170bd55a..f8fed95c98 100644 --- a/strings/traumas.json +++ b/strings/traumas.json @@ -131,7 +131,8 @@ "@pick(semicolon)*weh", "@pick(semicolon)My balls finally feel full, again.", "@pick(semicolon)Assaltign a sec osficer aren't crime if ur @pick(roles)", - ";SEC I SPILED MU JICE HELELPH HELPJ JLEP HELP" + ";SEC I SPILED MU JICE HELELPH HELPJ JLEP HELP", + "@pick(semicolon) atmos is chemistyr is radation fast air is FASTER cheemsitry and FASTER RADIATION AND FASTER DEATH!!!" ], "mutations": [ @@ -199,7 +200,7 @@ "abdoocters", "revinent" ], - + "bug": [ "", "IS TIS A BUG??", @@ -207,7 +208,7 @@ "BUG!!!", "HUE, FEATURE!!" ], - + "semicolon": [ "", ";", @@ -271,7 +272,7 @@ "arrdee", "sek" ], - + "cargo": [ "GUNS", "HATS", @@ -279,7 +280,7 @@ "MEMES", "GLOWY CYSTAL" ], - + "s_roles": [ "ert", "shadowlig", diff --git a/tgstation.dme b/tgstation.dme index 13a426045b..1776b88bdf 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -96,6 +96,7 @@ #include "code\__DEFINES\reagents_specific_heat.dm" #include "code\__DEFINES\research.dm" #include "code\__DEFINES\robots.dm" +#include "code\__DEFINES\rockpaperscissors.dm" #include "code\__DEFINES\role_preferences.dm" #include "code\__DEFINES\rust_g.dm" #include "code\__DEFINES\say.dm" @@ -284,6 +285,7 @@ #include "code\controllers\configuration\entries\general.dm" #include "code\controllers\configuration\entries\plushies.dm" #include "code\controllers\configuration\entries\policy.dm" +#include "code\controllers\configuration\entries\resources.dm" #include "code\controllers\subsystem\acid.dm" #include "code\controllers\subsystem\adjacent_air.dm" #include "code\controllers\subsystem\air.dm" @@ -327,7 +329,6 @@ #include "code\controllers\subsystem\parallax.dm" #include "code\controllers\subsystem\pathfinder.dm" #include "code\controllers\subsystem\persistence.dm" -#include "code\controllers\subsystem\ping.dm" #include "code\controllers\subsystem\profiler.dm" #include "code\controllers\subsystem\radiation.dm" #include "code\controllers\subsystem\radio.dm" @@ -363,6 +364,7 @@ #include "code\controllers\subsystem\processing\status_effects.dm" #include "code\controllers\subsystem\processing\weather.dm" #include "code\controllers\subsystem\processing\wet_floors.dm" +#include "code\datums\accents.dm" #include "code\datums\action.dm" #include "code\datums\ai_laws.dm" #include "code\datums\armor.dm" @@ -435,6 +437,7 @@ #include "code\datums\components\footstep.dm" #include "code\datums\components\fried.dm" #include "code\datums\components\gps.dm" +#include "code\datums\components\honkspam.dm" #include "code\datums\components\identification.dm" #include "code\datums\components\igniter.dm" #include "code\datums\components\infective.dm" @@ -442,6 +445,7 @@ #include "code\datums\components\killerqueen.dm" #include "code\datums\components\knockback.dm" #include "code\datums\components\knockoff.dm" +#include "code\datums\components\label.dm" #include "code\datums\components\lifesteal.dm" #include "code\datums\components\lockon_aiming.dm" #include "code\datums\components\magnetic_catch.dm" @@ -1095,6 +1099,7 @@ #include "code\game\objects\items\devices\paicard.dm" #include "code\game\objects\items\devices\pipe_painter.dm" #include "code\game\objects\items\devices\polycircuit.dm" +#include "code\game\objects\items\devices\portable_chem_mixer.dm" #include "code\game\objects\items\devices\powersink.dm" #include "code\game\objects\items\devices\pressureplates.dm" #include "code\game\objects\items\devices\quantum_keycard.dm" @@ -1706,11 +1711,12 @@ #include "code\modules\assembly\signaler.dm" #include "code\modules\assembly\timer.dm" #include "code\modules\assembly\voice.dm" -#include "code\modules\asset_cache\asset_cache.dm" #include "code\modules\asset_cache\asset_cache_client.dm" #include "code\modules\asset_cache\asset_cache_item.dm" #include "code\modules\asset_cache\asset_list.dm" #include "code\modules\asset_cache\asset_list_items.dm" +#include "code\modules\asset_cache\transports\asset_transport.dm" +#include "code\modules\asset_cache\transports\webroot_transport.dm" #include "code\modules\atmospherics\multiz.dm" #include "code\modules\atmospherics\environmental\LINDA_fire.dm" #include "code\modules\atmospherics\environmental\LINDA_system.dm" @@ -1857,7 +1863,6 @@ #include "code\modules\client\client_colour.dm" #include "code\modules\client\client_defines.dm" #include "code\modules\client\client_procs.dm" -#include "code\modules\client\darkmode.dm" #include "code\modules\client\message.dm" #include "code\modules\client\player_details.dm" #include "code\modules\client\preferences.dm" @@ -2105,7 +2110,6 @@ #include "code\modules\food_and_drinks\recipes\tablecraft\recipes_spaghetti.dm" #include "code\modules\games\cas.dm" #include "code\modules\games\unum.dm" -#include "code\modules\goonchat\browserOutput.dm" #include "code\modules\holiday\easter.dm" #include "code\modules\holiday\holidays.dm" #include "code\modules\holiday\halloween\bartholomew.dm" @@ -2776,6 +2780,7 @@ #include "code\modules\modular_computers\file_system\programs\alarm.dm" #include "code\modules\modular_computers\file_system\programs\arcade.dm" #include "code\modules\modular_computers\file_system\programs\atmosscan.dm" +#include "code\modules\modular_computers\file_system\programs\borg_monitor.dm" #include "code\modules\modular_computers\file_system\programs\card.dm" #include "code\modules\modular_computers\file_system\programs\cargobounty.dm" #include "code\modules\modular_computers\file_system\programs\configurator.dm" @@ -2802,6 +2807,7 @@ #include "code\modules\modular_computers\hardware\portable_disk.dm" #include "code\modules\modular_computers\hardware\printer.dm" #include "code\modules\modular_computers\hardware\recharger.dm" +#include "code\modules\modular_computers\hardware\sensor_package.dm" #include "code\modules\modular_computers\NTNet\NTNRC\conversation.dm" #include "code\modules\movespeed\_movespeed_modifier.dm" #include "code\modules\movespeed\modifiers\components.dm" @@ -2846,6 +2852,7 @@ #include "code\modules\NTNet\network.dm" #include "code\modules\NTNet\relays.dm" #include "code\modules\NTNet\services\_service.dm" +#include "code\modules\paperwork\carbonpaper.dm" #include "code\modules\paperwork\clipboard.dm" #include "code\modules\paperwork\contract.dm" #include "code\modules\paperwork\filingcabinet.dm" @@ -2859,6 +2866,7 @@ #include "code\modules\paperwork\pen.dm" #include "code\modules\paperwork\photocopier.dm" #include "code\modules\paperwork\stamps.dm" +#include "code\modules\paperwork\ticketmachine.dm" #include "code\modules\photography\_pictures.dm" #include "code\modules\photography\camera\camera.dm" #include "code\modules\photography\camera\camera_image_capturing.dm" @@ -3425,6 +3433,7 @@ #include "code\modules\tgui\states\always.dm" #include "code\modules\tgui\states\conscious.dm" #include "code\modules\tgui\states\contained.dm" +#include "code\modules\tgui\states\debug.dm" #include "code\modules\tgui\states\deep_inventory.dm" #include "code\modules\tgui\states\default.dm" #include "code\modules\tgui\states\hands.dm" @@ -3437,6 +3446,11 @@ #include "code\modules\tgui\states\physical.dm" #include "code\modules\tgui\states\self.dm" #include "code\modules\tgui\states\zlevel.dm" +#include "code\modules\tgui_panel\audio.dm" +#include "code\modules\tgui_panel\external.dm" +#include "code\modules\tgui_panel\telemetry.dm" +#include "code\modules\tgui_panel\tgui_panel.dm" +#include "code\modules\tgui_panel\to_chat.dm" #include "code\modules\tooltip\tooltip.dm" #include "code\modules\unit_tests\_unit_tests.dm" #include "code\modules\uplink\uplink_devices.dm" diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml index 9fd4db9fd2..e3c3144e5b 100644 --- a/tgui/.eslintrc.yml +++ b/tgui/.eslintrc.yml @@ -114,7 +114,7 @@ rules: ## Require return statements to either always or never specify values # consistent-return: error ## Enforce consistent brace style for all control statements - curly: [error, all] + curly: [error, multi-line] ## Require default cases in switch statements # default-case: error ## Enforce default parameters to be last @@ -374,6 +374,7 @@ rules: ignorePattern: '^(import\s.+\sfrom\s|.*require\()', ignoreUrls: true, ignoreRegExpLiterals: true, + ignoreStrings: true, }] ## Enforce a maximum number of lines per file # max-lines: error diff --git a/tgui/.gitattributes b/tgui/.gitattributes index 9382416e69..d9cdc20fbd 100644 --- a/tgui/.gitattributes +++ b/tgui/.gitattributes @@ -17,3 +17,4 @@ bin/tgui text eol=lf ## Treat bundles as binary and ignore them during conflicts *.bundle.* binary merge=tgui-merge-bundle +*.chunk.* binary merge=tgui-merge-bundle diff --git a/tgui/bin/tgui b/tgui/bin/tgui index 97a86159e6..5627b40413 100755 --- a/tgui/bin/tgui +++ b/tgui/bin/tgui @@ -67,7 +67,7 @@ task-clean() { task-validate-build() { cd "${base_dir}" local diff - diff="$(git diff packages/tgui/public/tgui.bundle.*)" + diff="$(git diff packages/tgui/public/*)" if [[ -n ${diff} ]]; then echo "Error: our build differs from the build committed into git." echo "Please rebuild tgui." diff --git a/tgui/docs/component-reference.md b/tgui/docs/component-reference.md index ff1b4e7dfd..23909c639f 100644 --- a/tgui/docs/component-reference.md +++ b/tgui/docs/component-reference.md @@ -227,7 +227,7 @@ be truncated with an ellipsis. Be careful however, because this prop breaks the baseline alignment. - `title: string` - A native browser tooltip, which appears when hovering over the button. -- `content/children: any` - Content to render inside the button. +- `children: any` - Content to render inside the button. - `onClick: function` - Called when element is clicked. ### `Button.Checkbox` @@ -389,7 +389,9 @@ to the left, and certain elements to the right: - ``` @@ -625,7 +627,9 @@ to perform some sort of action), there is a way to do that: + )}> Content @@ -643,7 +647,7 @@ to perform some sort of action), there is a way to do that: - `label: string` - Item label. - `color: string` - Sets the color of the text. - `buttons: any` - Buttons to render aside the content. -- `content/children: any` - Content of this labeled item. +- `children: any` - Content of this labeled item. ### `LabeledList.Divider` @@ -748,7 +752,7 @@ percentage and how filled the bar is. - `ranges: { color: [from, to] }` - Applies a `color` to the progress bar based on whether the value lands in the range between `from` and `to`. - `color: string` - Color of the progress bar. -- `content/children: any` - Content to render inside the progress bar. +- `children: any` - Content to render inside the progress bar. ### `Section` @@ -773,7 +777,9 @@ If you want to have a button on the right side of an section title
+ )}> Here you can order supply crates.
@@ -784,7 +790,10 @@ If you want to have a button on the right side of an section title - `level: number` - Section level in hierarchy. Default is 1, higher number means deeper level of nesting. Must be an integer number. - `buttons: any` - Buttons to render aside the section title. -- `content/children: any` - Content of this section. +- `fill: boolean` - If true, fills all available vertical space. +- `fitted: boolean` - If true, removes all section padding. +- `scrollable: boolean` - Shows or hides the scrollbar. +- `children: any` - Content of this section. ### `Slider` @@ -953,7 +962,7 @@ Usage: **Props:** - `position: string` - Tooltip position. -- `content/children: string` - Content of the tooltip. Must be a plain string. +- `content: string` - Content of the tooltip. Must be a plain string. Fragments or other elements are **not** supported. ## `tgui/layouts` @@ -978,6 +987,7 @@ Example: **Props:** +- See inherited props: [Box](#box) - `className: string` - Applies a CSS class to the element. - `theme: string` - A name of the theme. - For a list of themes, see `packages/tgui/styles/themes`. @@ -995,6 +1005,8 @@ Can be scrollable. **Props:** +- See inherited props: [Box](#box) - `className: string` - Applies a CSS class to the element. +- `fitted: boolean` - If true, removes all padding. - `scrollable: boolean` - Shows or hides the scrollbar. - `children: any` - Main content of your window. diff --git a/tgui/docs/converting-old-tgui-interfaces.md b/tgui/docs/converting-old-tgui-interfaces.md index fe2feebfee..a42724e05c 100644 --- a/tgui/docs/converting-old-tgui-interfaces.md +++ b/tgui/docs/converting-old-tgui-interfaces.md @@ -73,7 +73,6 @@ This might look a bit intimidating compared to the reactive part but it's not as You don't really need to know all this to understand how to use it, but I find it helps with understanding when things go wrong. Ractive conditionals can have an `else` as well - ```ractive {{#if data.condition}} value @@ -117,7 +116,7 @@ and you can mix string literals, values, and tags as well. Ractive has loops for iterating over data and inserting something for each member of an array or object -```ractive +``` {{#each data.list_of_foo}} foo {{number}} is here. {{/each}} @@ -136,7 +135,6 @@ Objects are represented by `{}`, arrays by `[]` `list("bla", "blo")` would become `["bla", "blo"]` and `list("foo" = 1, "bar" = 2)` would become `{"foo": 1, "bar": 2}` First things first, above the `return` of the function you're making the interface in, you're going to want to add something like this - ```jsx const things = data.things || []; ``` @@ -144,7 +142,6 @@ const things = data.things || []; This ensures that you'll never be reading a null entry by mistake. Substitute `{}` for objects as appropriate. If it's an array, you'll want to do this in the template - ```jsx {things.map(thing => ( @@ -190,7 +187,7 @@ const fooArray = toArray(fooObject); Also occasionally you'd see an else: -```ractive +``` {{#each data.potentially_empty_list}} Thing "{{name}}" is in this list! {{else}} @@ -223,7 +220,7 @@ This will be a reference of tgui components and the tgui-next equivalent. Equivalent of `` is `
` -```ractive +``` Contents @@ -239,7 +236,7 @@ becomes A feature sometimes used is if `ui-display` has the `button` property, it will contain a `partial` command. This becomes the `buttons` property on `Section`: -```ractive +``` {{#partial button}} // lots more button bullshit here @@ -266,7 +263,7 @@ Very important to note `ui-section` is NOT the equivalent of `Section` `` does not have a direct equivalent, but the closest equivalent is `` -```ractive +``` No Power @@ -296,7 +293,7 @@ Also good to know that if you need the contents of a `LabeledList.Item` to be co `` has a direct equivalent in `` -```ractive +``` Notice stuff! @@ -314,7 +311,7 @@ becomes The equivalent of `ui-button` is `Button` but it works quite a bit differently. -```ractive +``` ( + new Color( + parseInt(hex.substr(1, 2), 16), + parseInt(hex.substr(3, 2), 16), + parseInt(hex.substr(5, 2), 16)) +); + +/** + * Linear interpolation of two colors. + */ +Color.lerp = (c1, c2, n) => ( + new Color( + (c2.r - c1.r) * n + c1.r, + (c2.g - c1.g) * n + c1.g, + (c2.b - c1.b) * n + c1.b, + (c2.a - c1.a) * n + c1.a) +); + +/** + * Loops up the color in the provided list of colors + * with linear interpolation. + */ +Color.lookup = (value, colors = []) => { + const len = colors.length; + if (len < 2) { + throw new Error('Needs at least two colors!'); + } + const scaled = value * (len - 1); + if (value < EPSILON) { + return colors[0]; + } + if (value >= 1 - EPSILON) { + return colors[len - 1]; + } + const ratio = scaled % 1; + const index = scaled | 0; + return Color.lerp(colors[index], colors[index + 1], ratio); +}; diff --git a/tgui/packages/common/events.js b/tgui/packages/common/events.js new file mode 100644 index 0000000000..6d590a3445 --- /dev/null +++ b/tgui/packages/common/events.js @@ -0,0 +1,42 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +export class EventEmitter { + constructor() { + this.listeners = {}; + } + + on(name, listener) { + this.listeners[name] = this.listeners[name] || []; + this.listeners[name].push(listener); + } + + off(name, listener) { + const listeners = this.listeners[name]; + if (!listeners) { + throw new Error(`There is no listeners for "${name}"`); + } + this.listeners[name] = listeners + .filter(existingListener => { + return existingListener !== listener; + }); + } + + emit(name, ...params) { + const listeners = this.listeners[name]; + if (!listeners) { + return; + } + for (let i = 0, len = listeners.length; i < len; i += 1) { + const listener = listeners[i]; + listener(...params); + } + } + + clear() { + this.listeners = {}; + } +} diff --git a/tgui/packages/common/keycodes.js b/tgui/packages/common/keycodes.js new file mode 100644 index 0000000000..8f18b154b5 --- /dev/null +++ b/tgui/packages/common/keycodes.js @@ -0,0 +1,86 @@ +/** + * All possible browser keycodes, in one file. + * + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +export const KEY_BACKSPACE = 8; +export const KEY_TAB = 9; +export const KEY_ENTER = 13; +export const KEY_SHIFT = 16; +export const KEY_CTRL = 17; +export const KEY_ALT = 18; +export const KEY_PAUSE = 19; +export const KEY_CAPSLOCK = 20; +export const KEY_ESCAPE = 27; +export const KEY_SPACE = 32; +export const KEY_PAGEUP = 33; +export const KEY_PAGEDOWN = 34; +export const KEY_END = 35; +export const KEY_HOME = 36; +export const KEY_LEFT = 37; +export const KEY_UP = 38; +export const KEY_RIGHT = 39; +export const KEY_DOWN = 40; +export const KEY_INSERT = 45; +export const KEY_DELETE = 46; +export const KEY_0 = 48; +export const KEY_1 = 49; +export const KEY_2 = 50; +export const KEY_3 = 51; +export const KEY_4 = 52; +export const KEY_5 = 53; +export const KEY_6 = 54; +export const KEY_7 = 55; +export const KEY_8 = 56; +export const KEY_9 = 57; +export const KEY_A = 65; +export const KEY_B = 66; +export const KEY_C = 67; +export const KEY_D = 68; +export const KEY_E = 69; +export const KEY_F = 70; +export const KEY_G = 71; +export const KEY_H = 72; +export const KEY_I = 73; +export const KEY_J = 74; +export const KEY_K = 75; +export const KEY_L = 76; +export const KEY_M = 77; +export const KEY_N = 78; +export const KEY_O = 79; +export const KEY_P = 80; +export const KEY_Q = 81; +export const KEY_R = 82; +export const KEY_S = 83; +export const KEY_T = 84; +export const KEY_U = 85; +export const KEY_V = 86; +export const KEY_W = 87; +export const KEY_X = 88; +export const KEY_Y = 89; +export const KEY_Z = 90; +export const KEY_F1 = 112; +export const KEY_F2 = 113; +export const KEY_F3 = 114; +export const KEY_F4 = 115; +export const KEY_F5 = 116; +export const KEY_F6 = 117; +export const KEY_F7 = 118; +export const KEY_F8 = 119; +export const KEY_F9 = 120; +export const KEY_F10 = 121; +export const KEY_F11 = 122; +export const KEY_F12 = 123; +export const KEY_SEMICOLON = 186; +export const KEY_EQUAL = 187; +export const KEY_COMMA = 188; +export const KEY_MINUS = 189; +export const KEY_PERIOD = 190; +export const KEY_SLASH = 191; +export const KEY_LEFT_BRACKET = 219; +export const KEY_BACKSLASH = 220; +export const KEY_RIGHT_BRACKET = 221; +export const KEY_QUOTE = 222; diff --git a/tgui/packages/common/package.json b/tgui/packages/common/package.json index fbd255abab..22560d92f9 100644 --- a/tgui/packages/common/package.json +++ b/tgui/packages/common/package.json @@ -1,6 +1,6 @@ { "private": true, "name": "common", - "version": "3.0.0", + "version": "4.1.0", "type": "module" } diff --git a/tgui/packages/common/perf.js b/tgui/packages/common/perf.js index 319b77cea3..8414971f93 100644 --- a/tgui/packages/common/perf.js +++ b/tgui/packages/common/perf.js @@ -1,21 +1,32 @@ /** * Ghetto performance measurement tools. * - * Uses NODE_ENV to redact itself from production bundles. + * Uses NODE_ENV to remove itself from production builds. * * @file * @copyright 2020 Aleksej Komarov * @license MIT */ -let markersByLabel = {}; +const FPS = 60; +const FRAME_DURATION = 1000 / FPS; + +// True if Performance API is supported +const supportsPerf = !!window.performance?.now; +// High precision markers +let hpMarkersByName = {}; +// Low precision markers +let lpMarkersByName = {}; /** * Marks a certain spot in the code for later measurements. */ -const mark = (label, timestamp) => { +const mark = (name, timestamp) => { if (process.env.NODE_ENV !== 'production') { - markersByLabel[label] = timestamp || Date.now(); + if (supportsPerf && !timestamp) { + hpMarkersByName[name] = performance.now(); + } + lpMarkersByName[name] = timestamp || Date.now(); } }; @@ -24,18 +35,23 @@ const mark = (label, timestamp) => { * * Use logger.log() to print the measurement. */ -const measure = (markerA, markerB) => { +const measure = (markerNameA, markerNameB) => { if (process.env.NODE_ENV !== 'production') { - return timeDiff( - markersByLabel[markerA], - markersByLabel[markerB]); + let markerA = hpMarkersByName[markerNameA]; + let markerB = hpMarkersByName[markerNameB]; + if (!markerA || !markerB) { + markerA = lpMarkersByName[markerNameA]; + markerB = lpMarkersByName[markerNameB]; + } + const duration = Math.abs(markerB - markerA); + return formatDuration(duration); } }; -const timeDiff = (startedAt, finishedAt) => { - const diff = Math.abs(finishedAt - startedAt); - const diffFrames = (diff / 16.6667).toFixed(2); - return `${diff}ms (${diffFrames} frames)`; +const formatDuration = duration => { + const durationInFrames = duration / FRAME_DURATION; + return duration.toFixed(duration < 10 ? 1 : 0) + 'ms ' + + '(' + durationInFrames.toFixed(2) + ' frames)'; }; export const perf = { diff --git a/tgui/packages/common/react.js b/tgui/packages/common/react.js index dba84b7b10..c0b24563f1 100644 --- a/tgui/packages/common/react.js +++ b/tgui/packages/common/react.js @@ -64,10 +64,10 @@ export const pureComponentHooks = { }; /** - * A helper to determine whether to render an item. + * A helper to determine whether the object is renderable by React. */ -export const isFalsy = value => { - return value === undefined - || value === null - || value === false; +export const canRender = value => { + return value !== undefined + && value !== null + && typeof value !== 'boolean'; }; diff --git a/tgui/packages/common/redux.js b/tgui/packages/common/redux.js index dc486ff7b8..12aacadb5f 100644 --- a/tgui/packages/common/redux.js +++ b/tgui/packages/common/redux.js @@ -4,6 +4,7 @@ * @license MIT */ +import { Component } from 'inferno'; import { compose } from './fp'; /** @@ -26,7 +27,9 @@ export const createStore = (reducer, enhancer) => { const dispatch = action => { currentState = reducer(currentState, action); - listeners.forEach(fn => fn()); + for (let i = 0; i < listeners.length; i++) { + listeners[i](); + } }; // This creates the initial store by causing each reducer to be called @@ -81,7 +84,7 @@ export const applyMiddleware = (...middlewares) => { export const combineReducers = reducersObj => { const keys = Object.keys(reducersObj); let hasChanged = false; - return (prevState, action) => { + return (prevState = {}, action) => { const nextState = { ...prevState }; for (let key of keys) { const reducer = reducersObj[key]; @@ -97,3 +100,55 @@ export const combineReducers = reducersObj => { : prevState; }; }; + +/** + * A utility function to create an action creator for the given action + * type string. The action creator accepts a single argument, which will + * be included in the action object as a field called payload. The action + * creator function will also have its toString() overriden so that it + * returns the action type, allowing it to be used in reducer logic that + * is looking for that action type. + * + * @param type The action type to use for created actions. + * @param prepare (optional) a method that takes any number of arguments + * and returns { payload } or { payload, meta }. If this is given, the + * resulting action creator will pass it's arguments to this method to + * calculate payload & meta. + * + * @public + */ +export const createAction = (type, prepare) => { + const actionCreator = (...args) => { + if (!prepare) { + return { type, payload: args[0] }; + } + const prepared = prepare(...args); + if (!prepared) { + throw new Error('prepare function did not return an object'); + } + const action = { type }; + if ('payload' in prepared) { + action.payload = prepared.payload; + } + if ('meta' in prepared) { + action.meta = prepared.meta; + } + return action; + }; + actionCreator.toString = () => '' + type; + actionCreator.type = type; + actionCreator.match = action => action.type === type; + return actionCreator; +}; + + +// Implementation specific +// -------------------------------------------------------- + +export const useDispatch = context => { + return context.store.dispatch; +}; + +export const useSelector = (context, selector) => { + return selector(context.store.getState()); +}; diff --git a/tgui/packages/common/storage.js b/tgui/packages/common/storage.js index 8e1d2183e4..115a7ff0ff 100644 --- a/tgui/packages/common/storage.js +++ b/tgui/packages/common/storage.js @@ -6,71 +6,191 @@ * @license MIT */ -export const STORAGE_NONE = 0; -export const STORAGE_LOCAL_STORAGE = 1; -export const STORAGE_INDEXED_DB = 2; +export const IMPL_MEMORY = 0; +export const IMPL_LOCAL_STORAGE = 1; +export const IMPL_INDEXED_DB = 2; -const createMock = () => { - let storage = {}; - const get = key => storage[key]; - const set = (key, value) => { - storage[key] = value; - }; - const remove = key => { - storage[key] = undefined; - }; - const clear = () => { - // NOTE: On IE8, this will probably leak memory if used often. - storage = {}; - }; - return { - get, - set, - remove, - clear, - engine: STORAGE_NONE, - }; -}; +const INDEXED_DB_VERSION = 1; +const INDEXED_DB_NAME = 'tgui'; +const INDEXED_DB_STORE_NAME = 'storage-v1'; -const createLocalStorage = () => { - const get = key => { - const value = localStorage.getItem(key); - if (typeof value !== 'string') { - return; - } - return JSON.parse(value); - }; - const set = (key, value) => { - localStorage.setItem(key, JSON.stringify(value)); - }; - const remove = key => { - localStorage.removeItem(key); - }; - const clear = () => { - localStorage.clear(); - }; - return { - get, - set, - remove, - clear, - engine: STORAGE_LOCAL_STORAGE, - }; -}; +const READ_ONLY = 'readonly'; +const READ_WRITE = 'readwrite'; -const testLocalStorage = () => { - // Localstorage can sometimes throw an error, even if DOM storage is not - // disabled in IE11 settings. - // See: https://superuser.com/questions/1080011 +const testGeneric = testFn => () => { try { - return Boolean(window.localStorage && window.localStorage.getItem); + return Boolean(testFn()); } catch { return false; } }; -export const storage = ( - testLocalStorage() && createLocalStorage() - || createMock() -); +// Localstorage can sometimes throw an error, even if DOM storage is not +// disabled in IE11 settings. +// See: https://superuser.com/questions/1080011 +const testLocalStorage = testGeneric(() => ( + window.localStorage && window.localStorage.getItem +)); + +const testIndexedDb = testGeneric(() => ( + (window.indexedDB || window.msIndexedDB) + && (window.IDBTransaction || window.msIDBTransaction) +)); + +class MemoryBackend { + constructor() { + this.impl = IMPL_MEMORY; + this.store = {}; + } + + get(key) { + return this.store[key]; + } + + set(key, value) { + this.store[key] = value; + } + + remove(key) { + this.store[key] = undefined; + } + + clear() { + this.store = {}; + } +} + +class LocalStorageBackend { + constructor() { + this.impl = IMPL_LOCAL_STORAGE; + this.store = {}; + } + + get(key) { + const value = localStorage.getItem(key); + if (typeof value === 'string') { + return JSON.parse(value); + } + } + + set(key, value) { + localStorage.setItem(key, JSON.stringify(value)); + } + + remove(key) { + localStorage.removeItem(key); + } + + clear() { + localStorage.clear(); + } +} + +class IndexedDbBackend { + constructor() { + this.impl = IMPL_INDEXED_DB; + /** @type {Promise} */ + this.dbPromise = new Promise((resolve, reject) => { + const indexedDB = window.indexedDB || window.msIndexedDB; + const req = indexedDB.open(INDEXED_DB_NAME, INDEXED_DB_VERSION); + req.onupgradeneeded = () => { + try { + req.result.createObjectStore(INDEXED_DB_STORE_NAME); + } + catch (err) { + reject(new Error('Failed to upgrade IDB: ' + req.error)); + } + }; + req.onsuccess = () => resolve(req.result); + req.onerror = () => { + reject(new Error('Failed to open IDB: ' + req.error)); + }; + }); + } + + getStore(mode) { + return this.dbPromise.then(db => db + .transaction(INDEXED_DB_STORE_NAME, mode) + .objectStore(INDEXED_DB_STORE_NAME)); + } + + async get(key) { + const store = await this.getStore(READ_ONLY); + return new Promise((resolve, reject) => { + const req = store.get(key); + req.onsuccess = () => resolve(req.result); + req.onerror = () => reject(req.error); + }); + } + + async set(key, value) { + // The reason we don't _save_ null is because IE 10 does + // not support saving the `null` type in IndexedDB. How + // ironic, given the bug below! + // See: https://github.com/mozilla/localForage/issues/161 + if (value === null) { + value = undefined; + } + // NOTE: We deliberately make this operation transactionless + const store = await this.getStore(READ_WRITE); + store.put(value, key); + } + + async remove(key) { + // NOTE: We deliberately make this operation transactionless + const store = await this.getStore(READ_WRITE); + store.delete(key); + } + + async clear() { + // NOTE: We deliberately make this operation transactionless + const store = await this.getStore(READ_WRITE); + store.clear(); + } +} + +/** + * Web Storage Proxy object, which selects the best backend available + * depending on the environment. + */ +class StorageProxy { + constructor() { + this.backendPromise = (async () => { + if (testIndexedDb()) { + try { + const backend = new IndexedDbBackend(); + await backend.dbPromise; + return backend; + } + catch {} + } + if (testLocalStorage()) { + return new LocalStorageBackend(); + } + return new MemoryBackend(); + })(); + } + + async get(key) { + const backend = await this.backendPromise; + return backend.get(key); + } + + async set(key, value) { + const backend = await this.backendPromise; + return backend.set(key, value); + } + + async remove(key) { + const backend = await this.backendPromise; + return backend.remove(key); + } + + async clear() { + const backend = await this.backendPromise; + return backend.clear(); + } +} + +export const storage = new StorageProxy(); diff --git a/tgui/packages/common/timer.js b/tgui/packages/common/timer.js index f4e26fa5aa..1177071b9c 100644 --- a/tgui/packages/common/timer.js +++ b/tgui/packages/common/timer.js @@ -27,3 +27,12 @@ export const debounce = (fn, time, immediate = false) => { } }; }; + +/** + * Suspends an asynchronous function for N milliseconds. + * + * @param {number} time + */ +export const sleep = time => ( + new Promise(resolve => setTimeout(resolve, time)) +); diff --git a/tgui/packages/common/uuid.js b/tgui/packages/common/uuid.js new file mode 100644 index 0000000000..7721af6494 --- /dev/null +++ b/tgui/packages/common/uuid.js @@ -0,0 +1,19 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +/** + * Creates a UUID v4 string + * + * @return {string} + */ +export const createUuid = () => { + let d = new Date().getTime(); + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { + const r = (d + Math.random() * 16) % 16 | 0; + d = Math.floor(d / 16); + return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); + }); +}; diff --git a/tgui/packages/tgui-dev-server/dreamseeker.js b/tgui/packages/tgui-dev-server/dreamseeker.js new file mode 100644 index 0000000000..c36fe55f28 --- /dev/null +++ b/tgui/packages/tgui-dev-server/dreamseeker.js @@ -0,0 +1,87 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import axios from 'axios'; +import { exec } from 'child_process'; +import { createLogger } from 'common/logging.js'; +import { promisify } from 'util'; + +const logger = createLogger('dreamseeker'); + +const instanceByPid = new Map(); + +export class DreamSeeker { + constructor(pid, addr) { + this.pid = pid; + this.addr = addr; + this.client = axios.create({ + baseURL: `http://${addr}/`, + }); + } + + topic(params = {}) { + const query = Object.keys(params) + .map(key => encodeURIComponent(key) + + '=' + encodeURIComponent(params[key])) + .join('&'); + return this.client.get('/dummy?' + query); + } +} + +/** + * @param {number[]} pids + * @returns {DreamSeeker[]} + */ +DreamSeeker.getInstancesByPids = async pids => { + if (process.platform !== 'win32') { + return []; + } + const instances = []; + const pidsToResolve = []; + for (let pid of pids) { + const instance = instanceByPid.get(pid); + if (instance) { + instances.push(instance); + } + else { + pidsToResolve.push(pid); + } + } + if (pidsToResolve.length > 0) { + try { + const command = 'netstat -a -n -o'; + const { stdout } = await promisify(exec)(command); + // Line format: + // proto addr mask mode pid + const entries = stdout + .split('\r\n') + .filter(line => line.includes('LISTENING')) + .map(line => { + const words = line.match(/\S+/g); + return { + addr: words[1], + pid: parseInt(words[4], 10), + }; + }) + .filter(entry => pidsToResolve.includes(entry.pid)); + const len = entries.length; + logger.log('found', len, plural('instance', len)); + for (let entry of entries) { + const { pid, addr } = entry; + const instance = new DreamSeeker(pid, addr); + instances.push(instance); + instanceByPid.set(pid, instance); + } + } + catch (err) { + logger.error(err); + return []; + } + } + return instances; +}; + +const plural = (word, n) => n !== 1 ? word + 's' : word; diff --git a/tgui/packages/tgui-dev-server/index.js b/tgui/packages/tgui-dev-server/index.js index f4e8155d29..43acd87474 100644 --- a/tgui/packages/tgui-dev-server/index.js +++ b/tgui/packages/tgui-dev-server/index.js @@ -8,12 +8,15 @@ import { setupWebpack, getWebpackConfig } from './webpack.js'; import { reloadByondCache } from './reloader.js'; const noHot = process.argv.includes('--no-hot'); +const noTmp = process.argv.includes('--no-tmp'); const reloadOnce = process.argv.includes('--reload'); const setupServer = async () => { const config = await getWebpackConfig({ mode: 'development', hot: !noHot, + devServer: true, + useTmpFolder: !noTmp, }); // Reload cache once if (reloadOnce) { diff --git a/tgui/packages/tgui-dev-server/link/client.js b/tgui/packages/tgui-dev-server/link/client.js index 4671b340c5..5a4d2f6112 100644 --- a/tgui/packages/tgui-dev-server/link/client.js +++ b/tgui/packages/tgui-dev-server/link/client.js @@ -37,7 +37,7 @@ if (process.env.NODE_ENV !== 'production') { window.onunload = () => socket && socket.close(); } -const subscribe = fn => subscribers.push(fn); +export const subscribe = fn => subscribers.push(fn); /** * A json serializer which handles circular references and other junk. @@ -68,7 +68,10 @@ const serializeObject = obj => { } refs.push(value); // Error object - if (value instanceof Error) { + const isError = value instanceof Error || ( + value.code && value.message && value.message.includes('Error') + ); + if (isError) { return { __error__: true, string: String(value), @@ -88,7 +91,7 @@ const serializeObject = obj => { return json; }; -const sendRawMessage = msg => { +export const sendMessage = msg => { if (process.env.NODE_ENV !== 'production') { const json = serializeObject(msg); // Send message using WebSocket @@ -109,8 +112,8 @@ const sendRawMessage = msg => { else { const DEV_SERVER_IP = process.env.DEV_SERVER_IP || '127.0.0.1'; const req = new XMLHttpRequest(); - req.open('POST', `http://${DEV_SERVER_IP}:3001`); - req.timeout = 500; + req.open('POST', `http://${DEV_SERVER_IP}:3001`, true); + req.timeout = 250; req.send(json); } } @@ -119,7 +122,7 @@ const sendRawMessage = msg => { export const sendLogEntry = (level, ns, ...args) => { if (process.env.NODE_ENV !== 'production') { try { - sendRawMessage({ + sendMessage({ type: 'log', payload: { level, diff --git a/tgui/packages/tgui-dev-server/link/retrace.js b/tgui/packages/tgui-dev-server/link/retrace.js index e0b17a01d6..4bcb8e40c6 100644 --- a/tgui/packages/tgui-dev-server/link/retrace.js +++ b/tgui/packages/tgui-dev-server/link/retrace.js @@ -39,6 +39,10 @@ export const loadSourceMaps = async bundleDir => { }; export const retrace = stack => { + if (typeof stack !== 'string') { + logger.log('ERROR: Stack is not a string!', stack); + return stack; + } const header = stack.split(/\n\s.*at/)[0]; const mappedStack = StackTraceParser.parse(stack) .map(frame => { diff --git a/tgui/packages/tgui-dev-server/link/server.js b/tgui/packages/tgui-dev-server/link/server.js index 94a79c9ad5..2586d77779 100644 --- a/tgui/packages/tgui-dev-server/link/server.js +++ b/tgui/packages/tgui-dev-server/link/server.js @@ -16,37 +16,108 @@ const DEBUG = process.argv.includes('--debug'); export { loadSourceMaps }; -export const setupLink = () => { - logger.log('setting up'); - const wss = setupWebSocketLink(); - setupHttpLink(); - return { - wss, - }; -}; +export const setupLink = () => new LinkServer(); -export const broadcastMessage = (link, msg) => { - const { wss } = link; - const clients = [...wss.clients]; - logger.log(`broadcasting ${msg.type} to ${clients.length} clients`); - for (let client of clients) { - const json = JSON.stringify(msg); - client.send(json); +class LinkServer { + constructor() { + logger.log('setting up'); + this.wss = null; + this.setupWebSocketLink(); + this.setupHttpLink(); } -}; + + // WebSocket-based client link + setupWebSocketLink() { + const port = 3000; + this.wss = new WebSocket.Server({ port }); + this.wss.on('connection', ws => { + logger.log('client connected'); + ws.on('message', json => { + const msg = deserializeObject(json); + this.handleLinkMessage(ws, msg); + }); + ws.on('close', () => { + logger.log('client disconnected'); + }); + }); + logger.log(`listening on port ${port} (WebSocket)`); + } + + // One way HTTP-based client link for IE8 + setupHttpLink() { + const port = 3001; + this.httpServer = http.createServer((req, res) => { + if (req.method === 'POST') { + let body = ''; + req.on('data', chunk => { + body += chunk.toString(); + }); + req.on('end', () => { + const msg = deserializeObject(body); + this.handleLinkMessage(null, msg); + res.end(); + }); + return; + } + res.write('Hello'); + res.end(); + }); + this.httpServer.listen(port); + logger.log(`listening on port ${port} (HTTP)`); + } + + handleLinkMessage(ws, msg) { + const { type, payload } = msg; + if (type === 'log') { + const { level, ns, args } = payload; + // Skip debug messages + if (level <= 0 && !DEBUG) { + return; + } + directLog(ns, ...args.map(arg => { + if (typeof arg === 'object') { + return inspect(arg, { + depth: Infinity, + colors: true, + compact: 8, + }); + } + return arg; + })); + return; + } + if (type === 'relay') { + for (let client of this.wss.clients) { + if (client === ws) { + continue; + } + this.sendMessage(client, msg); + } + return; + } + logger.log('unhandled message', msg); + } + + sendMessage(ws, msg) { + ws.send(JSON.stringify(msg)); + } + + broadcastMessage(msg) { + const clients = [...this.wss.clients]; + if (clients.length === 0) { + return; + } + logger.log(`broadcasting ${msg.type} to ${clients.length} clients`); + for (let client of clients) { + const json = JSON.stringify(msg); + client.send(json); + } + } +} const deserializeObject = str => { return JSON.parse(str, (key, value) => { if (typeof value === 'object' && value !== null) { - if (value.__error__) { - if (!value.stack) { - return value.string; - } - return retrace(value.stack); - } - if (value.__number__) { - return parseFloat(value.__number__); - } if (value.__undefined__) { // NOTE: You should not rely on deserialized object's undefined, // this is purely for inspection purposes. @@ -54,80 +125,17 @@ const deserializeObject = str => { [inspect.custom]: () => undefined, }; } + if (value.__number__) { + return parseFloat(value.__number__); + } + if (value.__error__) { + if (!value.stack) { + return value.string; + } + return retrace(value.stack); + } return value; } return value; }); }; - -const handleLinkMessage = msg => { - const { type, payload } = msg; - - if (type === 'log') { - const { level, ns, args } = payload; - // Skip debug messages - if (level <= 0 && !DEBUG) { - return; - } - directLog(ns, ...args.map(arg => { - if (typeof arg === 'object') { - return inspect(arg, { - depth: Infinity, - colors: true, - compact: 8, - }); - } - return arg; - })); - return; - } - - logger.log('unhandled message', msg); -}; - -// WebSocket-based client link -const setupWebSocketLink = () => { - const port = 3000; - const wss = new WebSocket.Server({ port }); - - wss.on('connection', ws => { - logger.log('client connected'); - - ws.on('message', json => { - const msg = deserializeObject(json); - handleLinkMessage(msg); - }); - - ws.on('close', () => { - logger.log('client disconnected'); - }); - }); - - logger.log(`listening on port ${port} (WebSocket)`); - return wss; -}; - -// One way HTTP-based client link for IE8 -const setupHttpLink = () => { - const port = 3001; - - const server = http.createServer((req, res) => { - if (req.method === 'POST') { - let body = ''; - req.on('data', chunk => { - body += chunk.toString(); - }); - req.on('end', () => { - const msg = deserializeObject(body); - handleLinkMessage(msg); - res.end(); - }); - return; - } - res.write('Hello'); - res.end(); - }); - - server.listen(port); - logger.log(`listening on port ${port} (HTTP)`); -}; diff --git a/tgui/packages/tgui-dev-server/package.json b/tgui/packages/tgui-dev-server/package.json index e397fe3e0b..3e50a97f0e 100644 --- a/tgui/packages/tgui-dev-server/package.json +++ b/tgui/packages/tgui-dev-server/package.json @@ -1,9 +1,10 @@ { "private": true, "name": "tgui-dev-server", - "version": "3.0.0", + "version": "4.1.0", "type": "module", "dependencies": { + "axios": "^0.19.2", "glob": "^7.1.4", "source-map": "^0.7.3", "stacktrace-parser": "^0.1.7", diff --git a/tgui/packages/tgui-dev-server/reloader.js b/tgui/packages/tgui-dev-server/reloader.js index e33f7226b9..1271e67d2a 100644 --- a/tgui/packages/tgui-dev-server/reloader.js +++ b/tgui/packages/tgui-dev-server/reloader.js @@ -11,6 +11,7 @@ import { basename } from 'path'; import { promisify } from 'util'; import { resolveGlob, resolvePath } from './util.js'; import { regQuery } from './winreg.js'; +import { DreamSeeker } from './dreamseeker.js'; const logger = createLogger('reloader'); @@ -43,7 +44,7 @@ export const findCacheRoot = async () => { const paths = await resolveGlob(pattern); if (paths.length > 0) { cacheRoot = paths[0]; - logger.log(`found cache at '${cacheRoot}'`); + onCacheRootFound(cacheRoot); return cacheRoot; } } @@ -58,13 +59,19 @@ export const findCacheRoot = async () => { .replace(/\\$/, '') .replace(/\\/g, '/') + '/cache'; - logger.log(`found cache at '${cacheRoot}'`); + onCacheRootFound(cacheRoot); return cacheRoot; } } logger.log('found no cache directories'); }; +const onCacheRootFound = cacheRoot => { + logger.log(`found cache at '${cacheRoot}'`); + // Plant dummy + fs.closeSync(fs.openSync(cacheRoot + '/dummy', 'w')); +}; + export const reloadByondCache = async bundleDir => { const cacheRoot = await findCacheRoot(); if (!cacheRoot) { @@ -76,10 +83,16 @@ export const reloadByondCache = async bundleDir => { logger.log('found no tmp folder in cache'); return; } - const assets = await resolveGlob(bundleDir, './*.+(bundle|hot-update).*'); + // Get dreamseeker instances + const pids = cacheDirs.map(cacheDir => ( + parseInt(cacheDir.split('/cache/tmp').pop(), 10) + )); + const dssPromise = DreamSeeker.getInstancesByPids(pids); + // Copy assets + const assets = await resolveGlob(bundleDir, './*.+(bundle|chunk|hot-update).*'); for (let cacheDir of cacheDirs) { // Clear garbage - const garbage = await resolveGlob(cacheDir, './*.+(bundle|hot-update).*'); + const garbage = await resolveGlob(cacheDir, './*.+(bundle|chunk|hot-update).*'); for (let file of garbage) { await promisify(fs.unlink)(file); } @@ -90,4 +103,15 @@ export const reloadByondCache = async bundleDir => { } logger.log(`copied ${assets.length} files to '${cacheDir}'`); } + // Notify dreamseeker + const dss = await dssPromise; + if (dss.length > 0) { + logger.log(`notifying dreamseeker`); + for (let dreamseeker of dss) { + dreamseeker.topic({ + tgui: 1, + type: 'cacheReloaded', + }); + } + } }; diff --git a/tgui/packages/tgui-dev-server/webpack.js b/tgui/packages/tgui-dev-server/webpack.js index c625827409..7a1f600253 100644 --- a/tgui/packages/tgui-dev-server/webpack.js +++ b/tgui/packages/tgui-dev-server/webpack.js @@ -9,7 +9,7 @@ import fs from 'fs'; import { createRequire } from 'module'; import { promisify } from 'util'; import webpack from 'webpack'; -import { broadcastMessage, loadSourceMaps, setupLink } from './link/server.js'; +import { loadSourceMaps, setupLink } from './link/server.js'; import { reloadByondCache } from './reloader.js'; import { resolveGlob } from './util.js'; @@ -44,7 +44,7 @@ export const setupWebpack = async config => { // Reload cache await reloadByondCache(bundleDir); // Notify all clients that update has happened - broadcastMessage(link, { + link.broadcastMessage({ type: 'hotUpdate', }); }); @@ -55,6 +55,9 @@ export const setupWebpack = async config => { logger.error('compilation error', err); return; } - logger.log(stats.toString(config.devServer.stats)); + stats + .toString(config.devServer.stats) + .split('\n') + .forEach(line => logger.log(line)); }); }; diff --git a/tgui/packages/tgui-panel/Notifications.js b/tgui/packages/tgui-panel/Notifications.js new file mode 100644 index 0000000000..a64ddd8e30 --- /dev/null +++ b/tgui/packages/tgui-panel/Notifications.js @@ -0,0 +1,41 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { Flex } from 'tgui/components'; + +export const Notifications = props => { + const { children } = props; + return ( +
+ {children} +
+ ); +}; + +const NotificationsItem = props => { + const { + rightSlot, + children, + } = props; + return ( + + + {children} + + {rightSlot && ( + + {rightSlot} + + )} + + ); +}; + +Notifications.Item = NotificationsItem; diff --git a/tgui/packages/tgui-panel/Panel.js b/tgui/packages/tgui-panel/Panel.js new file mode 100644 index 0000000000..0350fb94c3 --- /dev/null +++ b/tgui/packages/tgui-panel/Panel.js @@ -0,0 +1,142 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { Button, Flex, Section } from 'tgui/components'; +import { Pane } from 'tgui/layouts'; +import { NowPlayingWidget, useAudio } from './audio'; +import { ChatPanel, ChatTabs } from './chat'; +import { useGame } from './game'; +import { Notifications } from './Notifications'; +import { PingIndicator } from './ping'; +import { SettingsPanel, useSettings } from './settings'; + +export const Panel = (props, context) => { + if (Byond.IS_LTE_IE8) { + return ( + + ); + } + const audio = useAudio(context); + const settings = useSettings(context); + const game = useGame(context); + if (process.env.NODE_ENV !== 'production') { + const { useDebug, KitchenSink } = require('tgui/debug'); + const debug = useDebug(context); + if (debug.kitchenSink) { + return ( + + ); + } + } + return ( // yes i know it's bad. + + + +
+ + + + + + + + +
+
+ {audio.visible && ( + +
+ +
+
+ )} + {settings.visible && ( + + + + )} + +
+ + + + + {game.connectionLostAt && ( + Byond.command('.reconnect')}> + Reconnect + + )}> + You are either AFK, experiencing lag or the connection + has closed. + + )} + {game.roundRestartedAt && ( + + The connection has been closed because the server is + restarting. Please wait while you automatically reconnect. + + )} + +
+
+
+
+ ); +}; + +// IE8: Needs special treatment +const HoboIE8Panel = (props, context) => { + const settings = useSettings(context); + return ( + + + + {settings.visible && ( + + + + ) || ( + + )} + + + ); +}; diff --git a/tgui/packages/tgui-panel/audio/NowPlayingWidget.js b/tgui/packages/tgui-panel/audio/NowPlayingWidget.js new file mode 100644 index 0000000000..654f2a6b7c --- /dev/null +++ b/tgui/packages/tgui-panel/audio/NowPlayingWidget.js @@ -0,0 +1,69 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { toFixed } from 'common/math'; +import { useDispatch, useSelector } from 'common/redux'; +import { Fragment } from 'inferno'; +import { Button, Flex, Knob } from 'tgui/components'; +import { useSettings } from '../settings'; +import { selectAudio } from './selectors'; + +export const NowPlayingWidget = (props, context) => { + const audio = useSelector(context, selectAudio); + const dispatch = useDispatch(context); + const settings = useSettings(context); + const title = audio.meta?.title; + return ( + + {audio.playing && ( + + + Now playing: + + + {title || 'Unknown Track'} + + + ) || ( + + Nothing to play. + + )} + {audio.playing && ( + + + + + +
+ {MESSAGE_TYPES + .filter(typeDef => !typeDef.important && !typeDef.admin) + .map(typeDef => ( + dispatch(toggleAcceptedType({ + pageId: page.id, + type: typeDef.type, + }))}> + {typeDef.name} + + ))} + + {MESSAGE_TYPES + .filter(typeDef => !typeDef.important && typeDef.admin) + .map(typeDef => ( + dispatch(toggleAcceptedType({ + pageId: page.id, + type: typeDef.type, + }))}> + {typeDef.name} + + ))} + +
+
+ ); +}; diff --git a/tgui/packages/tgui-panel/chat/ChatPanel.js b/tgui/packages/tgui-panel/chat/ChatPanel.js new file mode 100644 index 0000000000..bf7eb90e6f --- /dev/null +++ b/tgui/packages/tgui-panel/chat/ChatPanel.js @@ -0,0 +1,71 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { shallowDiffers } from 'common/react'; +import { Component, createRef, Fragment } from 'inferno'; +import { Button } from 'tgui/components'; +import { chatRenderer } from './renderer'; + +export class ChatPanel extends Component { + constructor() { + super(); + this.ref = createRef(); + this.state = { + scrollTracking: true, + }; + this.handleScrollTrackingChange = value => this.setState({ + scrollTracking: value, + }); + } + + componentDidMount() { + chatRenderer.mount(this.ref.current); + chatRenderer.events.on('scrollTrackingChanged', + this.handleScrollTrackingChange); + this.componentDidUpdate(); + } + + componentWillUnmount() { + chatRenderer.events.off('scrollTrackingChanged', + this.handleScrollTrackingChange); + } + + componentDidUpdate(prevProps) { + requestAnimationFrame(() => { + chatRenderer.ensureScrollTracking(); + }); + const shouldUpdateStyle = ( + !prevProps || shallowDiffers(this.props, prevProps) + ); + if (shouldUpdateStyle) { + chatRenderer.assignStyle({ + 'width': '100%', + 'white-space': 'pre-wrap', + 'font-size': this.props.fontSize, + 'line-height': this.props.lineHeight, + }); + } + } + + render() { + const { + scrollTracking, + } = this.state; + return ( + +
+ {!scrollTracking && ( + + )} + + ); + } +} diff --git a/tgui/packages/tgui-panel/chat/ChatTabs.js b/tgui/packages/tgui-panel/chat/ChatTabs.js new file mode 100644 index 0000000000..a0e6cc59e5 --- /dev/null +++ b/tgui/packages/tgui-panel/chat/ChatTabs.js @@ -0,0 +1,61 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { useDispatch, useSelector } from 'common/redux'; +import { Box, Tabs, Flex, Button } from 'tgui/components'; +import { changeChatPage, addChatPage } from './actions'; +import { selectChatPages, selectCurrentChatPage } from './selectors'; +import { openChatSettings } from '../settings/actions'; + +const UnreadCountWidget = ({ value }) => ( + + {Math.min(value, 99)} + +); + +export const ChatTabs = (props, context) => { + const pages = useSelector(context, selectChatPages); + const currentPage = useSelector(context, selectCurrentChatPage); + const dispatch = useDispatch(context); + return ( + + + + {pages.map(page => ( + 0 && ( + + )} + onClick={() => dispatch(changeChatPage({ + pageId: page.id, + }))}> + {page.name} + + ))} + + + +