diff --git a/Dockerfile b/Dockerfile index a28b231eab..ec3694c7e8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM tgstation/byond:512.1467 as base +FROM tgstation/byond:512.1488 as base FROM base as build_base diff --git a/README.md b/README.md index 3e123280a2..ed82fb08e6 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Based and maintained from /tg/station.
**Forums:** http://citadel-station.net/forum/
**Ban Appeals:** http://citadel-station.net/forum/forumdisplay.php?fid=8
**Code:** https://github.com/Citadel-Station-13/Citadel-Station-13
-**Discord:** [Here](https://discord.gg/3gJ9pnM).
+**Discord:** [Here](https://discord.gg/E6SQuhz).
## DOWNLOADING diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm index 738148c948..ed5b07168f 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm @@ -508,7 +508,9 @@ /obj/structure/stone_tile/cracked{ dir = 4 }, -/obj/item/construction/rcd/loaded, +/obj/item/construction/rcd/loaded/upgraded{ + + }, /turf/open/indestructible/boss, /area/ruin/unpowered/ash_walkers) "bn" = ( diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm index 980e23188a..30a2af7457 100644 --- a/_maps/map_files/BoxStation/BoxStation.dmm +++ b/_maps/map_files/BoxStation/BoxStation.dmm @@ -41,6 +41,21 @@ }, /turf/open/floor/plasteel, /area/crew_quarters/bar) +"aad" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/structure/table, +/obj/item/restraints/handcuffs, +/turf/open/floor/plasteel, +/area/security/prison) "aae" = ( /obj/effect/landmark/carpspawn, /turf/open/space, @@ -577,6 +592,22 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/closed/wall/r_wall, /area/security/execution/transfer) +"abz" = ( +/obj/machinery/atmospherics/pipe/manifold/supply/hidden, +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/structure/cable{ + icon_state = "2-8" + }, +/turf/open/floor/plasteel, +/area/security/prison) "abA" = ( /obj/machinery/light, /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ @@ -1849,16 +1880,16 @@ /turf/open/floor/plasteel/dark, /area/security/execution/transfer) "aef" = ( -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden, /obj/effect/turf_decal/tile/red{ dir = 1 }, +/obj/effect/turf_decal/tile/red, /obj/effect/turf_decal/tile/red{ dir = 8 }, +/obj/structure/cable{ + icon_state = "1-2" + }, /turf/open/floor/plasteel, /area/security/prison) "aeg" = ( @@ -2118,6 +2149,31 @@ }, /turf/open/floor/plating, /area/maintenance/fore/secondary) +"aeD" = ( +/obj/machinery/door/airlock/security{ + name = "Firing Range"; + req_access_txt = "2" + }, +/obj/structure/cable{ + icon_state = "1-2" + }, +/turf/open/floor/plasteel, +/area/security/prison) +"aeE" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 9 + }, +/obj/machinery/light/small{ + dir = 8 + }, +/turf/open/floor/plating, +/area/security/prison) +"aeF" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/turf/open/floor/plating, +/area/security/prison) "aeG" = ( /obj/structure/cable, /obj/machinery/power/solar{ @@ -2193,17 +2249,13 @@ /turf/open/floor/plasteel/dark, /area/security/execution/transfer) "aeM" = ( -/obj/machinery/atmospherics/pipe/manifold/supply/hidden, -/obj/structure/cable{ - icon_state = "4-8" +/obj/effect/turf_decal/stripes/line{ + dir = 5 }, -/obj/effect/turf_decal/tile/red{ - dir = 1 +/obj/structure/window/reinforced{ + dir = 4 }, -/obj/effect/turf_decal/tile/red{ - dir = 8 - }, -/turf/open/floor/plasteel, +/turf/open/floor/plating, /area/security/prison) "aeN" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ @@ -2494,6 +2546,10 @@ }, /turf/open/floor/plasteel, /area/security/main) +"afn" = ( +/obj/structure/table, +/turf/open/floor/plasteel/showroomfloor, +/area/security/warden) "afo" = ( /obj/machinery/door/airlock/external{ name = "Escape Pod Three" @@ -2514,6 +2570,20 @@ }, /turf/open/space/basic, /area/space) +"afq" = ( +/obj/machinery/atmospherics/components/unary/vent_scrubber/on, +/turf/open/floor/plasteel, +/area/security/prison) +"afr" = ( +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/security/prison) +"afs" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/turf/open/floor/plating, +/area/security/prison) "aft" = ( /obj/machinery/atmospherics/pipe/simple/general/visible{ dir = 5 @@ -2570,16 +2640,16 @@ /turf/open/floor/plasteel/dark, /area/security/execution/transfer) "afz" = ( -/obj/structure/table, -/obj/item/restraints/handcuffs, -/obj/effect/turf_decal/tile/red{ - dir = 1 +/obj/effect/turf_decal/stripes/line{ + dir = 4 }, -/obj/effect/turf_decal/tile/red, -/obj/effect/turf_decal/tile/red{ - dir = 8 +/obj/machinery/door/window/westleft{ + base_state = "right"; + dir = 4; + icon_state = "right"; + name = "Shooting Range" }, -/turf/open/floor/plasteel, +/turf/open/floor/plating, /area/security/prison) "afA" = ( /turf/closed/wall/r_wall, @@ -2823,6 +2893,16 @@ /obj/machinery/atmospherics/pipe/manifold4w/general/visible, /turf/open/floor/plasteel, /area/engine/atmos) +"age" = ( +/obj/machinery/door/window/southleft{ + name = "Target Storage" + }, +/obj/item/target/clown, +/obj/item/target/clown, +/obj/item/target, +/obj/item/target, +/turf/open/floor/plating, +/area/security/prison) "agf" = ( /obj/structure/table, /obj/item/stack/sheet/metal, @@ -2924,7 +3004,7 @@ /turf/open/floor/plasteel/showroomfloor, /area/security/warden) "agp" = ( -/obj/machinery/computer/prisoner, +/obj/machinery/computer/prisoner/management, /turf/open/floor/plasteel/showroomfloor, /area/security/warden) "agq" = ( @@ -2976,6 +3056,16 @@ }, /turf/open/floor/plasteel/showroomfloor, /area/security/warden) +"agv" = ( +/obj/structure/cable{ + icon_state = "2-8" + }, +/obj/structure/cable{ + icon_state = "2-4" + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/turf/open/floor/plasteel, +/area/security/prison) "agw" = ( /obj/structure/table, /obj/machinery/syndicatebomb/training, @@ -3100,10 +3190,13 @@ /turf/open/floor/plasteel, /area/security/main) "agH" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 +/obj/machinery/door/window/southright{ + name = "Target Storage" }, -/turf/open/floor/plasteel/dark, +/obj/item/target/alien, +/obj/item/target/alien, +/obj/item/target/syndicate, +/turf/open/floor/plating, /area/security/prison) "agI" = ( /obj/machinery/airalarm{ @@ -3474,6 +3567,15 @@ }, /turf/open/floor/plasteel/showroomfloor, /area/security/warden) +"ahw" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 10 + }, +/obj/machinery/light/small{ + dir = 8 + }, +/turf/open/floor/plating, +/area/security/prison) "ahx" = ( /obj/structure/cable{ icon_state = "4-8" @@ -4489,7 +4591,7 @@ /turf/open/space, /area/solar/port/fore) "ajr" = ( -/obj/machinery/computer/gulag_teleporter_computer, +/obj/machinery/computer/prisoner/gulag_teleporter_computer, /turf/open/floor/plasteel, /area/security/processing) "ajs" = ( @@ -4497,26 +4599,18 @@ /turf/open/floor/plasteel, /area/security/processing) "ajt" = ( -/obj/structure/sign/warning/securearea{ - pixel_x = 32 - }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 4 - }, -/obj/structure/table, -/obj/item/storage/box/prisoner, -/obj/machinery/camera{ - c_tag = "Labor Shuttle Dock North" - }, -/turf/open/floor/plasteel, -/area/security/processing) +/obj/effect/turf_decal/stripes/line, +/turf/open/floor/plating, +/area/security/prison) "aju" = ( -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ +/obj/effect/turf_decal/stripes/line{ dir = 6 }, -/obj/machinery/computer/security/labor, -/turf/open/floor/plasteel, -/area/security/processing) +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/open/floor/plating, +/area/security/prison) "ajv" = ( /obj/machinery/light{ dir = 8 @@ -4830,6 +4924,29 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper, /turf/open/floor/plating, /area/maintenance/solars/port/fore) +"ajX" = ( +/obj/structure/table, +/obj/structure/cable{ + icon_state = "0-2" + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/machinery/recharger, +/obj/item/gun/energy/laser/practice, +/obj/item/gun/energy/laser/practice, +/turf/open/floor/plasteel, +/area/security/prison) +"ajY" = ( +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/turf/open/floor/plasteel, +/area/security/prison) "ajZ" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/sign/warning/vacuum/external{ @@ -4854,20 +4971,18 @@ /area/security/processing) "akc" = ( /obj/structure/cable{ - icon_state = "4-8" + icon_state = "1-2" }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 6 +/obj/machinery/airalarm{ + dir = 8; + pixel_x = 24 }, /turf/open/floor/plasteel, -/area/security/processing) +/area/security/prison) "akd" = ( -/obj/structure/cable{ - icon_state = "2-4" - }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/turf/open/floor/plasteel, -/area/security/processing) +/obj/structure/lattice, +/turf/closed/wall, +/area/security/prison) "ake" = ( /obj/structure/cable{ icon_state = "4-8" @@ -5136,6 +5251,44 @@ }, /turf/open/floor/plating, /area/maintenance/solars/port/fore) +"akC" = ( +/obj/machinery/door/airlock/security{ + name = "Labor Shuttle"; + req_access_txt = "2" + }, +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/turf/open/floor/plasteel, +/area/security/processing) +"akD" = ( +/obj/effect/spawner/structure/window/reinforced, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/turf/open/floor/plating, +/area/security/prison) +"akE" = ( +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ + dir = 8 + }, +/turf/open/floor/plasteel, +/area/security/processing) +"akF" = ( +/obj/structure/sign/warning/securearea{ + pixel_x = 32 + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/obj/machinery/camera{ + c_tag = "Labor Shuttle Dock North" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/turf/open/floor/plasteel, +/area/security/processing) "akG" = ( /obj/structure/sign/warning/vacuum/external{ pixel_y = 32 @@ -5156,11 +5309,12 @@ /turf/open/floor/plasteel, /area/security/processing) "akJ" = ( -/obj/machinery/light_switch{ - pixel_x = 27 +/obj/structure/cable{ + icon_state = "2-4" }, -/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ - dir = 4 +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/obj/structure/cable{ + icon_state = "1-2" }, /turf/open/floor/plasteel, /area/security/processing) @@ -5445,12 +5599,42 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/maintenance/port/fore) +"alj" = ( +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ + dir = 8 + }, +/turf/open/floor/plasteel, +/area/security/processing) "alk" = ( /obj/machinery/atmospherics/pipe/manifold/scrubbers/visible{ dir = 1 }, /turf/open/floor/plasteel, /area/engine/atmos) +"all" = ( +/obj/machinery/light_switch{ + pixel_x = 27 + }, +/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ + dir = 4 + }, +/obj/machinery/computer/security/labor, +/turf/open/floor/plasteel, +/area/security/processing) +"alm" = ( +/obj/machinery/airalarm{ + dir = 8; + pixel_x = 24 + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/structure/chair{ + dir = 1 + }, +/turf/open/floor/plasteel, +/area/security/processing) "aln" = ( /obj/effect/mapping_helpers/airlock/cyclelink_helper{ dir = 4 @@ -5462,6 +5646,12 @@ }, /turf/open/floor/plating, /area/security/processing) +"alo" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/structure/table, +/obj/item/storage/box/prisoner, +/turf/open/floor/plasteel, +/area/security/processing) "alp" = ( /turf/open/floor/plating, /area/security/processing) @@ -5469,13 +5659,10 @@ /turf/open/floor/plasteel, /area/security/processing) "alr" = ( -/obj/machinery/airalarm{ - dir = 8; - pixel_x = 24 - }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/turf/open/floor/plasteel, -/area/security/processing) +/obj/structure/target_stake, +/obj/item/target/syndicate, +/turf/open/floor/plating, +/area/security/prison) "als" = ( /obj/structure/cable{ icon_state = "1-2" @@ -5527,6 +5714,18 @@ }, /turf/open/floor/plasteel, /area/security/brig) +"alx" = ( +/obj/structure/cable{ + icon_state = "1-4" + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/turf/open/floor/plasteel, +/area/security/prison) "aly" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 1 @@ -5681,6 +5880,24 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plating, /area/maintenance/fore/secondary) +"alM" = ( +/obj/structure/cable{ + icon_state = "1-8" + }, +/obj/structure/cable{ + icon_state = "0-8" + }, +/obj/machinery/light{ + dir = 4 + }, +/obj/machinery/power/apc{ + areastring = "/area/security/main"; + dir = 4; + name = "Firing Range APC"; + pixel_x = 24 + }, +/turf/open/floor/plasteel, +/area/security/prison) "alO" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -5736,6 +5953,44 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/visible, /turf/open/floor/plasteel, /area/engine/atmos) +"alY" = ( +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/machinery/camera{ + c_tag = "Firing Range"; + dir = 1 + }, +/turf/open/floor/plasteel, +/area/security/prison) +"alZ" = ( +/obj/structure/table, +/obj/machinery/atmospherics/components/unary/vent_pump/on, +/obj/item/clothing/glasses/sunglasses{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/item/clothing/glasses/sunglasses{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/item/clothing/ears/earmuffs{ + pixel_x = -3; + pixel_y = -2 + }, +/obj/item/clothing/ears/earmuffs{ + pixel_x = -3; + pixel_y = -2 + }, +/obj/machinery/firealarm{ + dir = 4; + pixel_x = 24 + }, +/turf/open/floor/plasteel, +/area/security/prison) "ama" = ( /mob/living/simple_animal/sloth/paperwork, /turf/open/floor/plasteel, @@ -5755,10 +6010,6 @@ }, /turf/open/floor/plasteel, /area/security/processing) -"amd" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/turf/open/floor/plasteel, -/area/security/processing) "ame" = ( /obj/structure/cable{ icon_state = "1-2" @@ -6031,26 +6282,6 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel, /area/security/processing) -"amN" = ( -/obj/structure/table, -/obj/item/clothing/glasses/sunglasses{ - pixel_x = 3; - pixel_y = 3 - }, -/obj/item/clothing/glasses/sunglasses{ - pixel_x = 3; - pixel_y = 3 - }, -/obj/item/clothing/ears/earmuffs{ - pixel_x = -3; - pixel_y = -2 - }, -/obj/item/clothing/ears/earmuffs{ - pixel_x = -3; - pixel_y = -2 - }, -/turf/open/floor/plasteel/showroomfloor, -/area/security/warden) "amQ" = ( /obj/structure/cable{ icon_state = "0-4" @@ -9240,7 +9471,7 @@ }, /obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/plating, -/area/lawoffice) +/area/maintenance/fore) "avj" = ( /obj/machinery/firealarm{ dir = 8; @@ -17686,6 +17917,9 @@ /obj/effect/turf_decal/stripes/line{ dir = 9 }, +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-20" + }, /turf/open/floor/plasteel, /area/hallway/secondary/entry) "aPu" = ( @@ -19371,7 +19605,7 @@ /turf/closed/wall, /area/bridge) "aTR" = ( -/obj/machinery/computer/prisoner, +/obj/machinery/computer/prisoner/management, /obj/effect/turf_decal/tile/red{ dir = 1 }, @@ -20964,6 +21198,11 @@ /obj/effect/turf_decal/stripes/corner{ dir = 2 }, +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-21"; + pixel_x = -3; + pixel_y = 3 + }, /turf/open/floor/plasteel, /area/crew_quarters/locker) "aXu" = ( @@ -21064,6 +21303,9 @@ /obj/effect/turf_decal/stripes/line{ dir = 4 }, +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-05" + }, /turf/open/floor/plasteel, /area/hallway/secondary/exit) "aXI" = ( @@ -21702,6 +21944,9 @@ dir = 1; pixel_y = -24 }, +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-08" + }, /turf/open/floor/wood, /area/library) "aZe" = ( @@ -24326,6 +24571,9 @@ /obj/effect/turf_decal/tile/purple{ dir = 8 }, +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-13" + }, /turf/open/floor/plasteel, /area/hallway/primary/starboard) "bfX" = ( @@ -24372,6 +24620,9 @@ /obj/effect/turf_decal/tile/purple{ dir = 8 }, +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-10" + }, /turf/open/floor/plasteel, /area/hallway/primary/starboard) "bgc" = ( @@ -27040,11 +27291,6 @@ }, /turf/open/floor/plasteel, /area/quartermaster/sorting) -"bml" = ( -/obj/effect/spawner/structure/window/reinforced, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/plasteel, -/area/quartermaster/office) "bmm" = ( /obj/structure/cable{ icon_state = "1-2" @@ -27874,6 +28120,7 @@ /obj/effect/turf_decal/tile/blue{ dir = 4 }, +/obj/effect/mapping_helpers/airlock/unres, /turf/open/floor/plasteel/white, /area/medical/medbay/central) "bof" = ( @@ -27897,6 +28144,7 @@ /obj/effect/turf_decal/tile/blue{ dir = 4 }, +/obj/effect/mapping_helpers/airlock/unres, /turf/open/floor/plasteel/white, /area/medical/medbay/central) "boi" = ( @@ -29805,6 +30053,7 @@ name = "Genetics"; req_access_txt = "5; 68" }, +/obj/effect/mapping_helpers/airlock/unres, /turf/open/floor/plasteel/white, /area/medical/genetics) "bsv" = ( @@ -30089,6 +30338,9 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 }, +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-16" + }, /turf/open/floor/plasteel/white, /area/science/research) "bti" = ( @@ -32307,19 +32559,16 @@ /turf/open/floor/plasteel/cafeteria, /area/crew_quarters/heads/hor) "byc" = ( -/obj/structure/table, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 }, -/obj/item/stamp/qm, /obj/effect/turf_decal/tile/brown, /obj/effect/turf_decal/tile/brown{ dir = 8 }, +/obj/machinery/computer/card/minor/qm{ + dir = 1 + }, /turf/open/floor/plasteel, /area/quartermaster/qm) "byd" = ( @@ -32359,6 +32608,11 @@ /obj/effect/turf_decal/tile/brown{ dir = 8 }, +/obj/item/stamp/qm, +/obj/item/paper_bin{ + pixel_x = -3; + pixel_y = 7 + }, /turf/open/floor/plasteel, /area/quartermaster/qm) "byh" = ( @@ -39479,6 +39733,7 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 }, +/obj/item/twohanded/required/kirbyplants, /turf/open/floor/plasteel/white/side{ dir = 1 }, @@ -56869,6 +57124,18 @@ }, /turf/open/floor/plating, /area/maintenance/port) +"hwu" = ( +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-24" + }, +/turf/open/floor/plasteel, +/area/security/prison) +"hEm" = ( +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-22" + }, +/turf/open/floor/plasteel/white, +/area/medical/medbay/central) "hRa" = ( /obj/structure/table/reinforced, /obj/machinery/light{ @@ -57328,6 +57595,15 @@ }, /turf/open/floor/plasteel, /area/science/circuit) +"qoW" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 10 + }, +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-18" + }, +/turf/open/floor/plasteel, +/area/hallway/secondary/entry) "quT" = ( /obj/structure/lattice, /obj/structure/grille/broken, @@ -57339,6 +57615,12 @@ }, /turf/open/floor/carpet, /area/crew_quarters/cryopod) +"qQP" = ( +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-14" + }, +/turf/open/floor/plasteel, +/area/hallway/primary/port) "rcD" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 @@ -57349,6 +57631,15 @@ /obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden, /turf/open/floor/plasteel, /area/maintenance/disposal/incinerator) +"rjA" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-04" + }, +/turf/open/floor/plasteel/white, +/area/medical/medbay/central) "rmX" = ( /obj/structure/table, /obj/item/reagent_containers/food/drinks/beer, @@ -57492,6 +57783,12 @@ icon_state = "wood-broken5" }, /area/maintenance/bar) +"tlO" = ( +/obj/item/twohanded/required/kirbyplants{ + icon_state = "applebush" + }, +/turf/open/floor/plasteel, +/area/hallway/primary/port) "tqg" = ( /obj/machinery/cryopod{ dir = 4 @@ -57648,6 +57945,20 @@ }, /turf/open/floor/plating, /area/maintenance/starboard/aft) +"uZN" = ( +/obj/machinery/light{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-03" + }, +/turf/open/floor/plasteel/white/corner{ + dir = 1 + }, +/area/hallway/secondary/entry) "vbD" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/command/glass{ @@ -57822,6 +58133,20 @@ }, /turf/closed/wall/r_wall, /area/maintenance/disposal/incinerator) +"xFY" = ( +/obj/machinery/light{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-06" + }, +/turf/open/floor/plasteel/white/corner{ + dir = 1 + }, +/area/hallway/secondary/entry) "xIa" = ( /obj/structure/table, /obj/effect/spawner/lootdrop/grille_or_trash, @@ -67194,7 +67519,7 @@ apN apN apN apJ -awY +xFY ayk awW aAD @@ -67218,7 +67543,7 @@ aaa aaa aaa arB -awY +uZN ayk awW aAD @@ -69790,7 +70115,7 @@ awW arB awZ aym -azB +qoW awW aaf aaa @@ -79544,7 +79869,7 @@ aKt aLN aMS aOi -aLE +tlO aPK aSl aTH @@ -80535,12 +80860,12 @@ adB aaa aaa aaa -aaa -aaa -aaa -aaa -aaa -aaa +acd +acd +acd +afr +acd +acd aaa aaa aaa @@ -80793,11 +81118,11 @@ abc abc afu abc -aaa -aaa -aaa -aaa -aaa +age +aeE +afs +ahw +acd aaa aaa aaa @@ -80852,7 +81177,7 @@ boS bfm bNK bkN -bml +bfm bwe bwe bwd @@ -81050,11 +81375,11 @@ aea aeH aft abc -aaa -aaa -aaa -aaa -aaa +agH +aeF +alr +ajt +afr aaa aaa aiU @@ -81308,10 +81633,10 @@ aeJ afw abc abc -aaf -aaa -aaf -aaf +aeF +aay +ajt +akd aaf aaf aiU @@ -81565,9 +81890,9 @@ aeI afv agf abc -aaf -aaa -aaa +aeF +aay +ajt aiT aiT aiV @@ -81600,7 +81925,7 @@ aKA aLN aMS aOz -aLE +qQP aPQ aSa aSr @@ -81822,9 +82147,9 @@ aeL afy agh abc -aaf -aaa -aaf +aeM +afz +aju aiT ajs akb @@ -82079,9 +82404,9 @@ aeK afx agg abc -aaf -aaa -aaa +ajX +alx +alY aiU ajr aka @@ -82335,13 +82660,13 @@ aeg aeN afA afA -afA -aaf -aaa -aaa -aiU -aju -akd +abc +afq +agv +ajY +akC +akE +akJ akK als ame @@ -82588,20 +82913,20 @@ acd acC ada adF +aad +abz aef -aeM -afz -aai -aai -aai -aai -aai -aai -ajt +aeD +aav akc -akJ -alr -amd +alM +alZ +akD +akF +alj +all +alm +alo amL anu alq @@ -82848,11 +83173,11 @@ adH aei aeO afJ -acd -agL -agK -agK -aiB +aai +aai +aai +aai +aai aai ajw akf @@ -83106,10 +83431,10 @@ aeh aeO afI agl -agH +agL ags ags -aho +aiB acd ajv ake @@ -85152,7 +85477,7 @@ aaG aaK aaP aaX -aat +hwu aat acd acD @@ -87731,7 +88056,7 @@ bkA acF aes avB -amN +afn agt awN aHp @@ -94720,7 +95045,7 @@ aYV aYV bet bfH -bhh +hEm bhh bhg bln @@ -97056,7 +97381,7 @@ bCR bqQ bGX bCR -bqQ +rjA bRN bIK bPq diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index cefc0eb1a5..2b14f91a11 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -1010,10 +1010,6 @@ }, /turf/open/floor/plasteel, /area/hallway/secondary/entry) -"aez" = ( -/obj/machinery/keycard_auth, -/turf/closed/wall, -/area/quartermaster/qm) "aeB" = ( /obj/machinery/status_display, /turf/closed/wall, @@ -1075,6 +1071,10 @@ "aeF" = ( /turf/open/floor/plating, /area/construction/mining/aux_base) +"aeH" = ( +/obj/structure/lattice, +/turf/open/space/basic, +/area/space/nearstation) "aeR" = ( /obj/structure/closet/emcloset, /obj/effect/decal/cleanable/dirt, @@ -4503,7 +4503,7 @@ /turf/open/floor/plasteel, /area/security/checkpoint) "anx" = ( -/obj/machinery/computer/prisoner{ +/obj/machinery/computer/prisoner/management{ dir = 8 }, /obj/machinery/status_display{ @@ -9270,7 +9270,6 @@ /obj/item/restraints/legcuffs/beartrap, /obj/item/restraints/legcuffs/beartrap, /obj/item/restraints/legcuffs/beartrap, -/obj/effect/decal/cleanable/cobweb, /obj/effect/turf_decal/bot, /obj/effect/turf_decal/tile/neutral{ dir = 1 @@ -9288,7 +9287,6 @@ /obj/machinery/status_display{ pixel_y = 32 }, -/obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/bot, /obj/machinery/vending/wardrobe/jani_wardrobe, /obj/effect/turf_decal/tile/neutral{ @@ -9336,7 +9334,6 @@ name = "Custodial RC"; pixel_y = 32 }, -/obj/effect/decal/cleanable/dirt, /obj/machinery/camera{ c_tag = "Custodial Closet"; dir = 2; @@ -9855,22 +9852,35 @@ /turf/open/floor/plasteel, /area/janitor) "awC" = ( -/obj/effect/decal/cleanable/dirt, /obj/effect/landmark/start/janitor, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 }, -/turf/open/floor/plating, +/obj/effect/turf_decal/tile/purple{ + dir = 4 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 8 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 1 + }, +/turf/open/floor/plasteel/white, /area/janitor) "awD" = ( -/obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ icon_state = "1-2" }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 }, -/turf/open/floor/plating, +/obj/effect/turf_decal/tile/green{ + dir = 4 + }, +/obj/effect/turf_decal/tile/green{ + dir = 1 + }, +/turf/open/floor/plasteel/white, /area/janitor) "awE" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ @@ -9885,7 +9895,6 @@ /turf/open/floor/plasteel/white, /area/janitor) "awF" = ( -/obj/effect/decal/cleanable/dirt, /obj/effect/landmark/start/janitor, /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 8 @@ -9905,7 +9914,6 @@ pixel_x = 32 }, /obj/item/reagent_containers/glass/bucket, -/obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/bot, /obj/effect/turf_decal/tile/neutral{ dir = 1 @@ -10567,8 +10575,6 @@ }, /obj/item/storage/box/lights/mixed, /obj/item/lightreplacer, -/obj/effect/decal/cleanable/dirt, -/obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /obj/effect/turf_decal/tile/neutral{ dir = 1 @@ -10611,7 +10617,6 @@ /turf/open/floor/plasteel, /area/janitor) "axX" = ( -/obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/bot, /obj/effect/landmark/event_spawn, /obj/effect/turf_decal/tile/neutral{ @@ -10639,11 +10644,21 @@ /area/janitor) "axZ" = ( /obj/structure/janitorialcart, -/obj/effect/decal/cleanable/dirt, /obj/structure/sign/poster/official/work_for_a_future{ pixel_x = 32 }, -/turf/open/floor/plating, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/turf/open/floor/plasteel, /area/janitor) "aya" = ( /obj/effect/decal/cleanable/dirt, @@ -11231,7 +11246,6 @@ }, /obj/item/reagent_containers/glass/bucket, /obj/item/reagent_containers/glass/bucket, -/obj/effect/decal/cleanable/dirt, /obj/item/mop, /obj/item/mop, /obj/item/radio/intercom{ @@ -11274,15 +11288,12 @@ /obj/machinery/light_switch{ pixel_y = -26 }, -/obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ icon_state = "1-8" }, +/obj/effect/turf_decal/tile/green, /obj/effect/turf_decal/tile/green{ - dir = 1 - }, -/obj/effect/turf_decal/tile/green{ - dir = 4 + dir = 8 }, /turf/open/floor/plasteel/white, /area/janitor) @@ -11301,7 +11312,6 @@ /turf/open/floor/plasteel, /area/janitor) "aze" = ( -/obj/effect/decal/cleanable/dirt, /obj/structure/disposalpipe/segment{ dir = 6 }, @@ -11323,7 +11333,6 @@ /obj/structure/extinguisher_cabinet{ pixel_x = 26 }, -/obj/effect/decal/cleanable/dirt, /obj/machinery/light/small, /obj/structure/disposalpipe/trunk{ dir = 8 @@ -11774,11 +11783,15 @@ /area/engine/atmospherics_engine) "aAb" = ( /turf/closed/wall, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aAc" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/closed/wall, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aAd" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/tile/neutral{ @@ -11824,7 +11837,6 @@ /turf/closed/wall, /area/janitor) "aAi" = ( -/obj/effect/decal/cleanable/dirt, /obj/machinery/door/airlock{ name = "Custodial Closet"; req_access_txt = "26" @@ -11881,6 +11893,7 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, +/obj/effect/decal/cleanable/dirt, /turf/open/floor/plasteel, /area/maintenance/port/fore) "aAm" = ( @@ -12358,7 +12371,9 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aBh" = ( /obj/item/twohanded/required/kirbyplants/random, /obj/machinery/light/small{ @@ -12367,27 +12382,27 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aBi" = ( /obj/structure/table, /obj/item/clothing/suit/apron/overalls, /obj/item/cultivator, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aBj" = ( /obj/machinery/biogenerator, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aBk" = ( /obj/structure/table, -/obj/item/seeds/poppy/lily{ - pixel_x = 4; - pixel_y = 4 - }, -/obj/item/seeds/poppy/geranium, -/obj/effect/decal/cleanable/dirt, /obj/item/reagent_containers/food/snacks/grown/wheat, /obj/item/reagent_containers/food/snacks/grown/corn, /obj/item/reagent_containers/food/snacks/grown/apple, @@ -12395,15 +12410,23 @@ /obj/machinery/airalarm{ pixel_y = 23 }, +/obj/item/seeds/poppy/lily{ + pixel_x = 4; + pixel_y = 4 + }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aBl" = ( /obj/machinery/hydroponics/soil, /obj/item/seeds/tea, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aBm" = ( /obj/item/twohanded/required/kirbyplants/random, /obj/machinery/status_display{ @@ -12411,18 +12434,21 @@ }, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aBn" = ( /obj/structure/table, /obj/item/stack/packageWrap, -/obj/effect/decal/cleanable/dirt, /obj/item/hand_labeler, /obj/item/reagent_containers/food/snacks/grown/tea, /obj/item/reagent_containers/food/snacks/grown/grapes, /obj/item/reagent_containers/food/snacks/grown/cherries, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aBo" = ( /obj/item/twohanded/required/kirbyplants/random, /obj/machinery/light/small{ @@ -12432,14 +12458,18 @@ /obj/effect/turf_decal/delivery, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aBp" = ( /obj/machinery/hydroponics/soil, /obj/item/seeds/poppy, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aBq" = ( /obj/structure/cable/white{ icon_state = "2-4" @@ -12537,7 +12567,6 @@ /turf/open/floor/plating, /area/maintenance/port/fore) "aBx" = ( -/obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ icon_state = "4-8" }, @@ -12557,7 +12586,6 @@ /turf/open/floor/plasteel, /area/maintenance/port/fore) "aBy" = ( -/obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ icon_state = "1-8" }, @@ -12602,7 +12630,6 @@ /turf/open/floor/plasteel, /area/maintenance/port/fore) "aBB" = ( -/obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 }, @@ -12624,16 +12651,6 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/turf/open/floor/plating, -/area/maintenance/port/fore) -"aBD" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 4 - }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, /obj/effect/turf_decal/tile/neutral{ dir = 1 }, @@ -13032,26 +13049,34 @@ /turf/closed/wall/r_wall, /area/engine/atmospherics_engine) "aCv" = ( -/obj/effect/decal/cleanable/dirt, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aCw" = ( +/obj/effect/turf_decal/delivery, +/obj/machinery/hydroponics/soil, +/obj/item/seeds/watermelon, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aCx" = ( -/obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 4 }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aCy" = ( -/obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ dir = 4 }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aCz" = ( /obj/structure/cable/white{ icon_state = "1-2" @@ -13080,13 +13105,19 @@ /turf/closed/wall, /area/maintenance/port/fore) "aCD" = ( -/obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 }, /obj/structure/disposalpipe/segment, -/turf/open/floor/plating, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/turf/open/floor/plasteel, /area/maintenance/port/fore) "aCE" = ( /obj/structure/cable/white{ @@ -13101,7 +13132,6 @@ /turf/open/floor/plasteel, /area/maintenance/port/fore) "aCF" = ( -/obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ icon_state = "4-8" }, @@ -13134,10 +13164,16 @@ icon_state = "4-8" }, /obj/machinery/atmospherics/pipe/manifold/supply/hidden, -/turf/open/floor/plating, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/plasteel, /area/maintenance/port/fore) "aCI" = ( -/obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ icon_state = "4-8" }, @@ -13658,15 +13694,21 @@ }, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aDy" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aDz" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aDA" = ( /obj/structure/sink{ dir = 4; @@ -13677,7 +13719,9 @@ }, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aDB" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, @@ -14188,37 +14232,38 @@ /turf/open/space, /area/space/nearstation) "aEB" = ( -/obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ icon_state = "2-4" }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aEC" = ( /obj/structure/cable/white{ icon_state = "4-8" }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aED" = ( -/obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ icon_state = "4-8" }, /obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aEE" = ( /obj/structure/cable/white{ icon_state = "2-8" }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) -"aEF" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aEG" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ @@ -14854,14 +14899,18 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aFK" = ( /obj/machinery/hydroponics/soil, /obj/item/seeds/harebell, /obj/machinery/light/small, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aFL" = ( /obj/structure/table, /obj/item/shovel/spade, @@ -14872,47 +14921,54 @@ pixel_x = -5 }, /obj/item/reagent_containers/syringe, -/obj/effect/decal/cleanable/dirt, /obj/structure/cable/white, /obj/machinery/power/apc{ - dir = 2; - name = "Abandoned Garden APC"; areastring = "/area/hydroponics/garden/abandoned"; + dir = 2; + name = "Maintenance Garden APC"; pixel_y = -26 }, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aFM" = ( -/obj/machinery/seed_extractor, /obj/machinery/status_display{ pixel_y = -32 }, -/obj/item/reagent_containers/glass/bucket, /obj/effect/turf_decal/delivery, +/obj/machinery/hydroponics/soil, +/obj/item/seeds/poppy/geranium, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aFN" = ( /obj/structure/table, /obj/item/plant_analyzer, /obj/item/hatchet, -/obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aFO" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aFP" = ( -/obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ icon_state = "1-2" }, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aFQ" = ( /obj/structure/table, /obj/item/crowbar, @@ -14920,7 +14976,9 @@ /obj/item/reagent_containers/glass/bucket, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aFR" = ( /obj/machinery/hydroponics/soil, /obj/item/seeds/tower, @@ -14929,7 +14987,9 @@ /obj/effect/turf_decal/delivery, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aFS" = ( /obj/structure/cable/white{ icon_state = "1-2" @@ -15021,6 +15081,9 @@ location = "Bar" }, /obj/effect/turf_decal/delivery, +/obj/machinery/door/window/eastleft{ + req_access_txt = "25" + }, /turf/open/floor/plasteel, /area/crew_quarters/bar) "aGc" = ( @@ -15922,18 +15985,16 @@ /area/engine/atmospherics_engine) "aHl" = ( /obj/effect/spawner/structure/window/reinforced, -/obj/structure/barricade/wooden, /turf/open/floor/plating, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aHm" = ( /obj/machinery/door/airlock/maintenance_hatch{ name = "Maintenance Hatch"; req_access_txt = "12" }, -/obj/effect/decal/cleanable/dirt, -/obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/simple/supply/hidden, -/obj/structure/barricade/wooden, /obj/effect/turf_decal/stripes/line{ dir = 2 }, @@ -15941,17 +16002,17 @@ dir = 1 }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aHn" = ( /obj/machinery/door/airlock/maintenance_hatch{ name = "Maintenance Hatch"; req_access_txt = "12" }, -/obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ icon_state = "1-2" }, -/obj/structure/barricade/wooden, /obj/effect/turf_decal/stripes/line{ dir = 2 }, @@ -15959,13 +16020,16 @@ dir = 1 }, /turf/open/floor/plasteel, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aHo" = ( /obj/effect/spawner/structure/window/reinforced, -/obj/structure/barricade/wooden, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plating, -/area/hydroponics/garden/abandoned) +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "aHp" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ @@ -16794,7 +16858,6 @@ /turf/open/floor/plasteel, /area/maintenance/port/fore) "aII" = ( -/obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 5 }, @@ -16808,7 +16871,8 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 }, -/turf/open/floor/plating, +/obj/effect/turf_decal/delivery, +/turf/open/floor/plasteel, /area/maintenance/port/fore) "aIK" = ( /obj/structure/cable/white{ @@ -16818,10 +16882,18 @@ dir = 4 }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/turf/open/floor/plating, +/obj/machinery/light/small{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/turf/open/floor/plasteel, /area/maintenance/port/fore) "aIL" = ( -/obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ icon_state = "4-8" }, @@ -17687,10 +17759,16 @@ /area/engine/atmospherics_engine) "aKg" = ( /obj/structure/closet/emcloset, -/turf/open/floor/plating, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/plasteel, /area/maintenance/port/fore) "aKh" = ( -/obj/effect/decal/cleanable/dirt, /obj/structure/rack, /obj/effect/spawner/lootdrop/maintenance{ lootcount = 2; @@ -17704,7 +17782,11 @@ /area/maintenance/port/fore) "aKi" = ( /obj/structure/closet, -/turf/open/floor/plating, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/plasteel, /area/maintenance/port/fore) "aKj" = ( /obj/effect/decal/cleanable/dirt, @@ -17715,16 +17797,20 @@ /turf/open/floor/plasteel, /area/maintenance/port/fore) "aKk" = ( -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /obj/effect/turf_decal/tile/neutral, /obj/effect/turf_decal/tile/neutral{ dir = 8 }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel, /area/maintenance/port/fore) "aKl" = ( /obj/effect/landmark/event_spawn, /obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral, /obj/effect/turf_decal/tile/neutral{ dir = 8 }, @@ -21299,6 +21385,9 @@ location = "Theatre" }, /obj/effect/turf_decal/delivery, +/obj/machinery/door/window/westleft{ + req_access_txt = "46" + }, /turf/open/floor/plasteel, /area/crew_quarters/theatre) "aQj" = ( @@ -26153,6 +26242,9 @@ dir = 4 }, /obj/effect/turf_decal/delivery, +/obj/machinery/door/window/eastright{ + req_one_access_txt = "48,50" + }, /turf/open/floor/plasteel, /area/quartermaster/office) "aXg" = ( @@ -27548,7 +27640,6 @@ /turf/open/floor/plasteel, /area/quartermaster/qm) "aZh" = ( -/obj/structure/table/reinforced, /obj/machinery/light{ dir = 4 }, @@ -27559,6 +27650,9 @@ /obj/effect/turf_decal/tile/brown{ dir = 8 }, +/obj/machinery/computer/card/minor/qm{ + dir = 8 + }, /turf/open/floor/plasteel, /area/quartermaster/qm) "aZi" = ( @@ -29213,6 +29307,9 @@ location = "Kitchen" }, /obj/effect/turf_decal/delivery, +/obj/machinery/door/window/eastleft{ + req_access_txt = "28" + }, /turf/open/floor/plasteel, /area/crew_quarters/kitchen) "bcd" = ( @@ -30865,6 +30962,9 @@ location = "Hydroponics" }, /obj/effect/turf_decal/delivery, +/obj/machinery/door/window/westleft{ + req_access_txt = "35" + }, /turf/open/floor/plasteel, /area/hallway/secondary/service) "beW" = ( @@ -36055,6 +36155,9 @@ /obj/structure/plasticflaps/opaque, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery, +/obj/machinery/door/window/northright{ + req_access_txt = "48" + }, /turf/open/floor/plasteel, /area/maintenance/starboard/fore) "bnB" = ( @@ -36400,7 +36503,7 @@ /turf/open/floor/plasteel/grimy, /area/crew_quarters/heads/hos) "boc" = ( -/obj/machinery/computer/prisoner{ +/obj/machinery/computer/prisoner/management{ dir = 8 }, /turf/open/floor/plasteel/grimy, @@ -38706,7 +38809,7 @@ /turf/open/floor/plasteel/grimy, /area/crew_quarters/heads/hos) "brE" = ( -/obj/machinery/computer/prisoner{ +/obj/machinery/computer/prisoner/management{ dir = 8 }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ @@ -39195,13 +39298,6 @@ name = "Hydroponics Desk"; req_access_txt = "35" }, -/obj/item/seeds/lime{ - pixel_x = 6 - }, -/obj/item/seeds/watermelon, -/obj/item/seeds/grape{ - pixel_x = -6 - }, /obj/item/reagent_containers/food/snacks/grown/wheat, /obj/item/reagent_containers/food/snacks/grown/watermelon, /obj/item/reagent_containers/food/snacks/grown/banana, @@ -39610,7 +39706,7 @@ /turf/open/floor/plasteel/dark, /area/security/execution/transfer) "bth" = ( -/obj/machinery/computer/gulag_teleporter_computer{ +/obj/machinery/computer/prisoner/gulag_teleporter_computer{ dir = 1 }, /obj/structure/disposalpipe/segment{ @@ -41789,6 +41885,9 @@ location = "Atmospherics" }, /obj/effect/turf_decal/delivery, +/obj/machinery/door/window/westleft{ + req_access_txt = "24" + }, /turf/open/floor/plasteel, /area/engine/atmos) "bwH" = ( @@ -42723,7 +42822,7 @@ /turf/open/floor/plasteel/dark, /area/bridge) "byk" = ( -/obj/machinery/computer/prisoner, +/obj/machinery/computer/prisoner/management, /obj/effect/turf_decal/tile/red{ dir = 1 }, @@ -56938,7 +57037,7 @@ /turf/open/floor/plasteel, /area/security/warden) "bTh" = ( -/obj/machinery/computer/prisoner{ +/obj/machinery/computer/prisoner/management{ dir = 8 }, /obj/structure/cable/white{ @@ -71753,6 +71852,9 @@ location = "Security" }, /obj/effect/turf_decal/delivery, +/obj/machinery/door/window/northleft{ + req_access_txt = "63" + }, /turf/open/floor/plasteel, /area/security/range) "cqh" = ( @@ -82547,6 +82649,9 @@ location = "Engineering" }, /obj/effect/turf_decal/delivery, +/obj/machinery/door/window/northright{ + req_access_txt = "10" + }, /turf/open/floor/plasteel, /area/engine/engineering) "cIr" = ( @@ -87734,6 +87839,9 @@ location = "Medbay" }, /obj/effect/turf_decal/delivery, +/obj/machinery/door/window/westleft{ + req_access_txt = "5" + }, /turf/open/floor/plasteel, /area/medical/storage) "cRl" = ( @@ -88597,6 +88705,9 @@ /obj/effect/turf_decal/stripes/line{ dir = 4 }, +/obj/effect/mapping_helpers/airlock/unres{ + dir = 4 + }, /turf/open/floor/plasteel, /area/medical/medbay/central) "cSK" = ( @@ -89771,6 +89882,9 @@ /obj/effect/turf_decal/stripes/line{ dir = 4 }, +/obj/effect/mapping_helpers/airlock/unres{ + dir = 4 + }, /turf/open/floor/plasteel, /area/medical/medbay/central) "cUD" = ( @@ -114081,6 +114195,9 @@ location = "Robotics" }, /obj/effect/turf_decal/delivery, +/obj/machinery/door/window/northright{ + req_access_txt = "47" + }, /turf/open/floor/plasteel, /area/science/robotics/lab) "dKl" = ( @@ -120412,7 +120529,6 @@ name = "Station Intercom"; pixel_x = -26 }, -/obj/item/clothing/under/burial, /obj/effect/turf_decal/tile/neutral{ dir = 1 }, @@ -122476,14 +122592,19 @@ /turf/open/floor/plating, /area/maintenance/port/aft) "dZM" = ( -/obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ icon_state = "1-2" }, /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 9 }, -/turf/open/floor/plating, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/turf/open/floor/plasteel, /area/maintenance/port/aft) "dZN" = ( /turf/closed/wall/r_wall, @@ -122835,11 +122956,6 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 5 }, -/obj/machinery/button/crematorium{ - id = "cremawheat"; - pixel_x = -26; - req_access_txt = "27" - }, /obj/effect/turf_decal/tile/neutral{ dir = 1 }, @@ -123160,24 +123276,6 @@ /obj/structure/sign/nanotrasen, /turf/closed/wall/r_wall, /area/maintenance/port/aft) -"ebd" = ( -/obj/structure/bodycontainer/morgue, -/obj/machinery/airalarm{ - dir = 4; - pixel_x = -22 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel/dark, -/area/chapel/office) "ebe" = ( /obj/machinery/light{ dir = 4 @@ -123536,12 +123634,6 @@ /turf/open/floor/plasteel, /area/maintenance/port/aft) "ebS" = ( -/obj/structure/table, -/obj/item/storage/box/gloves{ - pixel_x = 3; - pixel_y = 3 - }, -/obj/item/storage/box/bodybags, /obj/effect/turf_decal/tile/neutral{ dir = 1 }, @@ -123552,6 +123644,9 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, /turf/open/floor/plasteel/dark, /area/chapel/office) "ebT" = ( @@ -123827,6 +123922,7 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, +/obj/structure/closet/crate/coffin, /turf/open/floor/plasteel/dark, /area/chapel/office) "ecx" = ( @@ -125917,7 +126013,7 @@ /turf/open/floor/plasteel, /area/security/checkpoint/escape) "egj" = ( -/obj/machinery/computer/prisoner{ +/obj/machinery/computer/prisoner/management{ dir = 1 }, /obj/structure/cable/white{ @@ -126276,6 +126372,20 @@ }, /turf/open/floor/plasteel/dark, /area/library) +"enO" = ( +/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/effect/turf_decal/tile/neutral, +/obj/structure/bodycontainer/morgue, +/turf/open/floor/plasteel/dark, +/area/chapel/office) "exE" = ( /obj/machinery/air_sensor/atmos/toxins_mixing_tank, /turf/open/floor/engine/vacuum, @@ -126408,13 +126518,6 @@ /obj/machinery/door/airlock/research/glass/incinerator/toxmix_interior, /turf/open/floor/engine, /area/science/mixing) -"fwr" = ( -/obj/machinery/door/poddoor{ - id = "chapelgun"; - name = "Chapel Launcher Door" - }, -/turf/open/floor/plating, -/area/chapel/office) "fFK" = ( /obj/machinery/ore_silo, /obj/effect/turf_decal/tile/neutral{ @@ -126469,6 +126572,10 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/closed/wall/r_wall, /area/science/circuit) +"gFk" = ( +/obj/structure/grille, +/turf/open/space, +/area/space/nearstation) "gJj" = ( /obj/machinery/door/window/northleft{ name = "Mass Driver" @@ -126510,6 +126617,10 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 4 }, +/obj/machinery/airalarm{ + dir = 2; + pixel_y = 22 + }, /turf/open/floor/plasteel/dark, /area/chapel/office) "gNS" = ( @@ -126536,6 +126647,20 @@ /obj/effect/turf_decal/tile/purple, /turf/open/floor/plasteel/white, /area/science/research) +"gQw" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/turf/open/floor/plasteel{ + heat_capacity = 1e+006 + }, +/area/maintenance/port/aft) "gQS" = ( /turf/open/floor/plasteel/white/side{ dir = 9 @@ -126680,11 +126805,50 @@ /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, /area/science/research) +"ibC" = ( +/obj/effect/turf_decal/delivery, +/obj/machinery/hydroponics/soil, +/obj/item/seeds/lime{ + pixel_x = 6 + }, +/turf/open/floor/plasteel, +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) +"iiH" = ( +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/turf/open/floor/plasteel, +/area/maintenance/port/fore) +"ijd" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/turf/open/floor/plasteel/dark, +/area/chapel/office) "ijB" = ( /obj/structure/reagent_dispensers/keg/aphro/strong, /obj/item/reagent_containers/glass/beaker, /turf/open/floor/plating, /area/crew_quarters/abandoned_gambling_den) +"ivF" = ( +/obj/machinery/door/poddoor{ + id = "chapelgun"; + name = "Chapel Launcher Door" + }, +/obj/structure/fans/tiny, +/turf/open/floor/plating, +/area/chapel/office) "iwL" = ( /obj/machinery/status_display{ pixel_x = 32 @@ -126771,6 +126935,18 @@ /turf/open/floor/plasteel, /area/science/research/abandoned) "jhK" = ( +/obj/structure/rack, +/obj/effect/spawner/lootdrop/maintenance{ + lootcount = 2; + name = "2maintenance loot spawner" + }, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, /turf/open/floor/plasteel, /area/maintenance/port/aft) "jjN" = ( @@ -126881,15 +127057,6 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, -/obj/machinery/computer/pod/old{ - density = 0; - icon = 'icons/obj/airlock_machines.dmi'; - icon_state = "airlock_control_standby"; - id = "chapelgun"; - name = "Mass Driver Controller"; - pixel_x = 0; - pixel_y = -24 - }, /turf/open/floor/plasteel/dark, /area/chapel/office) "jRy" = ( @@ -126945,6 +127112,31 @@ }, /turf/open/floor/plasteel, /area/maintenance/port) +"kzw" = ( +/obj/machinery/button/crematorium{ + id = "cremawheat"; + pixel_x = -26; + req_access_txt = "27" + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/structure/table, +/obj/item/clothing/under/burial, +/obj/item/clothing/under/burial, +/obj/item/clothing/under/burial, +/obj/item/clothing/under/burial, +/obj/item/clothing/under/burial, +/obj/item/clothing/under/burial, +/turf/open/floor/plasteel/dark, +/area/chapel/office) "kLu" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 @@ -127109,6 +127301,14 @@ dir = 1 }, /area/science/circuit) +"mdk" = ( +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/machinery/light/small, +/turf/open/floor/plasteel, +/area/maintenance/port/fore) "mkm" = ( /obj/machinery/atmospherics/components/binary/valve, /obj/machinery/embedded_controller/radio/airlock_controller/incinerator_toxmix{ @@ -127189,7 +127389,11 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/plasteel/dark, /area/chapel/office) -"nht" = ( +"nyN" = ( +/obj/machinery/vending/kink, +/turf/open/floor/plating, +/area/crew_quarters/abandoned_gambling_den) +"nOV" = ( /obj/effect/turf_decal/tile/neutral{ dir = 8 }, @@ -127200,17 +127404,54 @@ dir = 4 }, /obj/effect/turf_decal/tile/neutral, -/obj/machinery/light, +/obj/machinery/light{ + dir = 8 + }, /turf/open/floor/plasteel/dark, /area/chapel/office) -"nyN" = ( -/obj/machinery/vending/kink, -/turf/open/floor/plating, -/area/crew_quarters/abandoned_gambling_den) "nSh" = ( /obj/machinery/atmospherics/pipe/simple/general/hidden, /turf/closed/wall/r_wall, /area/maintenance/disposal/incinerator) +"oaP" = ( +/obj/machinery/seed_extractor, +/obj/item/reagent_containers/glass/bucket, +/obj/effect/turf_decal/delivery, +/turf/open/floor/plasteel, +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) +"obe" = ( +/obj/structure/cable/white{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/plasteel, +/area/maintenance/port/fore) +"ost" = ( +/obj/structure/filingcabinet/chestdrawer, +/obj/effect/turf_decal/tile/brown{ + dir = 1 + }, +/obj/effect/turf_decal/tile/brown{ + dir = 4 + }, +/obj/effect/turf_decal/tile/brown{ + dir = 8 + }, +/obj/machinery/keycard_auth{ + pixel_x = -25 + }, +/turf/open/floor/plasteel, +/area/quartermaster/qm) "owr" = ( /obj/effect/turf_decal/tile/neutral{ dir = 1 @@ -127224,6 +127465,29 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/plasteel/dark, /area/chapel/office) +"owI" = ( +/obj/structure/cable/white{ + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/turf/open/floor/plasteel, +/area/maintenance/port/fore) +"ozr" = ( +/obj/effect/turf_decal/delivery, +/obj/machinery/hydroponics/soil, +/obj/item/seeds/grape, +/turf/open/floor/plasteel, +/area/hydroponics/garden/abandoned{ + name = "Maintenance Garden" + }) "oIl" = ( /obj/machinery/atmospherics/components/unary/portables_connector/visible{ dir = 1 @@ -127273,6 +127537,19 @@ }, /turf/open/floor/plasteel/dark, /area/science/mixing) +"oOb" = ( +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/machinery/door/window{ + req_one_access_txt = "48,50" + }, +/obj/effect/turf_decal/bot, +/turf/open/floor/plasteel, +/area/quartermaster/sorting) "oSD" = ( /obj/machinery/meter, /obj/machinery/atmospherics/pipe/manifold/general/visible{ @@ -127363,6 +127640,15 @@ }, /turf/open/floor/plating, /area/science/research/abandoned) +"pHf" = ( +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/turf/open/floor/plasteel, +/area/maintenance/port/fore) "pQm" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable/white{ @@ -127408,6 +127694,13 @@ }, /turf/open/floor/plating, /area/chapel/office) +"rbY" = ( +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/closet/crate/coffin, +/turf/open/floor/plating, +/area/chapel/office) "rhO" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 1 @@ -127455,6 +127748,16 @@ }, /turf/open/floor/plasteel, /area/quartermaster/miningoffice) +"rYR" = ( +/obj/structure/cable/white{ + icon_state = "4-8" + }, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/turf/open/floor/plasteel, +/area/maintenance/port/aft) "saw" = ( /turf/closed/wall, /area/science/circuit) @@ -127466,6 +127769,13 @@ /obj/machinery/door/poddoor/incinerator_toxmix, /turf/open/floor/engine/vacuum, /area/science/mixing) +"tbC" = ( +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/plasteel, +/area/maintenance/port/fore) "tmi" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -127479,14 +127789,58 @@ }, /turf/open/floor/plasteel, /area/crew_quarters/fitness/recreation) +"tAi" = ( +/obj/structure/reagent_dispensers/watertank, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/plasteel, +/area/maintenance/port/fore) "tCh" = ( /turf/closed/wall, /area/science/misc_lab) +"tEW" = ( +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/structure/bodycontainer/morgue, +/turf/open/floor/plasteel/dark, +/area/chapel/office) "tMk" = ( /turf/open/floor/plasteel/white/side{ dir = 10 }, /area/science/misc_lab) +"tOQ" = ( +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/machinery/computer/pod/old{ + density = 0; + icon = 'icons/obj/airlock_machines.dmi'; + icon_state = "airlock_control_standby"; + id = "chapelgun"; + name = "Mass Driver Controller"; + pixel_x = -24; + pixel_y = 0 + }, +/turf/open/floor/plasteel/dark, +/area/chapel/office) "tRT" = ( /obj/effect/turf_decal/tile/blue{ dir = 4 @@ -127557,14 +127911,6 @@ }, /turf/open/floor/engine, /area/engine/supermatter) -"uZN" = ( -/obj/structure/rack, -/obj/effect/spawner/lootdrop/maintenance{ - lootcount = 2; - name = "2maintenance loot spawner" - }, -/turf/open/floor/plasteel, -/area/maintenance/port/aft) "vhA" = ( /obj/item/clothing/under/color/grey, /turf/open/floor/plating, @@ -127578,10 +127924,45 @@ }, /turf/open/floor/plasteel/white, /area/science/mixing) +"vFw" = ( +/obj/machinery/door/window/northright{ + name = "Coffin Storage" + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/closet/crate/coffin, +/turf/open/floor/plating, +/area/chapel/office) +"vON" = ( +/obj/structure/cable/white{ + icon_state = "4-8" + }, +/obj/structure/cable/white{ + icon_state = "2-4" + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/turf/open/floor/plasteel, +/area/maintenance/port/aft) "wei" = ( /obj/effect/turf_decal/stripes/line, /turf/open/floor/plasteel, /area/science/circuit) +"whX" = ( +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/turf/open/floor/plasteel, +/area/maintenance/port/fore) "wAA" = ( /obj/structure/sign/nanotrasen, /turf/closed/wall/r_wall, @@ -127703,9 +128084,6 @@ /turf/open/floor/plasteel, /area/science/research) "xWZ" = ( -/obj/structure/bodycontainer/morgue{ - dir = 2 - }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 }, @@ -156627,7 +157005,7 @@ arB aug alg alg -aAd +whX aKg alf aMO @@ -156884,7 +157262,7 @@ aAb aAb aAb aAb -arB +pHf aKh alf aMN @@ -157138,11 +157516,11 @@ aAb aBg aCv aDx -aCw +aCv aFJ aAb aIH -aoY +tAi alf aMP aDw @@ -157393,8 +157771,8 @@ axI ayU aAb aBh -aCw -aCw +aCv +aCv aCv aFK aHl @@ -157650,13 +158028,13 @@ axJ aoY aAb aBi -aCw +aCv aCw aEB aFL aAb -arB -alg +aIH +tbC avm arB arB @@ -157908,11 +158286,11 @@ alf aAb aBj aCv -aCw +oaP aEC aFM aAb -alg +aIH ary aLz aLz @@ -158039,7 +158417,7 @@ edL eep eff aad -ajr +aeH aaa ajr aaa @@ -158165,11 +158543,11 @@ alg aAb aBk aCv -aCw +ozr aEC aFN aAb -arA +aIH aKi aLz aMQ @@ -158296,7 +158674,7 @@ edM eeq dLY aad -aad +gFk aaa aad aaa @@ -158421,13 +158799,13 @@ axL ayV aAb aBl -aCw +aCv aDy aED aFO aHm aII -aKj +mdk aLz aMR aOq @@ -158678,13 +159056,13 @@ axM ayW aAb aBm -aCw -aCw +aCv +aCv aEE aFP aHn aIJ -aKj +iiH aLz aMS aOr @@ -158936,12 +159314,12 @@ ayX aAb aBn aCx -aCw -aCw +ibC +aCv aFQ aAb -asQ -arB +owI +iiH aLz aMT aOs @@ -159194,7 +159572,7 @@ aAc aBo aCy aDz -aEF +aDz aFR aHo aIK @@ -159314,7 +159692,7 @@ dXz dYt dON dOl -eap +vON eaX dYu ect @@ -159449,7 +159827,7 @@ alf alf aAb aBp -aCw +aCv aDA aCv aFJ @@ -159570,8 +159948,8 @@ dON dON dON dON -dZJ -ean +gQw +rYR ebc ebQ ecu @@ -159712,7 +160090,7 @@ aAb aAb aAb aIL -arB +tbC aLz aLz aLz @@ -159835,10 +160213,10 @@ dZN dZN fHS dZN -dLY +dZN dLY aad -ajr +aeH aad aad aaa @@ -159969,7 +160347,7 @@ aEG aFS aHp aIM -aEG +obe aLB aFS aEG @@ -160084,15 +160462,15 @@ dWJ dLW dYu dZg -jhK +hLO jhK dZN owr -mXJ +nOV +tOQ gJj qMR -fwr -aaa +ivF aaa iyd aaa @@ -160341,15 +160719,15 @@ dTw dTw dTw dTw -hLO -uZN +dZN +dZN hei gNJ jPA +jPA +vFw +rbY dTw -dTw -dTw -aad aad aad aad @@ -160590,7 +160968,7 @@ dPJ dQC dRB dSz -dTx +tEW dUi dTx dVN @@ -160598,11 +160976,11 @@ dTx dXB dYv dTw -dZN -dZN -dZN +dZO +kzw +enO xWZ -nht +mXJ dTw dTw dTw @@ -160855,9 +161233,9 @@ dUj dXC dYw dZh -dZO +ijd eau -ebd +edr ebS ecw dTw @@ -162790,7 +163168,7 @@ axX azd aAj aBA -asQ +asK aDL aEO aGc @@ -163047,7 +163425,7 @@ axY aze aAk aBB -asM +asK aDL aEP aGd @@ -163560,7 +163938,7 @@ auh auh auh auh -aBD +aBz aCG aDL aER @@ -167677,7 +168055,7 @@ aCP avQ aFe aGp -aHH +oOb aJg aKF aLP @@ -174623,7 +175001,7 @@ aaa aaa aad aQQ -aez +aQQ aUp aVY aXE @@ -174880,7 +175258,7 @@ aaa aaa aad aQR -aSs +ost aUq aVR aXF diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 75d7466371..93bd61964c 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -2017,7 +2017,7 @@ /turf/open/floor/plasteel/dark, /area/crew_quarters/heads/hos) "aeu" = ( -/obj/machinery/computer/prisoner, +/obj/machinery/computer/prisoner/management, /turf/open/floor/plasteel/dark, /area/crew_quarters/heads/hos) "aev" = ( @@ -8416,7 +8416,7 @@ /turf/open/floor/plating, /area/security/warden) "apI" = ( -/obj/machinery/computer/prisoner{ +/obj/machinery/computer/prisoner/management{ dir = 4 }, /obj/structure/cable/yellow{ @@ -10514,7 +10514,7 @@ /turf/open/floor/plating, /area/maintenance/port/fore) "atK" = ( -/obj/machinery/computer/gulag_teleporter_computer{ +/obj/machinery/computer/prisoner/gulag_teleporter_computer{ dir = 1 }, /turf/open/floor/plasteel/dark, @@ -20376,6 +20376,7 @@ /obj/machinery/keycard_auth{ pixel_y = 23 }, +/obj/item/clipboard, /turf/open/floor/plasteel, /area/quartermaster/qm) "aNS" = ( @@ -21064,6 +21065,7 @@ departmentType = 2; pixel_x = 32 }, +/obj/item/stamp/qm, /turf/open/floor/plasteel, /area/quartermaster/qm) "aPl" = ( @@ -21619,12 +21621,12 @@ /turf/open/floor/plasteel, /area/quartermaster/qm) "aQt" = ( -/obj/structure/table, -/obj/item/clipboard, -/obj/item/stamp/qm, /obj/machinery/status_display/supply{ pixel_x = 32 }, +/obj/machinery/computer/card/minor/qm{ + dir = 8 + }, /turf/open/floor/plasteel, /area/quartermaster/qm) "aQu" = ( @@ -30781,7 +30783,7 @@ /obj/structure/cable/yellow{ icon_state = "2-4" }, -/obj/machinery/computer/prisoner, +/obj/machinery/computer/prisoner/management, /obj/effect/turf_decal/tile/red{ dir = 1 }, @@ -57384,6 +57386,7 @@ /obj/effect/turf_decal/tile/blue{ dir = 8 }, +/obj/effect/mapping_helpers/airlock/unres, /turf/open/floor/plasteel/white, /area/medical/medbay/central) "cha" = ( @@ -57404,6 +57407,7 @@ /obj/effect/turf_decal/tile/blue{ dir = 8 }, +/obj/effect/mapping_helpers/airlock/unres, /turf/open/floor/plasteel/white, /area/medical/medbay/central) "chb" = ( @@ -66401,6 +66405,9 @@ /obj/effect/turf_decal/tile/blue{ dir = 8 }, +/obj/effect/mapping_helpers/airlock/unres{ + dir = 4 + }, /turf/open/floor/plasteel/white, /area/medical/genetics/cloning) "cxZ" = ( diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm index 0c55f05fcb..5202f170d0 100644 --- a/_maps/map_files/OmegaStation/OmegaStation.dmm +++ b/_maps/map_files/OmegaStation/OmegaStation.dmm @@ -27287,6 +27287,9 @@ /obj/effect/turf_decal/stripes/line{ dir = 1 }, +/obj/effect/mapping_helpers/airlock/unres{ + dir = 1 + }, /turf/open/floor/plasteel, /area/medical/medbay/zone3) "aWM" = ( @@ -28598,6 +28601,9 @@ /obj/effect/turf_decal/stripes/line{ dir = 4 }, +/obj/effect/mapping_helpers/airlock/unres{ + dir = 8 + }, /turf/open/floor/plasteel, /area/medical/medbay/zone3) "aYU" = ( @@ -29134,6 +29140,9 @@ /obj/effect/turf_decal/stripes/line{ dir = 4 }, +/obj/effect/mapping_helpers/airlock/unres{ + dir = 8 + }, /turf/open/floor/plasteel, /area/medical/medbay/zone3) "aZQ" = ( diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm index ee97af14fc..d43f5927a0 100644 --- a/_maps/map_files/PubbyStation/PubbyStation.dmm +++ b/_maps/map_files/PubbyStation/PubbyStation.dmm @@ -4897,7 +4897,7 @@ /turf/open/floor/plasteel/showroomfloor, /area/security/warden) "amT" = ( -/obj/machinery/computer/prisoner, +/obj/machinery/computer/prisoner/management, /obj/machinery/airalarm{ pixel_y = 22 }, @@ -6072,7 +6072,7 @@ /turf/open/floor/plating, /area/security/brig) "apF" = ( -/obj/machinery/computer/gulag_teleporter_computer{ +/obj/machinery/computer/prisoner/gulag_teleporter_computer{ dir = 1 }, /turf/open/floor/plasteel/dark, @@ -7048,7 +7048,7 @@ /turf/open/floor/plasteel/dark, /area/bridge) "arR" = ( -/obj/machinery/computer/prisoner, +/obj/machinery/computer/prisoner/management, /obj/effect/turf_decal/tile/red{ dir = 1 }, @@ -24477,16 +24477,11 @@ /turf/open/floor/plasteel/dark, /area/science/robotics/mechbay) "bfB" = ( -/obj/structure/table, /obj/machinery/requests_console{ department = "Cargo Bay"; departmentType = 2; pixel_x = -30 }, -/obj/item/paper_bin{ - layer = 2.9 - }, -/obj/item/pen, /obj/effect/turf_decal/tile/brown{ dir = 1 }, @@ -24494,6 +24489,9 @@ /obj/effect/turf_decal/tile/brown{ dir = 8 }, +/obj/machinery/computer/card/minor/qm{ + dir = 4 + }, /turf/open/floor/plasteel, /area/quartermaster/qm) "bfC" = ( @@ -27566,6 +27564,7 @@ }, /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/effect/mapping_helpers/airlock/unres, /turf/open/floor/plasteel/freezer, /area/storage/emergency/port) "bnx" = ( @@ -29314,6 +29313,9 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/effect/mapping_helpers/airlock/unres{ + dir = 8 + }, /turf/open/floor/plasteel/white, /area/medical/genetics) "brb" = ( @@ -30153,6 +30155,7 @@ /obj/effect/turf_decal/tile/blue{ dir = 8 }, +/obj/effect/mapping_helpers/airlock/unres, /turf/open/floor/plasteel/white, /area/medical/medbay/central) "bsI" = ( @@ -30164,6 +30167,7 @@ name = "Medbay" }, /obj/effect/turf_decal/tile/blue, +/obj/effect/mapping_helpers/airlock/unres, /turf/open/floor/plasteel/white, /area/medical/medbay/central) "bsJ" = ( @@ -47466,8 +47470,8 @@ /area/hydroponics/garden/monastery) "cgL" = ( /obj/structure/closet/cabinet, -/obj/item/clothing/suit/holidaypriest, -/obj/item/clothing/suit/nun, +/obj/item/clothing/suit/chaplain/holidaypriest, +/obj/item/clothing/suit/chaplain/nun, /obj/item/clothing/head/nun_hood, /obj/machinery/button/door{ id = "Cell1"; @@ -47834,8 +47838,8 @@ /area/space/nearstation) "cio" = ( /obj/structure/closet/cabinet, -/obj/item/clothing/suit/holidaypriest, -/obj/item/clothing/suit/nun, +/obj/item/clothing/suit/chaplain/holidaypriest, +/obj/item/clothing/suit/chaplain/nun, /obj/item/clothing/head/nun_hood, /obj/machinery/button/door{ id = "Cell2"; @@ -50476,8 +50480,8 @@ /obj/structure/closet, /obj/item/storage/backpack/cultpack, /obj/item/clothing/head/nun_hood, -/obj/item/clothing/suit/nun, -/obj/item/clothing/suit/holidaypriest, +/obj/item/clothing/suit/chaplain/nun, +/obj/item/clothing/suit/chaplain/holidaypriest, /obj/effect/turf_decal/tile/neutral{ dir = 1 }, @@ -62094,6 +62098,7 @@ name = "Port Emergency Storage"; req_access_txt = "0" }, +/obj/effect/mapping_helpers/airlock/unres, /turf/open/floor/plasteel/freezer, /area/storage/emergency/port) "xSX" = ( diff --git a/_maps/map_files/debug/runtimestation.dmm b/_maps/map_files/debug/runtimestation.dmm index 3bdf64ac27..feb8fc8cc0 100644 --- a/_maps/map_files/debug/runtimestation.dmm +++ b/_maps/map_files/debug/runtimestation.dmm @@ -523,7 +523,7 @@ /turf/open/floor/plasteel, /area/science) "bC" = ( -/obj/machinery/computer/gulag_teleporter_computer{ +/obj/machinery/computer/prisoner/gulag_teleporter_computer{ dir = 4 }, /turf/open/floor/plasteel, diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index f2e0599ae6..80e80cd6a9 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -1264,9 +1264,9 @@ /area/holodeck/rec_center/chapelcourt) "dw" = ( /obj/structure/table/wood/fancy, -/obj/item/clothing/suit/nun, +/obj/item/clothing/suit/chaplain/nun, /obj/item/clothing/head/nun_hood, -/obj/item/clothing/suit/holidaypriest, +/obj/item/clothing/suit/chaplain/holidaypriest, /turf/open/floor/holofloor{ dir = 8; icon_state = "dark" @@ -3864,7 +3864,7 @@ /turf/open/floor/plasteel, /area/centcom/control) "jS" = ( -/obj/machinery/computer/prisoner, +/obj/machinery/computer/prisoner/management, /obj/effect/turf_decal/stripes/line, /turf/open/floor/plasteel, /area/centcom/control) @@ -5857,7 +5857,7 @@ /turf/open/floor/plasteel, /area/centcom/control) "nY" = ( -/obj/machinery/computer/prisoner{ +/obj/machinery/computer/prisoner/management{ dir = 1 }, /obj/effect/turf_decal/stripes/line{ @@ -10495,7 +10495,7 @@ /turf/open/floor/plasteel/dark, /area/centcom/control) "yv" = ( -/obj/machinery/computer/prisoner{ +/obj/machinery/computer/prisoner/management{ dir = 1 }, /obj/machinery/light, diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index 4ed099d9e2..7be3533d47 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -98,7 +98,15 @@ #define DRINKSBLOOD 15 #define NOEYES 16 #define MARKINGS 17 +#define MUTCOLORS2 18 +#define MUTCOLORS3 19 +#define NOAROUSAL 20 //Stops all arousal effects +#define NOGENITALS 21 //Cannot create, use, or otherwise have genitals +#define MATRIXED 22 //if icon is color matrix'd +#define SKINTONE 23 //uses skin tones +#define HORNCOLOR 24 +//organ slots #define ORGAN_SLOT_BRAIN "brain" #define ORGAN_SLOT_APPENDIX "appendix" #define ORGAN_SLOT_RIGHT_ARM_AUG "r_arm_device" @@ -122,3 +130,8 @@ #define ORGAN_SLOT_BRAIN_ANTISTUN "brain_antistun" #define ORGAN_SLOT_TAIL "tail" #define ORGAN_SLOT_PENIS "penis" + +////organ defines +#define STANDARD_ORGAN_THRESHOLD 100 +#define STANDARD_ORGAN_HEALING 0.001 +#define STANDARD_ORGAN_DECAY 0.00222 //designed to fail organs when left to decay for ~15 minutes \ No newline at end of file diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index fb42bc46c0..b875998f0a 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -81,3 +81,10 @@ #define SPAM_TRIGGER_WARNING 5 //Number of identical messages required before the spam-prevention will warn you to stfu #define SPAM_TRIGGER_AUTOMUTE 10 //Number of identical messages required before the spam-prevention will automute you + +///Max length of a keypress command before it's considered to be a forged packet/bogus command +#define MAX_KEYPRESS_COMMANDLENGTH 16 +///Max amount of keypress messages per second over two seconds before client is autokicked +#define MAX_KEYPRESS_AUTOKICK 100 +///Length of held key rolling buffer +#define HELD_KEY_BUFFER_LENGTH 15 diff --git a/code/__DEFINES/atom_hud.dm b/code/__DEFINES/atom_hud.dm index 4fd1669771..0e4f0f3a6e 100644 --- a/code/__DEFINES/atom_hud.dm +++ b/code/__DEFINES/atom_hud.dm @@ -23,8 +23,9 @@ #define AI_DETECT_HUD "19" #define NANITE_HUD "20" #define DIAG_NANITE_FULL_HUD "21" +#define RAD_HUD "22" //radation alerts for medical huds //for antag huds. these are used at the /mob level -#define ANTAG_HUD "22" +#define ANTAG_HUD "23" //by default everything in the hud_list of an atom is an image //a value in hud_list with one of these will change that behavior diff --git a/code/__DEFINES/citadel_defines.dm b/code/__DEFINES/citadel_defines.dm index da5981ff8a..d966c73d23 100644 --- a/code/__DEFINES/citadel_defines.dm +++ b/code/__DEFINES/citadel_defines.dm @@ -89,17 +89,6 @@ //Damage stuffs #define AROUSAL "arousal" -//DNA stuffs. Remember to change this if upstream adds more snowflake options - - -//Species stuffs. Remember to change this if upstream updates species flags -#define MUTCOLORS2 35 -#define MUTCOLORS3 36 -#define NOAROUSAL 37 //Stops all arousal effects -#define NOGENITALS 38 //Cannot create, use, or otherwise have genitals -#define MATRIXED 39 //if icon is color matrix'd -#define SKINTONE 40 //uses skin tones - //Citadel istypes #define isgenital(A) (istype(A, /obj/item/organ/genital)) diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 06c3c352ca..cc2cd8834a 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -4,15 +4,15 @@ #define COLOR_INPUT_ENABLED "#D3B5B5" #define COLOR_DARKMODE_INFO_BUTTONS_BG "#40628A" -#define COLOR_DARKMODE_ISSUE_BUTTON_BG "#ce4242" -#define COLOR_DARKMODE_BACKGROUND "#202020" -#define COLOR_DARKMODE_DARKBACKGROUND "#171717" -#define COLOR_DARKMODE_TEXT "#f0f0f0" +#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 "#ffffff" -#define COLOR_WHITEMODE_DARKBACKGROUND "#eeeeee" +#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" diff --git a/code/__DEFINES/construction.dm b/code/__DEFINES/construction.dm index ee51fb6909..c394d003d8 100644 --- a/code/__DEFINES/construction.dm +++ b/code/__DEFINES/construction.dm @@ -109,3 +109,5 @@ #define RCD_AIRLOCK 2 #define RCD_DECONSTRUCT 3 #define RCD_WINDOWGRILLE 4 +#define RCD_MACHINE 8 +#define RCD_COMPUTER 16 \ No newline at end of file diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index d64fa30047..d9b30bf0e3 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -10,6 +10,8 @@ #define CHECK_BITFIELD(variable, flag) (variable & (flag)) #define TOGGLE_BITFIELD(variable, flag) (variable ^= (flag)) +#define CHECK_MULTIPLE_BITFIELDS(flagvar, flags) (((flagvar) & (flags)) == (flags)) + GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768)) // for /datum/var/datum_flags diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 8ff40c3ef9..8dc7464230 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -50,13 +50,13 @@ #define isgolem(A) (is_species(A, /datum/species/golem)) #define islizard(A) (is_species(A, /datum/species/lizard)) #define isplasmaman(A) (is_species(A, /datum/species/plasmaman)) -#define ispodperson(A) (is_species(A, /datum/species/podperson)) +#define ispodperson(A) (is_species(A, /datum/species/pod)) #define isflyperson(A) (is_species(A, /datum/species/fly)) #define isjellyperson(A) (is_species(A, /datum/species/jelly)) #define isslimeperson(A) (is_species(A, /datum/species/jelly/slime)) #define isluminescent(A) (is_species(A, /datum/species/jelly/luminescent)) #define iszombie(A) (is_species(A, /datum/species/zombie)) -#define ismoth(A) (is_species(A, /datum/species/moth)) +#define ismoth(A) (is_species(A, /datum/species/insect)) #define ishumanbasic(A) (is_species(A, /datum/species/human)) #define iscatperson(A) (ishumanbasic(A) && istype(A.dna.species, /datum/species/human/felinid) ) diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm index dc4080a789..e6eb9f313e 100644 --- a/code/__DEFINES/jobs.dm +++ b/code/__DEFINES/jobs.dm @@ -48,3 +48,41 @@ #define JOB_UNAVAILABLE_PLAYTIME 3 #define JOB_UNAVAILABLE_ACCOUNTAGE 4 #define JOB_UNAVAILABLE_SLOTFULL 5 + +#define DEFAULT_RELIGION "Christianity" +#define DEFAULT_DEITY "Space Jesus" + +#define JOB_DISPLAY_ORDER_DEFAULT 0 + +#define JOB_DISPLAY_ORDER_ASSISTANT 1 +#define JOB_DISPLAY_ORDER_CAPTAIN 2 +#define JOB_DISPLAY_ORDER_HEAD_OF_PERSONNEL 3 +#define JOB_DISPLAY_ORDER_BARTENDER 4 +#define JOB_DISPLAY_ORDER_COOK 5 +#define JOB_DISPLAY_ORDER_BOTANIST 6 +#define JOB_DISPLAY_ORDER_JANITOR 7 +#define JOB_DISPLAY_ORDER_CLOWN 8 +#define JOB_DISPLAY_ORDER_MIME 9 +#define JOB_DISPLAY_ORDER_CURATOR 10 +#define JOB_DISPLAY_ORDER_LAWYER 11 +#define JOB_DISPLAY_ORDER_CHAPLAIN 12 +#define JOB_DISPLAY_ORDER_QUARTERMASTER 13 +#define JOB_DISPLAY_ORDER_CARGO_TECHNICIAN 14 +#define JOB_DISPLAY_ORDER_SHAFT_MINER 15 +#define JOB_DISPLAY_ORDER_CHIEF_ENGINEER 16 +#define JOB_DISPLAY_ORDER_STATION_ENGINEER 17 +#define JOB_DISPLAY_ORDER_ATMOSPHERIC_TECHNICIAN 18 +#define JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER 19 +#define JOB_DISPLAY_ORDER_MEDICAL_DOCTOR 20 +#define JOB_DISPLAY_ORDER_CHEMIST 21 +#define JOB_DISPLAY_ORDER_GENETICIST 22 +#define JOB_DISPLAY_ORDER_VIROLOGIST 23 +#define JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR 24 +#define JOB_DISPLAY_ORDER_SCIENTIST 25 +#define JOB_DISPLAY_ORDER_ROBOTICIST 26 +#define JOB_DISPLAY_ORDER_HEAD_OF_SECURITY 27 +#define JOB_DISPLAY_ORDER_WARDEN 28 +#define JOB_DISPLAY_ORDER_DETECTIVE 29 +#define JOB_DISPLAY_ORDER_SECURITY_OFFICER 30 +#define JOB_DISPLAY_ORDER_AI 31 +#define JOB_DISPLAY_ORDER_CYBORG 32 diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index 2ba5012301..fe50319295 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -35,6 +35,7 @@ #define LOG_GAME (1 << 12) #define LOG_ADMIN_PRIVATE (1 << 13) #define LOG_ASAY (1 << 14) +#define LOG_VIRUS (1 << 15) //Individual logging panel pages #define INDIVIDUAL_ATTACK_LOG (LOG_ATTACK) diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm index d344f7f010..5bce51293f 100644 --- a/code/__DEFINES/maths.dm +++ b/code/__DEFINES/maths.dm @@ -201,4 +201,10 @@ return list(region_x1 & region_x2, region_y1 & region_y2) +#define EXP_DISTRIBUTION(desired_mean) ( -(1/(1/desired_mean)) * log(rand(1, 1000) * 0.001) ) + +#define LORENTZ_DISTRIBUTION(x, s) ( s*TAN(TODEGREES(PI*(rand()-0.5))) + x ) +#define LORENTZ_CUMULATIVE_DISTRIBUTION(x, y, s) ( (1/PI)*TORADIANS(arctan((x-y)/s)) + 1/2 ) + +#define RULE_OF_THREE(a, b, x) ((a*x)/b) // ) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index c71780e7e9..ba5e105041 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -314,7 +314,7 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S #define MAP_MAXZ 6 // Defib stats -#define DEFIB_TIME_LIMIT 120 +#define DEFIB_TIME_LIMIT 960 #define DEFIB_TIME_LOSS 60 // Diagonal movement @@ -472,6 +472,9 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S #define EGG_LAYING_MESSAGES list("lays an egg.","squats down and croons.","begins making a huge racket.","begins clucking raucously.") +// list of all null rod weapons +#define HOLY_WEAPONS /obj/item/nullrod, /obj/item/twohanded/dualsaber/hypereutactic/chaplain, /obj/item/gun/energy/laser/redtag/hitscan/chaplain, /obj/item/multitool/chaplain, /obj/item/melee/baseball_bat/chaplain + // Used by PDA and cartridge code to reduce repetitiveness of spritesheets #define PDAIMG(what) {""} diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 1d1dda6d99..fed2987a89 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -116,6 +116,8 @@ #define BIOWARE_GENERIC "generic" #define BIOWARE_NERVES "nerves" #define BIOWARE_CIRCULATION "circulation" +#define BIOWARE_LIGAMENTS "ligaments" +#define BIOWARE_DISSECTION "dissected" //Health hud screws for carbon mobs #define SCREWYHUD_NONE 0 @@ -203,8 +205,6 @@ #define MAX_CHICKENS 50 -#define UNHEALING_EAR_DAMAGE 100 - #define INCORPOREAL_MOVE_BASIC 1 #define INCORPOREAL_MOVE_SHADOW 2 // leaves a trail of shadows @@ -256,6 +256,9 @@ #define MAX_QUIRKS 6 //The maximum amount of quirks one character can have at roundstart +#define MAX_REVIVE_FIRE_DAMAGE 180 +#define MAX_REVIVE_BRUTE_DAMAGE 180 + // AI Toggles #define AI_CAMERA_LUMINOSITY 5 #define AI_VOX // Comment out if you don't want VOX to be enabled and have players download the voice sounds. @@ -264,3 +267,6 @@ #define BODYPART_LIFE_UPDATE_HEALTH (1<<0) #define HUMAN_FIRE_STACK_ICON_NUM 3 + +#define PULL_PRONE_SLOWDOWN 0.6 +#define HUMAN_CARRY_SLOWDOWN 0 \ No newline at end of file diff --git a/code/__DEFINES/movespeed_modification.dm b/code/__DEFINES/movespeed_modification.dm index c0aac36415..4336ad28f7 100644 --- a/code/__DEFINES/movespeed_modification.dm +++ b/code/__DEFINES/movespeed_modification.dm @@ -31,3 +31,6 @@ #define MOVESPEED_ID_PAI_SPACEWALK_SPEEDMOD "PAI_SPACEWALK_MODIFIER" #define MOVESPEED_ID_SANITY "MOOD_SANITY" + +#define MOVESPEED_ID_PRONE_DRAGGING "PRONE_DRAG" +#define MOVESPEED_ID_HUMAN_CARRYING "HUMAN_CARRY" \ No newline at end of file diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm index 01d95d7ff9..ba8d229f12 100644 --- a/code/__DEFINES/obj_flags.dm +++ b/code/__DEFINES/obj_flags.dm @@ -10,6 +10,7 @@ #define UNIQUE_RENAME (1<<6) // can you customize the description/name of the thing? #define USES_TGUI (1<<7) //put on things that use tgui on ui_interact instead of custom/old UI. #define FROZEN (1<<8) +#define SHOVABLE_ONTO (1<<9) //called on turf.shove_act() to consider whether an object should have a niche effect (defined in their own shove_act()) when someone is pushed onto it, or do a sanity CanPass() check. // If you add new ones, be sure to add them to /obj/Initialize as well for complete mapping support @@ -38,3 +39,12 @@ #define THICKMATERIAL (1<<5) //prevents syringes, parapens and hypos if the external suit or helmet (if targeting head) has this flag. Example: space suits, biosuit, bombsuits, thick suits that cover your body. #define VOICEBOX_TOGGLABLE (1<<6) // The voicebox in this clothing can be toggled. #define VOICEBOX_DISABLED (1<<7) // The voicebox is currently turned off. + +// Flags for the organ_flags var on /obj/item/organ + +#define ORGAN_SYNTHETIC (1<<0) //Synthetic organs, or cybernetic organs. Reacts to EMPs and don't deteriorate or heal +#define ORGAN_FROZEN (1<<1) //Frozen organs, don't deteriorate +#define ORGAN_FAILING (1<<2) //Failing organs perform damaging effects until replaced or fixed +#define ORGAN_EXTERNAL (1<<3) //Was this organ implanted/inserted/etc, if true will not be removed during species change. +#define ORGAN_VITAL (1<<4) //Currently only the brain +#define ORGAN_NO_SPOIL (1<<5) //Currently only the brain diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index 02f151dcd9..0992b2e586 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -69,4 +69,9 @@ //Flags in the players table in the db #define DB_FLAG_EXEMPT 1 -#define DEFAULT_CYBORG_NAME "Default Cyborg Name" \ No newline at end of file +#define DEFAULT_CYBORG_NAME "Default Cyborg Name" + +//Job preferences levels +#define JP_LOW 1 +#define JP_MEDIUM 2 +#define JP_HIGH 3 diff --git a/code/__DEFINES/reagents.dm b/code/__DEFINES/reagents.dm index 174289d3bb..36ea64de2c 100644 --- a/code/__DEFINES/reagents.dm +++ b/code/__DEFINES/reagents.dm @@ -34,3 +34,17 @@ #define DEL_REAGENT 1 // reagent deleted (fully cleared) #define ADD_REAGENT 2 // reagent added #define REM_REAGENT 3 // reagent removed (may still exist) + +//reagent bitflags, used for altering how they works +#define REAGENT_DEAD_PROCESS (1<<0) //calls on_mob_dead() if present in a dead body +#define REAGENT_DONOTSPLIT (1<<1) //Do not split the chem at all during processing +#define REAGENT_ONLYINVERSE (1<<2) //Only invert chem, no splitting +#define REAGENT_ONMOBMERGE (1<<3) //Call on_mob_life proc when reagents are merging. +#define REAGENT_INVISIBLE (1<<4) //Doesn't appear on handheld health analyzers. +#define REAGENT_FORCEONNEW (1<<5) //Forces a on_new() call without a data overhead +#define REAGENT_SNEAKYNAME (1<<6) //When inverted, the inverted chem uses the name of the original chem +#define REAGENT_SPLITRETAINVOL (1<<7) //Retains initial volume of chem when splitting + +//Chemical reaction flags, for determining reaction specialties +#define REACTION_CLEAR_IMPURE (1<<0) //Convert into impure/pure on reaction completion +#define REACTION_CLEAR_INVERSE (1<<1) //Convert into inverse on reaction completion when purity is low enough diff --git a/code/__DEFINES/stat.dm b/code/__DEFINES/stat.dm index 1d3ea1347a..b975aa1f89 100644 --- a/code/__DEFINES/stat.dm +++ b/code/__DEFINES/stat.dm @@ -8,6 +8,9 @@ #define UNCONSCIOUS 2 #define DEAD 3 +//Maximum healthiness an individual can have +#define MAX_SATIETY 600 + // bitflags for machine stat variable #define BROKEN (1<<0) #define NOPOWER (1<<1) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index ed0007135d..79ba18b5d5 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -95,6 +95,8 @@ #define TRAIT_NOHUNGER "no_hunger" #define TRAIT_EASYDISMEMBER "easy_dismember" #define TRAIT_LIMBATTACHMENT "limb_attach" +#define TRAIT_NOLIMBDISABLE "no_limb_disable" +#define TRAIT_EASYLIMBDISABLE "easy_limb_disable" #define TRAIT_TOXINLOVER "toxinlover" #define TRAIT_NOBREATH "no_breath" #define TRAIT_ANTIMAGIC "anti_magic" @@ -118,6 +120,7 @@ #define TRAIT_LAW_ENFORCEMENT_METABOLISM "law-enforcement-metabolism" #define TRAIT_STRONG_GRABBER "strong_grabber" #define TRAIT_CALCIUM_HEALER "calcium_healer" +#define TRAIT_CAPTAIN_METABOLISM "captain-metabolism" //non-mob traits #define TRAIT_PARALYSIS "paralysis" //Used for limb-based paralysis, where replacing the limb will fix it @@ -162,6 +165,7 @@ #define OBESITY "obesity" #define MAGIC_TRAIT "magic" #define TRAUMA_TRAIT "trauma" +#define DISEASE_TRAIT "disease" #define SPECIES_TRAIT "species" #define ORGAN_TRAIT "organ" #define JOB_TRAIT "job" @@ -203,4 +207,4 @@ #define FLIGHTSUIT_TRAIT "flightsuit" #define LOCKED_HELMET_TRAIT "locked-helmet" #define NINJA_SUIT_TRAIT "ninja-suit" -#define ANTI_DROP_IMPLANT_TRAIT "anti-drop-implant" \ No newline at end of file +#define ANTI_DROP_IMPLANT_TRAIT "anti-drop-implant" diff --git a/code/__HELPERS/_cit_helpers.dm b/code/__HELPERS/_cit_helpers.dm index 8683f75b08..86d0a34e3e 100644 --- a/code/__HELPERS/_cit_helpers.dm +++ b/code/__HELPERS/_cit_helpers.dm @@ -94,6 +94,12 @@ GLOBAL_LIST_INIT(dildo_colors, list(//mostly neon colors "Purple" = "#e300ff"//purple )) +GLOBAL_LIST_INIT(meat_types, list( + "Mammalian" = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/mammal, + "Aquatic" = /obj/item/reagent_containers/food/snacks/carpmeat/aquatic, + "Avian" = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/avian, + "Inesct" = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/insect)) + //Crew objective and miscreants stuff GLOBAL_VAR_INIT(miscreants_allowed, FALSE) diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index ac17df5773..c19a7a64b0 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -58,6 +58,10 @@ if (CONFIG_GET(flag/log_game)) WRITE_LOG(GLOB.world_game_log, "GAME: [text]") +/proc/log_virus(text) + if (CONFIG_GET(flag/log_virus)) + WRITE_LOG(GLOB.world_virus_log, "VIRUS: [text]") + /proc/log_access(text) if (CONFIG_GET(flag/log_access)) WRITE_LOG(GLOB.world_game_log, "ACCESS: [text]") diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index e09ebcb10c..37f36a7e0d 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -81,3 +81,6 @@ GLOBAL_VAR_INIT(cmp_field, "name") /proc/cmp_advdisease_resistance_asc(datum/disease/advance/A, datum/disease/advance/B) return A.totalResistance() - B.totalResistance() + +/proc/cmp_job_display_asc(datum/job/A, datum/job/B) + return A.display_order - B.display_order diff --git a/code/__HELPERS/files.dm b/code/__HELPERS/files.dm index 7f0b5a3c07..5271b407a5 100644 --- a/code/__HELPERS/files.dm +++ b/code/__HELPERS/files.dm @@ -3,7 +3,7 @@ for(var/file in args) src << browse_rsc(file) -/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list("txt","log","htm", "html")) +/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list("txt","log","htm", "html", "md")) var/path = root for(var/i=0, i") /client/proc/roundend_report_file() diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 1ee5ce986f..6908a5057f 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -791,3 +791,6 @@ GLOBAL_LIST_INIT(binary, list("0","1")) rearranged += cword message = "[prefix][jointext(rearranged," ")]" . = message + +#define is_alpha(X) ((text2ascii(X) <= 122) && (text2ascii(X) >= 97)) +#define is_digit(X) ((length(X) == 1) && (length(text2num(X)) == 1)) \ No newline at end of file diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm index b28e7b5807..f0d5a7b252 100644 --- a/code/__HELPERS/time.dm +++ b/code/__HELPERS/time.dm @@ -73,3 +73,11 @@ GLOBAL_VAR_INIT(rollovercheck_last_timeofday, 0) /proc/daysSince(realtimev) return round((world.realtime - realtimev) / (24 HOURS)) + +/proc/worldtime2text() + return gameTimestamp("hh:mm:ss", world.time) + +/proc/gameTimestamp(format = "hh:mm:ss", wtime=null) + if(!wtime) + wtime = world.time + return time2text(wtime - GLOB.timezoneOffset, format) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 3b32745b5c..755105dc27 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -32,6 +32,7 @@ GLOBAL_LIST_INIT(bitfields, list( "UNIQUE_RENAME" = UNIQUE_RENAME, "USES_TGUI" = USES_TGUI, "FROZEN" = FROZEN, + "SHOVABLE_ONTO" = SHOVABLE_ONTO ), "datum_flags" = list( "DF_USE_TAG" = DF_USE_TAG, @@ -171,10 +172,32 @@ GLOBAL_LIST_INIT(bitfields, list( "rad_flags" = list( "RAD_PROTECT_CONTENTS" = RAD_PROTECT_CONTENTS, "RAD_NO_CONTAMINATE" = RAD_NO_CONTAMINATE, - ), + ), "disease_flags" = list( "CURABLE" = CURABLE, "CAN_CARRY" = CAN_CARRY, "CAN_RESIST" = CAN_RESIST - ), - )) \ No newline at end of file + ), + "chemical_flags" = list( + "REAGENT_DEAD_PROCESS" = REAGENT_DEAD_PROCESS, + "REAGENT_DONOTSPLIT" = REAGENT_DONOTSPLIT, + "REAGENT_ONLYINVERSE" = REAGENT_ONLYINVERSE, + "REAGENT_ONMOBMERGE" = REAGENT_ONMOBMERGE, + "REAGENT_INVISIBLE" = REAGENT_INVISIBLE, + "REAGENT_FORCEONNEW" = REAGENT_FORCEONNEW, + "REAGENT_SNEAKYNAME" = REAGENT_SNEAKYNAME, + "REAGENT_SPLITRETAINVOL" = REAGENT_SPLITRETAINVOL + ), + "clear_conversion" = list( + "REACTION_CLEAR_IMPURE" = REACTION_CLEAR_IMPURE, + "REACTION_CLEAR_INVERSE" = REACTION_CLEAR_INVERSE + ), + "organ_flags" = list( + "ORGAN_SYNTHETIC" = ORGAN_SYNTHETIC, + "ORGAN_FROZEN" = ORGAN_FROZEN, + "ORGAN_FAILING" = ORGAN_FAILING, + "ORGAN_EXTERNAL" = ORGAN_EXTERNAL, + "ORGAN_VITAL" = ORGAN_VITAL, + "ORGAN_NO_SPOIL" = ORGAN_NO_SPOIL + ), + )) diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index d09368d7b7..c759658bd4 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -34,7 +34,8 @@ GLOBAL_LIST_EMPTY(ears_list) GLOBAL_LIST_EMPTY(wings_list) GLOBAL_LIST_EMPTY(wings_open_list) GLOBAL_LIST_EMPTY(r_wings_list) -GLOBAL_LIST_EMPTY(moth_wings_list) +GLOBAL_LIST_EMPTY(insect_wings_list) +GLOBAL_LIST_EMPTY(insect_fluffs_list) GLOBAL_LIST_EMPTY(caps_list) GLOBAL_LIST_INIT(ghost_forms_with_directions_list, list("ghost")) //stores the ghost forms that support directional sprites diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index bd7f4afe62..8e5da083e3 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -26,6 +26,8 @@ GLOBAL_VAR(query_debug_log) GLOBAL_PROTECT(query_debug_log) GLOBAL_VAR(world_job_debug_log) GLOBAL_PROTECT(world_job_debug_log) +GLOBAL_VAR(world_virus_log) +GLOBAL_PROTECT(world_virus_log) GLOBAL_LIST_EMPTY(bombers) GLOBAL_PROTECT(bombers) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 22210bfd08..5f0e7c9b22 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -67,7 +67,7 @@ /mob/proc/ClickOn( atom/A, params ) if(world.time <= next_click) return - next_click = world.time + 1 + next_click = world.time + world.tick_lag if(check_click_intercept(params,A)) return diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index 006207c3f3..7f63b74945 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -55,6 +55,17 @@ var/obj/item/W = get_active_held_item() + if(!W && A.Adjacent(src) && (isobj(A) || ismob(A))) + var/atom/movable/C = A + if(C.can_buckle && C.has_buckled_mobs()) + if(C.buckled_mobs.len > 1) + var/unbuckled = input(src, "Who do you wish to unbuckle?","Unbuckle Who?") as null|mob in C.buckled_mobs + if(C.user_unbuckle_mob(unbuckled,src)) + return + else + if(C.user_unbuckle_mob(C.buckled_mobs[1],src)) + return + if(!W && get_dist(src,A) <= interaction_range) A.attack_robot(src) return diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 595e1d9ca0..f0e504ef04 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -39,6 +39,8 @@ /datum/config_entry/flag/log_game // log game events +/datum/config_entry/flag/log_virus // log virology data + /datum/config_entry/flag/log_vote // log voting /datum/config_entry/flag/log_whisper // log client whisper diff --git a/code/controllers/subsystem/assets.dm b/code/controllers/subsystem/assets.dm index 7285298283..7b6554bd3f 100644 --- a/code/controllers/subsystem/assets.dm +++ b/code/controllers/subsystem/assets.dm @@ -6,7 +6,17 @@ SUBSYSTEM_DEF(assets) var/list/preload = list() /datum/controller/subsystem/assets/Initialize(timeofday) - for(var/type in typesof(/datum/asset)) + + var/list/priority_assets = list( + /datum/asset/simple/oui_theme_nano, + /datum/asset/simple/goonchat + ) + + for(var/type in priority_assets) + var/datum/asset/A = new type() + A.register() + + for(var/type in typesof(/datum/asset) - (priority_assets | list(/datum/asset, /datum/asset/simple))) var/datum/asset/A = type if (type != initial(A._abstract)) get_asset_datum(type) diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm index 72dfed6af0..58476d472f 100644 --- a/code/controllers/subsystem/blackbox.dm +++ b/code/controllers/subsystem/blackbox.dm @@ -294,7 +294,7 @@ Versioning var/lakey = L.lastattackerckey var/sqlbrute = L.getBruteLoss() var/sqlfire = L.getFireLoss() - var/sqlbrain = L.getBrainLoss() + var/sqlbrain = L.getOrganLoss(ORGAN_SLOT_BRAIN) var/sqloxy = L.getOxyLoss() var/sqltox = L.getToxLoss() var/sqlclone = L.getCloneLoss() @@ -308,7 +308,7 @@ Versioning if(!SSdbcore.Connect()) return - + sqlname = sanitizeSQL(sqlname) sqlkey = sanitizeSQL(sqlkey) sqljob = sanitizeSQL(sqljob) diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index 4001c666ee..ea7e7ce812 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -110,13 +110,16 @@ SUBSYSTEM_DEF(job) if(job.required_playtime_remaining(player.client)) JobDebug("FOC player not enough xp, Player: [player]") continue + if(!player.client.prefs.pref_species.qualifies_for_rank(job.title, player.client.prefs.features)) + JobDebug("FOC non-human failed, Player: [player]") + continue if(flag && (!(flag in player.client.prefs.be_special))) JobDebug("FOC flag failed, Player: [player], Flag: [flag], ") continue if(player.mind && job.title in player.mind.restricted_roles) JobDebug("FOC incompatible with antagonist role, Player: [player]") continue - if(player.client.prefs.GetJobDepartment(job, level) & job.flag) + if(player.client.prefs.job_preferences["[job.title]"] == level) JobDebug("FOC pass, Player: [player], Level:[level]") candidates += player return candidates @@ -145,6 +148,10 @@ SUBSYSTEM_DEF(job) JobDebug("GRJ player not old enough, Player: [player]") continue + if(!player.client.prefs.pref_species.qualifies_for_rank(job.title, player.client.prefs.features)) + JobDebug("GRJ non-human failed, Player: [player]") + continue + if(job.required_playtime_remaining(player.client)) JobDebug("GRJ player not enough xp, Player: [player]") continue @@ -182,7 +189,7 @@ SUBSYSTEM_DEF(job) if((job.current_positions >= job.total_positions) && job.total_positions != -1) continue var/list/candidates = FindOccupationCandidates(job, level) - if(!candidates.len) + if(!candidates?.len) continue var/mob/dead/new_player/candidate = pick(candidates) if(AssignRole(candidate, command_position)) @@ -200,7 +207,7 @@ SUBSYSTEM_DEF(job) if((job.current_positions >= job.total_positions) && job.total_positions != -1) continue var/list/candidates = FindOccupationCandidates(job, level) - if(!candidates.len) + if(!candidates?.len) continue var/mob/dead/new_player/candidate = pick(candidates) AssignRole(candidate, command_position) @@ -228,7 +235,7 @@ SUBSYSTEM_DEF(job) * fills var "assigned_role" for all ready players. * This proc must not have any side effect besides of modifying "assigned_role". **/ -/datum/controller/subsystem/job/proc/DivideOccupations() +/datum/controller/subsystem/job/proc/DivideOccupations(list/required_jobs) //Setup new player list and get the jobs list JobDebug("Running DO") @@ -241,14 +248,14 @@ SUBSYSTEM_DEF(job) //Get the players who are ready for(var/mob/dead/new_player/player in GLOB.player_list) - if(player.ready == PLAYER_READY_TO_PLAY && player.mind && !player.mind.assigned_role) + if(player.ready == PLAYER_READY_TO_PLAY && player.check_preferences() && player.mind && !player.mind.assigned_role) unassigned += player initial_players_to_assign = unassigned.len - JobDebug("DO, Len: [unassigned.len]") + JobDebug("DO, Len: [unassigned?.len]") if(unassigned.len == 0) - return 0 + return validate_required_jobs(required_jobs) //Scale number of open security officer slots to population setup_officer_positions() @@ -269,8 +276,8 @@ SUBSYSTEM_DEF(job) //People who wants to be the overflow role, sure, go on. JobDebug("DO, Running Overflow Check 1") var/datum/job/overflow = GetJob(SSjob.overflow_role) - var/list/overflow_candidates = FindOccupationCandidates(overflow, 3) - JobDebug("AC1, Candidates: [overflow_candidates.len]") + var/list/overflow_candidates = FindOccupationCandidates(overflow, JP_LOW) + JobDebug("AC1, Candidates: [overflow_candidates?.len]") for(var/mob/dead/new_player/player in overflow_candidates) JobDebug("AC1 pass, Player: [player]") AssignRole(player, SSjob.overflow_role) @@ -297,7 +304,8 @@ SUBSYSTEM_DEF(job) // Loop through all levels from high to low var/list/shuffledoccupations = shuffle(occupations) - for(var/level = 1 to 3) + var/list/levels = list(JP_HIGH,JP_MEDIUM,JP_LOW) + for(var/level in levels) //Check the head jobs first each level CheckHeadPositions(level) @@ -326,13 +334,17 @@ SUBSYSTEM_DEF(job) if(job.required_playtime_remaining(player.client)) JobDebug("DO player not enough xp, Player: [player], Job:[job.title]") continue + + if(!player.client.prefs.pref_species.qualifies_for_rank(job.title, player.client.prefs.features)) + JobDebug("DO non-human failed, Player: [player], Job:[job.title]") + continue if(player.mind && job.title in player.mind.restricted_roles) JobDebug("DO incompatible with antagonist role, Player: [player], Job:[job.title]") continue // If the player wants that job on this level, then try give it to him. - if(player.client.prefs.GetJobDepartment(job, level) & job.flag) + if(player.client.prefs.job_preferences["[job.title]"] == level) // If the job isn't filled if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) JobDebug("DO pass, Player: [player], Level:[level], Job:[job.title]") @@ -351,9 +363,28 @@ SUBSYSTEM_DEF(job) //Mop up people who can't leave. for(var/mob/dead/new_player/player in unassigned) //Players that wanted to back out but couldn't because they're antags (can you feel the edge case?) if(!GiveRandomJob(player)) - AssignRole(player, SSjob.overflow_role) //If everything is already filled, make them an assistant + if(!AssignRole(player, SSjob.overflow_role)) //If everything is already filled, make them an assistant + return FALSE //Living on the edge, the forced antagonist couldn't be assigned to overflow role (bans, client age) - just reroll - return 1 + return validate_required_jobs(required_jobs) + +/datum/controller/subsystem/job/proc/validate_required_jobs(list/required_jobs) + if(!required_jobs.len) + return TRUE + for(var/required_group in required_jobs) + var/group_ok = TRUE + for(var/rank in required_group) + var/datum/job/J = GetJob(rank) + if(!J) + SSticker.mode.setup_error = "Invalid job [rank] in gamemode required jobs." + return FALSE + if(J.current_positions < required_group[rank]) + group_ok = FALSE + break + if(group_ok) + return TRUE + SSticker.mode.setup_error = "Required jobs not present." + return FALSE //We couldn't find a job from prefs for this guy. /datum/controller/subsystem/job/proc/HandleUnassigned(mob/dead/new_player/player) @@ -406,7 +437,7 @@ SUBSYSTEM_DEF(job) if(length(GLOB.jobspawn_overrides[rank])) S = pick(GLOB.jobspawn_overrides[rank]) if(S) - SendToAtom(H, S, buckle = FALSE) + S.JoinPlayerHere(H, FALSE) if(!S) //if there isn't a spawnpoint send them to latejoin, if there's no latejoin go yell at your mapper log_world("Couldn't find a round start spawn point for [rank]") SendToLateJoin(H) @@ -418,7 +449,7 @@ SUBSYSTEM_DEF(job) if(job) if(!job.dresscodecompliant)// CIT CHANGE - dress code compliance equip_loadout(N, H) // CIT CHANGE - allows players to spawn with loadout items - var/new_mob = job.equip(H, null, null, joined_late) + var/new_mob = job.equip(H, null, null, joined_late , null, M.client) if(ismob(new_mob)) H = new_mob if(!joined_late) @@ -428,12 +459,18 @@ SUBSYSTEM_DEF(job) SSpersistence.antag_rep_change[M.client.ckey] += job.GetAntagRep() +/* if(M.client.holder) + if(CONFIG_GET(flag/auto_deadmin_players) || (M.client.prefs?.toggles & DEADMIN_ALWAYS)) + M.client.holder.auto_deadmin() + else + handle_auto_deadmin_roles(M.client, rank) */ + to_chat(M, "You are the [rank].") if(job) to_chat(M, "As the [rank] you answer directly to [job.supervisors]. Special circumstances may change this.") - to_chat(M, "To speak on your departments radio, use the :h button. To see others, look closely at your headset.") + job.radio_help_message(M) if(job.req_admin_notify) - to_chat(M, "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.") + to_chat(M, "You are playing a job that is important for Game Progression. If you have to disconnect immediately, please notify the admins via adminhelp. Otherwise put your locker gear back into the locker and cryo out.") if(job.custom_spawn_text) to_chat(M, "[job.custom_spawn_text]") if(CONFIG_GET(number/minimal_access_threshold)) @@ -446,12 +483,24 @@ SUBSYSTEM_DEF(job) equip_loadout(N, H, TRUE)//CIT CHANGE - makes players spawn with in-backpack loadout items properly. A little hacky but it works return H - +/* +/datum/controller/subsystem/job/proc/handle_auto_deadmin_roles(client/C, rank) + if(!C?.holder) + return TRUE + var/datum/job/job = GetJob(rank) + if(!job) + return + if((job.auto_deadmin_role_flags & DEADMIN_POSITION_HEAD) && (CONFIG_GET(flag/auto_deadmin_heads) || (C.prefs?.toggles & DEADMIN_POSITION_HEAD))) + return C.holder.auto_deadmin() + else if((job.auto_deadmin_role_flags & DEADMIN_POSITION_SECURITY) && (CONFIG_GET(flag/auto_deadmin_security) || (C.prefs?.toggles & DEADMIN_POSITION_SECURITY))) + return C.holder.auto_deadmin() + else if((job.auto_deadmin_role_flags & DEADMIN_POSITION_SILICON) && (CONFIG_GET(flag/auto_deadmin_silicons) || (C.prefs?.toggles & DEADMIN_POSITION_SILICON))) //in the event there's ever psuedo-silicon roles added, ie synths. + return C.holder.auto_deadmin()*/ /datum/controller/subsystem/job/proc/setup_officer_positions() var/datum/job/J = SSjob.GetJob("Security Officer") if(!J) - throw EXCEPTION("setup_officer_positions(): Security officer job is missing") + CRASH("setup_officer_positions(): Security officer job is missing") var/ssc = CONFIG_GET(number/security_scaling_coeff) if(ssc > 0) @@ -502,13 +551,15 @@ SUBSYSTEM_DEF(job) if(job.required_playtime_remaining(player.client)) young++ continue - if(player.client.prefs.GetJobDepartment(job, 1) & job.flag) - high++ - else if(player.client.prefs.GetJobDepartment(job, 2) & job.flag) - medium++ - else if(player.client.prefs.GetJobDepartment(job, 3) & job.flag) - low++ - else never++ //not selected + switch(player.client.prefs.job_preferences["[job.title]"]) + if(JP_HIGH) + high++ + if(JP_MEDIUM) + medium++ + if(JP_LOW) + low++ + else + never++ SSblackbox.record_feedback("nested tally", "job_preferences", high, list("[job.title]", "high")) SSblackbox.record_feedback("nested tally", "job_preferences", medium, list("[job.title]", "medium")) SSblackbox.record_feedback("nested tally", "job_preferences", low, list("[job.title]", "low")) @@ -551,51 +602,61 @@ SUBSYSTEM_DEF(job) newjob.spawn_positions = J.spawn_positions newjob.current_positions = J.current_positions -/datum/controller/subsystem/job/proc/SendToAtom(mob/M, atom/A, buckle) - if(buckle && isliving(M) && istype(A, /obj/structure/chair)) - var/obj/structure/chair/C = A - if(C.buckle_mob(M, FALSE, FALSE)) - return - M.forceMove(get_turf(A)) +/atom/proc/JoinPlayerHere(mob/M, buckle) + // By default, just place the mob on the same turf as the marker or whatever. + M.forceMove(get_turf(src)) + +/obj/structure/chair/JoinPlayerHere(mob/M, buckle) + // Placing a mob in a chair will attempt to buckle it, or else fall back to default. + if (buckle && isliving(M) && buckle_mob(M, FALSE, FALSE)) + return + ..() /datum/controller/subsystem/job/proc/SendToLateJoin(mob/M, buckle = TRUE) + var/atom/destination if(M.mind && M.mind.assigned_role && length(GLOB.jobspawn_overrides[M.mind.assigned_role])) //We're doing something special today. - SendToAtom(M,pick(GLOB.jobspawn_overrides[M.mind.assigned_role]),FALSE) + destination = pick(GLOB.jobspawn_overrides[M.mind.assigned_role]) + destination.JoinPlayerHere(M, FALSE) return if(latejoin_trackers.len) - SendToAtom(M, pick(latejoin_trackers), buckle) - else - //bad mojo - var/area/shuttle/arrival/A = GLOB.areas_by_type[/area/shuttle/arrival] - if(A) - //first check if we can find a chair - var/obj/structure/chair/C = locate() in A - if(C) - SendToAtom(M, C, buckle) - return - else //last hurrah - var/list/avail = list() - for(var/turf/T in A) - if(!is_blocked_turf(T, TRUE)) - avail += T - if(avail.len) - SendToAtom(M, pick(avail), FALSE) - return + destination = pick(latejoin_trackers) + destination.JoinPlayerHere(M, buckle) + return - //pick an open spot on arrivals and dump em - var/list/arrivals_turfs = shuffle(get_area_turfs(/area/shuttle/arrival)) - if(arrivals_turfs.len) - for(var/turf/T in arrivals_turfs) - if(!is_blocked_turf(T, TRUE)) - SendToAtom(M, T, FALSE) - return - //last chance, pick ANY spot on arrivals and dump em - SendToAtom(M, arrivals_turfs[1], FALSE) - else - var/msg = "Unable to send mob [M] to late join!" - message_admins(msg) - CRASH(msg) + //bad mojo + var/area/shuttle/arrival/A = GLOB.areas_by_type[/area/shuttle/arrival] + if(A) + //first check if we can find a chair + var/obj/structure/chair/C = locate() in A + if(C) + C.JoinPlayerHere(M, buckle) + return + + //last hurrah + var/list/avail = list() + for(var/turf/T in A) + if(!is_blocked_turf(T, TRUE)) + avail += T + if(avail.len) + destination = pick(avail) + destination.JoinPlayerHere(M, FALSE) + return + + //pick an open spot on arrivals and dump em + var/list/arrivals_turfs = shuffle(get_area_turfs(/area/shuttle/arrival)) + if(arrivals_turfs.len) + for(var/turf/T in arrivals_turfs) + if(!is_blocked_turf(T, TRUE)) + T.JoinPlayerHere(M, FALSE) + return + //last chance, pick ANY spot on arrivals and dump em + destination = arrivals_turfs[1] + destination.JoinPlayerHere(M, FALSE) + else + var/msg = "Unable to send mob [M] to late join!" + message_admins(msg) + CRASH(msg) /////////////////////////////////// @@ -637,4 +698,4 @@ SUBSYSTEM_DEF(job) . |= player.mind /datum/controller/subsystem/job/proc/JobDebug(message) - log_job_debug(message) \ No newline at end of file + log_job_debug(message) diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index f2469e8f7d..14e1e86f7d 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -255,7 +255,7 @@ SUBSYSTEM_DEF(ticker) var/can_continue = 0 can_continue = src.mode.pre_setup() //Choose antagonists CHECK_TICK - SSjob.DivideOccupations() //Distribute jobs + can_continue = can_continue && SSjob.DivideOccupations(mode.required_jobs) //Distribute jobs CHECK_TICK if(!GLOB.Debug2) diff --git a/code/datums/components/earhealing.dm b/code/datums/components/earhealing.dm index 6eb71285e0..bd3d57480d 100644 --- a/code/datums/components/earhealing.dm +++ b/code/datums/components/earhealing.dm @@ -26,5 +26,5 @@ if(!HAS_TRAIT(wearer, TRAIT_DEAF)) var/obj/item/organ/ears/ears = wearer.getorganslot(ORGAN_SLOT_EARS) if (ears) - ears.deaf = max(ears.deaf - 1, (ears.ear_damage < UNHEALING_EAR_DAMAGE ? 0 : 1)) // Do not clear deafness while above the unhealing ear damage threshold - ears.ear_damage = max(ears.ear_damage - 0.1, 0) + ears.deaf = max(ears.deaf - 1, (ears.damage < ears.maxHealth ? 0 : 1)) // Do not clear deafness if our ears are too damaged + ears.damage = max(ears.damage - 0.1, 0) diff --git a/code/datums/components/footstep.dm b/code/datums/components/footstep.dm index e6c9f20de6..a8c46e5488 100644 --- a/code/datums/components/footstep.dm +++ b/code/datums/components/footstep.dm @@ -1,108 +1,108 @@ -/datum/component/footstep - var/steps = 0 - var/volume - var/e_range - -/datum/component/footstep/Initialize(volume_ = 0.5, e_range_ = -1) - if(!isliving(parent)) - return COMPONENT_INCOMPATIBLE - volume = volume_ - e_range = e_range_ - RegisterSignal(parent, list(COMSIG_MOVABLE_MOVED), .proc/play_footstep) - -/datum/component/footstep/proc/play_footstep() - var/turf/open/T = get_turf(parent) - if(!istype(T)) - return - - var/mob/living/LM = parent - var/v = volume - var/e = e_range - if(!T.footstep || LM.buckled || LM.lying || !LM.canmove || LM.resting || LM.buckled || LM.throwing || LM.movement_type & (VENTCRAWLING | FLYING)) - if (LM.lying && !LM.buckled && !(!T.footstep || LM.movement_type & (VENTCRAWLING | FLYING))) //play crawling sound if we're lying - playsound(T, 'sound/effects/footstep/crawl1.ogg', 15 * v) - return - - if(HAS_TRAIT(LM, TRAIT_SILENT_STEP)) - return - - if(iscarbon(LM)) - var/mob/living/carbon/C = LM - if(!C.get_bodypart(BODY_ZONE_L_LEG) && !C.get_bodypart(BODY_ZONE_R_LEG)) - return - if(ishuman(C) && C.m_intent == MOVE_INTENT_WALK) - v /= 2 - e -= 5 - steps++ - - if(steps >= 3) - steps = 0 - - else - return - - if(prob(80) && !LM.has_gravity(T)) // don't need to step as often when you hop around - return - - //begin playsound shenanigans// - - //for barefooted non-clawed mobs like monkeys - if(isbarefoot(LM)) - playsound(T, pick(GLOB.barefootstep[T.barefootstep][1]), - GLOB.barefootstep[T.barefootstep][2] * v, - TRUE, - GLOB.barefootstep[T.barefootstep][3] + e) - return - - //for xenomorphs, dogs, and other clawed mobs - if(isclawfoot(LM)) - if(isalienadult(LM)) //xenos are stealthy and get quieter footsteps - v /= 3 - e -= 5 - - playsound(T, pick(GLOB.clawfootstep[T.clawfootstep][1]), - GLOB.clawfootstep[T.clawfootstep][2] * v, - TRUE, - GLOB.clawfootstep[T.clawfootstep][3] + e) - return - - //for megafauna and other large and imtimidating mobs such as the bloodminer - if(isheavyfoot(LM)) - playsound(T, pick(GLOB.heavyfootstep[T.heavyfootstep][1]), - GLOB.heavyfootstep[T.heavyfootstep][2] * v, - TRUE, - GLOB.heavyfootstep[T.heavyfootstep][3] + e) - return - - //for slimes - if(isslime(LM)) - playsound(T, 'sound/effects/footstep/slime1.ogg', 15 * v) - return - - //for (simple) humanoid mobs (clowns, russians, pirates, etc.) - if(isshoefoot(LM)) - if(!ishuman(LM)) - playsound(T, pick(GLOB.footstep[T.footstep][1]), - GLOB.footstep[T.footstep][2] * v, - TRUE, - GLOB.footstep[T.footstep][3] + e) - return - if(ishuman(LM)) //for proper humans, they're special - var/mob/living/carbon/human/H = LM - var/feetCover = (H.wear_suit && (H.wear_suit.body_parts_covered & FEET)) || (H.w_uniform && (H.w_uniform.body_parts_covered & FEET)) - - if (H.dna.features["taur"] == "Naga" || H.dna.features["taur"] == "Tentacle") //are we a naga or tentacle taur creature - playsound(T, 'sound/effects/footstep/crawl1.ogg', 15 * v) - return - - if(H.shoes || feetCover) //are we wearing shoes - playsound(T, pick(GLOB.footstep[T.footstep][1]), - GLOB.footstep[T.footstep][2] * v, - TRUE, - GLOB.footstep[T.footstep][3] + e) - - if((!H.shoes && !feetCover)) //are we NOT wearing shoes - playsound(T, pick(GLOB.barefootstep[T.barefootstep][1]), - GLOB.barefootstep[T.barefootstep][2] * v, - TRUE, +/datum/component/footstep + var/steps = 0 + var/volume + var/e_range + +/datum/component/footstep/Initialize(volume_ = 0.5, e_range_ = -1) + if(!isliving(parent)) + return COMPONENT_INCOMPATIBLE + volume = volume_ + e_range = e_range_ + RegisterSignal(parent, list(COMSIG_MOVABLE_MOVED), .proc/play_footstep) + +/datum/component/footstep/proc/play_footstep() + var/turf/open/T = get_turf(parent) + if(!istype(T)) + return + + var/mob/living/LM = parent + var/v = volume + var/e = e_range + if(!T.footstep || LM.buckled || LM.lying || !LM.canmove || LM.resting || LM.buckled || LM.throwing || LM.movement_type & (VENTCRAWLING | FLYING)) + if (LM.lying && !LM.buckled && !(!T.footstep || LM.movement_type & (VENTCRAWLING | FLYING))) //play crawling sound if we're lying + playsound(T, 'sound/effects/footstep/crawl1.ogg', 15 * v) + return + + if(HAS_TRAIT(LM, TRAIT_SILENT_STEP)) + return + + if(iscarbon(LM)) + var/mob/living/carbon/C = LM + if(!C.get_bodypart(BODY_ZONE_L_LEG) && !C.get_bodypart(BODY_ZONE_R_LEG)) + return + if(ishuman(C) && C.m_intent == MOVE_INTENT_WALK) + v /= 2 + e -= 5 + steps++ + + if(steps >= 3) + steps = 0 + + else + return + + if(prob(80) && !LM.has_gravity(T)) // don't need to step as often when you hop around + return + + //begin playsound shenanigans// + + //for barefooted non-clawed mobs like monkeys + if(isbarefoot(LM)) + playsound(T, pick(GLOB.barefootstep[T.barefootstep][1]), + GLOB.barefootstep[T.barefootstep][2] * v, + TRUE, + GLOB.barefootstep[T.barefootstep][3] + e) + return + + //for xenomorphs, dogs, and other clawed mobs + if(isclawfoot(LM)) + if(isalienadult(LM)) //xenos are stealthy and get quieter footsteps + v /= 3 + e -= 5 + + playsound(T, pick(GLOB.clawfootstep[T.clawfootstep][1]), + GLOB.clawfootstep[T.clawfootstep][2] * v, + TRUE, + GLOB.clawfootstep[T.clawfootstep][3] + e) + return + + //for megafauna and other large and imtimidating mobs such as the bloodminer + if(isheavyfoot(LM)) + playsound(T, pick(GLOB.heavyfootstep[T.heavyfootstep][1]), + GLOB.heavyfootstep[T.heavyfootstep][2] * v, + TRUE, + GLOB.heavyfootstep[T.heavyfootstep][3] + e) + return + + //for slimes + if(isslime(LM)) + playsound(T, 'sound/effects/footstep/slime1.ogg', 15 * v) + return + + //for (simple) humanoid mobs (clowns, russians, pirates, etc.) + if(isshoefoot(LM)) + if(!ishuman(LM)) + playsound(T, pick(GLOB.footstep[T.footstep][1]), + GLOB.footstep[T.footstep][2] * v, + TRUE, + GLOB.footstep[T.footstep][3] + e) + return + if(ishuman(LM)) //for proper humans, they're special + var/mob/living/carbon/human/H = LM + var/feetCover = (H.wear_suit && (H.wear_suit.body_parts_covered & FEET)) || (H.w_uniform && (H.w_uniform.body_parts_covered & FEET)) + + if (H.dna.features["taur"] == "Naga" || H.dna.features["taur"] == "Tentacle") //are we a naga or tentacle taur creature + playsound(T, 'sound/effects/footstep/crawl1.ogg', 15 * v) + return + + if(H.shoes || feetCover) //are we wearing shoes + playsound(T, pick(GLOB.footstep[T.footstep][1]), + GLOB.footstep[T.footstep][2] * v, + TRUE, + GLOB.footstep[T.footstep][3] + e) + + if((!H.shoes && !feetCover)) //are we NOT wearing shoes + playsound(T, pick(GLOB.barefootstep[T.barefootstep][1]), + GLOB.barefootstep[T.barefootstep][2] * v, + TRUE, GLOB.barefootstep[T.barefootstep][3] + e) \ No newline at end of file diff --git a/code/datums/components/riding.dm b/code/datums/components/riding.dm index 7b80f87657..6fc58b9f5d 100644 --- a/code/datums/components/riding.dm +++ b/code/datums/components/riding.dm @@ -195,21 +195,47 @@ . = ..() RegisterSignal(parent, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, .proc/on_host_unarmed_melee) +/datum/component/riding/human/vehicle_mob_unbuckle(datum/source, mob/living/M, force = FALSE) + var/mob/living/carbon/human/H = parent + H.remove_movespeed_modifier(MOVESPEED_ID_HUMAN_CARRYING) + . = ..() + +/datum/component/riding/human/vehicle_mob_buckle(datum/source, mob/living/M, force = FALSE) + . = ..() + var/mob/living/carbon/human/H = parent + H.add_movespeed_modifier(MOVESPEED_ID_HUMAN_CARRYING, multiplicative_slowdown = HUMAN_CARRY_SLOWDOWN) + /datum/component/riding/human/proc/on_host_unarmed_melee(atom/target) - var/mob/living/carbon/human/AM = parent - if(AM.a_intent == INTENT_DISARM && (target in AM.buckled_mobs)) + var/mob/living/carbon/human/H = parent + if(H.a_intent == INTENT_DISARM && (target in H.buckled_mobs)) force_dismount(target) /datum/component/riding/human/handle_vehicle_layer() var/atom/movable/AM = parent if(AM.buckled_mobs && AM.buckled_mobs.len) - if(AM.dir == SOUTH) - AM.layer = ABOVE_MOB_LAYER + for(var/mob/M in AM.buckled_mobs) //ensure proper layering of piggyback and carry, sometimes weird offsets get applied + M.layer = MOB_LAYER + if(!AM.buckle_lying) + if(AM.dir == SOUTH) + AM.layer = ABOVE_MOB_LAYER + else + AM.layer = OBJ_LAYER else - AM.layer = OBJ_LAYER + if(AM.dir == NORTH) + AM.layer = OBJ_LAYER + else + AM.layer = ABOVE_MOB_LAYER else AM.layer = MOB_LAYER +/datum/component/riding/human/get_offsets(pass_index) + var/mob/living/carbon/human/H = parent + if(H.buckle_lying) + return list(TEXT_NORTH = list(0, 6), TEXT_SOUTH = list(0, 6), TEXT_EAST = list(0, 6), TEXT_WEST = list(0, 6)) + else + return list(TEXT_NORTH = list(0, 6), TEXT_SOUTH = list(0, 6), TEXT_EAST = list(-6, 4), TEXT_WEST = list( 6, 4)) + + /datum/component/riding/human/force_dismount(mob/living/user) var/atom/movable/AM = parent AM.unbuckle_mob(user) @@ -273,12 +299,15 @@ M.throw_at(target, 14, 5, AM) M.Knockdown(60) -/datum/component/riding/proc/equip_buckle_inhands(mob/living/carbon/human/user, amount_required = 1) +/datum/component/riding/proc/equip_buckle_inhands(mob/living/carbon/human/user, amount_required = 1, riding_target_override = null) var/atom/movable/AM = parent var/amount_equipped = 0 for(var/amount_needed = amount_required, amount_needed > 0, amount_needed--) var/obj/item/riding_offhand/inhand = new /obj/item/riding_offhand(user) - inhand.rider = user + if(!riding_target_override) + inhand.rider = user + else + inhand.rider = riding_target_override inhand.parent = AM if(user.put_in_hands(inhand, TRUE)) amount_equipped++ @@ -318,7 +347,7 @@ . = ..() /obj/item/riding_offhand/equipped() - if(loc != rider) + if(loc != rider && loc != parent) selfdeleting = TRUE qdel(src) . = ..() diff --git a/code/datums/components/storage/concrete/pockets.dm b/code/datums/components/storage/concrete/pockets.dm index e0a23b0209..84be4fdca4 100644 --- a/code/datums/components/storage/concrete/pockets.dm +++ b/code/datums/components/storage/concrete/pockets.dm @@ -52,7 +52,7 @@ . = ..() cant_hold = typecacheof(list(/obj/item/screwdriver/power)) can_hold = typecacheof(list( - /obj/item/kitchen/knife, /obj/item/switchblade, /obj/item/pen, + /obj/item/kitchen/knife, /obj/item/switchblade, /obj/item/pen, /obj/item/melee/cultblade/dagger, /obj/item/scalpel, /obj/item/reagent_containers/syringe, /obj/item/dnainjector, /obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/dropper, /obj/item/implanter, /obj/item/screwdriver, /obj/item/weldingtool/mini, @@ -63,7 +63,7 @@ . = ..() cant_hold = typecacheof(list(/obj/item/screwdriver/power)) can_hold = typecacheof(list( - /obj/item/kitchen/knife, /obj/item/switchblade, /obj/item/pen, + /obj/item/kitchen/knife, /obj/item/switchblade, /obj/item/pen, /obj/item/melee/cultblade/dagger, /obj/item/scalpel, /obj/item/reagent_containers/syringe, /obj/item/dnainjector, /obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/dropper, /obj/item/implanter, /obj/item/screwdriver, /obj/item/weldingtool/mini, diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index 0c701cb943..adec6f67ce 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -234,7 +234,7 @@ /datum/component/storage/proc/quick_empty(mob/M) var/atom/A = parent - if((!ishuman(M) && (A.loc != M)) || (M.stat != CONSCIOUS) || M.restrained() || !M.canmove) + if(!M.canUseStorage() || !A.Adjacent(M) || M.incapacitated()) return if(check_locked(null, M, TRUE)) return FALSE diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index f0fb7f6095..cfbd8be32c 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -94,10 +94,23 @@ TOXIN:[M.getToxLoss()] OXY:[M.getOxyLoss()] CLONE:[M.getCloneLoss()] - BRAIN:[M.getBrainLoss()] + BRAIN:[M.getOrganLoss(ORGAN_SLOT_BRAIN)] STAMINA:[M.getStaminaLoss()] AROUSAL:[M.getArousalLoss()] - + "} + if(GLOB.Debug2) + atomsnowflake += {" + HEART:[M.getOrganLoss(ORGAN_SLOT_HEART)] + LIVER:[M.getOrganLoss(ORGAN_SLOT_LIVER)] + LUNGS:[M.getOrganLoss(ORGAN_SLOT_LUNGS)] + EYES:[M.getOrganLoss(ORGAN_SLOT_EYES)] + EARS:[M.getOrganLoss(ORGAN_SLOT_EARS)] + STOMACH:[M.getOrganLoss(ORGAN_SLOT_STOMACH)] + TONGUE:[M.getOrganLoss(ORGAN_SLOT_TONGUE)] + APPENDIX:[M.getOrganLoss(ORGAN_SLOT_APPENDIX)] + "} + atomsnowflake += {" + "} else atomsnowflake += "[D]" @@ -1334,8 +1347,8 @@ L.adjustOxyLoss(amount) newamt = L.getOxyLoss() if("brain") - L.adjustBrainLoss(amount) - newamt = L.getBrainLoss() + L.adjustOrganLoss(ORGAN_SLOT_BRAIN, amount) + newamt = L.getOrganLoss(ORGAN_SLOT_BRAIN) if("clone") L.adjustCloneLoss(amount) newamt = L.getCloneLoss() @@ -1345,6 +1358,30 @@ if("arousal") L.adjustArousalLoss(amount) newamt = L.getArousalLoss() + if("heart") + L.adjustOrganLoss(ORGAN_SLOT_HEART, amount) + newamt = L.getOrganLoss(ORGAN_SLOT_HEART) + if("liver") + L.adjustOrganLoss(ORGAN_SLOT_LIVER, amount) + newamt = L.getOrganLoss(ORGAN_SLOT_LIVER) + if("lungs") + L.adjustOrganLoss(ORGAN_SLOT_LUNGS, amount) + newamt = L.getOrganLoss(ORGAN_SLOT_LUNGS) + if("eye_sight") + L.adjustOrganLoss(ORGAN_SLOT_EYES, amount) + newamt = L.getOrganLoss(ORGAN_SLOT_EYES) + if("ears") + L.adjustOrganLoss(ORGAN_SLOT_EARS, amount) + newamt = L.getOrganLoss(ORGAN_SLOT_EARS) + if("stomach") + L.adjustOrganLoss(ORGAN_SLOT_STOMACH, amount) + newamt = L.getOrganLoss(ORGAN_SLOT_STOMACH) + if("tongue") + L.adjustOrganLoss(ORGAN_SLOT_TONGUE, amount) + newamt = L.getOrganLoss(ORGAN_SLOT_TONGUE) + if("appendix") + L.adjustOrganLoss(ORGAN_SLOT_APPENDIX, amount) + newamt = L.getOrganLoss(ORGAN_SLOT_APPENDIX) else to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]") return @@ -1360,4 +1397,4 @@ return var/mob/living/carbon/human/H = locate(href_list["copyoutfit"]) in GLOB.carbon_list if(istype(H)) - H.copy_outfit() \ No newline at end of file + H.copy_outfit() diff --git a/code/datums/diseases/_MobProcs.dm b/code/datums/diseases/_MobProcs.dm index e1432bf9a6..c125a9b7c7 100644 --- a/code/datums/diseases/_MobProcs.dm +++ b/code/datums/diseases/_MobProcs.dm @@ -144,3 +144,9 @@ if(!((locate(thing) in bodyparts) || (locate(thing) in internal_organs))) return FALSE return ..() + +/mob/living/proc/CanSpreadAirborneDisease() + return !is_mouth_covered() + +/mob/living/carbon/CanSpreadAirborneDisease() + return !((head && (head.flags_cover & HEADCOVERSMOUTH) && (head.armor.getRating("bio") >= 25)) || (wear_mask && (wear_mask.flags_cover & MASKCOVERSMOUTH) && (wear_mask.armor.getRating("bio") >= 25))) \ No newline at end of file diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm index 0af4eea8ac..8a6966666e 100644 --- a/code/datums/diseases/_disease.dm +++ b/code/datums/diseases/_disease.dm @@ -55,6 +55,13 @@ D.after_add() infectee.med_hud_set_status() + var/turf/source_turf = get_turf(infectee) + log_virus("[key_name(infectee)] was infected by virus: [src.admin_details()] at [loc_name(source_turf)]") + +//Return a string for admin logging uses, should describe the disease in detail +/datum/disease/proc/admin_details() + return "[src.name] : [src.type]" + /datum/disease/proc/stage_act() var/cure = has_cure() @@ -65,15 +72,17 @@ if(!cure) if(prob(stage_prob)) - stage = min(stage + 1,max_stages) + update_stage(min(stage + 1,max_stages)) else if(prob(cure_chance)) - stage = max(stage - 1, 1) + update_stage(max(stage - 1, 1)) if(disease_flags & CURABLE) if(cure && prob(cure_chance)) cure() +/datum/disease/proc/update_stage(new_stage) + stage = new_stage /datum/disease/proc/has_cure() if(!(disease_flags & CURABLE)) diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index a4a4936f0e..8d5e915b31 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -31,9 +31,9 @@ var/id = "" var/processing = FALSE var/mutable = TRUE //set to FALSE to prevent most in-game methods of altering the disease via virology - var/oldres + var/oldres //To prevent setting new cures unless resistance changes. - // The order goes from easy to cure to hard to cure. + // The order goes from easy to cure to hard to cure. Keep in mind that sentient diseases pick two cures from tier 6 and up, ensure they wont react away in bodies. var/static/list/advance_cures = list( list( // level 1 "copper", "silver", "iodine", "iron", "carbon" @@ -110,15 +110,22 @@ return if(symptoms && symptoms.len) - if(!processing) processing = TRUE for(var/datum/symptom/S in symptoms) - S.Start(src) + if(S.Start(src)) //this will return FALSE if the symptom is neutered + S.next_activation = world.time + rand(S.symptom_delay_min * 10, S.symptom_delay_max * 10) + S.on_stage_change(src) for(var/datum/symptom/S in symptoms) S.Activate(src) +// Tell symptoms stage changed +/datum/disease/advance/update_stage(new_stage) + ..() + for(var/datum/symptom/S in symptoms) + S.on_stage_change(src) + // Compares type then ID. /datum/disease/advance/IsSame(datum/disease/advance/D) @@ -138,9 +145,18 @@ A.properties = properties.Copy() A.id = id A.mutable = mutable + A.oldres = oldres //this is a new disease starting over at stage 1, so processing is not copied return A +//Describe this disease to an admin in detail (for logging) +/datum/disease/advance/admin_details() + var/list/name_symptoms = list() + for(var/datum/symptom/S in symptoms) + name_symptoms += S.name + return "[name] sym:[english_list(name_symptoms)] r:[totalResistance()] s:[totalStealth()] ss:[totalStageSpeed()] t:[totalTransmittable()]" + + /* NEW PROCS @@ -191,6 +207,10 @@ /datum/disease/advance/proc/Refresh(new_name = FALSE) GenerateProperties() AssignProperties() + if(processing && symptoms && symptoms.len) + for(var/datum/symptom/S in symptoms) + S.Start(src) + S.on_stage_change(src) id = null var/the_id = GetDiseaseID() @@ -342,28 +362,28 @@ return id -// Add a symptom, if it is over the limit (with a small chance to be able to go over) -// we take a random symptom away and add the new one. +// Add a symptom, if it is over the limit we take a random symptom away and add the new one. /datum/disease/advance/proc/AddSymptom(datum/symptom/S) if(HasSymptom(S)) return - if(symptoms.len < (VIRUS_SYMPTOM_LIMIT - 1) + rand(-1, 1)) - symptoms += S - else + if(!(symptoms.len < (VIRUS_SYMPTOM_LIMIT - 1) + rand(-1, 1))) RemoveSymptom(pick(symptoms)) - symptoms += S + symptoms += S + S.OnAdd(src) // Simply removes the symptom. /datum/disease/advance/proc/RemoveSymptom(datum/symptom/S) symptoms -= S + S.OnRemove(src) // Neuter a symptom, so it will only affect stats /datum/disease/advance/proc/NeuterSymptom(datum/symptom/S) if(!S.neutered) S.neutered = TRUE S.name += " (neutered)" + S.OnRemove(src) /* @@ -417,7 +437,7 @@ var/i = VIRUS_SYMPTOM_LIMIT - var/datum/disease/advance/D = new(0, null) + var/datum/disease/advance/D = new() D.symptoms = list() var/list/symptoms = list() @@ -445,9 +465,6 @@ D.AssignName(new_name) D.Refresh() - for(var/datum/disease/advance/AD in SSdisease.active_diseases) - AD.Refresh() - for(var/mob/living/carbon/human/H in shuffle(GLOB.alive_mob_list)) if(!is_station_level(H.z)) continue @@ -458,8 +475,8 @@ var/list/name_symptoms = list() for(var/datum/symptom/S in D.symptoms) name_symptoms += S.name - message_admins("[key_name_admin(user)] has triggered a custom virus outbreak of [D.name]! It has these symptoms: [english_list(name_symptoms)]") - + message_admins("[key_name_admin(user)] has triggered a custom virus outbreak of [D.admin_details()]") + log_virus("[key_name(user)] has triggered a custom virus outbreak of [D.admin_details()]!") /datum/disease/advance/proc/totalStageSpeed() return properties["stage_rate"] diff --git a/code/datums/diseases/advance/symptoms/choking.dm b/code/datums/diseases/advance/symptoms/choking.dm index 81cdad8042..a54b132de1 100644 --- a/code/datums/diseases/advance/symptoms/choking.dm +++ b/code/datums/diseases/advance/symptoms/choking.dm @@ -144,5 +144,5 @@ Bonus /datum/symptom/asphyxiation/proc/Asphyxiate_death(mob/living/M, datum/disease/advance/A) var/get_damage = rand(25,35) * power M.adjustOxyLoss(get_damage) - M.adjustBrainLoss(get_damage/2) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, get_damage/2) return 1 diff --git a/code/datums/diseases/advance/symptoms/confusion.dm b/code/datums/diseases/advance/symptoms/confusion.dm index e7315c6bb1..260c6888d7 100644 --- a/code/datums/diseases/advance/symptoms/confusion.dm +++ b/code/datums/diseases/advance/symptoms/confusion.dm @@ -55,7 +55,7 @@ Bonus to_chat(M, "You can't think straight!") M.confused = min(100 * power, M.confused + 8) if(brain_damage) - M.adjustBrainLoss(3 * power, 80) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3 * power, 80) M.updatehealth() return diff --git a/code/datums/diseases/advance/symptoms/cough.dm b/code/datums/diseases/advance/symptoms/cough.dm index 83d93c55e0..e4283101f5 100644 --- a/code/datums/diseases/advance/symptoms/cough.dm +++ b/code/datums/diseases/advance/symptoms/cough.dm @@ -70,6 +70,6 @@ BONUS addtimer(CALLBACK(M, /mob/.proc/emote, "cough"), 6) addtimer(CALLBACK(M, /mob/.proc/emote, "cough"), 12) addtimer(CALLBACK(M, /mob/.proc/emote, "cough"), 18) - if(infective) + if(infective && M.CanSpreadAirborneDisease()) A.spread(1) diff --git a/code/datums/diseases/advance/symptoms/deafness.dm b/code/datums/diseases/advance/symptoms/deafness.dm index cc388f0b59..3718104b48 100644 --- a/code/datums/diseases/advance/symptoms/deafness.dm +++ b/code/datums/diseases/advance/symptoms/deafness.dm @@ -50,9 +50,9 @@ Bonus if(5) if(power > 2) var/obj/item/organ/ears/ears = M.getorganslot(ORGAN_SLOT_EARS) - if(istype(ears) && ears.ear_damage < UNHEALING_EAR_DAMAGE) + if(istype(ears) && ears.damage < ears.maxHealth) to_chat(M, "Your ears pop painfully and start bleeding!") - ears.ear_damage = max(ears.ear_damage, UNHEALING_EAR_DAMAGE) + ears.damage = max(ears.damage, ears.maxHealth) M.emote("scream") else to_chat(M, "Your ears pop and begin ringing loudly!") diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm index e666c7acd6..1ab3456c22 100644 --- a/code/datums/diseases/advance/symptoms/heal.dm +++ b/code/datums/diseases/advance/symptoms/heal.dm @@ -219,8 +219,10 @@ level = 8 passive_message = "The pain from your wounds makes you feel oddly sleepy..." var/deathgasp = FALSE + var/stabilize = FALSE var/active_coma = FALSE //to prevent multiple coma procs threshold_desc = "Stealth 2: Host appears to die when falling into a coma.
\ + Resistance 4: The virus also stabilizes the host while they are in critical condition.
\ Stage Speed 7: Increases healing speed." /datum/symptom/heal/coma/Start(datum/disease/advance/A) @@ -228,9 +230,25 @@ return if(A.properties["stage_rate"] >= 7) power = 1.5 + if(A.properties["resistance"] >= 4) + stabilize = TRUE if(A.properties["stealth"] >= 2) deathgasp = TRUE +/datum/symptom/heal/coma/on_stage_change(datum/disease/advance/A) //mostly copy+pasted from the code for self-respiration's TRAIT_NOBREATH stuff + if(!..()) + return FALSE + if(A.stage >= 4 && stabilize) + ADD_TRAIT(A.affected_mob, TRAIT_NOCRITDAMAGE, DISEASE_TRAIT) + else + REMOVE_TRAIT(A.affected_mob, TRAIT_NOCRITDAMAGE, DISEASE_TRAIT) + return TRUE + +/datum/symptom/heal/coma/End(datum/disease/advance/A) + if(!..()) + return + REMOVE_TRAIT(A.affected_mob, TRAIT_NOCRITDAMAGE, DISEASE_TRAIT) + /datum/symptom/heal/coma/CanHeal(datum/disease/advance/A) var/mob/living/M = A.affected_mob if(HAS_TRAIT(M, TRAIT_DEATHCOMA)) diff --git a/code/datums/diseases/advance/symptoms/oxygen.dm b/code/datums/diseases/advance/symptoms/oxygen.dm index cb7d1a6d6d..65f8307101 100644 --- a/code/datums/diseases/advance/symptoms/oxygen.dm +++ b/code/datums/diseases/advance/symptoms/oxygen.dm @@ -50,3 +50,19 @@ Bonus if(prob(base_message_chance)) to_chat(M, "[pick("Your lungs feel great.", "You realize you haven't been breathing.", "You don't feel the need to breathe.")]") return + +/datum/symptom/oxygen/on_stage_change(datum/disease/advance/A) + if(!..()) + return FALSE + var/mob/living/carbon/M = A.affected_mob + if(A.stage >= 4) + ADD_TRAIT(M, TRAIT_NOBREATH, DISEASE_TRAIT) + else + REMOVE_TRAIT(M, TRAIT_NOBREATH, DISEASE_TRAIT) + return TRUE + +/datum/symptom/oxygen/End(datum/disease/advance/A) + if(!..()) + return + if(A.stage >= 4) + REMOVE_TRAIT(A.affected_mob, TRAIT_NOBREATH, DISEASE_TRAIT) \ No newline at end of file diff --git a/code/datums/diseases/advance/symptoms/sensory.dm b/code/datums/diseases/advance/symptoms/sensory.dm index 8d7cc5ed70..2705e0b168 100644 --- a/code/datums/diseases/advance/symptoms/sensory.dm +++ b/code/datums/diseases/advance/symptoms/sensory.dm @@ -51,7 +51,7 @@ M.hallucination = max(0, M.hallucination - 10) if(A.stage >= 5) - M.adjustBrainLoss(-3) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, -3) if(trauma_heal_mild && iscarbon(M)) var/mob/living/carbon/C = M if(prob(10)) @@ -100,8 +100,8 @@ else if(M.eye_blind || M.eye_blurry) M.set_blindness(0) M.set_blurriness(0) - else if(eyes.eye_damage > 0) - M.adjust_eye_damage(-1) + else if(eyes.damage > 0) + eyes.applyOrganDamage(-1) else if(prob(base_message_chance)) - to_chat(M, "[pick("Your eyes feel great.","You feel like your eyes can focus more clearly.", "You don't feel the need to blink.","Your ears feel great.","Your healing feels more acute.")]") \ No newline at end of file + to_chat(M, "[pick("Your eyes feel great.","You feel like your eyes can focus more clearly.", "You don't feel the need to blink.","Your ears feel great.","Your healing feels more acute.")]") diff --git a/code/datums/diseases/advance/symptoms/sneeze.dm b/code/datums/diseases/advance/symptoms/sneeze.dm index 8fe70c542f..5e21fb3dd9 100644 --- a/code/datums/diseases/advance/symptoms/sneeze.dm +++ b/code/datums/diseases/advance/symptoms/sneeze.dm @@ -48,4 +48,5 @@ Bonus M.emote("sniff") else M.emote("sneeze") - A.spread(4 + power) \ No newline at end of file + if(M.CanSpreadAirborneDisease()) //don't spread germs if they covered their mouth + A.spread(4 + power) \ No newline at end of file diff --git a/code/datums/diseases/advance/symptoms/species.dm b/code/datums/diseases/advance/symptoms/species.dm index d0c32f3244..a8b18ae735 100644 --- a/code/datums/diseases/advance/symptoms/species.dm +++ b/code/datums/diseases/advance/symptoms/species.dm @@ -8,12 +8,14 @@ level = 5 severity = 0 -/datum/symptom/undead_adaptation/Start(datum/disease/advance/A) - if(!..()) - return +/datum/symptom/undead_adaptation/OnAdd(datum/disease/advance/A) A.process_dead = TRUE A.infectable_biotypes |= MOB_UNDEAD +/datum/symptom/undead_adaptation/OnRemove(datum/disease/advance/A) + A.process_dead = FALSE + A.infectable_biotypes -= MOB_UNDEAD + /datum/symptom/inorganic_adaptation name = "Inorganic Biology" desc = "The virus can survive and replicate even in an inorganic environment, increasing its resistance and infection rate." @@ -24,7 +26,8 @@ level = 5 severity = 0 -/datum/symptom/inorganic_adaptation/Start(datum/disease/advance/A) - if(!..()) - return - A.infectable_biotypes |= MOB_INORGANIC \ No newline at end of file +/datum/symptom/inorganic_adaptation/OnAdd(datum/disease/advance/A) + A.infectable_biotypes |= MOB_INORGANIC + +/datum/symptom/inorganic_adaptation/OnRemove(datum/disease/advance/A) + A.infectable_biotypes -= MOB_INORGANIC \ No newline at end of file diff --git a/code/datums/diseases/advance/symptoms/symptoms.dm b/code/datums/diseases/advance/symptoms/symptoms.dm index e42b68cc05..99dbd397a2 100644 --- a/code/datums/diseases/advance/symptoms/symptoms.dm +++ b/code/datums/diseases/advance/symptoms/symptoms.dm @@ -38,11 +38,10 @@ return CRASH("We couldn't assign an ID!") -// Called when processing of the advance disease, which holds this symptom, starts. +// Called when processing of the advance disease that holds this symptom infects a host and upon each Refresh() of that advance disease. /datum/symptom/proc/Start(datum/disease/advance/A) if(neutered) return FALSE - next_activation = world.time + rand(symptom_delay_min * 10, symptom_delay_max * 10) //so it doesn't instantly activate on infection return TRUE // Called when the advance disease is going to be deleted or when the advance disease stops processing. @@ -60,6 +59,11 @@ next_activation = world.time + rand(symptom_delay_min * 10, symptom_delay_max * 10) return TRUE +/datum/symptom/proc/on_stage_change(datum/disease/advance/A) + if(neutered) + return FALSE + return TRUE + /datum/symptom/proc/Copy() var/datum/symptom/new_symp = new type new_symp.name = name @@ -69,3 +73,9 @@ /datum/symptom/proc/generate_threshold_desc() return + +/datum/symptom/proc/OnAdd(datum/disease/advance/A) //Overload when a symptom needs to be active before processing, like changing biotypes. + return + +/datum/symptom/proc/OnRemove(datum/disease/advance/A) //But dont forget to remove them too. + return \ No newline at end of file diff --git a/code/datums/diseases/advance/symptoms/vision.dm b/code/datums/diseases/advance/symptoms/vision.dm index d1cc6905a6..b4a33cb837 100644 --- a/code/datums/diseases/advance/symptoms/vision.dm +++ b/code/datums/diseases/advance/symptoms/vision.dm @@ -45,7 +45,7 @@ Bonus return var/mob/living/carbon/M = A.affected_mob var/obj/item/organ/eyes/eyes = M.getorganslot(ORGAN_SLOT_EYES) - if(istype(eyes)) + if(eyes) switch(A.stage) if(1, 2) if(prob(base_message_chance) && !suppress_warning) @@ -53,20 +53,20 @@ Bonus if(3, 4) to_chat(M, "Your eyes burn!") M.blur_eyes(10) - M.adjust_eye_damage(1) + eyes.applyOrganDamage(1) else M.blur_eyes(20) - M.adjust_eye_damage(5) - if(eyes.eye_damage >= 10) + eyes.applyOrganDamage(5) + if(eyes.damage >= 10) M.become_nearsighted(EYE_DAMAGE) - if(prob(eyes.eye_damage - 10 + 1)) + if(prob(eyes.damage - 10 + 1)) if(!remove_eyes) if(!HAS_TRAIT(M, TRAIT_BLIND)) to_chat(M, "You go blind!") - M.become_blind(EYE_DAMAGE) + eyes.applyOrganDamage(eyes.maxHealth) else M.visible_message("[M]'s eyes fall off their sockets!", "Your eyes fall off their sockets!") eyes.Remove(M) eyes.forceMove(get_turf(M)) else - to_chat(M, "Your eyes burn horrifically!") \ No newline at end of file + to_chat(M, "Your eyes burn horrifically!") diff --git a/code/datums/diseases/appendicitis.dm b/code/datums/diseases/appendicitis.dm index 5708447542..be7e6ceecd 100644 --- a/code/datums/diseases/appendicitis.dm +++ b/code/datums/diseases/appendicitis.dm @@ -27,8 +27,10 @@ A.update_icon() if(prob(3)) to_chat(affected_mob, "You feel a stabbing pain in your abdomen!") + affected_mob.adjustOrganLoss(ORGAN_SLOT_APPENDIX, 5) affected_mob.Stun(rand(40,60)) affected_mob.adjustToxLoss(1) if(3) if(prob(1)) affected_mob.vomit(95) + affected_mob.adjustOrganLoss(ORGAN_SLOT_APPENDIX, 15) diff --git a/code/datums/diseases/brainrot.dm b/code/datums/diseases/brainrot.dm index 0a34501763..98ae74270b 100644 --- a/code/datums/diseases/brainrot.dm +++ b/code/datums/diseases/brainrot.dm @@ -24,7 +24,7 @@ if(prob(2)) to_chat(affected_mob, "You don't feel like yourself.") if(prob(5)) - affected_mob.adjustBrainLoss(1, 170) + affected_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1, 170) affected_mob.updatehealth() if(3) if(prob(2)) @@ -32,7 +32,7 @@ if(prob(2)) affected_mob.emote("drool") if(prob(10)) - affected_mob.adjustBrainLoss(2, 170) + affected_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2, 170) affected_mob.updatehealth() if(prob(2)) to_chat(affected_mob, "Your try to remember something important...but can't.") @@ -43,7 +43,7 @@ if(prob(2)) affected_mob.emote("drool") if(prob(15)) - affected_mob.adjustBrainLoss(3, 170) + affected_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3, 170) affected_mob.updatehealth() if(prob(2)) to_chat(affected_mob, "Strange buzzing fills your head, removing all thoughts.") diff --git a/code/datums/ert.dm b/code/datums/ert.dm index 4976891543..4b4cce3794 100644 --- a/code/datums/ert.dm +++ b/code/datums/ert.dm @@ -61,7 +61,7 @@ teamsize = 1 opendoors = FALSE enforce_human = FALSE - roles = /datum/antagonist/greybois + roles = list(/datum/antagonist/greybois) leader_role = /datum/antagonist/greybois/greygod rename_team = "Emergency Assistants" polldesc = "an Emergency Assistant" diff --git a/code/datums/map_config.dm b/code/datums/map_config.dm index 99b84a1b35..dcc16f1ba5 100644 --- a/code/datums/map_config.dm +++ b/code/datums/map_config.dm @@ -18,7 +18,7 @@ var/map_file = "BoxStation.dmm" var/traits = null - var/space_ruin_levels = 1 //Citadel edit - reduces the default space ruin z-level count to 1 + var/space_ruin_levels = 2 var/space_empty_levels = 1 var/minetype = "lavaland" diff --git a/code/datums/martial/cqc.dm b/code/datums/martial/cqc.dm index 71545476ff..c7644997ee 100644 --- a/code/datums/martial/cqc.dm +++ b/code/datums/martial/cqc.dm @@ -79,7 +79,7 @@ "[A] kicks your head, knocking you out!") playsound(get_turf(A), 'sound/weapons/genhit1.ogg', 50, 1, -1) D.SetSleeping(300) - D.adjustBrainLoss(15, 150) + D.adjustOrganLoss(ORGAN_SLOT_BRAIN, 15, 150) return TRUE /datum/martial_art/cqc/proc/Pressure(mob/living/carbon/human/A, mob/living/carbon/human/D) diff --git a/code/datums/martial/psychotic_brawl.dm b/code/datums/martial/psychotic_brawl.dm index 8f1a33a8b3..34301516dc 100644 --- a/code/datums/martial/psychotic_brawl.dm +++ b/code/datums/martial/psychotic_brawl.dm @@ -46,7 +46,7 @@ D.apply_damage(rand(5,10), BRUTE, BODY_ZONE_HEAD) A.apply_damage(rand(5,10), BRUTE, BODY_ZONE_HEAD) if(!istype(D.head,/obj/item/clothing/head/helmet/) && !istype(D.head,/obj/item/clothing/head/hardhat)) - D.adjustBrainLoss(5) + D.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5) A.Stun(rand(10,45)) D.Knockdown(rand(5,30))//CIT CHANGE - makes stuns from martial arts always use Knockdown instead of Stun for the sake of consistency if(5,6) diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm index e205d7a435..801e8c8c7a 100644 --- a/code/datums/martial/sleeping_carp.dm +++ b/code/datums/martial/sleeping_carp.dm @@ -224,7 +224,7 @@ H.visible_message("[user] delivers a heavy hit to [H]'s head, knocking [H.p_them()] out cold!", \ "[user] knocks you unconscious!") H.SetSleeping(600) - H.adjustBrainLoss(15, 150) + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 15, 150) else return ..() diff --git a/code/datums/outfit.dm b/code/datums/outfit.dm index 1f5c28d3c2..68165af956 100755 --- a/code/datums/outfit.dm +++ b/code/datums/outfit.dm @@ -21,6 +21,7 @@ var/l_hand = null var/internals_slot = null //ID of slot containing a gas tank var/list/backpack_contents = null // In the list(path=count,otherpath=count) format + var/box // Internals box. Will be inserted at the start of backpack_contents var/list/implants = null var/accessory = null @@ -83,6 +84,13 @@ H.equip_to_slot_or_del(new l_pocket(H),SLOT_L_STORE) if(r_pocket) H.equip_to_slot_or_del(new r_pocket(H),SLOT_R_STORE) + + if(box) + if(!backpack_contents) + backpack_contents = list() + backpack_contents.Insert(1, box) + backpack_contents[box] = 1 + if(backpack_contents) for(var/path in backpack_contents) var/number = backpack_contents[path] diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index 336945f622..563d06ca57 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -521,7 +521,7 @@ itemUser.adjustToxLoss(-1.5, forced = TRUE) //Because Slime People are people too itemUser.adjustOxyLoss(-1.5) itemUser.adjustStaminaLoss(-1.5) - itemUser.adjustBrainLoss(-1.5) + itemUser.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1.5) itemUser.adjustCloneLoss(-0.5) //Becasue apparently clone damage is the bastion of all health //Heal all those around you, unbiased for(var/mob/living/L in view(7, owner)) @@ -533,7 +533,7 @@ L.adjustToxLoss(-3.5, forced = TRUE) //Because Slime People are people too L.adjustOxyLoss(-3.5) L.adjustStaminaLoss(-3.5) - L.adjustBrainLoss(-3.5) + L.adjustOrganLoss(ORGAN_SLOT_BRAIN, -3.5) L.adjustCloneLoss(-1) //Becasue apparently clone damage is the bastion of all health else if(issilicon(L)) L.adjustBruteLoss(-3.5) diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm index f77fe37333..f42fe688c5 100644 --- a/code/datums/traits/negative.dm +++ b/code/datums/traits/negative.dm @@ -119,7 +119,7 @@ medical_record_text = "Patient has a tumor in their brain that is slowly driving them to brain death." /datum/quirk/brainproblems/on_process() - quirk_holder.adjustBrainLoss(0.2) + quirk_holder.adjustOrganLoss(ORGAN_SLOT_BRAIN, 0.2) /datum/quirk/nearsighted //t. errorage name = "Nearsighted" @@ -345,10 +345,12 @@ gain_text = "You find yourself unable to speak!" lose_text = "You feel a growing strength in your vocal chords." medical_record_text = "Functionally mute, patient is unable to use their voice in any capacity." + var/datum/brain_trauma/severe/mute/mute /datum/quirk/mute/add() var/mob/living/carbon/human/H = quirk_holder - H.gain_trauma(TRAIT_MUTE, TRAUMA_RESILIENCE_SURGERY) + mute = new + H.gain_trauma(mute, TRAUMA_RESILIENCE_SURGERY) /datum/quirk/mute/on_process() if(quirk_holder.mind && LAZYLEN(quirk_holder.mind.antag_datums)) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 6665e4d59b..7c9dc3f4fe 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -235,6 +235,11 @@ SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) . = P.on_hit(src, 0, def_zone) +//used on altdisarm() for special interactions between the shoved victim (target) and the src, with user being the one shoving the target on it. +// IMPORTANT: if you wish to add a new own shove_act() to a certain object, remember to add SHOVABLE_ONTO to its obj_flags bitfied var first. +/atom/proc/shove_act(mob/living/target, mob/living/user) + return FALSE + /atom/proc/in_contents_of(container)//can take class or object instance as argument if(ispath(container)) if(istype(src.loc, container)) diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 8506a4133a..65a7c6a954 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -18,7 +18,7 @@ /datum/atom_hud/data /datum/atom_hud/data/human/medical - hud_icons = list(STATUS_HUD, HEALTH_HUD, NANITE_HUD) + hud_icons = list(STATUS_HUD, HEALTH_HUD, NANITE_HUD, RAD_HUD) /datum/atom_hud/data/human/medical/basic @@ -162,6 +162,7 @@ holder.icon_state = "hud[RoundHealth(src)]" var/icon/I = icon(icon, icon_state, dir) holder.pixel_y = I.Height() - world.icon_size + med_hud_set_radstatus() //for carbon suit sensors /mob/living/carbon/med_hud_set_health() @@ -211,6 +212,22 @@ holder.icon_state = "hudhealthy" +/mob/living/proc/med_hud_set_radstatus() + var/image/radholder = hud_list[RAD_HUD] + var/icon/I = icon(icon, icon_state, dir) + radholder.pixel_y = I.Height() - world.icon_size + var/mob/living/M = src + var/rads = M.radiation + switch(rads) + if(-INFINITY to RAD_MOB_SAFE) + radholder.icon_state = "hudradsafe" + if((RAD_MOB_SAFE+1) to RAD_MOB_MUTATE) + radholder.icon_state = "hudraddanger" + if((RAD_MOB_MUTATE+1) to RAD_MOB_VOMIT) + radholder.icon_state = "hudradlethal" + if((RAD_MOB_VOMIT+1) to INFINITY) + radholder.icon_state = "hudradnuke" + /*********************************************** Security HUDs! Basic mode shows only the job. ************************************************/ diff --git a/code/game/gamemodes/brother/traitor_bro.dm b/code/game/gamemodes/brother/traitor_bro.dm index 18611ebfcb..8bbe7f54ed 100644 --- a/code/game/gamemodes/brother/traitor_bro.dm +++ b/code/game/gamemodes/brother/traitor_bro.dm @@ -6,7 +6,7 @@ name = "traitor+brothers" config_tag = "traitorbro" restricted_jobs = list("AI", "Cyborg") - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Quartermaster", "Chief Engineer", "Research Director") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") announce_span = "danger" announce_text = "There are Syndicate agents and Blood Brothers on the station!\n\ diff --git a/code/game/gamemodes/clock_cult/clock_cult.dm b/code/game/gamemodes/clock_cult/clock_cult.dm index 808022d25f..51a34f4194 100644 --- a/code/game/gamemodes/clock_cult/clock_cult.dm +++ b/code/game/gamemodes/clock_cult/clock_cult.dm @@ -135,7 +135,7 @@ Credit where due: required_enemies = 3 recommended_enemies = 5 enemy_minimum_age = 7 - protected_jobs = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain") //Silicons can eventually be converted + protected_jobs = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") //Silicons can eventually be converted restricted_jobs = list("Chaplain", "Captain") announce_span = "brass" announce_text = "Servants of Ratvar are trying to summon the Justiciar!\n\ diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index e7cc3c53ae..497cc2f1c3 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -35,8 +35,8 @@ config_tag = "cult" antag_flag = ROLE_CULTIST false_report_weight = 10 - restricted_jobs = list("Chaplain","AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel") - protected_jobs = list() + restricted_jobs = list("AI", "Cyborg") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") required_players = 30 required_enemies = 3 recommended_enemies = 5 diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm new file mode 100644 index 0000000000..e22b785670 --- /dev/null +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -0,0 +1,748 @@ +#define CURRENT_LIVING_PLAYERS 1 +#define CURRENT_LIVING_ANTAGS 2 +#define CURRENT_DEAD_PLAYERS 3 +#define CURRENT_OBSERVERS 4 + +#define ONLY_RULESET 1 +#define HIGHLANDER_RULESET 2 +#define TRAITOR_RULESET 4 +#define MINOR_RULESET 8 + +#define RULESET_STOP_PROCESSING 1 + +// -- Injection delays +GLOBAL_VAR_INIT(dynamic_latejoin_delay_min, (5 MINUTES)) +GLOBAL_VAR_INIT(dynamic_latejoin_delay_max, (25 MINUTES)) + +GLOBAL_VAR_INIT(dynamic_midround_delay_min, (15 MINUTES)) +GLOBAL_VAR_INIT(dynamic_midround_delay_max, (35 MINUTES)) + +// Are HIGHLANDER_RULESETs allowed to stack? +GLOBAL_VAR_INIT(dynamic_no_stacking, TRUE) +// A number between -5 and +5. +// A negative value will give a more peaceful round and +// a positive value will give a round with higher threat. +GLOBAL_VAR_INIT(dynamic_curve_centre, 0) +// A number between 0.5 and 4. +// Higher value will favour extreme rounds and +// lower value rounds closer to the average. +GLOBAL_VAR_INIT(dynamic_curve_width, 1.8) +// If enabled only picks a single starting rule and executes only autotraitor midround ruleset. +GLOBAL_VAR_INIT(dynamic_classic_secret, FALSE) +// How many roundstart players required for high population override to take effect. +GLOBAL_VAR_INIT(dynamic_high_pop_limit, 55) +// If enabled does not accept or execute any rulesets. +GLOBAL_VAR_INIT(dynamic_forced_extended, FALSE) +// How high threat is required for HIGHLANDER_RULESETs stacking. +// This is independent of dynamic_no_stacking. +GLOBAL_VAR_INIT(dynamic_stacking_limit, 90) +// List of forced roundstart rulesets. +GLOBAL_LIST_EMPTY(dynamic_forced_roundstart_ruleset) +// Forced threat level, setting this to zero or higher forces the roundstart threat to the value. +GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) + +/datum/game_mode/dynamic + name = "dynamic mode" + config_tag = "dynamic" + + announce_span = "danger" + announce_text = "Dynamic mode!" // This needs to be changed maybe + + reroll_friendly = FALSE; + + // Threat logging vars + /// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations + var/threat_level = 0 + /// Set at the beginning of the round. Spent by the mode to "purchase" rules. + var/threat = 0 + /// Running information about the threat. Can store text or datum entries. + var/list/threat_log = list() + /// List of roundstart rules used for selecting the rules. + var/list/roundstart_rules = list() + /// List of latejoin rules used for selecting the rules. + var/list/latejoin_rules = list() + /// List of midround rules used for selecting the rules. + var/list/midround_rules = list() + /** # Pop range per requirement. + * If the value is five the range is: + * 0-4, 5-9, 10-14, 15-19, 20-24, 25-29, 30-34, 35-39, 40-54, 45+ + * If it is six the range is: + * 0-5, 6-11, 12-17, 18-23, 24-29, 30-35, 36-41, 42-47, 48-53, 54+ + * If it is seven the range is: + * 0-6, 7-13, 14-20, 21-27, 28-34, 35-41, 42-48, 49-55, 56-62, 63+ + */ + var/pop_per_requirement = 6 + /// The requirement used for checking if a second rule should be selected. + var/list/second_rule_req = list(100, 100, 80, 70, 60, 50, 30, 20, 10, 0) + /// The requirement used for checking if a third rule should be selected. + var/list/third_rule_req = list(100, 100, 100, 90, 80, 70, 60, 50, 40, 30) + /// Threat requirement for a second ruleset when high pop override is in effect. + var/high_pop_second_rule_req = 40 + /// Threat requirement for a third ruleset when high pop override is in effect. + var/high_pop_third_rule_req = 60 + /// Number of players who were ready on roundstart. + var/roundstart_pop_ready = 0 + /// List of candidates used on roundstart rulesets. + var/list/candidates = list() + /// Rules that are processed, rule_process is called on the rules in this list. + var/list/current_rules = list() + /// List of executed rulesets. + var/list/executed_rules = list() + /// Associative list of current players, in order: living players, living antagonists, dead players and observers. + var/list/list/current_players = list(CURRENT_LIVING_PLAYERS, CURRENT_LIVING_ANTAGS, CURRENT_DEAD_PLAYERS, CURRENT_OBSERVERS) + /// When world.time is over this number the mode tries to inject a latejoin ruleset. + var/latejoin_injection_cooldown = 0 + /// When world.time is over this number the mode tries to inject a midround ruleset. + var/midround_injection_cooldown = 0 + /// When TRUE GetInjectionChance returns 100. + var/forced_injection = FALSE + /// Forced ruleset to be executed for the next latejoin. + var/datum/dynamic_ruleset/latejoin/forced_latejoin_rule = null + /// When current_players was updated last time. + var/pop_last_updated = 0 + /// How many percent of the rounds are more peaceful. + var/peaceful_percentage = 50 + /// If a highlander executed. + var/highlander_executed = FALSE + /// If a only ruleset has been executed. + var/only_ruleset_executed = FALSE + +/datum/game_mode/dynamic/admin_panel() + var/list/dat = list("Game Mode Panel

Game Mode Panel

") + dat += "Dynamic Mode \[VV\]
" + dat += "Threat Level: [threat_level]
" + + dat += "Threat to Spend: [threat] \[Adjust\] \[View Log\]
" + dat += "
" + dat += "Parameters: centre = [GLOB.dynamic_curve_centre] ; width = [GLOB.dynamic_curve_width].
" + dat += "On average, [peaceful_percentage]% of the rounds are more peaceful.
" + dat += "Forced extended: [GLOB.dynamic_forced_extended ? "On" : "Off"]
" + dat += "Classic secret (only autotraitor): [GLOB.dynamic_classic_secret ? "On" : "Off"]
" + dat += "No stacking (only one round-ender): [GLOB.dynamic_no_stacking ? "On" : "Off"]
" + dat += "Stacking limit: [GLOB.dynamic_stacking_limit] \[Adjust\]" + dat += "
" + dat += "Executed rulesets: " + if (executed_rules.len > 0) + dat += "
" + for (var/datum/dynamic_ruleset/DR in executed_rules) + dat += "[DR.ruletype] - [DR.name]
" + else + dat += "none.
" + dat += "
Injection Timers: ([get_injection_chance(TRUE)]% chance)
" + dat += "Latejoin: [(latejoin_injection_cooldown-world.time)>60*10 ? "[round((latejoin_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(latejoin_injection_cooldown-world.time)] seconds"] \[Now!\]
" + dat += "Midround: [(midround_injection_cooldown-world.time)>60*10 ? "[round((midround_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(midround_injection_cooldown-world.time)] seconds"] \[Now!\]
" + usr << browse(dat.Join(), "window=gamemode_panel;size=500x500") + +/datum/game_mode/dynamic/Topic(href, href_list) + if (..()) // Sanity, maybe ? + return + if(!check_rights(R_ADMIN)) + message_admins("[usr.key] has attempted to override the game mode panel!") + log_admin("[key_name(usr)] tried to use the game mode panel without authorization.") + return + if (href_list["forced_extended"]) + GLOB.dynamic_forced_extended = !GLOB.dynamic_forced_extended + else if (href_list["no_stacking"]) + GLOB.dynamic_no_stacking = !GLOB.dynamic_no_stacking + else if (href_list["classic_secret"]) + GLOB.dynamic_classic_secret = !GLOB.dynamic_classic_secret + else if (href_list["adjustthreat"]) + var/threatadd = input("Specify how much threat to add (negative to subtract). This can inflate the threat level.", "Adjust Threat", 0) as null|num + if(!threatadd) + return + if(threatadd > 0) + create_threat(threatadd) + else + spend_threat(-threatadd) + else if (href_list["injectlate"]) + latejoin_injection_cooldown = 0 + forced_injection = TRUE + message_admins("[key_name(usr)] forced a latejoin injection.", 1) + else if (href_list["injectmid"]) + midround_injection_cooldown = 0 + forced_injection = TRUE + message_admins("[key_name(usr)] forced a midround injection.", 1) + else if (href_list["threatlog"]) + show_threatlog(usr) + else if (href_list["stacking_limit"]) + GLOB.dynamic_stacking_limit = input(usr,"Change the threat limit at which round-endings rulesets will start to stack.", "Change stacking limit", null) as num + + admin_panel() // Refreshes the window + +// Checks if there are HIGHLANDER_RULESETs and calls the rule's round_result() proc +/datum/game_mode/dynamic/set_round_result() + for(var/datum/dynamic_ruleset/rule in executed_rules) + if(rule.flags & HIGHLANDER_RULESET) + if(rule.check_finished()) // Only the rule that actually finished the round sets round result. + return rule.round_result() + // If it got to this part, just pick one highlander if it exists + for(var/datum/dynamic_ruleset/rule in executed_rules) + if(rule.flags & HIGHLANDER_RULESET) + return rule.round_result() + return ..() + +/datum/game_mode/dynamic/send_intercept() + . = "Central Command Status Summary
" + switch(round(threat_level)) + if(0 to 19) + update_playercounts() + if(!current_players[CURRENT_LIVING_ANTAGS].len) + . += "Peaceful Waypoint
" + . += "Your station orbits deep within controlled, core-sector systems and serves as a waypoint for routine traffic through Nanotrasen's trade empire. Due to the combination of high security, interstellar traffic, and low strategic value, it makes any direct threat of violence unlikely. Your primary enemies will be incompetence and bored crewmen: try to organize team-building events to keep staffers interested and productive." + else + . += "Core Territory
" + . += "Your station orbits within reliably mundane, secure space. Although Nanotrasen has a firm grip on security in your region, the valuable resources and strategic position aboard your station make it a potential target for infiltrations. Monitor crew for non-loyal behavior, but expect a relatively tame shift free of large-scale destruction. We expect great things from your station." + if(20 to 39) + . += "Anomalous Exogeology
" + . += "Although your station lies within what is generally considered Nanotrasen-controlled space, the course of its orbit has caused it to cross unusually close to exogeological features with anomalous readings. Although these features offer opportunities for our research department, it is known that these little understood readings are often correlated with increased activity from competing interstellar organizations and individuals, among them the Wizard Federation and Cult of the Geometer of Blood - all known competitors for Anomaly Type B sites. Exercise elevated caution." + if(40 to 65) + . += "Contested System
" + . += "Your station's orbit passes along the edge of Nanotrasen's sphere of influence. While subversive elements remain the most likely threat against your station, hostile organizations are bolder here, where our grip is weaker. Exercise increased caution against elite Syndicate strike forces, or Executives forbid, some kind of ill-conceived unionizing attempt." + if(66 to 79) + . += "Uncharted Space
" + . += "Congratulations and thank you for participating in the NT 'Frontier' space program! Your station is actively orbiting a high value system far from the nearest support stations. Little is known about your region of space, and the opportunity to encounter the unknown invites greater glory. You are encouraged to elevate security as necessary to protect Nanotrasen assets." + if(80 to 99) + . += "Black Orbit
" + . += "As part of a mandatory security protocol, we are required to inform you that as a result of your orbital pattern directly behind an astrological body (oriented from our nearest observatory), your station will be under decreased monitoring and support. It is anticipated that your extreme location and decreased surveillance could pose security risks. Avoid unnecessary risks and attempt to keep your station in one piece." + if(100) + . += "Impending Doom
" + . += "Your station is somehow in the middle of hostile territory, in clear view of any enemy of the corporation. Your likelihood to survive is low, and station destruction is expected and almost inevitable. Secure any sensitive material and neutralize any enemy you will come across. It is important that you at least try to maintain the station.
" + . += "Good luck." + + if(station_goals.len) + . += "
Special Orders for [station_name()]:" + for(var/datum/station_goal/G in station_goals) + G.on_report() + . += G.get_report() + + print_command_report(., "Central Command Status Summary", announce=FALSE) + priority_announce("A summary has been copied and printed to all communications consoles.", "Security level elevated.", 'sound/ai/intercept.ogg') + if(GLOB.security_level < SEC_LEVEL_BLUE) + set_security_level(SEC_LEVEL_BLUE) + +// Yes, this is copy pasted from game_mode +/datum/game_mode/dynamic/check_finished(force_ending) + if(!SSticker.setup_done || !gamemode_ready) + return FALSE + if(replacementmode && round_converted == 2) + return replacementmode.check_finished() + if(SSshuttle.emergency && (SSshuttle.emergency.mode == SHUTTLE_ENDGAME)) + return TRUE + if(station_was_nuked) + return TRUE + if(force_ending) + return TRUE + for(var/datum/dynamic_ruleset/rule in executed_rules) + if(rule.flags & HIGHLANDER_RULESET) + return rule.check_finished() + +/datum/game_mode/dynamic/proc/show_threatlog(mob/admin) + if(!SSticker.HasRoundStarted()) + alert("The round hasn't started yet!") + return + + if(!check_rights(R_ADMIN)) + return + + var/list/out = list("Threat LogThreat Log
Starting Threat: [threat_level]
") + + for(var/entry in threat_log) + if(istext(entry)) + out += "[entry]
" + + out += "Remaining threat/threat_level: [threat]/[threat_level]" + + usr << browse(out.Join(), "window=threatlog;size=700x500") + +/// Generates the threat level using lorentz distribution and assigns peaceful_percentage. +/datum/game_mode/dynamic/proc/generate_threat() + var/relative_threat = LORENTZ_DISTRIBUTION(GLOB.dynamic_curve_centre, GLOB.dynamic_curve_width) + threat_level = round(lorentz_to_threat(relative_threat), 0.1) + + peaceful_percentage = round(LORENTZ_CUMULATIVE_DISTRIBUTION(relative_threat, GLOB.dynamic_curve_centre, GLOB.dynamic_curve_width), 0.01)*100 + + threat = threat_level + +/datum/game_mode/dynamic/can_start() + /* Disabled for now, had some changes that need to be tested and this might interfere with that. + if(GLOB.dynamic_curve_centre == 0) + // 10 is when the centre starts to decrease + // 6 is just 1 + 5 (from the maximum value and the one decreased) + // 1 just makes the curve look better, I don't know. + // Limited between 1 and 5 then inverted and rounded + // With this you get a centre curve that stays at -5 until 10 then first rapidly decreases but slows down at the end + GLOB.dynamic_curve_centre = round(-CLAMP((10*6/GLOB.player_list.len)-1, 0, 5), 0.5) + */ + message_admins("Dynamic mode parameters for the round:") + message_admins("Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].") + message_admins("Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].") + log_game("DYNAMIC: Dynamic mode parameters for the round:") + log_game("DYNAMIC: Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].") + log_game("DYNAMIC: Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].") + if(GLOB.dynamic_forced_threat_level >= 0) + threat_level = round(GLOB.dynamic_forced_threat_level, 0.1) + threat = threat_level + else + generate_threat() + + var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min) + latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max)) + world.time + + var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min) + midround_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max)) + world.time + log_game("DYNAMIC: Dynamic Mode initialized with a Threat Level of... [threat_level]!") + return TRUE + +/datum/game_mode/dynamic/pre_setup() + for (var/rule in subtypesof(/datum/dynamic_ruleset)) + var/datum/dynamic_ruleset/ruleset = new rule() + // Simple check if the ruleset should be added to the lists. + if(ruleset.name == "") + continue + switch(ruleset.ruletype) + if("Roundstart") + roundstart_rules += ruleset + if ("Latejoin") + latejoin_rules += ruleset + if ("Midround") + if (ruleset.weight) + midround_rules += ruleset + for(var/mob/dead/new_player/player in GLOB.player_list) + if(player.ready == PLAYER_READY_TO_PLAY && player.mind) + roundstart_pop_ready++ + candidates.Add(player) + log_game("DYNAMIC: Listing [roundstart_rules.len] round start rulesets, and [candidates.len] players ready.") + if (candidates.len <= 0) + return TRUE + if (roundstart_rules.len <= 0) + return TRUE + + if(GLOB.dynamic_forced_roundstart_ruleset.len > 0) + rigged_roundstart() + else + roundstart() + + var/starting_rulesets = "" + for (var/datum/dynamic_ruleset/roundstart/DR in executed_rules) + starting_rulesets += "[DR.name], " + candidates.Cut() + return TRUE + +/datum/game_mode/dynamic/post_setup(report) + update_playercounts() + + for(var/datum/dynamic_ruleset/roundstart/rule in executed_rules) + rule.candidates.Cut() // The rule should not use candidates at this point as they all are null. + if(!rule.execute()) + stack_trace("The starting rule \"[rule.name]\" failed to execute.") + ..() + +/// A simple roundstart proc used when dynamic_forced_roundstart_ruleset has rules in it. +/datum/game_mode/dynamic/proc/rigged_roundstart() + message_admins("[GLOB.dynamic_forced_roundstart_ruleset.len] rulesets being forced. Will now attempt to draft players for them.") + log_game("DYNAMIC: [GLOB.dynamic_forced_roundstart_ruleset.len] rulesets being forced. Will now attempt to draft players for them.") + for (var/datum/dynamic_ruleset/roundstart/rule in GLOB.dynamic_forced_roundstart_ruleset) + message_admins("Drafting players for forced ruleset [rule.name].") + log_game("DYNAMIC: Drafting players for forced ruleset [rule.name].") + rule.mode = src + rule.candidates = candidates.Copy() + rule.trim_candidates() + if (rule.ready(TRUE)) + picking_roundstart_rule(list(rule), forced = TRUE) + +/datum/game_mode/dynamic/proc/roundstart() + if (GLOB.dynamic_forced_extended) + log_game("DYNAMIC: Starting a round of forced extended.") + return TRUE + var/list/drafted_rules = list() + for (var/datum/dynamic_ruleset/roundstart/rule in roundstart_rules) + if (rule.acceptable(roundstart_pop_ready, threat_level) && threat >= rule.cost) // If we got the population and threat required + rule.candidates = candidates.Copy() + rule.trim_candidates() + if (rule.ready() && rule.candidates.len > 0) + drafted_rules[rule] = rule.weight + + var/indice_pop = min(10,round(roundstart_pop_ready/pop_per_requirement)+1) + var/extra_rulesets_amount = 0 + if (GLOB.dynamic_classic_secret) + extra_rulesets_amount = 0 + else + if (roundstart_pop_ready > GLOB.dynamic_high_pop_limit) + message_admins("High Population Override is in effect! Threat Level will have more impact on which roles will appear, and player population less.") + log_game("DYNAMIC: High Population Override is in effect! Threat Level will have more impact on which roles will appear, and player population less.") + if (threat_level > high_pop_second_rule_req) + extra_rulesets_amount++ + if (threat_level > high_pop_third_rule_req) + extra_rulesets_amount++ + else + if (threat_level >= second_rule_req[indice_pop]) + extra_rulesets_amount++ + if (threat_level >= third_rule_req[indice_pop]) + extra_rulesets_amount++ + + if (drafted_rules.len > 0 && picking_roundstart_rule(drafted_rules)) + if (extra_rulesets_amount > 0) // We've got enough population and threat for a second rulestart rule + for (var/datum/dynamic_ruleset/roundstart/rule in drafted_rules) + if (rule.cost > threat) + drafted_rules -= rule + if (drafted_rules.len > 0 && picking_roundstart_rule(drafted_rules)) + if (extra_rulesets_amount > 1) // We've got enough population and threat for a third rulestart rule + for (var/datum/dynamic_ruleset/roundstart/rule in drafted_rules) + if (rule.cost > threat) + drafted_rules -= rule + picking_roundstart_rule(drafted_rules) + else + return FALSE + return TRUE + +/// Picks a random roundstart rule from the list given as an argument and executes it. +/datum/game_mode/dynamic/proc/picking_roundstart_rule(list/drafted_rules = list(), forced = FALSE) + var/datum/dynamic_ruleset/roundstart/starting_rule = pickweight(drafted_rules) + if(!starting_rule) + return FALSE + + if(!forced) + if(only_ruleset_executed) + return FALSE + // Check if a blocking ruleset has been executed. + else if(check_blocking(starting_rule.blocking_rules, executed_rules)) + drafted_rules -= starting_rule + if(drafted_rules.len <= 0) + return FALSE + starting_rule = pickweight(drafted_rules) + // Check if the ruleset is highlander and if a highlander ruleset has been executed + else if(starting_rule.flags & HIGHLANDER_RULESET) + if(threat < GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking) + if(highlander_executed) + drafted_rules -= starting_rule + if(drafted_rules.len <= 0) + return FALSE + starting_rule = pickweight(drafted_rules) + + log_game("DYNAMIC: Picking a [istype(starting_rule, /datum/dynamic_ruleset/roundstart/delayed/) ? " delayed " : ""] ruleset [starting_rule.name]") + + roundstart_rules -= starting_rule + drafted_rules -= starting_rule + + if (istype(starting_rule, /datum/dynamic_ruleset/roundstart/delayed/)) + var/datum/dynamic_ruleset/roundstart/delayed/rule = starting_rule + addtimer(CALLBACK(src, .proc/execute_delayed, rule), rule.delay) + + starting_rule.trim_candidates() + if (starting_rule.pre_execute()) + spend_threat(starting_rule.cost) + threat_log += "[worldtime2text()]: Roundstart [starting_rule.name] spent [starting_rule.cost]" + if(starting_rule.flags & HIGHLANDER_RULESET) + highlander_executed = TRUE + else if(starting_rule.flags & ONLY_RULESET) + only_ruleset_executed = TRUE + executed_rules += starting_rule + if (starting_rule.persistent) + current_rules += starting_rule + for(var/mob/M in starting_rule.assigned) + for (var/datum/dynamic_ruleset/roundstart/rule in roundstart_rules) + if (!rule.ready()) + drafted_rules -= rule // And removing rules that are no longer elligible + return TRUE + else + stack_trace("The starting rule \"[starting_rule.name]\" failed to pre_execute.") + return FALSE + +/// Executes delayed roundstart rules and has a hack in it. +/datum/game_mode/dynamic/proc/execute_delayed(datum/dynamic_ruleset/roundstart/delayed/rule) + update_playercounts() + rule.candidates = current_players[CURRENT_LIVING_PLAYERS].Copy() + rule.trim_candidates() + if(rule.execute()) + executed_rules += rule + if (rule.persistent) + current_rules += rule + return TRUE + else + stack_trace("The delayed roundstart rule \"[rule.name]\" failed to execute.") + return FALSE + +/// Picks a random midround OR latejoin rule from the list given as an argument and executes it. +/// Also this could be named better. +/datum/game_mode/dynamic/proc/picking_midround_latejoin_rule(list/drafted_rules = list(), forced = FALSE) + var/datum/dynamic_ruleset/rule = pickweight(drafted_rules) + if(!rule) + return FALSE + + if(!forced) + if(only_ruleset_executed) + return FALSE + // Check if a blocking ruleset has been executed. + else if(check_blocking(rule.blocking_rules, executed_rules)) + drafted_rules -= rule + if(drafted_rules.len <= 0) + return FALSE + rule = pickweight(drafted_rules) + // Check if the ruleset is highlander and if a highlander ruleset has been executed + else if(rule.flags & HIGHLANDER_RULESET) + if(threat < GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking) + if(highlander_executed) + drafted_rules -= rule + if(drafted_rules.len <= 0) + return FALSE + rule = pickweight(drafted_rules) + + if(!rule.repeatable) + if(rule.ruletype == "Latejoin") + latejoin_rules = remove_from_list(latejoin_rules, rule.type) + else if(rule.type == "Midround") + midround_rules = remove_from_list(midround_rules, rule.type) + + if (rule.execute()) + log_game("DYNAMIC: Injected a [rule.ruletype == "latejoin" ? "latejoin" : "midround"] ruleset [rule.name].") + spend_threat(rule.cost) + threat_log += "[worldtime2text()]: [rule.ruletype] [rule.name] spent [rule.cost]" + if(rule.flags & HIGHLANDER_RULESET) + highlander_executed = TRUE + else if(rule.flags & ONLY_RULESET) + only_ruleset_executed = TRUE + if(rule.ruletype == "Latejoin") + var/mob/M = pick(rule.candidates) + message_admins("[key_name(M)] joined the station, and was selected by the [rule.name] ruleset.") + log_game("DYNAMIC: [key_name(M)] joined the station, and was selected by the [rule.name] ruleset.") + executed_rules += rule + rule.candidates.Cut() + if (rule.persistent) + current_rules += rule + return TRUE + else + stack_trace("The [rule.ruletype] rule \"[rule.name]\" failed to execute.") + return FALSE + +/// An experimental proc to allow admins to call rules on the fly or have rules call other rules. +/datum/game_mode/dynamic/proc/picking_specific_rule(ruletype, forced = FALSE) + var/datum/dynamic_ruleset/midround/new_rule + if(ispath(ruletype)) + new_rule = new ruletype() // You should only use it to call midround rules though. + else if(istype(ruletype, /datum/dynamic_ruleset)) + new_rule = ruletype + else + return FALSE + + if(!new_rule) + return FALSE + + if(!forced) + if(only_ruleset_executed) + return FALSE + // Check if a blocking ruleset has been executed. + else if(check_blocking(new_rule.blocking_rules, executed_rules)) + return FALSE + // Check if the ruleset is highlander and if a highlander ruleset has been executed + else if(new_rule.flags & HIGHLANDER_RULESET) + if(threat < GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking) + if(highlander_executed) + return FALSE + + update_playercounts() + if ((forced || (new_rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && new_rule.cost <= threat))) + new_rule.candidates = current_players.Copy() + new_rule.trim_candidates() + if (new_rule.ready(forced)) + spend_threat(new_rule.cost) + threat_log += "[worldtime2text()]: Forced rule [new_rule.name] spent [new_rule.cost]" + if (new_rule.execute()) // This should never fail since ready() returned 1 + if(new_rule.flags & HIGHLANDER_RULESET) + highlander_executed = TRUE + else if(new_rule.flags & ONLY_RULESET) + only_ruleset_executed = TRUE + log_game("DYNAMIC: Making a call to a specific ruleset...[new_rule.name]!") + executed_rules += new_rule + if (new_rule.persistent) + current_rules += new_rule + return TRUE + else if (forced) + log_game("DYNAMIC: The ruleset [new_rule.name] couldn't be executed due to lack of elligible players.") + return FALSE + +/datum/game_mode/dynamic/process() + if (pop_last_updated < world.time - (60 SECONDS)) + pop_last_updated = world.time + update_playercounts() + + for (var/datum/dynamic_ruleset/rule in current_rules) + if(rule.rule_process() == RULESET_STOP_PROCESSING) // If rule_process() returns 1 (RULESET_STOP_PROCESSING), stop processing. + current_rules -= rule + + if (midround_injection_cooldown < world.time) + if (GLOB.dynamic_forced_extended) + return + + // Somehow it manages to trigger midround multiple times so this was moved here. + // There is no way this should be able to trigger an injection twice now. + var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min) + midround_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max)) + world.time) + + // Time to inject some threat into the round + if(EMERGENCY_ESCAPED_OR_ENDGAMED) // Unless the shuttle is gone + return + + log_game("DYNAMIC: Checking state of the round.") + + update_playercounts() + + if (prob(get_injection_chance())) + var/list/drafted_rules = list() + for (var/datum/dynamic_ruleset/midround/rule in midround_rules) + if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost) + // Classic secret : only autotraitor/minor roles + if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET))) + continue + rule.candidates = list() + rule.candidates = current_players.Copy() + rule.trim_candidates() + if (rule.ready() && rule.candidates.len > 0) + drafted_rules[rule] = rule.get_weight() + if (drafted_rules.len > 0) + picking_midround_latejoin_rule(drafted_rules) + +/// Updates current_players. +/datum/game_mode/dynamic/proc/update_playercounts() + current_players[CURRENT_LIVING_PLAYERS] = list() + current_players[CURRENT_LIVING_ANTAGS] = list() + current_players[CURRENT_DEAD_PLAYERS] = list() + current_players[CURRENT_OBSERVERS] = list() + for (var/mob/M in GLOB.player_list) + if (istype(M, /mob/dead/new_player)) + continue + if (M.stat != DEAD) + current_players[CURRENT_LIVING_PLAYERS].Add(M) + if (M.mind && (M.mind.special_role || M.mind.antag_datums?.len > 0)) + current_players[CURRENT_LIVING_ANTAGS].Add(M) + else + if (istype(M,/mob/dead/observer)) + var/mob/dead/observer/O = M + if (O.started_as_observer) // Observers + current_players[CURRENT_OBSERVERS].Add(M) + continue + current_players[CURRENT_DEAD_PLAYERS].Add(M) // Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow) + +/// Gets the chance for latejoin and midround injection, the dry_run argument is only used for forced injection. +/datum/game_mode/dynamic/proc/get_injection_chance(dry_run = FALSE) + if(forced_injection) + forced_injection = !dry_run + return 100 + var/chance = 0 + // If the high pop override is in effect, we reduce the impact of population on the antag injection chance + var/high_pop_factor = (current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit) + var/max_pop_per_antag = max(5,15 - round(threat_level/10) - round(current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5))) + if (!current_players[CURRENT_LIVING_ANTAGS].len) + chance += 50 // No antags at all? let's boost those odds! + else + var/current_pop_per_antag = current_players[CURRENT_LIVING_PLAYERS].len / current_players[CURRENT_LIVING_ANTAGS].len + if (current_pop_per_antag > max_pop_per_antag) + chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag)) + else + chance += 25-10*(max_pop_per_antag-current_pop_per_antag) + if (current_players[CURRENT_DEAD_PLAYERS].len > current_players[CURRENT_LIVING_PLAYERS].len) + chance -= 30 // More than half the crew died? ew, let's calm down on antags + if (threat > 70) + chance += 15 + if (threat < 30) + chance -= 15 + return round(max(0,chance)) + +/// Removes type from the list +/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type) + for(var/I in type_list) + if(istype(I, type)) + type_list -= I + return type_list + +/// Checks if a type in blocking_list is in rule_list. +/datum/game_mode/dynamic/proc/check_blocking(list/blocking_list, list/rule_list) + if(blocking_list.len > 0) + for(var/blocking in blocking_list) + for(var/datum/executed in rule_list) + if(blocking == executed.type) + return TRUE + return FALSE + +/// Checks if client age is age or older. +/datum/game_mode/dynamic/proc/check_age(client/C, age) + enemy_minimum_age = age + if(get_remaining_days(C) == 0) + enemy_minimum_age = initial(enemy_minimum_age) + return TRUE // Available in 0 days = available right now = player is old enough to play. + enemy_minimum_age = initial(enemy_minimum_age) + return FALSE + +/datum/game_mode/dynamic/make_antag_chance(mob/living/carbon/human/newPlayer) + if (GLOB.dynamic_forced_extended) + return + if(EMERGENCY_ESCAPED_OR_ENDGAMED) // No more rules after the shuttle has left + return + + update_playercounts() + + if (forced_latejoin_rule) + forced_latejoin_rule.candidates = list(newPlayer) + forced_latejoin_rule.trim_candidates() + log_game("DYNAMIC: Forcing ruleset [forced_latejoin_rule]") + if (forced_latejoin_rule.ready(TRUE)) + picking_midround_latejoin_rule(list(forced_latejoin_rule), forced = TRUE) + forced_latejoin_rule = null + + else if (latejoin_injection_cooldown < world.time && prob(get_injection_chance())) + var/list/drafted_rules = list() + for (var/datum/dynamic_ruleset/latejoin/rule in latejoin_rules) + if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost) + // Classic secret : only autotraitor/minor roles + if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET))) + continue + // No stacking : only one round-enter, unless > stacking_limit threat. + if (threat < GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking) + if(rule.flags & HIGHLANDER_RULESET && highlander_executed) + continue + + rule.candidates = list(newPlayer) + rule.trim_candidates() + if (rule.ready()) + drafted_rules[rule] = rule.get_weight() + + if (drafted_rules.len > 0 && picking_midround_latejoin_rule(drafted_rules)) + var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min) + latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max)) + world.time + +/// Refund threat, but no more than threat_level. +/datum/game_mode/dynamic/proc/refund_threat(regain) + threat = min(threat_level,threat+regain) + +/// Generate threat and increase the threat_level if it goes beyond, capped at 100 +/datum/game_mode/dynamic/proc/create_threat(gain) + threat = min(100, threat+gain) + if(threat > threat_level) + threat_level = threat + +/// Expend threat, can't fall under 0. +/datum/game_mode/dynamic/proc/spend_threat(cost) + threat = max(threat-cost,0) + +/// Turns the value generated by lorentz distribution to threat value between 0 and 100. +/datum/game_mode/dynamic/proc/lorentz_to_threat(x) + switch (x) + if (-INFINITY to -20) + return rand(0, 10) + if (-20 to -10) + return RULE_OF_THREE(-40, -20, x) + 50 + if (-10 to -5) + return RULE_OF_THREE(-30, -10, x) + 50 + if (-5 to -2.5) + return RULE_OF_THREE(-20, -5, x) + 50 + if (-2.5 to -0) + return RULE_OF_THREE(-10, -2.5, x) + 50 + if (0 to 2.5) + return RULE_OF_THREE(10, 2.5, x) + 50 + if (2.5 to 5) + return RULE_OF_THREE(20, 5, x) + 50 + if (5 to 10) + return RULE_OF_THREE(30, 10, x) + 50 + if (10 to 20) + return RULE_OF_THREE(40, 20, x) + 50 + if (20 to INFINITY) + return rand(90, 100) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm new file mode 100644 index 0000000000..66afcbfb92 --- /dev/null +++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm @@ -0,0 +1,211 @@ +/datum/dynamic_ruleset + /// For admin logging and round end screen. + var/name = "" + /// For admin logging and round end screen, do not change this unless making a new rule type. + var/ruletype = "" + /// If set to TRUE, the rule won't be discarded after being executed, and dynamic will call rule_process() every time it ticks. + var/persistent = FALSE + /// If set to TRUE, dynamic mode will be able to draft this ruleset again later on. (doesn't apply for roundstart rules) + var/repeatable = FALSE + /// If set higher than 0 decreases weight by itself causing the ruleset to appear less often the more it is repeated. + var/repeatable_weight_decrease = 2 + /// List of players that are being drafted for this rule + var/list/mob/candidates = list() + /// List of players that were selected for this rule + var/list/datum/mind/assigned = list() + /// Preferences flag such as ROLE_WIZARD that need to be turned on for players to be antag + var/antag_flag = null + /// The antagonist datum that is assigned to the mobs mind on ruleset execution. + var/datum/antagonist/antag_datum = null + /// The required minimum account age for this ruleset. + var/minimum_required_age = 7 + /// If set, and config flag protect_roles_from_antagonist is false, then the rule will not pick players from these roles. + var/list/protected_roles = list() + /// If set, rule will deny candidates from those roles always. + var/list/restricted_roles = list() + /// If set, rule will only accept candidates from those roles, IMPORTANT: DOES NOT WORK ON ROUNDSTART RULESETS. + var/list/exclusive_roles = list() + /// If set, there needs to be a certain amount of players doing those roles (among the players who won't be drafted) for the rule to be drafted IMPORTANT: DOES NOT WORK ON ROUNDSTART RULESETS. + var/list/enemy_roles = list() + /// If enemy_roles was set, this is the amount of enemy job workers needed per threat_level range (0-10,10-20,etc) IMPORTANT: DOES NOT WORK ON ROUNDSTART RULESETS. + var/required_enemies = list(1,1,0,0,0,0,0,0,0,0) + /// The rule needs this many candidates (post-trimming) to be executed (example: Cult needs 4 players at round start) + var/required_candidates = 0 + /// 1 -> 9, probability for this rule to be picked against other rules + var/weight = 5 + /// Threat cost for this rule, this is decreased from the mode's threat when the rule is executed. + var/cost = 0 + /// A flag that determines how the ruleset is handled + /// HIGHLANDER_RULESET are rulesets can end the round. + /// TRAITOR_RULESET and MINOR_RULESET can't end the round and have no difference right now. + var/flags = 0 + /// Pop range per requirement. If zero defaults to mode's pop_per_requirement. + var/pop_per_requirement = 0 + /// Requirements are the threat level requirements per pop range. + /// With the default values, The rule will never get drafted below 10 threat level (aka: "peaceful extended"), and it requires a higher threat level at lower pops. + var/list/requirements = list(40,30,20,10,10,10,10,10,10,10) + /// An alternative, static requirement used instead when pop is over mode's high_pop_limit. + var/high_population_requirement = 10 + /// Reference to the mode, use this instead of SSticker.mode. + var/datum/game_mode/dynamic/mode = null + /// If a role is to be considered another for the purpose of banning. + var/antag_flag_override = null + /// If a ruleset type which is in this list has been executed, then the ruleset will not be executed. + var/list/blocking_rules = list() + /// The minimum amount of players required for the rule to be considered. + var/minimum_players = 0 + /// The maximum amount of players required for the rule to be considered. + /// Anything below zero or exactly zero is ignored. + var/maximum_players = 0 + + +/datum/dynamic_ruleset/New() + ..() + if(CONFIG_GET(flag/protect_roles_from_antagonist)) + restricted_roles += protected_roles + if(CONFIG_GET(flag/protect_assistant_from_antagonist)) + restricted_roles += "Assistant" + + if (istype(SSticker.mode, /datum/game_mode/dynamic)) + mode = SSticker.mode + else if (GLOB.master_mode != "dynamic") // This is here to make roundstart forced ruleset function. + qdel(src) + +/datum/dynamic_ruleset/roundstart // One or more of those drafted at roundstart + ruletype = "Roundstart" + +/datum/dynamic_ruleset/roundstart/delayed/ // Executed with a 30 seconds delay + var/delay = 30 SECONDS + var/required_type = /mob/living/carbon/human // No ghosts, new players or silicons allowed. + +// Can be drafted when a player joins the server +/datum/dynamic_ruleset/latejoin + ruletype = "Latejoin" + +/// By default, a rule is acceptable if it satisfies the threat level/population requirements. +/// If your rule has extra checks, such as counting security officers, do that in ready() instead +/datum/dynamic_ruleset/proc/acceptable(population = 0, threat_level = 0) + if(minimum_players > population) + return FALSE + if(maximum_players > 0 && population > maximum_players) + return FALSE + if (population >= GLOB.dynamic_high_pop_limit) + return (threat_level >= high_population_requirement) + else + pop_per_requirement = pop_per_requirement > 0 ? pop_per_requirement : mode.pop_per_requirement + var/indice_pop = min(10,round(population/pop_per_requirement)+1) + return (threat_level >= requirements[indice_pop]) + +/// This is called if persistent variable is true everytime SSTicker ticks. +/datum/dynamic_ruleset/proc/rule_process() + return + +/// Called on game mode pre_setup, used for non-delayed roundstart rulesets only. +/// Do everything you need to do before job is assigned here. +/// IMPORTANT: ASSIGN special_role HERE +/datum/dynamic_ruleset/proc/pre_execute() + return TRUE + +/// Called on post_setup on roundstart and when the rule executes on midround and latejoin. +/// Give your candidates or assignees equipment and antag datum here. +/datum/dynamic_ruleset/proc/execute() + for(var/datum/mind/M in assigned) + M.add_antag_datum(antag_datum) + return TRUE + +/// Called after delay set in ruleset. +/// Give your candidates or assignees equipment and antag datum here. +/datum/dynamic_ruleset/roundstart/delayed/execute() + if (SSticker && SSticker.current_state < GAME_STATE_PLAYING) + CRASH("The delayed ruleset [name] executed before the round started.") + +/// Here you can perform any additional checks you want. (such as checking the map etc) +/// Remember that on roundstart no one knows what their job is at this point. +/// IMPORTANT: If ready() returns TRUE, that means pre_execute() or execute() should never fail! +/datum/dynamic_ruleset/proc/ready(forced = 0) + if (required_candidates > candidates.len) + return FALSE + return TRUE + +/// Gets weight of the ruleset +/// Note that this decreases weight if repeatable is TRUE and repeatable_weight_decrease is higher than 0 +/// Note: If you don't want repeatable rulesets to decrease their weight use the weight variable directly +/datum/dynamic_ruleset/proc/get_weight() + if(repeatable && weight > 1 && repeatable_weight_decrease > 0) + for(var/datum/dynamic_ruleset/DR in mode.executed_rules) + if(istype(DR, type)) + weight = max(weight-repeatable_weight_decrease,1) + return weight + +/// Here you can remove candidates that do not meet your requirements. +/// This means if their job is not correct or they have disconnected you can remove them from candidates here. +/// Usually this does not need to be changed unless you need some specific requirements from your candidates. +/datum/dynamic_ruleset/proc/trim_candidates() + return + +/// Counts how many players are ready at roundstart. +/// Used only by non-delayed roundstart rulesets. +/datum/dynamic_ruleset/proc/num_players() + . = 0 + for(var/mob/dead/new_player/P in GLOB.player_list) + if(P.client && P.ready == PLAYER_READY_TO_PLAY) + . ++ + +/// Set mode result and news report here. +/// Only called if ruleset is flagged as HIGHLANDER_RULESET +/datum/dynamic_ruleset/proc/round_result() + +/// Checks if round is finished, return true to end the round. +/// Only called if ruleset is flagged as HIGHLANDER_RULESET +/datum/dynamic_ruleset/proc/check_finished() + return FALSE + +////////////////////////////////////////////// +// // +// ROUNDSTART RULESETS // +// // +////////////////////////////////////////////// + +/// Checks if candidates are connected and if they are banned or don't want to be the antagonist. +/datum/dynamic_ruleset/roundstart/trim_candidates() + for(var/mob/dead/new_player/P in candidates) + if (!P.client || !P.mind) // Are they connected? + candidates.Remove(P) + continue + if(!mode.check_age(P.client, minimum_required_age)) + candidates.Remove(P) + continue + if(P.mind.special_role) // We really don't want to give antag to an antag. + candidates.Remove(P) + continue + if (!(antag_flag in P.client.prefs.be_special) || jobban_isbanned(P.ckey, list(antag_flag, ROLE_SYNDICATE)) || (antag_flag_override && jobban_isbanned(P.ckey, list(antag_flag_override, ROLE_SYNDICATE))))//are they willing and not antag-banned? + candidates.Remove(P) + continue + +/// Checks if candidates are required mob type, connected, banned and if the job is exclusive to the role. +/datum/dynamic_ruleset/roundstart/delayed/trim_candidates() + . = ..() + for (var/mob/P in candidates) + if (!istype(P, required_type)) + candidates.Remove(P) // Can be a new_player, etc. + continue + if(!mode.check_age(P.client, minimum_required_age)) + candidates.Remove(P) + continue + if (!P.client || !P.mind || !P.mind.assigned_role) // Are they connected? + candidates.Remove(P) + continue + if(P.mind.special_role || P.mind.antag_datums?.len > 0) // Are they an antag already? + candidates.Remove(P) + continue + if (!(antag_flag in P.client.prefs.be_special) || jobban_isbanned(P.ckey, list(antag_flag, ROLE_SYNDICATE)) || (antag_flag_override && jobban_isbanned(P.ckey, list(antag_flag_override, ROLE_SYNDICATE))))//are they willing and not antag-banned? + candidates.Remove(P) + continue + if ((exclusive_roles.len > 0) && !(P.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job? + candidates.Remove(P) + continue + +/// Do your checks if the ruleset is ready to be executed here. +/// Should ignore certain checks if forced is TRUE +/datum/dynamic_ruleset/roundstart/ready(forced = FALSE) + return ..() diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm new file mode 100644 index 0000000000..24b4c67357 --- /dev/null +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -0,0 +1,110 @@ +////////////////////////////////////////////// +// // +// LATEJOIN RULESETS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/latejoin/trim_candidates() + for(var/mob/P in candidates) + if (!P.client || !P.mind || !P.mind.assigned_role) // Are they connected? + candidates.Remove(P) + continue + if(!mode.check_age(P.client, minimum_required_age)) + candidates.Remove(P) + continue + if (!(antag_flag in P.client.prefs.be_special) || jobban_isbanned(P.ckey, list(antag_flag, ROLE_SYNDICATE)) || (antag_flag_override && jobban_isbanned(P.ckey, list(antag_flag_override))))//are they willing and not antag-banned? + candidates.Remove(P) + continue + if (P.mind.assigned_role in restricted_roles) // Does their job allow for it? + candidates.Remove(P) + continue + if ((exclusive_roles.len > 0) && !(P.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job? + candidates.Remove(P) + continue + +/datum/dynamic_ruleset/latejoin/ready(forced = 0) + if (!forced) + var/job_check = 0 + if (enemy_roles.len > 0) + for (var/mob/M in mode.current_players[CURRENT_LIVING_PLAYERS]) + if (M.stat == DEAD) + continue // Dead players cannot count as opponents + if (M.mind && M.mind.assigned_role && (M.mind.assigned_role in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role in restricted_roles))) + job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it + + var/threat = round(mode.threat_level/10) + if (job_check < required_enemies[threat]) + return FALSE + return ..() + +/datum/dynamic_ruleset/latejoin/execute() + var/mob/M = pick(candidates) + assigned += M.mind + M.mind.special_role = antag_flag + M.mind.add_antag_datum(antag_datum) + return TRUE + +////////////////////////////////////////////// +// // +// SYNDICATE TRAITORS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/latejoin/infiltrator + name = "Syndicate Infiltrator" + antag_datum = /datum/antagonist/traitor + antag_flag = ROLE_TRAITOR + restricted_roles = list("AI", "Cyborg") + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + required_candidates = 1 + weight = 7 + cost = 5 + requirements = list(40,30,20,10,10,10,10,10,10,10) + high_population_requirement = 10 + repeatable = TRUE + flags = TRAITOR_RULESET + +////////////////////////////////////////////// +// // +// REVOLUTIONARY PROVOCATEUR // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/latejoin/provocateur + name = "Provocateur" + antag_datum = /datum/antagonist/rev/head + antag_flag = ROLE_REV_HEAD + antag_flag_override = ROLE_REV + restricted_roles = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + enemy_roles = list("AI", "Cyborg", "Security Officer","Detective","Head of Security", "Captain", "Warden") + required_enemies = list(2,2,1,1,1,1,1,0,0,0) + required_candidates = 1 + weight = 2 + cost = 20 + requirements = list(101,101,70,40,30,20,20,20,20,20) + high_population_requirement = 50 + flags = HIGHLANDER_RULESET + var/required_heads = 3 + +/datum/dynamic_ruleset/latejoin/provocateur/ready(forced=FALSE) + if (forced) + required_heads = 1 + if(!..()) + return FALSE + var/head_check = 0 + for(var/mob/player in mode.current_players[CURRENT_LIVING_PLAYERS]) + if (player.mind.assigned_role in GLOB.command_positions) + head_check++ + return (head_check >= required_heads) + +/datum/dynamic_ruleset/latejoin/provocateur/execute() + var/mob/M = pick(candidates) + assigned += M.mind + M.mind.special_role = antag_flag + var/datum/antagonist/rev/head/new_head = new() + new_head.give_flash = TRUE + new_head.give_hud = TRUE + new_head.remove_clumsy = TRUE + new_head = M.mind.add_antag_datum(new_head) + new_head.rev_team.max_headrevs = 1 // Only one revhead if it is latejoin. + return TRUE diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm new file mode 100644 index 0000000000..2acef4f06b --- /dev/null +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -0,0 +1,460 @@ +////////////////////////////////////////////// +// // +// MIDROUND RULESETS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround // Can be drafted once in a while during a round + ruletype = "Midround" + /// If the ruleset should be restricted from ghost roles. + var/restrict_ghost_roles = TRUE + /// What type the ruleset is restricted to. + var/required_type = /mob/living/carbon/human + var/list/living_players = list() + var/list/living_antags = list() + var/list/dead_players = list() + var/list/list_observers = list() + +/datum/dynamic_ruleset/midround/from_ghosts + weight = 0 + /// Whether the ruleset should call generate_ruleset_body or not. + var/makeBody = TRUE + +/datum/dynamic_ruleset/midround/trim_candidates() + // Unlike the previous two types, these rulesets are not meant for /mob/dead/new_player + // And since I want those rulesets to be as flexible as possible, I'm not gonna put much here, + // + // All you need to know is that here, the candidates list contains 4 lists itself, indexed with the following defines: + // Candidates = list(CURRENT_LIVING_PLAYERS, CURRENT_LIVING_ANTAGS, CURRENT_DEAD_PLAYERS, CURRENT_OBSERVERS) + // So for example you can get the list of all current dead players with var/list/dead_players = candidates[CURRENT_DEAD_PLAYERS] + // Make sure to properly typecheck the mobs in those lists, as the dead_players list could contain ghosts, or dead players still in their bodies. + // We're still gonna trim the obvious (mobs without clients, jobbanned players, etc) + living_players = trim_list(mode.current_players[CURRENT_LIVING_PLAYERS]) + living_antags = trim_list(mode.current_players[CURRENT_LIVING_ANTAGS]) + dead_players = trim_list(mode.current_players[CURRENT_DEAD_PLAYERS]) + list_observers = trim_list(mode.current_players[CURRENT_OBSERVERS]) + +/datum/dynamic_ruleset/midround/proc/trim_list(list/L = list()) + var/list/trimmed_list = L.Copy() + var/antag_name = initial(antag_flag) + for(var/mob/M in trimmed_list) + if (!istype(M, required_type)) + trimmed_list.Remove(M) + continue + if (!M.client) // Are they connected? + trimmed_list.Remove(M) + continue + if(!mode.check_age(M.client, minimum_required_age)) + trimmed_list.Remove(M) + continue + if (!(antag_name in M.client.prefs.be_special) || jobban_isbanned(M.ckey, list(antag_name, ROLE_SYNDICATE)))//are they willing and not antag-banned? + trimmed_list.Remove(M) + continue + if (M.mind) + if (restrict_ghost_roles && M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL]) // Are they playing a ghost role? + trimmed_list.Remove(M) + continue + if (M.mind.assigned_role in restricted_roles || HAS_TRAIT(M, TRAIT_MINDSHIELD)) // Does their job allow it or are they mindshielded? + trimmed_list.Remove(M) + continue + if ((exclusive_roles.len > 0) && !(M.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job? + trimmed_list.Remove(M) + continue + return trimmed_list + +// You can then for example prompt dead players in execute() to join as strike teams or whatever +// Or autotator someone + +// IMPORTANT, since /datum/dynamic_ruleset/midround may accept candidates from both living, dead, and even antag players, you need to manually check whether there are enough candidates +// (see /datum/dynamic_ruleset/midround/autotraitor/ready(var/forced = FALSE) for example) +/datum/dynamic_ruleset/midround/ready(forced = FALSE) + if (!forced) + var/job_check = 0 + if (enemy_roles.len > 0) + for (var/mob/M in living_players) + if (M.stat == DEAD) + continue // Dead players cannot count as opponents + if (M.mind && M.mind.assigned_role && (M.mind.assigned_role in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role in restricted_roles))) + job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it + + var/threat = round(mode.threat_level/10) + if (job_check < required_enemies[threat]) + return FALSE + return TRUE + +/datum/dynamic_ruleset/midround/from_ghosts/execute() + var/list/possible_candidates = list() + possible_candidates.Add(dead_players) + possible_candidates.Add(list_observers) + send_applications(possible_candidates) + if(assigned.len > 0) + return TRUE + else + return FALSE + +/// This sends a poll to ghosts if they want to be a ghost spawn from a ruleset. +/datum/dynamic_ruleset/midround/from_ghosts/proc/send_applications(list/possible_volunteers = list()) + if (possible_volunteers.len <= 0) // This shouldn't happen, as ready() should return FALSE if there is not a single valid candidate + message_admins("Possible volunteers was 0. This shouldn't appear, because of ready(), unless you forced it!") + return + message_admins("Polling [possible_volunteers.len] players to apply for the [name] ruleset.") + log_game("DYNAMIC: Polling [possible_volunteers.len] players to apply for the [name] ruleset.") + + candidates = pollGhostCandidates("The mode is looking for volunteers to become [antag_flag] for [name]", antag_flag, SSticker.mode, antag_flag, poll_time = 300) + + if(!candidates || candidates.len <= 0) + message_admins("The ruleset [name] received no applications.") + log_game("DYNAMIC: The ruleset [name] received no applications.") + mode.refund_threat(cost) + mode.threat_log += "[worldtime2text()]: Rule [name] refunded [cost] (no applications)" + mode.executed_rules -= src + return + + message_admins("[candidates.len] players volunteered for the ruleset [name].") + log_game("DYNAMIC: [candidates.len] players volunteered for [name].") + review_applications() + +/// Here is where you can check if your ghost applicants are valid for the ruleset. +/// Called by send_applications(). +/datum/dynamic_ruleset/midround/from_ghosts/proc/review_applications() + for (var/i = 1, i <= required_candidates, i++) + if(candidates.len <= 0) + if(i == 1) + // We have found no candidates so far and we are out of applicants. + mode.refund_threat(cost) + mode.threat_log += "[worldtime2text()]: Rule [name] refunded [cost] (all applications invalid)" + mode.executed_rules -= src + break + var/mob/applicant = pick(candidates) + candidates -= applicant + if(!isobserver(applicant)) + if(applicant.stat == DEAD) // Not an observer? If they're dead, make them one. + applicant = applicant.ghostize(FALSE) + else // Not dead? Disregard them, pick a new applicant + i-- + continue + + if(!applicant) + i-- + continue + + var/mob/new_character = applicant + + if (makeBody) + new_character = generate_ruleset_body(applicant) + + finish_setup(new_character, i) + assigned += applicant + notify_ghosts("[new_character] has been picked for the ruleset [name]!", source = new_character, action = NOTIFY_ORBIT, header="Something Interesting!") + +/datum/dynamic_ruleset/midround/from_ghosts/proc/generate_ruleset_body(mob/applicant) + var/mob/living/carbon/human/new_character = makeBody(applicant) + new_character.dna.remove_all_mutations() + return new_character + +/datum/dynamic_ruleset/midround/from_ghosts/proc/finish_setup(mob/new_character, index) + var/datum/antagonist/new_role = new antag_datum() + setup_role(new_role) + new_character.mind.add_antag_datum(new_role) + new_character.mind.special_role = antag_flag + +/datum/dynamic_ruleset/midround/from_ghosts/proc/setup_role(datum/antagonist/new_role) + return + +////////////////////////////////////////////// +// // +// SYNDICATE TRAITORS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/autotraitor + name = "Syndicate Sleeper Agent" + antag_datum = /datum/antagonist/traitor + antag_flag = ROLE_TRAITOR + restricted_roles = list("AI", "Cyborg", "Positronic Brain") + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + required_candidates = 1 + weight = 7 + cost = 10 + requirements = list(50,40,30,20,10,10,10,10,10,10) + repeatable = TRUE + high_population_requirement = 10 + flags = TRAITOR_RULESET + +/datum/dynamic_ruleset/midround/autotraitor/acceptable(population = 0, threat = 0) + var/player_count = mode.current_players[CURRENT_LIVING_PLAYERS].len + var/antag_count = mode.current_players[CURRENT_LIVING_ANTAGS].len + var/max_traitors = round(player_count / 10) + 1 + if ((antag_count < max_traitors) && prob(mode.threat_level))//adding traitors if the antag population is getting low + return ..() + else + return FALSE + +/datum/dynamic_ruleset/midround/autotraitor/trim_candidates() + ..() + for(var/mob/living/player in living_players) + if(issilicon(player)) // Your assigned role doesn't change when you are turned into a silicon. + living_players -= player + continue + if(is_centcom_level(player.z)) + living_players -= player // We don't autotator people in CentCom + continue + if(player.mind && (player.mind.special_role || player.mind.antag_datums?.len > 0)) + living_players -= player // We don't autotator people with roles already + +/datum/dynamic_ruleset/midround/autotraitor/ready(forced = FALSE) + if (required_candidates > living_players.len) + return FALSE + return ..() + +/datum/dynamic_ruleset/midround/autotraitor/execute() + var/mob/M = pick(living_players) + assigned += M + living_players -= M + var/datum/antagonist/traitor/newTraitor = new + M.mind.add_antag_datum(newTraitor) + return TRUE + + +////////////////////////////////////////////// +// // +// Malfunctioning AI // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/malf + name = "Malfunctioning AI" + antag_datum = /datum/antagonist/traitor + antag_flag = ROLE_MALF + enemy_roles = list("Security Officer", "Warden","Detective","Head of Security", "Captain", "Scientist", "Chemist", "Research Director", "Chief Engineer") + exclusive_roles = list("AI") + required_enemies = list(4,4,4,4,4,4,2,2,2,0) + required_candidates = 1 + weight = 3 + cost = 35 + requirements = list(101,101,80,70,60,60,50,50,40,40) + high_population_requirement = 35 + required_type = /mob/living/silicon/ai + var/ion_announce = 33 + var/removeDontImproveChance = 10 + +/datum/dynamic_ruleset/midround/malf/trim_candidates() + ..() + candidates = candidates[CURRENT_LIVING_PLAYERS] + for(var/mob/living/player in candidates) + if(!isAI(player)) + candidates -= player + continue + if(is_centcom_level(player.z)) + candidates -= player + continue + if(player.mind && (player.mind.special_role || player.mind.antag_datums?.len > 0)) + candidates -= player + +/datum/dynamic_ruleset/midround/malf/execute() + if(!candidates || !candidates.len) + return FALSE + var/mob/living/silicon/ai/M = pick(candidates) + candidates -= M + assigned += M.mind + var/datum/antagonist/traitor/AI = new + M.mind.special_role = antag_flag + M.mind.add_antag_datum(AI) + if(prob(ion_announce)) + priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", 'sound/ai/ionstorm.ogg') + if(prob(removeDontImproveChance)) + M.replace_random_law(generate_ion_law(), list(LAW_INHERENT, LAW_SUPPLIED, LAW_ION)) + else + M.add_ion_law(generate_ion_law()) + return TRUE + +////////////////////////////////////////////// +// // +// WIZARD (GHOST) // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/from_ghosts/wizard + name = "Wizard" + antag_datum = /datum/antagonist/wizard + antag_flag = ROLE_WIZARD + enemy_roles = list("Security Officer","Detective","Head of Security", "Captain") + required_enemies = list(2,2,1,1,1,1,1,0,0,0) + required_candidates = 1 + weight = 1 + cost = 20 + requirements = list(90,90,70,40,30,20,10,10,10,10) + high_population_requirement = 50 + repeatable = TRUE + +/datum/dynamic_ruleset/midround/from_ghosts/wizard/ready(forced = FALSE) + if (required_candidates > (dead_players.len + list_observers.len)) + return FALSE + if(GLOB.wizardstart.len == 0) + log_admin("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.") + message_admins("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.") + return FALSE + return ..() + +/datum/dynamic_ruleset/midround/from_ghosts/wizard/finish_setup(mob/new_character, index) + ..() + new_character.forceMove(pick(GLOB.wizardstart)) + +////////////////////////////////////////////// +// // +// NUCLEAR OPERATIVES (MIDROUND) // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/from_ghosts/nuclear + name = "Nuclear Assault" + antag_flag = ROLE_OPERATIVE + antag_datum = /datum/antagonist/nukeop + enemy_roles = list("AI", "Cyborg", "Security Officer", "Warden","Detective","Head of Security", "Captain") + required_enemies = list(3,3,3,3,3,2,1,1,0,0) + required_candidates = 5 + weight = 5 + cost = 35 + requirements = list(90,90,90,80,60,40,30,20,10,10) + high_population_requirement = 10 + var/operative_cap = list(2,2,3,3,4,5,5,5,5,5) + var/datum/team/nuclear/nuke_team + flags = HIGHLANDER_RULESET + +/datum/dynamic_ruleset/midround/from_ghosts/nuclear/acceptable(population=0, threat=0) + if (locate(/datum/dynamic_ruleset/roundstart/nuclear) in mode.executed_rules) + return FALSE // Unavailable if nuke ops were already sent at roundstart + var/indice_pop = min(10,round(living_players.len/5)+1) + required_candidates = operative_cap[indice_pop] + return ..() + +/datum/dynamic_ruleset/midround/from_ghosts/nuclear/ready(forced = FALSE) + if (required_candidates > (dead_players.len + list_observers.len)) + return FALSE + return ..() + +/datum/dynamic_ruleset/midround/from_ghosts/nuclear/finish_setup(mob/new_character, index) + new_character.mind.special_role = "Nuclear Operative" + new_character.mind.assigned_role = "Nuclear Operative" + if (index == 1) // Our first guy is the leader + var/datum/antagonist/nukeop/leader/new_role = new + nuke_team = new_role.nuke_team + new_character.mind.add_antag_datum(new_role) + else + return ..() + +////////////////////////////////////////////// +// // +// BLOB (GHOST) // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/from_ghosts/blob + name = "Blob" + antag_datum = /datum/antagonist/blob + antag_flag = ROLE_BLOB + enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain") + required_enemies = list(2,2,1,1,1,1,1,0,0,0) + required_candidates = 1 + weight = 4 + cost = 10 + requirements = list(101,101,101,80,60,50,30,20,10,10) + high_population_requirement = 50 + repeatable = TRUE + +/datum/dynamic_ruleset/midround/from_ghosts/blob/generate_ruleset_body(mob/applicant) + var/body = applicant.become_overmind() + return body + +////////////////////////////////////////////// +// // +// XENOMORPH (GHOST) // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/from_ghosts/xenomorph + name = "Alien Infestation" + antag_datum = /datum/antagonist/xeno + antag_flag = ROLE_ALIEN + enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain") + required_enemies = list(2,2,1,1,1,1,1,0,0,0) + required_candidates = 1 + weight = 3 + cost = 10 + requirements = list(101,101,101,70,50,40,20,15,10,10) + high_population_requirement = 50 + repeatable = TRUE + var/list/vents = list() + +/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/execute() + // 50% chance of being incremented by one + required_candidates += prob(50) + for(var/obj/machinery/atmospherics/components/unary/vent_pump/temp_vent in GLOB.machines) + if(QDELETED(temp_vent)) + continue + if(is_station_level(temp_vent.loc.z) && !temp_vent.welded) + var/datum/pipeline/temp_vent_parent = temp_vent.parents[1] + if(!temp_vent_parent) + continue // No parent vent + // Stops Aliens getting stuck in small networks. + // See: Security, Virology + if(temp_vent_parent.other_atmosmch.len > 20) + vents += temp_vent + if(!vents.len) + return FALSE + . = ..() + +/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/generate_ruleset_body(mob/applicant) + var/obj/vent = pick_n_take(vents) + var/mob/living/carbon/alien/larva/new_xeno = new(vent.loc) + new_xeno.key = applicant.key + message_admins("[ADMIN_LOOKUPFLW(new_xeno)] has been made into an alien by the midround ruleset.") + log_game("DYNAMIC: [key_name(new_xeno)] was spawned as an alien by the midround ruleset.") + return new_xeno + +////////////////////////////////////////////// +// // +// NIGHTMARE (GHOST) // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/from_ghosts/nightmare + name = "Nightmare" + antag_datum = /datum/antagonist/nightmare + antag_flag = "Nightmare" + antag_flag_override = ROLE_ALIEN + enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain") + required_enemies = list(2,2,1,1,1,1,1,0,0,0) + required_candidates = 1 + weight = 3 + cost = 10 + requirements = list(101,101,101,70,50,40,20,15,10,10) + high_population_requirement = 50 + repeatable = TRUE + var/list/spawn_locs = list() + +/datum/dynamic_ruleset/midround/from_ghosts/nightmare/execute() + for(var/X in GLOB.xeno_spawn) + var/turf/T = X + var/light_amount = T.get_lumcount() + if(light_amount < SHADOW_SPECIES_LIGHT_THRESHOLD) + spawn_locs += T + if(!spawn_locs.len) + return FALSE + . = ..() + +/datum/dynamic_ruleset/midround/from_ghosts/nightmare/generate_ruleset_body(mob/applicant) + var/datum/mind/player_mind = new /datum/mind(applicant.key) + player_mind.active = TRUE + + var/mob/living/carbon/human/S = new (pick(spawn_locs)) + player_mind.transfer_to(S) + player_mind.assigned_role = "Nightmare" + player_mind.special_role = "Nightmare" + player_mind.add_antag_datum(/datum/antagonist/nightmare) + S.set_species(/datum/species/shadow/nightmare) + + playsound(S, 'sound/magic/ethereal_exit.ogg', 50, 1, -1) + message_admins("[ADMIN_LOOKUPFLW(S)] has been made into a Nightmare by the midround ruleset.") + log_game("DYNAMIC: [key_name(S)] was spawned as a Nightmare by the midround ruleset.") + return S diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm new file mode 100644 index 0000000000..38ce6f68d0 --- /dev/null +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -0,0 +1,732 @@ + +////////////////////////////////////////////// +// // +// SYNDICATE TRAITORS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/traitor + name = "Traitors" + persistent = TRUE + antag_flag = ROLE_TRAITOR + antag_datum = /datum/antagonist/traitor/ + minimum_required_age = 0 + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster", "Cyborg") + restricted_roles = list("Cyborg") + required_candidates = 1 + weight = 5 + cost = 10 + requirements = list(10,10,10,10,10,10,10,10,10,10) + high_population_requirement = 10 + var/autotraitor_cooldown = 450 // 15 minutes (ticks once per 2 sec) + +/datum/dynamic_ruleset/roundstart/traitor/pre_execute() + var/traitor_scaling_coeff = 10 - max(0,round(mode.threat_level/10)-5) // Above 50 threat level, coeff goes down by 1 for every 10 levels + var/num_traitors = min(round(mode.candidates.len / traitor_scaling_coeff) + 1, candidates.len) + for (var/i = 1 to num_traitors) + var/mob/M = pick(candidates) + candidates -= M + assigned += M.mind + M.mind.special_role = ROLE_TRAITOR + M.mind.restricted_roles = restricted_roles + return TRUE + +/datum/dynamic_ruleset/roundstart/traitor/rule_process() + if (autotraitor_cooldown > 0) + autotraitor_cooldown-- + else + autotraitor_cooldown = 450 // 15 minutes + message_admins("Checking if we can turn someone into a traitor.") + log_game("DYNAMIC: Checking if we can turn someone into a traitor.") + mode.picking_specific_rule(/datum/dynamic_ruleset/midround/autotraitor) + +////////////////////////////////////////// +// // +// BLOOD BROTHERS // +// // +////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/traitorbro + name = "Blood Brothers" + antag_flag = ROLE_BROTHER + antag_datum = /datum/antagonist/brother/ + restricted_roles = list("AI", "Cyborg") + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + required_candidates = 2 + weight = 4 + cost = 10 + requirements = list(40,30,30,20,20,15,15,15,10,10) + high_population_requirement = 15 + var/list/datum/team/brother_team/pre_brother_teams = list() + var/const/team_amount = 2 // Hard limit on brother teams if scaling is turned off + var/const/min_team_size = 2 + +/datum/dynamic_ruleset/roundstart/traitorbro/pre_execute() + var/num_teams = team_amount + var/bsc = CONFIG_GET(number/brother_scaling_coeff) + if(bsc) + num_teams = max(1, round(num_players() / bsc)) + + for(var/j = 1 to num_teams) + if(candidates.len < min_team_size || candidates.len < required_candidates) + break + var/datum/team/brother_team/team = new + var/team_size = prob(10) ? min(3, candidates.len) : 2 + for(var/k = 1 to team_size) + var/mob/bro = pick(candidates) + candidates -= bro + assigned += bro.mind + team.add_member(bro.mind) + bro.mind.special_role = "brother" + bro.mind.restricted_roles = restricted_roles + pre_brother_teams += team + return TRUE + +/datum/dynamic_ruleset/roundstart/traitorbro/execute() + for(var/datum/team/brother_team/team in pre_brother_teams) + team.pick_meeting_area() + team.forge_brother_objectives() + for(var/datum/mind/M in team.members) + M.add_antag_datum(/datum/antagonist/brother, team) + team.update_name() + mode.brother_teams += pre_brother_teams + return TRUE + +////////////////////////////////////////////// +// // +// CHANGELINGS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/changeling + name = "Changelings" + antag_flag = ROLE_CHANGELING + antag_datum = /datum/antagonist/changeling + restricted_roles = list("AI", "Cyborg") + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + required_candidates = 1 + weight = 3 + cost = 30 + requirements = list(80,70,60,50,40,20,20,10,10,10) + high_population_requirement = 10 + var/team_mode_probability = 30 + +/datum/dynamic_ruleset/roundstart/changeling/pre_execute() + var/num_changelings = min(round(mode.candidates.len / 10) + 1, candidates.len) + for (var/i = 1 to num_changelings) + var/mob/M = pick(candidates) + candidates -= M + assigned += M.mind + M.mind.restricted_roles = restricted_roles + M.mind.special_role = ROLE_CHANGELING + return TRUE + +/datum/dynamic_ruleset/roundstart/changeling/execute() + var/team_mode = FALSE + if(prob(team_mode_probability)) + team_mode = TRUE + var/list/team_objectives = subtypesof(/datum/objective/changeling_team_objective) + var/list/possible_team_objectives = list() + for(var/T in team_objectives) + var/datum/objective/changeling_team_objective/CTO = T + if(assigned.len >= initial(CTO.min_lings)) + possible_team_objectives += T + + if(possible_team_objectives.len && prob(20*assigned.len)) + GLOB.changeling_team_objective_type = pick(possible_team_objectives) + for(var/datum/mind/changeling in assigned) + var/datum/antagonist/changeling/new_antag = new antag_datum() + new_antag.team_mode = team_mode + changeling.add_antag_datum(new_antag) + return TRUE + +////////////////////////////////////////////// +// // +// WIZARDS // +// // +////////////////////////////////////////////// + +// Dynamic is a wonderful thing that adds wizards to every round and then adds even more wizards during the round. +/datum/dynamic_ruleset/roundstart/wizard + name = "Wizard" + antag_flag = ROLE_WIZARD + antag_datum = /datum/antagonist/wizard + minimum_required_age = 14 + restricted_roles = list("Head of Security", "Captain") // Just to be sure that a wizard getting picked won't ever imply a Captain or HoS not getting drafted + required_candidates = 1 + weight = 1 + cost = 30 + requirements = list(90,90,70,40,30,20,10,10,10,10) + high_population_requirement = 10 + var/list/roundstart_wizards = list() + +/datum/dynamic_ruleset/roundstart/wizard/acceptable(population=0, threat=0) + if(GLOB.wizardstart.len == 0) + log_admin("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.") + message_admins("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.") + return FALSE + return ..() + +/datum/dynamic_ruleset/roundstart/wizard/pre_execute() + if(GLOB.wizardstart.len == 0) + return FALSE + + var/mob/M = pick(candidates) + if (M) + candidates -= M + assigned += M.mind + M.mind.assigned_role = ROLE_WIZARD + M.mind.special_role = ROLE_WIZARD + + return TRUE + +/datum/dynamic_ruleset/roundstart/wizard/execute() + for(var/datum/mind/M in assigned) + M.current.forceMove(pick(GLOB.wizardstart)) + M.add_antag_datum(new antag_datum()) + return TRUE + +////////////////////////////////////////////// +// // +// BLOOD CULT // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/bloodcult + name = "Blood Cult" + antag_flag = ROLE_CULTIST + antag_datum = /datum/antagonist/cult + minimum_required_age = 14 + restricted_roles = list("AI", "Cyborg") + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + required_candidates = 2 + weight = 3 + cost = 30 + requirements = list(100,90,80,60,40,30,10,10,10,10) + high_population_requirement = 10 + pop_per_requirement = 5 + flags = HIGHLANDER_RULESET + var/cultist_cap = list(2,2,2,3,3,4,4,4,4,4) + var/datum/team/cult/main_cult + +/datum/dynamic_ruleset/roundstart/bloodcult/ready(forced = FALSE) + var/indice_pop = min(10,round(mode.roundstart_pop_ready/pop_per_requirement)+1) + required_candidates = cultist_cap[indice_pop] + . = ..() + +/datum/dynamic_ruleset/roundstart/bloodcult/pre_execute() + var/indice_pop = min(10,round(mode.roundstart_pop_ready/pop_per_requirement)+1) + var/cultists = cultist_cap[indice_pop] + for(var/cultists_number = 1 to cultists) + if(candidates.len <= 0) + break + var/mob/M = pick(candidates) + candidates -= M + assigned += M.mind + M.mind.special_role = ROLE_CULTIST + M.mind.restricted_roles = restricted_roles + return TRUE + +/datum/dynamic_ruleset/roundstart/bloodcult/execute() + main_cult = new + for(var/datum/mind/M in assigned) + var/datum/antagonist/cult/new_cultist = new antag_datum() + new_cultist.cult_team = main_cult + new_cultist.give_equipment = TRUE + M.add_antag_datum(new_cultist) + main_cult.setup_objectives() + return TRUE + +/datum/dynamic_ruleset/roundstart/bloodcult/round_result() + ..() + if(main_cult.check_cult_victory()) + SSticker.mode_result = "win - cult win" + SSticker.news_report = CULT_SUMMON + else + SSticker.mode_result = "loss - staff stopped the cult" + SSticker.news_report = CULT_FAILURE + +////////////////////////////////////////////// +// // +// NUCLEAR OPERATIVES // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/nuclear + name = "Nuclear Emergency" + antag_flag = ROLE_OPERATIVE + antag_datum = /datum/antagonist/nukeop + var/datum/antagonist/antag_leader_datum = /datum/antagonist/nukeop/leader + minimum_required_age = 14 + restricted_roles = list("Head of Security", "Captain") // Just to be sure that a nukie getting picked won't ever imply a Captain or HoS not getting drafted + required_candidates = 5 + weight = 3 + cost = 40 + requirements = list(90,90,90,80,60,40,30,20,10,10) + high_population_requirement = 10 + pop_per_requirement = 5 + flags = HIGHLANDER_RULESET + var/operative_cap = list(2,2,2,3,3,3,4,4,5,5) + var/datum/team/nuclear/nuke_team + +/datum/dynamic_ruleset/roundstart/nuclear/ready(forced = FALSE) + var/indice_pop = min(10,round(mode.roundstart_pop_ready/pop_per_requirement)+1) + required_candidates = operative_cap[indice_pop] + . = ..() + +/datum/dynamic_ruleset/roundstart/nuclear/pre_execute() + // If ready() did its job, candidates should have 5 or more members in it + + var/indice_pop = min(10,round(mode.roundstart_pop_ready/5)+1) + var/operatives = operative_cap[indice_pop] + for(var/operatives_number = 1 to operatives) + if(candidates.len <= 0) + break + var/mob/M = pick(candidates) + candidates -= M + assigned += M.mind + M.mind.assigned_role = "Nuclear Operative" + M.mind.special_role = "Nuclear Operative" + return TRUE + +/datum/dynamic_ruleset/roundstart/nuclear/execute() + var/leader = TRUE + for(var/datum/mind/M in assigned) + if (leader) + leader = FALSE + var/datum/antagonist/nukeop/leader/new_op = M.add_antag_datum(antag_leader_datum) + nuke_team = new_op.nuke_team + else + var/datum/antagonist/nukeop/new_op = new antag_datum() + M.add_antag_datum(new_op) + return TRUE + +/datum/dynamic_ruleset/roundstart/nuclear/round_result() + var result = nuke_team.get_result() + switch(result) + if(NUKE_RESULT_FLUKE) + SSticker.mode_result = "loss - syndicate nuked - disk secured" + SSticker.news_report = NUKE_SYNDICATE_BASE + if(NUKE_RESULT_NUKE_WIN) + SSticker.mode_result = "win - syndicate nuke" + SSticker.news_report = STATION_NUKED + if(NUKE_RESULT_NOSURVIVORS) + SSticker.mode_result = "halfwin - syndicate nuke - did not evacuate in time" + SSticker.news_report = STATION_NUKED + if(NUKE_RESULT_WRONG_STATION) + SSticker.mode_result = "halfwin - blew wrong station" + SSticker.news_report = NUKE_MISS + if(NUKE_RESULT_WRONG_STATION_DEAD) + SSticker.mode_result = "halfwin - blew wrong station - did not evacuate in time" + SSticker.news_report = NUKE_MISS + if(NUKE_RESULT_CREW_WIN_SYNDIES_DEAD) + SSticker.mode_result = "loss - evacuation - disk secured - syndi team dead" + SSticker.news_report = OPERATIVES_KILLED + if(NUKE_RESULT_CREW_WIN) + SSticker.mode_result = "loss - evacuation - disk secured" + SSticker.news_report = OPERATIVES_KILLED + if(NUKE_RESULT_DISK_LOST) + SSticker.mode_result = "halfwin - evacuation - disk not secured" + SSticker.news_report = OPERATIVE_SKIRMISH + if(NUKE_RESULT_DISK_STOLEN) + SSticker.mode_result = "halfwin - detonation averted" + SSticker.news_report = OPERATIVE_SKIRMISH + else + SSticker.mode_result = "halfwin - interrupted" + SSticker.news_report = OPERATIVE_SKIRMISH + +////////////////////////////////////////////// +// // +// REVS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/delayed/revs + name = "Revolution" + persistent = TRUE + antag_flag = ROLE_REV_HEAD + antag_flag_override = ROLE_REV + antag_datum = /datum/antagonist/rev/head + minimum_required_age = 14 + restricted_roles = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + required_candidates = 3 + weight = 2 + cost = 35 + requirements = list(101,101,70,40,30,20,10,10,10,10) + high_population_requirement = 10 + delay = 5 MINUTES + flags = HIGHLANDER_RULESET + // I give up, just there should be enough heads with 35 players... + minimum_players = 35 + var/datum/team/revolution/revolution + var/finished = 0 + +/datum/dynamic_ruleset/roundstart/delayed/revs/execute() + var/max_canditates = 4 + revolution = new() + for(var/i = 1 to max_canditates) + if(candidates.len <= 0) + break + var/mob/M = pick(candidates) + candidates -= M + assigned += M.mind + M.mind.restricted_roles = restricted_roles + M.mind.special_role = antag_flag + var/datum/antagonist/rev/head/new_head = new antag_datum() + new_head.give_flash = TRUE + new_head.give_hud = TRUE + new_head.remove_clumsy = TRUE + M.mind.add_antag_datum(new_head,revolution) + + revolution.update_objectives() + revolution.update_heads() + SSshuttle.registerHostileEnvironment(src) + + return TRUE + +/datum/dynamic_ruleset/roundstart/delayed/revs/rule_process() + if(check_rev_victory()) + finished = 1 + else if(check_heads_victory()) + finished = 2 + return + +/datum/dynamic_ruleset/roundstart/delayed/revs/check_finished() + if(CONFIG_GET(keyed_list/continuous)["revolution"]) + if(finished) + SSshuttle.clearHostileEnvironment(src) + return ..() + if(finished != 0) + return TRUE + else + return ..() + +/datum/dynamic_ruleset/roundstart/delayed/revs/proc/check_rev_victory() + for(var/datum/objective/mutiny/objective in revolution.objectives) + if(!(objective.check_completion())) + return FALSE + return TRUE + +/datum/dynamic_ruleset/roundstart/delayed/revs/proc/check_heads_victory() + for(var/datum/mind/rev_mind in revolution.head_revolutionaries()) + var/turf/T = get_turf(rev_mind.current) + if(!considered_afk(rev_mind) && considered_alive(rev_mind) && is_station_level(T.z)) + if(ishuman(rev_mind.current) || ismonkey(rev_mind.current)) + return FALSE + return TRUE + +/datum/dynamic_ruleset/roundstart/delayed/revs/round_result() + if(finished == 1) + SSticker.mode_result = "win - heads killed" + SSticker.news_report = REVS_WIN + else if(finished == 2) + SSticker.mode_result = "loss - rev heads killed" + SSticker.news_report = REVS_LOSE + +// Admin only rulesets. The threat requirement is 101 so it is not possible to roll them. + +////////////////////////////////////////////// +// // +// EXTENDED // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/extended + name = "Extended" + antag_flag = null + antag_datum = null + restricted_roles = list() + required_candidates = 0 + weight = 3 + cost = 0 + requirements = list(101,101,101,101,101,101,101,101,101,101) + high_population_requirement = 101 + +/datum/dynamic_ruleset/roundstart/extended/pre_execute() + message_admins("Starting a round of extended.") + log_game("Starting a round of extended.") + mode.spend_threat(mode.threat) + return TRUE + +////////////////////////////////////////////// +// // +// CLOCKCULT // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/clockcult + name = "Clockcult" + antag_flag = ROLE_SERVANT_OF_RATVAR + antag_datum = /datum/antagonist/clockcult + restricted_roles = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + required_candidates = 4 + weight = 3 + cost = 0 + requirements = list(101,101,101,101,101,101,101,101,101,101) + high_population_requirement = 101 + flags = HIGHLANDER_RULESET + var/ark_time + +/datum/dynamic_ruleset/roundstart/clockcult/pre_execute() + var/list/errorList = list() + var/list/reebes = SSmapping.LoadGroup(errorList, "Reebe", "map_files/generic", "City_of_Cogs.dmm", default_traits = ZTRAITS_REEBE, silent = TRUE) + if(errorList.len) + message_admins("Reebe failed to load!") + log_game("Reebe failed to load!") + return FALSE + for(var/datum/parsed_map/PM in reebes) + PM.initTemplateBounds() + + var/starter_servants = 4 + var/number_players = num_players() + if(number_players > 30) + number_players -= 30 + starter_servants += round(number_players / 10) + starter_servants = min(starter_servants, 8) + for (var/i in 1 to starter_servants) + var/mob/servant = pick(candidates) + candidates -= servant + assigned += servant.mind + servant.mind.assigned_role = ROLE_SERVANT_OF_RATVAR + servant.mind.special_role = ROLE_SERVANT_OF_RATVAR + ark_time = 30 + round((number_players / 5)) + ark_time = min(ark_time, 35) + return TRUE + +/datum/dynamic_ruleset/roundstart/clockcult/execute() + var/list/spread_out_spawns = GLOB.servant_spawns.Copy() + for(var/datum/mind/servant in assigned) + var/mob/S = servant.current + if(!spread_out_spawns.len) + spread_out_spawns = GLOB.servant_spawns.Copy() + log_game("[key_name(servant)] was made an initial servant of Ratvar") + var/turf/T = pick_n_take(spread_out_spawns) + S.forceMove(T) + greet_servant(S) + equip_servant(S) + add_servant_of_ratvar(S, TRUE) + var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar //that's a mouthful + G.final_countdown(ark_time) + return TRUE + +/datum/dynamic_ruleset/roundstart/clockcult/proc/greet_servant(mob/M) //Description of their role + if(!M) + return 0 + to_chat(M, "You are a servant of Ratvar, the Clockwork Justiciar!") + to_chat(M, "You have approximately [ark_time] minutes until the Ark activates.") + to_chat(M, "Unlock Script scripture by converting a new servant.") + to_chat(M, "Application scripture will be unlocked halfway until the Ark's activation.") + M.playsound_local(get_turf(M), 'sound/ambience/antag/clockcultalr.ogg', 100, FALSE, pressure_affected = FALSE) + return 1 + +/datum/dynamic_ruleset/roundstart/clockcult/proc/equip_servant(mob/living/M) //Grants a clockwork slab to the mob, with one of each component + if(!M || !ishuman(M)) + return FALSE + var/mob/living/carbon/human/L = M + L.equipOutfit(/datum/outfit/servant_of_ratvar) + var/obj/item/clockwork/slab/S = new + var/slot = "At your feet" + var/list/slots = list("In your left pocket" = SLOT_L_STORE, "In your right pocket" = SLOT_R_STORE, "In your backpack" = SLOT_IN_BACKPACK, "On your belt" = SLOT_BELT) + if(ishuman(L)) + var/mob/living/carbon/human/H = L + slot = H.equip_in_one_of_slots(S, slots) + if(slot == "In your backpack") + slot = "In your [H.back.name]" + if(slot == "At your feet") + if(!S.forceMove(get_turf(L))) + qdel(S) + if(S && !QDELETED(S)) + to_chat(L, "There is a paper in your backpack! It'll tell you if anything's changed, as well as what to expect.") + to_chat(L, "[slot] is a clockwork slab, a multipurpose tool used to construct machines and invoke ancient words of power. If this is your first time \ + as a servant, you can find a concise tutorial in the Recollection category of its interface.") + to_chat(L, "If you want more information, you can read the wiki page to learn more.") + return TRUE + return FALSE + +/datum/dynamic_ruleset/roundstart/clockcult/round_result() + if(GLOB.clockwork_gateway_activated) + SSticker.news_report = CLOCK_SUMMON + SSticker.mode_result = "win - servants completed their objective (summon ratvar)" + else + SSticker.news_report = CULT_FAILURE + SSticker.mode_result = "loss - servants failed their objective (summon ratvar)" + +////////////////////////////////////////////// +// // +// CLOWN OPS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/nuclear/clown_ops + name = "Clown Ops" + antag_datum = /datum/antagonist/nukeop/clownop + antag_leader_datum = /datum/antagonist/nukeop/leader/clownop + requirements = list(101,101,101,101,101,101,101,101,101,101) + high_population_requirement = 101 + +/datum/dynamic_ruleset/roundstart/nuclear/clown_ops/pre_execute() + . = ..() + if(.) + for(var/obj/machinery/nuclearbomb/syndicate/S in GLOB.nuke_list) + var/turf/T = get_turf(S) + if(T) + qdel(S) + new /obj/machinery/nuclearbomb/syndicate/bananium(T) + for(var/datum/mind/V in assigned) + V.assigned_role = "Clown Operative" + V.special_role = "Clown Operative" + +////////////////////////////////////////////// +// // +// DEVIL // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/devil + name = "Devil" + antag_flag = ROLE_DEVIL + antag_datum = /datum/antagonist/devil + restricted_roles = list("Lawyer", "Curator", "Chaplain", "Head of Security", "Captain", "AI") + required_candidates = 1 + weight = 3 + cost = 0 + requirements = list(101,101,101,101,101,101,101,101,101,101) + high_population_requirement = 101 + var/devil_limit = 4 // Hard limit on devils if scaling is turned off + +/datum/dynamic_ruleset/roundstart/devil/pre_execute() + var/tsc = CONFIG_GET(number/traitor_scaling_coeff) + var/num_devils = 1 + + if(tsc) + num_devils = max(required_candidates, min(round(num_players() / (tsc * 3)) + 2, round(num_players() / (tsc * 1.5)))) + else + num_devils = max(required_candidates, min(num_players(), devil_limit)) + + for(var/j = 0, j < num_devils, j++) + if (!candidates.len) + break + var/mob/devil = pick(candidates) + assigned += devil + candidates -= devil + devil.mind.special_role = ROLE_DEVIL + devil.mind.restricted_roles = restricted_roles + + log_game("[key_name(devil)] has been selected as a devil") + return TRUE + +/datum/dynamic_ruleset/roundstart/devil/execute() + for(var/datum/mind/devil in assigned) + add_devil(devil.current, ascendable = TRUE) + add_devil_objectives(devil,2) + return TRUE + +/datum/dynamic_ruleset/roundstart/devil/proc/add_devil_objectives(datum/mind/devil_mind, quantity) + var/list/validtypes = list(/datum/objective/devil/soulquantity, /datum/objective/devil/soulquality, /datum/objective/devil/sintouch, /datum/objective/devil/buy_target) + var/datum/antagonist/devil/D = devil_mind.has_antag_datum(/datum/antagonist/devil) + for(var/i = 1 to quantity) + var/type = pick(validtypes) + var/datum/objective/devil/objective = new type(null) + objective.owner = devil_mind + D.objectives += objective + if(!istype(objective, /datum/objective/devil/buy_target)) + validtypes -= type + else + objective.find_target() + +////////////////////////////////////////////// +// // +// MONKEY // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/monkey + name = "Monkey" + antag_flag = ROLE_MONKEY + antag_datum = /datum/antagonist/monkey/leader + restricted_roles = list("Cyborg", "AI") + required_candidates = 1 + weight = 3 + cost = 0 + requirements = list(101,101,101,101,101,101,101,101,101,101) + high_population_requirement = 101 + var/players_per_carrier = 30 + var/monkeys_to_win = 1 + var/escaped_monkeys = 0 + var/datum/team/monkey/monkey_team + +/datum/dynamic_ruleset/roundstart/monkey/pre_execute() + var/carriers_to_make = max(round(num_players()/players_per_carrier, 1), 1) + + for(var/j = 0, j < carriers_to_make, j++) + if (!candidates.len) + break + var/mob/carrier = pick(candidates) + candidates -= carrier + assigned += carrier.mind + carrier.mind.special_role = "Monkey Leader" + carrier.mind.restricted_roles = restricted_roles + log_game("[key_name(carrier)] has been selected as a Jungle Fever carrier") + return TRUE + +/datum/dynamic_ruleset/roundstart/monkey/execute() + for(var/datum/mind/carrier in assigned) + var/datum/antagonist/monkey/M = add_monkey_leader(carrier) + if(M) + monkey_team = M.monkey_team + return TRUE + +/datum/dynamic_ruleset/roundstart/monkey/proc/check_monkey_victory() + if(SSshuttle.emergency.mode != SHUTTLE_ENDGAME) + return FALSE + var/datum/disease/D = new /datum/disease/transformation/jungle_fever() + for(var/mob/living/carbon/monkey/M in GLOB.alive_mob_list) + if (M.HasDisease(D)) + if(M.onCentCom() || M.onSyndieBase()) + escaped_monkeys++ + if(escaped_monkeys >= monkeys_to_win) + return TRUE + else + return FALSE + +// This does not get called. Look into making it work. +/datum/dynamic_ruleset/roundstart/monkey/round_result() + if(check_monkey_victory()) + SSticker.mode_result = "win - monkey win" + else + SSticker.mode_result = "loss - staff stopped the monkeys" + +////////////////////////////////////////////// +// // +// METEOR // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/meteor + name = "Meteor" + persistent = TRUE + required_candidates = 0 + weight = 3 + cost = 0 + requirements = list(101,101,101,101,101,101,101,101,101,101) + high_population_requirement = 101 + var/meteordelay = 2000 + var/nometeors = 0 + var/rampupdelta = 5 + +/datum/dynamic_ruleset/roundstart/meteor/rule_process() + if(nometeors || meteordelay > world.time - SSticker.round_start_time) + return + + var/list/wavetype = GLOB.meteors_normal + var/meteorminutes = (world.time - SSticker.round_start_time - meteordelay) / 10 / 60 + + if (prob(meteorminutes)) + wavetype = GLOB.meteors_threatening + + if (prob(meteorminutes/2)) + wavetype = GLOB.meteors_catastrophic + + var/ramp_up_final = CLAMP(round(meteorminutes/rampupdelta), 1, 10) + + spawn_meteors(ramp_up_final, wavetype) diff --git a/code/game/gamemodes/dynamic/readme.md b/code/game/gamemodes/dynamic/readme.md new file mode 100644 index 0000000000..6bd064cf7c --- /dev/null +++ b/code/game/gamemodes/dynamic/readme.md @@ -0,0 +1,57 @@ +# DYNAMIC + +## ROUNDSTART + +Dynamic rolls threat based on a special sauce formula: +"dynamic_curve_width \* tan((3.1416 \* (rand() - 0.5) \* 57.2957795)) + dynamic_curve_centre" + +Latejoin and midround injection cooldowns are set using exponential distribution between +5 minutes and 25 for latejoin +15 minutes and 35 for midround +this value is then added to world.time and assigned to the injection cooldown variables. + +rigged_roundstart() is called instead if there are forced rules (an admin set the mode) + +can_start() -> pre_setup() -> roundstart() OR rigged_roundstart() -> picking_roundstart_rule(drafted_rules) -> post_setup() + +## PROCESS + +Calls rule_process on every rule which is in the current_rules list. +Every sixty seconds, update_playercounts() +Midround injection time is checked against world.time to see if an injection should happen. +If midround injection time is lower than world.time, it updates playercounts again, then tries to inject and generates a new cooldown regardless of whether a rule is picked. + +## LATEJOIN + +make_antag_chance(newPlayer) -> [For each latespawn rule...] +-> acceptable(living players, threat_level) -> trim_candidates() -> ready(forced=FALSE) +**If true, add to drafted rules +**NOTE that acceptable uses threat_level not threat! +**NOTE Latejoin timer is ONLY reset if at least one rule was drafted. +**NOTE the new_player.dm AttemptLateSpawn() calls OnPostSetup for all roles (unless assigned role is MODE) +[After collecting all draftble rules...] +-> picking_latejoin_ruleset(drafted_rules) -> spend threat -> ruleset.execute() +## MIDROUND +process() -> [For each midround rule...] +-> acceptable(living players, threat_level) -> trim_candidates() -> ready(forced=FALSE) +[After collecting all draftble rules...] +-> picking_midround_ruleset(drafted_rules) -> spend threat -> ruleset.execute() +## FORCED +For latejoin, it simply sets forced_latejoin_rule +make_antag_chance(newPlayer) -> trim_candidates() -> ready(forced=TRUE) **NOTE no acceptable() call +For midround, calls the below proc with forced = TRUE +picking_specific_rule(ruletype,forced) -> forced OR acceptable(living_players, threat_level) -> trim_candidates() -> ready(forced) -> spend threat -> execute() +**NOTE specific rule can be called by RS traitor->MR autotraitor w/ forced=FALSE +**NOTE that due to short circuiting acceptable() need not be called if forced. +## RULESET +acceptable(population,threat) just checks if enough threat_level for population indice. +**NOTE that we currently only send threat_level as the second arg, not threat. +ready(forced) checks if enough candidates and calls the map's map_ruleset(dynamic_ruleset) at the parent level +trim_candidates() varies significantly according to the ruleset type +Roundstart: All candidates are new_player mobs. Check them for standard stuff: connected, desire role, not banned, etc. +**NOTE Roundstart deals with both candidates (trimmed list of valid players) and mode.candidates (everyone readied up). Don't confuse them! +Latejoin: Only one candidate, the latejoiner. Standard checks. +Midround: Instead of building a single list candidates, candidates contains four lists: living, dead, observing, and living antags. Standard checks in trim_list(list). +Midround - Rulesets have additional types +/from_ghosts: execute() -> send_applications() -> review_applications() -> finish_setup(mob/newcharacter, index) -> setup_role(role) +**NOTE: execute() here adds dead players and observers to candidates list diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index d16cbebb2a..f790053863 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -24,6 +24,7 @@ var/list/datum/mind/antag_candidates = list() // List of possible starting antags goes here var/list/restricted_jobs = list() // Jobs it doesn't make sense to be. I.E chaplain or AI cultist var/list/protected_jobs = list() // Jobs that can't be traitors because + var/list/required_jobs = list() // alternative required job groups eg list(list(cap=1),list(hos=1,sec=2)) translates to one captain OR one hos and two secmans var/required_players = 0 var/maximum_players = -1 // -1 is no maximum, positive numbers limit the selection of a mode on overstaffed stations var/required_enemies = 0 @@ -355,7 +356,7 @@ // Ultimate randomizing code right here for(var/mob/dead/new_player/player in GLOB.player_list) - if(player.client && player.ready == PLAYER_READY_TO_PLAY) + if(player.client && player.ready == PLAYER_READY_TO_PLAY && player.check_preferences()) players += player // Shuffling, the players list is now ping-independent!!! @@ -558,3 +559,7 @@ SSticker.news_report = STATION_EVACUATED if(SSshuttle.emergency.is_hijacked()) SSticker.news_report = SHUTTLE_HIJACK + +/// Mode specific admin panel. +/datum/game_mode/proc/admin_panel() + return diff --git a/code/game/gamemodes/overthrow/overthrow.dm b/code/game/gamemodes/overthrow/overthrow.dm index 1548556515..dca0c1ade1 100644 --- a/code/game/gamemodes/overthrow/overthrow.dm +++ b/code/game/gamemodes/overthrow/overthrow.dm @@ -3,7 +3,8 @@ name = "overthrow" config_tag = "overthrow" antag_flag = ROLE_OVERTHROW - restricted_jobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer") + restricted_jobs = list("AI", "Cyborg") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") required_players = 20 // the core idea is of a swift, bloodless coup, so it shouldn't be as chaotic as revs. required_enemies = 2 // minimum two teams, otherwise it's just nerfed revs. recommended_enemies = 4 diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index 09047b05a9..8459819b5b 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -12,7 +12,8 @@ config_tag = "revolution" antag_flag = ROLE_REV false_report_weight = 10 - restricted_jobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer") + restricted_jobs = list("AI", "Cyborg") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") required_players = 30 required_enemies = 2 recommended_enemies = 3 diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index 789737ec0f..4a6e72cf67 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -96,4 +96,4 @@ /datum/game_mode/traitor/generate_report() return "Although more specific threats are commonplace, you should always remain vigilant for Syndicate agents aboard your station. Syndicate communications have implied that many \ - Nanotrasen employees are Syndicate agents with hidden memories that may be activated at a moment's notice, so it's possible that these agents might not even know their positions." \ No newline at end of file + Nanotrasen employees are Syndicate agents with hidden memories that may be activated at a moment's notice, so it's possible that these agents might not even know their positions." diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 76ea855c77..513fea4360 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -88,17 +88,50 @@ return close_machine(target) -/obj/machinery/sleeper/attackby(obj/item/I, mob/user, params) - if(!state_open && !occupant) - if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), I)) - return +/obj/machinery/sleeper/screwdriver_act(mob/living/user, obj/item/I) + . = TRUE + if(..()) + return + if(occupant) + to_chat(user, "[src] is currently occupied!") + return + if(state_open) + to_chat(user, "[src] must be closed to [panel_open ? "close" : "open"] its maintenance hatch!") + return + if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), I)) + return + return FALSE + +/obj/machinery/sleeper/wrench_act(mob/living/user, obj/item/I) + . = ..() if(default_change_direction_wrench(user, I)) - return + return TRUE + +/obj/machinery/sleeper/crowbar_act(mob/living/user, obj/item/I) + . = ..() if(default_pry_open(I)) - return + return TRUE if(default_deconstruction_crowbar(I)) + return TRUE + +/obj/machinery/sleeper/default_pry_open(obj/item/I) //wew + . = !(state_open || panel_open || (flags_1 & NODECONSTRUCT_1)) && I.tool_behaviour == TOOL_CROWBAR + if(.) + I.play_tool_sound(src, 50) + visible_message("[usr] pries open [src].", "You pry open [src].") + open_machine() + +/obj/machinery/sleeper/AltClick(mob/user) + if(!user.canUseTopic(src, !issilicon(user))) return - return ..() + if(state_open) + close_machine() + else + open_machine() + +/obj/machinery/sleeper/examine(mob/user) + ..() + to_chat(user, "Alt-click [src] to [state_open ? "close" : "open"] it.") /obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) @@ -146,7 +179,7 @@ data["occupant"]["toxLoss"] = mob_occupant.getToxLoss() data["occupant"]["fireLoss"] = mob_occupant.getFireLoss() data["occupant"]["cloneLoss"] = mob_occupant.getCloneLoss() - data["occupant"]["brainLoss"] = mob_occupant.getBrainLoss() + data["occupant"]["brainLoss"] = mob_occupant.getOrganLoss(ORGAN_SLOT_BRAIN) data["occupant"]["reagents"] = list() if(mob_occupant.reagents && mob_occupant.reagents.reagent_list.len) for(var/datum/reagent/R in mob_occupant.reagents.reagent_list) @@ -190,7 +223,7 @@ if(inject_chem(chem, usr)) . = TRUE if(scrambled_chems && prob(5)) - to_chat(usr, "Chem System Re-route detected, results may not be as expected!") + to_chat(usr, "Chemical system re-route detected, results may not be as expected!") /obj/machinery/sleeper/emag_act(mob/user) . = ..() diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index b0e4c699d7..ada77f3b3e 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -113,6 +113,11 @@ Class Procs: var/atom/movable/occupant = null var/speed_process = FALSE // Process as fast as possible? var/obj/item/circuitboard/circuit // Circuit to be created and inserted when the machinery is created + var/obj/item/card/id/inserted_scan_id + var/obj/item/card/id/inserted_modify_id + var/list/region_access = null // For the identification console (card.dm) + var/list/head_subordinates = null // For the identification console (card.dm) + var/authenticated = 0 // For the identification console (card.dm) var/interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_SET_MACHINE @@ -131,7 +136,7 @@ Class Procs: else START_PROCESSING(SSfastprocess, src) power_change() - AddComponent(/datum/component/redirect, list(COMSIG_ENTER_AREA = CALLBACK(src, .proc/power_change))) + RegisterSignal(src, COMSIG_ENTER_AREA, .proc/power_change) if (occupant_typecache) occupant_typecache = typecacheof(occupant_typecache) @@ -143,6 +148,10 @@ Class Procs: else STOP_PROCESSING(SSfastprocess, src) dropContents() + if(length(component_parts)) + for(var/atom/A in component_parts) + qdel(A) + component_parts.Cut() return ..() /obj/machinery/proc/locate_machinery() @@ -179,12 +188,15 @@ Class Procs: L.update_canmove() occupant = null +/obj/machinery/proc/can_be_occupant(atom/movable/am) + return occupant_typecache ? is_type_in_typecache(am, occupant_typecache) : isliving(am) + /obj/machinery/proc/close_machine(atom/movable/target = null) state_open = FALSE density = TRUE if(!target) for(var/am in loc) - if (!(occupant_typecache ? is_type_in_typecache(am, occupant_typecache) : isliving(am))) + if (!(can_be_occupant(am))) continue var/atom/movable/AM = am if(AM.has_buckled_mobs()) @@ -311,6 +323,7 @@ Class Procs: spawn_frame(disassembled) for(var/obj/item/I in component_parts) I.forceMove(loc) + component_parts.Cut() qdel(src) /obj/machinery/proc/spawn_frame(disassembled) @@ -348,8 +361,8 @@ Class Procs: panel_open = FALSE icon_state = icon_state_closed to_chat(user, "You close the maintenance hatch of [src].") - return 1 - return 0 + return TRUE + return FALSE /obj/machinery/proc/default_change_direction_wrench(mob/user, obj/item/I) if(panel_open && I.tool_behaviour == TOOL_WRENCH) @@ -401,7 +414,7 @@ Class Procs: var/obj/item/circuitboard/machine/CB = locate(/obj/item/circuitboard/machine) in component_parts var/P if(W.works_from_distance) - display_parts(user) + to_chat(user, display_parts(user)) for(var/obj/item/A in component_parts) for(var/D in CB.req_components) if(ispath(A.type, D)) @@ -429,34 +442,38 @@ Class Procs: break RefreshParts() else - display_parts(user) + to_chat(user, display_parts(user)) if(shouldplaysound) W.play_rped_sound() return TRUE return FALSE /obj/machinery/proc/display_parts(mob/user) - to_chat(user, "It contains the following parts:") + . = list() + . += "It contains the following parts:" for(var/obj/item/C in component_parts) - to_chat(user, "[icon2html(C, user)] \A [C].") + . += "[icon2html(C, user)] \A [C]." + . = jointext(., "") /obj/machinery/examine(mob/user) - ..() + . = ..() if(stat & BROKEN) - to_chat(user, "It looks broken and non-functional.") + . += "It looks broken and non-functional." if(!(resistance_flags & INDESTRUCTIBLE)) if(resistance_flags & ON_FIRE) - to_chat(user, "It's on fire!") + . += "It's on fire!" var/healthpercent = (obj_integrity/max_integrity) * 100 switch(healthpercent) if(50 to 99) - to_chat(user, "It looks slightly damaged.") + . += "It looks slightly damaged." if(25 to 50) - to_chat(user, "It appears heavily damaged.") + . += "It appears heavily damaged." if(0 to 25) - to_chat(user, "It's falling apart!") + . += "It's falling apart!" if(user.research_scanner && component_parts) - display_parts(user) + . += display_parts(user, TRUE) + if(inserted_scan_id || inserted_modify_id) + . += "Alt-click to eject the ID card." //called on machinery construction (i.e from frame to machinery) but not on initialization /obj/machinery/proc/on_construction() @@ -490,3 +507,73 @@ Class Procs: . = . % 9 AM.pixel_x = -8 + ((.%3)*8) AM.pixel_y = -8 + (round( . / 3)*8) + +/obj/machinery/proc/id_insert_scan(mob/user, obj/item/card/id/I) + I = user.get_active_held_item() + if(istype(I)) + if(inserted_scan_id) + to_chat(user, "There's already an ID card in the console!") + return + if(!user.transferItemToLoc(I, src)) + return + inserted_scan_id = I + user.visible_message("[user] inserts an ID card into the console.", \ + "You insert the ID card into the console.") + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + updateUsrDialog() + +/obj/machinery/proc/id_eject_scan(mob/user) + if(!inserted_scan_id) + to_chat(user, "There's no ID card in the console!") + return + if(inserted_scan_id) + inserted_scan_id.forceMove(drop_location()) + if(!issilicon(user) && Adjacent(user)) + user.put_in_hands(inserted_scan_id) + inserted_scan_id = null + user.visible_message("[user] gets an ID card from the console.", \ + "You get the ID card from the console.") + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + updateUsrDialog() + +/obj/machinery/proc/id_eject_modify(mob/user) + if(inserted_modify_id) + GLOB.data_core.manifest_modify(inserted_modify_id.registered_name, inserted_modify_id.assignment) + inserted_modify_id.update_label() + inserted_modify_id.forceMove(drop_location()) + if(!issilicon(user) && Adjacent(user)) + user.put_in_hands(inserted_modify_id) + user.visible_message("[user] gets an ID card from the console.", \ + "You get the ID card from the console.") + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + inserted_modify_id = null + region_access = null + head_subordinates = null + updateUsrDialog() + +/obj/machinery/proc/id_insert_modify(mob/user) + var/obj/item/card/id/I = user.get_active_held_item() + if(istype(I)) + if(inserted_modify_id) + to_chat(user, "There's already an ID card in the console!") + return + if(!user.transferItemToLoc(I, src)) + return + inserted_modify_id = I + user.visible_message("[user] inserts an ID card into the console.", \ + "You insert the ID card into the console.") + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + updateUsrDialog() + +/obj/machinery/AltClick(mob/user) + . = ..() + if(!user.canUseTopic(src, !issilicon(user)) || !is_operational()) + return + if(inserted_modify_id) + id_eject_modify(user) + authenticated = FALSE + return + if(inserted_scan_id) + id_eject_scan(user) + authenticated = FALSE + return diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index a175558f74..8e49c0a126 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -241,13 +241,14 @@ var/obj/item/I = pick_n_take(unattached_flesh) if(isorgan(I)) var/obj/item/organ/O = I + O.organ_flags &= ~ORGAN_FROZEN O.Insert(mob_occupant) else if(isbodypart(I)) var/obj/item/bodypart/BP = I BP.attach_limb(mob_occupant) //Premature clones may have brain damage. - mob_occupant.adjustBrainLoss(-((speed_coeff / 2) * dmg_mult)) + mob_occupant.adjustOrganLoss(ORGAN_SLOT_BRAIN, -((speed_coeff / 2) * dmg_mult)) use_power(7500) //This might need tweaking. @@ -261,6 +262,7 @@ for(var/i in unattached_flesh) if(isorgan(i)) var/obj/item/organ/O = i + O.organ_flags &= ~ORGAN_FROZEN O.Insert(mob_occupant) else if(isbodypart(i)) var/obj/item/bodypart/BP = i @@ -346,6 +348,9 @@ if(mess) //Clean that mess and dump those gibs! for(var/obj/fl in unattached_flesh) fl.forceMove(T) + if(istype(fl, /obj/item/organ)) + var/obj/item/organ/O = fl + O.organ_flags &= ~ORGAN_FROZEN unattached_flesh.Cut() mess = FALSE new /obj/effect/gibspawner/generic(get_turf(src)) @@ -442,7 +447,7 @@ unattached_flesh.Cut() H.setCloneLoss(CLONE_INITIAL_DAMAGE) //Yeah, clones start with very low health, not with random, because why would they start with random health - H.setBrainLoss(CLONE_INITIAL_DAMAGE) + //H.setOrganLoss(ORGAN_SLOT_BRAIN, CLONE_INITIAL_DAMAGE) // In addition to being cellularly damaged and having barely any // brain function, they also have no limbs or internal organs. @@ -458,8 +463,9 @@ for(var/o in H.internal_organs) var/obj/item/organ/organ = o - if(!istype(organ) || organ.vital) + if(!istype(organ) || (organ.organ_flags & ORGAN_VITAL)) continue + organ.organ_flags |= ORGAN_FROZEN organ.Remove(H, special=TRUE) organ.forceMove(src) unattached_flesh += organ diff --git a/code/game/machinery/computer/apc_control.dm b/code/game/machinery/computer/apc_control.dm index 1c517d6e8f..6d505c8f95 100644 --- a/code/game/machinery/computer/apc_control.dm +++ b/code/game/machinery/computer/apc_control.dm @@ -11,7 +11,6 @@ var/list/result_filters //For sorting the results var/checking_logs = 0 var/list/logs - var/authenticated = 0 var/auth_id = "\[NULL\]" /obj/machinery/computer/apc_control/Initialize() diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 09bf401e12..886851a6a8 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -13,11 +13,8 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) circuit = /obj/item/circuitboard/computer/card var/obj/item/card/id/scan = null var/obj/item/card/id/modify = null - var/authenticated = 0 var/mode = 0 var/printing = null - var/list/region_access = null - var/list/head_subordinates = null var/target_dept = 0 //Which department this computer has access to. 0=all departments //Cooldown for closing positions in seconds @@ -377,7 +374,6 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) else if((ACCESS_HOP in scan.access) && ((target_dept==1) || !target_dept)) region_access |= 1 - region_access |= 6 get_subordinates("Head of Personnel") if((ACCESS_HOS in scan.access) && ((target_dept==2) || !target_dept)) region_access |= 2 @@ -391,6 +387,9 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) if((ACCESS_CE in scan.access) && ((target_dept==5) || !target_dept)) region_access |= 5 get_subordinates("Chief Engineer") + if((ACCESS_QM in scan.access) && ((target_dept==6) || !target_dept)) + region_access |= 6 + get_subordinates("Quartermaster") if(region_access) authenticated = 1 else if ((!( authenticated ) && issilicon(usr)) && (!modify)) @@ -610,7 +609,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) typed_circuit.target_dept = target_dept else target_dept = typed_circuit.target_dept - var/list/dept_list = list("general","security","medical","science","engineering") + var/list/dept_list = list("civilian","security","medical","science","engineering","cargo") name = "[dept_list[target_dept]] department console" /obj/machinery/computer/card/minor/hos @@ -634,3 +633,9 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) icon_screen = "idce" light_color = LIGHT_COLOR_YELLOW + +/obj/machinery/computer/card/minor/qm + target_dept = 6 + icon_screen = "idqm" + + light_color = LIGHT_COLOR_ORANGE \ No newline at end of file diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 1654768de0..26f0d06ecb 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -64,9 +64,6 @@ if(!(scanner && LAZYLEN(pods) && autoprocess)) return - if(scanner.occupant && scanner.scan_level > 2) - scan_occupant(scanner.occupant) - for(var/datum/data/record/R in records) var/obj/machinery/clonepod/pod = GetAvailableEfficientPod(R.fields["mind"]) diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index eceaefd2b2..557f2f0636 100755 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -6,7 +6,6 @@ icon_keyboard = "tech_key" req_access = list(ACCESS_HEADS) circuit = /obj/item/circuitboard/computer/communications - var/authenticated = 0 var/auth_id = "Unknown" //Who is currently logged in? var/list/datum/comm_message/messages = list() var/datum/comm_message/currmsg diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 8af83ae08e..d4fe3e27a2 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -7,13 +7,10 @@ icon_keyboard = "med_key" req_one_access = list(ACCESS_MEDICAL, ACCESS_FORENSICS_LOCKERS) circuit = /obj/item/circuitboard/computer/med_data - var/obj/item/card/id/scan = null - var/authenticated = null var/rank = null var/screen = null var/datum/data/record/active1 var/datum/data/record/active2 - var/a_id = null var/temp = null var/printing = null //Sorting Variables @@ -25,24 +22,22 @@ /obj/machinery/computer/med_data/syndie icon_keyboard = "syndie_key" -/obj/machinery/computer/med_data/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/card/id) && !scan) - if(!user.transferItemToLoc(O, src)) - return - scan = O - to_chat(user, "You insert [O].") +/obj/machinery/computer/med_data/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/card/id)) + id_insert_scan(user) else return ..() /obj/machinery/computer/med_data/ui_interact(mob/user) . = ..() + if(isliving(user)) + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) var/dat if(temp) dat = text("[temp]

Clear Screen") else - dat = text("Confirm Identity: []
", (src.scan ? text("[]", src.scan.name) : "----------")) - if(src.authenticated) - switch(src.screen) + if(authenticated) + switch(screen) if(1) dat += {" Search Records @@ -116,7 +111,7 @@ dat += "" dat += "" dat += "ID:[active1.fields["id"]]" - dat += "Sex: [active1.fields["sex"]] " + dat += "Gender: [active1.fields["gender"]] " dat += "Age: [active1.fields["age"]] " dat += "Species: [active1.fields["species"]] " dat += "Fingerprint: [active1.fields["fingerprint"]] " @@ -141,7 +136,7 @@ dat += "
Comments/Log" var/counter = 1 - while(src.active2.fields[text("com_[]", counter)]) + while(active2.fields[text("com_[]", counter)]) dat += "[active2.fields[text("com_[]", counter)]]Delete Entry" counter++ dat += "Add Entry" @@ -169,7 +164,7 @@ dat += "
Medical Robots:" var/bdat = null for(var/mob/living/simple_animal/bot/medbot/M in GLOB.alive_mob_list) - if(M.z != src.z) + if(M.z != z) continue //only find medibots on the same z-level as the computer var/turf/bl = get_turf(M) if(bl) //if it can't find a turf for the medibot, then it probably shouldn't be showing up @@ -189,7 +184,7 @@ 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(src.icon, src.icon_state)) + popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) popup.open() /obj/machinery/computer/med_data/Topic(href, href_list) @@ -197,29 +192,20 @@ if(.) return . if(!(active1 in GLOB.data_core.general)) - src.active1 = null + active1 = null if(!(active2 in GLOB.data_core.medical)) - src.active2 = null + active2 = null if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr) || IsAdminGhost(usr)) usr.set_machine(src) if(href_list["temp"]) - src.temp = null - if(href_list["scan"]) - if(src.scan) - usr.put_in_hands(scan) - scan = null - else - var/obj/item/I = usr.is_holding_item_of_type(/obj/item/card/id) - if(I) - if(!usr.transferItemToLoc(I, src)) - return - src.scan = I + temp = null else if(href_list["logout"]) - src.authenticated = null - src.screen = null - src.active1 = null - src.active2 = null + authenticated = null + screen = null + active1 = null + active2 = null + playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE) else if(href_list["choice"]) // SORTING! if(href_list["choice"] == "Sorting") @@ -234,34 +220,37 @@ sortBy = href_list["sort"] order = initial(order) else if(href_list["login"]) - if(issilicon(usr)) - src.active1 = null - src.active2 = null - src.authenticated = 1 - src.rank = "AI" - src.screen = 1 - else if(IsAdminGhost(usr)) - src.active1 = null - src.active2 = null - src.authenticated = 1 - src.rank = "Central Command" - src.screen = 1 - else if(istype(src.scan, /obj/item/card/id)) - src.active1 = null - src.active2 = null - if(src.check_access(src.scan)) - src.authenticated = src.scan.registered_name - src.rank = src.scan.assignment - src.screen = 1 - if(src.authenticated) - + var/mob/M = usr + var/obj/item/card/id/I = M.get_idcard(TRUE) + if(issilicon(M)) + active1 = null + active2 = null + authenticated = 1 + rank = "AI" + screen = 1 + else if(IsAdminGhost(M)) + active1 = null + active2 = null + authenticated = 1 + rank = "Central Command" + screen = 1 + else if(istype(I) && check_access(I)) + active1 = null + active2 = null + authenticated = I.registered_name + rank = I.assignment + screen = 1 + else + to_chat(usr, "Unauthorized access.") + playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE) + if(authenticated) if(href_list["screen"]) - src.screen = text2num(href_list["screen"]) - if(src.screen < 1) - src.screen = 1 + screen = text2num(href_list["screen"]) + if(screen < 1) + screen = 1 - src.active1 = null - src.active2 = null + active1 = null + active2 = null else if(href_list["vir"]) var/type = href_list["vir"] @@ -269,7 +258,7 @@ var/AfS = "" for(var/mob/M in Dis.viable_mobtypes) AfS += " [initial(M.name)];" - src.temp = {"Name: [Dis.name] + temp = {"Name: [Dis.name]
Number of stages: [Dis.max_stages]
Spread: [Dis.spread_text] Transmission
Possible Cure: [(Dis.cure_text||"none")] @@ -280,110 +269,112 @@
Severity: [Dis.severity]"} else if(href_list["del_all"]) - src.temp = "Are you sure you wish to delete all records?
\n\tYes
\n\tNo
" + temp = "Are you sure you wish to delete all records?
\n\tYes
\n\tNo
" else if(href_list["del_all2"]) investigate_log("[key_name(usr)] has deleted all medical records.", INVESTIGATE_RECORDS) GLOB.data_core.medical.Cut() - src.temp = "All records deleted." + temp = "All records deleted." else if(href_list["field"]) - var/a1 = src.active1 - var/a2 = src.active2 + var/a1 = active1 + var/a2 = active2 switch(href_list["field"]) if("fingerprint") if(active1) - var/t1 = stripped_input("Please input fingerprint hash:", "Med. records", src.active1.fields["fingerprint"], null) + var/t1 = stripped_input("Please input fingerprint hash:", "Med. records", active1.fields["fingerprint"], null) if(!canUseMedicalRecordsConsole(usr, t1, a1)) return - src.active1.fields["fingerprint"] = t1 - if("sex") + active1.fields["fingerprint"] = t1 + if("gender") if(active1) - if(src.active1.fields["sex"] == "Male") - src.active1.fields["sex"] = "Female" + if(active1.fields["gender"] == "Male") + active1.fields["gender"] = "Female" + else if(active1.fields["gender"] == "Female") + active1.fields["gender"] = "Other" else - src.active1.fields["sex"] = "Male" + active1.fields["gender"] = "Male" if("age") if(active1) - var/t1 = input("Please input age:", "Med. records", src.active1.fields["age"], null) as num + var/t1 = input("Please input age:", "Med. records", active1.fields["age"], null) as num if(!canUseMedicalRecordsConsole(usr, t1, a1)) return - src.active1.fields["age"] = t1 + active1.fields["age"] = t1 if("species") if(active1) - var/t1 = stripped_input("Please input species name", "Med. records", src.active1.fields["species"], null) + var/t1 = stripped_input("Please input species name", "Med. records", active1.fields["species"], null) if(!canUseMedicalRecordsConsole(usr, t1, a1)) return active1.fields["species"] = t1 if("mi_dis") if(active2) - var/t1 = stripped_input("Please input minor disabilities list:", "Med. records", src.active2.fields["mi_dis"], null) + var/t1 = stripped_input("Please input minor disabilities list:", "Med. records", active2.fields["mi_dis"], null) if(!canUseMedicalRecordsConsole(usr, t1, null, a2)) return - src.active2.fields["mi_dis"] = t1 + active2.fields["mi_dis"] = t1 if("mi_dis_d") if(active2) - var/t1 = stripped_input("Please summarize minor dis.:", "Med. records", src.active2.fields["mi_dis_d"], null) + var/t1 = stripped_input("Please summarize minor dis.:", "Med. records", active2.fields["mi_dis_d"], null) if(!canUseMedicalRecordsConsole(usr, t1, null, a2)) return - src.active2.fields["mi_dis_d"] = t1 + active2.fields["mi_dis_d"] = t1 if("ma_dis") if(active2) - var/t1 = stripped_input("Please input major disabilities list:", "Med. records", src.active2.fields["ma_dis"], null) + var/t1 = stripped_input("Please input major disabilities list:", "Med. records", active2.fields["ma_dis"], null) if(!canUseMedicalRecordsConsole(usr, t1, null, a2)) return - src.active2.fields["ma_dis"] = t1 + active2.fields["ma_dis"] = t1 if("ma_dis_d") if(active2) - var/t1 = stripped_input("Please summarize major dis.:", "Med. records", src.active2.fields["ma_dis_d"], null) + var/t1 = stripped_input("Please summarize major dis.:", "Med. records", active2.fields["ma_dis_d"], null) if(!canUseMedicalRecordsConsole(usr, t1, null, a2)) return - src.active2.fields["ma_dis_d"] = t1 + active2.fields["ma_dis_d"] = t1 if("alg") if(active2) - var/t1 = stripped_input("Please state allergies:", "Med. records", src.active2.fields["alg"], null) + var/t1 = stripped_input("Please state allergies:", "Med. records", active2.fields["alg"], null) if(!canUseMedicalRecordsConsole(usr, t1, null, a2)) return - src.active2.fields["alg"] = t1 + active2.fields["alg"] = t1 if("alg_d") if(active2) - var/t1 = stripped_input("Please summarize allergies:", "Med. records", src.active2.fields["alg_d"], null) + var/t1 = stripped_input("Please summarize allergies:", "Med. records", active2.fields["alg_d"], null) if(!canUseMedicalRecordsConsole(usr, t1, null, a2)) return - src.active2.fields["alg_d"] = t1 + active2.fields["alg_d"] = t1 if("cdi") if(active2) - var/t1 = stripped_input("Please state diseases:", "Med. records", src.active2.fields["cdi"], null) + var/t1 = stripped_input("Please state diseases:", "Med. records", active2.fields["cdi"], null) if(!canUseMedicalRecordsConsole(usr, t1, null, a2)) return - src.active2.fields["cdi"] = t1 + active2.fields["cdi"] = t1 if("cdi_d") if(active2) - var/t1 = stripped_input("Please summarize diseases:", "Med. records", src.active2.fields["cdi_d"], null) + var/t1 = stripped_input("Please summarize diseases:", "Med. records", active2.fields["cdi_d"], null) if(!canUseMedicalRecordsConsole(usr, t1, null, a2)) return - src.active2.fields["cdi_d"] = t1 + active2.fields["cdi_d"] = t1 if("notes") if(active2) - var/t1 = stripped_input("Please summarize notes:", "Med. records", src.active2.fields["notes"], null) + var/t1 = stripped_input("Please summarize notes:", "Med. records", active2.fields["notes"], null) if(!canUseMedicalRecordsConsole(usr, t1, null, a2)) return - src.active2.fields["notes"] = t1 + active2.fields["notes"] = t1 if("p_stat") if(active1) - src.temp = "Physical Condition:
\n\t*Deceased*
\n\t*Unconscious*
\n\tActive
\n\tPhysically Unfit
" + temp = "Physical Condition:
\n\t*Deceased*
\n\t*Unconscious*
\n\tActive
\n\tPhysically Unfit
" if("m_stat") if(active1) - src.temp = "Mental Condition:
\n\t*Insane*
\n\t*Unstable*
\n\t*Watch*
\n\tStable
" + temp = "Mental Condition:
\n\t*Insane*
\n\t*Unstable*
\n\t*Watch*
\n\tStable
" if("blood_type") if(active2) - src.temp = "Blood Type:
\n\tA- A+
\n\tB- B+
\n\tAB- AB+
\n\tO- O+
" + temp = "Blood Type:
\n\tA- A+
\n\tB- B+
\n\tAB- AB+
\n\tO- O+
" if("b_dna") if(active2) - var/t1 = stripped_input("Please input DNA hash:", "Med. records", src.active2.fields["b_dna"], null) + var/t1 = stripped_input("Please input DNA hash:", "Med. records", active2.fields["b_dna"], null) if(!canUseMedicalRecordsConsole(usr, t1, null, a2)) return - src.active2.fields["b_dna"] = t1 + active2.fields["b_dna"] = t1 if("show_photo_front") if(active1) if(active1.fields["photo_front"]) @@ -402,51 +393,51 @@ if(active1) switch(href_list["p_stat"]) if("deceased") - src.active1.fields["p_stat"] = "*Deceased*" + active1.fields["p_stat"] = "*Deceased*" if("unconscious") - src.active1.fields["p_stat"] = "*Unconscious*" + active1.fields["p_stat"] = "*Unconscious*" if("active") - src.active1.fields["p_stat"] = "Active" + active1.fields["p_stat"] = "Active" if("unfit") - src.active1.fields["p_stat"] = "Physically Unfit" + active1.fields["p_stat"] = "Physically Unfit" else if(href_list["m_stat"]) if(active1) switch(href_list["m_stat"]) if("insane") - src.active1.fields["m_stat"] = "*Insane*" + active1.fields["m_stat"] = "*Insane*" if("unstable") - src.active1.fields["m_stat"] = "*Unstable*" + active1.fields["m_stat"] = "*Unstable*" if("watch") - src.active1.fields["m_stat"] = "*Watch*" + active1.fields["m_stat"] = "*Watch*" if("stable") - src.active1.fields["m_stat"] = "Stable" + active1.fields["m_stat"] = "Stable" else if(href_list["blood_type"]) if(active2) switch(href_list["blood_type"]) if("an") - src.active2.fields["blood_type"] = "A-" + active2.fields["blood_type"] = "A-" if("bn") - src.active2.fields["blood_type"] = "B-" + active2.fields["blood_type"] = "B-" if("abn") - src.active2.fields["blood_type"] = "AB-" + active2.fields["blood_type"] = "AB-" if("on") - src.active2.fields["blood_type"] = "O-" + active2.fields["blood_type"] = "O-" if("ap") - src.active2.fields["blood_type"] = "A+" + active2.fields["blood_type"] = "A+" if("bp") - src.active2.fields["blood_type"] = "B+" + active2.fields["blood_type"] = "B+" if("abp") - src.active2.fields["blood_type"] = "AB+" + active2.fields["blood_type"] = "AB+" if("op") - src.active2.fields["blood_type"] = "O+" + active2.fields["blood_type"] = "O+" else if(href_list["del_r"]) if(active2) - src.temp = "Are you sure you wish to delete the record (Medical Portion Only)?
\n\tYes
\n\tNo
" + temp = "Are you sure you wish to delete the record (Medical Portion Only)?
\n\tYes
\n\tNo
" else if(href_list["del_r2"]) investigate_log("[key_name(usr)] has deleted the medical records for [active1.fields["name"]].", INVESTIGATE_RECORDS) @@ -463,10 +454,10 @@ screen = 4 else if(href_list["new"]) - if((istype(src.active1, /datum/data/record) && !( istype(src.active2, /datum/data/record) ))) + if((istype(active1, /datum/data/record) && !( istype(active2, /datum/data/record) ))) var/datum/data/record/R = new /datum/data/record( ) - R.fields["name"] = src.active1.fields["name"] - R.fields["id"] = src.active1.fields["id"] + R.fields["name"] = active1.fields["name"] + R.fields["id"] = active1.fields["id"] R.name = text("Medical Record #[]", R.fields["id"]) R.fields["blood_type"] = "Unknown" R.fields["b_dna"] = "Unknown" @@ -480,76 +471,77 @@ R.fields["cdi_d"] = "No diseases have been diagnosed at the moment." R.fields["notes"] = "No notes." GLOB.data_core.medical += R - src.active2 = R - src.screen = 4 + active2 = R + screen = 4 else if(href_list["add_c"]) if(!(active2 in GLOB.data_core.medical)) return - var/a2 = src.active2 + var/a2 = active2 var/t1 = stripped_multiline_input("Add Comment:", "Med. records", null, null) if(!canUseMedicalRecordsConsole(usr, t1, null, a2)) return var/counter = 1 - while(src.active2.fields[text("com_[]", counter)]) + while(active2.fields[text("com_[]", counter)]) counter++ - src.active2.fields[text("com_[]", counter)] = text("Made by [] ([]) on [] [], []
[]", src.authenticated, src.rank, STATION_TIME_TIMESTAMP("hh:mm:ss"), time2text(world.realtime, "MMM DD"), GLOB.year_integer+540, t1) + active2.fields[text("com_[]", counter)] = text("Made by [] ([]) on [] [], []
[]", authenticated, rank, STATION_TIME_TIMESTAMP("hh:mm:ss"), time2text(world.realtime, "MMM DD"), GLOB.year_integer+540, t1) else if(href_list["del_c"]) - if((istype(src.active2, /datum/data/record) && src.active2.fields[text("com_[]", href_list["del_c"])])) - src.active2.fields[text("com_[]", href_list["del_c"])] = "Deleted" + if((istype(active2, /datum/data/record) && active2.fields[text("com_[]", href_list["del_c"])])) + active2.fields[text("com_[]", href_list["del_c"])] = "Deleted" else if(href_list["search"]) var/t1 = stripped_input(usr, "Search String: (Name, DNA, or ID)", "Med. records") if(!canUseMedicalRecordsConsole(usr, t1)) return - src.active1 = null - src.active2 = null + active1 = null + active2 = null t1 = lowertext(t1) for(var/datum/data/record/R in GLOB.data_core.medical) if((lowertext(R.fields["name"]) == t1 || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"]))) - src.active2 = R + active2 = R else //Foreach continue //goto(3229) - if(!( src.active2 )) - src.temp = text("Could not locate record [].", sanitize(t1)) + if(!( active2 )) + temp = text("Could not locate record [].", sanitize(t1)) else for(var/datum/data/record/E in GLOB.data_core.general) - if((E.fields["name"] == src.active2.fields["name"] || E.fields["id"] == src.active2.fields["id"])) - src.active1 = E + if((E.fields["name"] == active2.fields["name"] || E.fields["id"] == active2.fields["id"])) + active1 = E else //Foreach continue //goto(3334) - src.screen = 4 + screen = 4 else if(href_list["print_p"]) - if(!( src.printing )) - src.printing = 1 + if(!( printing )) + printing = 1 GLOB.data_core.medicalPrintCount++ playsound(loc, 'sound/items/poster_being_created.ogg', 100, 1) sleep(30) - var/obj/item/paper/P = new /obj/item/paper( src.loc ) + var/obj/item/paper/P = new /obj/item/paper( loc ) P.info = "
Medical Record - (MR-[GLOB.data_core.medicalPrintCount])

" if(active1 in GLOB.data_core.general) - P.info += text("Name: [] ID: []
\nSex: []
\nAge: []
", src.active1.fields["name"], src.active1.fields["id"], src.active1.fields["sex"], src.active1.fields["age"]) + P.info += text("Name: [] ID: []
\nGender: []
\nAge: []
", active1.fields["name"], active1.fields["id"], active1.fields["gender"], active1.fields["age"]) P.info += "\nSpecies: [active1.fields["species"]]
" - P.info += text("\nFingerprint: []
\nPhysical Status: []
\nMental Status: []
", src.active1.fields["fingerprint"], src.active1.fields["p_stat"], src.active1.fields["m_stat"]) + P.info += text("\nFingerprint: []
\nPhysical Status: []
\nMental Status: []
", active1.fields["fingerprint"], active1.fields["p_stat"], active1.fields["m_stat"]) else P.info += "General Record Lost!
" if(active2 in GLOB.data_core.medical) - P.info += text("
\n
Medical Data

\nBlood Type: []
\nDNA: []
\n
\nMinor Disabilities: []
\nDetails: []
\n
\nMajor Disabilities: []
\nDetails: []
\n
\nAllergies: []
\nDetails: []
\n
\nCurrent Diseases: [] (per disease info placed in log/comment section)
\nDetails: []
\n
\nImportant Notes:
\n\t[]
\n
\n
Comments/Log

", src.active2.fields["blood_type"], src.active2.fields["b_dna"], src.active2.fields["mi_dis"], src.active2.fields["mi_dis_d"], src.active2.fields["ma_dis"], src.active2.fields["ma_dis_d"], src.active2.fields["alg"], src.active2.fields["alg_d"], src.active2.fields["cdi"], src.active2.fields["cdi_d"], src.active2.fields["notes"]) + P.info += text("
\n
Medical Data

\nBlood Type: []
\nDNA: []
\n
\nMinor Disabilities: []
\nDetails: []
\n
\nMajor Disabilities: []
\nDetails: []
\n
\nAllergies: []
\nDetails: []
\n
\nCurrent Diseases: [] (per disease info placed in log/comment section)
\nDetails: []
\n
\nImportant Notes:
\n\t[]
\n
\n
Comments/Log

", active2.fields["blood_type"], active2.fields["b_dna"], active2.fields["mi_dis"], active2.fields["mi_dis_d"], active2.fields["ma_dis"], active2.fields["ma_dis_d"], active2.fields["alg"], active2.fields["alg_d"], active2.fields["cdi"], active2.fields["cdi_d"], active2.fields["notes"]) var/counter = 1 - while(src.active2.fields[text("com_[]", counter)]) - P.info += text("[]
", src.active2.fields[text("com_[]", counter)]) + while(active2.fields[text("com_[]", counter)]) + P.info += text("[]
", active2.fields[text("com_[]", counter)]) counter++ - P.name = text("MR-[] '[]'", GLOB.data_core.medicalPrintCount, src.active1.fields["name"]) + P.name = text("MR-[] '[]'", GLOB.data_core.medicalPrintCount, active1.fields["name"]) else P.info += "Medical Record Lost!
" P.name = text("MR-[] '[]'", GLOB.data_core.medicalPrintCount, "Record Lost") P.info += "" - src.printing = null + P.update_icon() + printing = null - src.add_fingerprint(usr) - src.updateUsrDialog() + add_fingerprint(usr) + updateUsrDialog() return /obj/machinery/computer/med_data/emp_act(severity) @@ -560,11 +552,11 @@ switch(rand(1,6)) if(1) if(prob(10)) - R.fields["name"] = random_unique_lizard_name(R.fields["sex"],1) + R.fields["name"] = random_unique_lizard_name(R.fields["gender"],1) else - R.fields["name"] = random_unique_name(R.fields["sex"],1) + R.fields["name"] = random_unique_name(R.fields["gender"],1) if(2) - R.fields["sex"] = pick("Male", "Female") + R.fields["gender"] = pick("Male", "Female", "Other") if(3) R.fields["age"] = rand(AGE_MIN, AGE_MAX) if(4) @@ -583,7 +575,7 @@ if(user) if(message) if(authenticated) - if(user.canUseTopic(src)) + if(user.canUseTopic(src, BE_CLOSE)) if(!record1 || record1 == active1) if(!record2 || record2 == active2) return 1 diff --git a/code/game/machinery/computer/prisoner.dm b/code/game/machinery/computer/prisoner.dm index 798b0e4c65..a852612d9d 100644 --- a/code/game/machinery/computer/prisoner.dm +++ b/code/game/machinery/computer/prisoner.dm @@ -1,143 +1 @@ /obj/machinery/computer/prisoner - name = "prisoner management console" - desc = "Used to manage tracking implants placed inside criminals." - icon_screen = "explosive" - icon_keyboard = "security_key" - req_access = list(ACCESS_BRIG) - var/id = 0 - var/temp = null - var/status = 0 - var/timeleft = 60 - var/stop = 0 - var/screen = 0 // 0 - No Access Denied, 1 - Access allowed - var/obj/item/card/id/prisoner/inserted_id - circuit = /obj/item/circuitboard/computer/prisoner - - light_color = LIGHT_COLOR_RED - -/obj/machinery/computer/prisoner/ui_interact(mob/user) - . = ..() - var/dat = "" - if(screen == 0) - dat += "
Unlock Console" - else if(screen == 1) - dat += "

Prisoner ID Management

" - if(inserted_id) - dat += text("[inserted_id]
") - dat += text("Collected Points: [inserted_id.points]. Reset.
") - dat += text("Card goal: [inserted_id.goal]. Set
") - dat += text("Space Law recommends quotas of 100 points per minute they would normally serve in the brig.
") - else - dat += text("Insert Prisoner ID.
") - dat += "

Prisoner Implant Management

" - dat += "
Chemical Implants
" - var/turf/Tr = null - for(var/obj/item/implant/chem/C in GLOB.tracked_chem_implants) - Tr = get_turf(C) - if((Tr) && (Tr.z != src.z)) - continue//Out of range - if(!C.imp_in) - continue - dat += "ID: [C.imp_in.name] | Remaining Units: [C.reagents.total_volume]
" - dat += "| Inject: " - dat += "((1))" - dat += "((5))" - dat += "((10))
" - dat += "********************************
" - dat += "
Tracking Implants
" - for(var/obj/item/implant/tracking/T in GLOB.tracked_implants) - if(!isliving(T.imp_in)) - continue - Tr = get_turf(T) - if((Tr) && (Tr.z != src.z)) - continue//Out of range - - var/loc_display = "Unknown" - var/mob/living/M = T.imp_in - if(is_station_level(Tr.z) && !isspaceturf(M.loc)) - var/turf/mob_loc = get_turf(M) - loc_display = mob_loc.loc - - dat += "ID: [T.imp_in.name] | Location: [loc_display]
" - dat += "(Message Holder) |
" - dat += "********************************
" - dat += "
Lock Console" - 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 - -/obj/machinery/computer/prisoner/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/card/id)) - return attack_hand(user) - else - return ..() - -/obj/machinery/computer/prisoner/process() - if(!..()) - src.updateDialog() - return - - -/obj/machinery/computer/prisoner/Topic(href, href_list) - if(..()) - return - if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr)) - usr.set_machine(src) - - if(href_list["id"]) - if(href_list["id"] =="insert" && !inserted_id) - var/obj/item/card/id/prisoner/I = usr.is_holding_item_of_type(/obj/item/card/id/prisoner) - if(I) - if(!usr.transferItemToLoc(I, src)) - return - inserted_id = I - else - to_chat(usr, "No valid ID.") - else if(inserted_id) - switch(href_list["id"]) - if("eject") - inserted_id.forceMove(drop_location()) - inserted_id.verb_pickup() - inserted_id = null - if("reset") - inserted_id.points = 0 - if("setgoal") - var/num = round(input(usr, "Choose prisoner's goal:", "Input an Integer", null) as num|null) - if(num >= 0) - num = min(num,1000) //Cap the quota to the equivilent of 10 minutes. - inserted_id.goal = num - else if(href_list["inject1"]) - var/obj/item/implant/I = locate(href_list["inject1"]) in GLOB.tracked_chem_implants - if(I && istype(I)) - I.activate(1) - else if(href_list["inject5"]) - var/obj/item/implant/I = locate(href_list["inject5"]) in GLOB.tracked_chem_implants - if(I && istype(I)) - I.activate(5) - - else if(href_list["inject10"]) - var/obj/item/implant/I = locate(href_list["inject10"]) in GLOB.tracked_chem_implants - if(I && istype(I)) - I.activate(10) - - else if(href_list["lock"]) - if(src.allowed(usr)) - screen = !screen - else - to_chat(usr, "Unauthorized Access.") - - else if(href_list["warn"]) - var/warning = copytext(sanitize(input(usr,"Message:","Enter your message here!","")),1,MAX_MESSAGE_LEN) - if(!warning) - return - var/obj/item/implant/I = locate(href_list["warn"]) in GLOB.tracked_implants - if(I && istype(I) && I.imp_in) - var/mob/living/R = I.imp_in - to_chat(R, "You hear a voice in your head saying: '[warning]'") - log_directed_talk(usr, R, warning, LOG_SAY, "implant message") - - src.add_fingerprint(usr) - src.updateUsrDialog() - return diff --git a/code/game/machinery/computer/prisoner/_prisoner.dm b/code/game/machinery/computer/prisoner/_prisoner.dm new file mode 100644 index 0000000000..d07c351a22 --- /dev/null +++ b/code/game/machinery/computer/prisoner/_prisoner.dm @@ -0,0 +1,52 @@ +/obj/machinery/computer/prisoner + var/obj/item/card/id/prisoner/contained_id + +/obj/machinery/computer/prisoner/Destroy() + if(contained_id) + contained_id.forceMove(get_turf(src)) + return ..() + + +/obj/machinery/computer/prisoner/examine(mob/user) + . = ..() + if(contained_id) + . += "Alt-click to eject the ID card." + + + +/obj/machinery/computer/prisoner/AltClick(mob/user) + id_eject(user) + return ..() + +/obj/machinery/computer/prisoner/proc/id_insert(mob/user, obj/item/card/id/prisoner/P) + if(istype(P)) + if(contained_id) + to_chat(user, "There's already an ID card in the console!") + return + if(!user.transferItemToLoc(P, src)) + return + contained_id = P + user.visible_message("[user] inserts an ID card into the console.", \ + "You insert the ID card into the console.") + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + updateUsrDialog() + +/obj/machinery/computer/prisoner/proc/id_eject(mob/user) + if(!contained_id) + to_chat(user, "There's no ID card in the console!") + return + else + contained_id.forceMove(drop_location()) + if(!issilicon(user) && Adjacent(user)) + user.put_in_hands(contained_id) + contained_id = null + user.visible_message("[user] gets an ID card from the console.", \ + "You get the ID card from the console.") + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + updateUsrDialog() + +/obj/machinery/computer/prisoner/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/card/id/prisoner)) + id_insert(user, I) + else + return ..() diff --git a/code/game/machinery/computer/prisoner/gulag_teleporter.dm b/code/game/machinery/computer/prisoner/gulag_teleporter.dm new file mode 100644 index 0000000000..f6e164efe9 --- /dev/null +++ b/code/game/machinery/computer/prisoner/gulag_teleporter.dm @@ -0,0 +1,142 @@ +//computer that handle the points and teleports the prisoner +/obj/machinery/computer/prisoner/gulag_teleporter_computer + name = "labor camp teleporter console" + desc = "Used to send criminals to the Labor Camp." + icon_screen = "explosive" + icon_keyboard = "security_key" + req_access = list(ACCESS_ARMORY) + circuit = /obj/item/circuitboard/computer/gulag_teleporter_console + var/default_goal = 200 + var/obj/machinery/gulag_teleporter/teleporter = null + var/obj/structure/gulag_beacon/beacon = null + var/mob/living/carbon/human/prisoner = null + var/datum/data/record/temporary_record = null + + light_color = LIGHT_COLOR_RED + +/obj/machinery/computer/prisoner/gulag_teleporter_computer/Initialize() + . = ..() + scan_machinery() + +/obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ + datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "gulag_console", name, 455, 440, master_ui, state) + ui.open() + +/obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_data(mob/user) + var/list/data = list() + + var/list/prisoner_list = list() + var/can_teleport = FALSE + + if(teleporter && (teleporter.occupant && ishuman(teleporter.occupant))) + prisoner = teleporter.occupant + prisoner_list["name"] = prisoner.real_name + if(contained_id) + can_teleport = TRUE + if(!isnull(GLOB.data_core.general)) + for(var/r in GLOB.data_core.security) + var/datum/data/record/R = r + if(R.fields["name"] == prisoner_list["name"]) + temporary_record = R + prisoner_list["crimstat"] = temporary_record.fields["criminal"] + + data["prisoner"] = prisoner_list + + if(teleporter) + data["teleporter"] = teleporter + data["teleporter_location"] = "([teleporter.x], [teleporter.y], [teleporter.z])" + data["teleporter_lock"] = teleporter.locked + data["teleporter_state_open"] = teleporter.state_open + if(beacon) + data["beacon"] = beacon + data["beacon_location"] = "([beacon.x], [beacon.y], [beacon.z])" + if(contained_id) + data["id"] = contained_id + data["id_name"] = contained_id.registered_name + data["goal"] = contained_id.goal + data["can_teleport"] = can_teleport + + return data + +/obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_act(action, list/params) + if(isliving(usr)) + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) + if(..()) + return + if(!allowed(usr)) + to_chat(usr, "Access denied.") + return + switch(action) + if("scan_teleporter") + teleporter = findteleporter() + if("scan_beacon") + beacon = findbeacon() + if("handle_id") + if(contained_id) + id_eject(usr) + else + id_insert(usr) + if("set_goal") + var/new_goal = input("Set the amount of points:", "Points", contained_id.goal) as num|null + if(!isnum(new_goal)) + return + if(!new_goal) + new_goal = default_goal + if (new_goal > 1000) + to_chat(usr, "The entered amount of points is too large. Points have instead been set to the maximum allowed amount.") + contained_id.goal = CLAMP(new_goal, 0, 1000) //maximum 1000 points + if("toggle_open") + if(teleporter.locked) + to_chat(usr, "The teleporter is locked") + return + teleporter.toggle_open() + if("teleporter_lock") + if(teleporter.state_open) + to_chat(usr, "Close the teleporter before locking!") + return + teleporter.locked = !teleporter.locked + if("teleport") + if(!teleporter || !beacon) + return + addtimer(CALLBACK(src, .proc/teleport, usr), 5) + +/obj/machinery/computer/prisoner/gulag_teleporter_computer/proc/scan_machinery() + teleporter = findteleporter() + beacon = findbeacon() + +/obj/machinery/computer/prisoner/gulag_teleporter_computer/proc/findteleporter() + var/obj/machinery/gulag_teleporter/teleporterf = null + + for(var/direction in GLOB.cardinals) + teleporterf = locate(/obj/machinery/gulag_teleporter, get_step(src, direction)) + if(teleporterf && teleporterf.is_operational()) + return teleporterf + +/obj/machinery/computer/prisoner/gulag_teleporter_computer/proc/findbeacon() + return locate(/obj/structure/gulag_beacon) + +/obj/machinery/computer/prisoner/gulag_teleporter_computer/proc/teleport(mob/user) + if(!contained_id) //incase the ID was removed after the transfer timer was set. + say("Warning: Unable to transfer prisoner without a valid Prisoner ID inserted!") + return + var/id_goal_not_set + if(!contained_id.goal) + id_goal_not_set = TRUE + contained_id.goal = default_goal + say("[contained_id]'s ID card goal defaulting to [contained_id.goal] points.") + log_game("[key_name(user)] teleported [key_name(prisoner)] to the Labor Camp [COORD(beacon)] for [id_goal_not_set ? "default goal of ":""][contained_id.goal] points.") + teleporter.handle_prisoner(contained_id, temporary_record) + playsound(src, 'sound/weapons/emitter.ogg', 50, 1) + prisoner.forceMove(get_turf(beacon)) + prisoner.Stun(40) // small travel dizziness + to_chat(prisoner, "The teleportation makes you a little dizzy.") + new /obj/effect/particle_effect/sparks(get_turf(prisoner)) + playsound(src, "sparks", 50, 1) + if(teleporter.locked) + teleporter.locked = FALSE + teleporter.toggle_open() + contained_id = null + temporary_record = null diff --git a/code/game/machinery/computer/prisoner/management.dm b/code/game/machinery/computer/prisoner/management.dm new file mode 100644 index 0000000000..e231a1748a --- /dev/null +++ b/code/game/machinery/computer/prisoner/management.dm @@ -0,0 +1,139 @@ + +/obj/machinery/computer/prisoner/management + name = "prisoner management console" + desc = "Used to manage tracking implants placed inside criminals." + icon_screen = "explosive" + icon_keyboard = "security_key" + req_access = list(ACCESS_BRIG) + var/id = 0 + var/temp = null + var/status = 0 + var/timeleft = 60 + var/stop = 0 + var/screen = 0 // 0 - No Access Denied, 1 - Access allowed + circuit = /obj/item/circuitboard/computer/prisoner + + light_color = LIGHT_COLOR_RED + +/obj/machinery/computer/prisoner/management/ui_interact(mob/user) + . = ..() + if(isliving(user)) + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) + var/dat = "" + if(screen == 0) + dat += "
{Log In}" + else if(screen == 1) + dat += "

Prisoner ID Management

" + if(contained_id) + dat += text("[contained_id]
") + dat += text("Collected Points: [contained_id.points]. Reset.
") + dat += text("Card goal: [contained_id.goal]. Set
") + dat += text("Space Law recommends quotas of 100 points per minute they would normally serve in the brig.
") + else + dat += text("Insert Prisoner ID.
") + dat += "

Prisoner Implant Management

" + dat += "
Chemical Implants
" + var/turf/Tr = null + for(var/obj/item/implant/chem/C in GLOB.tracked_chem_implants) + Tr = get_turf(C) + if((Tr) && (Tr.z != src.z)) + continue//Out of range + if(!C.imp_in) + continue + dat += "ID: [C.imp_in.name] | Remaining Units: [C.reagents.total_volume]
" + dat += "| Inject: " + dat += "((1))" + dat += "((5))" + dat += "((10))
" + dat += "********************************
" + dat += "
Tracking Implants
" + for(var/obj/item/implant/tracking/T in GLOB.tracked_implants) + if(!isliving(T.imp_in)) + continue + Tr = get_turf(T) + if((Tr) && (Tr.z != src.z)) + continue//Out of range + + var/loc_display = "Unknown" + var/mob/living/M = T.imp_in + if(is_station_level(Tr.z) && !isspaceturf(M.loc)) + var/turf/mob_loc = get_turf(M) + loc_display = mob_loc.loc + + dat += "ID: [T.imp_in.name] | Location: [loc_display]
" + dat += "(Message Holder) |
" + dat += "********************************
" + 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 + +/obj/machinery/computer/prisoner/management/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/card/id)) + if(screen) + id_insert(user) + else + to_chat(user, "Unauthorized access.") + else + return ..() + +/obj/machinery/computer/prisoner/management/process() + if(!..()) + src.updateDialog() + return + +/obj/machinery/computer/prisoner/management/Topic(href, href_list) + if(..()) + return + if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr)) + usr.set_machine(src) + + if(href_list["id"]) + if(href_list["id"] =="insert" && !contained_id) + id_insert(usr) + else if(contained_id) + switch(href_list["id"]) + if("eject") + id_eject(usr) + if("reset") + contained_id.points = 0 + if("setgoal") + var/num = round(input(usr, "Choose prisoner's goal:", "Input an Integer", null) as num|null) + if(num >= 0) + num = min(num,1000) //Cap the quota to the equivilent of 10 minutes. + contained_id.goal = num + else if(href_list["inject1"]) + var/obj/item/implant/I = locate(href_list["inject1"]) in GLOB.tracked_chem_implants + if(I && istype(I)) + I.activate(1) + else if(href_list["inject5"]) + var/obj/item/implant/I = locate(href_list["inject5"]) in GLOB.tracked_chem_implants + if(I && istype(I)) + I.activate(5) + else if(href_list["inject10"]) + var/obj/item/implant/I = locate(href_list["inject10"]) in GLOB.tracked_chem_implants + if(I && istype(I)) + I.activate(10) + + else if(href_list["lock"]) + if(allowed(usr)) + screen = !screen + playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE) + else + to_chat(usr, "Unauthorized access.") + + else if(href_list["warn"]) + var/warning = copytext(sanitize(input(usr,"Message:","Enter your message here!","")),1,MAX_MESSAGE_LEN) + if(!warning) + return + var/obj/item/implant/I = locate(href_list["warn"]) in GLOB.tracked_implants + if(I && istype(I) && I.imp_in) + var/mob/living/R = I.imp_in + to_chat(R, "You hear a voice in your head saying: '[warning]'") + log_directed_talk(usr, R, warning, LOG_SAY, "implant message") + + src.add_fingerprint(usr) + src.updateUsrDialog() + return diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 607f8dbe78..1823e34100 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -5,13 +5,10 @@ icon_keyboard = "security_key" req_one_access = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS) circuit = /obj/item/circuitboard/computer/secure_data - var/obj/item/card/id/scan = null - var/authenticated = null var/rank = null var/screen = null var/datum/data/record/active1 = null var/datum/data/record/active2 = null - var/a_id = null var/temp = null var/printing = null var/can_change_id = 0 @@ -23,11 +20,6 @@ light_color = LIGHT_COLOR_RED -/obj/machinery/computer/secure_data/examine(mob/user) - ..() - if(scan) - to_chat(user, "Alt-click to eject the ID card.") - /obj/machinery/computer/secure_data/syndie icon_keyboard = "syndie_key" @@ -40,32 +32,19 @@ clockwork = TRUE //it'd look weird pass_flags = PASSTABLE -/obj/machinery/computer/secure_data/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/card/id)) - if(!scan) - if(!user.transferItemToLoc(O, src)) - return - scan = O - to_chat(user, "You insert [O].") - playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) - updateUsrDialog() - else - to_chat(user, "There's already an ID card in the console.") - else - return ..() - //Someone needs to break down the dat += into chunks instead of long ass lines. /obj/machinery/computer/secure_data/ui_interact(mob/user) . = ..() + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) if(src.z > 6) to_chat(user, "Unable to establish a connection: \black You're too far away from the station!") return var/dat if(temp) - dat = text("[]

Clear Screen", temp) + dat = "[temp]

Clear Screen" else - dat = text("Confirm Identity: []
", (scan ? text("[]", scan.name) : "----------")) + dat = "" if(authenticated) switch(screen) if(1) @@ -190,7 +169,7 @@ dat += {"\s*$/g,rb={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"
- + "} dat += "" dat += {" @@ -309,36 +288,39 @@ What a mess.*/ active1 = null active2 = null - if("Confirm Identity") - eject_id(usr) - if("Log Out") authenticated = null screen = null active1 = null active2 = null + playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE) if("Log In") - if(issilicon(usr)) - var/mob/living/silicon/borg = usr + var/mob/M = usr + var/obj/item/card/id/I = M.get_idcard(TRUE) + if(issilicon(M)) + var/mob/living/silicon/borg = M active1 = null active2 = null authenticated = borg.name rank = "AI" screen = 1 - else if(IsAdminGhost(usr)) + else if(IsAdminGhost(M)) active1 = null active2 = null - authenticated = usr.client.holder.admin_signature + authenticated = M.client.holder.admin_signature rank = "Central Command" screen = 1 - else if(istype(scan, /obj/item/card/id)) + else if(I && check_access(I)) active1 = null active2 = null - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment - screen = 1 + authenticated = I.registered_name + rank = I.assignment + screen = 1 + else + to_chat(usr, "Unauthorized Access.") + playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE) + //RECORD FUNCTIONS if("Record Maintenance") screen = 2 @@ -346,16 +328,14 @@ What a mess.*/ active2 = null if("Browse Record") - var/datum/data/record/R = locate(href_list["d_rec"]) - var/S = locate(href_list["d_rec"]) - if(!( GLOB.data_core.general.Find(R) )) + var/datum/data/record/R = locate(href_list["d_rec"]) in GLOB.data_core.general + if(!R) temp = "Record Not Found!" else + active1 = active2 = R for(var/datum/data/record/E in GLOB.data_core.security) if((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) - S = E - active1 = R - active2 = S + active2 = E screen = 3 @@ -368,7 +348,7 @@ What a mess.*/ var/obj/item/paper/P = new /obj/item/paper( loc ) P.info = "
Security Record - (SR-[GLOB.data_core.securityPrintCount])

" if((istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1))) - P.info += text("Name: [] ID: []
\nSex: []
\nAge: []
", active1.fields["name"], active1.fields["id"], active1.fields["sex"], active1.fields["age"]) + P.info += text("Name: [] ID: []
\nGender: []
\nAge: []
", active1.fields["name"], active1.fields["id"], active1.fields["gender"], active1.fields["age"]) P.info += "\nSpecies: [active1.fields["species"]]
" P.info += text("\nFingerprint: []
\nPhysical Status: []
\nMental Status: []
", active1.fields["fingerprint"], active1.fields["p_stat"], active1.fields["m_stat"]) else @@ -419,6 +399,7 @@ What a mess.*/ P.info += "Security Record Lost!
" P.name = text("SR-[] '[]'", GLOB.data_core.securityPrintCount, "Record Lost") P.info += "" + P.update_icon() printing = null if("Print Poster") if(!( printing )) @@ -512,7 +493,7 @@ What a mess.*/ G.fields["name"] = "New Record" G.fields["id"] = "[num2hex(rand(1, 1.6777215E7), 6)]" G.fields["rank"] = "Unassigned" - G.fields["sex"] = "Male" + G.fields["gender"] = "Male" G.fields["age"] = "Unknown" G.fields["species"] = "Human" G.fields["photo_front"] = new /icon() @@ -584,12 +565,14 @@ What a mess.*/ if(!canUseSecurityRecordsConsole(usr, t1, a1)) return active1.fields["fingerprint"] = t1 - if("sex") + if("gender") if(istype(active1, /datum/data/record)) - if(active1.fields["sex"] == "Male") - active1.fields["sex"] = "Female" + if(active1.fields["gender"] == "Male") + active1.fields["gender"] = "Female" + else if(active1.fields["gender"] == "Female") + active1.fields["gender"] = "Other" else - active1.fields["sex"] = "Male" + active1.fields["gender"] = "Male" if("age") if(istype(active1, /datum/data/record)) var/t1 = input("Please input age:", "Secure. records", active1.fields["age"], null) as num @@ -767,19 +750,14 @@ What a mess.*/ P = user.get_active_held_item() return P -/obj/machinery/computer/secure_data/proc/print_photo(icon/temp, name) +/obj/machinery/computer/secure_data/proc/print_photo(icon/temp, person_name) if (printing) return printing = TRUE sleep(20) var/obj/item/photo/P = new/obj/item/photo(drop_location()) - var/icon/small_img = icon(temp) - var/icon/ic = icon('icons/obj/items_and_weapons.dmi',"photo") - small_img.Scale(8, 8) - ic.Blend(small_img,ICON_OVERLAY, 13, 13) - P.icon = ic - P.picture.picture_image = temp - P.desc = "The photo on file for [name]." + var/datum/picture/toEmbed = new(name = person_name, desc = "The photo on file for [person_name].", image = temp) + P.set_picture(toEmbed, TRUE, TRUE) P.pixel_x = rand(-10, 10) P.pixel_y = rand(-10, 10) printing = FALSE @@ -799,7 +777,7 @@ What a mess.*/ else R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" if(2) - R.fields["sex"] = pick("Male", "Female") + R.fields["gender"] = pick("Male", "Female", "Other") if(3) R.fields["age"] = rand(5, 85) if(4) @@ -823,7 +801,7 @@ What a mess.*/ /obj/machinery/computer/secure_data/proc/canUseSecurityRecordsConsole(mob/user, message1 = 0, record1, record2) if(user) if(authenticated) - if(user.canUseTopic(src)) + if(user.canUseTopic(src, BE_CLOSE)) if(!trim(message1)) return 0 if(!record1 || record1 == active1) @@ -831,22 +809,3 @@ What a mess.*/ return 1 return 0 -/obj/machinery/computer/secure_data/AltClick(mob/user) - if(user.canUseTopic(src)) - eject_id(user) - -/obj/machinery/computer/secure_data/proc/eject_id(mob/user) - if(scan) - scan.forceMove(drop_location()) - if(!issilicon(user) && Adjacent(user)) - user.put_in_hands(scan) - scan = null - playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) - else //switching the ID with the one you're holding - if(issilicon(user) || !Adjacent(user)) - return - var/obj/item/card/id/held_id = user.is_holding_item_of_type(/obj/item/card/id) - if(QDELETED(held_id) || !user.transferItemToLoc(held_id, src)) - return - scan = held_id - playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) diff --git a/modular_citadel/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm similarity index 69% rename from modular_citadel/code/game/machinery/cryopod.dm rename to code/game/machinery/cryopod.dm index 15e7d34c65..e358f10346 100644 --- a/modular_citadel/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -20,105 +20,146 @@ req_one_access = list(ACCESS_HEADS, ACCESS_ARMORY) //Heads of staff or the warden can go here to claim recover items from their department that people went were cryodormed with. var/mode = null + var/menu = 1 //Which menu screen to display + //Used for logging people entering cryosleep and important items they are carrying. var/list/frozen_crew = list() var/list/frozen_items = list() - var/storage_type = "crewmembers" - var/storage_name = "Cryogenic Oversight Control" + // Used for containing rare items traitors need to steal, so it's not + // game-over if they get iced + var/list/objective_items = list() + // A cache of theft datums so you don't have to re-create them for + // each item check + var/list/theft_cache = list() + var/allow_items = TRUE +/obj/machinery/computer/cryopod/attack_ai() + attack_hand() + /obj/machinery/computer/cryopod/ui_interact(mob/user = usr) - . = ..() + if(!is_operational()) + return user.set_machine(src) add_fingerprint(user) - var/dat + var/dat = "" - dat += "

[storage_name]
" - dat += "Welcome, [user.real_name].


" - dat += "View storage log.
" - if(allow_items) - dat += "View objects.
" - dat += "Recover object.
" - dat += "Recover all objects.
" + dat += "

Welcome, [user.real_name].


" + dat += "

" - user << browse(dat, "window=cryopod_console") - onclose(user, "cryopod_console") + switch(src.menu) + if(1) + dat += "View crew storage log

" + if(allow_items) + dat += "View objects storage log

" + dat += "Recover object

" + dat += "Recover all objects
" + if(2) + dat += "<< Back

" + dat += "

Recently stored Crew




" + if(!frozen_crew.len) + dat += "There has been no storage usage at this terminal.
" + else + for(var/person in frozen_crew) + dat += "[person]
" + dat += "
" + if(3) + dat += "<< Back

" + dat += "

Recently stored objects




" + if(!frozen_items.len) + dat += "There has been no storage usage at this terminal.
" + else + for(var/obj/item/I in frozen_items) + dat += "[I.name]
" + dat += "
" + + 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) if(..()) - return 1 + return TRUE var/mob/user = usr add_fingerprint(user) - if(href_list["log"]) - - var/dat = "Recently stored [storage_type]


" - for(var/person in frozen_crew) - dat += "[person]
" - dat += "
" - - user << browse(dat, "window=cryolog") - - if(href_list["view"]) - if(!allow_items) return - - var/dat = "Recently stored objects


" - for(var/obj/item/I in frozen_items) - dat += "[I.name]
" - dat += "
" - - user << browse(dat, "window=cryoitems") - - else if(href_list["item"]) + if(href_list["item"]) if(!allowed(user)) to_chat(user, "Access Denied.") + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + updateUsrDialog() return if(!allow_items) return if(frozen_items.len == 0) to_chat(user, "There is nothing to recover from storage.") + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + updateUsrDialog() return var/obj/item/I = input(user, "Please choose which object to retrieve.","Object recovery",null) as null|anything in frozen_items + playsound(src, "terminal_type", 25, 0) if(!I) return if(!(I in frozen_items)) to_chat(user, "\The [I] is no longer in storage.") + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + updateUsrDialog() return visible_message("The console beeps happily as it disgorges \the [I].") + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - I.forceMove(get_turf(src)) + I.forceMove(drop_location()) + if(user && Adjacent(user) && !issiliconoradminghost(user)) + user.put_in_hands(I) frozen_items -= I + updateUsrDialog() else if(href_list["allitems"]) + playsound(src, "terminal_type", 25, 0) if(!allowed(user)) to_chat(user, "Access Denied.") + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + updateUsrDialog() return if(!allow_items) return if(frozen_items.len == 0) to_chat(user, "There is nothing to recover from storage.") + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) return visible_message("The console beeps happily as it disgorges the desired objects.") + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) for(var/obj/item/I in frozen_items) - I.forceMove(get_turf(src)) + I.forceMove(drop_location()) frozen_items -= I + updateUsrDialog() + else if (href_list["menu"]) + src.menu = text2num(href_list["menu"]) + playsound(src, "terminal_type", 25, 0) + updateUsrDialog() + + ui_interact(usr) updateUsrDialog() + return /obj/item/circuitboard/cryopodcontrol name = "Circuit board (Cryogenic Oversight Console)" build_path = "/obj/machinery/computer/cryopod" +/obj/machinery/computer/cryopod/contents_explosion() + return //Cryopods themselves. /obj/machinery/cryopod @@ -176,16 +217,17 @@ /obj/item/gun/energy/laser/cyborg ) -/obj/machinery/cryopod/Initialize() +/obj/machinery/cryopod/Initialize(mapload) . = ..() update_icon() - find_control_computer(TRUE) + find_control_computer(mapload) /obj/machinery/cryopod/proc/find_control_computer(urgent = FALSE) for(var/obj/machinery/computer/cryopod/C in get_area(src)) control_computer = C if(C) return C + break // Don't send messages unless we *need* the computer, and less than five minutes have passed since last time we messaged if(!control_computer && urgent && last_no_computer_message + 5*60*10 < world.time) @@ -193,7 +235,7 @@ message_admins("Cryopod in [get_area(src)] could not find control computer!") last_no_computer_message = world.time - return null + return control_computer != null /obj/machinery/cryopod/close_machine(mob/user) if(!control_computer) @@ -242,10 +284,75 @@ despawn_occupant() +#define CRYO_DESTROY 0 +#define CRYO_PRESERVE 1 +#define CRYO_OBJECTIVE 2 + +/obj/machinery/cryopod/proc/should_preserve_item(obj/item/I) + for(var/datum/objective_item/steal/T in control_computer.theft_cache) + if(istype(I, T.targetitem) && T.check_special_completion(I)) + return CRYO_OBJECTIVE + for(var/T in preserve_items) + if(istype(I, T) && !(I.type in do_not_preserve_items)) + return CRYO_PRESERVE + return CRYO_DESTROY + // This function can not be undone; do not call this unless you are sure /obj/machinery/cryopod/proc/despawn_occupant() + if(!control_computer) + find_control_computer() + var/mob/living/mob_occupant = occupant + //Handle Borg stuff first + if(iscyborg(mob_occupant)) + var/mob/living/silicon/robot/R = mob_occupant + if(!istype(R)) return ..() + + R.contents -= R.mmi + qdel(R.mmi) + for(var/obj/item/I in R.module) // the tools the borg has; metal, glass, guns etc + for(var/obj/item/O in I) // the things inside the tools, if anything; mainly for janiborg trash bags + if(should_preserve_item(O) != CRYO_DESTROY) // Preserve important things inside the item + continue + O.forceMove(src) + R.module.remove_module(I, TRUE) //delete the module itself so it doesn't transfer over. + + //Drop all items into the pod. + for(var/obj/item/I in mob_occupant) + mob_occupant.doUnEquip(I) + I.forceMove(src) + + if(I.contents.len) //Make sure we catch anything not handled by qdel() on the items. + if(should_preserve_item(I) != CRYO_DESTROY) // Don't remove the contents of things that need preservation + continue + for(var/obj/item/O in I.contents) + if(istype(O, /obj/item/tank)) //Stop eating pockets, you fuck! + continue + O.forceMove(src) + + //Delete all items not on the preservation list. + var/list/items = contents + items -= mob_occupant // Don't delete the occupant + + for(var/obj/item/I in items) + if(istype(I, /obj/item/pda)) + var/obj/item/pda/P = I + QDEL_NULL(P.id) + qdel(P) + continue + + var/preserve = should_preserve_item(I) + if(preserve == CRYO_DESTROY) + qdel(I) + else if(control_computer && control_computer.allow_items) + control_computer.frozen_items += I + if(preserve == CRYO_OBJECTIVE) + control_computer.objective_items += I + I.loc = null + else + I.forceMove(loc) + //Update any existing objectives involving this mob. for(var/datum/objective/O in GLOB.objectives) // We don't want revs to get objectives that aren't for heads of staff. Letting @@ -302,30 +409,6 @@ announcer.announce("CRYOSTORAGE", mob_occupant.real_name, announce_rank, list()) visible_message("\The [src] hums and hisses as it moves [mob_occupant.real_name] into storage.") - - for(var/obj/item/W in mob_occupant.GetAllContents()) - if(W.loc.loc && (( W.loc.loc == loc ) || (W.loc.loc == control_computer))) - continue//means we already moved whatever this thing was in - //I'm a professional, okay - for(var/T in preserve_items) - if(istype(W, T)) - if(control_computer && control_computer.allow_items) - control_computer.frozen_items += W - mob_occupant.transferItemToLoc(W, control_computer, TRUE) - else - mob_occupant.transferItemToLoc(W, loc, TRUE) - - for(var/obj/item/W in mob_occupant.GetAllContents()) - qdel(W)//because we moved all items to preserve away - //and yes, this totally deletes their bodyparts one by one, I just couldn't bother - - if(iscyborg(mob_occupant)) - var/mob/living/silicon/robot/R = occupant - if(!istype(R)) return ..() - - R.contents -= R.mmi - qdel(R.mmi) - // Ghost and delete the mob. if(!mob_occupant.get_ghost(1)) mob_occupant.ghostize(0) // Players who cryo out may not re-enter the round @@ -334,6 +417,10 @@ open_machine() name = initial(name) +#undef CRYO_DESTROY +#undef CRYO_PRESERVE +#undef CRYO_OBJECTIVE + /obj/machinery/cryopod/MouseDrop_T(mob/living/target, mob/user) if(!istype(target) || user.incapacitated() || !target.Adjacent(user) || !Adjacent(user) || !ismob(target) || (!ishuman(user) && !iscyborg(user)) || !istype(user.loc, /turf) || target.buckled) return @@ -358,7 +445,7 @@ var/generic_plsnoleave_message = " Please adminhelp before leaving the round, even if there are no administrators online!" - if(target == user && world.time - target.client.cryo_warned > 5 * 600)//if we haven't warned them in the last 5 minutes + if(target == user && world.time - target.client.cryo_warned > 5 MINUTES)//if we haven't warned them in the last 5 minutes var/caught = FALSE if(target.mind.assigned_role in GLOB.command_positions) alert("You're a Head of Staff![generic_plsnoleave_message] Be sure to put your locker items back into your locker!") @@ -366,13 +453,13 @@ if(iscultist(target) || is_servant_of_ratvar(target)) to_chat(target, "You're a Cultist![generic_plsnoleave_message]") caught = TRUE - if(istype(SSticker.mode, /datum/antagonist/blob)) - if(target.mind in GLOB.overminds) - alert("You're a Blob![generic_plsnoleave_message]") - caught = TRUE if(is_devil(target)) alert("You're a Devil![generic_plsnoleave_message]") caught = TRUE + if(istype(SSticker.mode, /datum/antagonist/gang)) + if(target.mind.has_antag_datum(/datum/antagonist/gang)) + alert("You're a Gangster![generic_plsnoleave_message]") + caught = TRUE if(istype(SSticker.mode, /datum/antagonist/rev)) if(target.mind.has_antag_datum(/datum/antagonist/rev/head)) alert("You're a Head Revolutionary![generic_plsnoleave_message]") @@ -385,8 +472,9 @@ target.client.cryo_warned = world.time return - if(!Adjacent(user)) + if(!target || user.incapacitated() || !target.Adjacent(user) || !Adjacent(user) || (!ishuman(user) && !iscyborg(user)) || !istype(user.loc, /turf) || target.buckled) return + //rerun the checks in case of shenanigans if(target == user) visible_message("[user] starts climbing into the cryo pod.") @@ -396,7 +484,6 @@ if(occupant) to_chat(user, "\The [src] is in use.") return - close_machine(target) to_chat(target, "If you ghost, log out or close your client now, your character will shortly be permanently removed from the round.") @@ -407,4 +494,4 @@ //Attacks/effects. /obj/machinery/cryopod/blob_act() - return //Sorta gamey, but we don't really want these to be destroyed. + return //Sorta gamey, but we don't really want these to be destroyed. \ No newline at end of file diff --git a/code/game/machinery/dna_scanner.dm b/code/game/machinery/dna_scanner.dm index 7895fb8c9f..c9e1e7195b 100644 --- a/code/game/machinery/dna_scanner.dm +++ b/code/game/machinery/dna_scanner.dm @@ -89,11 +89,11 @@ return C return null -/obj/machinery/dna_scannernew/close_machine(mob/living/carbon/user) +/obj/machinery/dna_scannernew/close_machine(atom/movable/target) if(!state_open) return FALSE - ..(user) + ..(target) // search for ghosts, if the corpse is empty and the scanner is connected to a cloner var/mob/living/mob_occupant = get_mob_or_brainmob(occupant) @@ -111,7 +111,7 @@ return TRUE /obj/machinery/dna_scannernew/open_machine() - if(state_open) + if(state_open || panel_open) return FALSE ..() @@ -126,23 +126,48 @@ return open_machine() -/obj/machinery/dna_scannernew/attackby(obj/item/I, mob/user, params) - - if(!occupant && default_deconstruction_screwdriver(user, icon_state, icon_state, I))//sent icon_state is irrelevant... - update_icon()//..since we're updating the icon here, since the scanner can be unpowered when opened/closed +/obj/machinery/dna_scannernew/screwdriver_act(mob/living/user, obj/item/I) + . = TRUE + if(..()) return + if(occupant) + to_chat(user, "[src] is currently occupied!") + return + if(state_open) + to_chat(user, "[src] must be closed to [panel_open ? "close" : "open"] its maintenance hatch!") + return + if(default_deconstruction_screwdriver(user, icon_state, icon_state, I)) //sent icon_state is irrelevant... + update_icon() //..since we're updating the icon here, since the scanner can be unpowered when opened/closed + return + return FALSE +/obj/machinery/dna_scannernew/wrench_act(mob/living/user, obj/item/I) + . = ..() + if(default_change_direction_wrench(user, I)) + return TRUE + +/obj/machinery/dna_scannernew/crowbar_act(mob/living/user, obj/item/I) + . = ..() if(default_pry_open(I)) - return - + return TRUE if(default_deconstruction_crowbar(I)) - return + return TRUE - return ..() +/obj/machinery/dna_scannernew/default_pry_open(obj/item/I) //wew + . = !(state_open || panel_open || (flags_1 & NODECONSTRUCT_1)) && I.tool_behaviour == TOOL_CROWBAR + if(.) + I.play_tool_sound(src, 50) + visible_message("[usr] pries open [src].", "You pry open [src].") + open_machine() /obj/machinery/dna_scannernew/interact(mob/user) toggle_open(user) +/obj/machinery/dna_scannernew/AltClick(mob/user) + if(!user.canUseTopic(src, !issilicon(user))) + return + interact(user) + /obj/machinery/dna_scannernew/MouseDrop_T(mob/target, mob/user) if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser()) return diff --git a/code/game/machinery/gulag_item_reclaimer.dm b/code/game/machinery/gulag_item_reclaimer.dm index 320430be39..f51c145635 100644 --- a/code/game/machinery/gulag_item_reclaimer.dm +++ b/code/game/machinery/gulag_item_reclaimer.dm @@ -9,7 +9,6 @@ idle_power_usage = 100 active_power_usage = 2500 var/list/stored_items = list() - var/obj/item/card/id/prisoner/inserted_id = null var/obj/machinery/gulag_teleporter/linked_teleporter = null /obj/machinery/gulag_item_reclaimer/Destroy() @@ -18,9 +17,6 @@ I.forceMove(get_turf(src)) if(linked_teleporter) linked_teleporter.linked_reclaimer = null - if(inserted_id) - inserted_id.forceMove(get_turf(src)) - inserted_id = null return ..() /obj/machinery/gulag_item_reclaimer/emag_act(mob/user) @@ -31,18 +27,6 @@ obj_flags |= EMAGGED return TRUE -/obj/machinery/gulag_item_reclaimer/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/card/id)) - if(!inserted_id) - if(!user.transferItemToLoc(I, src)) - return - inserted_id = I - to_chat(user, "You insert [I].") - return - else - to_chat(user, "There's an ID inserted already.") - return ..() - /obj/machinery/gulag_item_reclaimer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) @@ -57,15 +41,19 @@ if(allowed(user)) can_reclaim = TRUE - if(inserted_id) - data["id"] = inserted_id - data["id_name"] = inserted_id.registered_name - if(inserted_id.points >= inserted_id.goal) + var/obj/item/card/id/I = user.get_idcard(TRUE) + if(istype(I, /obj/item/card/id/prisoner)) + var/obj/item/card/id/prisoner/P = I + if(P.points >= P.goal) can_reclaim = TRUE var/list/mobs = list() for(var/i in stored_items) var/mob/thismob = i + if(QDELETED(thismob)) + say("Alert! Unable to locate vital signals of a previously processed prisoner. Ejecting equipment!") + drop_items(thismob) + continue var/list/mob_info = list() mob_info["name"] = thismob.real_name mob_info["mob"] = "[REF(thismob)]" @@ -80,16 +68,6 @@ /obj/machinery/gulag_item_reclaimer/ui_act(action, list/params) switch(action) - if("handle_id") - if(inserted_id) - usr.put_in_hands(inserted_id) - inserted_id = null - else - var/obj/item/I = usr.is_holding_item_of_type(/obj/item/card/id) - if(I) - if(!usr.transferItemToLoc(I, src)) - return - inserted_id = I if("release_items") var/mob/M = locate(params["mobref"]) if(M == usr || allowed(usr)) @@ -100,8 +78,9 @@ /obj/machinery/gulag_item_reclaimer/proc/drop_items(mob/user) if(!stored_items[user]) return + var/drop_location = drop_location() for(var/i in stored_items[user]) var/obj/item/W = i stored_items[user] -= W - W.forceMove(get_turf(src)) + W.forceMove(drop_location) stored_items -= user diff --git a/code/game/machinery/gulag_teleporter.dm b/code/game/machinery/gulag_teleporter.dm index 47136e8c27..6b944f762d 100644 --- a/code/game/machinery/gulag_teleporter.dm +++ b/code/game/machinery/gulag_teleporter.dm @@ -136,16 +136,17 @@ The console is located at computer/gulag_teleporter.dm linked_reclaimer.stored_items[occupant] = list() var/mob/living/mob_occupant = occupant for(var/obj/item/W in mob_occupant) - if(!is_type_in_typecache(W, telegulag_required_items) && mob_occupant.temporarilyRemoveItemFromInventory(W)) - if(istype(W, /obj/item/restraints/handcuffs)) - W.forceMove(get_turf(src)) - continue - if(linked_reclaimer) - linked_reclaimer.stored_items[mob_occupant] += W - linked_reclaimer.contents += W - W.forceMove(linked_reclaimer) - else - W.forceMove(src) + if(!is_type_in_typecache(W, telegulag_required_items)) + if(mob_occupant.temporarilyRemoveItemFromInventory(W)) + if(istype(W, /obj/item/restraints/handcuffs)) + W.forceMove(get_turf(src)) + continue + if(linked_reclaimer) + linked_reclaimer.stored_items[mob_occupant] += W + linked_reclaimer.contents += W + W.forceMove(linked_reclaimer) + else + W.forceMove(src) /obj/machinery/gulag_teleporter/proc/handle_prisoner(obj/item/id, datum/data/record/R) if(!ishuman(occupant)) diff --git a/code/game/machinery/harvester.dm b/code/game/machinery/harvester.dm index 1fccadda21..db015eb7c1 100644 --- a/code/game/machinery/harvester.dm +++ b/code/game/machinery/harvester.dm @@ -138,7 +138,7 @@ to_chat(user, "[src] is currently occupied!") return if(state_open) - to_chat(user, "[src] must be closed to [panel_open ? "close" : "open"] it's maintenance hatch!") + to_chat(user, "[src] must be closed to [panel_open ? "close" : "open"] its maintenance hatch!") return if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), I)) return diff --git a/code/game/machinery/limbgrower.dm b/code/game/machinery/limbgrower.dm index 59806e97a4..88ab4ec6f8 100644 --- a/code/game/machinery/limbgrower.dm +++ b/code/game/machinery/limbgrower.dm @@ -27,8 +27,10 @@ "human", "lizard", "fly", - "moth", + "insect", "plasmaman", + "mammal", + "xeno", "other" ) diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 90166dacf0..d27357d654 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -210,13 +210,13 @@ add_fingerprint(user) /obj/machinery/suit_storage_unit/proc/cook() + var/mob/living/mob_occupant = occupant if(uv_cycles) uv_cycles-- uv = TRUE locked = TRUE update_icon() if(occupant) - var/mob/living/mob_occupant = occupant if(uv_super) mob_occupant.adjustFireLoss(rand(20, 36)) else @@ -246,9 +246,25 @@ else visible_message("[src]'s door slides open, barraging you with the nauseating smell of charred flesh.") playsound(src, 'sound/machines/airlockclose.ogg', 25, 1) - for(var/obj/item/I in src) //Scorches away blood and forensic evidence, although the SSU itself is unaffected - SEND_SIGNAL(I, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRONG) - var/datum/component/radioactive/contamination = I.GetComponent(/datum/component/radioactive) + var/list/things_to_clear = list() //Done this way since using GetAllContents on the SSU itself would include circuitry and such. + if(suit) + things_to_clear += suit + things_to_clear += suit.GetAllContents() + if(helmet) + things_to_clear += helmet + things_to_clear += helmet.GetAllContents() + if(mask) + things_to_clear += mask + things_to_clear += mask.GetAllContents() + if(storage) + things_to_clear += storage + things_to_clear += storage.GetAllContents() + if(occupant) + things_to_clear += occupant + things_to_clear += occupant.GetAllContents() + for(var/atom/movable/AM in things_to_clear) //Scorches away blood and forensic evidence, although the SSU itself is unaffected + SEND_SIGNAL(AM, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRONG) + var/datum/component/radioactive/contamination = AM.GetComponent(/datum/component/radioactive) if(contamination) qdel(contamination) open_machine(FALSE) diff --git a/code/game/machinery/telecomms/computers/message.dm b/code/game/machinery/telecomms/computers/message.dm index ebb40148d8..64f4cc7835 100644 --- a/code/game/machinery/telecomms/computers/message.dm +++ b/code/game/machinery/telecomms/computers/message.dm @@ -421,6 +421,7 @@ "name" = "[customsender]", "job" = "[customjob]", "message" = custommessage, + "emoji_message" = emoji_parse(custommessage), "targets" = list("[customrecepient.owner] ([customrecepient.ownjob])") )) // this will log the signal and transmit it to the target diff --git a/code/game/machinery/telecomms/machines/message_server.dm b/code/game/machinery/telecomms/machines/message_server.dm index cc390e1fc0..56870c5198 100644 --- a/code/game/machinery/telecomms/machines/message_server.dm +++ b/code/game/machinery/telecomms/machines/message_server.dm @@ -106,10 +106,11 @@ return "Everyone" return data["targets"][1] -/datum/signal/subspace/pda/proc/format_message() +/datum/signal/subspace/pda/proc/format_message(emojify = FALSE) + var/message = emojify ? data["emoji_message"] : data["message"] if (logged && data["photo"]) - return "\"[data["message"]]\" (Photo)" - return "\"[data["message"]]\"" + return "\"[message]\" (Photo)" + return "\"[message]\"" /datum/signal/subspace/pda/broadcast() if (!logged) // Can only go through if a message server logs it diff --git a/code/game/mecha/combat/gygax.dm b/code/game/mecha/combat/gygax.dm index f9fa2544b8..564f308df3 100644 --- a/code/game/mecha/combat/gygax.dm +++ b/code/game/mecha/combat/gygax.dm @@ -6,6 +6,7 @@ dir_in = 1 //Facing North. max_integrity = 250 deflect_chance = 5 + force = 20 armor = list("melee" = 25, "bullet" = 20, "laser" = 30, "energy" = 15, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) max_temperature = 25000 infra_luminosity = 6 @@ -13,6 +14,7 @@ internal_damage_threshold = 35 max_equip = 3 step_energy_drain = 3 + leg_overload_coeff = 300 /obj/mecha/combat/gygax/dark desc = "A lightweight exosuit, painted in a dark scheme. This model appears to have some modifications." @@ -20,6 +22,7 @@ icon_state = "darkgygax" max_integrity = 300 deflect_chance = 15 + force = 25 armor = list("melee" = 40, "bullet" = 40, "laser" = 50, "energy" = 35, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) max_temperature = 35000 leg_overload_coeff = 100 diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm index 21bbdfd14b..ae3fdb15b4 100644 --- a/code/game/mecha/equipment/tools/medical_tools.dm +++ b/code/game/mecha/equipment/tools/medical_tools.dm @@ -166,7 +166,7 @@ Toxin Content: [patient.getToxLoss()]%
Burn Severity: [patient.getFireLoss()]%
[patient.getCloneLoss() ? "Subject appears to have cellular damage." : ""]
- [patient.getBrainLoss() ? "Significant brain damage detected." : ""]
+ [patient.getOrganLoss(ORGAN_SLOT_BRAIN) ? "Significant brain damage detected." : ""]
[length(patient.get_traumas()) ? "Brain Traumas detected." : ""]
"} diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index 2e5a13beb2..9f8f3ef742 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -279,12 +279,12 @@ name = "\improper Melon Seed \"Scattershot\"" desc = "A weapon for combat exosuits. Shoots a spread of pellets, shaped as seed." icon_state = "mecha_scatter" - equip_cooldown = 30 + equip_cooldown = 20 projectile = /obj/item/projectile/bullet/seed - projectiles = 4 - projectile_energy_cost = 55 + projectiles = 20 + projectile_energy_cost = 25 projectiles_per_shot = 10 - variance = 20 + variance = 25 harmful = TRUE /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm index cc69b88cc4..9f072c48b6 100644 --- a/code/game/objects/effects/decals/cleanable/misc.dm +++ b/code/game/objects/effects/decals/cleanable/misc.dm @@ -42,6 +42,9 @@ /obj/effect/decal/cleanable/glass/ex_act() qdel(src) +/obj/effect/decal/cleanable/glass/plasma + icon_state = "plasmatiny" + /obj/effect/decal/cleanable/dirt name = "dirt" desc = "Someone should clean that up." diff --git a/code/game/objects/effects/decals/turfdecal/tilecoloring.dm b/code/game/objects/effects/decals/turfdecal/tilecoloring.dm index 1708753dc1..1e755bc7e9 100644 --- a/code/game/objects/effects/decals/turfdecal/tilecoloring.dm +++ b/code/game/objects/effects/decals/turfdecal/tilecoloring.dm @@ -36,4 +36,203 @@ /obj/effect/turf_decal/tile/neutral name = "neutral corner" color = "#D4D4D4" - alpha = 50 \ No newline at end of file + alpha = 50 + +/obj/effect/turf_decal/trimline + layer = TURF_PLATING_DECAL_LAYER + alpha = 110 + icon_state = "trimline_box" + +/obj/effect/turf_decal/trimline/white + color = "#FFFFFF" + +/obj/effect/turf_decal/trimline/white/line + name = "trim decal" + icon_state = "trimline" + +/obj/effect/turf_decal/trimline/white/corner + icon_state = "trimline_corner" + +/obj/effect/turf_decal/trimline/white/end + icon_state = "trimline_end" + +/obj/effect/turf_decal/trimline/white/filled + icon_state = "trimline_box_fill" + +/obj/effect/turf_decal/trimline/white/filled/line + icon_state = "trimline_fill" + +/obj/effect/turf_decal/trimline/white/filled/corner + icon_state = "trimline_corner_fill" + +/obj/effect/turf_decal/trimline/white/filled/end + icon_state = "trimline_end_fill" + +/obj/effect/turf_decal/trimline/red + color = "#DE3A3A" + +/obj/effect/turf_decal/trimline/red/line + icon_state = "trimline" + +/obj/effect/turf_decal/trimline/red/corner + icon_state = "trimline_corner" + +/obj/effect/turf_decal/trimline/red/end + icon_state = "trimline_end" + +/obj/effect/turf_decal/trimline/red/filled + icon_state = "trimline_box_fill" + +/obj/effect/turf_decal/trimline/red/filled/line + icon_state = "trimline_fill" + +/obj/effect/turf_decal/trimline/red/filled/corner + icon_state = "trimline_corner_fill" + +/obj/effect/turf_decal/trimline/red/filled/end + icon_state = "trimline_end_fill" + +/obj/effect/turf_decal/trimline/green + color = "#9FED58" + +/obj/effect/turf_decal/trimline/green/line + icon_state = "trimline" + +/obj/effect/turf_decal/trimline/green/corner + icon_state = "trimline_corner" + +/obj/effect/turf_decal/trimline/green/end + icon_state = "trimline_end" + +/obj/effect/turf_decal/trimline/green/filled + icon_state = "trimline_box_fill" + +/obj/effect/turf_decal/trimline/green/filled/line + icon_state = "trimline_fill" + +/obj/effect/turf_decal/trimline/green/filled/corner + icon_state = "trimline_corner_fill" + +/obj/effect/turf_decal/trimline/green/filled/end + icon_state = "trimline_end_fill" + +/obj/effect/turf_decal/trimline/blue + color = "#52B4E9" + +/obj/effect/turf_decal/trimline/blue/line + icon_state = "trimline" + +/obj/effect/turf_decal/trimline/blue/corner + icon_state = "trimline_corner" + +/obj/effect/turf_decal/trimline/blue/end + icon_state = "trimline_end" + +/obj/effect/turf_decal/trimline/blue/filled + icon_state = "trimline_box_fill" + +/obj/effect/turf_decal/trimline/blue/filled/line + icon_state = "trimline_fill" + +/obj/effect/turf_decal/trimline/blue/filled/corner + icon_state = "trimline_corner_fill" + +/obj/effect/turf_decal/trimline/blue/filled/end + icon_state = "trimline_end_fill" + +/obj/effect/turf_decal/trimline/yellow + color = "#EFB341" + +/obj/effect/turf_decal/trimline/yellow/line + icon_state = "trimline" + +/obj/effect/turf_decal/trimline/yellow/corner + icon_state = "trimline_corner" + +/obj/effect/turf_decal/trimline/yellow/end + icon_state = "trimline_end" + +/obj/effect/turf_decal/trimline/yellow/filled + icon_state = "trimline_box_fill" + +/obj/effect/turf_decal/trimline/yellow/filled/line + icon_state = "trimline_fill" + +/obj/effect/turf_decal/trimline/yellow/filled/corner + icon_state = "trimline_corner_fill" + +/obj/effect/turf_decal/trimline/yellow/filled/end + icon_state = "trimline_end_fill" + +/obj/effect/turf_decal/trimline/purple + color = "#D381C9" + +/obj/effect/turf_decal/trimline/purple/line + icon_state = "trimline" + +/obj/effect/turf_decal/trimline/purple/corner + icon_state = "trimline_corner" + +/obj/effect/turf_decal/trimline/purple/end + icon_state = "trimline_end" + +/obj/effect/turf_decal/trimline/purple/filled + icon_state = "trimline_box_fill" + +/obj/effect/turf_decal/trimline/purple/filled/line + icon_state = "trimline_fill" + +/obj/effect/turf_decal/trimline/purple/filled/corner + icon_state = "trimline_corner_fill" + +/obj/effect/turf_decal/trimline/purple/filled/end + icon_state = "trimline_end_fill" + +/obj/effect/turf_decal/trimline/brown + color = "#A46106" + +/obj/effect/turf_decal/trimline/brown/line + icon_state = "trimline" + +/obj/effect/turf_decal/trimline/brown/corner + icon_state = "trimline_corner" + +/obj/effect/turf_decal/trimline/brown/end + icon_state = "trimline_end" + +/obj/effect/turf_decal/trimline/brown/filled + icon_state = "trimline_box_fill" + +/obj/effect/turf_decal/trimline/brown/filled/line + icon_state = "trimline_fill" + +/obj/effect/turf_decal/trimline/brown/filled/corner + icon_state = "trimline_corner_fill" + +/obj/effect/turf_decal/trimline/brown/filled/end + icon_state = "trimline_end_fill" + +/obj/effect/turf_decal/trimline/neutral + color = "#D4D4D4" + alpha = 50 + +/obj/effect/turf_decal/trimline/neutral/line + icon_state = "trimline" + +/obj/effect/turf_decal/trimline/neutral/corner + icon_state = "trimline_corner" + +/obj/effect/turf_decal/trimline/neutral/end + icon_state = "trimline_end" + +/obj/effect/turf_decal/trimline/neutral/filled + icon_state = "trimline_box_fill" + +/obj/effect/turf_decal/trimline/neutral/filled/line + icon_state = "trimline_fill" + +/obj/effect/turf_decal/trimline/neutral/filled/corner + icon_state = "trimline_corner_fill" + +/obj/effect/turf_decal/trimline/brown/filled/end + icon_state = "trimline_end_fill" \ No newline at end of file diff --git a/code/game/objects/effects/spawners/bundle.dm b/code/game/objects/effects/spawners/bundle.dm index 2fe8d2a460..b9acba70d9 100644 --- a/code/game/objects/effects/spawners/bundle.dm +++ b/code/game/objects/effects/spawners/bundle.dm @@ -133,7 +133,7 @@ /obj/effect/spawner/bundle/costume/holiday_priest name = "holiday priest costume spawner" items = list( - /obj/item/clothing/suit/holidaypriest) + /obj/item/clothing/suit/chaplain/holidaypriest) /obj/effect/spawner/bundle/costume/marisawizard name = "marisa wizard costume spawner" diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 566a64577d..b1e5505fa8 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -68,6 +68,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) var/strip_delay = 40 //In deciseconds, how long an item takes to remove from another person var/breakouttime = 0 var/list/materials + var/reskinned = FALSE var/list/attack_verb //Used in attackby() to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]" var/list/species_exception = null // list() of species types, if a species cannot put items in a certain slot, but species type is in list, it will be able to wear that item @@ -513,12 +514,12 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) log_combat(user, M, "attacked", "[src.name]", "(INTENT: [uppertext(user.a_intent)])") - M.adjust_blurriness(3) - M.adjust_eye_damage(rand(2,4)) var/obj/item/organ/eyes/eyes = M.getorganslot(ORGAN_SLOT_EYES) if (!eyes) return - if(eyes.eye_damage >= 10) + M.adjust_blurriness(3) + eyes.applyOrganDamage(rand(2,4)) + if(eyes.damage >= 10) M.adjust_blurriness(15) if(M.stat != DEAD) to_chat(M, "Your eyes start to bleed profusely!") @@ -532,7 +533,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) M.adjust_blurriness(10) M.Unconscious(20) M.Knockdown(40) - if (prob(eyes.eye_damage - 10 + 1)) + if (prob(eyes.damage - 10 + 1)) M.become_blind(EYE_DAMAGE) to_chat(M, "You go blind!") diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm index 1ff3c7df9f..cf706359f7 100644 --- a/code/game/objects/items/RCD.dm +++ b/code/game/objects/items/RCD.dm @@ -37,6 +37,7 @@ RLD var/has_ammobar = FALSE //controls whether or not does update_icon apply ammo indicator overlays var/ammo_sections = 10 //amount of divisions in the ammo indicator overlay/number of ammo indicator states var/custom_range = 7 + var/upgrade = FALSE /obj/item/construction/Initialize() . = ..() @@ -82,6 +83,11 @@ RLD loaded = loadwithsheets(W, sheetmultiplier * 0.25, user) // 1 matter for 1 floortile, as 4 tiles are produced from 1 metal if(loaded) to_chat(user, "[src] now holds [matter]/[max_matter] matter-units.") + else if(istype(W, /obj/item/rcd_upgrade)) + to_chat(user, "You upgrade the RCD with the [W]!") + upgrade = TRUE + playsound(src.loc, 'sound/machines/click.ogg', 50, 1) + qdel(W) else return ..() update_icon() //ensures that ammo counters (if present) get updated @@ -148,6 +154,7 @@ RLD has_ammobar = TRUE var/mode = 1 var/ranged = FALSE + var/computer_dir = 1 var/airlock_type = /obj/machinery/door/airlock var/airlock_glass = FALSE // So the floor's rcd_act knows how much ammo to use var/window_type = /obj/structure/window/fulltile @@ -270,6 +277,28 @@ RLD return FALSE return TRUE +/obj/item/construction/rcd/proc/change_computer_dir(mob/user) + if(!user) + return + var/list/computer_dirs = list( + "NORTH" = image(icon = 'icons/mob/radial.dmi', icon_state = "cnorth"), + "EAST" = image(icon = 'icons/mob/radial.dmi', icon_state = "ceast"), + "SOUTH" = image(icon = 'icons/mob/radial.dmi', icon_state = "csouth"), + "WEST" = image(icon = 'icons/mob/radial.dmi', icon_state = "cwest") + ) + var/computerdirs = show_radial_menu(user, src, computer_dirs, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = TRUE) + if(!check_menu(user)) + return + switch(computerdirs) + if("NORTH") + computer_dir = 1 + if("EAST") + computer_dir = 4 + if("SOUTH") + computer_dir = 2 + if("WEST") + computer_dir = 8 + /obj/item/construction/rcd/proc/change_airlock_setting(mob/user) if(!user) return @@ -434,10 +463,15 @@ RLD ..() var/list/choices = list( "Airlock" = image(icon = 'icons/mob/radial.dmi', icon_state = "airlock"), - "Deconstruct" = image(icon= 'icons/mob/radial.dmi', icon_state = "delete"), "Grilles & Windows" = image(icon = 'icons/mob/radial.dmi', icon_state = "grillewindow"), "Floors & Walls" = image(icon = 'icons/mob/radial.dmi', icon_state = "wallfloor") ) + if(upgrade) + choices += list( + "Deconstruct" = image(icon= 'icons/mob/radial.dmi', icon_state = "delete"), + "Machine Frames" = image(icon = 'icons/mob/radial.dmi', icon_state = "machine"), + "Computer Frames" = image(icon = 'icons/mob/radial.dmi', icon_state = "computer_dir"), + ) if(mode == RCD_AIRLOCK) choices += list( "Change Access" = image(icon = 'icons/mob/radial.dmi', icon_state = "access"), @@ -459,6 +493,12 @@ RLD mode = RCD_DECONSTRUCT if("Grilles & Windows") mode = RCD_WINDOWGRILLE + if("Machine Frames") + mode = RCD_MACHINE + if("Computer Frames") + mode = RCD_COMPUTER + change_computer_dir(user) + return if("Change Access") change_airlock_access(user) return @@ -511,6 +551,7 @@ RLD no_ammo_message = "Insufficient charge." desc = "A device used to rapidly build walls and floors." canRturf = TRUE + upgrade = TRUE /obj/item/construction/rcd/borg/useResource(amount, mob/user) @@ -542,6 +583,9 @@ RLD /obj/item/construction/rcd/loaded matter = 160 +/obj/item/construction/rcd/loaded/upgraded + upgrade = TRUE + /obj/item/construction/rcd/combat name = "Combat RCD" desc = "A device used to rapidly build and deconstruct. Reload with metal, plasteel, glass or compressed matter cartridges. This RCD has been upgraded to be able to remove Rwalls!" @@ -582,7 +626,7 @@ RLD name = "admin RCD" max_matter = INFINITY matter = INFINITY - + upgrade = TRUE // Ranged RCD @@ -776,6 +820,12 @@ RLD return TRUE return FALSE +/obj/item/rcd_upgrade + name = "RCD advanced design disk" + desc = "It contains the design for machine frames, computer frames, and deconstruction." + icon = 'icons/obj/module.dmi' + icon_state = "datadisk3" + #undef GLOW_MODE #undef LIGHT_MODE #undef REMOVE_MODE diff --git a/code/game/objects/items/RSF.dm b/code/game/objects/items/RSF.dm index 01205ee889..9c343c2e06 100644 --- a/code/game/objects/items/RSF.dm +++ b/code/game/objects/items/RSF.dm @@ -182,7 +182,7 @@ RSF to_chat(user, "Fabricating Cookie..") var/obj/item/reagent_containers/food/snacks/cookie/S = new /obj/item/reagent_containers/food/snacks/cookie(T) if(toxin) - S.reagents.add_reagent("chloralhydratedelayed", 10) + S.reagents.add_reagent("chloralhydrate", 10) if (iscyborg(user)) var/mob/living/silicon/robot/R = user R.cell.charge -= 100 diff --git a/code/game/objects/items/body_egg.dm b/code/game/objects/items/body_egg.dm index 80fc0f43fd..ea72197cf0 100644 --- a/code/game/objects/items/body_egg.dm +++ b/code/game/objects/items/body_egg.dm @@ -17,26 +17,27 @@ /obj/item/organ/body_egg/Insert(var/mob/living/carbon/M, special = 0) ..() ADD_TRAIT(owner, TRAIT_XENO_HOST, TRAIT_GENERIC) - START_PROCESSING(SSobj, src) owner.med_hud_set_status() INVOKE_ASYNC(src, .proc/AddInfectionImages, owner) /obj/item/organ/body_egg/Remove(var/mob/living/carbon/M, special = 0) - STOP_PROCESSING(SSobj, src) if(owner) REMOVE_TRAIT(owner, TRAIT_XENO_HOST, TRAIT_GENERIC) owner.med_hud_set_status() INVOKE_ASYNC(src, .proc/RemoveInfectionImages, owner) ..() -/obj/item/organ/body_egg/process() +/obj/item/organ/body_egg/on_death() + . = ..() if(!owner) return - if(!(src in owner.internal_organs)) - Remove(owner) - return egg_process() +/obj/item/organ/body_egg/on_life() + . = ..() + egg_process() + + /obj/item/organ/body_egg/proc/egg_process() return diff --git a/code/game/objects/items/circuitboards/computer_circuitboards.dm b/code/game/objects/items/circuitboards/computer_circuitboards.dm index bb45f80f2d..ac7879f9ec 100644 --- a/code/game/objects/items/circuitboards/computer_circuitboards.dm +++ b/code/game/objects/items/circuitboards/computer_circuitboards.dm @@ -59,7 +59,7 @@ name = "Department Management Console (Computer Board)" build_path = /obj/machinery/computer/card/minor var/target_dept = 1 - var/list/dept_list = list("General","Security","Medical","Science","Engineering") + var/list/dept_list = list("Civilian","Security","Medical","Science","Engineering","Cargo") /obj/item/circuitboard/computer/card/minor/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/screwdriver)) @@ -162,10 +162,11 @@ /obj/item/circuitboard/computer/prisoner name = "Prisoner Management Console (Computer Board)" - build_path = /obj/machinery/computer/prisoner + build_path = /obj/machinery/computer/prisoner/management + /obj/item/circuitboard/computer/gulag_teleporter_console name = "Labor Camp teleporter console (Computer Board)" - build_path = /obj/machinery/computer/gulag_teleporter_computer + build_path = /obj/machinery/computer/prisoner/gulag_teleporter_computer /obj/item/circuitboard/computer/rdconsole/production name = "R&D Console Production Only (Computer Board)" @@ -373,4 +374,4 @@ /obj/item/circuitboard/computer/nanite_cloud_controller name = "Nanite Cloud Control (Computer Board)" - build_path = /obj/machinery/computer/nanite_cloud_controller \ No newline at end of file + build_path = /obj/machinery/computer/nanite_cloud_controller diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm index 2a01a7e18f..639d570462 100644 --- a/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -399,6 +399,7 @@ /obj/machinery/smartfridge/food = "food", /obj/machinery/smartfridge/drinks = "drinks", /obj/machinery/smartfridge/extract = "slimes", + /obj/machinery/smartfridge/organ = "organs", /obj/machinery/smartfridge/chemistry = "chems", /obj/machinery/smartfridge/chemistry/virology = "viruses", /obj/machinery/smartfridge/disks = "disks") diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 99d6c874e8..f2531d3dfd 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -1,6 +1,9 @@ #define RANDOM_GRAFFITI "Random Graffiti" #define RANDOM_LETTER "Random Letter" +#define RANDOM_PUNCTUATION "Random Punctuation" #define RANDOM_NUMBER "Random Number" +#define RANDOM_SYMBOL "Random Symbol" +#define RANDOM_DRAWING "Random Drawing" #define RANDOM_ORIENTED "Random Oriented" #define RANDOM_RUNE "Random Rune" #define RANDOM_ANY "Random Anything" @@ -32,16 +35,16 @@ var/drawtype var/text_buffer = "" - var/list/graffiti = list("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa","body","cyka","arrow","star","poseur tag","prolizard","antilizard", "tile") //cit edit - var/list/letters = list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z") - var/list/numerals = list("0","1","2","3","4","5","6","7","8","9") - var/list/oriented = list("arrow","body") // These turn to face the same way as the drawer - var/list/runes = list("rune1","rune2","rune3","rune4","rune5","rune6") - var/list/randoms = list(RANDOM_ANY, RANDOM_RUNE, RANDOM_ORIENTED, - RANDOM_NUMBER, RANDOM_GRAFFITI, RANDOM_LETTER) - var/list/graffiti_large_h = list("yiffhell", "secborg", "paint") + var/static/list/graffiti = list("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa","body","cyka","star","poseur tag","prolizard","antilizard", "tile") + var/static/list/symbols = list("danger","firedanger","electricdanger","biohazard","radiation","safe","evac","space","med","trade","shop","food","peace","like","skull","nay","heart","credit") + var/static/list/drawings = list("smallbrush","brush","largebrush","splatter","snake","stickman","carp","ghost","clown","taser","disk","fireaxe","toolbox","corgi","cat","toilet","blueprint","beepsky","scroll","bottle","shotgun") + var/static/list/oriented = list("arrow","line","thinline","shortline","body","chevron","footprint","clawprint","pawprint") // These turn to face the same way as the drawer + var/static/list/runes = list("rune1","rune2","rune3","rune4","rune5","rune6") + var/static/list/randoms = list(RANDOM_ANY, RANDOM_RUNE, RANDOM_ORIENTED, + RANDOM_NUMBER, RANDOM_GRAFFITI, RANDOM_LETTER, RANDOM_SYMBOL, RANDOM_PUNCTUATION, RANDOM_DRAWING) + var/static/list/graffiti_large_h = list("yiffhell", "secborg", "paint") - var/list/all_drawables + var/static/list/all_drawables = graffiti + symbols + drawings + oriented + runes + graffiti_large_h var/paint_mode = PAINT_NORMAL @@ -54,8 +57,6 @@ var/instant = FALSE var/self_contained = TRUE // If it deletes itself when it is empty - var/list/validSurfaces = list(/turf/open/floor) - var/edible = TRUE // That doesn't mean eating it is a good idea var/list/reagent_contents = list("nutriment" = 1) @@ -71,6 +72,9 @@ var/datum/team/gang/gang //For marking territory. var/gang_tag_delay = 30 //this is the delay for gang mode tag applications on anything that gang = true on. +/obj/item/toy/crayon/proc/isValidSurface(surface) + return istype(surface, /turf/open/floor) + /obj/item/toy/crayon/suicide_act(mob/user) user.visible_message("[user] is jamming [src] up [user.p_their()] nose and into [user.p_their()] brain. It looks like [user.p_theyre()] trying to commit suicide!") return (BRUTELOSS|OXYLOSS) @@ -81,7 +85,6 @@ if(name == "crayon") name = "[item_color] crayon" - all_drawables = graffiti + letters + numerals + oriented + runes + graffiti_large_h drawtype = pick(all_drawables) refill() @@ -153,55 +156,62 @@ to_chat(user, "The cap on [src] is now [is_capped ? "on" : "off"].") update_icon() -/obj/item/toy/crayon/ui_data() - var/list/data = list() - data["drawables"] = list() - var/list/D = data["drawables"] +/obj/item/toy/crayon/proc/staticDrawables() + + . = list() var/list/g_items = list() - D += list(list("name" = "Graffiti", "items" = g_items)) + . += list(list("name" = "Graffiti", "items" = g_items)) for(var/g in graffiti) g_items += list(list("item" = g)) var/list/glh_items = list() - D += list(list("name" = "Graffiti Large Horizontal", "items" = glh_items)) + . += list(list("name" = "Graffiti Large Horizontal", "items" = glh_items)) for(var/glh in graffiti_large_h) glh_items += list(list("item" = glh)) - var/list/L_items = list() - D += list(list("name" = "Letters", "items" = L_items)) - for(var/L in letters) - L_items += list(list("item" = L)) + var/list/S_items = list() + . += list(list("name" = "Symbols", "items" = S_items)) + for(var/S in symbols) + S_items += list(list("item" = S)) - var/list/N_items = list() - D += list(list(name = "Numerals", "items" = N_items)) - for(var/N in numerals) - N_items += list(list("item" = N)) + var/list/D_items = list() + . += list(list("name" = "Drawings", "items" = D_items)) + for(var/D in drawings) + D_items += list(list("item" = D)) var/list/O_items = list() - D += list(list(name = "Oriented", "items" = O_items)) + . += list(list(name = "Oriented", "items" = O_items)) for(var/O in oriented) O_items += list(list("item" = O)) var/list/R_items = list() - D += list(list(name = "Runes", "items" = R_items)) + . += list(list(name = "Runes", "items" = R_items)) for(var/R in runes) R_items += list(list("item" = R)) var/list/rand_items = list() - D += list(list(name = "Random", "items" = rand_items)) + . += list(list(name = "Random", "items" = rand_items)) for(var/i in randoms) rand_items += list(list("item" = i)) - data["selected_stencil"] = drawtype - data["text_buffer"] = text_buffer - data["has_cap"] = has_cap - data["is_capped"] = is_capped - data["can_change_colour"] = can_change_colour - data["current_colour"] = paint_color +/obj/item/toy/crayon/ui_data() - return data + var/static/list/crayon_drawables + + if (!crayon_drawables) + crayon_drawables = staticDrawables() + + . = list() + .["drawables"] = crayon_drawables + .["selected_stencil"] = drawtype + .["text_buffer"] = text_buffer + + .["has_cap"] = has_cap + .["is_capped"] = is_capped + .["can_change_colour"] = can_change_colour + .["current_colour"] = paint_color /obj/item/toy/crayon/ui_act(action, list/params) if(..()) @@ -216,6 +226,7 @@ if(stencil in all_drawables + randoms) drawtype = stencil . = TRUE + text_buffer = "" if(stencil in graffiti_large_h) paint_mode = PAINT_LARGE_HORIZONTAL text_buffer = "" @@ -223,8 +234,13 @@ paint_mode = PAINT_NORMAL if("select_colour") if(can_change_colour) - paint_color = input(usr,"","Choose Color",paint_color) as color|null - . = TRUE + var/chosen_colour = input(usr,"","Choose Color",paint_color) as color|null + + if (!isnull(chosen_colour)) + paint_color = chosen_colour + . = TRUE + else + . = FALSE if("enter_text") var/txt = stripped_input(usr,"Choose what to write.", "Scribbles",default = text_buffer) @@ -235,18 +251,16 @@ update_icon() /obj/item/toy/crayon/proc/crayon_text_strip(text) - var/list/base = string2charlist(lowertext(text)) - var/list/out = list() - for(var/a in base) - if(a in (letters|numerals)) - out += a - return jointext(out,"") + var/static/regex/crayon_r = new /regex(@"[^\w!?,.=%#&+\/\-]") + return replacetext(lowertext(text), crayon_r, "") /obj/item/toy/crayon/afterattack(atom/target, mob/user, proximity, params) . = ..() if(!proximity || !check_allowed_items(target)) return + var/static/list/punctuation = list("!","?",".",",","/","+","-","=","%","#","&") + var/cost = 1 if(paint_mode == PAINT_LARGE_HORIZONTAL) cost = 5 @@ -264,13 +278,19 @@ if(istype(target, /obj/effect/decal/cleanable)) target = target.loc - if(!is_type_in_list(target,validSurfaces)) + if(!isValidSurface(target)) return var/drawing = drawtype switch(drawtype) if(RANDOM_LETTER) - drawing = pick(letters) + drawing = ascii2text(rand(97, 122)) // a-z + if(RANDOM_PUNCTUATION) + drawing = pick(punctuation) + if(RANDOM_SYMBOL) + drawing = pick(symbols) + if(RANDOM_DRAWING) + drawing = pick(drawings) if(RANDOM_GRAFFITI) drawing = pick(graffiti) if(RANDOM_RUNE) @@ -278,17 +298,23 @@ if(RANDOM_ORIENTED) drawing = pick(oriented) if(RANDOM_NUMBER) - drawing = pick(numerals) + drawing = ascii2text(rand(48, 57)) // 0-9 if(RANDOM_ANY) drawing = pick(all_drawables) var/temp = "rune" - if(drawing in letters) + if(is_alpha(drawing)) temp = "letter" - else if(drawing in graffiti) - temp = "graffiti" - else if(drawing in numerals) + else if(is_digit(drawing)) temp = "number" + else if(drawing in punctuation) + temp = "punctuation mark" + else if(drawing in symbols) + temp = "symbol" + else if(drawing in drawings) + temp = "drawing" + else if(drawing in graffiti|oriented) + temp = "graffiti" // If a gang member is using a gang spraycan, it'll behave differently var/gang_mode = FALSE @@ -338,7 +364,7 @@ return if(length(text_buffer)) - drawing = copytext(text_buffer,1,2) + drawing = text_buffer[1] var/list/turf/affected_turfs = list() @@ -362,7 +388,7 @@ if(PAINT_LARGE_HORIZONTAL) var/turf/left = locate(target.x-1,target.y,target.z) var/turf/right = locate(target.x+1,target.y,target.z) - if(is_type_in_list(left, validSurfaces) && is_type_in_list(right, validSurfaces)) + if(isValidSurface(left) && isValidSurface(right)) var/obj/effect/decal/cleanable/crayon/C = new(left, paint_color, drawing, temp, graf_rot, PAINT_LARGE_HORIZONTAL_ICON) C.add_hiddenprint(user) affected_turfs += left @@ -377,8 +403,9 @@ else to_chat(user, "You spray a [temp] on \the [target.name]") - if(length(text_buffer)) + if(length(text_buffer) > 1) text_buffer = copytext(text_buffer,2) + SStgui.update_uis(src) if(post_noise) audible_message("You hear spraying.") @@ -516,7 +543,7 @@ charges = -1 -/obj/item/toy/crayon/rainbow/afterattack(atom/target, mob/user, proximity) +/obj/item/toy/crayon/rainbow/afterattack(atom/target, mob/user, proximity, params) paint_color = rgb(rand(0,255), rand(0,255), rand(0,255)) . = ..() @@ -591,12 +618,14 @@ can_change_colour = TRUE gang = TRUE //Gang check is true for all things upon the honored hierarchy of spraycans, except those that are FALSE. - validSurfaces = list(/turf/open/floor, /turf/closed/wall) reagent_contents = list("welding_fuel" = 1, "ethanol" = 1) pre_noise = TRUE post_noise = FALSE +/obj/item/toy/crayon/spraycan/isValidSurface(surface) + return (istype(surface, /turf/open/floor) || istype(surface, /turf/closed/wall)) + /obj/item/toy/crayon/spraycan/suicide_act(mob/user) var/mob/living/carbon/human/H = user if(is_capped || !actually_paints) @@ -622,8 +651,8 @@ return (OXYLOSS) -/obj/item/toy/crayon/spraycan/New() - ..() +/obj/item/toy/crayon/spraycan/Initialize() + . = ..() // If default crayon red colour, pick a more fun spraycan colour if(!paint_color) paint_color = pick("#DA0000","#FF9300","#FFF200","#A8E61D","#00B7EF", @@ -640,7 +669,7 @@ to_chat(user, "It is empty.") to_chat(user, "Alt-click [src] to [ is_capped ? "take the cap off" : "put the cap on"].") -/obj/item/toy/crayon/spraycan/afterattack(atom/target, mob/user, proximity) +/obj/item/toy/crayon/spraycan/afterattack(atom/target, mob/user, proximity, params) if(!proximity) return @@ -656,8 +685,7 @@ playsound(user.loc, 'sound/effects/spray.ogg', 25, 1, 5) var/mob/living/carbon/C = target - user.visible_message("[user] sprays [src] into the face of [target]!") - to_chat(target, "[user] sprays [src] into your face!") + C.visible_message("[user] sprays [src] into the face of [C]!", "[user] sprays [src] into your face!") if(C.client) C.blur_eyes(3) @@ -678,13 +706,20 @@ return - if(istype(target, /obj/structure/window)) + if(isobj(target)) if(actually_paints) + if(color_hex2num(paint_color) < 350 && !istype(target, /obj/structure/window) && !istype(target, /obj/effect/decal/cleanable/crayon)) //Colors too dark are rejected + to_chat(usr, "A color that dark on an object like this? Surely not...") + return FALSE + target.add_atom_colour(paint_color, WASHABLE_COLOUR_PRIORITY) - if(color_hex2num(paint_color) < 255) - target.set_opacity(255) - else - target.set_opacity(initial(target.opacity)) + + if(istype(target, /obj/structure/window)) + if(color_hex2num(paint_color) < 255) + target.set_opacity(255) + else + target.set_opacity(initial(target.opacity)) + . = use_charges(user, 2) var/fraction = min(1, . / reagents.maximum_volume) reagents.reaction(target, TOUCH, fraction * volume_multiplier) @@ -692,6 +727,7 @@ if(pre_noise || post_noise) playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5) + user.visible_message("[user] coats [target] with spray paint!", "You coat [target] with spray paint.") return . = ..() @@ -709,7 +745,7 @@ desc = "A metallic container containing shiny synthesised paint." charges = -1 -/obj/item/toy/crayon/spraycan/borg/afterattack(atom/target,mob/user,proximity) +/obj/item/toy/crayon/spraycan/borg/afterattack(atom/target,mob/user,proximity, params) var/diff = ..() if(!iscyborg(user)) to_chat(user, "How did you get this?") @@ -754,7 +790,9 @@ reagent_contents = list("lube" = 1, "banana" = 1) volume_multiplier = 5 - validSurfaces = list(/turf/open/floor) + +/obj/item/toy/crayon/spraycan/lubecan/isValidSurface(surface) + return istype(surface, /turf/open/floor) /obj/item/toy/crayon/spraycan/mimecan name = "silent spraycan" @@ -792,8 +830,16 @@ if(user.mind && user.mind.has_antag_datum(/datum/antagonist/gang) || isobserver(user)) to_chat(user, "This spraycan has been specially modified with a stage 2 nozzle kit, making it faster.") +/obj/item/toy/crayon/spraycan/infinite + name = "infinite spraycan" + charges = -1 + desc = "Now with 30% more bluespace technology." + #undef RANDOM_GRAFFITI #undef RANDOM_LETTER +#undef RANDOM_PUNCTUATION +#undef RANDOM_SYMBOL +#undef RANDOM_DRAWING #undef RANDOM_NUMBER #undef RANDOM_ORIENTED #undef RANDOM_RUNE diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm index 5bef86c6d2..a4f286bf88 100644 --- a/code/game/objects/items/defib.dm +++ b/code/game/objects/items/defib.dm @@ -440,9 +440,20 @@ do_help(H, user) -/obj/item/twohanded/shockpaddles/proc/can_defib(mob/living/carbon/H) +/obj/item/twohanded/shockpaddles/proc/can_defib(mob/living/carbon/H) //Our code here is different than tg, if it breaks in testing; BUG_PROBABLE_CAUSE + var/obj/item/organ/heart = H.getorgan(/obj/item/organ/heart) + if(H.suiciding || H.hellbound || HAS_TRAIT(H, TRAIT_HUSK)) + return + if((world.time - H.timeofdeath) > tlimit) + return + if((H.getBruteLoss() >= MAX_REVIVE_BRUTE_DAMAGE) || (H.getFireLoss() >= MAX_REVIVE_FIRE_DAMAGE)) + return + if(!heart || (heart.organ_flags & ORGAN_FAILING)) + return var/obj/item/organ/brain/BR = H.getorgan(/obj/item/organ/brain) - return (!H.suiciding && !(HAS_TRAIT(H, TRAIT_NOCLONE)) && !H.hellbound && ((world.time - H.timeofdeath) < tlimit) && (H.getBruteLoss() < 180) && (H.getFireLoss() < 180) && H.getorgan(/obj/item/organ/heart) && BR && !BR.damaged_brain) + if(QDELETED(BR) || BR.brain_death || (BR.organ_flags & ORGAN_FAILING) || H.suiciding) + return + return TRUE /obj/item/twohanded/shockpaddles/proc/shock_touching(dmg, mob/H) if(req_defib) @@ -557,14 +568,12 @@ if(do_after(user, primetimer, target = H)) //beginning to place the paddles on patient's chest to allow some time for people to move away to stop the process user.visible_message("[user] places [src] on [H]'s chest.", "You place [src] on [H]'s chest.") playsound(src, 'sound/machines/defib_charge.ogg', 75, 0) - var/tplus = world.time - H.timeofdeath - // past this much time the patient is unrecoverable - // (in deciseconds) - // brain damage starts setting in on the patient after - // some time left rotting + // patients rot when they are killed, and die when they are dead + var/tplus = world.time - H.timeofdeath //length of time spent dead var/tloss = deathtimer var/total_burn = 0 var/total_brute = 0 + var/obj/item/organ/heart = H.getorgan(/obj/item/organ/heart) if(do_after(user, primetimer2, target = H)) //placed on chest and short delay to shock for dramatic effect, revive time is 5sec total for(var/obj/item/carried_item in H.contents) if(istype(carried_item, /obj/item/clothing/suit/space)) @@ -589,16 +598,26 @@ failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's soul appears to be on another plane of existence. Further attempts futile." else if (tplus > tlimit) failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Body has decayed for too long. Further attempts futile." - else if (!H.getorgan(/obj/item/organ/heart)) + else if (!heart) failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's heart is missing." + else if (heart.organ_flags & ORGAN_FAILING) + failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's heart too damaged." else if(total_burn >= 180 || total_brute >= 180) failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Severe tissue damage makes recovery of patient impossible via defibrillator. Further attempts futile." else if(H.get_ghost()) failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - No activity in patient's brain. Further attempts may be successful." else var/obj/item/organ/brain/BR = H.getorgan(/obj/item/organ/brain) - if(!BR || BR.damaged_brain) - failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's brain is missing or damaged beyond point of no return. Further attempts futile." + if(BR) //BUG_PROBABLE_CAUSE - slight difference between us and tg + if(BR.organ_flags & ORGAN_FAILING) + failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's brain tissue is damaged making recovery of patient impossible via defibrillator. Further attempts futile." + if(BR.brain_death) + failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's brain damaged beyond point of no return. Further attempts futile." + if(H.suiciding || BR.brainmob?.suiciding) + failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - No intelligence pattern can be detected in patient's brain. Further attempts futile." + else + failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's brain is missing. Further attempts futile." + if(failed) user.visible_message(failed) @@ -623,7 +642,7 @@ H.Jitter(100) SEND_SIGNAL(H, COMSIG_LIVING_MINOR_SHOCK) if(tplus > tloss) - H.adjustBrainLoss( max(0, min(99, ((tlimit - tplus) / tlimit * 100))), 150) + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, max(0, min(99, ((tlimit - tplus) / tlimit * 100))), 150) log_combat(user, H, "revived", defib) if(req_defib) if(defib.healdisk) @@ -641,7 +660,11 @@ playsound(src, 'sound/machines/defib_failed.ogg', 50, 0) else if(H.undergoing_cardiac_arrest()) H.set_heartattack(FALSE) - user.visible_message("[req_defib ? "[defib]" : "[src]"] pings: Patient's heart is now beating again.") + if(!(heart.organ_flags & ORGAN_FAILING)) + H.set_heartattack(FALSE) + user.visible_message("[req_defib ? "[defib]" : "[src]"] pings: Patient's heart is now beating again.") + else + user.visible_message("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed, heart damage detected.") playsound(src, 'sound/machines/defib_zap.ogg', 50, 1, -1) diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 45baa542d4..b0494539ce 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -714,6 +714,7 @@ GLOBAL_LIST_EMPTY(PDAs) return if((last_text && world.time < last_text + 10) || (everyone && last_everyone && world.time < last_everyone + PDA_SPAM_DELAY)) return + var/emoji_message = emoji_parse(message) if(prob(1)) message += "\nSent from my PDA" // Send the signal @@ -734,7 +735,8 @@ GLOBAL_LIST_EMPTY(PDAs) "name" = "[owner]", "job" = "[ownjob]", "message" = message, - "targets" = string_targets + "targets" = string_targets, + "emoji_message" = emoji_message )) if (picture) signal.data["photo"] = picture @@ -751,13 +753,13 @@ GLOBAL_LIST_EMPTY(PDAs) // Log it in our logs tnote += "→ To [target_text]:
[signal.format_message()]
" // Show it to ghosts - var/ghost_message = "[owner] PDA Message --> [target_text]: [signal.format_message()]" + var/ghost_message = "[owner] PDA Message --> [target_text]: [signal.format_message(TRUE)]" for(var/mob/M in GLOB.player_list) if(isobserver(M) && M.client && (M.client.prefs.chat_toggles & CHAT_GHOSTPDA)) to_chat(M, "[FOLLOW_LINK(M, user)] [ghost_message]") // Log in the talk log user.log_talk(message, LOG_PDA, tag="PDA: [initial(name)] to [target_text]") - to_chat(user, "Message sent to [target_text]: \"[message]\"") + to_chat(user, "Message sent to [target_text]: \"[emoji_message]\"") if (!silent) playsound(src, 'sound/machines/terminal_success.ogg', 15, 1) // Reset the photo @@ -787,7 +789,7 @@ GLOBAL_LIST_EMPTY(PDAs) hrefstart = "" hrefend = "" - to_chat(L, "[icon2html(src)] Message from [hrefstart][signal.data["name"]] ([signal.data["job"]])[hrefend], [signal.format_message()] (Reply)") + to_chat(L, "[icon2html(src)] Message from [hrefstart][signal.data["name"]] ([signal.data["job"]])[hrefend], [signal.format_message(TRUE)] (Reply)") update_icon(TRUE) diff --git a/code/game/objects/items/devices/dogborg_sleeper.dm b/code/game/objects/items/devices/dogborg_sleeper.dm index 7f3c7f3bfc..ca1648bb07 100644 --- a/code/game/objects/items/devices/dogborg_sleeper.dm +++ b/code/game/objects/items/devices/dogborg_sleeper.dm @@ -211,7 +211,7 @@ data["occupant"]["toxLoss"] = mob_occupant.getToxLoss() data["occupant"]["fireLoss"] = mob_occupant.getFireLoss() data["occupant"]["cloneLoss"] = mob_occupant.getCloneLoss() - data["occupant"]["brainLoss"] = mob_occupant.getBrainLoss() + data["occupant"]["brainLoss"] = mob_occupant.getOrganLoss(ORGAN_SLOT_BRAIN) data["occupant"]["reagents"] = list() if(mob_occupant.reagents.reagent_list.len) for(var/datum/reagent/R in mob_occupant.reagents.reagent_list) diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index 722510dccb..b937f961c0 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -96,7 +96,7 @@ if(BODY_ZONE_PRECISE_MOUTH) - if((M.head && M.head.flags_cover & HEADCOVERSMOUTH) || (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSMOUTH)) + if(M.is_mouth_covered()) to_chat(user, "You're going to need to remove that [(M.head && M.head.flags_cover & HEADCOVERSMOUTH) ? "helmet" : "mask"] first.") return diff --git a/code/game/objects/items/devices/geiger_counter.dm b/code/game/objects/items/devices/geiger_counter.dm index 38133e37bc..90cdd0386c 100644 --- a/code/game/objects/items/devices/geiger_counter.dm +++ b/code/game/objects/items/devices/geiger_counter.dm @@ -158,7 +158,7 @@ if(!M.radiation) to_chat(user, "[icon2html(src, user)] Radiation levels within normal boundaries.") else - to_chat(user, "[icon2html(src, user)] Subject is irradiated. Radiation levels: [M.radiation].") + to_chat(user, "[icon2html(src, user)] Subject is irradiated. Radiation levels: [M.radiation] rad.") if(rad_strength) to_chat(user, "[icon2html(src, user)] Target contains radioactive contamination. Radioactive strength: [rad_strength]") diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm index a3ab9153ce..207497922e 100644 --- a/code/game/objects/items/devices/multitool.dm +++ b/code/game/objects/items/devices/multitool.dm @@ -32,6 +32,24 @@ var/datum/integrated_io/selected_io = null //functional for integrated circuits. var/mode = 0 +/obj/item/multitool/chaplain + name = "\improper hypertool" + desc = "Used for pulsing wires to test which to cut. Also emits microwaves to fry some brains!" + damtype = BRAIN + force = 18 + armour_penetration = 35 + hitsound = 'sound/effects/sparks4.ogg' + var/chaplain_spawnable = TRUE + total_mass = TOTAL_MASS_MEDIEVAL_WEAPON + throw_speed = 3 + throw_range = 4 + throwforce = 10 + obj_flags = UNIQUE_RENAME + +/obj/item/multitool/chaplain/Initialize() + . = ..() + AddComponent(/datum/component/anti_magic, TRUE, TRUE) + /obj/item/multitool/examine(mob/user) ..() if(selected_io) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 0059938720..7655654f78 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -135,7 +135,7 @@ SLIME SCANNER var/mob/living/carbon/human/H = M if(H.undergoing_cardiac_arrest() && H.stat != DEAD) to_chat(user, "Subject suffering from heart attack: Apply defibrillation or other electric shock immediately!") - if(H.undergoing_liver_failure() && H.stat != DEAD) + if(H.undergoing_liver_failure() && H.stat != DEAD) //might be depreciated BUG_PROBABLE_CAUSE to_chat(user, "Subject is suffering from liver failure: Apply Corazone and begin a liver transplant immediately!") var/msg = "*---------*\nAnalyzing results for [M]:\n\tOverall status: [mob_status]\n" @@ -157,12 +157,8 @@ SLIME SCANNER msg += "\tSubject appears to have [M.getCloneLoss() > 30 ? "Severe" : "Minor"] cellular damage.\n" if(advanced) msg += "\tCellular Damage Level: [M.getCloneLoss()].\n" - if (M.getBrainLoss() >= 200 || !M.getorgan(/obj/item/organ/brain)) - msg += "\tSubject's brain function is non-existent.\n" - else if (M.getBrainLoss() >= 120) - msg += "\tSevere brain damage detected. Subject likely to have mental traumas.\n" - else if (M.getBrainLoss() >= 45) - msg += "\tBrain damage detected.\n" + if (!M.getorgan(/obj/item/organ/brain)) + to_chat(user, "\tSubject lacks a brain.") //Unsure how this won't proc for 50% of the cit playerbase (This is a joke everyone on cit a cute.) if(ishuman(M) && advanced) // Should I make this not advanced? var/mob/living/carbon/human/H = M var/obj/item/organ/liver/L = H.getorganslot("liver") @@ -194,6 +190,14 @@ SLIME SCANNER if(Br.cached_size>5) msg += "\tSubject has a sizeable bosom with a [Br.size] cup.\n" + if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 200 || !M.getorgan(/obj/item/organ/brain)) + msg += "\tSubject's brain function is non-existent.\n" + else if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 120) + msg += "\tSevere brain damage detected. Subject likely to have mental traumas.\n" + else if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 45) + msg += "\tBrain damage detected.\n" + + if(iscarbon(M)) var/mob/living/carbon/C = M if(LAZYLEN(C.get_traumas())) var/list/trauma_text = list() @@ -212,11 +216,10 @@ SLIME SCANNER if(C.roundstart_quirks.len) msg += "\tSubject has the following physiological traits: [C.get_trait_string()].\n" if(advanced) - msg += "\tBrain Activity Level: [(200 - M.getBrainLoss())/2]%.\n" + msg += "\tBrain Activity Level: [(200 - M.getOrganLoss(ORGAN_SLOT_BRAIN))/2]%.\n" if(M.radiation) msg += "\tSubject is irradiated.\n" - if(advanced) - msg += "\tRadiation Level: [M.radiation]%.\n" + msg += "\tRadiation Level: [M.radiation] rad\n" if(advanced && M.hallucinating()) msg += "\tSubject is hallucinating.\n" @@ -247,11 +250,11 @@ SLIME SCANNER healthy = FALSE msg += "\tSubject is deaf.\n" else - if(ears.ear_damage) - msg += "\tSubject has [ears.ear_damage > UNHEALING_EAR_DAMAGE? "permanent ": "temporary "]hearing damage.\n" + if(ears.damage) + to_chat(user, "\tSubject has [ears.damage > ears.maxHealth ? "permanent ": "temporary "]hearing damage.") healthy = FALSE if(ears.deaf) - msg += "\tSubject is [ears.ear_damage > UNHEALING_EAR_DAMAGE ? "permanently ": "temporarily "] deaf.\n" + to_chat(user, "\tSubject is [ears.damage > ears.maxHealth ? "permanently ": "temporarily "] deaf.") healthy = FALSE if(healthy) msg += "\tHealthy.\n" @@ -267,13 +270,13 @@ SLIME SCANNER if(HAS_TRAIT(C, TRAIT_NEARSIGHT)) msg += "\tSubject is nearsighted.\n" healthy = FALSE - if(eyes.eye_damage > 30) + if(eyes.damage > 30) msg += "\tSubject has severe eye damage.\n" healthy = FALSE - else if(eyes.eye_damage > 20) + else if(eyes.damage > 20) msg += "\tSubject has significant eye damage.\n" healthy = FALSE - else if(eyes.eye_damage) + else if(eyes.damage) msg += "\tSubject has minor eye damage.\n" healthy = FALSE if(healthy) @@ -297,6 +300,59 @@ SLIME SCANNER for(var/obj/item/bodypart/org in damaged) msg += "\t\t[capitalize(org.name)]: [(org.brute_dam > 0) ? "[org.brute_dam]" : "0"]-[(org.burn_dam > 0) ? "[org.burn_dam]" : "0"]\n" + //Organ damages report + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/minor_damage + var/major_damage + var/max_damage + var/report_organs = FALSE + + //Piece together the lists to be reported + for(var/O in H.internal_organs) + var/obj/item/organ/organ = O + if(organ.organ_flags & ORGAN_FAILING) + report_organs = TRUE //if we report one organ, we report all organs, even if the lists are empty, just for consistency + if(max_damage) + max_damage += ", " //prelude the organ if we've already reported an organ + max_damage += organ.name //this just slaps the organ name into the string of text + else + max_damage = "\tNon-Functional Organs: " //our initial statement + max_damage += organ.name + else if(organ.damage > organ.high_threshold) + report_organs = TRUE + if(major_damage) + major_damage += ", " + major_damage += organ.name + else + major_damage = "\tSeverely Damaged Organs: " + major_damage += organ.name + else if(organ.damage > organ.low_threshold) + report_organs = TRUE + if(minor_damage) + minor_damage += ", " + minor_damage += organ.name + else + minor_damage = "\tMildly Damaged Organs: " + minor_damage += organ.name + + if(report_organs) //we either finish the list, or set it to be empty if no organs were reported in that category + if(!max_damage) + max_damage = "\tNon-Functional Organs: " + else + max_damage += "" + if(!major_damage) + major_damage = "\tSeverely Damaged Organs: " + else + major_damage += "" + if(!minor_damage) + minor_damage = "\tMildly Damaged Organs: " + else + minor_damage += "" + msg += "[minor_damage]" + msg += "[major_damage]" + msg += "[max_damage]" + // Species and body temperature if(ishuman(M)) @@ -385,7 +441,7 @@ SLIME SCANNER if(M.reagents.reagent_list.len) var/list/datum/reagent/reagents = list() for(var/datum/reagent/R in M.reagents.reagent_list) - if(R.invisible) + if(R.chemical_flags & REAGENT_INVISIBLE) continue reagents += R @@ -404,6 +460,21 @@ SLIME SCANNER msg += "[R.name]\n" else msg += "Subject is not addicted to any reagents.\n" + + if(M.reagents.has_reagent("fermiTox")) + var/datum/reagent/fermiTox = M.reagents.has_reagent("fermiTox") + switch(fermiTox.volume) + if(5 to 10) + msg += "Subject contains a low amount of toxic isomers.\n" + if(10 to 25) + msg += "Subject contains toxic isomers.\n" + if(25 to 50) + msg += "Subject contains a substantial amount of toxic isomers.\n" + if(50 to 95) + msg += "Subject contains a high amount of toxic isomers.\n" + if(95 to INFINITY) + msg += "Subject contains a extremely dangerous amount of toxic isomers.\n" + msg += "*---------*" to_chat(user, msg) diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm index 71687f0d5a..e4e16fbfb4 100644 --- a/code/game/objects/items/granters.dm +++ b/code/game/objects/items/granters.dm @@ -440,7 +440,15 @@ oneuse = FALSE remarks = list("So that is how icing is made!", "Placing fruit on top? How simple...", "Huh layering cake seems harder then this...", "This book smells like candy", "A clown must have made this page, or they forgot to spell check it before printing...", "Wait, a way to cook slime to be safe?") -//Later content when I have free time - Trilby Date:02-Aug-2019 +/obj/item/book/granter/crafting_recipe/coldcooking //IceCream + name = "Cooking with Ice" + desc = "A cook book that teaches you many old icecream treats." + crafting_recipe_types = list(/datum/crafting_recipe/food/banana_split, /datum/crafting_recipe/food/root_float, /datum/crafting_recipe/food/bluecharrie_float, /datum/crafting_recipe/food/charrie_float) + icon_state = "cooking_learing_ice" + oneuse = FALSE + remarks = list("Looks like these would sell much better in a plasma fire...", "Using glass bowls rather then cones?", "Mixing soda and ice-cream?", "Tall glasses with of liquids and solids...", "Just add a bit of icecream and cherry on top?") + +//Later content when I have free time - Trilby Date:24-Aug-2019 /obj/item/book/granter/crafting_recipe/under_the_oven //Illegal cook book name = "Under The Oven" @@ -449,11 +457,3 @@ icon_state = "cooking_learing_illegal" oneuse = FALSE remarks = list() - -/obj/item/book/granter/crafting_recipe/coldcooking //IceCream - name = "Cooking with Ice" - desc = "A cook book that teaches you many old icecream treats." - crafting_recipe_types = list() - icon_state = "cooking_learing_ice" - oneuse = FALSE - remarks = list() \ No newline at end of file diff --git a/code/game/objects/items/handcuffs.dm b/code/game/objects/items/handcuffs.dm index a306b48385..246dd77684 100644 --- a/code/game/objects/items/handcuffs.dm +++ b/code/game/objects/items/handcuffs.dm @@ -1,5 +1,6 @@ /obj/item/restraints breakouttime = 600 + var/demoralize_criminals = TRUE // checked on carbon/carbon.dm to decide wheter to apply the handcuffed negative moodlet or not. /obj/item/restraints/suicide_act(mob/living/carbon/user) user.visible_message("[user] is strangling [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit suicide!") @@ -220,6 +221,7 @@ name = "fake handcuffs" desc = "Fake handcuffs meant for gag purposes." breakouttime = 10 //Deciseconds = 1s + demoralize_criminals = FALSE /obj/item/restraints/handcuffs/fake/kinky name = "kinky handcuffs" diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm index 6e3deaa694..5e8250ea00 100644 --- a/code/game/objects/items/holy_weapons.dm +++ b/code/game/objects/items/holy_weapons.dm @@ -34,6 +34,7 @@ desc = "God wills it!" icon_state = "knight_templar" item_state = "knight_templar" + allowed = list(/obj/item/storage/book/bible, HOLY_WEAPONS, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) // CITADEL CHANGES: More variants /obj/item/clothing/suit/armor/riot/chaplain/teutonic @@ -122,7 +123,6 @@ icon_state = "studentuni" item_state = "studentuni" body_parts_covered = ARMS|CHEST - allowed = list(/obj/item/storage/book/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) /obj/item/clothing/head/helmet/chaplain/cage name = "cage" @@ -166,7 +166,6 @@ icon_state = "witchhunter" item_state = "witchhunter" body_parts_covered = CHEST|GROIN|LEGS|ARMS - allowed = list(/obj/item/storage/book/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) /obj/item/clothing/head/helmet/chaplain/witchunter_hat name = "witchunter hat" @@ -191,7 +190,7 @@ icon_state = "chaplain_hoodie" item_state = "chaplain_hoodie" body_parts_covered = CHEST|GROIN|LEGS|ARMS - allowed = list(/obj/item/storage/book/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) + allowed = list(/obj/item/storage/book/bible, HOLY_WEAPONS, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) hoodtype = /obj/item/clothing/head/hooded/chaplain_hood /obj/item/clothing/head/hooded/chaplain_hood @@ -229,8 +228,8 @@ throwforce = 10 w_class = WEIGHT_CLASS_TINY obj_flags = UNIQUE_RENAME - var/reskinned = FALSE var/chaplain_spawnable = TRUE + total_mass = TOTAL_MASS_MEDIEVAL_WEAPON /obj/item/nullrod/Initialize() . = ..() @@ -247,10 +246,8 @@ /obj/item/nullrod/proc/reskin_holy_weapon(mob/M) if(GLOB.holy_weapon_type) return - var/obj/item/nullrod/holy_weapon - var/list/holy_weapons_list = typesof(/obj/item/nullrod) + list( - /obj/item/melee/transforming/energy/sword/cx/chaplain - ) + var/obj/item/holy_weapon + var/list/holy_weapons_list = subtypesof(/obj/item/nullrod) + list(HOLY_WEAPONS) var/list/display_names = list() for(var/V in holy_weapons_list) var/obj/item/nullrod/rodtype = V @@ -273,6 +270,13 @@ qdel(src) M.put_in_active_hand(holy_weapon) +/obj/item/nullrod/proc/jedi_spin(mob/living/user) + for(var/i in list(NORTH,SOUTH,EAST,WEST,EAST,SOUTH,NORTH,SOUTH,EAST,WEST,EAST,SOUTH)) + user.setDir(i) + if(i == WEST) + user.emote("flip") + sleep(1) + /obj/item/nullrod/godhand icon_state = "disintegrate" item_state = "disintegrate" @@ -328,7 +332,6 @@ sharpness = IS_SHARP hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - total_mass = TOTAL_MASS_MEDIEVAL_WEAPON /obj/item/nullrod/claymore/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) if(attack_type == PROJECTILE_ATTACK) @@ -522,7 +525,6 @@ slot_flags = ITEM_SLOT_BELT w_class = WEIGHT_CLASS_HUGE attack_verb = list("smashed", "bashed", "hammered", "crunched") - total_mass = TOTAL_MASS_MEDIEVAL_WEAPON /obj/item/nullrod/chainsaw name = "chainsaw hand" @@ -585,6 +587,7 @@ lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' slot_flags = ITEM_SLOT_BELT + force = 12 reach = 2 attack_verb = list("whipped", "lashed") hitsound = 'sound/weapons/chainhit.ogg' @@ -661,6 +664,44 @@ lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' +/obj/item/nullrod/claymore/bostaff/attack(mob/target, mob/living/user) + add_fingerprint(user) + if((HAS_TRAIT(user, TRAIT_CLUMSY)) && prob(50)) + to_chat(user, "You club yourself over the head with [src].") + user.Knockdown(60) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.apply_damage(2*force, BRUTE, BODY_ZONE_HEAD) + else + user.take_bodypart_damage(2*force) + return + if(iscyborg(target)) + return ..() + if(!isliving(target)) + return ..() + var/mob/living/carbon/C = target + if(C.stat || C.health < 0 || C.staminaloss > 130 ) + to_chat(user, "It would be dishonorable to attack a foe while they cannot retaliate.") + return + if(user.a_intent == INTENT_DISARM) + if(!ishuman(target)) + return ..() + var/mob/living/carbon/human/H = target + var/list/fluffmessages = list("[user] clubs [H] with [src]!", \ + "[user] smacks [H] with the butt of [src]!", \ + "[user] broadsides [H] with [src]!", \ + "[user] smashes [H]'s head with [src]!", \ + "[user] beats [H] with front of [src]!", \ + "[user] twirls and slams [H] with [src]!") + H.visible_message("[pick(fluffmessages)]", \ + "[pick(fluffmessages)]") + playsound(get_turf(user), 'sound/effects/woodhit.ogg', 75, 1, -1) + H.adjustStaminaLoss(rand(12,18)) + if(prob(25)) + (INVOKE_ASYNC(src, .proc/jedi_spin, user)) + else + return ..() + /obj/item/nullrod/tribal_knife icon_state = "crysknife" item_state = "crysknife" @@ -674,7 +715,6 @@ hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") item_flags = SLOWS_WHILE_IN_HAND - total_mass = TOTAL_MASS_NORMAL_ITEM /obj/item/nullrod/tribal_knife/Initialize(mapload) . = ..() diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index cdb9c146e2..10b84917bb 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -180,8 +180,13 @@ slot_flags = ITEM_SLOT_BELT force = 12 //9 hit crit w_class = WEIGHT_CLASS_NORMAL - var/cooldown = 0 + var/cooldown = 13 var/on = TRUE + var/last_hit = 0 + var/stun_stam_cost_coeff = 1.25 + var/hardstun_ds = 1 + var/softstun_ds = 0 + var/stam_dmg = 30 /obj/item/melee/classic_baton/attack(mob/living/target, mob/living/user) if(!on) @@ -207,12 +212,10 @@ if(!isliving(target)) return if (user.a_intent == INTENT_HARM) - if(!..()) - return - if(!iscyborg(target)) + if(!..() || !iscyborg(target)) return else - if(cooldown <= world.time) + if(last_hit < world.time) if(ishuman(target)) var/mob/living/carbon/human/H = target if (H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) @@ -220,7 +223,7 @@ if(check_martial_counter(H, user)) return playsound(get_turf(src), 'sound/effects/woodhit.ogg', 75, 1, -1) - target.Knockdown(60) + target.Knockdown(softstun_ds, TRUE, FALSE, hardstun_ds, stam_dmg) log_combat(user, target, "stunned", src) src.add_fingerprint(user) target.visible_message("[user] has knocked down [target] with [src]!", \ @@ -229,7 +232,7 @@ target.LAssailant = null else target.LAssailant = user - cooldown = world.time + 40 + last_hit = world.time + cooldown user.adjustStaminaLossBuffered(getweight())//CIT CHANGE - makes swinging batons cost stamina /obj/item/melee/classic_baton/telescopic @@ -245,7 +248,7 @@ item_flags = NONE force = 0 on = FALSE - total_mass = TOTAL_MASS_SMALL_ITEM + total_mass = TOTAL_MASS_NORMAL_ITEM /obj/item/melee/classic_baton/telescopic/suicide_act(mob/user) var/mob/living/carbon/human/H = user diff --git a/code/game/objects/items/paint.dm b/code/game/objects/items/paint.dm index a6f5830dd4..cc2f5e9be7 100644 --- a/code/game/objects/items/paint.dm +++ b/code/game/objects/items/paint.dm @@ -90,14 +90,14 @@ add_fingerprint(user) -/obj/item/paint/afterattack(turf/target, mob/user, proximity) +/obj/item/paint/afterattack(atom/target, mob/user, proximity) . = ..() if(!proximity) return if(paintleft <= 0) icon_state = "paint_empty" return - if(!istype(target) || isspaceturf(target)) + if(!isturf(target) || isspaceturf(target)) return var/newcolor = "#" + item_color target.add_atom_colour(newcolor, WASHABLE_COLOUR_PRIORITY) @@ -105,12 +105,14 @@ /obj/item/paint/paint_remover gender = PLURAL name = "paint remover" - desc = "Used to remove color from floors and walls." + desc = "Used to remove color from anything." icon_state = "paint_neutral" -/obj/item/paint/paint_remover/afterattack(turf/target, mob/user, proximity) +/obj/item/paint/paint_remover/afterattack(atom/target, mob/user, proximity) . = ..() if(!proximity) return - if(istype(target) && target.color != initial(target.color)) + if(!isturf(target) || !isobj(target)) + return + if(target.color != initial(target.color)) target.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm index 40380dfe80..48588cf9f3 100644 --- a/code/game/objects/items/plushes.dm +++ b/code/game/objects/items/plushes.dm @@ -629,7 +629,7 @@ /obj/item/toy/plush/mothplushie name = "insect plushie" - desc = "An adorable stuffed toy that resembles some kind of insect" + desc = "An adorable stuffed toy that resembles some kind of insect." icon_state = "bumble" item_state = "bumble" squeak_override = list('modular_citadel/sound/voice/mothsqueak.ogg' = 1) @@ -773,8 +773,10 @@ item_state = "blep" /obj/item/toy/plush/mammal/circe + desc = "A luxuriously soft toy that resembles a nine-tailed kitsune." icon_state = "circe" item_state = "circe" + attack_verb = list("medicated", "tailhugged", "kissed") /obj/item/toy/plush/mammal/robin icon_state = "robin" @@ -839,8 +841,10 @@ item_state = "rae" /obj/item/toy/plush/mammal/zed + desc = "A masked stuffed toy that resembles a fierce miner. He even comes with his own little crusher!" icon_state = "zed" item_state = "zed" + attack_verb = list("ENDED", "CRUSHED", "GNOMED") /obj/item/toy/plush/mammal/justin icon_state = "justin" @@ -852,6 +856,12 @@ item_state = "reece" attack_verb = list("healed", "cured", "demoted") +/obj/item/toy/plush/mammal/redwood + desc = "An adorable stuffed toy resembling a Nanotrasen Captain. That just happens to be a bunny." + icon_state = "redwood" + item_state = "redwood" + attack_verb = list("ordered", "bapped", "reprimanded") + /obj/item/toy/plush/mammal/dog desc = "An adorable stuffed toy that resembles a canine." icon_state = "katlin" @@ -898,6 +908,12 @@ obj_flags = UNIQUE_RENAME unique_reskin = list("Goodboye" = "fritz", "Badboye" = "fritz_bad") +/obj/item/toy/plush/mammal/dog/jesse + desc = "An adorable wolf toy that resembles a cream-colored wolf. He has a little pride flag!" + icon_state = "jesse" + item_state = "jesse" + attack_verb = list("greeted", "merc'd", "howdy'd") + /obj/item/toy/plush/catgirl name = "feline plushie" desc = "An adorable stuffed toy that resembles a feline." @@ -935,3 +951,15 @@ item_state = "fermis" attack_verb = list("cuddled", "petpatted", "wigglepurred") squeak_override = list('modular_citadel/sound/voice/merowr.ogg' = 1) + +/obj/item/toy/plush/catgirl/mariaf + desc = "An adorable stuffed toy that resembles a very tall cat girl." + icon_state = "mariaf" + item_state = "mariaf" + attack_verb = list("hugged", "stabbed", "licked") + +/obj/item/toy/plush/catgirl/maya + desc = "An adorable stuffed toy that resembles an angry cat girl. She has her own tiny nuke disk!" + icon_state = "maya" + item_state = "maya" + attack_verb = list("nuked", "arrested", "harmbatonned") diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index cd2ccff170..95110692c9 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -254,7 +254,7 @@ to_chat(user, "The MMI indicates that their mind is currently inactive; it might change!") return - if(BM.stat == DEAD || (M.brain && M.brain.damaged_brain)) + if(BM.stat == DEAD || (M.brain && M.brain.organ_flags & ORGAN_FAILING)) to_chat(user, "Sticking a dead brain into the frame would sort of defeat the purpose!") return diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index d5806494e6..9c929a6ebf 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -9,7 +9,7 @@ var/locked = FALSE var/installed = 0 var/require_module = 0 - var/module_type = null + var/list/module_type // if true, is not stored in the robot to be ejected // if module is reset var/one_use = FALSE @@ -18,7 +18,7 @@ if(R.stat == DEAD) to_chat(user, "[src] will not function on a deceased cyborg.") return FALSE - if(module_type && !istype(R.module, module_type)) + if(module_type && !is_type_in_list(R.module, module_type)) to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") to_chat(user, "There's no mounting point for the module!") return FALSE @@ -93,7 +93,6 @@ desc = "Used to cool a mounted disabler, increasing the potential current in it and thus its recharge rate." icon_state = "cyborg_upgrade3" require_module = 1 - //module_type = /obj/item/robot_module/security /obj/item/borg/upgrade/disablercooler/action(mob/living/silicon/robot/R, user = usr) . = ..() @@ -141,7 +140,7 @@ desc = "A diamond drill replacement for the mining module's standard drill." icon_state = "cyborg_upgrade3" require_module = 1 - module_type = /obj/item/robot_module/miner + module_type = list(/obj/item/robot_module/miner) /obj/item/borg/upgrade/ddrill/action(mob/living/silicon/robot/R, user = usr) . = ..() @@ -173,7 +172,7 @@ desc = "A satchel of holding replacement for mining cyborg's ore satchel module." icon_state = "cyborg_upgrade3" require_module = 1 - module_type = /obj/item/robot_module/miner + module_type = list(/obj/item/robot_module/miner) /obj/item/borg/upgrade/soh/action(mob/living/silicon/robot/R) . = ..() @@ -200,7 +199,7 @@ desc = "A trash bag of holding replacement for the janiborg's standard trash bag." icon_state = "cyborg_upgrade3" require_module = 1 - module_type = /obj/item/robot_module/janitor + module_type = list(/obj/item/robot_module/janitor, /obj/item/robot_module/scrubpup) /obj/item/borg/upgrade/tboh/action(mob/living/silicon/robot/R) . = ..() @@ -227,7 +226,7 @@ desc = "An advanced mop replacement for the janiborg's standard mop." icon_state = "cyborg_upgrade3" require_module = 1 - module_type = /obj/item/robot_module/janitor + module_type = list(/obj/item/robot_module/janitor, /obj/item/robot_module/scrubpup) /obj/item/borg/upgrade/amop/action(mob/living/silicon/robot/R) . = ..() @@ -276,7 +275,7 @@ icon_state = "ash_plating" resistance_flags = LAVA_PROOF | FIRE_PROOF require_module = 1 - module_type = /obj/item/robot_module/miner + module_type = list(/obj/item/robot_module/miner) /obj/item/borg/upgrade/lavaproof/action(mob/living/silicon/robot/R, user = usr) . = ..() @@ -405,7 +404,9 @@ to produce more advanced and complex medical reagents." icon_state = "cyborg_upgrade3" require_module = 1 - module_type = /obj/item/robot_module/medical + module_type = list(/obj/item/robot_module/medical, + /obj/item/robot_module/syndicate_medical, + /obj/item/robot_module/medihound) var/list/additional_reagents = list() /obj/item/borg/upgrade/hypospray/action(mob/living/silicon/robot/R, user = usr) @@ -467,7 +468,9 @@ defibrillator, for on the scene revival." icon_state = "cyborg_upgrade3" require_module = 1 - module_type = /obj/item/robot_module/medical + module_type = list(/obj/item/robot_module/medical, + /obj/item/robot_module/syndicate_medical, + /obj/item/robot_module/medihound) /obj/item/borg/upgrade/defib/action(mob/living/silicon/robot/R, user = usr) . = ..() @@ -489,7 +492,9 @@ out procedures" icon_state = "cyborg_upgrade3" require_module = 1 - module_type = /obj/item/robot_module/medical + module_type = list(/obj/item/robot_module/medical, + /obj/item/robot_module/syndicate_medical, + /obj/item/robot_module/medihound) /obj/item/borg/upgrade/processor/action(mob/living/silicon/robot/R, user = usr) . = ..() @@ -514,7 +519,7 @@ /obj/item/robot_module/medical, /obj/item/robot_module/syndicate_medical, /obj/item/robot_module/medihound, - /obj/item/robot_module/borgi) + /obj/item/robot_module/borgi) /obj/item/borg/upgrade/advhealth/action(mob/living/silicon/robot/R, user = usr) . = ..() @@ -598,7 +603,7 @@ icon = 'icons/obj/storage.dmi' icon_state = "borgrped" require_module = TRUE - module_type = /obj/item/robot_module/engineering + module_type = list(/obj/item/robot_module/engineering) /obj/item/borg/upgrade/rped/action(mob/living/silicon/robot/R, user = usr) . = ..() @@ -626,7 +631,9 @@ icon = 'icons/obj/device.dmi' icon_state = "pinpointer_crew" require_module = TRUE - module_type = /obj/item/robot_module/medical + module_type = list(/obj/item/robot_module/medical, + /obj/item/robot_module/syndicate_medical, + /obj/item/robot_module/medihound) /obj/item/borg/upgrade/pinpointer/action(mob/living/silicon/robot/R, user = usr) . = ..() @@ -664,3 +671,33 @@ desc = "Allows you to to turn a cyborg into a clown, honk." icon_state = "cyborg_upgrade3" new_module = /obj/item/robot_module/clown + +// Citadel's Vtech Controller +/obj/effect/proc_holder/silicon/cyborg/vtecControl + name = "vTec Control" + desc = "Allows finer-grained control of the vTec speed boost." + action_icon = 'icons/mob/actions.dmi' + action_icon_state = "Chevron_State_0" + + var/currentState = 0 + var/maxReduction = 2 + + +/obj/effect/proc_holder/silicon/cyborg/vtecControl/Click(mob/living/silicon/robot/user) + var/mob/living/silicon/robot/self = usr + + currentState = (currentState + 1) % 3 + + if(usr) + switch(currentState) + if (0) + self.speed = maxReduction + if (1) + self.speed -= maxReduction*0.5 + if (2) + self.speed -= maxReduction*1.25 + + action.button_icon_state = "Chevron_State_[currentState]" + action.UpdateButtonIcon() + + return diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index cf967e25ba..fcadbf4270 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -23,8 +23,9 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 100) resistance_flags = ACID_PROOF merge_type = /obj/item/stack/sheet/glass - grind_results = list("silicon" = 20) + grind_results = list(/datum/reagent/silicon = 20) point_value = 1 + tableVariant = /obj/structure/table/glass /obj/item/stack/sheet/glass/suicide_act(mob/living/carbon/user) user.visible_message("[user] begins to slice [user.p_their()] neck with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!") @@ -87,7 +88,7 @@ GLOBAL_LIST_INIT(pglass_recipes, list ( \ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 100) resistance_flags = ACID_PROOF merge_type = /obj/item/stack/sheet/plasmaglass - grind_results = list("silicon" = 20, "plasma" = 10) + grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/toxin/plasma = 10) /obj/item/stack/sheet/plasmaglass/fifty amount = 50 @@ -138,7 +139,7 @@ GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 100) resistance_flags = ACID_PROOF merge_type = /obj/item/stack/sheet/rglass - grind_results = list("silicon" = 20, "iron" = 10) + grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/iron = 10) point_value = 4 /obj/item/stack/sheet/rglass/attackby(obj/item/W, mob/user, params) @@ -177,11 +178,11 @@ GLOBAL_LIST_INIT(prglass_recipes, list ( \ singular_name = "reinforced plasma glass sheet" icon_state = "sheet-prglass" item_state = "sheet-prglass" - materials = list(MAT_PLASMA=MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS=MINERAL_MATERIAL_AMOUNT, MAT_METAL = MINERAL_MATERIAL_AMOUNT * 0.5,) + materials = list(MAT_PLASMA=MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS=MINERAL_MATERIAL_AMOUNT, MAT_METAL=MINERAL_MATERIAL_AMOUNT * 0.5,) armor = list("melee" = 20, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) resistance_flags = ACID_PROOF merge_type = /obj/item/stack/sheet/plasmarglass - grind_results = list("silicon" = 20, "plasma" = 10, "iron" = 10) + grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/toxin/plasma = 10, /datum/reagent/iron = 10) point_value = 23 /obj/item/stack/sheet/plasmarglass/Initialize(mapload, new_amount, merge = TRUE) @@ -243,8 +244,9 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( resistance_flags = ACID_PROOF armor = list("melee" = 100, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 100) max_integrity = 40 - var/cooldown = 0 sharpness = IS_SHARP + var/icon_prefix + /obj/item/shard/suicide_act(mob/user) user.visible_message("[user] is slitting [user.p_their()] [pick("wrists", "throat")] with the shard of glass! It looks like [user.p_theyre()] trying to commit suicide.") @@ -266,9 +268,19 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( if("large") pixel_x = rand(-5, 5) pixel_y = rand(-5, 5) - var/matrix/M = matrix(transform) - M.Turn(rand(-170, 170)) - transform = M + if (icon_prefix) + icon_state = "[icon_prefix][icon_state]" + + var/turf/T = get_turf(src) + if(T && is_station_level(T.z)) + SSblackbox.record_feedback("tally", "station_mess_created", 1, name) + +/obj/item/shard/Destroy() + . = ..() + + var/turf/T = get_turf(src) + if(T && is_station_level(T.z)) + SSblackbox.record_feedback("tally", "station_mess_destroyed", 1, name) /obj/item/shard/afterattack(atom/A as mob|obj, mob/user, proximity) . = ..() @@ -298,6 +310,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( return ..() /obj/item/shard/welder_act(mob/living/user, obj/item/I) + ..() if(I.use_tool(src, user, 0, volume=50)) var/obj/item/stack/sheet/glass/NG = new (user.loc) for(var/obj/item/stack/sheet/glass/G in user.loc) @@ -316,4 +329,13 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( playsound(loc, 'sound/effects/glass_step.ogg', 30, 1) else playsound(loc, 'sound/effects/glass_step.ogg', 50, 1) - . = ..() + return ..() + +/obj/item/shard/plasma + name = "purple shard" + desc = "A nasty looking shard of plasma glass." + force = 6 + throwforce = 11 + icon_state = "plasmalarge" + materials = list(MAT_PLASMA=MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS=MINERAL_MATERIAL_AMOUNT) + icon_prefix = "plasma" \ No newline at end of file diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm index 7df165461b..46c0d47388 100644 --- a/code/game/objects/items/stacks/sheets/mineral.dm +++ b/code/game/objects/items/stacks/sheets/mineral.dm @@ -232,6 +232,7 @@ GLOBAL_LIST_INIT(gold_recipes, list ( \ grind_results = list("silver" = 20) point_value = 20 merge_type = /obj/item/stack/sheet/mineral/silver + tableVariant = /obj/structure/table/optable GLOBAL_LIST_INIT(silver_recipes, list ( \ new/datum/stack_recipe("silver door", /obj/structure/mineral_door/silver, 10, one_per_turf = 1, on_floor = 1), \ diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 8c808d0e5f..c085306892 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -107,6 +107,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \ merge_type = /obj/item/stack/sheet/metal grind_results = list("iron" = 20) point_value = 2 + tableVariant = /obj/structure/table /obj/item/stack/sheet/metal/ratvar_act() new /obj/item/stack/tile/brass(loc, amount) @@ -168,6 +169,7 @@ GLOBAL_LIST_INIT(plasteel_recipes, list ( \ merge_type = /obj/item/stack/sheet/plasteel grind_results = list("iron" = 20, "plasma" = 20) point_value = 23 + tableVariant = /obj/structure/table/reinforced /obj/item/stack/sheet/plasteel/Initialize(mapload, new_amount, merge = TRUE) recipes = GLOB.plasteel_recipes @@ -474,6 +476,7 @@ GLOBAL_LIST_INIT(brass_recipes, list ( \ novariants = FALSE grind_results = list("iron" = 5, "teslium" = 15, "holyoil" = 1) merge_type = /obj/item/stack/tile/brass + tableVariant = /obj/structure/table/reinforced/brass /obj/item/stack/tile/brass/narsie_act() new /obj/item/stack/sheet/runed_metal(loc, amount) @@ -526,6 +529,7 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \ novariants = FALSE grind_results = list("iron" = 5, "copper" = 3) //we have no "tin" reagent so this is the closest thing merge_type = /obj/item/stack/tile/bronze + tableVariant = /obj/structure/table/bronze /obj/item/stack/tile/bronze/attack_self(mob/living/user) if(is_servant_of_ratvar(user)) //still lets them build with it, just gives a message diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 4217a58f81..155d1f1643 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -22,6 +22,7 @@ var/full_w_class = WEIGHT_CLASS_NORMAL //The weight class the stack should have at amount > 2/3rds max_amount var/novariants = TRUE //Determines whether the item should update it's sprites based on amount. //NOTE: When adding grind_results, the amounts should be for an INDIVIDUAL ITEM - these amounts will be multiplied by the stack size in on_grind() + var/obj/structure/table/tableVariant // we tables now (stores table variant to be built from this stack) /obj/item/stack/on_grind() for(var/i in 1 to grind_results.len) //This should only call if it's ground, so no need to check if grind_results exists diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index 3b28c30402..847e1b521e 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -99,33 +99,117 @@ icon_state = "tile-carpet" turf_type = /turf/open/floor/carpet resistance_flags = FLAMMABLE - -/obj/item/stack/tile/carpet/fifty - amount = 50 + tableVariant = /obj/structure/table/wood/fancy /obj/item/stack/tile/carpet/black name = "black carpet" icon_state = "tile-carpet-black" turf_type = /turf/open/floor/carpet/black + tableVariant = /obj/structure/table/wood/fancy/black + +/obj/item/stack/tile/carpet/blackred + name = "red carpet" + icon_state = "tile-carpet-blackred" + turf_type = /turf/open/floor/carpet/blackred + tableVariant = /obj/structure/table/wood/fancy/blackred + +/obj/item/stack/tile/carpet/monochrome + name = "monochrome carpet" + icon_state = "tile-carpet-monochrome" + turf_type = /turf/open/floor/carpet/monochrome + tableVariant = /obj/structure/table/wood/fancy/monochrome + +/obj/item/stack/tile/carpet/blue + name = "blue carpet" + icon_state = "tile-carpet-blue" + item_state = "tile-carpet-blue" + turf_type = /turf/open/floor/carpet/blue + tableVariant = /obj/structure/table/wood/fancy/blue + +/obj/item/stack/tile/carpet/cyan + name = "cyan carpet" + icon_state = "tile-carpet-cyan" + item_state = "tile-carpet-cyan" + turf_type = /turf/open/floor/carpet/cyan + tableVariant = /obj/structure/table/wood/fancy/cyan + +/obj/item/stack/tile/carpet/green + name = "green carpet" + icon_state = "tile-carpet-green" + item_state = "tile-carpet-green" + turf_type = /turf/open/floor/carpet/green + tableVariant = /obj/structure/table/wood/fancy/green + +/obj/item/stack/tile/carpet/orange + name = "orange carpet" + icon_state = "tile-carpet-orange" + item_state = "tile-carpet-orange" + turf_type = /turf/open/floor/carpet/orange + tableVariant = /obj/structure/table/wood/fancy/orange + +/obj/item/stack/tile/carpet/purple + name = "purple carpet" + icon_state = "tile-carpet-purple" + item_state = "tile-carpet-purple" + turf_type = /turf/open/floor/carpet/purple + tableVariant = /obj/structure/table/wood/fancy/purple + +/obj/item/stack/tile/carpet/red + name = "red carpet" + icon_state = "tile-carpet-red" + item_state = "tile-carpet-red" + turf_type = /turf/open/floor/carpet/red + tableVariant = /obj/structure/table/wood/fancy/red + +/obj/item/stack/tile/carpet/royalblack + name = "royal black carpet" + icon_state = "tile-carpet-royalblack" + item_state = "tile-carpet-royalblack" + turf_type = /turf/open/floor/carpet/royalblack + tableVariant = /obj/structure/table/wood/fancy/royalblack + +/obj/item/stack/tile/carpet/royalblue + name = "royal blue carpet" + icon_state = "tile-carpet-royalblue" + item_state = "tile-carpet-royalblue" + turf_type = /turf/open/floor/carpet/royalblue + tableVariant = /obj/structure/table/wood/fancy/royalblue + +/obj/item/stack/tile/carpet/fifty + amount = 50 /obj/item/stack/tile/carpet/black/fifty amount = 50 -/obj/item/stack/tile/carpet/blackred - name = "red carpet" - icon_state = "tile-carpet-blackred" - turf_type = /turf/open/floor/carpet/blackred - /obj/item/stack/tile/carpet/blackred/fifty - amount = 50 - -/obj/item/stack/tile/carpet/monochrome - name = "monochrome carpet" - icon_state = "tile-carpet-monochrome" - turf_type = /turf/open/floor/carpet/monochrome + amount = 50 /obj/item/stack/tile/carpet/monochrome/fifty - amount = 50 + amount = 50 + +/obj/item/stack/tile/carpet/blue/fifty + amount = 50 + +/obj/item/stack/tile/carpet/cyan/fifty + amount = 50 + +/obj/item/stack/tile/carpet/green/fifty + amount = 50 + +/obj/item/stack/tile/carpet/orange/fifty + amount = 50 + +/obj/item/stack/tile/carpet/purple/fifty + amount = 50 + +/obj/item/stack/tile/carpet/red/fifty + amount = 50 + +/obj/item/stack/tile/carpet/royalblack/fifty + amount = 50 + +/obj/item/stack/tile/carpet/royalblue/fifty + amount = 50 /obj/item/stack/tile/fakespace name = "astral carpet" diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm index 887b6f5a2e..6532e9f7a5 100644 --- a/code/game/objects/items/storage/backpack.dm +++ b/code/game/objects/items/storage/backpack.dm @@ -46,6 +46,7 @@ item_flags = NO_MAT_REDEMPTION armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 50) component_type = /datum/component/storage/concrete/bluespace/bag_of_holding + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/backpack/holding/satchel name = "satchel of holding" @@ -53,6 +54,7 @@ icon_state = "holdingsat" item_state = "holdingsat" species_exception = list(/datum/species/angel) + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/backpack/holding/ComponentInitialize() . = ..() @@ -81,6 +83,7 @@ icon_state = "giftbag0" item_state = "giftbag" w_class = WEIGHT_CLASS_BULKY + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/backpack/santabag/ComponentInitialize() . = ..() @@ -133,6 +136,8 @@ desc = "It's a special backpack made exclusively for Nanotrasen officers." icon_state = "captainpack" item_state = "captainpack" + resistance_flags = FIRE_PROOF + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/backpack/industrial name = "industrial backpack" @@ -140,6 +145,7 @@ icon_state = "engiepack" item_state = "engiepack" resistance_flags = FIRE_PROOF + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/backpack/botany name = "botany backpack" @@ -194,6 +200,8 @@ desc = "A tough satchel with extra pockets." icon_state = "satchel-eng" item_state = "engiepack" + resistance_flags = FIRE_PROOF + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/backpack/satchel/med name = "medical satchel" @@ -261,6 +269,8 @@ desc = "An exclusive satchel for Nanotrasen officers." icon_state = "satchel-cap" item_state = "captainpack" + resistance_flags = FIRE_PROOF + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/backpack/satchel/flat name = "smuggler's satchel" @@ -356,6 +366,7 @@ new /obj/item/cautery(src) new /obj/item/surgical_drapes(src) new /obj/item/clothing/mask/surgical(src) + new /obj/item/reagent_containers/medspray/sterilizine(src) new /obj/item/razor(src) /obj/item/storage/backpack/duffelbag/sec @@ -377,12 +388,15 @@ new /obj/item/cautery(src) new /obj/item/surgical_drapes(src) new /obj/item/clothing/mask/surgical(src) + new /obj/item/reagent_containers/medspray/sterilizine(src) /obj/item/storage/backpack/duffelbag/engineering name = "industrial duffel bag" desc = "A large duffel bag for holding extra tools and supplies." icon_state = "duffel-eng" item_state = "duffel-eng" + resistance_flags = FIRE_PROOF + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/backpack/duffelbag/durathread name = "durathread duffel bag" @@ -398,6 +412,7 @@ icon_state = "duffel-drone" item_state = "duffel-drone" resistance_flags = FIRE_PROOF + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/backpack/duffelbag/drone/PopulateContents() new /obj/item/screwdriver(src) @@ -425,6 +440,7 @@ icon_state = "duffel-syndie" item_state = "duffel-syndieammo" slowdown = 0 + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/backpack/duffelbag/syndie/ComponentInitialize() . = ..() @@ -470,6 +486,7 @@ new /obj/item/mmi/syndie(src) new /obj/item/implantcase(src) new /obj/item/implanter(src) + new /obj/item/reagent_containers/medspray/sterilizine(src) /obj/item/storage/backpack/duffelbag/syndie/surgery_adv name = "advanced surgery duffel bag" @@ -489,6 +506,7 @@ new /obj/item/mmi/syndie(src) new /obj/item/implantcase(src) new /obj/item/implanter(src) + new /obj/item/reagent_containers/medspray/sterilizine(src) /obj/item/storage/backpack/duffelbag/syndie/ammo name = "ammunition duffel bag" diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm index e4debeff49..232d1bd5c9 100644 --- a/code/game/objects/items/storage/bags.dm +++ b/code/game/objects/items/storage/bags.dm @@ -81,6 +81,7 @@ desc = "The latest and greatest in custodial convenience, a trashbag that is capable of holding vast quantities of garbage." icon_state = "bluetrashbag" item_flags = NO_MAT_REDEMPTION + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/bag/trash/bluespace/ComponentInitialize() . = ..() @@ -105,6 +106,7 @@ component_type = /datum/component/storage/concrete/stack var/spam_protection = FALSE //If this is TRUE, the holder won't receive any messages when they fail to pick up ore through crossing it var/datum/component/mobhook + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/bag/ore/ComponentInitialize() . = ..() @@ -391,6 +393,7 @@ icon = 'icons/obj/chemical.dmi' icon_state = "bspace_biobag" desc = "A bag for the safe transportation and disposal of biowaste and other biological materials." + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/bag/bio/holding/ComponentInitialize() . = ..() diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index 4495e9da14..3fe4abeeec 100755 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -40,6 +40,7 @@ icon_state = "utilitybelt" item_state = "utility" content_overlays = TRUE + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE //because this is easier than trying to have showers wash all contents. /obj/item/storage/belt/utility/ComponentInitialize() . = ..() @@ -344,6 +345,7 @@ desc = "A set of tactical webbing worn by Syndicate boarding parties." icon_state = "militarywebbing" item_state = "militarywebbing" + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/belt/military/ComponentInitialize() . = ..() @@ -530,6 +532,7 @@ desc = "A belt designed to hold various rods of power. A veritable fanny pack of exotic magic." icon_state = "soulstonebelt" item_state = "soulstonebelt" + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/storage/belt/wands/ComponentInitialize() . = ..() @@ -599,7 +602,7 @@ icon_state = "bandolier-durathread" item_state = "bandolier-durathread" resistance_flags = FIRE_PROOF - + /obj/item/storage/belt/bandolier/durathread/ComponentInitialize() . = ..() GET_COMPONENT(STR, /datum/component/storage) diff --git a/code/game/objects/items/storage/book.dm b/code/game/objects/items/storage/book.dm index df7b203f91..85a06f0c1e 100644 --- a/code/game/objects/items/storage/book.dm +++ b/code/game/objects/items/storage/book.dm @@ -140,7 +140,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible", else if(iscarbon(M)) var/mob/living/carbon/C = M if(!istype(C.head, /obj/item/clothing/head)) - C.adjustBrainLoss(10, 80) + C.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10, 80) to_chat(C, "You feel dumber.") if(smack) diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm index 903c319644..dd6a6b8453 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -33,6 +33,7 @@ resistance_flags = FLAMMABLE var/foldable = /obj/item/stack/sheet/cardboard var/illustration = "writing" + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE //exploits ahoy /obj/item/storage/box/Initialize(mapload) . = ..() diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm index 5b99bb85bf..d18212be42 100644 --- a/code/game/objects/items/storage/toolbox.dm +++ b/code/game/objects/items/storage/toolbox.dm @@ -19,6 +19,7 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons) var/latches = "single_latch" var/has_latches = TRUE var/can_rubberify = TRUE + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE //very protecc too /obj/item/storage/toolbox/Initialize(mapload) . = ..() diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 6e41527b24..cf5d685b4a 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -1,7 +1,7 @@ /obj/item/storage/box/syndicate /obj/item/storage/box/syndicate/PopulateContents() - switch (pickweight(list("bloodyspai" = 3, "stealth" = 2, "bond" = 2, "screwed" = 2, "sabotage" = 3, "guns" = 2, "murder" = 2, "implant" = 1, "hacker" = 3, "darklord" = 1, "sniper" = 1, "metaops" = 1, "ninja" = 1))) + switch (pickweight(list("bloodyspai" = 3, "stealth" = 2, "bond" = 2, "screwed" = 2, "sabotage" = 3, "guns" = 2, "murder" = 2, "baseball" = 1, "implant" = 1, "hacker" = 3, "darklord" = 1, "sniper" = 1, "metaops" = 1, "ninja" = 1))) if("bloodyspai") // 30 tc now this is more right new /obj/item/clothing/under/chameleon(src) // 2 tc since it's not the full set new /obj/item/clothing/mask/chameleon(src) // Goes with above @@ -52,7 +52,7 @@ new /obj/item/clothing/under/suit_jacket/really_black(src) new /obj/item/screwdriver/power(src) //2 tc item - if("murder") // 35 tc now + if("murder") // 35 tc new /obj/item/melee/transforming/energy/sword/saber(src) new /obj/item/clothing/glasses/thermal/syndi(src) new /obj/item/card/emag(src) @@ -62,6 +62,17 @@ new /obj/item/clothing/glasses/phantomthief/syndicate(src) new /obj/item/reagent_containers/syringe/stimulants(src) + if("baseball") // 42~ tc + new /obj/item/melee/baseball_bat/ablative/syndi(src) //Lets say 12 tc, lesser sleeping carp + new /obj/item/clothing/glasses/sunglasses/garb(src) //Lets say 2 tc + new /obj/item/card/emag(src) //6 tc + new /obj/item/clothing/shoes/sneakers/noslip(src) //2tc + new /obj/item/encryptionkey/syndicate(src) //1tc + new /obj/item/autosurgeon/anti_drop(src) //Lets just say 7~ + new /obj/item/clothing/under/syndicate/baseball(src) //3tc + new /obj/item/clothing/head/soft/baseball(src) //Lets say 4 tc + new /obj/item/reagent_containers/hypospray/medipen/stimulants/baseball(src) //lets say 5tc + if("implant") // 67+ tc holy shit what the fuck this is a lottery disguised as fun boxes isn't it? new /obj/item/implanter/freedom(src) new /obj/item/implanter/uplink/precharged(src) diff --git a/code/game/objects/items/tools/crowbar.dm b/code/game/objects/items/tools/crowbar.dm index 70bfebb799..f891a48df6 100644 --- a/code/game/objects/items/tools/crowbar.dm +++ b/code/game/objects/items/tools/crowbar.dm @@ -31,7 +31,7 @@ name = "brass crowbar" desc = "A brass crowbar. It feels faintly warm to the touch." resistance_flags = FIRE_PROOF | ACID_PROOF - icon_state = "crowbar_brass" + icon_state = "crowbar_clock" toolspeed = 0.5 /obj/item/crowbar/bronze diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/screwdriver.dm index 68946f73cd..6cbede78a8 100644 --- a/code/game/objects/items/tools/screwdriver.dm +++ b/code/game/objects/items/tools/screwdriver.dm @@ -81,7 +81,7 @@ name = "brass screwdriver" desc = "A screwdriver made of brass. The handle feels freezing cold." resistance_flags = FIRE_PROOF | ACID_PROOF - icon_state = "screwdriver_brass" + icon_state = "screwdriver_clock" item_state = "screwdriver_brass" toolspeed = 0.5 random_color = FALSE diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm index 9b622a14c4..fb38e4335e 100644 --- a/code/game/objects/items/tools/weldingtool.dm +++ b/code/game/objects/items/tools/weldingtool.dm @@ -360,7 +360,7 @@ name = "brass welding tool" desc = "A brass welder that seems to constantly refuel itself. It is faintly warm to the touch." resistance_flags = FIRE_PROOF | ACID_PROOF - icon_state = "brasswelder" + icon_state = "clockwelder" item_state = "brasswelder" /obj/item/weldingtool/bronze diff --git a/code/game/objects/items/tools/wirecutters.dm b/code/game/objects/items/tools/wirecutters.dm index 527891afdb..e40ae8bdc1 100644 --- a/code/game/objects/items/tools/wirecutters.dm +++ b/code/game/objects/items/tools/wirecutters.dm @@ -63,9 +63,9 @@ /obj/item/wirecutters/brass name = "brass wirecutters" - desc = "A pair of wirecutters made of brass. The handle feels freezing cold to the touch." + desc = "A pair of eloquent wirecutters made of brass. The handle feels freezing cold to the touch." resistance_flags = FIRE_PROOF | ACID_PROOF - icon_state = "cutters_brass" + icon_state = "cutters_clock" random_color = FALSE toolspeed = 0.5 diff --git a/code/game/objects/items/tools/wrench.dm b/code/game/objects/items/tools/wrench.dm index 8ff960825a..462eb22aaa 100644 --- a/code/game/objects/items/tools/wrench.dm +++ b/code/game/objects/items/tools/wrench.dm @@ -32,7 +32,7 @@ name = "brass wrench" desc = "A brass wrench. It's faintly warm to the touch." resistance_flags = FIRE_PROOF | ACID_PROOF - icon_state = "wrench_brass" + icon_state = "wrench_clock" toolspeed = 0.5 /obj/item/wrench/bronze diff --git a/code/game/objects/items/trash.dm b/code/game/objects/items/trash.dm index 539a60986a..37ab948332 100644 --- a/code/game/objects/items/trash.dm +++ b/code/game/objects/items/trash.dm @@ -44,6 +44,10 @@ icon_state = "plate" resistance_flags = NONE +/obj/item/trash/plate/alt + desc = "Still some dip left. Sadly still just trash..." + icon_state = "plate1" + /obj/item/trash/pistachios name = "pistachios pack" icon_state = "pistachios_pack" diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index 781a82513e..a126d16777 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -304,7 +304,7 @@ var/obj/item/bodypart/head/myhead = user.get_bodypart(BODY_ZONE_HEAD)//stole from chainsaw code var/obj/item/organ/brain/B = user.getorganslot(ORGAN_SLOT_BRAIN) - B.vital = FALSE//this cant possibly be a good idea + B.organ_flags &= ~ORGAN_VITAL //this cant possibly be a good idea var/randdir for(var/i in 1 to 24)//like a headless chicken! if(user.is_holding(src)) @@ -487,6 +487,7 @@ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) var/obj/item/grenade/explosive = null var/war_cry = "AAAAARGH!!!" + var/icon_prefix = "spearglass" /obj/item/twohanded/spear/Initialize() . = ..() @@ -529,7 +530,7 @@ if(explosive) icon_state = "spearbomb[wielded]" else - icon_state = "spearglass[wielded]" + icon_state = "[icon_prefix][wielded]" /obj/item/twohanded/spear/afterattack(atom/movable/AM, mob/user, proximity) . = ..() @@ -556,6 +557,13 @@ src.war_cry = input /obj/item/twohanded/spear/CheckParts(list/parts_list) + var/obj/item/shard/tip = locate() in parts_list + if (istype(tip, /obj/item/shard/plasma)) + force_wielded = 19 + force_unwielded = 11 + throwforce = 21 + icon_prefix = "spearplasma" + qdel(tip) var/obj/item/twohanded/spear/S = locate() in parts_list if(S) if(S.explosive) diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index f2d9f328a1..4cb6fc74c0 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -255,7 +255,9 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 var/obj/item/twohanded/spear/S = new /obj/item/twohanded/spear remove_item_from_storage(user) - qdel(I) + if (!user.transferItemToLoc(I, S)) + return + S.CheckParts(list(I)) qdel(src) user.put_in_hands(S) @@ -513,6 +515,19 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 var/homerun_able = 0 total_mass = 2.7 //a regular wooden major league baseball bat weighs somewhere between 2 to 3.4 pounds, according to google +/obj/item/melee/baseball_bat/chaplain + name = "blessed baseball bat" + desc = "There ain't a cult in the league that can withstand a swatter." + force = 14 + throwforce = 14 + obj_flags = UNIQUE_RENAME + var/chaplain_spawnable = TRUE + total_mass = TOTAL_MASS_MEDIEVAL_WEAPON + +/obj/item/melee/baseball_bat/chaplain/Initialize() + . = ..() + AddComponent(/datum/component/anti_magic, TRUE, TRUE) + /obj/item/melee/baseball_bat/homerun name = "home run bat" desc = "This thing looks dangerous... Dangerously good at baseball, that is." @@ -563,6 +578,12 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 playsound(turf, 'sound/weapons/effects/batreflect2.ogg', 50, 1) return 1 +/obj/item/melee/baseball_bat/ablative/syndi + name = "syndicate major league bat" + desc = "A metal bat made by the syndicate for the major league team." + force = 18 //Spear damage... + throwforce = 30 + /obj/item/melee/flyswatter name = "flyswatter" desc = "Useful for killing insects of all sizes." diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm index 88d141a9ad..dde9bce1bc 100644 --- a/code/game/objects/structures/beds_chairs/chair.dm +++ b/code/game/objects/structures/beds_chairs/chair.dm @@ -489,3 +489,19 @@ . = ..() if(has_gravity()) playsound(src, 'sound/machines/clockcult/integration_cog_install.ogg', 50, TRUE) + +/obj/structure/chair/sofa + name = "old ratty sofa" + icon_state = "sofamiddle" + icon = 'icons/obj/sofa.dmi' + buildstackamount = 1 + item_chair = null + +/obj/structure/chair/sofa/left + icon_state = "sofaend_left" + +/obj/structure/chair/sofa/right + icon_state = "sofaend_right" + +/obj/structure/chair/sofa/corner + icon_state = "sofacorner" \ No newline at end of file diff --git a/code/game/objects/structures/crates_lockers/closets/job_closets.dm b/code/game/objects/structures/crates_lockers/closets/job_closets.dm index d2ab9ea6fb..b49d0a77d5 100644 --- a/code/game/objects/structures/crates_lockers/closets/job_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/job_closets.dm @@ -111,9 +111,9 @@ new /obj/item/clothing/accessory/pocketprotector/cosmetology(src) new /obj/item/clothing/under/rank/chaplain(src) new /obj/item/clothing/shoes/sneakers/black(src) - new /obj/item/clothing/suit/nun(src) + new /obj/item/clothing/suit/chaplain/nun(src) new /obj/item/clothing/head/nun_hood(src) - new /obj/item/clothing/suit/holidaypriest(src) + new /obj/item/clothing/suit/chaplain/holidaypriest(src) new /obj/item/storage/backpack/cultpack(src) new /obj/item/storage/fancy/candle_box(src) new /obj/item/storage/fancy/candle_box(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm index 167823b9be..a7adafdad4 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm @@ -33,6 +33,7 @@ new /obj/item/extinguisher/advanced(src) new /obj/item/storage/photo_album/CE(src) new /obj/item/storage/lockbox/medal/engineering(src) + new /obj/item/construction/rcd/loaded/upgraded(src) /obj/structure/closet/secure_closet/engineering_electrical name = "electrical supplies locker" diff --git a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm index 6e841bcd93..9dab3679fa 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm @@ -2,6 +2,24 @@ icon_state = "freezer" var/jones = FALSE +/obj/structure/closet/secure_closet/freezer/Destroy() + recursive_organ_check(src) + ..() + +/obj/structure/closet/secure_closet/freezer/Initialize() + ..() + recursive_organ_check(src) + +/obj/structure/closet/secure_closet/freezer/open(mob/living/user) + if(opened || !can_open(user)) //dupe check just so we don't let the organs decay when someone fails to open the locker + return FALSE + recursive_organ_check(src) + return ..() + +/obj/structure/closet/secure_closet/freezer/close(mob/living/user) + if(..()) //if we actually closed the locker + recursive_organ_check(src) + /obj/structure/closet/secure_closet/freezer/ex_act() if(!jones) jones = TRUE diff --git a/code/game/objects/structures/dresser.dm b/code/game/objects/structures/dresser.dm index cdca354563..05e62c196f 100644 --- a/code/game/objects/structures/dresser.dm +++ b/code/game/objects/structures/dresser.dm @@ -79,4 +79,4 @@ var/n_color = input(H, "Choose your [garment_type]'\s color.", "Character Preference", default_color) as color|null if(!n_color || !H.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) return default_color - return sanitize_hexcolor(n_color) + return sanitize_hexcolor(n_color, 3, FALSE, default_color) diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm index 12827a0aba..4a56272ac1 100644 --- a/code/game/objects/structures/ghost_role_spawners.dm +++ b/code/game/objects/structures/ghost_role_spawners.dm @@ -561,7 +561,7 @@ icon = 'icons/obj/machines/sleeper.dmi' icon_state = "sleeper" mob_name = "a space pirate" - mob_species = /datum/species/skeleton/pirate + mob_species = /datum/species/skeleton/space outfit = /datum/outfit/pirate/space roundstart = FALSE death = FALSE diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index 09e5510d62..eaf3629718 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -31,6 +31,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an /obj/structure/bodycontainer/Initialize() . = ..() GLOB.bodycontainers += src + recursive_organ_check(src) /obj/structure/bodycontainer/Destroy() GLOB.bodycontainers -= src @@ -101,6 +102,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an /obj/structure/bodycontainer/deconstruct(disassembled = TRUE) new /obj/item/stack/sheet/metal (loc, 5) + recursive_organ_check(src) qdel(src) /obj/structure/bodycontainer/container_resist(mob/living/user) @@ -120,6 +122,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an open() /obj/structure/bodycontainer/proc/open() + recursive_organ_check(src) playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) playsound(src, 'sound/effects/roll.ogg', 5, 1) var/turf/T = get_step(src, dir) @@ -134,6 +137,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an for(var/atom/movable/AM in connected.loc) if(!AM.anchored || AM == connected) AM.forceMove(src) + recursive_organ_check(src) update_icon() /obj/structure/bodycontainer/get_remote_view_fullscreens(mob/user) diff --git a/code/game/objects/structures/table_frames.dm b/code/game/objects/structures/table_frames.dm index e979d4f18e..f62bed878b 100644 --- a/code/game/objects/structures/table_frames.dm +++ b/code/game/objects/structures/table_frames.dm @@ -22,84 +22,22 @@ var/framestackamount = 2 /obj/structure/table_frame/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/wrench)) + if(I.tool_behaviour == TOOL_WRENCH) to_chat(user, "You start disassembling [src]...") I.play_tool_sound(src) if(I.use_tool(src, user, 30)) - playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) + playsound(src.loc, 'sound/items/deconstruct.ogg', 50, TRUE) deconstruct(TRUE) - else if(istype(I, /obj/item/stack/sheet/plasteel)) - var/obj/item/stack/sheet/plasteel/P = I - if(P.get_amount() < 1) - to_chat(user, "You need one plasteel sheet to do this!") + return + + var/obj/item/stack/material = I + if (istype(I, /obj/item/stack) && material?.tableVariant) + if(material.get_amount() < 1) + to_chat(user, "You need one [material.name] sheet to do this!") return - to_chat(user, "You start adding [P] to [src]...") - if(do_after(user, 50, target = src) && P.use(1)) - make_new_table(/obj/structure/table/reinforced) - else if(istype(I, /obj/item/stack/sheet/metal)) - var/obj/item/stack/sheet/metal/M = I - if(M.get_amount() < 1) - to_chat(user, "You need one metal sheet to do this!") - return - to_chat(user, "You start adding [M] to [src]...") - if(do_after(user, 20, target = src) && M.use(1)) - make_new_table(/obj/structure/table) - else if(istype(I, /obj/item/stack/sheet/glass)) - var/obj/item/stack/sheet/glass/G = I - if(G.get_amount() < 1) - to_chat(user, "You need one glass sheet to do this!") - return - to_chat(user, "You start adding [G] to [src]...") - if(do_after(user, 20, target = src) && G.use(1)) - make_new_table(/obj/structure/table/glass) - else if(istype(I, /obj/item/stack/sheet/mineral/silver)) - var/obj/item/stack/sheet/mineral/silver/S = I - if(S.get_amount() < 1) - to_chat(user, "You need one silver sheet to do this!") - return - to_chat(user, "You start adding [S] to [src]...") - if(do_after(user, 20, target = src) && S.use(1)) - make_new_table(/obj/structure/table/optable) - else if(istype(I, /obj/item/stack/tile/carpet/black)) - var/obj/item/stack/tile/carpet/black/C = I - if(C.get_amount() < 1) - to_chat(user, "You need one black carpet sheet to do this!") - return - to_chat(user, "You start adding [C] to [src]...") - if(do_after(user, 20, target = src) && C.use(1)) - make_new_table(/obj/structure/table/wood/fancy/black) - else if(istype(I, /obj/item/stack/tile/carpet/blackred)) - var/obj/item/stack/tile/carpet/blackred/C = I - if(C.get_amount() < 1) - to_chat(user, "You need one red carpet sheet to do this!") - return - to_chat(user, "You start adding [C] to [src]...") - if(do_after(user, 20, target = src) && C.use(1)) - make_new_table(/obj/structure/table/wood/fancy/blackred) - else if(istype(I, /obj/item/stack/tile/carpet/monochrome)) - var/obj/item/stack/tile/carpet/monochrome/C = I - if(C.get_amount() < 1) - to_chat(user, "You need one monochrome carpet sheet to do this!") - return - to_chat(user, "You start adding [C] to [src]...") - if(do_after(user, 20, target = src) && C.use(1)) - make_new_table(/obj/structure/table/wood/fancy/monochrome) - else if(istype(I, /obj/item/stack/tile/carpet)) - var/obj/item/stack/tile/carpet/C = I - if(C.get_amount() < 1) - to_chat(user, "You need one carpet sheet to do this!") - return - to_chat(user, "You start adding [C] to [src]...") - if(do_after(user, 20, target = src) && C.use(1)) - make_new_table(/obj/structure/table/wood/fancy) - else if(istype(I, /obj/item/stack/tile/bronze)) - var/obj/item/stack/tile/bronze/B = I - if(B.get_amount() < 1) - to_chat(user, "You need one bronze sheet to do this!") - return - to_chat(user, "You start adding [B] to [src]...") - if(do_after(user, 20, target = src) && B.use(1)) - make_new_table(/obj/structure/table/bronze) + to_chat(user, "You start adding [material] to [src]...") + if(do_after(user, 20, target = src) && material.use(1)) + make_new_table(material.tableVariant) else return ..() @@ -135,23 +73,21 @@ resistance_flags = FLAMMABLE /obj/structure/table_frame/wood/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/stack/sheet/mineral/wood)) - var/obj/item/stack/sheet/mineral/wood/W = I - if(W.get_amount() < 1) - to_chat(user, "You need one wood sheet to do this!") - return - to_chat(user, "You start adding [W] to [src]...") - if(do_after(user, 20, target = src) && W.use(1)) - make_new_table(/obj/structure/table/wood) - return - else if(istype(I, /obj/item/stack/tile/carpet)) - var/obj/item/stack/tile/carpet/C = I - if(C.get_amount() < 1) - to_chat(user, "You need one carpet sheet to do this!") - return - to_chat(user, "You start adding [C] to [src]...") - if(do_after(user, 20, target = src) && C.use(1)) - make_new_table(/obj/structure/table/wood/poker) + if (istype(I, /obj/item/stack)) + var/obj/item/stack/material = I + var/toConstruct // stores the table variant + if(istype(I, /obj/item/stack/sheet/mineral/wood)) + toConstruct = /obj/structure/table/wood + else if(istype(I, /obj/item/stack/tile/carpet)) + toConstruct = /obj/structure/table/wood/poker + + if (toConstruct) + if(material.get_amount() < 1) + to_chat(user, "You need one [material.name] sheet to do this!") + return + to_chat(user, "You start adding [material] to [src]...") + if(do_after(user, 20, target = src) && material.use(1)) + make_new_table(toConstruct) else return ..() diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 8c29c1b2c5..b12a26cd00 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -21,6 +21,7 @@ anchored = TRUE layer = TABLE_LAYER climbable = TRUE + obj_flags = CAN_BE_HIT|SHOVABLE_ONTO pass_flags = LETPASSTHROW //You can throw objects over this, despite it's density.") var/frame = /obj/structure/table_frame var/framestack = /obj/item/stack/rods @@ -115,6 +116,9 @@ log_combat(user, pushed_mob, "placed") /obj/structure/table/proc/tablepush(mob/living/user, mob/living/pushed_mob) + if(HAS_TRAIT(user, TRAIT_PACIFISM)) + to_chat(user, "Throwing [pushed_mob] onto the table might hurt them!") + return var/added_passtable = FALSE if(!pushed_mob.pass_flags & PASSTABLE) added_passtable = TRUE @@ -125,14 +129,23 @@ if(pushed_mob.loc != loc) //Something prevented the tabling return pushed_mob.Knockdown(40) - pushed_mob.visible_message("[user] pushes [pushed_mob] onto [src].", \ - "[user] pushes [pushed_mob] onto [src].") - log_combat(user, pushed_mob, "pushed") + pushed_mob.visible_message("[user] slams [pushed_mob] onto [src]!", \ + "[user] slams you onto [src]!") + log_combat(user, pushed_mob, "tabled", null, "onto [src]") if(!ishuman(pushed_mob)) return var/mob/living/carbon/human/H = pushed_mob SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "table", /datum/mood_event/table) +/obj/structure/table/shove_act(mob/living/target, mob/living/user) + if(!target.resting) + target.Knockdown(SHOVE_KNOCKDOWN_TABLE) + user.visible_message("[user.name] shoves [target.name] onto \the [src]!", + "You shove [target.name] onto \the [src]!", null, COMBAT_MESSAGE_RANGE) + target.forceMove(src.loc) + log_combat(user, target, "shoved", "onto [src] (table)") + return TRUE + /obj/structure/table/attackby(obj/item/I, mob/user, params) if(!(flags_1 & NODECONSTRUCT_1)) if(istype(I, /obj/item/screwdriver) && deconstruction_ready) @@ -258,6 +271,53 @@ for(var/obj/item/shard/S in debris) S.color = NARSIE_WINDOW_COLOUR +/* + * Plasmaglass tables + */ +/obj/structure/table/plasmaglass + name = "plasmaglass table" + desc = "A glasstable, but it's pink and more sturdy. What will Nanotrasen design next with plasma?" + icon = 'icons/obj/smooth_structures/plasmaglass_table.dmi' + icon_state = "plasmaglass_table" + climbable = TRUE + buildstack = /obj/item/stack/sheet/plasmaglass + canSmoothWith = null + max_integrity = 270 + resistance_flags = ACID_PROOF + armor = list("melee" = 10, "bullet" = 5, "laser" = 0, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) + var/list/debris = list() + +/obj/structure/table/plasmaglass/New() + . = ..() + debris += new frame + debris += new /obj/item/shard/plasma + +/obj/structure/table/plasmaglass/Destroy() + QDEL_LIST(debris) + . = ..() + +/obj/structure/table/plasmaglass/proc/check_break(mob/living/M) + return + +/obj/structure/table/plasmaglass/deconstruct(disassembled = TRUE, wrench_disassembly = 0) + if(!(flags_1 & NODECONSTRUCT_1)) + if(disassembled) + ..() + return + else + var/turf/T = get_turf(src) + playsound(T, "shatter", 50, 1) + for(var/X in debris) + var/atom/movable/AM = X + AM.forceMove(T) + debris -= AM + qdel(src) + +/obj/structure/table/plasmaglass/narsie_act() + color = NARSIE_WINDOW_COLOUR + for(var/obj/item/shard/S in debris) + S.color = NARSIE_WINDOW_COLOUR + /* * Wooden tables */ @@ -298,44 +358,83 @@ frame = /obj/structure/table_frame framestack = /obj/item/stack/rods buildstack = /obj/item/stack/tile/carpet - canSmoothWith = list(/obj/structure/table/wood/fancy, /obj/structure/table/wood/fancy/black, /obj/structure/table/wood/fancy/blackred, /obj/structure/table/wood/fancy/monochrome) + canSmoothWith = list(/obj/structure/table/wood/fancy, + /obj/structure/table/wood/fancy/black, + /obj/structure/table/wood/fancy/blackred, + /obj/structure/table/wood/fancy/monochrome, + /obj/structure/table/wood/fancy/blue, + /obj/structure/table/wood/fancy/cyan, + /obj/structure/table/wood/fancy/green, + /obj/structure/table/wood/fancy/orange, + /obj/structure/table/wood/fancy/purple, + /obj/structure/table/wood/fancy/red, + /obj/structure/table/wood/fancy/royalblack, + /obj/structure/table/wood/fancy/royalblue) + var/smooth_icon = 'icons/obj/smooth_structures/fancy_table.dmi' // see Initialize() -/obj/structure/table/wood/fancy/New() - // New() is used so that the /black subtype can override `icon` easily and - // the correct value will be used by the smoothing subsystem. +/obj/structure/table/wood/fancy/Initialize() . = ..() // Needs to be set dynamically because table smooth sprites are 32x34, // which the editor treats as a two-tile-tall object. The sprites are that // size so that the north/south corners look nice - examine the detail on // the sprites in the editor to see why. - icon = 'icons/obj/smooth_structures/fancy_table.dmi' + icon = smooth_icon /obj/structure/table/wood/fancy/black icon_state = "fancy_table_black" buildstack = /obj/item/stack/tile/carpet/black + smooth_icon = 'icons/obj/smooth_structures/fancy_table_black.dmi' /obj/structure/table/wood/fancy/blackred - icon = 'icons/obj/structures.dmi' - icon_state = "fancy_table_blackred" - buildstack = /obj/item/stack/tile/carpet/blackred - -/obj/structure/table/wood/fancy/blackred/New() - . = ..() - icon = 'icons/obj/smooth_structures/fancy_table_blackred.dmi' + icon_state = "fancy_table_blackred" + buildstack = /obj/item/stack/tile/carpet/blackred + smooth_icon = 'icons/obj/smooth_structures/fancy_table_blackred.dmi' /obj/structure/table/wood/fancy/monochrome - icon = 'icons/obj/structures.dmi' - icon_state = "fancy_table_monochrome" - buildstack = /obj/item/stack/tile/carpet/monochrome + icon_state = "fancy_table_monochrome" + buildstack = /obj/item/stack/tile/carpet/monochrome + smooth_icon = 'icons/obj/smooth_structures/fancy_table_monochrome.dmi' -/obj/structure/table/wood/fancy/monochrome/New() - . = ..() - icon = 'icons/obj/smooth_structures/fancy_table_monochrome.dmi' +/obj/structure/table/wood/fancy/blue + icon_state = "fancy_table_blue" + buildstack = /obj/item/stack/tile/carpet/blue + smooth_icon = 'icons/obj/smooth_structures/fancy_table_blue.dmi' + +/obj/structure/table/wood/fancy/cyan + icon_state = "fancy_table_cyan" + buildstack = /obj/item/stack/tile/carpet/cyan + smooth_icon = 'icons/obj/smooth_structures/fancy_table_cyan.dmi' + +/obj/structure/table/wood/fancy/green + icon_state = "fancy_table_green" + buildstack = /obj/item/stack/tile/carpet/green + smooth_icon = 'icons/obj/smooth_structures/fancy_table_green.dmi' + +/obj/structure/table/wood/fancy/orange + icon_state = "fancy_table_orange" + buildstack = /obj/item/stack/tile/carpet/orange + smooth_icon = 'icons/obj/smooth_structures/fancy_table_orange.dmi' + +/obj/structure/table/wood/fancy/purple + icon_state = "fancy_table_purple" + buildstack = /obj/item/stack/tile/carpet/purple + smooth_icon = 'icons/obj/smooth_structures/fancy_table_purple.dmi' + +/obj/structure/table/wood/fancy/red + icon_state = "fancy_table_red" + buildstack = /obj/item/stack/tile/carpet/red + smooth_icon = 'icons/obj/smooth_structures/fancy_table_red.dmi' + +/obj/structure/table/wood/fancy/royalblack + icon_state = "fancy_table_royalblack" + buildstack = /obj/item/stack/tile/carpet/royalblack + smooth_icon = 'icons/obj/smooth_structures/fancy_table_royalblack.dmi' + +/obj/structure/table/wood/fancy/royalblue + icon_state = "fancy_table_royalblue" + buildstack = /obj/item/stack/tile/carpet/royalblue + smooth_icon = 'icons/obj/smooth_structures/fancy_table_royalblue.dmi' -/obj/structure/table/wood/fancy/black/New() - . = ..() - // Ditto above. - icon = 'icons/obj/smooth_structures/fancy_table_black.dmi' /* * Reinforced tables */ diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index a26d57e3e1..9fc055c2ba 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -281,6 +281,8 @@ . += new /obj/effect/decal/cleanable/glass(location) if (reinf) . += new /obj/item/stack/rods(location, (fulltile ? 2 : 1)) + if (fulltile) + . += new /obj/item/shard(location) /obj/structure/window/proc/can_be_rotated(mob/user,rotation_type) if(anchored) @@ -409,6 +411,15 @@ glass_type = /obj/item/stack/sheet/plasmaglass rad_insulation = RAD_NO_INSULATION +/obj/structure/window/plasma/spawnDebris(location) + . = list() + . += new /obj/item/shard/plasma(location) + . += new /obj/effect/decal/cleanable/glass/plasma(location) + if (reinf) + . += new /obj/item/stack/rods(location, (fulltile ? 2 : 1)) + if (fulltile) + . += new /obj/item/shard/plasma(location) + /obj/structure/window/plasma/spawner/east dir = EAST diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index b00efc7ed6..194014b61a 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -236,6 +236,10 @@ return list("mode" = RCD_DECONSTRUCT, "delay" = 50, "cost" = 33) if(RCD_WINDOWGRILLE) return list("mode" = RCD_WINDOWGRILLE, "delay" = 10, "cost" = 4) + if(RCD_MACHINE) + return list("mode" = RCD_MACHINE, "delay" = 20, "cost" = 25) + if(RCD_COMPUTER) + return list("mode" = RCD_COMPUTER, "delay" = 20, "cost" = 25) return FALSE /turf/open/floor/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode) @@ -274,4 +278,20 @@ var/obj/structure/grille/G = new(src) G.anchored = TRUE return TRUE + if(RCD_MACHINE) + if(locate(/obj/structure/frame/machine) in src) + return FALSE + var/obj/structure/frame/machine/M = new(src) + M.state = 2 + M.icon_state = "box_1" + M.anchored = TRUE + return TRUE + if(RCD_COMPUTER) + if(locate(/obj/structure/frame/computer) in src) + return FALSE + var/obj/structure/frame/computer/C = new(src) + C.anchored = TRUE + C.setDir(the_rcd.computer_dir) + return TRUE + return FALSE diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm index 7b45aa1fbb..ed6e279088 100644 --- a/code/game/turfs/simulated/floor/fancy_floor.dm +++ b/code/game/turfs/simulated/floor/fancy_floor.dm @@ -209,6 +209,46 @@ icon_state = "tile-carpet-monochrome" canSmoothWith = list(/turf/open/floor/carpet/black, /turf/open/floor/carpet/blackred, /turf/open/floor/carpet/monochrome) +/turf/open/floor/carpet/blue + icon = 'icons/turf/floors/carpet_blue.dmi' + floor_tile = /obj/item/stack/tile/carpet/blue + canSmoothWith = list(/turf/open/floor/carpet/blue) + +/turf/open/floor/carpet/cyan + icon = 'icons/turf/floors/carpet_cyan.dmi' + floor_tile = /obj/item/stack/tile/carpet/cyan + canSmoothWith = list(/turf/open/floor/carpet/cyan) + +/turf/open/floor/carpet/green + icon = 'icons/turf/floors/carpet_green.dmi' + floor_tile = /obj/item/stack/tile/carpet/green + canSmoothWith = list(/turf/open/floor/carpet/green) + +/turf/open/floor/carpet/orange + icon = 'icons/turf/floors/carpet_orange.dmi' + floor_tile = /obj/item/stack/tile/carpet/orange + canSmoothWith = list(/turf/open/floor/carpet/orange) + +/turf/open/floor/carpet/purple + icon = 'icons/turf/floors/carpet_purple.dmi' + floor_tile = /obj/item/stack/tile/carpet/purple + canSmoothWith = list(/turf/open/floor/carpet/purple) + +/turf/open/floor/carpet/red + icon = 'icons/turf/floors/carpet_red.dmi' + floor_tile = /obj/item/stack/tile/carpet/red + canSmoothWith = list(/turf/open/floor/carpet/red) + +/turf/open/floor/carpet/royalblack + icon = 'icons/turf/floors/carpet_royalblack.dmi' + floor_tile = /obj/item/stack/tile/carpet/royalblack + canSmoothWith = list(/turf/open/floor/carpet/royalblack) + +/turf/open/floor/carpet/royalblue + icon = 'icons/turf/floors/carpet_royalblue.dmi' + floor_tile = /obj/item/stack/tile/carpet/royalblue + canSmoothWith = list(/turf/open/floor/carpet/royalblue) + /turf/open/floor/carpet/narsie_act(force, ignore_mobs, probability = 20) . = (prob(probability) || force) for(var/I in src) diff --git a/code/game/turfs/simulated/wall/misc_walls.dm b/code/game/turfs/simulated/wall/misc_walls.dm index dfc6972578..8b63d60939 100644 --- a/code/game/turfs/simulated/wall/misc_walls.dm +++ b/code/game/turfs/simulated/wall/misc_walls.dm @@ -7,6 +7,7 @@ smooth = SMOOTH_MORE sheet_type = /obj/item/stack/sheet/runed_metal sheet_amount = 1 + explosion_block = 10 girder_type = /obj/structure/girder/cult /turf/closed/wall/mineral/cult/Initialize() @@ -49,7 +50,7 @@ /turf/closed/wall/clockwork name = "clockwork wall" desc = "A huge chunk of warm metal. The clanging of machinery emanates from within." - explosion_block = 2 + explosion_block = 5 hardness = 10 slicing_duration = 80 sheet_type = /obj/item/stack/tile/brass diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 7110ff4405..181b72e4a2 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -382,6 +382,19 @@ if(ismob(A) || .) A.ratvar_act() +//called on /datum/species/proc/altdisarm() +/turf/shove_act(mob/living/target, mob/living/user, pre_act = FALSE) + var/list/possibilities + for(var/obj/O in contents) + if(CHECK_BITFIELD(O.obj_flags, SHOVABLE_ONTO)) + LAZYADD(possibilities, O) + else if(!O.CanPass(target, src)) + return FALSE + if(possibilities) + var/obj/O = pick(possibilities) + return O.shove_act(target, user) + return FALSE + /turf/proc/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) underlay_appearance.icon = icon underlay_appearance.icon_state = icon_state diff --git a/code/game/world.dm b/code/game/world.dm index e9c8433006..25b8c4d9f3 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -100,6 +100,7 @@ GLOBAL_VAR(restart_counter) GLOB.picture_log_directory = "data/picture_logs/[override_dir]" GLOB.world_game_log = "[GLOB.log_directory]/game.log" + GLOB.world_virus_log = "[GLOB.log_directory]/virus.log" GLOB.world_attack_log = "[GLOB.log_directory]/attack.log" GLOB.world_pda_log = "[GLOB.log_directory]/pda.log" GLOB.world_telecomms_log = "[GLOB.log_directory]/telecomms.log" diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index b400f44b98..555c35980d 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -423,6 +423,25 @@ 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)
" + if (GLOB.dynamic_forced_roundstart_ruleset.len > 0) + for(var/datum/dynamic_ruleset/roundstart/rule in GLOB.dynamic_forced_roundstart_ruleset) + dat += {"-> [rule.name] <-
"} + dat += "(Clear Rulesets)
" + dat += "(Dynamic mode options)
" + else if (SSticker.IsRoundInProgress()) + dat += "(Force Next Latejoin Ruleset)
" + if (SSticker && SSticker.mode && istype(SSticker.mode,/datum/game_mode/dynamic)) + var/datum/game_mode/dynamic/mode = SSticker.mode + if (mode.forced_latejoin_rule) + dat += {"-> [mode.forced_latejoin_rule.name] <-
"} + dat += "(Execute Midround Ruleset!)
" + dat += "
" + if(SSticker.IsRoundInProgress()) + dat += "(Game Mode Panel)
" + dat += {"
Create Object
@@ -839,6 +858,44 @@ browser.set_content(dat.Join()) browser.open() +/datum/admins/proc/dynamic_mode_options(mob/user) + var/dat = {" +

Dynamic Mode Options


+
+

Common options

+ All these options can be changed midround.
+
+ Force extended: - Option is [GLOB.dynamic_forced_extended ? "ON" : "OFF"]. +
This will force the round to be extended. No rulesets will be drafted.
+
+ No stacking: - Option is [GLOB.dynamic_no_stacking ? "ON" : "OFF"]. +
Unless the threat goes above [GLOB.dynamic_stacking_limit], only one "round-ender" ruleset will be drafted.
+
+ Classic secret mode: - Option is [GLOB.dynamic_classic_secret ? "ON" : "OFF"]. +
Only one roundstart ruleset will be drafted. Only traitors and minor roles will latespawn.
+
+
+ Forced threat level: Current value : [GLOB.dynamic_forced_threat_level]. +
The value threat is set to if it is higher than -1.
+
+ High population limit: Current value : [GLOB.dynamic_high_pop_limit]. +
The threshold at which "high population override" will be in effect.
+
+ Stacking threeshold: Current value : [GLOB.dynamic_stacking_limit]. +
The threshold at which "round-ender" rulesets will stack. A value higher than 100 ensure this never happens.
+

Advanced parameters

+ Curve centre: -> [GLOB.dynamic_curve_centre] <-
+ Curve width: -> [GLOB.dynamic_curve_width] <-
+ Latejoin injection delay:
+ Minimum: -> [GLOB.dynamic_latejoin_delay_min / 60 / 10] <- Minutes
+ Maximum: -> [GLOB.dynamic_latejoin_delay_max / 60 / 10] <- Minutes
+ Midround injection delay:
+ Minimum: -> [GLOB.dynamic_midround_delay_min / 60 / 10] <- Minutes
+ Maximum: -> [GLOB.dynamic_midround_delay_max / 60 / 10] <- Minutes
+ "} + + user << browse(dat, "window=dyn_mode_options;size=900x650") + /datum/admins/proc/create_or_modify_area() set category = "Debug" set name = "Create or modify area" diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index ac1ae51f69..626fa7f66f 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -169,6 +169,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug()) /client/proc/cmd_display_overlay_log, /client/proc/reload_configuration, /datum/admins/proc/create_or_modify_area, + /client/proc/generate_wikichem_list //DO NOT PRESS UNLESS YOU WANT SUPERLAG ) GLOBAL_PROTECT(admin_verbs_possess) GLOBAL_LIST_INIT(admin_verbs_possess, list(/proc/possess, /proc/release)) diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm index f66cda42dc..8b6f471e7d 100644 --- a/code/modules/admin/create_mob.dm +++ b/code/modules/admin/create_mob.dm @@ -34,7 +34,8 @@ H.dna.features["frills"] = pick(GLOB.frills_list) H.dna.features["spines"] = pick(GLOB.spines_list) H.dna.features["body_markings"] = pick(GLOB.body_markings_list) - H.dna.features["moth_wings"] = pick(GLOB.moth_wings_list) + H.dna.features["insect_wings"] = pick(GLOB.insect_wings_list) + H.dna.features["insect_fluff"] = pick(GLOB.insect_fluffs_list) H.update_body() H.update_hair() diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index 7d74387c43..9dc8b73d0e 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -458,7 +458,7 @@ SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Mass Braindamage")) for(var/mob/living/carbon/human/H in GLOB.player_list) to_chat(H, "You suddenly feel stupid.") - H.adjustBrainLoss(60, 80) + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 60, 80) message_admins("[key_name_admin(usr)] made everybody retarded") if("eagles")//SCRAW diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index d881044757..b0b9190556 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -291,6 +291,11 @@ else if(href_list["editrights"]) edit_rights_topic(href_list) + else if(href_list["gamemode_panel"]) + if(!check_rights(R_ADMIN)) + return + SSticker.mode.admin_panel() + else if(href_list["call_shuttle"]) if(!check_rights(R_ADMIN)) return @@ -1342,6 +1347,291 @@ else if(href_list["f_secret"]) return HandleFSecret() + + else if(href_list["f_dynamic_roundstart"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode.", null, null, null, null) + var/roundstart_rules = list() + for (var/rule in subtypesof(/datum/dynamic_ruleset/roundstart)) + var/datum/dynamic_ruleset/roundstart/newrule = new rule() + roundstart_rules[newrule.name] = newrule + var/added_rule = input(usr,"What ruleset do you want to force? This will bypass threat level and population restrictions.", "Rigging Roundstart", null) as null|anything in roundstart_rules + if (added_rule) + GLOB.dynamic_forced_roundstart_ruleset += roundstart_rules[added_rule] + log_admin("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.") + message_admins("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.", 1) + Game() + + else if(href_list["f_dynamic_roundstart_clear"]) + if(!check_rights(R_ADMIN)) + return + GLOB.dynamic_forced_roundstart_ruleset = list() + Game() + log_admin("[key_name(usr)] cleared the rigged roundstart rulesets. The mode will pick them as normal.") + message_admins("[key_name(usr)] cleared the rigged roundstart rulesets. The mode will pick them as normal.", 1) + + else if(href_list["f_dynamic_roundstart_remove"]) + if(!check_rights(R_ADMIN)) + return + var/datum/dynamic_ruleset/roundstart/rule = locate(href_list["f_dynamic_roundstart_remove"]) + GLOB.dynamic_forced_roundstart_ruleset -= rule + Game() + log_admin("[key_name(usr)] removed [rule] from the forced roundstart rulesets.") + message_admins("[key_name(usr)] removed [rule] from the forced roundstart rulesets.", 1) + + else if(href_list["f_dynamic_latejoin"]) + if(!check_rights(R_ADMIN)) + return + if(!SSticker || !SSticker.mode) + return alert(usr, "The game must start first.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/latejoin_rules = list() + for (var/rule in subtypesof(/datum/dynamic_ruleset/latejoin)) + var/datum/dynamic_ruleset/latejoin/newrule = new rule() + latejoin_rules[newrule.name] = newrule + var/added_rule = input(usr,"What ruleset do you want to force upon the next latejoiner? This will bypass threat level and population restrictions.", "Rigging Latejoin", null) as null|anything in latejoin_rules + if (added_rule) + var/datum/game_mode/dynamic/mode = SSticker.mode + mode.forced_latejoin_rule = latejoin_rules[added_rule] + log_admin("[key_name(usr)] set [added_rule] to proc on the next latejoin.") + message_admins("[key_name(usr)] set [added_rule] to proc on the next latejoin.", 1) + Game() + + else if(href_list["f_dynamic_latejoin_clear"]) + if(!check_rights(R_ADMIN)) + return + if (SSticker && SSticker.mode && istype(SSticker.mode,/datum/game_mode/dynamic)) + var/datum/game_mode/dynamic/mode = SSticker.mode + mode.forced_latejoin_rule = null + Game() + log_admin("[key_name(usr)] cleared the forced latejoin ruleset.") + message_admins("[key_name(usr)] cleared the forced latejoin ruleset.", 1) + + else if(href_list["f_dynamic_midround"]) + if(!check_rights(R_ADMIN)) + return + if(!SSticker || !SSticker.mode) + return alert(usr, "The game must start first.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/midround_rules = list() + for (var/rule in subtypesof(/datum/dynamic_ruleset/midround)) + var/datum/dynamic_ruleset/midround/newrule = new rule() + midround_rules[newrule.name] = rule + var/added_rule = input(usr,"What ruleset do you want to force right now? This will bypass threat level and population restrictions.", "Execute Ruleset", null) as null|anything in midround_rules + if (added_rule) + var/datum/game_mode/dynamic/mode = SSticker.mode + log_admin("[key_name(usr)] executed the [added_rule] ruleset.") + message_admins("[key_name(usr)] executed the [added_rule] ruleset.", 1) + mode.picking_specific_rule(midround_rules[added_rule],1) + + else if (href_list["f_dynamic_options"]) + if(!check_rights(R_ADMIN)) + return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_centre"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_centre = input(usr,"Change the centre of the dynamic mode threat curve. A negative value will give a more peaceful round ; a positive value, a round with higher threat. Any number between -5 and +5 is allowed.", "Change curve centre", null) as num + if (new_centre < -5 || new_centre > 5) + return alert(usr, "Only values between -5 and +5 are allowed.", null, null, null, null) + + log_admin("[key_name(usr)] changed the distribution curve center to [new_centre].") + message_admins("[key_name(usr)] changed the distribution curve center to [new_centre]", 1) + GLOB.dynamic_curve_centre = new_centre + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_width"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_width = input(usr,"Change the width of the dynamic mode threat curve. A higher value will favour extreme rounds ; a lower value, a round closer to the average. Any Number between 0.5 and 4 are allowed.", "Change curve width", null) as num + if (new_width < 0.5 || new_width > 4) + return alert(usr, "Only values between 0.5 and +2.5 are allowed.", null, null, null, null) + + log_admin("[key_name(usr)] changed the distribution curve width to [new_width].") + message_admins("[key_name(usr)] changed the distribution curve width to [new_width]", 1) + GLOB.dynamic_curve_width = new_width + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_latejoin_min"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_min = input(usr,"Change the minimum delay of latejoin injection in minutes.", "Change latejoin injection delay minimum", null) as num + if(new_min <= 0) + return alert(usr, "The minimum can't be zero or lower.", null, null, null, null) + if((new_min MINUTES) > GLOB.dynamic_latejoin_delay_max) + return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes.") + message_admins("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes", 1) + GLOB.dynamic_latejoin_delay_min = (new_min MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_latejoin_max"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_max = input(usr,"Change the maximum delay of latejoin injection in minutes.", "Change latejoin injection delay maximum", null) as num + if(new_max <= 0) + return alert(usr, "The maximum can't be zero or lower.", null, null, null, null) + if((new_max MINUTES) < GLOB.dynamic_latejoin_delay_min) + return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes.") + message_admins("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes", 1) + GLOB.dynamic_latejoin_delay_max = (new_max MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_midround_min"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_min = input(usr,"Change the minimum delay of midround injection in minutes.", "Change midround injection delay minimum", null) as num + if(new_min <= 0) + return alert(usr, "The minimum can't be zero or lower.", null, null, null, null) + if((new_min MINUTES) > GLOB.dynamic_midround_delay_max) + return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes.") + message_admins("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes", 1) + GLOB.dynamic_midround_delay_min = (new_min MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_midround_max"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_max = input(usr,"Change the maximum delay of midround injection in minutes.", "Change midround injection delay maximum", null) as num + if(new_max <= 0) + return alert(usr, "The maximum can't be zero or lower.", null, null, null, null) + if((new_max MINUTES) > GLOB.dynamic_midround_delay_max) + return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes.") + message_admins("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes", 1) + GLOB.dynamic_midround_delay_max = (new_max MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_force_extended"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_forced_extended = !GLOB.dynamic_forced_extended + log_admin("[key_name(usr)] set 'forced_extended' to [GLOB.dynamic_forced_extended].") + message_admins("[key_name(usr)] set 'forced_extended' to [GLOB.dynamic_forced_extended].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_no_stacking"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_no_stacking = !GLOB.dynamic_no_stacking + log_admin("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].") + message_admins("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_classic_secret"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_classic_secret = !GLOB.dynamic_classic_secret + log_admin("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].") + message_admins("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_stacking_limit"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_stacking_limit = input(usr,"Change the threat limit at which round-endings rulesets will start to stack.", "Change stacking limit", null) as num + log_admin("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].") + message_admins("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_high_pop_limit"]) + if(!check_rights(R_ADMIN)) + return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_value = input(usr, "Enter the high-pop override threshold for dynamic mode.", "High pop override") as num + if (new_value < 0) + return alert(usr, "Only positive values allowed!", null, null, null, null) + GLOB.dynamic_high_pop_limit = new_value + + log_admin("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].") + message_admins("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_forced_threat"]) + if(!check_rights(R_ADMIN)) + return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_value = input(usr, "Enter the forced threat level for dynamic mode.", "Forced threat level") as num + if (new_value > 100) + return alert(usr, "The value must be be under 100.", null, null, null, null) + GLOB.dynamic_forced_threat_level = new_value + + log_admin("[key_name(usr)] set 'forced_threat_level' to [GLOB.dynamic_forced_threat_level].") + message_admins("[key_name(usr)] set 'forced_threat_level' to [GLOB.dynamic_forced_threat_level].") + dynamic_mode_options(usr) else if(href_list["c_mode2"]) if(!check_rights(R_ADMIN|R_SERVER)) @@ -1752,7 +2042,7 @@ if(DEAD) status = "Dead" health_description = "Status = [status]" - health_description += "
Oxy: [L.getOxyLoss()] - Tox: [L.getToxLoss()] - Fire: [L.getFireLoss()] - Brute: [L.getBruteLoss()] - Clone: [L.getCloneLoss()] - Brain: [L.getBrainLoss()] - Stamina: [L.getStaminaLoss()]" + health_description += "
Oxy: [L.getOxyLoss()] - Tox: [L.getToxLoss()] - Fire: [L.getFireLoss()] - Brute: [L.getBruteLoss()] - Clone: [L.getCloneLoss()] - Brain: [L.getOrganLoss(ORGAN_SLOT_BRAIN)] - Stamina: [L.getStaminaLoss()]" else health_description = "This mob type has no health to speak of." diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index a725399b0f..c27b355ff1 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1264,7 +1264,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits target.electrocution_animation(40) to_chat(target, "The gods have punished you for your sins!") if(ADMIN_PUNISHMENT_BRAINDAMAGE) - target.adjustBrainLoss(199, 199) + target.adjustOrganLoss(ORGAN_SLOT_BRAIN, 199, 199) if(ADMIN_PUNISHMENT_GIB) target.gib(FALSE) if(ADMIN_PUNISHMENT_BSA) diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm index 8e29b38fe1..0fca957ffe 100644 --- a/code/modules/antagonists/abductor/abductor.dm +++ b/code/modules/antagonists/abductor/abductor.dm @@ -56,6 +56,7 @@ /datum/antagonist/abductor/greet() to_chat(owner.current, "You are the [owner.special_role]!") to_chat(owner.current, "With the help of your teammate, kidnap and experiment on station crew members!") + to_chat(owner.current, "Try not to disturb the habitat, it could lead to dead specimens.") to_chat(owner.current, "[greet_text]") owner.announce_objectives() diff --git a/code/modules/antagonists/abductor/equipment/abduction_surgery.dm b/code/modules/antagonists/abductor/equipment/abduction_surgery.dm index 819dbafd6a..98164de099 100644 --- a/code/modules/antagonists/abductor/equipment/abduction_surgery.dm +++ b/code/modules/antagonists/abductor/equipment/abduction_surgery.dm @@ -1,5 +1,5 @@ /datum/surgery/organ_extraction - name = "experimental dissection" + name = "experimental organ replacement" steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/retract_skin, /datum/surgery_step/incise, /datum/surgery_step/extract_organ, /datum/surgery_step/gland_insert) possible_locs = list(BODY_ZONE_CHEST) ignore_clothes = 1 diff --git a/code/modules/antagonists/abductor/equipment/gland.dm b/code/modules/antagonists/abductor/equipment/gland.dm index 8a3ff2186a..8e722ec33e 100644 --- a/code/modules/antagonists/abductor/equipment/gland.dm +++ b/code/modules/antagonists/abductor/equipment/gland.dm @@ -151,7 +151,7 @@ if(2) to_chat(H, "You hear an annoying buzz in your head.") H.confused += 15 - H.adjustBrainLoss(10, 160) + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10, 160) if(3) H.hallucination += 60 @@ -167,7 +167,7 @@ /obj/item/organ/heart/gland/pop/activate() to_chat(owner, "You feel unlike yourself.") randomize_human(owner) - var/species = pick(list(/datum/species/human, /datum/species/lizard, /datum/species/moth, /datum/species/fly)) + var/species = pick(list(/datum/species/human, /datum/species/lizard, /datum/species/insect, /datum/species/fly)) owner.set_species(species) /obj/item/organ/heart/gland/ventcrawling diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm index 252e2b5cbe..b6e3d66c35 100644 --- a/code/modules/antagonists/changeling/changeling.dm +++ b/code/modules/antagonists/changeling/changeling.dm @@ -83,7 +83,7 @@ if(istype(C)) var/obj/item/organ/brain/B = C.getorganslot(ORGAN_SLOT_BRAIN) if(B && (B.decoy_override != initial(B.decoy_override))) - B.vital = TRUE + B.organ_flags |= ORGAN_VITAL B.decoy_override = FALSE remove_changeling_powers() . = ..() @@ -340,7 +340,7 @@ if(istype(C)) var/obj/item/organ/brain/B = C.getorganslot(ORGAN_SLOT_BRAIN) if(B) - B.vital = FALSE + B.organ_flags &= ~ORGAN_VITAL B.decoy_override = TRUE update_changeling_icons_added() return diff --git a/code/modules/antagonists/changeling/powers/regenerate.dm b/code/modules/antagonists/changeling/powers/regenerate.dm index a1f8e1ef9b..1b27fa9694 100644 --- a/code/modules/antagonists/changeling/powers/regenerate.dm +++ b/code/modules/antagonists/changeling/powers/regenerate.dm @@ -35,7 +35,7 @@ B = new C.dna.species.mutant_brain() else B = new() - B.vital = FALSE + B.organ_flags &= ~ORGAN_VITAL B.decoy_override = TRUE B.Insert(C) if(ishuman(user)) diff --git a/code/modules/antagonists/changeling/powers/strained_muscles.dm b/code/modules/antagonists/changeling/powers/strained_muscles.dm index bdbd38b92d..081b1181dc 100644 --- a/code/modules/antagonists/changeling/powers/strained_muscles.dm +++ b/code/modules/antagonists/changeling/powers/strained_muscles.dm @@ -34,6 +34,7 @@ return TRUE /obj/effect/proc_holder/changeling/strained_muscles/proc/muscle_loop(mob/living/carbon/user) + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) while(active) ADD_TRAIT(user, TRAIT_GOTTAGOFAST, "changeling_muscles") if(user.stat != CONSCIOUS || user.staminaloss >= 90) @@ -41,6 +42,7 @@ to_chat(user, "Our muscles relax without the energy to strengthen them.") user.Knockdown(40) REMOVE_TRAIT(user, TRAIT_GOTTAGOFAST, "changeling_muscles") + changeling.chem_recharge_slowdown -= 0.5 break stacks++ diff --git a/code/modules/antagonists/clockcult/clock_items/clockwork_armor.dm b/code/modules/antagonists/clockcult/clock_items/clockwork_armor.dm index 0ac96c47f9..88cf420420 100644 --- a/code/modules/antagonists/clockcult/clock_items/clockwork_armor.dm +++ b/code/modules/antagonists/clockcult/clock_items/clockwork_armor.dm @@ -50,7 +50,7 @@ to_chat(user, "The helmet tries to drive a spike through your head as you scramble to remove it!") user.emote("scream") user.apply_damage(30, BRUTE, BODY_ZONE_HEAD) - user.adjustBrainLoss(30) + user.adjustOrganLoss(ORGAN_SLOT_BRAIN, 30) addtimer(CALLBACK(user, /mob/living.proc/dropItemToGround, src, TRUE), 1) //equipped happens before putting stuff on(but not before picking items up), 1). thus, we need to wait for it to be on before forcing it off. /obj/item/clothing/head/helmet/clockwork/mob_can_equip(mob/M, mob/equipper, slot, disable_warning = 0) diff --git a/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm b/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm index 56b36d13e6..f735d6bb29 100644 --- a/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm +++ b/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm @@ -132,9 +132,9 @@ continue if(isliving(M.current) && M.current.stat != DEAD) var/turf/t_turf = isAI(M.current) ? get_step(get_step(src, NORTH),NORTH) : get_turf(src) // AI too fat, must make sure it always ends up a 2 tiles north instead of on the ark. - do_teleport(M, t_turf, channel = TELEPORT_CHANNEL_CULT, forced = TRUE) - M.current.overlay_fullscreen("flash", /obj/screen/fullscreen/flash) - M.current.clear_fullscreen("flash", 5) + do_teleport(M.current, t_turf, channel = TELEPORT_CHANNEL_CULT, forced = TRUE) + M.current.overlay_fullscreen("flash", /obj/screen/fullscreen/flash) + M.current.clear_fullscreen("flash", 5) playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 50, FALSE) recalls_remaining-- recalling = FALSE diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm index 1dbae4ca98..fc4d945d51 100644 --- a/code/modules/antagonists/cult/blood_magic.dm +++ b/code/modules/antagonists/cult/blood_magic.dm @@ -4,6 +4,7 @@ desc = "Prepare blood magic by carving runes into your flesh. This rite is most effective with an empowering rune" var/list/spells = list() var/channeling = FALSE + var/holy_dispel = FALSE /datum/action/innate/cult/blood_magic/Grant() ..() @@ -33,6 +34,9 @@ B.button.moved = B.button.screen_loc /datum/action/innate/cult/blood_magic/Activate() + if(holy_dispel) + to_chat(owner, "Holy water currently scours your body, nullifying the power of the rites!") + return var/rune = FALSE var/limit = RUNELESS_MAX_BLOODCHARGE for(var/obj/effect/rune/empower/R in range(1, owner)) @@ -64,7 +68,7 @@ qdel(nullify_spell) return BS = possible_spells[entered_spell_name] - if(QDELETED(src) || owner.incapacitated() || !BS || (rune && !(locate(/obj/effect/rune/empower) in range(1, owner))) || (spells.len >= limit)) + if(QDELETED(src) || owner.incapacitated() || !BS || holy_dispel || (rune && !(locate(/obj/effect/rune/empower) in range(1, owner))) || (spells.len >= limit)) return to_chat(owner,"You begin to carve unnatural symbols into your flesh!") SEND_SOUND(owner, sound('sound/weapons/slice.ogg',0,1,10)) @@ -73,7 +77,7 @@ else to_chat(owner, "You are already invoking blood magic!") return - if(do_after(owner, 100 - rune*60, target = owner)) + if(do_after(owner, 100 - rune*60, target = owner) && !holy_dispel) if(ishuman(owner)) var/mob/living/carbon/human/H = owner H.bleed(40 - rune*32) @@ -644,6 +648,11 @@ desc = "A spell that will absorb blood from anything you touch.
Touching cultists and constructs can heal them.
Clicking the hand will potentially let you focus the spell into something stronger." color = "#7D1717" +/obj/item/melee/blood_magic/manipulator/examine(mob/user) + . = ..() + if(iscultist(user)) + to_chat(user, "The [name] currently has [uses] blood charges left.") + /obj/item/melee/blood_magic/manipulator/afterattack(atom/target, mob/living/carbon/human/user, proximity) if(proximity) if(ishuman(target)) @@ -678,9 +687,9 @@ if(ratio>1) ratio = 1 uses -= round(overall_damage) - H.visible_message("[H] is fully healed by [H==user ? "[H.p_their()]":"[H]'s"]'s blood magic!") + H.visible_message("[H] is fully healed by [H==user ? "[H.p_their()]":"[user]'s"] blood magic!") else - H.visible_message("[H] is partially healed by [H==user ? "[H.p_their()]":"[H]'s"] blood magic.") + H.visible_message("[H] is partially healed by [H==user ? "[H.p_their()]":"[user]'s"] blood magic.") uses = 0 ratio *= -1 H.adjustOxyLoss((overall_damage*ratio) * (H.getOxyLoss() / overall_damage), 0) @@ -762,7 +771,7 @@ switch(choice) if("Blood Spear (150)") if(uses < 150) - to_chat(user, "You need 200 charges to perform this rite.") + to_chat(user, "You need 150 charges to perform this rite.") else uses -= 150 var/turf/T = get_turf(user) @@ -778,7 +787,7 @@ "A [rite.name] materializes at your feet.") if("Blood Bolt Barrage (300)") if(uses < 300) - to_chat(user, "You need 400 charges to perform this rite.") + to_chat(user, "You need 300 charges to perform this rite.") else var/obj/rite = new /obj/item/gun/ballistic/shotgun/boltaction/enchanted/arcane_barrage/blood() uses -= 300 @@ -790,7 +799,7 @@ qdel(rite) if("Blood Beam (500)") if(uses < 500) - to_chat(user, "You need 600 charges to perform this rite.") + to_chat(user, "You need 500 charges to perform this rite.") else var/obj/rite = new /obj/item/blood_beam() uses -= 500 diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm index 25e3663c0b..10759afcd0 100644 --- a/code/modules/antagonists/cult/cult_items.dm +++ b/code/modules/antagonists/cult/cult_items.dm @@ -666,6 +666,7 @@ righthand_file = 'icons/mob/inhands/weapons/polearms_righthand.dmi' slot_flags = 0 force = 17 + force_unwielded = 17 force_wielded = 24 throwforce = 40 throw_speed = 2 diff --git a/code/modules/antagonists/cult/cult_structures.dm b/code/modules/antagonists/cult/cult_structures.dm index 499d7a861e..0dd6b08c4d 100644 --- a/code/modules/antagonists/cult/cult_structures.dm +++ b/code/modules/antagonists/cult/cult_structures.dm @@ -73,6 +73,10 @@ animate(src, color = previouscolor, time = 8) addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8) +/obj/structure/destructible/cult/proc/check_menu(mob/living/user) + if(!user || user.incapacitated() || !iscultist(user) || !anchored || cooldowntime > world.time) + return FALSE + return TRUE /obj/structure/destructible/cult/talisman name = "altar" @@ -80,9 +84,18 @@ icon_state = "talismanaltar" break_message = "The altar shatters, leaving only the wailing of the damned!" -/obj/structure/destructible/cult/talisman/attack_hand(mob/living/user) + var/static/image/radial_whetstone = image(icon = 'icons/obj/kitchen.dmi', icon_state = "cult_sharpener") + var/static/image/radial_shell = image(icon = 'icons/obj/wizard.dmi', icon_state = "construct-cult") + var/static/image/radial_unholy_water = image(icon = 'icons/obj/drinks.dmi', icon_state = "holyflask") + +/obj/structure/destructible/cult/talisman/Initialize() . = ..() - if(.) + radial_unholy_water.color = "#333333" + +/obj/structure/destructible/cult/talisman/ui_interact(mob/user) + . = ..() + + if(!user.canUseTopic(src, TRUE)) return if(!iscultist(user)) to_chat(user, "You're pretty sure you know exactly what this is used for and you can't seem to touch it.") @@ -91,22 +104,27 @@ to_chat(user, "You need to anchor [src] to the floor with your dagger first.") return if(cooldowntime > world.time) - to_chat(user, "The magic in [src] is weak, it will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].") + to_chat(user, "The magic in [src] is weak, it will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].") return - var/choice = alert(user,"You study the schematics etched into the altar...",,"Eldritch Whetstone","Construct Shell","Flask of Unholy Water") - var/list/pickedtype = list() + + to_chat(user, "You study the schematics etched into the altar...") + + var/list/options = list("Eldritch Whetstone" = radial_whetstone, "Construct Shell" = radial_shell, "Flask of Unholy Water" = radial_unholy_water) + var/choice = show_radial_menu(user, src, options, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = TRUE) + + var/reward switch(choice) if("Eldritch Whetstone") - pickedtype += /obj/item/sharpener/cult + reward = /obj/item/sharpener/cult if("Construct Shell") - pickedtype += /obj/structure/constructshell + reward = /obj/structure/constructshell if("Flask of Unholy Water") - pickedtype += /obj/item/reagent_containers/glass/beaker/unholywater - if(src && !QDELETED(src) && anchored && pickedtype && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time) + reward = /obj/item/reagent_containers/glass/beaker/unholywater + + if(!QDELETED(src) && reward && check_menu(user)) cooldowntime = world.time + 2400 - for(var/N in pickedtype) - new N(get_turf(src)) - to_chat(user, "You kneel before the altar and your faith is rewarded with the [choice]!") + new reward(get_turf(src)) + to_chat(user, "You kneel before the altar and your faith is rewarded with the [choice]!") /obj/structure/destructible/cult/forge name = "daemon forge" @@ -116,9 +134,14 @@ light_color = LIGHT_COLOR_LAVA break_message = "The force breaks apart into shards with a howling scream!" -/obj/structure/destructible/cult/forge/attack_hand(mob/living/user) + var/static/image/radial_flagellant = image(icon = 'icons/obj/clothing/suits.dmi', icon_state = "cultrobes") + var/static/image/radial_shielded = image(icon = 'icons/obj/clothing/suits.dmi', icon_state = "cult_armor") + var/static/image/radial_mirror = image(icon = 'icons/obj/items_and_weapons.dmi', icon_state = "mirror_shield") + +/obj/structure/destructible/cult/forge/ui_interact(mob/user) . = ..() - if(.) + + if(!user.canUseTopic(src, TRUE)) return if(!iscultist(user)) to_chat(user, "The heat radiating from [src] pushes you back.") @@ -129,24 +152,26 @@ if(cooldowntime > world.time) to_chat(user, "The magic in [src] is weak, it will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].") return - var/choice - if(user.mind.has_antag_datum(/datum/antagonist/cult/master)) - choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Mirror Shield") - else - choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Mirror Shield") - var/list/pickedtype = list() + + to_chat(user, "You study the schematics etched into the forge...") + + + var/list/options = list("Shielded Robe" = radial_shielded, "Flagellant's Robe" = radial_flagellant, "Mirror Shield" = radial_mirror) + var/choice = show_radial_menu(user, src, options, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = TRUE) + + var/reward switch(choice) if("Shielded Robe") - pickedtype += /obj/item/clothing/suit/hooded/cultrobes/cult_shield + reward = /obj/item/clothing/suit/hooded/cultrobes/cult_shield if("Flagellant's Robe") - pickedtype += /obj/item/clothing/suit/hooded/cultrobes/berserker + reward = /obj/item/clothing/suit/hooded/cultrobes/berserker if("Mirror Shield") - pickedtype += /obj/item/shield/mirror - if(src && !QDELETED(src) && anchored && pickedtype && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time) + reward = /obj/item/shield/mirror + + if(!QDELETED(src) && reward && check_menu(user)) cooldowntime = world.time + 2400 - for(var/N in pickedtype) - new N(get_turf(src)) - to_chat(user, "You work the forge as dark knowledge guides your hands, creating the [choice]!") + new reward(get_turf(src)) + to_chat(user, "You work the forge as dark knowledge guides your hands, creating the [choice]!") @@ -234,9 +259,14 @@ light_color = LIGHT_COLOR_FIRE break_message = "The books and tomes of the archives burn into ash as the desk shatters!" -/obj/structure/destructible/cult/tome/attack_hand(mob/living/user) + var/static/image/radial_blindfold = image(icon = 'icons/obj/clothing/glasses.dmi', icon_state = "blindfold") + var/static/image/radial_curse = image(icon = 'icons/obj/cult.dmi', icon_state ="shuttlecurse") + var/static/image/radial_veilwalker = image(icon = 'icons/obj/cult.dmi', icon_state ="shifter") + +/obj/structure/destructible/cult/tome/ui_interact(mob/user) . = ..() - if(.) + + if(!user.canUseTopic(src, TRUE)) return if(!iscultist(user)) to_chat(user, "These books won't open and it hurts to even try and read the covers.") @@ -247,21 +277,27 @@ if(cooldowntime > world.time) to_chat(user, "The magic in [src] is weak, it will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].") return - var/choice = alert(user,"You flip through the black pages of the archives...",,"Zealot's Blindfold","Shuttle Curse","Veil Walker Set") - var/list/pickedtype = list() + + to_chat(user, "You flip through the black pages of the archives...") + + var/list/options = list("Zealot's Blindfold" = radial_blindfold, "Shuttle Curse" = radial_curse, "Veil Walker Set" = radial_veilwalker) + var/choice = show_radial_menu(user, src, options, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = TRUE) + + var/reward switch(choice) if("Zealot's Blindfold") - pickedtype += /obj/item/clothing/glasses/hud/health/night/cultblind + reward = /obj/item/clothing/glasses/hud/health/night/cultblind if("Shuttle Curse") - pickedtype += /obj/item/shuttle_curse + reward = /obj/item/shuttle_curse if("Veil Walker Set") - pickedtype += /obj/item/cult_shift - pickedtype += /obj/item/flashlight/flare/culttorch - if(src && !QDELETED(src) && anchored && pickedtype.len && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time) + reward = /obj/effect/spawner/bundle/veil_walker + if(!QDELETED(src) && reward && check_menu(user)) cooldowntime = world.time + 2400 - for(var/N in pickedtype) - new N(get_turf(src)) - to_chat(user, "You summon the [choice] from the archives!") + new reward(get_turf(src)) + to_chat(user, "You summon the [choice] from the archives!") + +/obj/effect/spawner/bundle/veil_walker + items = list(/obj/item/cult_shift, /obj/item/flashlight/flare/culttorch) /obj/effect/gateway name = "gateway" diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index 9330e9b153..bfc4955f68 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -61,8 +61,8 @@ Runes can either be invoked by one's self or with many different cultists. Each if(do_after(user, 15, target = src)) to_chat(user, "You carefully erase the [lowertext(cultist_name)] rune.") qdel(src) - else if(istype(I, /obj/item/nullrod)) - user.say("BEGONE FOUL MAGIKS!!", forced = "nullrod") + else if(istype(I, /obj/item/storage/book/bible) || istype(I, /obj/item/nullrod)) + user.say("BEGONE FOUL MAGICKS!!", forced = "bible") to_chat(user, "You disrupt the magic of [src] with [I].") qdel(src) @@ -185,9 +185,6 @@ structure_check() searches for nearby cultist structures required for the invoca color = RUNE_COLOR_OFFER req_cultists = 1 rune_in_use = FALSE - var/mob/living/currentconversionman - var/conversiontimeout - var/conversionresult /obj/effect/rune/convert/do_invoke_glow() return @@ -233,37 +230,18 @@ structure_check() searches for nearby cultist structures required for the invoca addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 5) Cult_team.check_size() // Triggers the eye glow or aura effects if the cult has grown large enough relative to the crew rune_in_use = FALSE + /obj/effect/rune/convert/proc/do_convert(mob/living/convertee, list/invokers) if(invokers.len < 2) for(var/M in invokers) - to_chat(M, "You need at least two invokers to convert [convertee]!") + to_chat(M, "You need at least two invokers to convert [convertee]!") log_game("Offer rune failed - tried conversion with one invoker") return 0 - if(convertee.anti_magic_check(TRUE, TRUE)) + if(convertee.anti_magic_check(TRUE, TRUE, FALSE, 0)) //Not chargecost because it can be spammed for(var/M in invokers) to_chat(M, "Something is shielding [convertee]'s mind!") log_game("Offer rune failed - convertee had anti-magic") return 0 - to_chat(convertee, "The world goes red. All at once you are aware of an evil, eldritch truth taking roots into your mind.\n\ - Click here to become a follower of Nar'sie, or suffer a fate worse than death.") - INVOKE_ASYNC(src, .proc/optinalert, convertee) - currentconversionman = convertee - conversiontimeout = world.time + (14 SECONDS) - convertee.Stun(140) - ADD_TRAIT(convertee, TRAIT_MUTE, "conversionrune") - flash_color(convertee, list("#960000", "#960000", "#960000", rgb(0,0,0)), 50) - conversionresult = FALSE - while(world.time < conversiontimeout && convertee && !conversionresult) - stoplag(1) - currentconversionman = null - if(!convertee) - return FALSE - REMOVE_TRAIT(convertee, TRAIT_MUTE, "conversionrune") - if(get_turf(convertee) != get_turf(src)) - return FALSE - if(!conversionresult) - do_sacrifice(convertee, invokers, TRUE) - return FALSE var/brutedamage = convertee.getBruteLoss() var/burndamage = convertee.getFireLoss() if(brutedamage || burndamage) @@ -275,6 +253,8 @@ structure_check() searches for nearby cultist structures required for the invoca SSticker.mode.add_cultist(convertee.mind, 1) new /obj/item/melee/cultblade/dagger(get_turf(src)) convertee.mind.special_role = ROLE_CULTIST + to_chat(convertee, "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible, truth. The veil of reality has been ripped away \ + and something evil takes root.") to_chat(convertee, "Assist your new compatriots in their dark dealings. Your goal is theirs, and theirs is yours. You serve the Geometer above all else. Bring it back.\ ") if(ishuman(convertee)) @@ -284,18 +264,7 @@ structure_check() searches for nearby cultist structures required for the invoca H.cultslurring = 0 return 1 -/obj/effect/rune/convert/proc/optinalert(mob/living/convertee) - var/alert = alert(convertee, "Will you embrace the Geometer of Blood or perish in futile resistance?", "Choose your own fate", "Join the Blood Cult", "Suffer a horrible demise") - if(src && alert == "Join the Blood Cult") - signmeup(convertee) - -/obj/effect/rune/convert/proc/signmeup(mob/living/convertee) - if(currentconversionman == convertee) - conversionresult = TRUE - else - to_chat(convertee, "Your fate has already been set in stone.") - -/obj/effect/rune/convert/proc/do_sacrifice(mob/living/sacrificial, list/invokers, force_a_sac) +/obj/effect/rune/convert/proc/do_sacrifice(mob/living/sacrificial, list/invokers) var/mob/living/first_invoker = invokers[1] if(!first_invoker) return FALSE @@ -305,7 +274,7 @@ structure_check() searches for nearby cultist structures required for the invoca var/big_sac = FALSE - if(!force_a_sac && (((ishuman(sacrificial) || iscyborg(sacrificial)) && sacrificial.stat != DEAD) || C.cult_team.is_sacrifice_target(sacrificial.mind)) && invokers.len < 3) + if((((ishuman(sacrificial) || iscyborg(sacrificial)) && sacrificial.stat != DEAD) || C.cult_team.is_sacrifice_target(sacrificial.mind)) && invokers.len < 3) for(var/M in invokers) to_chat(M, "[sacrificial] is too greatly linked to the world! You need three acolytes!") log_game("Offer rune failed - not enough acolytes and target is living or sac target") @@ -345,10 +314,6 @@ structure_check() searches for nearby cultist structures required for the invoca sacrificial.gib() return TRUE -/obj/effect/rune/convert/Topic(href, href_list) - if(href_list["signmeup"]) - signmeup(usr) - /obj/effect/rune/empower cultist_name = "Empower" cultist_desc = "allows cultists to prepare greater amounts of blood magic at far less of a cost." diff --git a/code/modules/antagonists/disease/disease_abilities.dm b/code/modules/antagonists/disease/disease_abilities.dm index 07cd3030b1..dc2249006c 100644 --- a/code/modules/antagonists/disease/disease_abilities.dm +++ b/code/modules/antagonists/disease/disease_abilities.dm @@ -5,25 +5,48 @@ is currently following. */ GLOBAL_LIST_INIT(disease_ability_singletons, list( - new /datum/disease_ability/action/cough(), - new /datum/disease_ability/action/sneeze(), - new /datum/disease_ability/action/infect(), - new /datum/disease_ability/symptom/cough(), - new /datum/disease_ability/symptom/sneeze(),\ - new /datum/disease_ability/symptom/hallucigen(), - new /datum/disease_ability/symptom/choking(), - new /datum/disease_ability/symptom/confusion(), - new /datum/disease_ability/symptom/youth(), - new /datum/disease_ability/symptom/vomit(), - new /datum/disease_ability/symptom/voice_change(), - new /datum/disease_ability/symptom/visionloss(), - new /datum/disease_ability/symptom/viraladaptation(), - new /datum/disease_ability/symptom/vitiligo(), - new /datum/disease_ability/symptom/sensory_restoration(), - new /datum/disease_ability/symptom/itching(), - new /datum/disease_ability/symptom/weight_loss(), - new /datum/disease_ability/symptom/metabolism_heal(), - new /datum/disease_ability/symptom/coma_heal() + new /datum/disease_ability/action/cough, + new /datum/disease_ability/action/sneeze, + new /datum/disease_ability/action/infect, + new /datum/disease_ability/symptom/mild/cough, + new /datum/disease_ability/symptom/mild/sneeze, + new /datum/disease_ability/symptom/medium/shedding, + new /datum/disease_ability/symptom/medium/beard, + new /datum/disease_ability/symptom/medium/hallucigen, + new /datum/disease_ability/symptom/medium/choking, + new /datum/disease_ability/symptom/medium/confusion, + new /datum/disease_ability/symptom/medium/vomit, + new /datum/disease_ability/symptom/medium/voice_change, + new /datum/disease_ability/symptom/medium/visionloss, + new /datum/disease_ability/symptom/medium/deafness, + new /datum/disease_ability/symptom/powerful/narcolepsy, + new /datum/disease_ability/symptom/medium/fever, + new /datum/disease_ability/symptom/medium/shivering, + new /datum/disease_ability/symptom/medium/headache, + new /datum/disease_ability/symptom/medium/nano_boost, + new /datum/disease_ability/symptom/medium/nano_destroy, + new /datum/disease_ability/symptom/medium/viraladaptation, + new /datum/disease_ability/symptom/medium/viralevolution, + new /datum/disease_ability/symptom/medium/vitiligo, + new /datum/disease_ability/symptom/medium/revitiligo, + new /datum/disease_ability/symptom/medium/itching, + new /datum/disease_ability/symptom/medium/heal/weight_loss, + new /datum/disease_ability/symptom/medium/heal/sensory_restoration, + new /datum/disease_ability/symptom/medium/heal/mind_restoration, + new /datum/disease_ability/symptom/powerful/fire, + new /datum/disease_ability/symptom/powerful/flesh_eating, +// new /datum/disease_ability/symptom/powerful/genetic_mutation, + new /datum/disease_ability/symptom/powerful/inorganic_adaptation, + new /datum/disease_ability/symptom/powerful/heal/starlight, + new /datum/disease_ability/symptom/powerful/heal/oxygen, + new /datum/disease_ability/symptom/powerful/heal/chem, + new /datum/disease_ability/symptom/powerful/heal/metabolism, + new /datum/disease_ability/symptom/powerful/heal/dark, + new /datum/disease_ability/symptom/powerful/heal/water, + new /datum/disease_ability/symptom/powerful/heal/plasma, + new /datum/disease_ability/symptom/powerful/heal/radiation, + new /datum/disease_ability/symptom/powerful/heal/coma, + new /datum/disease_ability/symptom/powerful/youth )) /datum/disease_ability @@ -54,7 +77,13 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list( stage_speed += initial(S.stage_speed) transmittable += initial(S.transmittable) threshold_block += "

[initial(S.threshold_desc)]" - stat_block = "Resistance: [resistance]
Stealth: [stealth]
Stage Speed: [stage_speed]
Transmissibility: [transmittable]

" + stat_block = "Resistance: [resistance]
Stealth: [stealth]
Stage Speed: [stage_speed]
Transmissibility: [transmittable]

" + if(symptoms.len == 1) //lazy boy's dream + name = initial(S.name) + if(short_desc == "") + short_desc = initial(S.desc) + if(long_desc == "") + long_desc = initial(S.desc) /datum/disease_ability/proc/CanBuy(mob/camera/disease/D) if(world.time < D.next_adaptation_time) @@ -77,8 +106,10 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list( for(var/T in symptoms) var/datum/symptom/S = new T() SD.symptoms += S + S.OnAdd(SD) if(SD.processing) - S.Start(SD) + if(S.Start(SD)) + S.next_activation = world.time + rand(S.symptom_delay_min * 10, S.symptom_delay_max * 10) SD.Refresh() for(var/T in actions) var/datum/action/A = new T() @@ -105,6 +136,7 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list( var/datum/symptom/S = locate(T) in SD.symptoms if(S) SD.symptoms -= S + S.OnRemove(SD) if(SD.processing) S.End(SD) qdel(S) @@ -152,8 +184,9 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list( return FALSE to_chat(D, "You force [L.real_name] to cough.") L.emote("cough") - var/datum/disease/advance/sentient_disease/SD = D.hosts[L] - SD.spread(2) + if(L.CanSpreadAirborneDisease()) //don't spread germs if they covered their mouth + var/datum/disease/advance/sentient_disease/SD = D.hosts[L] + SD.spread(2) StartCooldown() return TRUE @@ -185,11 +218,12 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list( return FALSE to_chat(D, "You force [L.real_name] to sneeze.") L.emote("sneeze") - var/datum/disease/advance/sentient_disease/SD = D.hosts[L] + if(L.CanSpreadAirborneDisease()) //don't spread germs if they covered their mouth + var/datum/disease/advance/sentient_disease/SD = D.hosts[L] - for(var/mob/living/M in oview(4, SD.affected_mob)) - if(is_A_facing_B(SD.affected_mob, M) && disease_air_spread_walk(get_turf(SD.affected_mob), get_turf(M))) - M.AirborneContractDisease(SD, TRUE) + for(var/mob/living/M in oview(4, SD.affected_mob)) + if(is_A_facing_B(SD.affected_mob, M) && disease_air_spread_walk(get_turf(SD.affected_mob), get_turf(M))) + M.AirborneContractDisease(SD, TRUE) StartCooldown() return TRUE @@ -235,154 +269,192 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list( StartCooldown() return TRUE -//passive symptom abilities +/*******************BASE SYMPTOM TYPES*******************/ +// cost is for convenience and can be changed. If you're changing req_tot_points then don't use the subtype... +//healing costs more so you have to techswitch from naughty disease otherwise we'd have friendly disease for easy greentext (no fun!) -/datum/disease_ability/symptom/cough - name = "Involuntary Coughing" - symptoms = list(/datum/symptom/cough) +/datum/disease_ability/symptom/mild cost = 2 required_total_points = 4 + category = "Symptom (Weak)" + +/datum/disease_ability/symptom/medium + cost = 4 + required_total_points = 8 + category = "Symptom" + +/datum/disease_ability/symptom/medium/heal + cost = 5 + category = "Symptom (+)" + +/datum/disease_ability/symptom/powerful + cost = 4 + required_total_points = 16 + category = "Symptom (Strong)" + +/datum/disease_ability/symptom/powerful/heal + cost = 8 + category = "Symptom (Strong+)" + + +/******MILD******/ + +/datum/disease_ability/symptom/mild/cough + name = "Involuntary Coughing" + symptoms = list(/datum/symptom/cough) short_desc = "Cause victims to cough intermittently." long_desc = "Cause victims to cough intermittently, spreading your infection if your transmissibility is high." -/datum/disease_ability/symptom/sneeze +/datum/disease_ability/symptom/mild/sneeze name = "Involuntary Sneezing" symptoms = list(/datum/symptom/sneeze) - cost = 2 - required_total_points = 4 short_desc = "Cause victims to sneeze intermittently." long_desc = "Cause victims to sneeze intermittently, spreading your infection and also increasing transmissibility and resistance, at the cost of stealth." -/datum/disease_ability/symptom/beard - //I don't think I need to justify the fact that this is the best symptom - name = "Beard Growth" - symptoms = list(/datum/symptom/beard) - cost = 1 - required_total_points = 8 - short_desc = "Cause all victims to grow a luscious beard." - long_desc = "Cause all victims to grow a luscious beard. Decreases stats slightly. Ineffective against Santa Claus." +/******MEDIUM******/ -/datum/disease_ability/symptom/hallucigen - name = "Hallucinations" +/datum/disease_ability/symptom/medium/shedding + symptoms = list(/datum/symptom/shedding) + +/datum/disease_ability/symptom/medium/beard + symptoms = list(/datum/symptom/beard) + short_desc = "Cause all victims to grow a luscious beard." + long_desc = "Cause all victims to grow a luscious beard. Ineffective against Santa Claus." + +/datum/disease_ability/symptom/medium/hallucigen symptoms = list(/datum/symptom/hallucigen) - cost = 4 - required_total_points = 8 short_desc = "Cause victims to hallucinate." long_desc = "Cause victims to hallucinate. Decreases stats, especially resistance." - -/datum/disease_ability/symptom/choking - name = "Choking" +/datum/disease_ability/symptom/medium/choking symptoms = list(/datum/symptom/choking) - cost = 4 - required_total_points = 8 short_desc = "Cause victims to choke." long_desc = "Cause victims to choke, threatening asphyxiation. Decreases stats, especially transmissibility." - -/datum/disease_ability/symptom/confusion - name = "Confusion" +/datum/disease_ability/symptom/medium/confusion symptoms = list(/datum/symptom/confusion) - cost = 4 - required_total_points = 8 short_desc = "Cause victims to become confused." long_desc = "Cause victims to become confused intermittently." - -/datum/disease_ability/symptom/youth - name = "Eternal Youth" - symptoms = list(/datum/symptom/youth) - cost = 4 - required_total_points = 8 - short_desc = "Cause victims to become eternally young." - long_desc = "Cause victims to become eternally young. Provides boosts to all stats except transmissibility." - - -/datum/disease_ability/symptom/vomit - name = "Vomiting" +/datum/disease_ability/symptom/medium/vomit symptoms = list(/datum/symptom/vomit) - cost = 4 - required_total_points = 8 short_desc = "Cause victims to vomit." long_desc = "Cause victims to vomit. Slightly increases transmissibility. Vomiting also also causes the victims to lose nutrition and removes some toxin damage." - -/datum/disease_ability/symptom/voice_change - name = "Voice Changing" +/datum/disease_ability/symptom/medium/voice_change symptoms = list(/datum/symptom/voice_change) - cost = 4 - required_total_points = 8 short_desc = "Change the voice of victims." long_desc = "Change the voice of victims, causing confusion in communications." - -/datum/disease_ability/symptom/visionloss - name = "Vision Loss" +/datum/disease_ability/symptom/medium/visionloss symptoms = list(/datum/symptom/visionloss) - cost = 4 - required_total_points = 8 short_desc = "Damage the eyes of victims, eventually causing blindness." long_desc = "Damage the eyes of victims, eventually causing blindness. Decreases all stats." +/datum/disease_ability/symptom/medium/deafness + symptoms = list(/datum/symptom/deafness) -/datum/disease_ability/symptom/viraladaptation - name = "Self-Adaptation" +/datum/disease_ability/symptom/medium/fever + symptoms = list(/datum/symptom/fever) + +/datum/disease_ability/symptom/medium/shivering + symptoms = list(/datum/symptom/shivering) + +/datum/disease_ability/symptom/medium/headache + symptoms = list(/datum/symptom/headache) + +/datum/disease_ability/symptom/medium/nano_boost + symptoms = list(/datum/symptom/nano_boost) + +/datum/disease_ability/symptom/medium/nano_destroy + symptoms = list(/datum/symptom/nano_destroy) + +/datum/disease_ability/symptom/medium/viraladaptation symptoms = list(/datum/symptom/viraladaptation) - cost = 4 - required_total_points = 8 short_desc = "Cause your infection to become more resistant to detection and eradication." long_desc = "Cause your infection to mimic the function of normal body cells, becoming much harder to spot and to eradicate, but reducing its speed." +/datum/disease_ability/symptom/medium/viralevolution + symptoms = list(/datum/symptom/viralevolution) -/datum/disease_ability/symptom/vitiligo - name = "Skin Paleness" +/datum/disease_ability/symptom/medium/vitiligo symptoms = list(/datum/symptom/vitiligo) - cost = 1 - required_total_points = 8 - short_desc = "Cause victims to become pale." - long_desc = "Cause victims to become pale. Decreases all stats." +/datum/disease_ability/symptom/medium/revitiligo + symptoms = list(/datum/symptom/revitiligo) -/datum/disease_ability/symptom/sensory_restoration - name = "Sensory Restoration" - symptoms = list(/datum/symptom/sensory_restoration) - cost = 4 - required_total_points = 8 - short_desc = "Regenerate eye and ear damage of victims." - long_desc = "Regenerate eye and ear damage of victims." - - -/datum/disease_ability/symptom/itching - name = "Itching" +/datum/disease_ability/symptom/medium/itching symptoms = list(/datum/symptom/itching) - cost = 4 - required_total_points = 8 short_desc = "Cause victims to itch." long_desc = "Cause victims to itch, increasing all stats except stealth." - -/datum/disease_ability/symptom/weight_loss - name = "Weight Loss" +/datum/disease_ability/symptom/medium/heal/weight_loss symptoms = list(/datum/symptom/weight_loss) - cost = 4 - required_total_points = 8 short_desc = "Cause victims to lose weight." long_desc = "Cause victims to lose weight, and make it almost impossible for them to gain nutrition from food. Reduced nutrition allows your infection to spread more easily from hosts, especially by sneezing." +/datum/disease_ability/symptom/medium/heal/sensory_restoration + symptoms = list(/datum/symptom/sensory_restoration) + short_desc = "Regenerate eye and ear damage of victims." + long_desc = "Regenerate eye and ear damage of victims." -/datum/disease_ability/symptom/metabolism_heal - name = "Metabolic Boost" +/datum/disease_ability/symptom/medium/heal/mind_restoration + symptoms = list(/datum/symptom/mind_restoration) + +/******POWERFUL******/ + +/datum/disease_ability/symptom/powerful/fire + symptoms = list(/datum/symptom/fire) + +/datum/disease_ability/symptom/powerful/flesh_eating + symptoms = list(/datum/symptom/flesh_eating) + +/* +/datum/disease_ability/symptom/powerful/genetic_mutation + symptoms = list(/datum/symptom/genetic_mutation) + cost = 8 +*/ + +/datum/disease_ability/symptom/powerful/inorganic_adaptation + symptoms = list(/datum/symptom/inorganic_adaptation) + +/datum/disease_ability/symptom/powerful/narcolepsy + symptoms = list(/datum/symptom/narcolepsy) + +/datum/disease_ability/symptom/powerful/youth + symptoms = list(/datum/symptom/youth) + short_desc = "Cause victims to become eternally young." + long_desc = "Cause victims to become eternally young. Provides boosts to all stats except transmissibility." + +/****HEALING SUBTYPE****/ + +/datum/disease_ability/symptom/powerful/heal/starlight + symptoms = list(/datum/symptom/heal/starlight) + +/datum/disease_ability/symptom/powerful/heal/oxygen + symptoms = list(/datum/symptom/oxygen) + +/datum/disease_ability/symptom/powerful/heal/chem + symptoms = list(/datum/symptom/heal/chem) + +/datum/disease_ability/symptom/powerful/heal/metabolism symptoms = list(/datum/symptom/heal/metabolism) - cost = 4 - required_total_points = 16 short_desc = "Increase the metabolism of victims, causing them to process chemicals and grow hungry faster." long_desc = "Increase the metabolism of victims, causing them to process chemicals twice as fast and grow hungry more quickly." +/datum/disease_ability/symptom/powerful/heal/dark + symptoms = list(/datum/symptom/heal/darkness) -/datum/disease_ability/symptom/coma_heal - name = "Regenerative Coma" +/datum/disease_ability/symptom/powerful/heal/water + symptoms = list(/datum/symptom/heal/water) + +/datum/disease_ability/symptom/powerful/heal/plasma + symptoms = list(/datum/symptom/heal/plasma) + +/datum/disease_ability/symptom/powerful/heal/radiation + symptoms = list(/datum/symptom/heal/radiation) + +/datum/disease_ability/symptom/powerful/heal/coma symptoms = list(/datum/symptom/heal/coma) - cost = 8 - required_total_points = 16 short_desc = "Cause victims to fall into a healing coma when hurt." long_desc = "Cause victims to fall into a healing coma when hurt." diff --git a/code/modules/antagonists/disease/disease_disease.dm b/code/modules/antagonists/disease/disease_disease.dm index b4b8ac0956..c37abefab0 100644 --- a/code/modules/antagonists/disease/disease_disease.dm +++ b/code/modules/antagonists/disease/disease_disease.dm @@ -51,6 +51,7 @@ if(cures.len) return var/list/not_used = advance_cures.Copy() + not_used.Cut(1, 6) // Removes the first five tiers of cures. cures = list(pick(pick_n_take(not_used)), pick(pick_n_take(not_used))) // Get the cure name from the cure_id diff --git a/code/modules/antagonists/disease/disease_mob.dm b/code/modules/antagonists/disease/disease_mob.dm index 4d378af724..49c34131fc 100644 --- a/code/modules/antagonists/disease/disease_mob.dm +++ b/code/modules/antagonists/disease/disease_mob.dm @@ -18,7 +18,7 @@ the new instance inside the host to be updated to the template's stats. layer = BELOW_MOB_LAYER lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE sight = SEE_SELF|SEE_THRU - initial_language_holder = /datum/language_holder/empty + initial_language_holder = /datum/language_holder/universal var/freemove = TRUE var/freemove_end = 0 @@ -43,7 +43,7 @@ the new instance inside the host to be updated to the template's stats. var/move_delay = 1 var/next_adaptation_time = 0 - var/adaptation_cooldown = 1200 + var/adaptation_cooldown = 600 var/list/purchased_abilities var/list/unpurchased_abilities @@ -118,10 +118,28 @@ the new instance inside the host to be updated to the template's stats. follow_next(Dir & NORTHWEST) last_move_tick = world.time +/mob/camera/disease/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode) + . = ..() + var/atom/movable/to_follow = speaker + if(radio_freq) + var/atom/movable/virtualspeaker/V = speaker + to_follow = V.source + var/link + if(to_follow in hosts) + link = FOLLOW_LINK(src, to_follow) + else + link = "" + // Recompose the message, because it's scrambled by default + message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode) + to_chat(src, "[link] [message]") + + /mob/camera/disease/mind_initialize() . = ..() if(!mind.has_antag_datum(/datum/antagonist/disease)) mind.add_antag_datum(/datum/antagonist/disease) + var/datum/atom_hud/medsensor = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] + medsensor.add_hud_to(src) /mob/camera/disease/proc/pick_name() var/static/list/taken_names @@ -247,9 +265,12 @@ the new instance inside the host to be updated to the template's stats. if(!move_listener) move_listener = L.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/follow_mob))) else - L.TakeComponent(move_listener) - if(QDELING(move_listener)) - move_listener = null + if(L) + L.TakeComponent(move_listener) + if(QDELING(move_listener)) + move_listener = null + else + QDEL_NULL(move_listener) follow_mob() /mob/camera/disease/proc/follow_next(reverse = FALSE) diff --git a/code/modules/antagonists/slaughter/slaughter.dm b/code/modules/antagonists/slaughter/slaughter.dm index 4f31346136..03a176b054 100644 --- a/code/modules/antagonists/slaughter/slaughter.dm +++ b/code/modules/antagonists/slaughter/slaughter.dm @@ -80,6 +80,7 @@ desc = "Still it beats furiously, emanating an aura of utter hate." icon = 'icons/obj/surgery.dmi' icon_state = "demon_heart-on" + decay_factor = 0 /obj/item/organ/heart/demon/update_icon() return //always beating visually diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm index 8642484895..57488a6722 100644 --- a/code/modules/antagonists/wizard/equipment/artefact.dm +++ b/code/modules/antagonists/wizard/equipment/artefact.dm @@ -198,7 +198,7 @@ to_chat(user, "This artifact can only affect three undead at a time!") return - M.set_species(/datum/species/skeleton, icon_update=0) + M.set_species(/datum/species/skeleton/space, icon_update=0) M.revive(full_heal = 1, admin_revive = 1) spooky_scaries |= M to_chat(M, "You have been revived by [user.real_name]!") diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm index 35b30107d8..ccd13d8d4a 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm @@ -394,6 +394,21 @@ . = TRUE update_icon() +/obj/machinery/atmospherics/components/unary/cryo_cell/CtrlClick(mob/user) + if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) && !state_open) + on = !on + update_icon() + return ..() + +/obj/machinery/atmospherics/components/unary/cryo_cell/AltClick(mob/user) + if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + if(state_open) + close_machine() + else + open_machine() + update_icon() + return ..() + /obj/machinery/atmospherics/components/unary/cryo_cell/update_remote_sight(mob/living/user) return // we don't see the pipe network while inside cryo. diff --git a/code/modules/cargo/bounties/reagent.dm b/code/modules/cargo/bounties/reagent.dm index 3f458e2b12..9f1c76db3d 100644 --- a/code/modules/cargo/bounties/reagent.dm +++ b/code/modules/cargo/bounties/reagent.dm @@ -109,10 +109,9 @@ datum/bounty/reagent/complex_drink/New() /datum/reagent/consumable/ethanol/patron,\ /datum/reagent/consumable/ethanol/quadruple_sec,\ /datum/reagent/consumable/ethanol/quintuple_sec,\ - /datum/reagent/consumable/bluecherryshake,\ /datum/reagent/consumable/doctor_delight,\ /datum/reagent/consumable/ethanol/silencer) - + var/reagent_type = pick(possible_reagents) wanted_reagent = new reagent_type name = wanted_reagent.name diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index 1087f1ebb5..fa28da74d0 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -1762,15 +1762,31 @@ /obj/item/stack/tile/carpet/black/fifty) crate_name = "premium carpet crate" -/datum/supply_pack/service/carpet2 - name = "Premium Carpet Crate #2" - desc = "Plasteel floor tiles getting on your nerves? These stacks of extra soft carpet will tie any room together. Contains red, and monochrome" - cost = 1000 - contains = list(/obj/item/stack/tile/carpet/blackred/fifty, +/datum/supply_pack/service/carpet_exotic + name = "Exotic Carpet Crate" + desc = "Exotic carpets straight from Space Russia, for all your decorating needs. Contains 100 tiles each of 10 different flooring patterns." + cost = 4000 + contains = list(/obj/item/stack/tile/carpet/blue/fifty, + /obj/item/stack/tile/carpet/blue/fifty, + /obj/item/stack/tile/carpet/cyan/fifty, + /obj/item/stack/tile/carpet/cyan/fifty, + /obj/item/stack/tile/carpet/green/fifty, + /obj/item/stack/tile/carpet/green/fifty, + /obj/item/stack/tile/carpet/orange/fifty, + /obj/item/stack/tile/carpet/orange/fifty, + /obj/item/stack/tile/carpet/purple/fifty, + /obj/item/stack/tile/carpet/purple/fifty, + /obj/item/stack/tile/carpet/red/fifty, + /obj/item/stack/tile/carpet/red/fifty, + /obj/item/stack/tile/carpet/royalblue/fifty, + /obj/item/stack/tile/carpet/royalblue/fifty, + /obj/item/stack/tile/carpet/royalblack/fifty, + /obj/item/stack/tile/carpet/royalblack/fifty, + /obj/item/stack/tile/carpet/blackred/fifty, /obj/item/stack/tile/carpet/blackred/fifty, /obj/item/stack/tile/carpet/monochrome/fifty, /obj/item/stack/tile/carpet/monochrome/fifty) - crate_name = "premium carpet crate #2" + crate_name = "exotic carpet crate" /datum/supply_pack/service/lightbulbs name = "Replacement Lights" diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 5402353fd4..643d072469 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -571,6 +571,8 @@ GLOBAL_LIST_EMPTY(asset_datums) "fontawesome-webfont.woff" = 'tgui/assets/fonts/fontawesome-webfont.woff', "font-awesome.css" = 'code/modules/goonchat/browserassets/css/font-awesome.css', "browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css', + "browserOutput_dark.css" = 'code/modules/goonchat/browserassets/css/browserOutput_dark.css', + "browserOutput_light.css" = 'code/modules/goonchat/browserassets/css/browserOutput_light.css' ) /datum/asset/spritesheet/goonchat diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 7216b73af6..a54584d6cc 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -75,3 +75,8 @@ var/datum/player_details/player_details //these persist between logins/logouts during the same round. var/list/char_render_holders //Should only be a key-value list of north/south/east/west = obj/screen. + + var/client_keysend_amount = 0 + var/next_keysend_reset = 0 + var/next_keysend_trip_reset = 0 + var/keysend_tripped = FALSE \ No newline at end of file diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 06f23574e1..5442fcb932 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -817,6 +817,10 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) 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 index 9e8d136b3b..f806e5c964 100644 --- a/code/modules/client/darkmode.dm +++ b/code/modules/client/darkmode.dm @@ -5,7 +5,7 @@ This lets you switch chat themes by using winset and CSS loading, you must relog 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 = currentvalue;var to change = desired value") +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 @@ -14,104 +14,52 @@ 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_DARKMODE_DARKBACKGROUND];background-color = [COLOR_WHITEMODE_DARKBACKGROUND]") - winset(src, "infowindow", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "info", "background-color = [COLOR_DARKMODE_BACKGROUND];background-color = [COLOR_WHITEMODE_BACKGROUND]") - winset(src, "info", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "browseroutput", "background-color = [COLOR_DARKMODE_BACKGROUND];background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG]") - winset(src, "browseroutput", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "outputwindow", "background-color = [COLOR_DARKMODE_BACKGROUND];background-color = [COLOR_WHITEMODE_DARKBACKGROUND]") - winset(src, "outputwindow", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "mainwindow", "background-color = [COLOR_DARKMODE_DARKBACKGROUND];background-color = [COLOR_WHITEMODE_DARKBACKGROUND]") - winset(src, "split", "background-color = [COLOR_DARKMODE_DARKBACKGROUND];background-color = [COLOR_WHITEMODE_BACKGROUND]") + 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_DARKMODE_INFO_BUTTONS_BG];background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG]") - winset(src, "changelog", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "rules", "background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG];background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG]") - winset(src, "rules", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "wiki", "background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG];background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG]") - winset(src, "wiki", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "forum", "background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG];background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG]") - winset(src, "forum", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "github", "background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG];background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG]") - winset(src, "github", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "report-issue", "background-color = [COLOR_DARKMODE_ISSUE_BUTTON_BG];background-color = [COLOR_WHITEMODE_ISSUE_BUTTON_BG]") - winset(src, "report-issue", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") + 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_DARKMODE_DARKBACKGROUND];background-color = [COLOR_WHITEMODE_BACKGROUND]") - winset(src, "output", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "statwindow", "background-color = [COLOR_DARKMODE_BACKGROUND];background-color = [COLOR_WHITEMODE_DARKBACKGROUND]") - winset(src, "statwindow", "text-color = #eaeaea;text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "stat", "background-color = [COLOR_DARKMODE_DARKBACKGROUND];background-color = [COLOR_WHITEMODE_BACKGROUND]") - winset(src, "stat", "tab-background-color = [COLOR_DARKMODE_BACKGROUND];tab-background-color = [COLOR_WHITEMODE_DARKBACKGROUND]") - winset(src, "stat", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "stat", "tab-text-color = [COLOR_DARKMODE_TEXT];tab-text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "stat", "prefix-color = [COLOR_DARKMODE_TEXT];prefix-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "stat", "suffix-color = [COLOR_DARKMODE_TEXT];suffix-color = [COLOR_WHITEMODE_TEXT]") + 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_DARKMODE_DARKBACKGROUND];background-color = [COLOR_WHITEMODE_DARKBACKGROUND]") - winset(src, "say", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "asset_cache_browser", "background-color = [COLOR_DARKMODE_BACKGROUND];background-color = [COLOR_WHITEMODE_DARKBACKGROUND]") - winset(src, "asset_cache_browser", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") - winset(src, "tooltip", "background-color = [COLOR_DARKMODE_BACKGROUND];background-color = [COLOR_WHITEMODE_BACKGROUND]") - winset(src, "tooltip", "text-color = [COLOR_DARKMODE_TEXT];text-color = [COLOR_WHITEMODE_TEXT]") + 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_WHITEMODE_DARKBACKGROUND];background-color = [COLOR_DARKMODE_DARKBACKGROUND]") - winset(src, "infowindow", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "info", "background-color = [COLOR_WHITEMODE_BACKGROUND];background-color = [COLOR_DARKMODE_BACKGROUND]") - winset(src, "info", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "browseroutput", "background-color = [COLOR_WHITEMODE_BACKGROUND];background-color = [COLOR_DARKMODE_BACKGROUND]") - winset(src, "browseroutput", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "outputwindow", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];background-color = [COLOR_DARKMODE_BACKGROUND]") - winset(src, "outputwindow", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "mainwindow", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];background-color = [COLOR_DARKMODE_DARKBACKGROUND]") - winset(src, "split", "background-color = [COLOR_WHITEMODE_BACKGROUND];background-color = [COLOR_DARKMODE_BACKGROUND]") + 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_WHITEMODE_INFO_BUTTONS_BG];background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG]") - winset(src, "changelog", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "rules", "background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG];background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG]") - winset(src, "rules", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "wiki", "background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG];background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG]") - winset(src, "wiki", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "forum", "background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG];background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG]") - winset(src, "forum", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "github", "background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG];background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG]") - winset(src, "github", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "report-issue", "background-color = [COLOR_WHITEMODE_ISSUE_BUTTON_BG];background-color = [COLOR_DARKMODE_ISSUE_BUTTON_BG]") - winset(src, "report-issue", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") + 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_WHITEMODE_DARKBACKGROUND];background-color = [COLOR_DARKMODE_BACKGROUND]") - winset(src, "output", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "statwindow", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];background-color = [COLOR_DARKMODE_DARKBACKGROUND]") - winset(src, "statwindow", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "stat", "background-color = [COLOR_WHITEMODE_BACKGROUND];background-color = [COLOR_DARKMODE_DARKBACKGROUND]") - winset(src, "stat", "tab-background-color = [COLOR_WHITEMODE_DARKBACKGROUND];tab-background-color = [COLOR_DARKMODE_BACKGROUND]") - winset(src, "stat", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "stat", "tab-text-color = [COLOR_WHITEMODE_TEXT];tab-text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "stat", "prefix-color = [COLOR_WHITEMODE_TEXT];prefix-color = [COLOR_DARKMODE_TEXT]") - winset(src, "stat", "suffix-color = [COLOR_WHITEMODE_TEXT];suffix-color = [COLOR_DARKMODE_TEXT]") + 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_WHITEMODE_DARKBACKGROUND];background-color = [COLOR_DARKMODE_BACKGROUND]") - winset(src, "say", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "asset_cache_browser", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];background-color = [COLOR_DARKMODE_BACKGROUND]") - winset(src, "asset_cache_browser", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - winset(src, "tooltip", "background-color = [COLOR_WHITEMODE_BACKGROUND];background-color = [COLOR_DARKMODE_BACKGROUND]") - winset(src, "tooltip", "text-color = [COLOR_WHITEMODE_TEXT];text-color = [COLOR_DARKMODE_TEXT]") - - -/datum/asset/simple/goonchat - verify = FALSE - assets = list( - "json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js', - "errorHandler.js" = 'code/modules/goonchat/browserassets/js/errorHandler.js', - "browserOutput.js" = 'code/modules/goonchat/browserassets/js/browserOutput.js', - "fontawesome-webfont.eot" = 'tgui/assets/fonts/fontawesome-webfont.eot', - "fontawesome-webfont.svg" = 'tgui/assets/fonts/fontawesome-webfont.svg', - "fontawesome-webfont.ttf" = 'tgui/assets/fonts/fontawesome-webfont.ttf', - "fontawesome-webfont.woff" = 'tgui/assets/fonts/fontawesome-webfont.woff', - "font-awesome.css" = 'code/modules/goonchat/browserassets/css/font-awesome.css', - "browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css', - "browserOutput_white.css" = 'code/modules/goonchat/browserassets/css/browserOutput_white.css', - ) + 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 d487b873b1..ae6de5ba05 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -85,6 +85,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/facial_hair_color = "000" //Facial hair color var/skin_tone = "caucasian1" //Skin color var/eye_color = "000" //Eye color + var/horn_color = "85615a" //Horn color var/datum/species/pref_species = new /datum/species/human() //Mutant race var/list/features = list("mcolor" = "FFF", "tail_lizard" = "Smooth", @@ -96,8 +97,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) "frills" = "None", "spines" = "None", "body_markings" = "None", - "legs" = "Normal Legs", - "moth_wings" = "Plain", + "legs" = "Plantigrade", + "insect_wings" = "Plain", + "insect_fluff" = "None", "mcolor2" = "FFF", "mcolor3" = "FFF", "mam_body_markings" = "Plain", @@ -157,7 +159,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) "womb_fluid" = "femcum", "ipc_screen" = "Sunburst", "ipc_antenna" = "None", - "flavor_text" = "" + "flavor_text" = "", + "meat_type" = "Mammalian" ) var/list/custom_names = list() @@ -172,18 +175,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/list/all_quirks = list() var/list/character_quirks = list() - //Jobs, uses bitflags - var/job_civilian_high = 0 - var/job_civilian_med = 0 - var/job_civilian_low = 0 - - var/job_medsci_high = 0 - var/job_medsci_med = 0 - var/job_medsci_low = 0 - - var/job_engsec_high = 0 - var/job_engsec_med = 0 - var/job_engsec_low = 0 + //Job preferences 2.0 - indexed by job title , no key or value implies never + var/list/job_preferences = list() // Want randomjob if preferences already filled - Donkie var/joblessrole = BERANDOMJOB //defaults to 1 for fewer assistants @@ -241,7 +234,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) return #define APPEARANCE_CATEGORY_COLUMN "
" + mutant_category = 0 + + if("meat_type" in pref_species.default_features) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN + + dat += "

Meat Type

" + + dat += "[features["meat_type"]]" + mutant_category++ if(mutant_category >= MAX_MUTANT_ROWS) dat += "" @@ -471,6 +477,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "

Horns

" dat += "[features["horns"]]" + dat += "   Change
" + mutant_category++ if(mutant_category >= MAX_MUTANT_ROWS) @@ -537,6 +545,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(mutant_category >= MAX_MUTANT_ROWS) dat += "" mutant_category = 0 + if("ears" in pref_species.default_features) if(!mutant_category) dat += APPEARANCE_CATEGORY_COLUMN @@ -549,6 +558,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(mutant_category >= MAX_MUTANT_ROWS) dat += "" mutant_category = 0 + if("mam_snouts" in pref_species.default_features) if(!mutant_category) dat += APPEARANCE_CATEGORY_COLUMN @@ -573,14 +583,24 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(mutant_category >= MAX_MUTANT_ROWS) dat += "" mutant_category = 0 - if("moth_wings" in pref_species.default_features) + if("insect_wings" in pref_species.default_features) if(!mutant_category) dat += APPEARANCE_CATEGORY_COLUMN - dat += "

Moth wings

" + dat += "

Insect wings

" - dat += "[features["moth_wings"]]" + dat += "[features["insect_wings"]]" + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 + if("insect_fluff" in pref_species.default_features) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN + dat += "

Insect Fluff

" + + dat += "[features["insect_fluff"]]" mutant_category++ if(mutant_category >= MAX_MUTANT_ROWS) dat += "" @@ -681,13 +701,13 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "

Clothing & Equipment

" dat += "Underwear:[underwear]" if(UNDIE_COLORABLE(GLOB.underwear_list[underwear])) - dat += "Underwear Color:[undie_color]" + dat += "Underwear Color:   Change
" dat += "Undershirt:[undershirt]" if(UNDIE_COLORABLE(GLOB.undershirt_list[undershirt])) - dat += "Undershirt Color:[shirt_color]" + dat += "Undershirt Color:   Change
" dat += "Socks:[socks]" if(UNDIE_COLORABLE(GLOB.socks_list[socks])) - dat += "Socks Color:[socks_color]" + dat += "Socks Color:   Change
" dat += "Backpack:[backbag]" dat += "Jumpsuit:
[jumpsuit_style]
" dat += "Uplink Location:[uplink_spawn_loc]" @@ -992,9 +1012,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) //The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows. var/datum/job/lastJob - var/datum/job/overflow = SSjob.GetJob(SSjob.overflow_role) - - for(var/datum/job/job in SSjob.occupations) + for(var/datum/job/job in sortList(SSjob.occupations, /proc/cmp_job_display_asc)) index += 1 if((index >= limit) || (job.title in splitJobs)) @@ -1011,7 +1029,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/rank = job.title lastJob = job if(jobban_isbanned(user, rank)) - HTML += "[rank]" + HTML += "[rank]" continue var/required_playtime_remaining = job.required_playtime_remaining(user.client) if(required_playtime_remaining) @@ -1021,7 +1039,13 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/available_in_days = job.available_in_days(user.client) HTML += "[rank]" continue - if((job_civilian_low & overflow.flag) && (rank != SSjob.overflow_role) && !jobban_isbanned(user, SSjob.overflow_role)) + if(!user.client.prefs.pref_species.qualifies_for_rank(rank, user.client.prefs.features)) + if(user.client.prefs.pref_species.id == "human") + HTML += "[rank]" + else + HTML += "[rank]" + continue + if((job_preferences["[SSjob.overflow_role]"] == JP_LOW) && (rank != SSjob.overflow_role) && !jobban_isbanned(user, SSjob.overflow_role)) HTML += "[rank]" continue if((rank in GLOB.command_positions) || (rank == "AI"))//Bold head jobs @@ -1036,32 +1060,32 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/prefUpperLevel = -1 // level to assign on left click var/prefLowerLevel = -1 // level to assign on right click - if(GetJobDepartment(job, 1) & job.flag) - prefLevelLabel = "High" - prefLevelColor = "slateblue" - prefUpperLevel = 4 - prefLowerLevel = 2 - else if(GetJobDepartment(job, 2) & job.flag) - prefLevelLabel = "Medium" - prefLevelColor = "green" - prefUpperLevel = 1 - prefLowerLevel = 3 - else if(GetJobDepartment(job, 3) & job.flag) - prefLevelLabel = "Low" - prefLevelColor = "orange" - prefUpperLevel = 2 - prefLowerLevel = 4 - else - prefLevelLabel = "NEVER" - prefLevelColor = "red" - prefUpperLevel = 3 - prefLowerLevel = 1 - + switch(job_preferences["[job.title]"]) + if(JP_HIGH) + prefLevelLabel = "High" + prefLevelColor = "slateblue" + prefUpperLevel = 4 + prefLowerLevel = 2 + if(JP_MEDIUM) + prefLevelLabel = "Medium" + prefLevelColor = "green" + prefUpperLevel = 1 + prefLowerLevel = 3 + if(JP_LOW) + prefLevelLabel = "Low" + prefLevelColor = "orange" + prefUpperLevel = 2 + prefLowerLevel = 4 + else + prefLevelLabel = "NEVER" + prefLevelColor = "red" + prefUpperLevel = 3 + prefLowerLevel = 1 HTML += "" if(rank == SSjob.overflow_role)//Overflow is special - if(job_civilian_low & overflow.flag) + if(job_preferences["[SSjob.overflow_role]"] == JP_LOW) HTML += "Yes" else HTML += "No" @@ -1092,61 +1116,17 @@ GLOBAL_LIST_EMPTY(preferences_datums) /datum/preferences/proc/SetJobPreferenceLevel(datum/job/job, level) if (!job) - return 0 + return FALSE - if (level == 1) // to high - // remove any other job(s) set to high - job_civilian_med |= job_civilian_high - job_engsec_med |= job_engsec_high - job_medsci_med |= job_medsci_high - job_civilian_high = 0 - job_engsec_high = 0 - job_medsci_high = 0 + if (level == JP_HIGH) // to high + //Set all other high to medium + for(var/j in job_preferences) + if(job_preferences["[j]"] == JP_HIGH) + job_preferences["[j]"] = JP_MEDIUM + //technically break here - if (job.department_flag == CIVILIAN) - job_civilian_low &= ~job.flag - job_civilian_med &= ~job.flag - job_civilian_high &= ~job.flag - - switch(level) - if (1) - job_civilian_high |= job.flag - if (2) - job_civilian_med |= job.flag - if (3) - job_civilian_low |= job.flag - - return 1 - else if (job.department_flag == ENGSEC) - job_engsec_low &= ~job.flag - job_engsec_med &= ~job.flag - job_engsec_high &= ~job.flag - - switch(level) - if (1) - job_engsec_high |= job.flag - if (2) - job_engsec_med |= job.flag - if (3) - job_engsec_low |= job.flag - - return 1 - else if (job.department_flag == MEDSCI) - job_medsci_low &= ~job.flag - job_medsci_med &= ~job.flag - job_medsci_high &= ~job.flag - - switch(level) - if (1) - job_medsci_high |= job.flag - if (2) - job_medsci_med |= job.flag - if (3) - job_medsci_low |= job.flag - - return 1 - - return 0 + job_preferences["[job.title]"] = level + return TRUE /datum/preferences/proc/UpdateJobPreference(mob/user, role, desiredLvl) if(!SSjob || SSjob.occupations.len <= 0) @@ -1163,64 +1143,29 @@ GLOBAL_LIST_EMPTY(preferences_datums) ShowChoices(user) return - if(role == SSjob.overflow_role) - if(job_civilian_low & job.flag) - job_civilian_low &= ~job.flag - else - job_civilian_low |= job.flag - SetChoices(user) - return 1 + var/jpval = null + switch(desiredLvl) + if(3) + jpval = JP_LOW + if(2) + jpval = JP_MEDIUM + if(1) + jpval = JP_HIGH - SetJobPreferenceLevel(job, desiredLvl) + if(role == SSjob.overflow_role) + if(job_preferences["[job.title]"] == JP_LOW) + jpval = null + else + jpval = JP_LOW + + SetJobPreferenceLevel(job, jpval) SetChoices(user) return 1 /datum/preferences/proc/ResetJobs() - - job_civilian_high = 0 - job_civilian_med = 0 - job_civilian_low = 0 - - job_medsci_high = 0 - job_medsci_med = 0 - job_medsci_low = 0 - - job_engsec_high = 0 - job_engsec_med = 0 - job_engsec_low = 0 - - -/datum/preferences/proc/GetJobDepartment(datum/job/job, level) - if(!job || !level) - return 0 - switch(job.department_flag) - if(CIVILIAN) - switch(level) - if(1) - return job_civilian_high - if(2) - return job_civilian_med - if(3) - return job_civilian_low - if(MEDSCI) - switch(level) - if(1) - return job_medsci_high - if(2) - return job_medsci_med - if(3) - return job_medsci_low - if(ENGSEC) - switch(level) - if(1) - return job_engsec_high - if(2) - return job_engsec_med - if(3) - return job_engsec_low - return 0 + job_preferences = list() /datum/preferences/proc/SetQuirks(mob/user) if(!SSquirks) @@ -1562,9 +1507,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) eye_color = sanitize_hexcolor(new_eyes) if("species") - var/result = input(user, "Select a species", "Species Selection") as null|anything in GLOB.roundstart_races + var/result = input(user, "Select a species", "Species Selection") as null|anything in GLOB.roundstart_race_names if(result) - var/newtype = GLOB.species_list[result] + var/newtype = GLOB.species_list[GLOB.roundstart_race_names[result]] pref_species = new newtype() //let's ensure that no weird shit happens on species swapping. custom_species = null @@ -1692,6 +1637,12 @@ GLOBAL_LIST_EMPTY(preferences_datums) features["tail_human"] = "None" features["tail_lizard"] = "None" + if("meats") + var/new_meat + new_meat = input(user, "Choose your character's meat type:", "Character Preference") as null|anything in GLOB.meat_types + if(new_meat) + features["meat_type"] = new_meat + if("snout") var/list/snowflake_snouts_list = list() for(var/path in GLOB.snouts_list) @@ -1727,6 +1678,11 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(new_horns) features["horns"] = new_horns + if("horns_color") + var/new_horn_color = input(user, "Choose your character's horn colour:", "Character Preference","#"+horn_color) as color|null + if(new_horn_color) + horn_color = sanitize_hexcolor(new_horn_color) + if("wings") var/new_wings new_wings = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.r_wings_list @@ -1761,11 +1717,17 @@ GLOBAL_LIST_EMPTY(preferences_datums) features["legs"] = new_legs update_preview_icon() - if("moth_wings") - var/new_moth_wings - new_moth_wings = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.moth_wings_list - if(new_moth_wings) - features["moth_wings"] = new_moth_wings + if("insect_wings") + var/new_insect_wings + new_insect_wings = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.insect_wings_list + if(new_insect_wings) + features["insect_wings"] = new_insect_wings + + if("insect_fluffs") + var/new_insect_fluff + new_insect_fluff = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.insect_fluffs_list + if(new_insect_fluff) + features["insect_fluff"] = new_insect_fluff if("s_tone") var/new_s_tone = input(user, "Choose your character's skin-tone:", "Character Preference") as null|anything in GLOB.skin_tones @@ -2285,6 +2247,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) organ_eyes.old_eye_color = eye_color character.hair_color = hair_color character.facial_hair_color = facial_hair_color + character.horn_color = horn_color character.skin_tone = skin_tone character.hair_style = hair_style @@ -2325,7 +2288,10 @@ GLOBAL_LIST_EMPTY(preferences_datums) if("xenotail" in pref_species.default_features) character.dna.species.mutant_bodyparts |= "xenotail" - if(("legs" in character.dna.species.mutant_bodyparts) && character.dna.features["legs"] == "Digitigrade Legs") + if("meat_type" in pref_species.default_features) + character.type_of_meat = GLOB.meat_types[features["meat_type"]] + + if(("legs" in character.dna.species.mutant_bodyparts) && (character.dna.features["legs"] == "Digitigrade" || character.dna.features["legs"] == "Avian")) pref_species.species_traits |= DIGITIGRADE else pref_species.species_traits -= DIGITIGRADE diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 7911d92495..ebd135916c 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -5,7 +5,7 @@ // You do not need to raise this if you are adding new values that have sane defaults. // Only raise this value when changing the meaning/format/name/layout of an existing value // where you would want the updater procs below to run -#define SAVEFILE_VERSION_MAX 20 +#define SAVEFILE_VERSION_MAX 23 /* SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn @@ -49,6 +49,65 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car pda_style = "mono" if(current_version < 20) pda_color = "#808000" + if((current_version < 21) && features["meat_type"] && (features["meat_type"] == null)) + features["meat_type"] = "Mammalian" + if(current_version < 22) + + job_preferences = list() //It loaded null from nonexistant savefile field. + + var/job_civilian_high = 0 + var/job_civilian_med = 0 + var/job_civilian_low = 0 + + var/job_medsci_high = 0 + var/job_medsci_med = 0 + var/job_medsci_low = 0 + + var/job_engsec_high = 0 + var/job_engsec_med = 0 + var/job_engsec_low = 0 + + S["job_civilian_high"] >> job_civilian_high + S["job_civilian_med"] >> job_civilian_med + S["job_civilian_low"] >> job_civilian_low + S["job_medsci_high"] >> job_medsci_high + S["job_medsci_med"] >> job_medsci_med + S["job_medsci_low"] >> job_medsci_low + S["job_engsec_high"] >> job_engsec_high + S["job_engsec_med"] >> job_engsec_med + S["job_engsec_low"] >> job_engsec_low + + //Can't use SSjob here since this happens right away on login + for(var/job in subtypesof(/datum/job)) + var/datum/job/J = job + var/new_value + var/fval = initial(J.flag) + switch(initial(J.department_flag)) + if(CIVILIAN) + if(job_civilian_high & fval) + new_value = JP_HIGH + else if(job_civilian_med & fval) + new_value = JP_MEDIUM + else if(job_civilian_low & fval) + new_value = JP_LOW + if(MEDSCI) + if(job_medsci_high & fval) + new_value = JP_HIGH + else if(job_medsci_med & fval) + new_value = JP_MEDIUM + else if(job_medsci_low & fval) + new_value = JP_LOW + if(ENGSEC) + if(job_engsec_high & fval) + new_value = JP_HIGH + else if(job_engsec_med & fval) + new_value = JP_MEDIUM + else if(job_engsec_low & fval) + new_value = JP_LOW + if(new_value) + job_preferences["[initial(J.title)]"] = new_value + else if(current_version < 23) // we are fixing a gamebreaking bug. + job_preferences = list() //It loaded null from nonexistant savefile field. /datum/preferences/proc/load_path(ckey,filename="preferences.sav") if(!ckey) @@ -246,6 +305,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car var/species_id S["species"] >> species_id if(species_id) + if(species_id == "avian" || species_id == "aquatic") + species_id = "mammal" + else if(species_id == "moth") + species_id = "insect" + var/newtype = GLOB.species_list[species_id] if(newtype) pref_species = new newtype @@ -254,28 +318,29 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["features["mcolor"]"] , "#FFF") //Character - S["real_name"] >> real_name - S["nameless"] >> nameless - S["custom_species"] >> custom_species - S["name_is_always_random"] >> be_random_name - S["body_is_always_random"] >> be_random_body - S["gender"] >> gender - S["age"] >> age - S["hair_color"] >> hair_color - S["facial_hair_color"] >> facial_hair_color - S["eye_color"] >> eye_color - S["skin_tone"] >> skin_tone - S["hair_style_name"] >> hair_style - S["facial_style_name"] >> facial_hair_style - S["underwear"] >> underwear - S["undie_color"] >> undie_color - S["undershirt"] >> undershirt - S["shirt_color"] >> shirt_color - S["socks"] >> socks - S["socks_color"] >> socks_color - S["backbag"] >> backbag - S["jumpsuit_style"] >> jumpsuit_style - S["uplink_loc"] >> uplink_spawn_loc + S["real_name"] >> real_name + S["nameless"] >> nameless + S["custom_species"] >> custom_species + S["name_is_always_random"] >> be_random_name + S["body_is_always_random"] >> be_random_body + S["gender"] >> gender + S["age"] >> age + S["hair_color"] >> hair_color + S["facial_hair_color"] >> facial_hair_color + S["eye_color"] >> eye_color + S["skin_tone"] >> skin_tone + S["hair_style_name"] >> hair_style + S["facial_style_name"] >> facial_hair_style + S["underwear"] >> underwear + S["undie_color"] >> undie_color + S["undershirt"] >> undershirt + S["shirt_color"] >> shirt_color + S["socks"] >> socks + S["socks_color"] >> socks_color + S["horn_color"] >> horn_color + S["backbag"] >> backbag + S["jumpsuit_style"] >> jumpsuit_style + S["uplink_loc"] >> uplink_spawn_loc S["feature_mcolor"] >> features["mcolor"] S["feature_lizard_tail"] >> features["tail_lizard"] S["feature_lizard_snout"] >> features["snout"] @@ -284,29 +349,23 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["feature_lizard_spines"] >> features["spines"] S["feature_lizard_body_markings"] >> features["body_markings"] S["feature_lizard_legs"] >> features["legs"] - S["feature_moth_wings"] >> features["moth_wings"] S["feature_human_tail"] >> features["tail_human"] S["feature_human_ears"] >> features["ears"] + S["feature_insect_wings"] >> features["insect_wings"] + S["feature_insect_fluff"] >> features["insect_fluff"] //Custom names for(var/custom_name_id in GLOB.preferences_custom_names) var/savefile_slot_name = custom_name_id + "_name" //TODO remove this S[savefile_slot_name] >> custom_names[custom_name_id] - S["preferred_ai_core_display"] >> preferred_ai_core_display - S["prefered_security_department"] >> prefered_security_department + S["preferred_ai_core_display"] >> preferred_ai_core_display + S["prefered_security_department"] >> prefered_security_department //Jobs S["joblessrole"] >> joblessrole - S["job_civilian_high"] >> job_civilian_high - S["job_civilian_med"] >> job_civilian_med - S["job_civilian_low"] >> job_civilian_low - S["job_medsci_high"] >> job_medsci_high - S["job_medsci_med"] >> job_medsci_med - S["job_medsci_low"] >> job_medsci_low - S["job_engsec_high"] >> job_engsec_high - S["job_engsec_med"] >> job_engsec_med - S["job_engsec_low"] >> job_engsec_low + //Load prefs + S["job_preferences"] >> job_preferences //Quirks S["all_quirks"] >> all_quirks @@ -325,6 +384,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["feature_mam_tail_animated"] >> features["mam_tail_animated"] S["feature_taur"] >> features["taur"] S["feature_mam_snouts"] >> features["mam_snouts"] + S["feature_meat"] >> features["meat_type"] //Xeno features S["feature_xeno_tail"] >> features["xenotail"] S["feature_xeno_dors"] >> features["xenodorsal"] @@ -374,11 +434,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car //Sanitize - real_name = reject_bad_name(real_name) - gender = sanitize_gender(gender, TRUE, TRUE) + real_name = reject_bad_name(real_name) + gender = sanitize_gender(gender, TRUE, TRUE) if(!real_name) - real_name = random_unique_name(gender) - custom_species = reject_bad_name(custom_species) + real_name = random_unique_name(gender) + custom_species = reject_bad_name(custom_species) for(var/custom_name_id in GLOB.preferences_custom_names) var/namedata = GLOB.preferences_custom_names[custom_name_id] custom_names[custom_name_id] = reject_bad_name(custom_names[custom_name_id],namedata["allow_numbers"]) @@ -388,57 +448,55 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car if(!features["mcolor"] || features["mcolor"] == "#000") features["mcolor"] = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F") - nameless = sanitize_integer(nameless, 0, 1, initial(nameless)) + nameless = sanitize_integer(nameless, 0, 1, initial(nameless)) be_random_name = sanitize_integer(be_random_name, 0, 1, initial(be_random_name)) be_random_body = sanitize_integer(be_random_body, 0, 1, initial(be_random_body)) if(gender == MALE) - hair_style = sanitize_inlist(hair_style, GLOB.hair_styles_male_list) + hair_style = sanitize_inlist(hair_style, GLOB.hair_styles_male_list) facial_hair_style = sanitize_inlist(facial_hair_style, GLOB.facial_hair_styles_male_list) else - hair_style = sanitize_inlist(hair_style, GLOB.hair_styles_female_list) + hair_style = sanitize_inlist(hair_style, GLOB.hair_styles_female_list) facial_hair_style = sanitize_inlist(facial_hair_style, GLOB.facial_hair_styles_female_list) - underwear = sanitize_inlist(underwear, GLOB.underwear_list) - undie_color = sanitize_hexcolor(undie_color, 6, 1, initial(undie_color)) - undershirt = sanitize_inlist(undershirt, GLOB.undershirt_list) - shirt_color = sanitize_hexcolor(shirt_color, 6, 1, initial(shirt_color)) - socks = sanitize_inlist(socks, GLOB.socks_list) - socks_color = sanitize_hexcolor(socks_color, 6, 1, initial(socks_color)) - age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age)) - hair_color = sanitize_hexcolor(hair_color, 3, 0) - facial_hair_color = sanitize_hexcolor(facial_hair_color, 3, 0) - eye_color = sanitize_hexcolor(eye_color, 3, 0) - skin_tone = sanitize_inlist(skin_tone, GLOB.skin_tones) - backbag = sanitize_inlist(backbag, GLOB.backbaglist, initial(backbag)) - jumpsuit_style = sanitize_inlist(jumpsuit_style, GLOB.jumpsuitlist, initial(jumpsuit_style)) - uplink_spawn_loc = sanitize_inlist(uplink_spawn_loc, GLOB.uplink_spawn_loc_list, initial(uplink_spawn_loc)) - features["mcolor"] = sanitize_hexcolor(features["mcolor"], 3, 0) - features["tail_lizard"] = sanitize_inlist(features["tail_lizard"], GLOB.tails_list_lizard) - features["tail_human"] = sanitize_inlist(features["tail_human"], GLOB.tails_list_human) - features["snout"] = sanitize_inlist(features["snout"], GLOB.snouts_list) - features["horns"] = sanitize_inlist(features["horns"], GLOB.horns_list) - features["ears"] = sanitize_inlist(features["ears"], GLOB.ears_list) - features["frills"] = sanitize_inlist(features["frills"], GLOB.frills_list) - features["spines"] = sanitize_inlist(features["spines"], GLOB.spines_list) - features["body_markings"] = sanitize_inlist(features["body_markings"], GLOB.body_markings_list) + underwear = sanitize_inlist(underwear, GLOB.underwear_list) + undie_color = sanitize_hexcolor(undie_color, 3, FALSE, initial(undie_color)) + undershirt = sanitize_inlist(undershirt, GLOB.undershirt_list) + shirt_color = sanitize_hexcolor(shirt_color, 3, FALSE, initial(shirt_color)) + socks = sanitize_inlist(socks, GLOB.socks_list) + socks_color = sanitize_hexcolor(socks_color, 3, FALSE, initial(socks_color)) + age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age)) + hair_color = sanitize_hexcolor(hair_color, 3, 0) + facial_hair_color = sanitize_hexcolor(facial_hair_color, 3, 0) + eye_color = sanitize_hexcolor(eye_color, 3, 0) + skin_tone = sanitize_inlist(skin_tone, GLOB.skin_tones) + horn_color = sanitize_hexcolor(horn_color, 3, FALSE) + backbag = sanitize_inlist(backbag, GLOB.backbaglist, initial(backbag)) + jumpsuit_style = sanitize_inlist(jumpsuit_style, GLOB.jumpsuitlist, initial(jumpsuit_style)) + uplink_spawn_loc = sanitize_inlist(uplink_spawn_loc, GLOB.uplink_spawn_loc_list, initial(uplink_spawn_loc)) + features["mcolor"] = sanitize_hexcolor(features["mcolor"], 3, 0) + features["tail_lizard"] = sanitize_inlist(features["tail_lizard"], GLOB.tails_list_lizard) + features["tail_human"] = sanitize_inlist(features["tail_human"], GLOB.tails_list_human) + features["snout"] = sanitize_inlist(features["snout"], GLOB.snouts_list) + features["horns"] = sanitize_inlist(features["horns"], GLOB.horns_list) + features["ears"] = sanitize_inlist(features["ears"], GLOB.ears_list) + features["frills"] = sanitize_inlist(features["frills"], GLOB.frills_list) + features["spines"] = sanitize_inlist(features["spines"], GLOB.spines_list) + features["body_markings"] = sanitize_inlist(features["body_markings"], GLOB.body_markings_list) features["feature_lizard_legs"] = sanitize_inlist(features["legs"], GLOB.legs_list) - features["moth_wings"] = sanitize_inlist(features["moth_wings"], GLOB.moth_wings_list) + features["insect_wings"] = sanitize_inlist(features["insect_wings"], GLOB.insect_wings_list) + features["insect_fluff"] = sanitize_inlist(features["insect_fluff"], GLOB.insect_fluffs_list) joblessrole = sanitize_integer(joblessrole, 1, 3, initial(joblessrole)) - job_civilian_high = sanitize_integer(job_civilian_high, 0, 65535, initial(job_civilian_high)) - job_civilian_med = sanitize_integer(job_civilian_med, 0, 65535, initial(job_civilian_med)) - job_civilian_low = sanitize_integer(job_civilian_low, 0, 65535, initial(job_civilian_low)) - job_medsci_high = sanitize_integer(job_medsci_high, 0, 65535, initial(job_medsci_high)) - job_medsci_med = sanitize_integer(job_medsci_med, 0, 65535, initial(job_medsci_med)) - job_medsci_low = sanitize_integer(job_medsci_low, 0, 65535, initial(job_medsci_low)) - job_engsec_high = sanitize_integer(job_engsec_high, 0, 65535, initial(job_engsec_high)) - job_engsec_med = sanitize_integer(job_engsec_med, 0, 65535, initial(job_engsec_med)) - job_engsec_low = sanitize_integer(job_engsec_low, 0, 65535, initial(job_engsec_low)) + //Validate job prefs + for(var/j in job_preferences) + if(job_preferences["[j]"] != JP_LOW && job_preferences["[j]"] != JP_MEDIUM && job_preferences["[j]"] != JP_HIGH) + job_preferences -= j all_quirks = SANITIZE_LIST(all_quirks) + positive_quirks = SANITIZE_LIST(positive_quirks) negative_quirks = SANITIZE_LIST(negative_quirks) - neutral_quirks = SANITIZE_LIST(neutral_quirks) + neutral_quirks = SANITIZE_LIST(neutral_quirks) cit_character_pref_load(S) @@ -460,31 +518,32 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["version"] , SAVEFILE_VERSION_MAX) //load_character will sanitize any bad data, so assume up-to-date.) //Character - WRITE_FILE(S["real_name"] , real_name) - WRITE_FILE(S["nameless"] , nameless) - WRITE_FILE(S["custom_species"] , custom_species) - WRITE_FILE(S["name_is_always_random"] , be_random_name) - WRITE_FILE(S["body_is_always_random"] , be_random_body) - WRITE_FILE(S["gender"] , gender) - WRITE_FILE(S["age"] , age) - WRITE_FILE(S["hair_color"] , hair_color) - WRITE_FILE(S["facial_hair_color"] , facial_hair_color) - WRITE_FILE(S["eye_color"] , eye_color) - WRITE_FILE(S["skin_tone"] , skin_tone) - WRITE_FILE(S["hair_style_name"] , hair_style) - WRITE_FILE(S["facial_style_name"] , facial_hair_style) - WRITE_FILE(S["underwear"] , underwear) - WRITE_FILE(S["undie_color"] , undie_color) - WRITE_FILE(S["undershirt"] , undershirt) - WRITE_FILE(S["shirt_color"] , shirt_color) - WRITE_FILE(S["socks"] , socks) - WRITE_FILE(S["socks_color"] , socks_color) - WRITE_FILE(S["backbag"] , backbag) - WRITE_FILE(S["jumpsuit_style"] , jumpsuit_style) - WRITE_FILE(S["uplink_loc"] , uplink_spawn_loc) - WRITE_FILE(S["species"] , pref_species.id) + WRITE_FILE(S["real_name"] , real_name) + WRITE_FILE(S["nameless"] , nameless) + WRITE_FILE(S["custom_species"] , custom_species) + WRITE_FILE(S["name_is_always_random"] , be_random_name) + WRITE_FILE(S["body_is_always_random"] , be_random_body) + WRITE_FILE(S["gender"] , gender) + WRITE_FILE(S["age"] , age) + WRITE_FILE(S["hair_color"] , hair_color) + WRITE_FILE(S["facial_hair_color"] , facial_hair_color) + WRITE_FILE(S["eye_color"] , eye_color) + WRITE_FILE(S["skin_tone"] , skin_tone) + WRITE_FILE(S["hair_style_name"] , hair_style) + WRITE_FILE(S["facial_style_name"] , facial_hair_style) + WRITE_FILE(S["underwear"] , underwear) + WRITE_FILE(S["undie_color"] , undie_color) + WRITE_FILE(S["undershirt"] , undershirt) + WRITE_FILE(S["shirt_color"] , shirt_color) + WRITE_FILE(S["socks"] , socks) + WRITE_FILE(S["socks_color"] , socks_color) + WRITE_FILE(S["horn_color"] , horn_color) + WRITE_FILE(S["backbag"] , backbag) + WRITE_FILE(S["jumpsuit_style"] , jumpsuit_style) + WRITE_FILE(S["uplink_loc"] , uplink_spawn_loc) + WRITE_FILE(S["species"] , pref_species.id) WRITE_FILE(S["feature_mcolor"] , features["mcolor"]) - WRITE_FILE(S["feature_lizard_tail"] , features["tail_lizard"]) + WRITE_FILE(S["feature_lizard_tail"] , features["tail_lizard"]) WRITE_FILE(S["feature_human_tail"] , features["tail_human"]) WRITE_FILE(S["feature_lizard_snout"] , features["snout"]) WRITE_FILE(S["feature_lizard_horns"] , features["horns"]) @@ -492,28 +551,23 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["feature_lizard_frills"] , features["frills"]) WRITE_FILE(S["feature_lizard_spines"] , features["spines"]) WRITE_FILE(S["feature_lizard_body_markings"] , features["body_markings"]) - WRITE_FILE(S["feature_lizard_legs"] , features["legs"]) - WRITE_FILE(S["feature_moth_wings"] , features["moth_wings"]) + WRITE_FILE(S["feature_lizard_legs"] , features["legs"]) + WRITE_FILE(S["feature_insect_wings"] , features["insect_wings"]) + WRITE_FILE(S["feature_insect_fluff"] , features["insect_fluff"]) + WRITE_FILE(S["feature_meat"] , features["meat_type"]) //Custom names for(var/custom_name_id in GLOB.preferences_custom_names) var/savefile_slot_name = custom_name_id + "_name" //TODO remove this WRITE_FILE(S[savefile_slot_name],custom_names[custom_name_id]) - WRITE_FILE(S["preferred_ai_core_display"] , preferred_ai_core_display) - WRITE_FILE(S["prefered_security_department"] , prefered_security_department) + WRITE_FILE(S["preferred_ai_core_display"] , preferred_ai_core_display) + WRITE_FILE(S["prefered_security_department"] , prefered_security_department) //Jobs WRITE_FILE(S["joblessrole"] , joblessrole) - WRITE_FILE(S["job_civilian_high"] , job_civilian_high) - WRITE_FILE(S["job_civilian_med"] , job_civilian_med) - WRITE_FILE(S["job_civilian_low"] , job_civilian_low) - WRITE_FILE(S["job_medsci_high"] , job_medsci_high) - WRITE_FILE(S["job_medsci_med"] , job_medsci_med) - WRITE_FILE(S["job_medsci_low"] , job_medsci_low) - WRITE_FILE(S["job_engsec_high"] , job_engsec_high) - WRITE_FILE(S["job_engsec_med"] , job_engsec_med) - WRITE_FILE(S["job_engsec_low"] , job_engsec_low) + //Write prefs + WRITE_FILE(S["job_preferences"] , job_preferences) //Quirks WRITE_FILE(S["all_quirks"] , all_quirks) diff --git a/code/modules/client/verbs/looc.dm b/code/modules/client/verbs/looc.dm index 4049034203..b371372dfe 100644 --- a/code/modules/client/verbs/looc.dm +++ b/code/modules/client/verbs/looc.dm @@ -1,3 +1,6 @@ +GLOBAL_VAR_INIT(LOOC_COLOR, null)//If this is null, use the CSS for OOC. Otherwise, use a custom colour. +GLOBAL_VAR_INIT(normal_looc_colour, "#6699CC") + /client/verb/looc(msg as text) set name = "LOOC" set desc = "Local OOC, seen only by those in view." @@ -59,30 +62,17 @@ continue //Also handled later. if(C.prefs.toggles & CHAT_OOC) -// var/display_name = src.key -// if(holder) -// if(holder.fakekey) -// if(C.holder) -// display_name = "[holder.fakekey]/([src.key])" -// else -// display_name = holder.fakekey - to_chat(C,"LOOC: [src.mob.name]: [msg]") + if(GLOB.LOOC_COLOR) + to_chat(C, "LOOC: [src.mob.name]: [msg]") + else + to_chat(C, "LOOC: [src.mob.name]: [msg]") for(var/client/C in GLOB.admins) if(C.prefs.toggles & CHAT_OOC) var/prefix = "(R)LOOC" if (C.mob in heard) prefix = "LOOC" - to_chat(C,"[ADMIN_FLW(usr)][prefix]: [src.key]/[src.mob.name]: [msg]") - - /*for(var/mob/dead/observer/G in world) - if(!G.client) - continue - var/client/C = G.client - if (C in GLOB.admins) - continue //handled earlier. - if(C.prefs.toggles & CHAT_OOC) - var/prefix = "(G)LOOC" - if (C.mob in heard) - prefix = "LOOC" - to_chat(C,"[prefix]: [src.key]/[src.mob.name]: [msg]")*/ + if(GLOB.LOOC_COLOR) + to_chat(C, "[ADMIN_FLW(usr)] [prefix]: [src.key]/[src.mob.name]: [msg]") + else + to_chat(C, "[ADMIN_FLW(usr)] [prefix]: [src.key]/[src.mob.name]: [msg]") diff --git a/code/modules/clothing/glasses/vg_glasses.dm b/code/modules/clothing/glasses/vg_glasses.dm index 449b34b22e..a51a03242c 100644 --- a/code/modules/clothing/glasses/vg_glasses.dm +++ b/code/modules/clothing/glasses/vg_glasses.dm @@ -6,21 +6,18 @@ name = "purple sunglasses" icon_state = "sun_purple" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/glasses.dmi' /obj/item/clothing/glasses/sunglasses/star name = "star-shaped sunglasses" desc = "Novelty sunglasses, both lenses are in the shape of a star." icon_state = "sun_star" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/glasses.dmi' /obj/item/clothing/glasses/sunglasses/rockstar name = "red star-shaped sunglasses" desc = "Novelty sunglasses with a fancy silver frame and two red-tinted star-shaped lenses. You should probably stomp on them and get a pair of normal ones." icon_state = "sun_star_silver" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/glasses.dmi' /obj/item/clothing/glasses/gglasses name = "Green Glasses" @@ -28,7 +25,6 @@ icon_state = "gglasses" item_state = "gglasses" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/glasses.dmi' /obj/item/clothing/glasses/welding/superior name = "superior welding goggles" @@ -36,7 +32,6 @@ icon_state = "rwelding-g" item_state = "rwelding-g" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/glasses.dmi' actions_types = list(/datum/action/item_action/toggle) flash_protect = 2 tint = 1 diff --git a/code/modules/clothing/head/cit_hats.dm b/code/modules/clothing/head/cit_hats.dm index 7b0e68264f..df4a21d6c1 100644 --- a/code/modules/clothing/head/cit_hats.dm +++ b/code/modules/clothing/head/cit_hats.dm @@ -4,4 +4,3 @@ icon = 'modular_citadel/icons/obj/clothing/cit_hats.dmi' icon_state = "hunter" item_state = "hunter_worn" - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm index 5c7514541d..b14b0575f9 100644 --- a/code/modules/clothing/head/soft_caps.dm +++ b/code/modules/clothing/head/soft_caps.dm @@ -129,3 +129,14 @@ icon_state = "emtsoft" item_color = "emt" dog_fashion = null + +/obj/item/clothing/head/soft/baseball + name = "baseball cap" + desc = "It's a robust baseball hat, this one belongs to syndicate major league team." + icon_state = "baseballsoft" + item_color = "baseballsoft" + item_state = "baseballsoft" + flags_inv = HIDEEYES|HIDEFACE + armor = list("melee" = 35, "bullet" = 35, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 90) + strip_delay = 90 //You dont take a Major Leage cap + dog_fashion = null \ No newline at end of file diff --git a/code/modules/clothing/head/vg_hats.dm b/code/modules/clothing/head/vg_hats.dm index efeb5291db..dc245cd39a 100644 --- a/code/modules/clothing/head/vg_hats.dm +++ b/code/modules/clothing/head/vg_hats.dm @@ -1,4 +1,3 @@ - /obj/item/clothing/head/helmet/dredd name = "Judge Helmet" desc = "Judge, Jury, and Executioner." @@ -12,7 +11,6 @@ max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT strip_delay = 80 dog_fashion = null - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' /obj/item/clothing/head/helmet/aviatorhelmet name = "Aviator Helmet" @@ -21,15 +19,12 @@ item_state = "aviator_helmet" icon_state = "aviator_helmet" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' /obj/item/clothing/head/helmet/biker name = "Biker's Helmet" desc = "This helmet should protect you from russians and masked vigilantes." armor = list(melee = 25, bullet = 15, laser = 20, energy = 10, bomb = 10, bio = 0, rad = 0) icon_state = "biker_helmet" - icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR /obj/item/clothing/head/helmet/richard @@ -38,7 +33,6 @@ armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) icon_state = "richard" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR /obj/item/clothing/head/helmet/megahelmet @@ -47,7 +41,6 @@ icon_state = "megahelmet" item_state = "megahelmet" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' siemens_coefficient = 1 /obj/item/clothing/head/helmet/protohelmet @@ -56,7 +49,6 @@ icon_state = "protohelmet" item_state = "protohelmet" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' siemens_coefficient = 1 /obj/item/clothing/head/helmet/megaxhelmet @@ -65,7 +57,6 @@ icon_state = "megaxhelmet" item_state = "megaxhelmet" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' /obj/item/clothing/head/helmet/joehelmet name = "Sniper Helmet" @@ -74,7 +65,6 @@ flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR item_state = "joehelmet" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' /obj/item/clothing/head/helmet/doomguy name = "Doomguy's helmet" @@ -83,7 +73,6 @@ flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR item_state = "doom" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' armor = list(melee = 50, bullet = 40, laser = 40,energy = 40, bomb = 5, bio = 0, rad = 0) /obj/item/clothing/head/helmet/neorussian @@ -92,8 +81,6 @@ icon_state = "nr_helmet" item_state = "nr_helmet" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' - /obj/item/clothing/head/stalhelm name = "Stalhelm" @@ -101,7 +88,6 @@ icon_state = "stalhelm" item_state = "stalhelm" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' /obj/item/clothing/head/panzer name = "Panzer Cap" @@ -109,7 +95,6 @@ icon_state = "panzercap" item_state = "panzercap" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' /obj/item/clothing/head/naziofficer name = "Officer Cap" @@ -117,8 +102,6 @@ icon_state = "officercap" item_state = "officercap" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' - /obj/item/clothing/head/russobluecamohat name = "russian blue camo beret" @@ -133,7 +116,6 @@ icon_state = "russofurhat" item_state = "russofurhat" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' /obj/item/clothing/head/squatter_hat name = "slav squatter hat" @@ -141,7 +123,6 @@ item_state = "squatter_hat" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' desc = "Cyka blyat." - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' /obj/item/clothing/head/snake name = "snake head" @@ -149,8 +130,6 @@ icon_state = "snakehead" item_state = "snakehead" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' - /obj/item/clothing/head/mummy_rags name = "mummy rags" desc = "Ancient rags taken off from some mummy." @@ -158,7 +137,6 @@ item_state = "mummy" item_color = "mummy" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS /obj/item/clothing/head/clownpiece @@ -167,7 +145,6 @@ icon_state = "clownpiece" item_state = "clownpiece" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' /obj/item/clothing/head/mitre name = "mitre" @@ -175,7 +152,6 @@ icon_state = "mitre" item_state = "mitre" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' /obj/item/clothing/head/tinfoil name = "tinfoil hat" @@ -183,7 +159,6 @@ icon_state = "foilhat" item_state = "paper" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' siemens_coefficient = 2 /obj/item/clothing/head/celtic @@ -192,4 +167,3 @@ icon_state = "celtic_crown" item_state = "celtic_crown" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' diff --git a/code/modules/clothing/masks/vg_masks.dm b/code/modules/clothing/masks/vg_masks.dm index 7de27fb780..29b65a6b8e 100644 --- a/code/modules/clothing/masks/vg_masks.dm +++ b/code/modules/clothing/masks/vg_masks.dm @@ -3,18 +3,15 @@ desc = "Some pranksters are truly magical." icon_state = "wizzclown" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/masks.dmi' /obj/item/clothing/mask/chapmask name = "venetian mask" desc = "A plain porcelain mask that covers the entire face. Standard attire for particularly unspeakable religions. The eyes are wide shut." icon_state = "chapmask" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/masks.dmi' /obj/item/clothing/mask/neorussian name = "neo-Russian mask" desc = "Somehow, it makes you act and look way more polite than usual." icon_state = "nr_mask" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/masks.dmi' diff --git a/code/modules/clothing/outfits/plasmaman.dm b/code/modules/clothing/outfits/plasmaman.dm new file mode 100644 index 0000000000..20b67891d8 --- /dev/null +++ b/code/modules/clothing/outfits/plasmaman.dm @@ -0,0 +1,164 @@ +/datum/outfit/plasmaman/bar + name = "Bartender Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/white + uniform = /obj/item/clothing/under/plasmaman/enviroslacks + +/datum/outfit/plasmaman/chef + name = "Chef Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/white + uniform = /obj/item/clothing/under/plasmaman/chef + +/datum/outfit/plasmaman/botany + name = "Botany Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/botany + uniform = /obj/item/clothing/under/plasmaman/botany + +/datum/outfit/plasmaman/curator + name = "Curator Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/curator + uniform = /obj/item/clothing/under/plasmaman/curator + +/datum/outfit/plasmaman/chaplain + name = "Chaplain Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/chaplain + uniform = /obj/item/clothing/under/plasmaman/chaplain + +/datum/outfit/plasmaman/janitor + name = "Janitor Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/janitor + uniform = /obj/item/clothing/under/plasmaman/janitor + +/datum/outfit/plasmaman/hop + name = "Head of Personell Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/hop + uniform = /obj/item/clothing/under/plasmaman/hop + +/datum/outfit/plasmaman/captain + name = "Captain Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/captain + uniform = /obj/item/clothing/under/plasmaman/captain + +/datum/outfit/plasmaman/security + name = "Security Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/security + uniform = /obj/item/clothing/under/plasmaman/security + ears = /obj/item/radio/headset/headset_sec + +/datum/outfit/plasmaman/detective + name = "Detective Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/white + uniform = /obj/item/clothing/under/plasmaman/enviroslacks + +/datum/outfit/plasmaman/warden + name = "Warden Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/security/warden + uniform = /obj/item/clothing/under/plasmaman/security/warden + +/datum/outfit/plasmaman/hos + name = "Head of Security Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/security/hos + uniform = /obj/item/clothing/under/plasmaman/security/hos + +/datum/outfit/plasmaman/cargo + name = "Cargo Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/cargo + uniform = /obj/item/clothing/under/plasmaman/cargo + +/datum/outfit/plasmaman/mining + name = "Mining Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/mining + uniform = /obj/item/clothing/under/plasmaman/mining + +/datum/outfit/plasmaman/medical + name = "Medical Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/medical + uniform = /obj/item/clothing/under/plasmaman/medical + +/datum/outfit/plasmaman/cmo + name = "Chief Medical Officer Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/cmo + uniform = /obj/item/clothing/under/plasmaman/cmo + +/datum/outfit/plasmaman/viro + name = "Virology Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/viro + uniform = /obj/item/clothing/under/plasmaman/viro + +/datum/outfit/plasmaman/chemist + name = "Chemist Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/chemist + uniform = /obj/item/clothing/under/plasmaman/chemist + +/datum/outfit/plasmaman/genetics + name = "Genetics Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/genetics + uniform = /obj/item/clothing/under/plasmaman/genetics + +/datum/outfit/plasmaman/science + name = "Science Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/science + uniform = /obj/item/clothing/under/plasmaman/science + +/datum/outfit/plasmaman/rd + name = "Research Director Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/rd + uniform = /obj/item/clothing/under/plasmaman/rd + +/datum/outfit/plasmaman/robotics + name = "Robotics Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/robotics + uniform = /obj/item/clothing/under/plasmaman/robotics + +/datum/outfit/plasmaman/engineering + name = "Engineering Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/engineering + uniform = /obj/item/clothing/under/plasmaman/engineering + +/datum/outfit/plasmaman/ce + name = "Chief Engineer Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/engineering/ce + uniform = /obj/item/clothing/under/plasmaman/engineering/ce + +/datum/outfit/plasmaman/atmospherics + name = "Atmospherics Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/atmospherics + uniform = /obj/item/clothing/under/plasmaman/atmospherics + +/datum/outfit/plasmaman/mime + name = "Plasmamime" + + head = /obj/item/clothing/head/helmet/space/plasmaman/mime + uniform = /obj/item/clothing/under/plasmaman/mime + mask = /obj/item/clothing/mask/gas/mime + +/datum/outfit/plasmaman/clown + name = "Plasmaclown" + + head = /obj/item/clothing/head/helmet/space/plasmaman/clown + uniform = /obj/item/clothing/under/plasmaman/clown + mask = /obj/item/clothing/mask/gas/clown_hat \ No newline at end of file diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index 4659de3e6a..7fe527fec3 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -62,6 +62,16 @@ desc = "A pair of orange rubber boots, designed to prevent slipping on wet surfaces while also drying them." icon_state = "galoshes_dry" +/obj/item/clothing/shoes/sneakers/noslip + desc = "A pair of black shoes, they have the soles of galoshes making them unable to be slipped on a wet surface." + name = "black shoes" + icon_state = "black" + permeability_coefficient = 0.30 + clothing_flags = NOSLIP + strip_delay = 50 + equip_delay_other = 50 + resistance_flags = NONE + /obj/item/clothing/shoes/galoshes/dry/step_action() var/turf/open/t_loc = get_turf(src) SEND_SIGNAL(t_loc, COMSIG_TURF_MAKE_DRY, TURF_WET_WATER, TRUE, INFINITY) @@ -69,10 +79,8 @@ /obj/item/clothing/shoes/clown_shoes desc = "The prankster's standard-issue clowning shoes. Damn, they're huge!" name = "clown shoes" - icon_state = "clown" - item_state = "clown_shoes" + icon_state = "clown_shoes" slowdown = SHOES_SLOWDOWN+1 - item_color = "clown" pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes/clown /obj/item/clothing/shoes/clown_shoes/Initialize() @@ -98,7 +106,6 @@ name = "jackboots" desc = "Nanotrasen-issue Security combat boots for combat scenarios or combat situations. All combat, all the time." icon_state = "jackboots" - item_state = "jackboots" lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi' item_color = "hosred" @@ -115,7 +122,6 @@ name = "winter boots" desc = "Boots lined with 'synthetic' animal fur." icon_state = "winterboots" - item_state = "winterboots" permeability_coefficient = 0.15 cold_protection = FEET|LEGS min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT @@ -127,7 +133,6 @@ name = "work boots" desc = "Nanotrasen-issue Engineering lace-up work boots for the especially blue-collar." icon_state = "workboots" - item_state = "jackboots" lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi' permeability_coefficient = 0.15 @@ -145,7 +150,6 @@ name = "\improper Nar'Sien invoker boots" desc = "A pair of boots worn by the followers of Nar'Sie." icon_state = "cult" - item_state = "cult" item_color = "cult" cold_protection = FEET min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT @@ -178,7 +182,6 @@ name = "roman sandals" desc = "Sandals with buckled leather straps on it." icon_state = "roman" - item_state = "roman" strip_delay = 100 equip_delay_other = 100 permeability_coefficient = 0.9 @@ -187,14 +190,12 @@ name = "griffon boots" desc = "A pair of costume boots fashioned after bird talons." icon_state = "griffinboots" - item_state = "griffinboots" pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes /obj/item/clothing/shoes/bhop name = "jump boots" desc = "A specialized pair of combat boots with a built-in propulsion system for rapid foward movement." icon_state = "jetboots" - item_state = "jetboots" item_color = "hosred" resistance_flags = FIRE_PROOF pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes @@ -252,7 +253,6 @@ name = "Wheely-Heels" desc = "Uses patented retractable wheel technology. Never sacrifice speed for style - not that this provides much of either." //Thanks Fel icon_state = "wheelys" - item_state = "wheelys" actions_types = list(/datum/action/item_action/wheelys) var/wheelToggle = FALSE //False means wheels are not popped out var/obj/vehicle/ridden/scooter/wheelys/W @@ -291,7 +291,6 @@ name = "Kindle Kicks" desc = "They'll sure kindle something in you, and it's not childhood nostalgia..." icon_state = "kindleKicks" - item_state = "kindleKicks" actions_types = list(/datum/action/item_action/kindleKicks) var/lightCycle = 0 var/active = FALSE diff --git a/code/modules/clothing/shoes/vg_shoes.dm b/code/modules/clothing/shoes/vg_shoes.dm index 931c25b823..627a061181 100644 --- a/code/modules/clothing/shoes/vg_shoes.dm +++ b/code/modules/clothing/shoes/vg_shoes.dm @@ -5,7 +5,6 @@ icon_state = "leather" item_color = "leather" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/shoes/magboots/deathsquad @@ -14,7 +13,6 @@ icon_state = "DS-magboots0" magboot_state = "DS-magboots" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF mutantrace_variation = NO_MUTANTRACE_VARIATION @@ -23,7 +21,6 @@ name = "atmospherics magboots" icon_state = "atmosmagboots0" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' magboot_state = "atmosmagboots" resistance_flags = FIRE_PROOF mutantrace_variation = NO_MUTANTRACE_VARIATION @@ -33,7 +30,6 @@ desc = "Simon's Shoes." icon_state = "simonshoes" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/shoes/kneesocks @@ -41,7 +37,6 @@ desc = "A pair of girly knee-high socks." icon_state = "kneesock" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/shoes/jestershoes @@ -49,7 +44,6 @@ desc = "As worn by the clowns of old." icon_state = "jestershoes" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/shoes/aviatorboots @@ -57,7 +51,6 @@ desc = "Boots suitable for just about any occasion." icon_state = "aviator_boots" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/shoes/libertyshoes @@ -65,7 +58,6 @@ desc = "Freedom isn't free, neither were these shoes." icon_state = "libertyshoes" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/shoes/megaboots @@ -73,7 +65,6 @@ desc = "Large armored boots, very weak to large spikes." icon_state = "megaboots" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/shoes/protoboots @@ -81,7 +72,6 @@ desc = "Functionally identical to the DRN-001 model's boots, but in red." icon_state = "protoboots" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/shoes/megaxboots @@ -89,7 +79,6 @@ desc = "Regardless of how much stronger these boots are than the DRN-001 model's, they're still extremely easy to pierce with a large spike." icon_state = "megaxboots" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/shoes/joeboots @@ -97,7 +86,6 @@ desc = "Nearly identical to the Prototype's boots, except in black." icon_state = "joeboots" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/shoes/doomguy @@ -105,7 +93,6 @@ desc = "If you look closely, you might see skull fragments still buried in these boots." icon_state = "doom" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/shoes/rottenshoes @@ -113,7 +100,6 @@ desc = "These shoes seem perfect for sneaking around." icon_state = "rottenshoes" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/shoes/sandal/slippers @@ -121,7 +107,6 @@ icon_state = "slippers" desc = "For the wizard that puts comfort first. Who's going to laugh?" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/shoes/slippers_worn @@ -129,7 +114,6 @@ desc = "Fluffy..." icon_state = "slippers_worn" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/shoes/jackboots/neorussian @@ -137,5 +121,4 @@ desc = "Tovarish, no one will realize you stepped on a pile of shit if your pair already looks like shit." icon_state = "nr_boots" icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi' - alternate_worn_icon = 'modular_citadel/icons/mob/citadel/head.dmi' mutantrace_variation = NO_MUTANTRACE_VARIATION \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/_spacesuits.dm b/code/modules/clothing/spacesuits/_spacesuits.dm index 662a91c80c..57866b5131 100644 --- a/code/modules/clothing/spacesuits/_spacesuits.dm +++ b/code/modules/clothing/spacesuits/_spacesuits.dm @@ -22,6 +22,7 @@ resistance_flags = NONE dog_fashion = null mutantrace_variation = MUTANTRACE_VARIATION + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE /obj/item/clothing/suit/space name = "space suit" @@ -44,4 +45,5 @@ strip_delay = 80 equip_delay_other = 80 resistance_flags = NONE + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE //rated for cosmic radation :honk: tauric = TRUE //Citadel Add for tauric hardsuits diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm index d1ee88d5a8..0b07aafa5d 100644 --- a/code/modules/clothing/spacesuits/plasmamen.dm +++ b/code/modules/clothing/spacesuits/plasmamen.dm @@ -12,7 +12,7 @@ var/next_extinguish = 0 var/extinguish_cooldown = 100 var/extinguishes_left = 10 - + mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/suit/space/eva/plasmaman/examine(mob/user) ..() @@ -45,12 +45,18 @@ resistance_flags = FIRE_PROOF var/brightness_on = 4 //luminosity when the light is on var/on = FALSE + var/light_overlay = "envirohelm-light" actions_types = list(/datum/action/item_action/toggle_helmet_light) + mutantrace_variation = NO_MUTANTRACE_VARIATION /obj/item/clothing/head/helmet/space/plasmaman/attack_self(mob/user) + if(!light_overlay) + return on = !on - icon_state = "[initial(icon_state)][on ? "-light":""]" - item_state = icon_state + if(!on) + cut_overlay(light_overlay) + else + add_overlay(light_overlay) user.update_inv_head() //So the mob overlay updates if(on) @@ -61,3 +67,166 @@ for(var/X in actions) var/datum/action/A=X A.UpdateButtonIcon() + +/obj/item/clothing/head/helmet/space/plasmaman/worn_overlays(isinhands, icon_file) + . = ..() + if(!isinhands && on) + . += mutable_appearance(icon_file, light_overlay) + +/obj/item/clothing/head/helmet/space/plasmaman/security + name = "security plasma envirosuit helmet" + desc = "A plasmaman containment helmet designed for security officers, protecting them from being flashed and burning alive, along-side other undesirables." + icon_state = "security_envirohelm" + item_state = "security_envirohelm" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 0, "fire" = 100, "acid" = 75) + +/obj/item/clothing/head/helmet/space/plasmaman/security/warden + name = "warden's plasma envirosuit helmet" + desc = "A plasmaman containment helmet designed for the warden, a pair of white stripes being added to differeciate them from other members of security." + icon_state = "warden_envirohelm" + item_state = "warden_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/security/hos + name = "head of security's plasma envirosuit helmet" + desc = "A plasmaman containment helmet designed for the head of security, manacing black with red stripes, to differenciate them from other members of security." + icon_state = "hos_envirohelm" + item_state = "hos_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/medical + name = "medical's plasma envirosuit helmet" + desc = "An envriohelmet designed for plasmaman medical doctors, having two stripes down it's length to denote as much." + icon_state = "doctor_envirohelm" + item_state = "doctor_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/cmo + name = "chief medical officer's plasma envirosuit helmet" + desc = "A blue and white envriohelmet designed for the chief medical officer." + icon_state = "cmo_envirohelm" + item_state = "cmo_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/genetics + name = "geneticist's plasma envirosuit helmet" + desc = "A plasmaman envirohelmet designed for geneticists." + icon_state = "geneticist_envirohelm" + item_state = "geneticist_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/viro + name = "virology plasma envirosuit helmet" + desc = "The helmet worn by the safest people on the station, those who are completely immune to the monstrosities they create." + icon_state = "virologist_envirohelm" + item_state = "virologist_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/chemist + name = "chemistry plasma envirosuit helmet" + desc = "A plasmaman envirosuit designed for chemists, two orange stripes going down it's face." + icon_state = "chemist_envirohelm" + item_state = "chemist_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/science + name = "science plasma envirosuit helmet" + desc = "A plasmaman envirohelmet designed for scientists." + icon_state = "scientist_envirohelm" + item_state = "scientist_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/rd + name = "research director's plasma envirosuit helmet" + desc = "A sturdier plasmaman envirohelmet designed for research directors." + icon_state = "rd_envirohelm" + item_state = "rd_envirohelm" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 10, "bio" = 100, "rad" = 0, "fire" = 100, "acid" = 75) + +/obj/item/clothing/head/helmet/space/plasmaman/robotics + name = "robotics plasma envirosuit helmet" + desc = "A plasmaman envirohelmet designed for roboticists." + icon_state = "roboticist_envirohelm" + item_state = "roboticist_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/engineering + name = "engineering plasma envirosuit helmet" + desc = "A space-worthy helmet specially designed for engineer plasmamen, the usual purple stripes being replaced by engineering's orange." + icon_state = "engineer_envirohelm" + item_state = "engineer_envirohelm" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 10, "fire" = 100, "acid" = 75) + +/obj/item/clothing/head/helmet/space/plasmaman/engineering/ce + name = "chief engineer's plasma envirosuit helmet" + desc = "A space-worthy helmet specially designed for the chief engineer, white with gold stripes designed for high visibility." + icon_state = "ce_envirohelm" + item_state = "ce_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/atmospherics + name = "atmospherics plasma envirosuit helmet" + desc = "A space-worthy helmet specially designed for atmos technician plasmamen, the usual purple stripes being replaced by engineering's blue." + icon_state = "atmos_envirohelm" + item_state = "atmos_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/cargo + name = "cargo plasma envirosuit helmet" + desc = "An plasmaman envirohelmet designed for cargo techs and quartermasters." + icon_state = "cargo_envirohelm" + item_state = "cargo_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/mining + name = "mining plasma envirosuit helmet" + desc = "A khaki helmet given to plasmamen miners operating on lavaland." + icon_state = "explorer_envirohelm" + item_state = "explorer_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/chaplain + name = "chaplain's plasma envirosuit helmet" + desc = "An envirohelmet specially designed for only the most pious of plasmamen." + icon_state = "chap_envirohelm" + item_state = "chap_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/white + name = "white plasma envirosuit helmet" + desc = "A generic white envirohelm." + icon_state = "white_envirohelm" + item_state = "white_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/hop + name = "head of personell's plasma envirosuit helmet" + desc = "A finely tailored azure envirohelm designed for head of personell." + icon_state = "hop_envirohelm" + item_state = "hop_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/captain + name = "captain's plasma envirosuit helmet" + desc = "A blue and gold envirohelm designed for the station's captain, nonetheless. Made of superior materials to protect them from the station hazards and more." + icon_state = "captain_envirohelm" + item_state = "captain_envirohelm" + armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 10, "fire" = 100, "acid" = 85) + +/obj/item/clothing/head/helmet/space/plasmaman/curator + name = "curator's plasma envirosuit helmet" + desc = "A slight modification on a tradiational voidsuit helmet, this helmet was Nano-Trasen's first solution to the *logistical problems* that come with employing plasmamen. Despite their limitations, these helmets still see use by historian and old-styled plasmamen alike." + icon_state = "prototype_envirohelm" + item_state = "prototype_envirohelm" + light_overlay = null + actions_types = list() + +/obj/item/clothing/head/helmet/space/plasmaman/botany + name = "botany plasma envirosuit helmet" + desc = "A green and blue envirohelmet designating it's wearer as a botanist. While not specially designed for it, it would protect against minor planet-related injuries." + icon_state = "botany_envirohelm" + item_state = "botany_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/janitor + name = "janitor's plasma envirosuit helmet" + desc = "A grey helmet bearing a pair of purple stripes, designating the wearer as a janitor." + icon_state = "janitor_envirohelm" + item_state = "janitor_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/mime + name = "mime envirosuit helmet" + desc = "The make-up is painted on, it's a miracle it doesn't chip. It's not very colourful." + icon_state = "mime_envirohelm" + item_state = "mime_envirohelm" + light_overlay = "mime_envirohelm-light" + +/obj/item/clothing/head/helmet/space/plasmaman/clown + name = "clown envirosuit helmet" + desc = "The make-up is painted on, it's a miracle it doesn't chip. 'HONK!'" + icon_state = "clown_envirohelm" + item_state = "clown_envirohelm" + light_overlay = "clown_envirohelm-light" diff --git a/code/modules/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm index cf411ef367..3008043156 100644 --- a/code/modules/clothing/suits/_suits.dm +++ b/code/modules/clothing/suits/_suits.dm @@ -5,6 +5,7 @@ allowed = list(/obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) slot_flags = ITEM_SLOT_OCLOTHING + body_parts_covered = CHEST var/blood_overlay_type = "suit" var/togglename = null var/suittoggled = FALSE diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index 0840288eee..19050dc36f 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -1,6 +1,5 @@ /obj/item/clothing/suit/armor allowed = null - body_parts_covered = CHEST cold_protection = CHEST|GROIN min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT heat_protection = CHEST|GROIN diff --git a/code/modules/clothing/suits/cloaks.dm b/code/modules/clothing/suits/cloaks.dm index c3276213e1..b8287c7f4c 100644 --- a/code/modules/clothing/suits/cloaks.dm +++ b/code/modules/clothing/suits/cloaks.dm @@ -7,7 +7,7 @@ icon_state = "qmcloak" item_state = "qmcloak" w_class = WEIGHT_CLASS_SMALL - body_parts_covered = CHEST|GROIN|LEGS|ARMS + body_parts_covered = CHEST|LEGS|ARMS /obj/item/clothing/head/cloakhood name = "cloak hood" @@ -62,7 +62,7 @@ allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/pickaxe, /obj/item/twohanded/spear, /obj/item/twohanded/bonespear, /obj/item/organ/regenerative_core/legion, /obj/item/kitchen/knife/combat/bone, /obj/item/kitchen/knife/combat/survival) armor = list("melee" = 35, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) //a fair alternative to bone armor, requiring alternative materials and gaining a suit slot hoodtype = /obj/item/clothing/head/hooded/cloakhood/goliath - body_parts_covered = CHEST|GROIN|ARMS + body_parts_covered = CHEST|ARMS|LEGS /obj/item/clothing/head/hooded/cloakhood/goliath name = "goliath cloak hood" diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm index 3c3c8f0e9d..4318839845 100644 --- a/code/modules/clothing/suits/jobs.dm +++ b/code/modules/clothing/suits/jobs.dm @@ -23,30 +23,48 @@ allowed = list(/obj/item/disk, /obj/item/stamp, /obj/item/reagent_containers/food/drinks/flask, /obj/item/melee, /obj/item/storage/lockbox/medal, /obj/item/assembly/flash/handheld, /obj/item/storage/box/matches, /obj/item/lighter, /obj/item/clothing/mask/cigarette, /obj/item/storage/fancy/cigarettes, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) //Chaplain -/obj/item/clothing/suit/nun +/obj/item/clothing/suit/chaplain + name = "chaplain suit" + desc = "A piece of clothing adorned by the gods of Coding. Should never exist in this mortal realm." + allowed = list(/obj/item/storage/book/bible, HOLY_WEAPONS, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) + +/obj/item/clothing/suit/chaplain/nun name = "nun robe" desc = "Maximum piety in this star system." icon_state = "nun" item_state = "nun" body_parts_covered = CHEST|GROIN|LEGS|ARMS|HANDS flags_inv = HIDESHOES|HIDEJUMPSUIT - allowed = list(/obj/item/storage/book/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) -/obj/item/clothing/suit/studentuni +/obj/item/clothing/suit/chaplain/studentuni name = "student robe" desc = "The uniform of a bygone institute of learning." icon_state = "studentuni" item_state = "studentuni" body_parts_covered = ARMS|CHEST - allowed = list(/obj/item/storage/book/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) -/obj/item/clothing/suit/witchhunter +/obj/item/clothing/suit/chaplain/witchhunter name = "witchunter garb" desc = "This worn outfit saw much use back in the day." icon_state = "witchhunter" item_state = "witchhunter" body_parts_covered = CHEST|GROIN|LEGS|ARMS - allowed = list(/obj/item/storage/book/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) + +/obj/item/clothing/suit/chaplain/pharaoh + name = "pharaoh tunic" + desc = "Lavish space tomb not included." + icon_state = "pharaoh" + icon_state = "pharaoh" + body_parts_covered = CHEST|GROIN + +/obj/item/clothing/suit/chaplain/holidaypriest + name = "holiday priest" + desc = "This is a nice holiday, my son." + icon_state = "holidaypriest" + item_state = "w_suit" + body_parts_covered = CHEST|GROIN|LEGS|ARMS + flags_inv = HIDEJUMPSUIT + //Chef /obj/item/clothing/suit/toggle/chef @@ -135,6 +153,7 @@ icon = 'icons/obj/clothing/belts.dmi' icon_state = "suspenders" blood_overlay_type = "armor" //it's the less thing that I can put here + body_parts_covered = NONE //Security /obj/item/clothing/suit/security/officer @@ -197,3 +216,4 @@ item_state = "techpriesthood" body_parts_covered = HEAD flags_inv = HIDEHAIR|HIDEEARS + mutantrace_variation = MUTANTRACE_VARIATION diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 8a3dbbf274..942fdaed73 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -67,6 +67,7 @@ item_state = "death" flags_1 = CONDUCT_1 fire_resist = T0C+5200 + body_parts_covered = CHEST|GROIN|LEGS|ARMS|HANDS|FEET flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT @@ -75,6 +76,7 @@ desc = "this pretty much looks ridiculous" //Needs no fixing icon_state = "justice" item_state = "justice" + body_parts_covered = CHEST|GROIN|LEGS|ARMS|HANDS|FEET flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT @@ -100,14 +102,14 @@ desc = "A fancy purple apron for a stylish person." icon_state = "purplebartenderapron" item_state = "purplebartenderapron" - body_parts_covered = CHEST|GROIN|LEGS + body_parts_covered = CHEST /obj/item/clothing/suit/syndicatefake name = "black and red space suit replica" icon_state = "syndicate-black-red" item_state = "syndicate-black-red" desc = "A plastic replica of the Syndicate space suit. You'll look just like a real murderous Syndicate agent in this! This is a toy, it is not made for use in space!" - w_class = WEIGHT_CLASS_NORMAL + body_parts_covered = CHEST|ARMS|GROIN|LEGS|FEET|HANDS allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/toy) flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT resistance_flags = NONE @@ -154,7 +156,7 @@ icon_state = "owl_wings" item_state = "owl_wings" togglename = "wings" - body_parts_covered = ARMS|CHEST + body_parts_covered = ARMS actions_types = list(/datum/action/item_action/toggle_wings) /obj/item/clothing/suit/toggle/owlwings/Initialize() @@ -167,16 +169,6 @@ icon_state = "griffin_wings" item_state = "griffin_wings" - -/obj/item/clothing/suit/holidaypriest - name = "holiday priest" - desc = "This is a nice holiday, my son." - icon_state = "holidaypriest" - item_state = "w_suit" - body_parts_covered = CHEST|GROIN|LEGS|ARMS - flags_inv = HIDEJUMPSUIT - allowed = list(/obj/item/storage/book/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) - /obj/item/clothing/suit/cardborg name = "cardborg suit" desc = "An ordinary cardboard box with holes cut in the sides." @@ -219,6 +211,7 @@ desc = "Your classic, non-racist poncho." icon_state = "classicponcho" item_state = "classicponcho" + body_parts_covered = CHEST|GROIN /obj/item/clothing/suit/poncho/green name = "green poncho" @@ -255,7 +248,7 @@ desc = "A costume made from 'synthetic' carp scales, it smells." icon_state = "carp_casual" item_state = "labcoat" - body_parts_covered = CHEST|GROIN|ARMS + body_parts_covered = CHEST|GROIN|ARMS|LEGS|FEET cold_protection = CHEST|GROIN|ARMS min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT //Space carp like space, so you should too allowed = list(/obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/gun/ballistic/automatic/speargun) @@ -275,9 +268,7 @@ desc = "A costume that looks like someone made a human-like corgi, it won't guarantee belly rubs." icon_state = "ian" item_state = "labcoat" - body_parts_covered = CHEST|GROIN|ARMS - //cold_protection = CHEST|GROIN|ARMS - //min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT + body_parts_covered = CHEST|GROIN|ARMS|LEGS|FEET allowed = list() hoodtype = /obj/item/clothing/head/hooded/ian_hood dog_fashion = /datum/dog_fashion/back @@ -287,8 +278,6 @@ desc = "A hood that looks just like a corgi's head, it won't guarantee dog biscuits." icon_state = "ian" body_parts_covered = HEAD - //cold_protection = HEAD - //min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT flags_inv = HIDEHAIR|HIDEEARS /obj/item/clothing/suit/hooded/bee_costume // It's Hip! @@ -314,7 +303,7 @@ desc = "A horribly bloated suit made from human skins." icon_state = "lingspacesuit" item_state = "labcoat" - body_parts_covered = CHEST|GROIN|ARMS + body_parts_covered = CHEST|GROIN|ARMS|LEGS|FEET|HANDS allowed = list() actions_types = list(/datum/action/item_action/toggle_human_head) hoodtype = /obj/item/clothing/head/hooded/human_head @@ -355,18 +344,21 @@ desc = "A worn out, curiously comfortable t-shirt with a picture of Ian. You wouldn't go so far as to say it feels like being hugged when you wear it, but it's pretty close. Good for sleeping in." icon_state = "ianshirt" item_state = "ianshirt" + body_parts_covered = CHEST|GROIN /obj/item/clothing/suit/nerdshirt name = "gamer shirt" desc = "A baggy shirt with vintage game character Phanic the Weasel. Why would anyone wear this?" icon_state = "nerdshirt" item_state = "nerdshirt" + body_parts_covered = CHEST|GROIN /obj/item/clothing/suit/vapeshirt //wearing this is asking to get beat. name = "Vape Naysh shirt" desc = "A cheap white T-shirt with a big tacky \"VN\" on the front, Why would you wear this unironically?" icon_state = "vapeshirt" item_state = "vapeshirt" + body_parts_covered = CHEST|GROIN /obj/item/clothing/suit/jacket name = "bomber jacket" @@ -374,7 +366,7 @@ icon_state = "bomberjacket" item_state = "brownjsuit" allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/radio) - body_parts_covered = CHEST|GROIN|ARMS + body_parts_covered = CHEST|ARMS cold_protection = CHEST|GROIN|ARMS min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT @@ -399,6 +391,7 @@ desc = "A thick jacket with a rubbery, water-resistant shell." icon_state = "pufferjacket" item_state = "hostrench" + body_parts_covered = CHEST|GROIN|ARMS armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 0) /obj/item/clothing/suit/jacket/puffer/vest @@ -446,18 +439,21 @@ desc = "Looks like this belongs in a very old movie set." icon_state = "draculacoat" item_state = "draculacoat" + body_parts_covered = CHEST|ARMS /obj/item/clothing/suit/drfreeze_coat name = "doctor freeze's labcoat" desc = "A labcoat imbued with the power of features and freezes." icon_state = "drfreeze_coat" item_state = "drfreeze_coat" + body_parts_covered = CHEST|GROIN|ARMS /obj/item/clothing/suit/gothcoat name = "gothic coat" desc = "Perfect for those who want stalk in a corner of a bar." icon_state = "gothcoat" item_state = "gothcoat" + body_parts_covered = CHEST|ARMS|HAND_LEFT //peculiar /obj/item/clothing/suit/xenos name = "xenos suit" @@ -468,13 +464,6 @@ flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT allowed = list(/obj/item/clothing/mask/facehugger/toy) -/obj/item/clothing/suit/pharaoh - name = "pharaoh tunic" - desc = "Lavish space tomb not included." - icon_state = "pharaoh" - icon_state = "pharaoh" - body_parts_covered = CHEST|GROIN - // WINTER COATS @@ -603,6 +592,7 @@ name = "spooky ghost" desc = "This is obviously just a bedsheet, but maybe try it on?" icon_state = "bedsheet" + body_parts_covered = CHEST|GROIN|LEGS|ARMS|HANDS|HEAD user_vars_to_edit = list("name" = "Spooky Ghost", "real_name" = "Spooky Ghost" , "incorporeal_move" = INCORPOREAL_MOVE_BASIC, "appearance_flags" = KEEP_TOGETHER|TILE_BOUND, "alpha" = 150) alternate_worn_layer = ABOVE_BODY_FRONT_LAYER //so the bedsheet goes over everything but fire @@ -611,6 +601,7 @@ desc = "A big and clanky suit made of bronze that offers no protection and looks very unfashionable. Nice." icon = 'icons/obj/clothing/clockwork_garb.dmi' icon_state = "clockwork_cuirass_old" + body_parts_covered = CHEST|GROIN|LEGS armor = list("melee" = 5, "bullet" = 0, "laser" = -5, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 20) /obj/item/clothing/suit/ghost_sheet @@ -623,4 +614,5 @@ throw_range = 2 w_class = WEIGHT_CLASS_TINY flags_inv = HIDEGLOVES|HIDEEARS|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR + body_parts_covered = CHEST|GROIN|LEGS|ARMS|HANDS|HEAD alternate_worn_layer = UNDER_HEAD_LAYER diff --git a/code/modules/clothing/suits/utility.dm b/code/modules/clothing/suits/utility.dm index d003a20167..fbdca6ddbc 100644 --- a/code/modules/clothing/suits/utility.dm +++ b/code/modules/clothing/suits/utility.dm @@ -130,7 +130,7 @@ equip_delay_other = 60 flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH resistance_flags = NONE - rad_flags = RAD_PROTECT_CONTENTS + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE mutantrace_variation = MUTANTRACE_VARIATION /obj/item/clothing/suit/radiation @@ -150,5 +150,5 @@ equip_delay_other = 60 flags_inv = HIDEJUMPSUIT|HIDETAUR resistance_flags = NONE - rad_flags = RAD_PROTECT_CONTENTS + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE tauric = TRUE //Citadel Add for tauric hardsuits diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm index 6535e40d0d..878030bc5d 100644 --- a/code/modules/clothing/under/accessories.dm +++ b/code/modules/clothing/under/accessories.dm @@ -1,4 +1,4 @@ -/obj/item/clothing/accessory //Ties moved to neck slot items, but as there are still things like medals and armbands, this accessory system is being kept as-is +/obj/item/clothing/accessory //Ties moved to neck slot items, but as there are still things like medals, pokadots, and armbands, this accessory system is being kept as-is name = "Accessory" desc = "Something has gone wrong!" icon = 'icons/obj/clothing/accessories.dmi' @@ -368,7 +368,7 @@ /obj/item/clothing/accessory/kevlar name = "kevlar sheets" - desc = "Long thin sheets of kevlar to help resist bullets and some physical attacks.." + desc = "Long thin sheets of kevlar to help resist bullets and some physical attacks." icon_state = "padding" item_color = "nothing" armor = list("melee" = 10, "bullet" = 20, "laser" = 0, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 25) @@ -379,3 +379,35 @@ icon_state = "plastics" item_color = "nothing" armor = list("melee" = 0, "bullet" = 0, "laser" = 20, "energy" = 10, "bomb" = 0, "bio" = 30, "rad" = 0, "fire" = 0, "acid" = -40) + +///////////////////// +//Pokadots On Pants// +///////////////////// + +/obj/item/clothing/accessory/attrocious_pokadots + name = "atrocious pokadots" + desc = "They look like something out of a thrift store. Attaches to clothing not to be worn by itself." + icon_state = "attrocious_pokadots" + item_color = "attrocious_pokadots" + attack_verb = list("horrifed", "eye bleeded") + +/obj/item/clothing/accessory/black_white_pokadots + name = "checkered pokadots" + desc = "You can play a game of chess on these! Attaches to clothing not to be worn by itself." + icon_state = "black_white_pokadots" + item_color = "black_white_pokadots" + attack_verb = list("check", "mate") + +/obj/item/clothing/accessory/nt_pokadots + name = "blue and white pokadots" + desc = "To show your pride in your workplace, in the most annoying possable way. Attaches to clothing not to be worn by itself." + icon_state = "nt_pokadots" + item_color = "nt_pokadots" + attack_verb = list("eye bleeded", "annoyed") + +/obj/item/clothing/accessory/syndi_pokadots + name = "black and red pokadots" + desc = "King me. Attaches to clothing not to be worn by itself." //checkers! + icon_state = "syndi_pokadots" + item_color = "syndi_pokadots" + attack_verb = list("jumped", "taken") \ No newline at end of file diff --git a/code/modules/clothing/under/jobs/Plasmaman/civilian_service.dm b/code/modules/clothing/under/jobs/Plasmaman/civilian_service.dm new file mode 100644 index 0000000000..74181b05eb --- /dev/null +++ b/code/modules/clothing/under/jobs/Plasmaman/civilian_service.dm @@ -0,0 +1,100 @@ +/obj/item/clothing/under/plasmaman/cargo + name = "cargo plasma envirosuit" + desc = "A joint envirosuit used by plasmamen quartermasters and cargo techs alike, due to the logistical problems of differenciating the two with the length of their pant legs." + icon_state = "cargo_envirosuit" + item_state = "cargo_envirosuit" + item_color = "cargo_envirosuit" + +/obj/item/clothing/under/plasmaman/mining + name = "mining plasma envirosuit" + desc = "An air-tight khaki suit designed for operations on lavaland by plasmamen." + icon_state = "explorer_envirosuit" + item_state = "explorer_envirosuit" + item_color = "explorer_envirosuit" + + +/obj/item/clothing/under/plasmaman/chef + name = "chef's plasma envirosuit" + desc = "A white plasmaman envirosuit designed for cullinary practices. One might question why a member of a species that doesn't need to eat would become a chef." + icon_state = "chef_envirosuit" + item_state = "chef_envirosuit" + item_color = "chef_envirosuit" + +/obj/item/clothing/under/plasmaman/enviroslacks + name = "enviroslacks" + desc = "The pet project of a particularly posh plasmaman, this custom suit was quickly appropriated by Nano-Trasen for it's detectives, lawyers, and bar-tenders alike." + icon_state = "enviroslacks" + item_state = "enviroslacks" + item_color = "enviroslacks" + +/obj/item/clothing/under/plasmaman/chaplain + name = "chaplain's plasma envirosuit" + desc = "An envirosuit specially designed for only the most pious of plasmamen." + icon_state = "chap_envirosuit" + item_state = "chap_envirosuit" + item_color = "chap_envirosuit" + +/obj/item/clothing/under/plasmaman/curator + name = "curator's plasma envirosuit" + desc = "Made out of a modified voidsuit, this suit was Nano-Trasen's first solution to the *logistical problems* that come with employing plasmamen. Due to the modifications, the suit is no longer space-worthy. Despite their limitations, these suits are still in used by historian and old-styled plasmamen alike." + icon_state = "prototype_envirosuit" + item_state = "prototype_envirosuit" + item_color = "prototype_envirosuit" + +/obj/item/clothing/under/plasmaman/janitor + name = "janitor's plasma envirosuit" + desc = "A grey and purple envirosuit designated for plasmamen janitors." + icon_state = "janitor_envirosuit" + item_state = "janitor_envirosuit" + item_color = "janitor_envirosuit" + +/obj/item/clothing/under/plasmaman/botany + name = "botany envirosuit" + desc = "A green and blue envirosuit designed to protect plasmamen from minor plant-related injuries." + icon_state = "botany_envirosuit" + item_state = "botany_envirosuit" + item_color = "botany_envirosuit" + +/obj/item/clothing/under/plasmaman/hop + name = "head of personell's envirosuit" + desc = "A prestigious azure envirosuit designed for head of personell." + icon_state = "hop_envirosuit" + item_state = "hop_envirosuit" + item_color = "hop_envirosuit" + +/obj/item/clothing/under/plasmaman/captain + name = "captain's envirosuit" + desc = "An expensive piece of plasmaman envirosuit fashion. guaranteed to keep you cool while the station goes down in fierceful fires." + icon_state = "captain_envirosuit" + item_state = "captain_envirosuit" + item_color = "captain_envirosuit" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 0, "fire" = 95, "acid" = 95) + +/obj/item/clothing/under/plasmaman/mime + name = "mime envirosuit" + desc = "It's not very colourful." + icon_state = "mime_envirosuit" + item_state = "mime_envirosuit" + item_color = "mime_envirosuit" + +/obj/item/clothing/under/plasmaman/clown + name = "clown envirosuit" + desc = "'HONK!'" + icon_state = "clown_envirosuit" + item_state = "clown_envirosuit" + item_color = "clown_envirosuit" + +/obj/item/clothing/under/plasmaman/clown/Extinguish(mob/living/carbon/human/H) + if(!istype(H)) + return + + if(H.on_fire) + if(extinguishes_left) + if(next_extinguish > world.time) + return + next_extinguish = world.time + extinguish_cooldown + extinguishes_left-- + H.visible_message("[H]'s suit spews out a tonne of space lube!","Your suit spews out a tonne of space lube!") + H.ExtinguishMob() + new /obj/effect/particle_effect/foam(loc) //Truely terrifying. + return FALSE \ No newline at end of file diff --git a/code/modules/clothing/under/jobs/Plasmaman/engineering.dm b/code/modules/clothing/under/jobs/Plasmaman/engineering.dm new file mode 100644 index 0000000000..1f381df4cf --- /dev/null +++ b/code/modules/clothing/under/jobs/Plasmaman/engineering.dm @@ -0,0 +1,21 @@ +/obj/item/clothing/under/plasmaman/engineering + name = "engineering plasma envirosuit" + desc = "An air-tight suit designed to be used by plasmamen exployed as engineers, the usual purple stripes being replaced by engineer's orange. It protects the user from fire and acid damage." + icon_state = "engineer_envirosuit" + item_state = "engineer_envirosuit" + item_color = "engineer_envirosuit" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 10, "fire" = 95, "acid" = 95) + +/obj/item/clothing/under/plasmaman/engineering/ce + name = "chief engineer's plasma envirosuit" + desc = "An advanced air-tight suit designed to be used by plasmamen chief engineers, complete with ornamental widgets and gizmo. It protects the user from fire and acid damage." + icon_state = "ce_envirosuit" + item_state = "ce_envirosuit" + item_color = "ce_envirosuit" + +/obj/item/clothing/under/plasmaman/atmospherics + name = "atmospherics plasma envirosuit" + desc = "An air-tight suit designed to be used by plasmamen exployed as atmos technicians, the usual purple stripes being replaced by atmos's blue." + icon_state = "atmos_envirosuit" + item_state = "atmos_envirosuit" + item_color = "atmos_envirosuit" diff --git a/code/modules/clothing/under/jobs/Plasmaman/medsci.dm b/code/modules/clothing/under/jobs/Plasmaman/medsci.dm new file mode 100644 index 0000000000..6106d785a9 --- /dev/null +++ b/code/modules/clothing/under/jobs/Plasmaman/medsci.dm @@ -0,0 +1,56 @@ +/obj/item/clothing/under/plasmaman/medical + name = "medical plasma envirosuit" + desc = "A suit designed for the station's more plasma-based doctors." + icon_state = "doctor_envirosuit" + item_state = "doctor_envirosuit" + item_color = "doctor_envirosuit" + +/obj/item/clothing/under/plasmaman/cmo + name = "chief medical officer's plasma envirosuit" + desc = "A suit designed for the station's most plasma-based chief medical officer, but not for their cat." + icon_state = "cmo_envirosuit" + item_state = "cmo_envirosuit" + item_color = "cmo_envirosuit" + +/obj/item/clothing/under/plasmaman/science + name = "science plasma envirosuit" + desc = "A plasmaman envirosuit designed for scientists." + icon_state = "scientist_envirosuit" + item_state = "scientist_envirosuit" + item_color = "scientist_envirosuit" + +/obj/item/clothing/under/plasmaman/rd + name = "research direactor's plasma envirosuit" + desc = "A plasmaman envirosuit designed for the research director to aid them in their job of directing research into the right direction." + icon_state = "rd_envirosuit" + item_state = "rd_envirosuit" + item_color = "rd_envirosuit" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 10, "bio" = 100, "rad" = 0, "fire" = 95, "acid" = 95) + +/obj/item/clothing/under/plasmaman/robotics + name = "robotics plasma envirosuit" + desc = "A plasmaman envirosuit designed for roboticists." + icon_state = "roboticist_envirosuit" + item_state = "roboticist_envirosuit" + item_color = "roboticist_envirosuit" + +/obj/item/clothing/under/plasmaman/viro + name = "virology plasma envirosuit" + desc = "The suit worn by the safest people on the station, those who are completely immune to the monstrosities they create." + icon_state = "virologist_envirosuit" + item_state = "virologist_envirosuit" + item_color = "virologist_envirosuit" + +/obj/item/clothing/under/plasmaman/genetics + name = "genetics plasma envirosuit" + desc = "A plasmaman envirosuit designed for geneticists." + icon_state = "geneticist_envirosuit" + item_state = "geneticist_envirosuit" + item_color = "geneticist_envirosuit" + +/obj/item/clothing/under/plasmaman/chemist + name = "chemistry plasma envirosuit" + desc = "A plasmaman envirosuit designed for chemists." + icon_state = "chemist_envirosuit" + item_state = "chemist_envirosuit" + item_color = "chemist_envirosuit" diff --git a/code/modules/clothing/under/jobs/Plasmaman/security.dm b/code/modules/clothing/under/jobs/Plasmaman/security.dm new file mode 100644 index 0000000000..ff756e09fd --- /dev/null +++ b/code/modules/clothing/under/jobs/Plasmaman/security.dm @@ -0,0 +1,21 @@ +/obj/item/clothing/under/plasmaman/security + name = "security plasma envirosuit" + desc = "A plasmaman containment suit designed for security officers, offering a limited amount of extra protection." + icon_state = "security_envirosuit" + item_state = "security_envirosuit" + item_color = "security_envirosuit" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 0, "fire" = 95, "acid" = 95) + +/obj/item/clothing/under/plasmaman/security/warden + name = "warden plasma envirosuit" + desc = "A plasmaman containment suit designed for the warden, white stripes being added to differeciate them from other members of security." + icon_state = "warden_envirosuit" + item_state = "warden_envirosuit" + item_color = "warden_envirosuit" + +/obj/item/clothing/under/plasmaman/security/hos + name = "security plasma envirosuit" + desc = "A slick black and red plasmaman containment suit designed for the head of security, also called the LAW." + icon_state = "hos_envirosuit" + item_state = "hos_envirosuit" + item_color = "hos_envirosuit" diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index 616c00b6c1..52c046493a 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -589,6 +589,7 @@ item_state = "b_suit" item_color = "sailor" can_adjust = FALSE + /obj/item/clothing/under/plasmaman name = "plasma envirosuit" desc = "A special containment suit that allows plasma-based lifeforms to exist safely in an oxygenated environment, and automatically extinguishes them in a crisis. Despite being airtight, it's not spaceworthy." @@ -596,12 +597,14 @@ item_state = "plasmaman" item_color = "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 can_adjust = FALSE strip_delay = 80 var/next_extinguish = 0 var/extinguish_cooldown = 100 var/extinguishes_left = 5 + /obj/item/clothing/under/plasmaman/examine(mob/user) ..() to_chat(user, "There are [extinguishes_left] extinguisher charges left in this suit.") @@ -618,6 +621,7 @@ H.ExtinguishMob() new /obj/effect/particle_effect/water(get_turf(H)) return 0 + /obj/item/clothing/under/plasmaman/attackby(obj/item/E, mob/user, params) ..() if (istype(E, /obj/item/extinguisher_refill)) @@ -631,11 +635,13 @@ return return return + /obj/item/extinguisher_refill name = "envirosuit extinguisher cartridge" desc = "A cartridge loaded with a compressed extinguisher mix, used to refill the automatic extinguisher on plasma envirosuits." icon_state = "plasmarefill" icon = 'icons/obj/device.dmi' + /obj/item/clothing/under/rank/security/navyblue/russian name = "\improper Russian officer's uniform" desc = "The latest in fashionable russian outfits." diff --git a/code/modules/clothing/under/syndicate.dm b/code/modules/clothing/under/syndicate.dm index 9893edf248..4e3e1aa0cc 100644 --- a/code/modules/clothing/under/syndicate.dm +++ b/code/modules/clothing/under/syndicate.dm @@ -67,3 +67,14 @@ icon_state = "syndicate_combat" item_color = "syndicate_combat" can_adjust = FALSE + +/obj/item/clothing/under/syndicate/baseball + name = "major league, number unknown" + desc = "A major league outfit with the number faded number on the back. Seems rather robust for just a game" + icon_state = "syndicatebaseball" + item_state = "syndicatebaseball" + item_color = "syndicatebaseball" + has_sensor = NO_SENSORS + armor = list("melee" = 15, "bullet" = 5, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 40) + alt_covers_chest = TRUE + diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm index 2790a8cec7..7ae1c956c9 100644 --- a/code/modules/crafting/recipes.dm +++ b/code/modules/crafting/recipes.dm @@ -133,6 +133,18 @@ category = CAT_WEAPONRY subcategory = CAT_WEAPON +/datum/crafting_recipe/minigun + name = "Laser Minigun" + result = /obj/item/minigunpack2 + reqs = list(/obj/item/gun/energy/laser/carbine = 3, + /obj/item/stack/sheet/plasteel = 5, + /obj/item/stack/cable_coil = 30, + /obj/item/stock_parts/cell/bluespace = 2) + tools = list(TOOL_WIRECUTTER, TOOL_SCREWDRIVER, TOOL_WELDER) + time = 150 + category = CAT_WEAPONRY + subcategory = CAT_WEAPON + /datum/crafting_recipe/ed209 name = "ED209" result = /mob/living/simple_animal/bot/ed209 @@ -359,6 +371,7 @@ reqs = list(/obj/item/restraints/handcuffs/cable = 1, /obj/item/shard = 1, /obj/item/stack/rods = 1) + parts = list(/obj/item/shard = 1) time = 40 category = CAT_WEAPONRY subcategory = CAT_WEAPON diff --git a/code/modules/food_and_drinks/food/condiment.dm b/code/modules/food_and_drinks/food/condiment.dm index 31e99535d1..1d0b626d98 100644 --- a/code/modules/food_and_drinks/food/condiment.dm +++ b/code/modules/food_and_drinks/food/condiment.dm @@ -16,6 +16,7 @@ //Possible_states has the reagent id as key and a list of, in order, the icon_state, the name and the desc as values. Used in the on_reagent_change(changetype) to change names, descs and sprites. var/list/possible_states = list( "ketchup" = list("ketchup", "ketchup bottle", "You feel more American already."), + "mustard" = list("mustard", "mustard bottle", "A spice mixed with enzymes and water."), "capsaicin" = list("hotsauce", "hotsauce bottle", "You can almost TASTE the stomach ulcers now!"), "enzyme" = list("enzyme", "universal enzyme bottle", "Used in cooking various dishes"), "soysauce" = list("soysauce", "soy sauce bottle", "A salty soy-based flavoring"), @@ -282,6 +283,12 @@ originalname = "ketchup" list_reagents = list("ketchup" = 10) +//Mustard +/obj/item/reagent_containers/food/condiment/pack/mustard + name = "mustard pack" + originalname = "mustard" + list_reagents = list("mustard" = 10) + //Hot sauce /obj/item/reagent_containers/food/condiment/pack/hotsauce name = "hotsauce pack" diff --git a/code/modules/food_and_drinks/food/snacks/meat.dm b/code/modules/food_and_drinks/food/snacks/meat.dm index 1a82d1b406..ee169e9861 100644 --- a/code/modules/food_and_drinks/food/snacks/meat.dm +++ b/code/modules/food_and_drinks/food/snacks/meat.dm @@ -108,7 +108,7 @@ tastes = list("maggots" = 1, "the inside of a reactor" = 1) foodtype = MEAT | RAW | GROSS -/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/moth +/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/insect icon_state = "mothmeat" desc = "Unpleasantly powdery and dry. Kind of pretty, though." filling_color = "#BF896B" diff --git a/code/modules/food_and_drinks/food/snacks_cake.dm b/code/modules/food_and_drinks/food/snacks_cake.dm index d66cfdd667..98f80331a7 100644 --- a/code/modules/food_and_drinks/food/snacks_cake.dm +++ b/code/modules/food_and_drinks/food/snacks_cake.dm @@ -335,7 +335,7 @@ obj/item/reagent_containers/food/snacks/store/cake/pound_cake icon_state = "vanillacake" slice_path = /obj/item/reagent_containers/food/snacks/cakeslice/vanilla_slice bonus_reagents = list("sugar" = 15, "vanilla" = 15) - tastes = list("caje" = 1, "sugar" = 1, "vanilla" = 10) + tastes = list("cake" = 1, "sugar" = 1, "vanilla" = 10) foodtype = GRAIN | SUGAR | DAIRY /obj/item/reagent_containers/food/snacks/cakeslice/vanilla_slice @@ -361,4 +361,21 @@ obj/item/reagent_containers/food/snacks/store/cake/pound_cake icon_state = "clowncake_slice" filling_color = "#00FFFF" tastes = list("cake" = 1, "sugar" = 1, "joy" = 10) - foodtype = GRAIN | SUGAR | DAIRY \ No newline at end of file + foodtype = GRAIN | SUGAR | DAIRY + +/obj/item/reagent_containers/food/snacks/store/cake/peach_cake + name = "peach cake" + desc = "A peach filled cake." + icon_state = "peachcake" + slice_path = /obj/item/reagent_containers/food/snacks/cakeslice/peach_slice + bonus_reagents = list("sugar" = 5, "peachjuice" = 15) + tastes = list("cake" = 1, "sugar" = 1, "peachjuice" = 10) + foodtype = GRAIN | SUGAR | DAIRY + +/obj/item/reagent_containers/food/snacks/cakeslice/peach_slice + name = "peach cake slice" + desc = "A slice of peach cake." + icon_state = "peach_slice" + filling_color = "#00FFFF" + tastes = list("cake" = 1, "sugar" = 1, "peachjuice" = 10) + foodtype = GRAIN | SUGAR | DAIRY diff --git a/code/modules/food_and_drinks/food/snacks_frozen.dm b/code/modules/food_and_drinks/food/snacks_frozen.dm index c4f8ccf76b..3988909b30 100644 --- a/code/modules/food_and_drinks/food/snacks_frozen.dm +++ b/code/modules/food_and_drinks/food/snacks_frozen.dm @@ -28,6 +28,50 @@ tastes = list("ice cream" = 1, "banana" = 1, "a bad joke" = 1) foodtype = FRUIT | DAIRY | SUGAR +/obj/item/reagent_containers/food/snacks/banana_split + name = "banana split" + trash = /obj/item/reagent_containers/food/drinks/drinkingglass + desc = "A long glass dish filled with ice-cream, chocolate and a banana down the middle. A timeless classic by any standards." + icon_state = "banana_split" + bonus_reagents = list("nutriment" = 5, "vitamin" = 3) + list_reagents = list("nutriment" = 3, "banana" = 10, "vitamin" = 2) + filling_color = "#FFFACD" + tastes = list("ice cream" = 1, "banana" = 1, "charries" = 1) + foodtype = FRUIT | DAIRY | SUGAR + +/obj/item/reagent_containers/food/snacks/cola_float + name = "Root Beer Float" + trash = /obj/item/reagent_containers/food/drinks/drinkingglass + desc = "A glass filled with cream, soda and ice-cream with a cherry on top." + icon_state = "cola_float" + bonus_reagents = list("nutriment" = 3, "vitamin" = 1) + list_reagents = list("nutriment" = 3, "vitamin" = 2) + filling_color = "#FFFACD" + tastes = list("ice cream" = 1, "space coal" = 1, "cherries" = 1) + foodtype = FRUIT | DAIRY | SUGAR + +/obj/item/reagent_containers/food/snacks/charrie_float + name = "Cherry Shake" + trash = /obj/item/reagent_containers/food/drinks/drinkingglass + desc = "Cherries mixed with ice-cream, known for its filling tastes" + icon_state = "cherryshake" + bonus_reagents = list("nutriment" = 3, "vitamin" = 1) + list_reagents = list("nutriment" = 3, "vitamin" = 2, "cherryshake" = 15) + filling_color = "#FFFACD" + tastes = list("ice cream" = 1, "charries" = 1) + foodtype = FRUIT | DAIRY | SUGAR + +/obj/item/reagent_containers/food/snacks/bluecharrie_float + name = "Blue Cherry Shake" + trash = /obj/item/reagent_containers/food/drinks/drinkingglass + desc = "Cherries mixed with ice-cream, known for its filling tastes. This one is a exotic blue!" + icon_state = "bluecherryshake" + bonus_reagents = list("nutriment" = 3, "vitamin" = 1) + list_reagents = list("nutriment" = 3, "vitamin" = 2, "bluecherryshake" = 10) + filling_color = "#FFFACD" + tastes = list("ice cream" = 1, "blue cherries" = 1) + foodtype = FRUIT | DAIRY | SUGAR + /obj/item/reagent_containers/food/snacks/spacefreezy name = "space freezy" desc = "The best icecream in space." @@ -126,6 +170,22 @@ tastes = list("ice" = 1, "water" = 1, "berries" = 5) foodtype = FRUIT +/obj/item/reagent_containers/food/snacks/snowcones/peach + name = "peach flavored snowcone" + desc = "A peach flavord snowball in a paper cup." + icon_state = "peach_sc" + list_reagents = list("nutriment" = 1, "peachjuice" = 10) + tastes = list("ice" = 1, "water" = 1, " peach" = 5) + foodtype = FRUIT + +/obj/item/reagent_containers/food/snacks/snowcones/strawberry + name = "strawberry flavored snowcone" + desc = "A strawberry flavord snowball in a paper cup." + icon_state = "blue_sc" + list_reagents = list("nutriment" = 1, "berryjuice" = 10) + tastes = list("ice" = 1, "water" = 1, " strawberry" = 5) + foodtype = FRUIT + /obj/item/reagent_containers/food/snacks/snowcones/fruitsalad name = "mixed fruit flavored snowcone" desc = "A mix of different flavors dizzled on a snowball in a paper cup." diff --git a/code/modules/food_and_drinks/food/snacks_meat.dm b/code/modules/food_and_drinks/food/snacks_meat.dm index cf99bd78d9..0b53ec8822 100644 --- a/code/modules/food_and_drinks/food/snacks_meat.dm +++ b/code/modules/food_and_drinks/food/snacks_meat.dm @@ -322,6 +322,14 @@ filling_color = "#800000" tastes = list("meat" = 1, "butter" = 1) +/obj/item/reagent_containers/food/snacks/corndog + name = "corndog plate" + desc = "A plate with two small corn dogs, with two dimples of ketchup and mustard to dip them in." + icon_state = "dorndog" + trash = /obj/item/trash/plate/alt + tastes = list("hotdog" = 2, "mustard and ketchup" = 1, "fryed bread" = 1) + bonus_reagents = list("nutriment" = 6, "vitamin" = 2, "mustard" = 5, "ketchup" = 5) + /obj/item/reagent_containers/food/snacks/kebab/rat name = "rat-kebab" desc = "Not so delicious rat meat, on a stick." diff --git a/code/modules/food_and_drinks/food/snacks_other.dm b/code/modules/food_and_drinks/food/snacks_other.dm index c4b9451c7f..ad0824dd79 100644 --- a/code/modules/food_and_drinks/food/snacks_other.dm +++ b/code/modules/food_and_drinks/food/snacks_other.dm @@ -581,6 +581,14 @@ icon_state = "chocolatestrawberry" list_reagents = list("sugar" = 5, "nutriment" = 2) filling_color = "#ffdf26" - w_class = WEIGHT_CLASS_NORMAL tastes = list("strawberries" = 5, "chocolate" = 3) + foodtype = FRUIT | SUGAR + +/obj/item/reagent_containers/food/snacks/chocolatebanana + name = "Chocolate dipped banana" + desc = "A banana dipped in a bit of chocolate and held on a stick." + icon_state = "banana_coco" + list_reagents = list("sugar" = 5, "nutriment" = 3, "vitamin" = 1) + filling_color = "#ffdf26" + tastes = list("banana" = 5, "chocolate" = 3) foodtype = FRUIT | SUGAR \ No newline at end of file diff --git a/code/modules/food_and_drinks/food/snacks_pie.dm b/code/modules/food_and_drinks/food/snacks_pie.dm index be6c11fd68..bc66d353e1 100644 --- a/code/modules/food_and_drinks/food/snacks_pie.dm +++ b/code/modules/food_and_drinks/food/snacks_pie.dm @@ -319,8 +319,16 @@ /obj/item/reagent_containers/food/snacks/pie/strawberrypie name = "strawberry pie" - desc = "A strawberry.pie." + desc = "A strawberry pie." icon_state = "strawberrypie" bonus_reagents = list("nutriment" = 6, "vitamin" = 6) tastes = list("strawberry" = 1, "pie" = 1) foodtype = GRAIN | FRUIT | SUGAR + +/obj/item/reagent_containers/food/snacks/pie/peachpie + name = "peach pie" + desc = "A pie with peach filling." + icon_state = "strawberrypie" + bonus_reagents = list("nutriment" = 5, "vitamin" = 6, "peachjuice" = 15) + tastes = list("peach" = 1, "pie" = 1) + foodtype = GRAIN | FRUIT diff --git a/code/modules/food_and_drinks/kitchen_machinery/icecream_vat.dm b/code/modules/food_and_drinks/kitchen_machinery/icecream_vat.dm index 58d3ef69bb..0f901f2412 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/icecream_vat.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/icecream_vat.dm @@ -1,9 +1,13 @@ #define ICECREAM_VANILLA 1 #define ICECREAM_CHOCOLATE 2 #define ICECREAM_STRAWBERRY 3 -#define ICECREAM_BLUE 4 -#define CONE_WAFFLE 5 -#define CONE_CHOC 6 +#define ICECREAM_PEACH 4 +#define ICECREAM_GRAPE 5 +#define ICECREAM_BLUE 6 +#define CONE_WAFFLE 7 +#define CONE_CHOC 8 + + /obj/machinery/icecream_vat name = "ice cream vat" @@ -26,7 +30,9 @@ "cocoa" = 5, "vanilla" = 5, "berryjuice" = 5, - "singulo" = 5) + "singulo" = 5, + "peachjuice" = 5, + "grapejuice" = 5) /obj/machinery/icecream_vat/proc/get_ingredient_list(type) switch(type) @@ -34,6 +40,10 @@ return list("milk", "ice", "cocoa") if(ICECREAM_STRAWBERRY) return list("milk", "ice", "berryjuice") + if(ICECREAM_PEACH) + return list("milk", "ice", "peachjuice") + if(ICECREAM_GRAPE) + return list("milk", "ice", "grapejuice") if(ICECREAM_BLUE) return list("milk", "ice", "singulo") if(CONE_WAFFLE) @@ -50,6 +60,10 @@ return "chocolate" if(ICECREAM_STRAWBERRY) return "strawberry" + if(ICECREAM_PEACH) + return "peach" + if(ICECREAM_GRAPE) + return "grape" if(ICECREAM_BLUE) return "blue" if(CONE_WAFFLE) @@ -62,7 +76,7 @@ /obj/machinery/icecream_vat/Initialize() . = ..() - while(product_types.len < 6) + while(product_types.len < 8) product_types.Add(5) create_reagents(100, OPENCONTAINER | NO_REACT) for(var/reagent in icecream_vat_reagents) @@ -76,6 +90,8 @@ dat += "Vanilla ice cream: SelectMakex5 [product_types[ICECREAM_VANILLA]] scoops left. (Ingredients: milk, ice)
" dat += "Strawberry ice cream:SelectMakex5 [product_types[ICECREAM_STRAWBERRY]] dollops left. (Ingredients: milk, ice, berry juice)
" dat += "Chocolate ice cream:SelectMakex5 [product_types[ICECREAM_CHOCOLATE]] dollops left. (Ingredients: milk, ice, coco powder)
" + dat += "Peach ice cream:SelectMakex5 [product_types[ICECREAM_PEACH]] dollops left. (Ingredients: milk, ice, peach juice)
" + dat += "Grape ice cream:SelectMakex5 [product_types[ICECREAM_GRAPE]] dollops left. (Ingredients: milk, ice, grape juice)
" dat += "Blue ice cream:SelectMakex5 [product_types[ICECREAM_BLUE]] dollops left. (Ingredients: milk, ice, singulo)
" dat += "
CONES
" dat += "Waffle cones: Dispense Make x5 [product_types[CONE_WAFFLE]] cones left. (Ingredients: flour, sugar)
" @@ -207,6 +223,12 @@ if ("strawberry") desc = "A delicious [cone_type] cone filled with strawberry ice cream. Definitely not made with real strawberries." reagents.add_reagent("berryjuice", 2) + if ("peach") + desc = "A delicious [cone_type] cone filled with peach ice cream. Definitely made with real peaches!" + reagents.add_reagent("peachjuice", 2) + if ("grape") + desc = "A delicious [cone_type] cone filled with grape ice cream. Surprisingly, made with real pink grape, likely not real sugarcanes used." + reagents.add_reagent("grapejuice", 2) if ("blue") desc = "A delicious [cone_type] cone filled with blue ice cream. Made with real... blue?" reagents.add_reagent("singulo", 2) @@ -228,6 +250,8 @@ #undef ICECREAM_VANILLA #undef ICECREAM_CHOCOLATE #undef ICECREAM_STRAWBERRY +#undef ICECREAM_PEACH +#undef ICECREAM_GRAPE #undef ICECREAM_BLUE #undef CONE_WAFFLE #undef CONE_CHOC diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm index 483055cb0d..ceb7a01ede 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -144,6 +144,13 @@ else O.forceMove(src) return TRUE + +///Really simple proc, just moves the object "O" into the hands of mob "M" if able, done so I could modify the proc a little for the organ fridge +/obj/machinery/smartfridge/proc/dispense(obj/item/O, var/mob/M) + if(!M.put_in_hands(O)) + O.forceMove(drop_location()) + adjust_item_drop_location(O) + /obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) @@ -197,9 +204,7 @@ if(desired == 1 && Adjacent(usr) && !issilicon(usr)) for(var/obj/item/O in src) if(O.name == params["name"]) - if(!usr.put_in_hands(O)) - O.forceMove(drop_location()) - adjust_item_drop_location(O) + dispense(O, usr) break if (visible_contents) update_icon() @@ -209,8 +214,7 @@ if(desired <= 0) break if(O.name == params["name"]) - O.forceMove(drop_location()) - adjust_item_drop_location(O) + dispense(O, usr) desired-- if (visible_contents) update_icon() @@ -378,6 +382,44 @@ /obj/machinery/smartfridge/extract/preloaded initial_contents = list(/obj/item/slime_scanner = 2) +// ------------------------- You think you're better than Chem, huh? +// Organ Surgery Smartfridge +// ------------------------- Just wait till Tamiorgans +/obj/machinery/smartfridge/organ + name = "smart organ storage" + desc = "A refrigerated storage unit for organ storage." + max_n_of_items = 20 //vastly lower to prevent processing too long + var/repair_rate = 0 + +/obj/machinery/smartfridge/organ/accept_check(obj/item/O) + if(istype(O, /obj/item/organ)) + return TRUE + return FALSE + +/obj/machinery/smartfridge/organ/load(obj/item/O) + . = ..() + if(!.) //if the item loads, clear can_decompose + return + var/obj/item/organ/organ = O + organ.organ_flags |= ORGAN_FROZEN + +/obj/machinery/smartfridge/organ/RefreshParts() + for(var/obj/item/stock_parts/matter_bin/B in component_parts) + max_n_of_items = 20 * B.rating + repair_rate = max(0, STANDARD_ORGAN_HEALING * (B.rating - 1)) + +/obj/machinery/smartfridge/organ/process() + for(var/organ in contents) + var/obj/item/organ/O = organ + if(!istype(O)) + return + O.applyOrganDamage(-repair_rate) + +/obj/machinery/smartfridge/organ/Exited(obj/item/organ/AM, atom/newLoc) + . = ..() + if(istype(AM)) + AM.organ_flags &= ~ORGAN_FROZEN + // ----------------------------- // Chemistry Medical Smartfridge // ----------------------------- diff --git a/code/modules/food_and_drinks/recipes/drinks_recipes.dm b/code/modules/food_and_drinks/recipes/drinks_recipes.dm index 7c44857b1d..c83048e4b1 100644 --- a/code/modules/food_and_drinks/recipes/drinks_recipes.dm +++ b/code/modules/food_and_drinks/recipes/drinks_recipes.dm @@ -381,6 +381,36 @@ id = "neurotoxin" results = list("neurotoxin" = 2) required_reagents = list("gargleblaster" = 1, "morphine" = 1) + //FermiChem vars: Easy to make, but hard to make potent + OptimalTempMin = 200 // Lower area of bell curve for determining heat based rate reactions + OptimalTempMax = 950 // Upper end for above + ExplodeTemp = 999 //Temperature at which reaction explodes + OptimalpHMin = 4.6 // Lowest value of pH determining pH a 1 value for pH based rate reactions (Plateu phase) + OptimalpHMax = 5.2 // Higest value for above + ReactpHLim = 5 // How far out pH wil react, giving impurity place (Exponential phase) + CatalystFact = 0 // How much the catalyst affects the reaction (0 = no catalyst) + CurveSharpT = 2 // How sharp the temperature exponential curve is (to the power of value) + CurveSharppH = 4 // How sharp the pH exponential curve is (to the power of value) + ThermicConstant = 10 //Temperature change per 1u produced + HIonRelease = 0.02 //pH change per 1u reaction + RateUpLim = 5 //Optimal/max rate possible if all conditions are perfect + FermiChem = TRUE//If the chemical uses the Fermichem reaction mechanics + FermiExplode = FALSE //If the chemical explodes in a special way + PurityMin = 0 //The minimum purity something has to be above, otherwise it explodes. + clear_conversion = REACTION_CLEAR_INVERSE + +/* +/datum/chemical_reaction/neurotoxin/FermiFinish(datum/reagents/holder, var/atom/my_atom) + var/datum/reagent/consumable/ethanol/neurotoxin/Nt = locate(/datum/reagent/consumable/ethanol/neurotoxin) in my_atom.reagents.reagent_list + var/cached_volume = Nt.volume + if(Nt.purity < 0.5) + holder.remove_reagent(src.id, cached_volume) + holder.add_reagent("neuroweak", cached_volume) +*/ + +/datum/chemical_reaction/neurotoxin/FermiExplode(datum/reagents, var/atom/my_atom, volume, temp, pH)//reduced size + volume = volume/10 + ..() /datum/chemical_reaction/snowwhite name = "Snow White" @@ -454,18 +484,6 @@ results = list("vanillapudding" = 20) required_reagents = list("vanilla" = 5, "milk" = 5, "eggyolk" = 5) -/datum/chemical_reaction/cherryshake - name = "Cherry Shake" - id = "cherryshake" - results = list("cherryshake" = 3) - required_reagents = list("cherryjelly" = 1, "ice" = 1, "cream" = 1) - -/datum/chemical_reaction/bluecherryshake - name = "Blue Cherry Shake" - id = "bluecherryshake" - results = list("bluecherryshake" = 3) - required_reagents = list("bluecherryjelly" = 1, "ice" = 1, "cream" = 1) - /datum/chemical_reaction/drunkenblumpkin name = "Drunken Blumpkin" id = "drunkenblumpkin" @@ -794,4 +812,17 @@ name = "Red Queen" id = "red_queen" results = list("red_queen" = 10) - required_reagents = list("tea" = 6, "mercury" = 2, "blackpepper" = 1, "growthserum" = 1) \ No newline at end of file + required_reagents = list("tea" = 6, "mercury" = 2, "blackpepper" = 1, "growthserum" = 1) + +/datum/chemical_reaction/catnip_tea + name = "Catnip Tea" + id = "catnip_tea" + results = list("catnip_tea" = 3) + required_reagents = list("tea" = 5, "catnip" = 2) + +/datum/chemical_reaction/commander_and_chief + name = "Commander and Chief" + id = "commander_and_chief" + results = list("commander_and_chief" = 50) + required_reagents = list("alliescocktail" = 50, "champagne" = 20, "doctorsdelight" = 10, "quintuple_sec" = 10, "screwdrivercocktail" = 10) + mix_message = "When your powers combine, I am Captain Pl-..." diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_cake.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_cake.dm index e26d15dbd3..284affe2ec 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_cake.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_cake.dm @@ -169,6 +169,15 @@ result = /obj/item/reagent_containers/food/snacks/store/cake/vanilla_cake subcategory = CAT_CAKE +/datum/crafting_recipe/food/peachcake + name = "Peach cake" + reqs = list( + /obj/item/reagent_containers/food/snacks/store/cake/plain = 1, + /obj/item/reagent_containers/food/snacks/grown/peach = 5 + ) + result = /obj/item/reagent_containers/food/snacks/store/cake/peach_cake + subcategory = CAT_CAKE + /datum/crafting_recipe/food/cak name = "Living cat/cake hybrid" reqs = list( diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm index 73b09df68d..8f4c5d05f1 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm @@ -42,6 +42,52 @@ result = /obj/item/reagent_containers/food/snacks/honkdae subcategory = CAT_ICE +/datum/crafting_recipe/food/banana_split + name = "Banana Split" + always_availible = FALSE + reqs = list( + /obj/item/reagent_containers/food/snacks/icecream = 3, + /obj/item/reagent_containers/food/snacks/grown/banana = 1, + /obj/item/reagent_containers/food/snacks/grown/cherries = 1, + /obj/item/reagent_containers/food/snacks/chocolatebar = 1 + ) + result = /obj/item/reagent_containers/food/snacks/banana_split + subcategory = CAT_ICE + +/datum/crafting_recipe/food/root_float + name = "Cola Float" + always_availible = FALSE + reqs = list( + /obj/item/reagent_containers/food/snacks/icecream = 1, + /obj/item/reagent_containers/food/snacks/grown/cherries = 1, + /datum/reagent/consumable/space_cola = 10, + /obj/item/reagent_containers/food/drinks/drinkingglass = 1 + ) + result = /obj/item/reagent_containers/food/snacks/cola_float + subcategory = CAT_ICE + +/datum/crafting_recipe/food/charrie_float + name = "Cherry Shake" + always_availible = FALSE + reqs = list( + /obj/item/reagent_containers/food/snacks/icecream = 1, + /obj/item/reagent_containers/food/snacks/grown/cherries = 3, + /obj/item/reagent_containers/food/drinks/drinkingglass = 1 + ) + result = /obj/item/reagent_containers/food/snacks/charrie_float + subcategory = CAT_ICE + +/datum/crafting_recipe/food/bluecharrie_float + name = "Blue Cherry Shake" + always_availible = FALSE + reqs = list( + /obj/item/reagent_containers/food/snacks/icecream = 1, + /obj/item/reagent_containers/food/snacks/grown/bluecherries = 3, + /obj/item/reagent_containers/food/drinks/drinkingglass = 1 + ) + result = /obj/item/reagent_containers/food/snacks/bluecharrie_float + subcategory = CAT_ICE + //////////////////////////SNOW CONES/////////////////////// /datum/crafting_recipe/food/flaverless_sc @@ -232,6 +278,28 @@ result = /obj/item/reagent_containers/food/snacks/snowcones/honey subcategory = CAT_ICE +/datum/crafting_recipe/food/peach_sc + name = "Peach snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /obj/item/reagent_containers/food/snacks/grown/peach = 1 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/peach + subcategory = CAT_ICE + +/datum/crafting_recipe/food/strawberry_sc + name = "Strawberry snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /obj/item/reagent_containers/food/snacks/grown/strawberry = 2 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/strawberry + subcategory = CAT_ICE + /datum/crafting_recipe/food/honey_sc name = "Rainbow snowcone" reqs = list( diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm index 7eff2820c5..d17ccf585e 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm @@ -128,6 +128,18 @@ result = /obj/item/reagent_containers/food/snacks/pigblanket subcategory = CAT_MEAT +/datum/crafting_recipe/food/corndog + name = "Corndog meal" + reqs = list( + /obj/item/stack/rods = 1, + /obj/item/reagent_containers/food/snacks/meat/cutlet = 1, + /obj/item/reagent_containers/food/snacks/bun = 1, + /datum/reagent/consumable/mustard = 5, + /datum/reagent/consumable/ketchup = 5 + ) + result = /obj/item/reagent_containers/food/snacks/corndog + subcategory = CAT_MEAT + /datum/crafting_recipe/food/ratkebab name = "Rat Kebab" reqs = list( diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm index 931a78212f..365cf499bb 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm @@ -23,7 +23,7 @@ result = /obj/item/reagent_containers/food/snacks/donut subcategory = CAT_PASTRY -/datum/crafting_recipe/food/donut +/datum/crafting_recipe/food/donut/semen time = 15 name = "Semen donut" reqs = list( diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pie.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pie.dm index 81824dc4b8..30c265e237 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pie.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pie.dm @@ -214,4 +214,13 @@ /obj/item/slime_extract = 1 ) result = /obj/item/reagent_containers/food/snacks/pie/cocolavatart + subcategory = CAT_PIE + +/datum/crafting_recipe/food/peachpie + name = "Peach Pie" + reqs = list( + /obj/item/reagent_containers/food/snacks/pie/plain = 1, + /obj/item/reagent_containers/food/snacks/grown/peach = 3 + ) + result = /obj/item/reagent_containers/food/snacks/pie/peachpie subcategory = CAT_PIE \ No newline at end of file diff --git a/code/modules/goonchat/browserOutput.dm b/code/modules/goonchat/browserOutput.dm index 082f20f524..ae49e2cb45 100644 --- a/code/modules/goonchat/browserOutput.dm +++ b/code/modules/goonchat/browserOutput.dm @@ -82,11 +82,13 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico if("setMusicVolume") data = setMusicVolume(arglist(params)) - if("swaptodarkmode") - swaptodarkmode() + 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("swaptolightmode") - swaptolightmode() if(data) ehjax_send(data = data) @@ -246,10 +248,3 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico // 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") - - -/datum/chatOutput/proc/swaptolightmode() //Dark mode light mode stuff. Yell at KMC if this breaks! (See darkmode.dm for documentation) - owner.force_white_theme() - -/datum/chatOutput/proc/swaptodarkmode() - owner.force_dark_theme() diff --git a/code/modules/goonchat/browserassets/css/browserOutput.css b/code/modules/goonchat/browserassets/css/browserOutput.css index 7b942494f4..164d0d65c6 100644 --- a/code/modules/goonchat/browserassets/css/browserOutput.css +++ b/code/modules/goonchat/browserassets/css/browserOutput.css @@ -7,20 +7,16 @@ html, body { padding: 0; margin: 0; height: 100%; - color: #f0f0f0; + color: #000000; } body { - background: #171717; + background: #E0E0E0; /*CIT CHANGE - darkens chatbox a lil*/ font-family: Verdana, sans-serif; font-size: 9pt; - font-color: #f0f0f0; line-height: 1.2; overflow-x: hidden; overflow-y: scroll; - word-wrap: break-word; - scrollbar-face-color:#1A1A1A; - scrollbar-track-color:#171717; - scrollbar-highlight-color:#171717; + word-wrap: break-word; } em { @@ -60,9 +56,9 @@ img.icon { border-radius: 10px; } -a {color: #397ea5;} -a.visited {color: #7c00e6;} -a:visited {color: #7c00e6;} +a {color: #0000ff;} +a.visited {color: #ff00ff;} +a:visited {color: #ff00ff;} a.popt {text-decoration: none;} /***************************************** @@ -93,21 +89,21 @@ a.popt {text-decoration: none;} bottom: 0; right: 0; padding: 8px; - background: #202020; + background: #d0d0d0; text-decoration: none; font-variant: small-caps; font-size: 1.1em; font-weight: bold; - color: #a4bad6; + color: #333; } -#newMessages:hover {background: #171717;} +#newMessages:hover {background: #ccc;} #newMessages i {vertical-align: middle; padding-left: 3px;} #ping { position: fixed; top: 0; right: 115px; width: 45px; - background: #202020; + background: #d0d0d0; height: 30px; padding: 8px 0 2px 0; } @@ -124,19 +120,19 @@ a.popt {text-decoration: none;} right: 0; } #userBar .subCell { - background: #202020; + background: #d0d0d0; height: 30px; padding: 5px 0; display: block; - color: #a4bad6; + color: #333; text-decoration: none; line-height: 28px; - border-top: 1px solid #171717; + border-top: 1px solid #b4b4b4; } -#userBar .subCell:hover {background: #202020;} +#userBar .subCell:hover {background: #ccc;} #userBar .toggle { width: 40px; - background: #202020; + background: #ccc; border-top: 0; float: right; text-align: center; @@ -172,7 +168,7 @@ a.popt {text-decoration: none;} position: fixed; top: 50%; left: 50%; - background: #ddd; + background: #d0d0d0; } .popup .close { position: absolute; @@ -189,7 +185,7 @@ a.popt {text-decoration: none;} .popup .close:hover {background: #999;} .popup .head { background: #999; - color: #ddd; + color: #d0d0d0; padding: 0 10px; height: 30px; line-height: 30px; @@ -200,7 +196,7 @@ a.popt {text-decoration: none;} } .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: #ddd; text-transform: uppercase; font-size: 0.9em; font-weight: bold;} +.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;} @@ -214,7 +210,7 @@ a.popt {text-decoration: none;} /* ADMIN CONTEXT MENU */ .contextMenu { - background-color: #ddd; + background-color: #d0d0d0; position: fixed; margin: 2px; width: 150px; @@ -246,9 +242,9 @@ a.popt {text-decoration: none;} ******************************************/ /* MOTD */ -.motd {color: #a4bad6; font-family: Verdana, sans-serif;} -.motd h1, .motd h2, .motd h3, .motd h4, .motd h5, .motd h6 {color: #a4bad6; text-decoration: underline;} -.motd a, .motd a:link, .motd a:visited, .motd a:active, .motd a:hover {color: #a4bad6;} +.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;} @@ -259,90 +255,91 @@ a.popt {text-decoration: none;} /* OUTPUT COLORS */ .highlight {background: yellow;} -h1, h2, h3, h4, h5, h6 {color: #a4bad6;font-family: Georgia, Verdana, sans-serif;} -h1.alert, h2.alert {color: #a4bad6;} +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: #cca300; font-weight: bold;} -.antagooc {color: #ce254f; 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: #3d5bc3; font-weight: bold;} +.adminooc {color: #700038; font-weight: bold;} -.adminsay {color: #ff4500; font-weight: bold;} -.admin {color: #5975da; font-weight: bold;} +.adminsay {color: #FF4500} +.admin {color: #386aff; font-weight: bold;} .name { font-weight: bold;} .say {} -.deadsay {color: #e2c1ff;} +.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: #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;} +.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: #d82020;} -h1.alert, h2.alert {color: #99aab5;} +.alert {color: #ff0000;} +h1.alert, h2.alert {color: #000000;} .emote { font-style: italic;} .selecteddna {color: #ffffff; background-color: #001B1B} -.attack {color: #e01c1c;} -.disarm {color: #b42525;} -.passive {color: #a00f0f;} +.attack {color: #ff0000;} +.disarm {color: #990000;} +.passive {color: #660000;} -.userdanger {color: #c51e1e; font-weight: bold; font-size: 24px;} -.danger {color: #c51e1e;} -.warning {color: #c51e1e; font-style: italic;} +.userdanger {color: #ff0000; font-weight: bold; font-size: 24px;} +.danger {color: #ff0000;} +.warning {color: #ff0000; font-style: italic;} .alertwarning {color: #FF0000; font-weight: bold} -.boldwarning {color: #c51e1e; font-style: italic; font-weight: bold} -.announce {color: #c51e1e; font-weight: bold;} -.boldannounce {color: #c51e1e; font-weight: bold;} -.greenannounce {color: #059223; 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: #6685f5;} -.notice {color: #6685f5;} -.boldnotice {color: #6685f5; font-weight: bold;} -.adminnotice {color: #6685f5;} +.info {color: #0000CC;} +.notice {color: #000099;} +.boldnotice {color: #000099; font-weight: bold;} +.adminnotice {color: #0000ff;} .adminhelp {color: #ff0000; font-weight: bold;} -.unconscious {color: #a4bad6; font-weight: bold;} +.unconscious {color: #0000ff; font-weight: bold;} .suicide {color: #ff5050; font-style: italic;} -.red {color: #FF0000} -.pink {color: #ff70c1;} -.blue {color: #215cff} -.green {color: #059223;} -.nicegreen {color: #059223;} +.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: #8e8a99;} -.cult {color: #aa1c1c;} +.shadowling {color: #3b2769;} +.cult {color: #960000;} -.cultitalic {color: #aa1c1c; font-style: italic;} -.cultbold {color: #aa1c1c; font-style: italic; font-weight: bold;} -.cultboldtalic {color: #aa1c1c; font-weight: bold; font-size: 24px;} +.cultitalic {color: #960000; font-style: italic;} +.cultbold {color: #960000; font-style: italic; font-weight: bold;} +.cultboldtalic {color: #960000; font-weight: bold; font-size: 24px;} -.cultlarge {color: #aa1c1c; font-weight: bold; font-size: 24px;} -.narsie {color: #aa1c1c; font-weight: bold; font-size: 120px;} -.narsiesmall {color: #aa1c1c; font-weight: bold; font-size: 48px;} +.cultlarge {color: #960000; font-weight: bold; font-size: 24px;} +.narsie {color: #960000; font-weight: bold; font-size: 120px;} +.narsiesmall {color: #960000; font-weight: bold; font-size: 48px;} .colossus {color: #7F282A; font-size: 40px;} -.hierophant {color: #b441ee; font-weight: bold; font-style: italic;} -.hierophant_warning {color: #c56bf1; font-style: italic;} -.purple {color: #9956d3;} -.holoparasite {color: #88809c;} +.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;} @@ -350,11 +347,11 @@ h1.alert, h2.alert {color: #99aab5;} .revenminor {color: #823abb} .revenwarning {color: #760fbb; font-style: italic;} .revendanger {color: #760fbb; font-weight: bold; font-size: 24px;} -.umbra {color: #7c00e6;} -.umbra_emphasis {color: #7c00e6; font-weight: bold; font-style: italic;} -.umbra_large {color: #7c00e6; font-size: 24px; font-weight: bold; font-style: italic;} +.umbra {color: #5000A0;} +.umbra_emphasis {color: #5000A0; font-weight: bold; font-style: italic;} +.umbra_large {color: #5000A0; font-size: 24px; font-weight: bold; font-style: italic;} -.deconversion_message {color: #a947ff; font-size: 24px; font-style: italic;} +.deconversion_message {color: #5000A0; font-size: 24px; font-style: italic;} .brass {color: #BE8700;} .heavy_brass {color: #BE8700; font-weight: bold; font-style: italic;} @@ -379,17 +376,17 @@ h1.alert, h2.alert {color: #99aab5;} .neovgre {color: #6E001A; font-weight: bold; font-style: italic;} .neovgre_small {color: #6E001A;} -.newscaster {color: #c05d5d;} -.ghostalert {color: #6600ff; font-style: italic; font-weight: bold;} +.newscaster {color: #800000;} +.ghostalert {color: #5c00e6; font-style: italic; font-weight: bold;} -.alien {color: #855d85;} -.noticealien {color: #059223;} -.alertalien {color: #059223; font-weight: bold;} -.changeling {color: #059223; font-style: italic;} +.alien {color: #543354;} +.noticealien {color: #00c000;} +.alertalien {color: #00c000; font-weight: bold;} +.changeling {color: #800080; font-style: italic;} -.spider {color: #8800ff;} +.spider {color: #4d004d;} -.interface {color: #750e75;} +.interface {color: #330033;} .sans {font-family: "Comic Sans MS", cursive, sans-serif;} .papyrus {font-family: "Papyrus", cursive, sans-serif;} @@ -400,9 +397,9 @@ h1.alert, h2.alert {color: #99aab5;} .big {font-size: 24px;} .reallybig {font-size: 32px;} .extremelybig {font-size: 40px;} -.greentext {color: #059223; font-size: 24px;} -.redtext {color: #c51e1e; font-size: 24px;} -.clown {color: #ff70c1; font-size: 24px; font-family: "Comic Sans MS", cursive, sans-serif; font-weight: bold;} +.greentext {color: #00FF00; font-size: 24px;} +.redtext {color: #FF0000; font-size: 24px;} +.clown {color: #FF69Bf; font-size: 24px; 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;} .velvet {color: #660015; font-weight: bold; animation: velvet 5000ms infinite;} @keyframes velvet { @@ -434,20 +431,20 @@ h1.alert, h2.alert {color: #99aab5;} .memo {color: #638500; text-align: center;} .memoedit {text-align: center; font-size: 16px;} -.abductor {color: #c204c2; font-style: italic;} -.mind_control {color: #df3da9; 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;} .swarmer {color: #2C75FF;} .resonate {color: #298F85;} -.monkeyhive {color: #a56408;} -.monkeylead {color: #af6805; font-size: 2;} +.monkeyhive {color: #774704;} +.monkeylead {color: #774704; font-size: 2;} .connectionClosed, .fatalError {background: red; color: white; padding: 5px;} .connectionClosed.restored {background: green;} -.internal.boldnshit {color: #3d5bc3; font-weight: bold;} +.internal.boldnshit {color: #000099; font-weight: bold;} /* HELPER CLASSES */ .text-normal {font-weight: normal; font-style: normal;} diff --git a/code/modules/goonchat/browserassets/css/browserOutput_dark.css b/code/modules/goonchat/browserassets/css/browserOutput_dark.css new file mode 100644 index 0000000000..2e1fe8cdec --- /dev/null +++ b/code/modules/goonchat/browserassets/css/browserOutput_dark.css @@ -0,0 +1,159 @@ +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; font-family: Verdana, sans-serif;} +.motd h1, .motd h2, .motd h3, .motd h4, .motd h5, .motd h6 {color: #E0E0E0; text-decoration: underline;} +.motd a, .motd a:link, .motd a:visited, .motd a:active, .motd a:hover {color: #E0E0E0;} + +h1, h2, h3, h4, h5, h6 {color: #E0E0E0;font-family: Georgia, Verdana, sans-serif;} +h1.alert, h2.alert {color: #E0E0E0;} + +.ooc {color: #cca300; font-weight: bold;} +.looc {color: #d8b555; font-weight: bold;} +.antagooc {color: #ce254f; font-weight: bold;} +.adminobserverooc {color: #0099cc; font-weight: bold;} +.adminooc {color: #3d5bc3; font-weight: bold;} + +.admin {color: #5975da; font-weight: bold;} + +.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; font-weight: bold; font-size: 24px;} +.danger {color: #c51e1e;} +.warning {color: #c51e1e; font-style: italic;} +.alertwarning {color: #c51e1e; font-weight: bold} +.boldwarning {color: #c51e1e; font-style: italic; font-weight: bold} +.announce {color: #c51e1e; font-weight: bold;} +.boldannounce {color: #c51e1e; font-weight: bold;} +.greenannounce {color: #059223; font-weight: bold;} +.info {color: #6685f5;} +.notice {color: #6685f5;} +.boldnotice {color: #6685f5; font-weight: bold;} +.adminnotice {color: #6685f5;} +.adminhelp {color: #ff0000; font-weight: bold;} +.unconscious {color: #E0E0E0; font-weight: bold;} +.red {color: #FF0000} +.pink {color: #ff70c1;} +.blue {color: #215cff} +.green {color: #059223;} +.nicegreen {color: #059223;} +.userlove {color: #ff42a6; font-style: italic; font-weight: bold; text-shadow: 0 0 6px #82365e;} +.love {color: #ff4591; font-style: italic; text-shadow: 0 0 6px #994449;} +.shadowling {color: #8e8a99;} +.cult {color: #aa1c1c;} + +.cultitalic {color: #aa1c1c; font-style: italic;} +.cultbold {color: #aa1c1c; font-style: italic; font-weight: bold;} +.cultboldtalic {color: #aa1c1c; font-weight: bold; font-size: 24px;} + +.cultlarge {color: #aa1c1c; font-weight: bold; font-size: 24px;} +.narsie {color: #aa1c1c; font-weight: bold; font-size: 120px;} +.narsiesmall {color: #aa1c1c; font-weight: bold; font-size: 48px;} +.hierophant {color: #b441ee; font-weight: bold; font-style: italic;} +.hierophant_warning {color: #c56bf1; font-style: italic;} +.purple {color: #9956d3;} +.holoparasite {color: #88809c;} + +.revennotice {color: #3645aa;} +.revenboldnotice {color: #3645aa; font-weight: bold;} +.revenbignotice {color: #3645aa; font-weight: bold; font-size: 24px;} +.revenminor {color: #823ddd} +.revenwarning {color: #8911d9; font-style: italic;} +.revendanger {color: #8911d9; font-weight: bold; font-size: 24px;} +.umbra {color: #7c00e6;} +.umbra_emphasis {color: #7c00e6; font-weight: bold; font-style: italic;} +.umbra_large {color: #7c00e6; font-size: 24px; font-weight: bold; font-style: italic;} + +.deconversion_message {color: #a947ff; font-size: 24px; font-style: italic;} + +.alloy {color: #545b64;} +.heavy_alloy {color: #545b64; font-weight: bold; font-style: italic;} +.nezbere_large {color: #545b64; font-size: 24px; font-weight: bold; font-style: italic;} +.nezbere {color: #545b64; font-weight: bold; font-style: italic;} +.nezbere_small {color: #545b64;} +.inathneq_large {color: #1d7dc7; font-size: 24px; font-weight: bold; font-style: italic;} +.inathneq {color: #1d7dc7; font-weight: bold; font-style: italic;} +.inathneq_small {color: #1d7dc7;} +.neovgre_large {color: #7c0622; font-size: 24px; font-weight: bold; font-style: italic;} +.neovgre {color: #7c0622; font-weight: bold; font-style: italic;} +.neovgre_small {color: #7c0622;} + +.newscaster {color: #c05d5d;} +.ghostalert {color: #6600ff; font-style: italic; font-weight: bold;} + +.alien {color: #855d85;} +.noticealien {color: #059223;} +.alertalien {color: #059223; font-weight: bold;} +.changeling {color: #059223; font-style: italic;} + +.spider {color: #8800ff;} + +.interface {color: #750e75;} + +.greentext {color: #059223; font-size: 24px;} +.redtext {color: #c51e1e; font-size: 24px;} +.clown {color: #ff70c1; font-size: 24px; font-family: "Comic Sans MS", cursive, sans-serif; font-weight: bold;} +.velvet {color: #660015; font-weight: bold; animation: velvet 5000ms infinite;} +@keyframes velvet { + 0% { color: #890020; } + 40% { color: #c51e1e; } + 50% { color: #FF8888; } + 60% { color: #c51e1e; } + 100% { color: #890020; } +} + +.abductor {color: #c204c2; font-style: italic;} +.mind_control {color: #df3da9; font-size: 3; font-weight: bold; font-style: italic;} +.drone {color: #979795;} + +.monkeyhive {color: #a56408;} +.monkeylead {color: #af6805; font-size: 2;} + +.internal.boldnshit {color: #3d5bc3; font-weight: bold;} diff --git a/code/modules/goonchat/browserassets/css/browserOutput_white.css b/code/modules/goonchat/browserassets/css/browserOutput_light.css similarity index 58% rename from code/modules/goonchat/browserassets/css/browserOutput_white.css rename to code/modules/goonchat/browserassets/css/browserOutput_light.css index e21185449b..098bd25367 100644 --- a/code/modules/goonchat/browserassets/css/browserOutput_white.css +++ b/code/modules/goonchat/browserassets/css/browserOutput_light.css @@ -1,236 +1,18 @@ -/***************************************** -* -* GLOBAL STYLES -* -******************************************/ -html, body { - padding: 0; - margin: 0; - height: 100%; - color: #000000; -} -body { - background: #fff; - font-family: Verdana, sans-serif; - font-size: 9pt; - line-height: 1.2; - overflow-x: hidden; - overflow-y: scroll; - word-wrap: break-word; -} +body {background: #F1F1F1;} -em { - font-style: normal; - font-weight: bold; -} +#newMessages {background: #ddd;} +#ping {background: #ddd;} -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: #ddd; - 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: 115px; - width: 45px; - background: #ddd; - 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: #ddd; - 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: 40px; - background: #ccc; - border-top: 0; - float: right; - text-align: center; -} -#userBar .sub {clear: both; display: none; width: 160px;} -#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;} +#userBar .subCell {background: #ddd;} /* POPUPS */ -.popup { - position: fixed; - top: 50%; - left: 50%; - background: #ddd; -} -.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: #ddd; - 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: #ddd; 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;} +.popup {background: #ddd;} +.popup .head {color: #ddd;} +.popup input[type=submit] {color: #ddd;} /* ADMIN CONTEXT MENU */ -.contextMenu { - background-color: #ddd; - position: fixed; - margin: 2px; - width: 150px; -} -.contextMenu a { - display: block; - padding: 2px 5px; - text-decoration: none; - color: #333; -} +.contextMenu {background-color: #ddd;} -.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;} @@ -261,12 +43,13 @@ 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; font-weight: bold;} -.admin {color: #4473ff; font-weight: bold;} +.adminsay {color: #FF4500} +.admin {color: #386aff; font-weight: bold;} .name { font-weight: bold;} @@ -304,7 +87,7 @@ h1.alert, h2.alert {color: #000000;} .userdanger {color: #ff0000; font-weight: bold; font-size: 24px;} .danger {color: #ff0000;} .warning {color: #ff0000; font-style: italic;} -.alertwarning {color: #FF0000; font-weight: bold} +.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;} @@ -317,10 +100,10 @@ h1.alert, h2.alert {color: #000000;} .adminhelp {color: #ff0000; font-weight: bold;} .unconscious {color: #0000ff; font-weight: bold;} .suicide {color: #ff5050; font-style: italic;} -.green {color: #03ff39;} -.red {color: #FF0000} +.green {color: #03ff39;} +.red {color: #FF0000;} .pink {color: #FF69Bf;} -.blue {color: #0000FF} +.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;} @@ -329,7 +112,7 @@ h1.alert, h2.alert {color: #000000;} .cultitalic {color: #960000; font-style: italic;} .cultbold {color: #960000; font-style: italic; font-weight: bold;} -.cultboldtalic {color: #960000; font-weight: bold; font-size: 24px;} +.cultboldtalic {color: #960000; font-weight: bold; font-size: 24px;} .cultlarge {color: #960000; font-weight: bold; font-size: 24px;} .narsie {color: #960000; font-weight: bold; font-size: 120px;} @@ -400,13 +183,38 @@ h1.alert, h2.alert {color: #000000;} .redtext {color: #FF0000; font-size: 24px;} .clown {color: #FF69Bf; font-size: 24px; 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;} +.velvet {color: #660015; font-weight: bold; animation: velvet 5000ms infinite;} +@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: 16px;} .abductor {color: #800080; font-style: italic;} -.mind_control {color: #A00D6F; font-size: 3; font-weight: bold; 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;} @@ -418,9 +226,8 @@ h1.alert, h2.alert {color: #000000;} .connectionClosed, .fatalError {background: red; color: white; padding: 5px;} .connectionClosed.restored {background: green;} -.internal.boldnshit {color: blue; font-weight: bold;} +.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/html/browserOutput.html b/code/modules/goonchat/browserassets/html/browserOutput.html index 9c6d462a85..68da251702 100644 --- a/code/modules/goonchat/browserassets/html/browserOutput.html +++ b/code/modules/goonchat/browserassets/html/browserOutput.html @@ -5,8 +5,9 @@ - + + @@ -29,7 +30,7 @@ --ms
- +
diff --git a/code/modules/goonchat/browserassets/js/browserOutput.js b/code/modules/goonchat/browserassets/js/browserOutput.js index 93a498fd06..abd05d29ed 100644 --- a/code/modules/goonchat/browserassets/js/browserOutput.js +++ b/code/modules/goonchat/browserassets/js/browserOutput.js @@ -35,7 +35,7 @@ var opts = { 'wasd': false, //Is the user in wasd mode? 'priorChatHeight': 0, //Thing for height-resizing detection 'restarting': false, //Is the round restarting? - 'darkmode':false, //Are we using darkmode? If not WHY ARE YOU LIVING IN 2009??? + 'colorPreset': 0, // index in the color presets list. //Options menu 'selectedSubLoop': null, //Contains the interval loop for closing the selected sub menu @@ -73,6 +73,14 @@ var opts = { }; +// Array of names for chat display color presets. +// 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)) } @@ -96,6 +104,12 @@ if (typeof String.prototype.trim !== 'function') { }; } +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; @@ -395,19 +409,6 @@ function toHex(n) { 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}; @@ -615,7 +616,7 @@ $(function() { 'shighlightColor': getCookie('highlightcolor'), 'smusicVolume': getCookie('musicVolume'), 'smessagecombining': getCookie('messagecombining'), - 'sdarkmode': getCookie('darkmode'), + 'scolorPreset': getCookie('colorpreset'), }; if (savedConfig.sfontSize) { @@ -626,9 +627,6 @@ $(function() { $("body").css('line-height', savedConfig.slineHeight); internalOutput('Loaded line height setting of: '+savedConfig.slineHeight+'', 'internal'); } - if(savedConfig.sdarkmode == 'true'){ - swap(); - } if (savedConfig.spingDisabled) { if (savedConfig.spingDisabled == 'true') { opts.pingDisabled = true; @@ -654,6 +652,13 @@ $(function() { opts.highlightColor = savedConfig.shighlightColor; internalOutput('Loaded highlight color of: '+savedConfig.shighlightColor+'', 'internal'); } + + if (savedConfig.scolorPreset) { + opts.colorPreset = Number(savedConfig.scolorPreset); + updateColorPreset(); + internalOutput('Loaded color preset of: '+colorPresets[opts.colorPreset]+'', 'internal'); + } + if (savedConfig.smusicVolume) { var newVolume = clamp(savedConfig.smusicVolume, 0, 100); $('#adminMusic').prop('volume', newVolume / 100); @@ -839,9 +844,6 @@ $(function() { $('#toggleOptions').click(function(e) { handleToggleClick($subOptions, $(this)); }); - $('#darkmodetoggle').click(function(e) { - swap(); - }); $('#toggleAudio').click(function(e) { handleToggleClick($subAudio, $(this)); }); @@ -913,7 +915,7 @@ $(function() { $.ajax({ type: 'GET', - url: 'browserOutput_white.css', + url: 'browserOutput.css', success: function(styleData) { var blob = new Blob(['Chat Log', $messages.html(), '']); @@ -992,6 +994,13 @@ $(function() { opts.messageCount = 0; }); + $('#changeColorPreset').click(function() { + 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'); diff --git a/code/modules/hydroponics/grown/flowers.dm b/code/modules/hydroponics/grown/flowers.dm index f1aea5706a..af5919969c 100644 --- a/code/modules/hydroponics/grown/flowers.dm +++ b/code/modules/hydroponics/grown/flowers.dm @@ -37,7 +37,7 @@ species = "lily" plantname = "Lily Plants" product = /obj/item/reagent_containers/food/snacks/grown/poppy/lily - mutatelist = list() + mutatelist = list(/obj/item/seeds/bee_balm) /obj/item/reagent_containers/food/snacks/grown/poppy/lily seed = /obj/item/seeds/poppy/lily @@ -221,3 +221,61 @@ if(!user.gloves) to_chat(user, "The [name] burns your bare hand!") user.adjustFireLoss(rand(1, 5)) + +// Beebalm +/obj/item/seeds/bee_balm + name = "pack of Bee Balm seeds" + desc = "These seeds grow into Bee Balms." + icon_state = "seed-bee_balm" + species = "bee_balm" + plantname = "Bee Balm Buds" + product = /obj/item/reagent_containers/food/snacks/grown/bee_balm + endurance = 10 + maturation = 8 + yield = 3 + potency = 30 + growthstages = 3 + growing_icon = 'icons/obj/hydroponics/growing_flowers.dmi' + icon_grow = "bee_balm-grow" + icon_dead = "bee_balm-dead" + mutatelist = list(/obj/item/seeds/poppy/geranium, /obj/item/seeds/bee_balm/honey) //Lower odds of becoming honey + reagents_add = list("spaceacillin" = 0.1, "sterilizine" = 0.05) + +/obj/item/reagent_containers/food/snacks/grown/bee_balm + seed = /obj/item/seeds/bee_balm + name = "bee balm" + desc = "A flower used for medical antiseptic in history." + icon_state = "bee_balm" + filling_color = "#FF6347" + bitesize_mod = 8 + tastes = list("strong antiseptic " = 1) + foodtype = GROSS + +// Beebalm +/obj/item/seeds/bee_balm/honey + name = "pack of Honey Balm seeds" + desc = "These seeds grow into Honey Balms." + icon_state = "seed-bee_balmalt" + species = "seed-bee_balm_alt" + plantname = "Honey Balm Pods" + product = /obj/item/reagent_containers/food/snacks/grown/bee_balm/honey + endurance = 1 + maturation = 10 + yield = 1 + potency = 1 + growthstages = 3 + growing_icon = 'icons/obj/hydroponics/growing_flowers.dmi' + icon_grow = "bee_balmalt-grow" + icon_dead = "bee_balmalt-dead" + reagents_add = list("honey" = 0.1, "lye" = 0.3) //To make wax + rarity = 30 + +/obj/item/reagent_containers/food/snacks/grown/bee_balm/honey + seed = /obj/item/seeds/bee_balm/honey + name = "honey balm" + desc = "A large honey filled pod of a flower." + icon_state = "bee_balmalt" + filling_color = "#FF6347" + bitesize_mod = 8 + tastes = list("wax" = 1) + foodtype = SUGAR \ No newline at end of file diff --git a/code/modules/hydroponics/grown/misc.dm b/code/modules/hydroponics/grown/misc.dm index 107a6a94f9..0902052a11 100644 --- a/code/modules/hydroponics/grown/misc.dm +++ b/code/modules/hydroponics/grown/misc.dm @@ -1,7 +1,7 @@ // Starthistle /obj/item/seeds/starthistle name = "pack of starthistle seeds" - desc = "A robust species of weed that often springs up in-between the cracks of spaceship parking lots." + desc = "A robust species of weed that often springs up in-between the cracks of spaceship parking lots. Grind down these seeds for a substitution for mustardgrind." icon_state = "seed-starthistle" species = "starthistle" plantname = "Starthistle" @@ -9,9 +9,10 @@ endurance = 50 // damm pesky weeds maturation = 5 production = 1 - yield = 2 + yield = 6 potency = 10 growthstages = 3 + grind_results = list("mustardgrind" = 1) growing_icon = 'icons/obj/hydroponics/growing_flowers.dmi' genes = list(/datum/plant_gene/trait/plant_type/weed_hardy) mutatelist = list(/obj/item/seeds/harebell) diff --git a/code/modules/hydroponics/grown/tea_coffee.dm b/code/modules/hydroponics/grown/tea_coffee.dm index d9e775acc0..06cbb1df0c 100644 --- a/code/modules/hydroponics/grown/tea_coffee.dm +++ b/code/modules/hydroponics/grown/tea_coffee.dm @@ -33,7 +33,7 @@ species = "teaastra" plantname = "Tea Astra Plant" product = /obj/item/reagent_containers/food/snacks/grown/tea/astra - mutatelist = list() + mutatelist = list(/obj/item/seeds/tea/catnip) reagents_add = list("synaptizine" = 0.1, "vitamin" = 0.04, "teapowder" = 0.1) rarity = 20 @@ -44,6 +44,24 @@ filling_color = "#4582B4" grind_results = list("teapowder" = 0, "salglu_solution" = 0) +// Kitty drugs +/obj/item/seeds/tea/catnip + name = "pack of catnip seeds" + icon_state = "seed-catnip" + desc = "Long stocks with flowering tips that has a chemical to make feline attracted to it." + species = "catnip" + plantname = "Catnip Plant" + growthstages = 3 + product = /obj/item/reagent_containers/food/snacks/grown/tea/catnip + reagents_add = list("catnip" = 0.1, "vitamin" = 0.06, "teapowder" = 0.3) + rarity = 50 + +/obj/item/reagent_containers/food/snacks/grown/tea/catnip + seed = /obj/item/seeds/tea/catnip + name = "Catnip buds" + icon_state = "catnip_leaves" + filling_color = "#4582B4" + grind_results = list("catnp" = 2, "water" = 1) // Coffee /obj/item/seeds/coffee diff --git a/code/modules/jobs/job_exp.dm b/code/modules/jobs/job_exp.dm index 4b7b175240..f99bf65071 100644 --- a/code/modules/jobs/job_exp.dm +++ b/code/modules/jobs/job_exp.dm @@ -8,6 +8,8 @@ GLOBAL_PROTECT(exp_to_update) return 0 if(!CONFIG_GET(flag/use_exp_tracking)) return 0 + if(!SSdbcore.Connect()) + return 0 if(!exp_requirements || !exp_type) return 0 if(!job_is_xp_locked(src.title)) diff --git a/code/modules/jobs/job_types/job.dm b/code/modules/jobs/job_types/_job.dm similarity index 88% rename from code/modules/jobs/job_types/job.dm rename to code/modules/jobs/job_types/_job.dm index 9549b6100c..2eeffa8b7a 100644 --- a/code/modules/jobs/job_types/job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -1,240 +1,245 @@ -/datum/job - //The name of the job - var/title = "NOPE" - - //Job access. The use of minimal_access or access is determined by a config setting: config.jobs_have_minimal_access - var/list/minimal_access = list() //Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population) - var/list/access = list() //Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!) - - //Determines who can demote this position - var/department_head = list() - - //Tells the given channels that the given mob is the new department head. See communications.dm for valid channels. - var/list/head_announce = null - - //Bitflags for the job - var/flag = 0 - var/department_flag = 0 - - //Players will be allowed to spawn in as jobs that are set to "Station" - var/faction = "None" - - //How many players can be this job - var/total_positions = 0 - - //How many players can spawn in as this job - var/spawn_positions = 0 - - //How many players have this job - var/current_positions = 0 - - //Supervisors, who this person answers to directly - var/supervisors = "" - - //Sellection screen color - var/selection_color = "#ffffff" - - - //If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect. - var/req_admin_notify - - var/custom_spawn_text - - //If you have the use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.) - var/minimal_player_age = 0 - - var/outfit = null - - var/exp_requirements = 0 - - var/exp_type = "" - var/exp_type_department = "" - - //The amount of good boy points playing this role will earn you towards a higher chance to roll antagonist next round - //can be overridden by antag_rep.txt config - var/antag_rep = 10 - - var/list/mind_traits // Traits added to the mind of the mob assigned this job - - var/list/blacklisted_quirks //list of quirk typepaths blacklisted. - -//Only override this proc -//H is usually a human unless an /equip override transformed it -/datum/job/proc/after_spawn(mob/living/H, mob/M, latejoin = FALSE) - //do actions on H but send messages to M as the key may not have been transferred_yet - if(mind_traits) - for(var/t in mind_traits) - ADD_TRAIT(H.mind, t, JOB_TRAIT) - -/datum/job/proc/announce(mob/living/carbon/human/H) - if(head_announce) - announce_head(H, head_announce) - -/datum/job/proc/override_latejoin_spawn(mob/living/carbon/human/H) //Return TRUE to force latejoining to not automatically place the person in latejoin shuttle/whatever. - return FALSE - -//Used for a special check of whether to allow a client to latejoin as this job. -/datum/job/proc/special_check_latejoin(client/C) - return TRUE - -/datum/job/proc/GetAntagRep() - . = CONFIG_GET(keyed_list/antag_rep)[lowertext(title)] - if(. == null) - return antag_rep - -//Don't override this unless the job transforms into a non-human (Silicons do this for example) -/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, datum/outfit/outfit_override = null) - if(!H) - return FALSE - - if(CONFIG_GET(flag/enforce_human_authority) && (title in GLOB.command_positions)) - if(H.dna.species.id != "human") - H.set_species(/datum/species/human) - H.apply_pref_name("human", H.client) - - //Equip the rest of the gear - H.dna.species.before_equip_job(src, H, visualsOnly) - - if(outfit_override || outfit) - H.equipOutfit(outfit_override ? outfit_override : outfit, visualsOnly) - - H.dna.species.after_equip_job(src, H, visualsOnly) - - if(!visualsOnly && announce) - announce(H) - -/datum/job/proc/get_access() - if(!config) //Needed for robots. - return src.minimal_access.Copy() - - . = list() - - if(CONFIG_GET(flag/jobs_have_minimal_access)) - . = src.minimal_access.Copy() - else - . = src.access.Copy() - - if(CONFIG_GET(flag/everyone_has_maint_access)) //Config has global maint access set - . |= list(ACCESS_MAINT_TUNNELS) - -/datum/job/proc/announce_head(var/mob/living/carbon/human/H, var/channels) //tells the given channel that the given mob is the new department head. See communications.dm for valid channels. - if(H && GLOB.announcement_systems.len) - //timer because these should come after the captain announcement - SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/addtimer, CALLBACK(pick(GLOB.announcement_systems), /obj/machinery/announcement_system/proc/announce, "NEWHEAD", H.real_name, H.job, channels), 1)) - -//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 -/datum/job/proc/player_old_enough(client/C) - if(available_in_days(C) == 0) - return TRUE //Available in 0 days = available right now = player is old enough to play. - return FALSE - - -/datum/job/proc/available_in_days(client/C) - if(!C) - return 0 - if(!CONFIG_GET(flag/use_age_restriction_for_jobs)) - return 0 - if(C.prefs.db_flags & DB_FLAG_EXEMPT) - return 0 - if(!isnum(C.player_age)) - return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced - if(!isnum(minimal_player_age)) - return 0 - - return max(0, minimal_player_age - C.player_age) - -/datum/job/proc/config_check() - return TRUE - -/datum/job/proc/map_check() - return TRUE - - -/datum/outfit/job - name = "Standard Gear" - - var/jobtype = null - - uniform = /obj/item/clothing/under/color/grey - id = /obj/item/card/id - ears = /obj/item/radio/headset - belt = /obj/item/pda - back = /obj/item/storage/backpack - shoes = /obj/item/clothing/shoes/sneakers/black - - var/backpack = /obj/item/storage/backpack - var/satchel = /obj/item/storage/backpack/satchel - var/duffelbag = /obj/item/storage/backpack/duffelbag - var/box = /obj/item/storage/box/survival - - var/pda_slot = SLOT_BELT - -/datum/outfit/job/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - switch(H.backbag) - if(GBACKPACK) - back = /obj/item/storage/backpack //Grey backpack - if(GSATCHEL) - back = /obj/item/storage/backpack/satchel //Grey satchel - if(GDUFFELBAG) - back = /obj/item/storage/backpack/duffelbag //Grey Duffel bag - if(LSATCHEL) - back = /obj/item/storage/backpack/satchel/leather //Leather Satchel - if(DSATCHEL) - back = satchel //Department satchel - if(DDUFFELBAG) - back = duffelbag //Department duffel bag - else - back = backpack //Department backpack - - if(box) - if(!backpack_contents) - backpack_contents = list() - backpack_contents.Insert(1, box) // Box always takes a first slot in backpack - backpack_contents[box] = 1 - - //converts the uniform string into the path we'll wear, whether it's the skirt or regular variant - var/holder - if(H.jumpsuit_style == PREF_SKIRT) - holder = "[uniform]/skirt" - if(!text2path(holder)) - holder = "[uniform]" - else - holder = "[uniform]" - uniform = text2path(holder) - -/datum/outfit/job/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - if(visualsOnly) - return - - var/datum/job/J = SSjob.GetJobType(jobtype) - if(!J) - J = SSjob.GetJob(H.job) - - if(H.nameless && J.dresscodecompliant) - if(J.title in GLOB.command_positions) - H.real_name = J.title - else - H.real_name = "[J.title] #[rand(10000, 99999)]" - - var/obj/item/card/id/C = H.wear_id - if(istype(C)) - C.access = J.get_access() - shuffle_inplace(C.access) // Shuffle access list to make NTNet passkeys less predictable - C.registered_name = H.real_name - C.assignment = J.title - C.update_label() - H.sec_hud_set_ID() - - var/obj/item/pda/PDA = H.get_item_by_slot(pda_slot) - if(istype(PDA)) - PDA.owner = H.real_name - PDA.ownjob = J.title - PDA.update_label() - -/datum/outfit/job/get_chameleon_disguise_info() - var/list/types = ..() - types -= /obj/item/storage/backpack //otherwise this will override the actual backpacks - types += backpack - types += satchel - types += duffelbag - return types +/datum/job + //The name of the job , used for preferences, bans and more. Make sure you know what you're doing before changing this. + var/title = "NOPE" + + //Job access. The use of minimal_access or access is determined by a config setting: config.jobs_have_minimal_access + var/list/minimal_access = list() //Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population) + var/list/access = list() //Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!) + + //Determines who can demote this position + var/department_head = list() + + //Tells the given channels that the given mob is the new department head. See communications.dm for valid channels. + var/list/head_announce = null + + //Bitflags for the job + var/flag = NONE //Deprecated + var/department_flag = NONE //Deprecated +// var/auto_deadmin_role_flags = NONE + + //Players will be allowed to spawn in as jobs that are set to "Station" + var/faction = "None" + + //How many players can be this job + var/total_positions = 0 + + //How many players can spawn in as this job + var/spawn_positions = 0 + + //How many players have this job + var/current_positions = 0 + + //Supervisors, who this person answers to directly + var/supervisors = "" + + //Sellection screen color + var/selection_color = "#ffffff" + + + //If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect. + var/req_admin_notify + + // This is for Citadel specific tweaks to job notices. + var/custom_spawn_text + + //If you have the use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.) + var/minimal_player_age = 0 + + var/outfit = null + + var/exp_requirements = 0 + + var/exp_type = "" + var/exp_type_department = "" + + //The amount of good boy points playing this role will earn you towards a higher chance to roll antagonist next round + //can be overridden by antag_rep.txt config + var/antag_rep = 10 + + var/list/mind_traits // Traits added to the mind of the mob assigned this job + var/list/blacklisted_quirks //list of quirk typepaths blacklisted. + + var/display_order = JOB_DISPLAY_ORDER_DEFAULT + +//Only override this proc +//H is usually a human unless an /equip override transformed it +/datum/job/proc/after_spawn(mob/living/H, mob/M, latejoin = FALSE) + //do actions on H but send messages to M as the key may not have been transferred_yet + if(mind_traits) + for(var/t in mind_traits) + ADD_TRAIT(H.mind, t, JOB_TRAIT) + +/datum/job/proc/announce(mob/living/carbon/human/H) + if(head_announce) + announce_head(H, head_announce) + +/datum/job/proc/override_latejoin_spawn(mob/living/carbon/human/H) //Return TRUE to force latejoining to not automatically place the person in latejoin shuttle/whatever. + return FALSE + +//Used for a special check of whether to allow a client to latejoin as this job. +/datum/job/proc/special_check_latejoin(client/C) + return TRUE + +/datum/job/proc/GetAntagRep() + . = CONFIG_GET(keyed_list/antag_rep)[lowertext(title)] + if(. == null) + return antag_rep + +//Don't override this unless the job transforms into a non-human (Silicons do this for example) +/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, datum/outfit/outfit_override = null, client/preference_source) + if(!H) + return FALSE + + if(CONFIG_GET(flag/enforce_human_authority) && (title in GLOB.command_positions)) + if(H.dna.species.id != "human") + H.set_species(/datum/species/human) + H.apply_pref_name("human", preference_source) + + //Equip the rest of the gear + H.dna.species.before_equip_job(src, H, visualsOnly) + + if(outfit_override || outfit) + H.equipOutfit(outfit_override ? outfit_override : outfit, visualsOnly) + + H.dna.species.after_equip_job(src, H, visualsOnly) + + if(!visualsOnly && announce) + announce(H) + +/datum/job/proc/get_access() + if(!config) //Needed for robots. + return src.minimal_access.Copy() + + . = list() + + if(CONFIG_GET(flag/jobs_have_minimal_access)) + . = src.minimal_access.Copy() + else + . = src.access.Copy() + + if(CONFIG_GET(flag/everyone_has_maint_access)) //Config has global maint access set + . |= list(ACCESS_MAINT_TUNNELS) + +/datum/job/proc/announce_head(var/mob/living/carbon/human/H, var/channels) //tells the given channel that the given mob is the new department head. See communications.dm for valid channels. + if(H && GLOB.announcement_systems.len) + //timer because these should come after the captain announcement + SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/addtimer, CALLBACK(pick(GLOB.announcement_systems), /obj/machinery/announcement_system/proc/announce, "NEWHEAD", H.real_name, H.job, channels), 1)) + +//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 +/datum/job/proc/player_old_enough(client/C) + if(available_in_days(C) == 0) + return TRUE //Available in 0 days = available right now = player is old enough to play. + return FALSE + + +/datum/job/proc/available_in_days(client/C) + if(!C) + return 0 + if(!CONFIG_GET(flag/use_age_restriction_for_jobs)) + return 0 + if(!SSdbcore.Connect()) + return 0 //Without a database connection we can't get a player's age so we'll assume they're old enough for all jobs + if(C.prefs.db_flags & DB_FLAG_EXEMPT) + return 0 + if(!isnum(minimal_player_age)) + return 0 + + return max(0, minimal_player_age - C.player_age) + +/datum/job/proc/config_check() + return TRUE + +/datum/job/proc/map_check() + return TRUE + +/datum/job/proc/radio_help_message(mob/M) + to_chat(M, "Prefix your message with :h to speak on your department's radio. To see other prefixes, look closely at your headset.") + +/datum/outfit/job + name = "Standard Gear" + + var/jobtype = null + + uniform = /obj/item/clothing/under/color/grey + id = /obj/item/card/id + ears = /obj/item/radio/headset + belt = /obj/item/pda + back = /obj/item/storage/backpack + shoes = /obj/item/clothing/shoes/sneakers/black + box = /obj/item/storage/box/survival + + var/backpack = /obj/item/storage/backpack + var/satchel = /obj/item/storage/backpack/satchel + var/duffelbag = /obj/item/storage/backpack/duffelbag + + var/pda_slot = SLOT_BELT + +/datum/outfit/job/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + switch(H.backbag) + if(GBACKPACK) + back = /obj/item/storage/backpack //Grey backpack + if(GSATCHEL) + back = /obj/item/storage/backpack/satchel //Grey satchel + if(GDUFFELBAG) + back = /obj/item/storage/backpack/duffelbag //Grey Duffel bag + if(LSATCHEL) + back = /obj/item/storage/backpack/satchel/leather //Leather Satchel + if(DSATCHEL) + back = satchel //Department satchel + if(DDUFFELBAG) + back = duffelbag //Department duffel bag + else + back = backpack //Department backpack + + //converts the uniform string into the path we'll wear, whether it's the skirt or regular variant + var/holder + if(H.jumpsuit_style == PREF_SKIRT) + holder = "[uniform]/skirt" + if(!text2path(holder)) + holder = "[uniform]" + else + holder = "[uniform]" + uniform = text2path(holder) + +/datum/outfit/job/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + if(visualsOnly) + return + + var/datum/job/J = SSjob.GetJobType(jobtype) + if(!J) + J = SSjob.GetJob(H.job) + + if(H.nameless && J.dresscodecompliant) + if(J.title in GLOB.command_positions) + H.real_name = J.title + else + H.real_name = "[J.title] #[rand(10000, 99999)]" + + var/obj/item/card/id/C = H.wear_id + if(istype(C)) + C.access = J.get_access() + shuffle_inplace(C.access) // Shuffle access list to make NTNet passkeys less predictable + C.registered_name = H.real_name + C.assignment = J.title + C.update_label() + H.sec_hud_set_ID() + + var/obj/item/pda/PDA = H.get_item_by_slot(pda_slot) + if(istype(PDA)) + PDA.owner = H.real_name + PDA.ownjob = J.title + PDA.update_label() + +/datum/outfit/job/get_chameleon_disguise_info() + var/list/types = ..() + types -= /obj/item/storage/backpack //otherwise this will override the actual backpacks + types += backpack + types += satchel + types += duffelbag + return types + +//Warden and regular officers add this result to their get_access() +/datum/job/proc/check_config_for_sec_maint() + if(CONFIG_GET(flag/security_has_maint_access)) + return list(ACCESS_MAINT_TUNNELS) + return list() diff --git a/code/modules/jobs/job_types/silicon.dm b/code/modules/jobs/job_types/ai.dm similarity index 71% rename from code/modules/jobs/job_types/silicon.dm rename to code/modules/jobs/job_types/ai.dm index ab963eb8f3..4bcfab5836 100644 --- a/code/modules/jobs/job_types/silicon.dm +++ b/code/modules/jobs/job_types/ai.dm @@ -1,90 +1,69 @@ -/* -AI -*/ -/datum/job/ai - title = "AI" - flag = AI_JF - department_flag = ENGSEC - faction = "Station" - total_positions = 1 - spawn_positions = 1 - selection_color = "#ccffcc" - supervisors = "your laws" - req_admin_notify = TRUE - minimal_player_age = 30 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - exp_type_department = EXP_TYPE_SILICON - var/do_special_check = TRUE - -/datum/job/ai/equip(mob/living/carbon/human/H, visualsOnly, announce, latejoin, outfit_override) - . = H.AIize(latejoin) - -/datum/job/ai/after_spawn(mob/H, mob/M, latejoin) - . = ..() - if(latejoin) - var/obj/structure/AIcore/latejoin_inactive/lateJoinCore - for(var/obj/structure/AIcore/latejoin_inactive/P in GLOB.latejoin_ai_cores) - if(P.is_available()) - lateJoinCore = P - GLOB.latejoin_ai_cores -= P - break - if(lateJoinCore) - lateJoinCore.available = FALSE - H.forceMove(lateJoinCore.loc) - qdel(lateJoinCore) - var/mob/living/silicon/ai/AI = H - AI.apply_pref_name("ai", M.client) //If this runtimes oh well jobcode is fucked. - AI.set_core_display_icon(null, M.client) - - //we may have been created after our borg - if(SSticker.current_state == GAME_STATE_SETTING_UP) - for(var/mob/living/silicon/robot/R in GLOB.silicon_mobs) - if(!R.connected_ai) - R.TryConnectToAI() - - if(latejoin) - announce(AI) - -/datum/job/ai/override_latejoin_spawn() - return TRUE - -/datum/job/ai/special_check_latejoin(client/C) - if(!do_special_check) - return TRUE - for(var/i in GLOB.latejoin_ai_cores) - var/obj/structure/AIcore/latejoin_inactive/LAI = i - if(istype(LAI)) - if(LAI.is_available()) - return TRUE - return FALSE - -/datum/job/ai/announce(mob/living/silicon/ai/AI) - . = ..() - SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "[AI] has been downloaded to an empty bluespace-networked AI core at [AREACOORD(AI)].")) - -/datum/job/ai/config_check() - return CONFIG_GET(flag/allow_ai) - -/* -Cyborg -*/ -/datum/job/cyborg - title = "Cyborg" - flag = CYBORG - department_flag = ENGSEC - faction = "Station" - total_positions = 0 - spawn_positions = 1 - supervisors = "your laws and the AI" //Nodrak - selection_color = "#ddffdd" - minimal_player_age = 21 - exp_requirements = 120 - exp_type = EXP_TYPE_CREW - -/datum/job/cyborg/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, outfit_override = null) - return H.Robotize(FALSE, latejoin) - -/datum/job/cyborg/after_spawn(mob/living/silicon/robot/R, mob/M) - R.updatename(M.client) - R.gender = NEUTER +/datum/job/ai + title = "AI" + flag = AI_JF +// auto_deadmin_role_flags = DEADMIN_POSITION_SILICON + department_flag = ENGSEC + faction = "Station" + total_positions = 1 + spawn_positions = 1 + selection_color = "#ccffcc" + supervisors = "your laws" + req_admin_notify = TRUE + minimal_player_age = 30 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + exp_type_department = EXP_TYPE_SILICON + display_order = JOB_DISPLAY_ORDER_AI + var/do_special_check = TRUE + +/datum/job/ai/equip(mob/living/carbon/human/H, visualsOnly, announce, latejoin, datum/outfit/outfit_override, client/preference_source = null) + if(visualsOnly) + CRASH("dynamic preview is unsupported") + . = H.AIize(latejoin,preference_source) + +/datum/job/ai/after_spawn(mob/H, mob/M, latejoin) + . = ..() + if(latejoin) + var/obj/structure/AIcore/latejoin_inactive/lateJoinCore + for(var/obj/structure/AIcore/latejoin_inactive/P in GLOB.latejoin_ai_cores) + if(P.is_available()) + lateJoinCore = P + GLOB.latejoin_ai_cores -= P + break + if(lateJoinCore) + lateJoinCore.available = FALSE + H.forceMove(lateJoinCore.loc) + qdel(lateJoinCore) + var/mob/living/silicon/ai/AI = H + AI.apply_pref_name("ai", M.client) //If this runtimes oh well jobcode is fucked. + AI.set_core_display_icon(null, M.client) + + //we may have been created after our borg + if(SSticker.current_state == GAME_STATE_SETTING_UP) + for(var/mob/living/silicon/robot/R in GLOB.silicon_mobs) + if(!R.connected_ai) + R.TryConnectToAI() + + if(latejoin) + announce(AI) + +/datum/job/ai/override_latejoin_spawn() + return TRUE + +/datum/job/ai/special_check_latejoin(client/C) + if(!do_special_check) + return TRUE + for(var/i in GLOB.latejoin_ai_cores) + var/obj/structure/AIcore/latejoin_inactive/LAI = i + if(istype(LAI)) + if(LAI.is_available()) + return TRUE + return FALSE + +/datum/job/ai/announce(mob/living/silicon/ai/AI) + . = ..() + SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "[AI] has been downloaded to an empty bluespace-networked AI core at [AREACOORD(AI)].")) + +/datum/job/ai/config_check() + return CONFIG_GET(flag/allow_ai) + diff --git a/code/modules/jobs/job_types/assistant.dm b/code/modules/jobs/job_types/assistant.dm index ce6eea97b0..c04560f849 100644 --- a/code/modules/jobs/job_types/assistant.dm +++ b/code/modules/jobs/job_types/assistant.dm @@ -14,7 +14,7 @@ Assistant minimal_access = list() //See /datum/job/assistant/get_access() outfit = /datum/outfit/job/assistant antag_rep = 7 - + display_order = JOB_DISPLAY_ORDER_ASSISTANT /datum/job/assistant/get_access() if(CONFIG_GET(flag/assistants_have_maint_access) || !CONFIG_GET(flag/jobs_have_minimal_access)) //Config has assistant maint access set diff --git a/code/modules/jobs/job_types/atmospheric_technician.dm b/code/modules/jobs/job_types/atmospheric_technician.dm new file mode 100644 index 0000000000..93775beca9 --- /dev/null +++ b/code/modules/jobs/job_types/atmospheric_technician.dm @@ -0,0 +1,44 @@ +/datum/job/atmos + title = "Atmospheric Technician" + flag = ATMOSTECH + department_head = list("Chief Engineer") + department_flag = ENGSEC + faction = "Station" + total_positions = 3 + spawn_positions = 2 + supervisors = "the chief engineer" + selection_color = "#ff9b3d" + exp_requirements = 60 + exp_type = EXP_TYPE_CREW + + outfit = /datum/outfit/job/atmos + + access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, + ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_ATMOSPHERICS, ACCESS_MAINT_TUNNELS, ACCESS_CONSTRUCTION, ACCESS_MINERAL_STOREROOM) + display_order = JOB_DISPLAY_ORDER_ATMOSPHERIC_TECHNICIAN + +/datum/outfit/job/atmos + name = "Atmospheric Technician" + jobtype = /datum/job/atmos + + belt = /obj/item/storage/belt/utility/atmostech + l_pocket = /obj/item/pda/atmos + ears = /obj/item/radio/headset/headset_eng + uniform = /obj/item/clothing/under/rank/atmospheric_technician + r_pocket = /obj/item/analyzer + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel/eng + duffelbag = /obj/item/storage/backpack/duffelbag/engineering + box = /obj/item/storage/box/engineer + pda_slot = SLOT_L_STORE + backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1) + +/datum/outfit/job/atmos/rig + name = "Atmospheric Technician (Hardsuit)" + + mask = /obj/item/clothing/mask/gas + suit = /obj/item/clothing/suit/space/hardsuit/engine/atmos + suit_store = /obj/item/tank/internals/oxygen + internals_slot = SLOT_S_STORE diff --git a/code/modules/jobs/job_types/bartender.dm b/code/modules/jobs/job_types/bartender.dm new file mode 100644 index 0000000000..0ace449757 --- /dev/null +++ b/code/modules/jobs/job_types/bartender.dm @@ -0,0 +1,30 @@ +/datum/job/bartender + title = "Bartender" + flag = BARTENDER + department_head = list("Head of Personnel") + department_flag = CIVILIAN + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the head of personnel" + selection_color = "#bbe291" + exp_type_department = EXP_TYPE_SERVICE // This is so the jobs menu can work properly + + outfit = /datum/outfit/job/bartender + + access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_BAR, ACCESS_MINERAL_STOREROOM) + display_order = JOB_DISPLAY_ORDER_BARTENDER + +/datum/outfit/job/bartender + name = "Bartender" + jobtype = /datum/job/bartender + + glasses = /obj/item/clothing/glasses/sunglasses/reagent + belt = /obj/item/pda/bar + ears = /obj/item/radio/headset/headset_srv + uniform = /obj/item/clothing/under/rank/bartender + suit = /obj/item/clothing/suit/armor/vest + backpack_contents = list(/obj/item/storage/box/beanbag=1,/obj/item/book/granter/action/drink_fling=1) + shoes = /obj/item/clothing/shoes/laceup + diff --git a/code/modules/jobs/job_types/botanist.dm b/code/modules/jobs/job_types/botanist.dm new file mode 100644 index 0000000000..e6338d9b0a --- /dev/null +++ b/code/modules/jobs/job_types/botanist.dm @@ -0,0 +1,32 @@ +/datum/job/hydro + title = "Botanist" + flag = BOTANIST + department_head = list("Head of Personnel") + department_flag = CIVILIAN + faction = "Station" + total_positions = 3 + spawn_positions = 2 + supervisors = "the head of personnel" + selection_color = "#bbe291" + + outfit = /datum/outfit/job/botanist + + access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_HYDROPONICS, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) + display_order = JOB_DISPLAY_ORDER_BOTANIST + +/datum/outfit/job/botanist + name = "Botanist" + jobtype = /datum/job/hydro + + belt = /obj/item/pda/botanist + ears = /obj/item/radio/headset/headset_srv + uniform = /obj/item/clothing/under/rank/hydroponics + suit = /obj/item/clothing/suit/apron + gloves =/obj/item/clothing/gloves/botanic_leather + suit_store = /obj/item/plant_analyzer + + backpack = /obj/item/storage/backpack/botany + satchel = /obj/item/storage/backpack/satchel/hyd + + diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm old mode 100755 new mode 100644 index b3c952f536..fea8557b40 --- a/code/modules/jobs/job_types/captain.dm +++ b/code/modules/jobs/job_types/captain.dm @@ -1,114 +1,66 @@ -/* -Captain -*/ -/datum/job/captain - title = "Captain" - flag = CAPTAIN - department_head = list("CentCom") - department_flag = ENGSEC - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "Nanotrasen officials and Space law" - selection_color = "#ccccff" - req_admin_notify = 1 - minimal_player_age = 14 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - - outfit = /datum/outfit/job/captain - - access = list() //See get_access() - minimal_access = list() //See get_access() - - blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity) - -/datum/job/captain/get_access() - return get_all_accesses() - -/datum/job/captain/announce(mob/living/carbon/human/H) - ..() - SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Captain [H.nameless ? "" : "[H.real_name] "]on deck!")) - -/datum/outfit/job/captain - name = "Captain" - jobtype = /datum/job/captain - - id = /obj/item/card/id/gold - belt = /obj/item/pda/captain - glasses = /obj/item/clothing/glasses/sunglasses - ears = /obj/item/radio/headset/heads/captain/alt - gloves = /obj/item/clothing/gloves/color/captain - uniform = /obj/item/clothing/under/rank/captain - suit = /obj/item/clothing/suit/armor/vest/capcarapace - shoes = /obj/item/clothing/shoes/sneakers/brown - head = /obj/item/clothing/head/caphat - backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1, /obj/item/station_charter=1) - - backpack = /obj/item/storage/backpack/captain - satchel = /obj/item/storage/backpack/satchel/cap - duffelbag = /obj/item/storage/backpack/duffelbag/captain - - implants = list(/obj/item/implant/mindshield) - accessory = /obj/item/clothing/accessory/medal/gold/captain - - chameleon_extras = list(/obj/item/gun/energy/e_gun, /obj/item/stamp/captain) - -/datum/outfit/job/captain/hardsuit - name = "Captain (Hardsuit)" - - mask = /obj/item/clothing/mask/gas/sechailer - suit = /obj/item/clothing/suit/space/hardsuit/captain - suit_store = /obj/item/tank/internals/oxygen - -/* -Head of Personnel -*/ -/datum/job/hop - title = "Head of Personnel" - flag = HOP - department_head = list("Captain") - department_flag = CIVILIAN - head_announce = list(RADIO_CHANNEL_SERVICE) - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the captain" - selection_color = "#ddddff" - req_admin_notify = 1 - minimal_player_age = 10 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - exp_type_department = EXP_TYPE_SUPPLY - - outfit = /datum/outfit/job/hop - - access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_COURT, ACCESS_WEAPONS, - ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS, - ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, - ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, - ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_VAULT, ACCESS_MINING_STATION, - ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_COURT, ACCESS_WEAPONS, - ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS, - ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, - ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, - ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_VAULT, ACCESS_MINING_STATION, - ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM) - - blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/prosopagnosia, /datum/quirk/insanity) - -/datum/outfit/job/hop - name = "Head of Personnel" - jobtype = /datum/job/hop - - id = /obj/item/card/id/silver - belt = /obj/item/pda/heads/hop - ears = /obj/item/radio/headset/heads/hop - uniform = /obj/item/clothing/under/rank/head_of_personnel - shoes = /obj/item/clothing/shoes/sneakers/brown - head = /obj/item/clothing/head/hopcap - backpack_contents = list(/obj/item/storage/box/ids=1,\ - /obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/preset/advanced = 1) - - chameleon_extras = list(/obj/item/gun/energy/e_gun, /obj/item/stamp/hop) +/datum/job/captain + title = "Captain" + flag = CAPTAIN +// auto_deadmin_role_flags = DEADMIN_POSITION_HEAD|DEADMIN_POSITION_SECURITY //:eyes: + department_head = list("CentCom") + department_flag = ENGSEC + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "Nanotrasen officials and Space law" + selection_color = "#aac1ee" + req_admin_notify = 1 + minimal_player_age = 14 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + exp_type_department = EXP_TYPE_COMMAND + + outfit = /datum/outfit/job/captain + + access = list() //See get_access() + minimal_access = list() //See get_access() + + mind_traits = list(TRAIT_CAPTAIN_METABOLISM) +// mind_traits = list(TRAIT_DISK_VERIFIER) + + display_order = JOB_DISPLAY_ORDER_CAPTAIN + + blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity) + +/datum/job/captain/get_access() + return get_all_accesses() + +/datum/job/captain/announce(mob/living/carbon/human/H) + ..() + SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Captain [H.nameless ? "" : "[H.real_name] "]on deck!")) + +/datum/outfit/job/captain + name = "Captain" + jobtype = /datum/job/captain + + id = /obj/item/card/id/gold + belt = /obj/item/pda/captain + glasses = /obj/item/clothing/glasses/sunglasses + ears = /obj/item/radio/headset/heads/captain/alt + gloves = /obj/item/clothing/gloves/color/captain + uniform = /obj/item/clothing/under/rank/captain + suit = /obj/item/clothing/suit/armor/vest/capcarapace + shoes = /obj/item/clothing/shoes/sneakers/brown + head = /obj/item/clothing/head/caphat + backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1, /obj/item/station_charter=1) + + backpack = /obj/item/storage/backpack/captain + satchel = /obj/item/storage/backpack/satchel/cap + duffelbag = /obj/item/storage/backpack/duffelbag/captain + + implants = list(/obj/item/implant/mindshield) + accessory = /obj/item/clothing/accessory/medal/gold/captain + + chameleon_extras = list(/obj/item/gun/energy/e_gun, /obj/item/stamp/captain) + +/datum/outfit/job/captain/hardsuit + name = "Captain (Hardsuit)" + + mask = /obj/item/clothing/mask/gas/sechailer + suit = /obj/item/clothing/suit/space/hardsuit/captain + suit_store = /obj/item/tank/internals/oxygen \ No newline at end of file diff --git a/code/modules/jobs/job_types/cargo_service.dm b/code/modules/jobs/job_types/cargo_service.dm deleted file mode 100644 index 8e24ece655..0000000000 --- a/code/modules/jobs/job_types/cargo_service.dm +++ /dev/null @@ -1,293 +0,0 @@ -/* -Quartermaster -*/ -/datum/job/qm - title = "Quartermaster" - flag = QUARTERMASTER - department_head = list("Head of Personnel") - department_flag = CIVILIAN - head_announce = list(RADIO_CHANNEL_SUPPLY) - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the head of personnel" - selection_color = "#d7b088" - - outfit = /datum/outfit/job/quartermaster - - access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_VAULT) - minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_VAULT) - - blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity) - -/datum/outfit/job/quartermaster - name = "Quartermaster" - jobtype = /datum/job/qm - - belt = /obj/item/pda/quartermaster - ears = /obj/item/radio/headset/headset_cargo - uniform = /obj/item/clothing/under/rank/cargo - shoes = /obj/item/clothing/shoes/sneakers/brown - glasses = /obj/item/clothing/glasses/sunglasses - l_hand = /obj/item/clipboard - - chameleon_extras = /obj/item/stamp/qm - -/* -Cargo Technician -*/ -/datum/job/cargo_tech - title = "Cargo Technician" - flag = CARGOTECH - department_head = list("Head of Personnel") - department_flag = CIVILIAN - faction = "Station" - total_positions = 3 - spawn_positions = 2 - supervisors = "the quartermaster and the head of personnel" - selection_color = "#dcba97" - - outfit = /datum/outfit/job/cargo_tech - - access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM) - -/datum/outfit/job/cargo_tech - name = "Cargo Technician" - jobtype = /datum/job/cargo_tech - - belt = /obj/item/pda/cargo - ears = /obj/item/radio/headset/headset_cargo - uniform = /obj/item/clothing/under/rank/cargotech - l_hand = /obj/item/export_scanner - -/* -Shaft Miner -*/ -/datum/job/mining - title = "Shaft Miner" - flag = MINER - department_head = list("Head of Personnel") - department_flag = CIVILIAN - faction = "Station" - total_positions = 3 - spawn_positions = 3 - supervisors = "the quartermaster and the head of personnel" - selection_color = "#dcba97" - custom_spawn_text = "Remember, you are a miner, not a hunter. Hunting monsters is not a requirement of your job, the only requirement of your job is to provide materials for the station. Don't be afraid to run away if you're inexperienced with fighting the mining area's locals." - - outfit = /datum/outfit/job/miner - - access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM) - -/datum/outfit/job/miner - name = "Shaft Miner (Lavaland)" - jobtype = /datum/job/mining - - belt = /obj/item/pda/shaftminer - ears = /obj/item/radio/headset/headset_cargo/mining - shoes = /obj/item/clothing/shoes/workboots/mining - gloves = /obj/item/clothing/gloves/color/black - uniform = /obj/item/clothing/under/rank/miner/lavaland - l_pocket = /obj/item/reagent_containers/hypospray/medipen/survival - r_pocket = /obj/item/storage/bag/ore //causes issues if spawned in backpack - backpack_contents = list( - /obj/item/flashlight/seclite=1,\ - /obj/item/kitchen/knife/combat/survival=1,\ - /obj/item/mining_voucher=1,\ - /obj/item/suit_voucher=1,\ - /obj/item/stack/marker_beacon/ten=1) - - backpack = /obj/item/storage/backpack/explorer - satchel = /obj/item/storage/backpack/satchel/explorer - duffelbag = /obj/item/storage/backpack/duffelbag - box = /obj/item/storage/box/survival_mining - - chameleon_extras = /obj/item/gun/energy/kinetic_accelerator - -/datum/outfit/job/miner/asteroid - name = "Shaft Miner (Asteroid)" - uniform = /obj/item/clothing/under/rank/miner - shoes = /obj/item/clothing/shoes/workboots - -/datum/outfit/job/miner/equipped - name = "Shaft Miner (Lavaland + Equipment)" - suit = /obj/item/clothing/suit/hooded/explorer/standard - mask = /obj/item/clothing/mask/gas/explorer - glasses = /obj/item/clothing/glasses/meson - suit_store = /obj/item/tank/internals/oxygen - internals_slot = SLOT_S_STORE - backpack_contents = list( - /obj/item/flashlight/seclite=1,\ - /obj/item/kitchen/knife/combat/survival=1, - /obj/item/mining_voucher=1, - /obj/item/t_scanner/adv_mining_scanner/lesser=1, - /obj/item/gun/energy/kinetic_accelerator=1,\ - /obj/item/stack/marker_beacon/ten=1) - -/datum/outfit/job/miner/equipped/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - ..() - if(visualsOnly) - return - if(istype(H.wear_suit, /obj/item/clothing/suit/hooded)) - var/obj/item/clothing/suit/hooded/S = H.wear_suit - S.ToggleHood() - -/datum/outfit/job/miner/equipped/hardsuit - name = "Shaft Miner (Equipment + Hardsuit)" - suit = /obj/item/clothing/suit/space/hardsuit/mining - mask = /obj/item/clothing/mask/breath - - -/* -Bartender -*/ -/datum/job/bartender - title = "Bartender" - flag = BARTENDER - department_head = list("Head of Personnel") - department_flag = CIVILIAN - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the head of personnel" - selection_color = "#bbe291" - - outfit = /datum/outfit/job/bartender - - access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_BAR, ACCESS_MINERAL_STOREROOM) - - -/datum/outfit/job/bartender - name = "Bartender" - jobtype = /datum/job/bartender - - glasses = /obj/item/clothing/glasses/sunglasses/reagent - belt = /obj/item/pda/bar - ears = /obj/item/radio/headset/headset_srv - uniform = /obj/item/clothing/under/rank/bartender - suit = /obj/item/clothing/suit/armor/vest - backpack_contents = list(/obj/item/storage/box/beanbag=1,/obj/item/book/granter/action/drink_fling=1) - shoes = /obj/item/clothing/shoes/laceup - -/* -Cook -*/ -/datum/job/cook - title = "Cook" - flag = COOK - department_head = list("Head of Personnel") - department_flag = CIVILIAN - faction = "Station" - total_positions = 2 - spawn_positions = 1 - supervisors = "the head of personnel" - selection_color = "#bbe291" - var/cooks = 0 //Counts cooks amount - - outfit = /datum/outfit/job/cook - - access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) - -/datum/outfit/job/cook - name = "Cook" - jobtype = /datum/job/cook - - belt = /obj/item/pda/cook - ears = /obj/item/radio/headset/headset_srv - uniform = /obj/item/clothing/under/rank/chef - suit = /obj/item/clothing/suit/toggle/chef - head = /obj/item/clothing/head/chefhat - mask = /obj/item/clothing/mask/fakemoustache/italian - backpack_contents = list(/obj/item/sharpener = 1) - -/datum/outfit/job/cook/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - ..() - var/datum/job/cook/J = SSjob.GetJobType(jobtype) - if(J) // Fix for runtime caused by invalid job being passed - if(J.cooks>0)//Cooks - suit = /obj/item/clothing/suit/apron/chef - head = /obj/item/clothing/head/soft/mime - if(!visualsOnly) - J.cooks++ - -/datum/outfit/job/cook/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - ..() - if(visualsOnly) - return - var/list/possible_boxes = subtypesof(/obj/item/storage/box/ingredients) - var/chosen_box = pick(possible_boxes) - var/obj/item/storage/box/I = new chosen_box(src) - H.equip_to_slot_or_del(I,SLOT_IN_BACKPACK) - var/datum/martial_art/cqc/under_siege/justacook = new - justacook.teach(H) - -/* -Botanist -*/ -/datum/job/hydro - title = "Botanist" - flag = BOTANIST - department_head = list("Head of Personnel") - department_flag = CIVILIAN - faction = "Station" - total_positions = 3 - spawn_positions = 2 - supervisors = "the head of personnel" - selection_color = "#bbe291" - - outfit = /datum/outfit/job/botanist - - access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_HYDROPONICS, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) - // Removed tox and chem access because STOP PISSING OFF THE CHEMIST GUYS - // Removed medical access because WHAT THE FUCK YOU AREN'T A DOCTOR YOU GROW WHEAT - // Given Morgue access because they have a viable means of cloning. - - -/datum/outfit/job/botanist - name = "Botanist" - jobtype = /datum/job/hydro - - belt = /obj/item/pda/botanist - ears = /obj/item/radio/headset/headset_srv - uniform = /obj/item/clothing/under/rank/hydroponics - suit = /obj/item/clothing/suit/apron - gloves =/obj/item/clothing/gloves/botanic_leather - suit_store = /obj/item/plant_analyzer - - backpack = /obj/item/storage/backpack/botany - satchel = /obj/item/storage/backpack/satchel/hyd - - -/* -Janitor -*/ -/datum/job/janitor - title = "Janitor" - flag = JANITOR - department_head = list("Head of Personnel") - department_flag = CIVILIAN - faction = "Station" - total_positions = 2 - spawn_positions = 1 - supervisors = "the head of personnel" - selection_color = "#bbe291" - var/global/janitors = 0 - - outfit = /datum/outfit/job/janitor - - access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) - -/datum/outfit/job/janitor - name = "Janitor" - jobtype = /datum/job/janitor - - belt = /obj/item/pda/janitor - ears = /obj/item/radio/headset/headset_srv - uniform = /obj/item/clothing/under/rank/janitor - backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1) diff --git a/code/modules/jobs/job_types/cargo_technician.dm b/code/modules/jobs/job_types/cargo_technician.dm new file mode 100644 index 0000000000..3ceb29bae2 --- /dev/null +++ b/code/modules/jobs/job_types/cargo_technician.dm @@ -0,0 +1,27 @@ +/datum/job/cargo_tech + title = "Cargo Technician" + flag = CARGOTECH + department_head = list("Quartermaster") + department_flag = CIVILIAN + faction = "Station" + total_positions = 3 + spawn_positions = 2 + supervisors = "the quartermaster" + selection_color = "#ca8f55" + + outfit = /datum/outfit/job/cargo_tech + + access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_QM, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_CARGO, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM) + + display_order = JOB_DISPLAY_ORDER_CARGO_TECHNICIAN + +/datum/outfit/job/cargo_tech + name = "Cargo Technician" + jobtype = /datum/job/cargo_tech + + belt = /obj/item/pda/cargo + ears = /obj/item/radio/headset/headset_cargo + uniform = /obj/item/clothing/under/rank/cargotech + l_hand = /obj/item/export_scanner + diff --git a/code/modules/jobs/job_types/civilian_chaplain.dm b/code/modules/jobs/job_types/chaplain.dm similarity index 65% rename from code/modules/jobs/job_types/civilian_chaplain.dm rename to code/modules/jobs/job_types/chaplain.dm index 2d190cfe60..97b1edc8c2 100644 --- a/code/modules/jobs/job_types/civilian_chaplain.dm +++ b/code/modules/jobs/job_types/chaplain.dm @@ -1,95 +1,121 @@ -//Due to how large this one is it gets its own file -/* -Chaplain -*/ -/datum/job/chaplain - title = "Chaplain" - flag = CHAPLAIN - department_head = list("Head of Personnel") - department_flag = CIVILIAN - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the head of personnel" - selection_color = "#dddddd" - - outfit = /datum/outfit/job/chaplain - - access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_THEATRE) - minimal_access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_THEATRE) - -/datum/job/chaplain/after_spawn(mob/living/H, mob/M) - . = ..() - if(H.mind) - H.mind.isholy = TRUE - - var/obj/item/storage/book/bible/booze/B = new - - if(GLOB.religion) - B.deity_name = GLOB.deity - B.name = GLOB.bible_name - B.icon_state = GLOB.bible_icon_state - B.item_state = GLOB.bible_item_state - to_chat(H, "There is already an established religion onboard the station. You are an acolyte of [GLOB.deity]. Defer to the Chaplain.") - H.equip_to_slot_or_del(B, SLOT_IN_BACKPACK) - var/nrt = GLOB.holy_weapon_type || /obj/item/nullrod - var/obj/item/nullrod/N = new nrt(H) - H.put_in_hands(N) - return - - var/new_religion = "Christianity" - if(M.client && M.client.prefs.custom_names["religion"]) - new_religion = M.client.prefs.custom_names["religion"] - - var/new_deity = "Space Jesus" - if(M.client && M.client.prefs.custom_names["deity"]) - new_deity = M.client.prefs.custom_names["deity"] - - B.deity_name = new_deity - - - switch(lowertext(new_religion)) - if("christianity") - B.name = pick("The Holy Bible","The Dead Sea Scrolls") - if("satanism") - B.name = "The Unholy Bible" - if("cthulhu") - B.name = "The Necronomicon" - if("islam") - B.name = "Quran" - if("scientology") - B.name = pick("The Biography of L. Ron Hubbard","Dianetics") - if("chaos") - B.name = "The Book of Lorgar" - if("imperium") - B.name = "Uplifting Primer" - if("toolboxia") - B.name = "Toolbox Manifesto" - if("homosexuality") - B.name = "Guys Gone Wild" - if("lol", "wtf", "gay", "penis", "ass", "poo", "badmin", "shitmin", "deadmin", "cock", "cocks", "meme", "memes") - B.name = pick("Woodys Got Wood: The Aftermath", "War of the Cocks", "Sweet Bro and Hella Jef: Expanded Edition") - H.adjustBrainLoss(100) // starts off retarded as fuck - if("science") - B.name = pick("Principle of Relativity", "Quantum Enigma: Physics Encounters Consciousness", "Programming the Universe", "Quantum Physics and Theology", "String Theory for Dummies", "How To: Build Your Own Warp Drive", "The Mysteries of Bluespace", "Playing God: Collector's Edition") - else - B.name = "The Holy Book of [new_religion]" - - GLOB.religion = new_religion - GLOB.bible_name = B.name - GLOB.deity = B.deity_name - - H.equip_to_slot_or_del(B, SLOT_IN_BACKPACK) - - SSblackbox.record_feedback("text", "religion_name", 1, "[new_religion]", 1) - SSblackbox.record_feedback("text", "religion_deity", 1, "[new_deity]", 1) - -/datum/outfit/job/chaplain - name = "Chaplain" - jobtype = /datum/job/chaplain - - belt = /obj/item/pda/chaplain - uniform = /obj/item/clothing/under/rank/chaplain - backpack_contents = list(/obj/item/camera/spooky = 1) - backpack = /obj/item/storage/backpack/cultpack - satchel = /obj/item/storage/backpack/cultpack +/datum/job/chaplain + title = "Chaplain" + flag = CHAPLAIN + department_head = list("Head of Personnel") + department_flag = CIVILIAN + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the head of personnel" + selection_color = "#dddddd" + + outfit = /datum/outfit/job/chaplain + + access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_THEATRE) + minimal_access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_THEATRE) + + display_order = JOB_DISPLAY_ORDER_CHAPLAIN + + +/datum/job/chaplain/after_spawn(mob/living/H, mob/M) + . = ..() + if(H.mind) + H.mind.isholy = TRUE + + var/obj/item/storage/book/bible/booze/B = new + + if(GLOB.religion) + B.deity_name = GLOB.deity + B.name = GLOB.bible_name + B.icon_state = GLOB.bible_icon_state + B.item_state = GLOB.bible_item_state + to_chat(H, "There is already an established religion onboard the station. You are an acolyte of [GLOB.deity]. Defer to the Chaplain.") + H.equip_to_slot_or_del(B, SLOT_IN_BACKPACK) + var/nrt = GLOB.holy_weapon_type || /obj/item/nullrod + var/obj/item/nullrod/N = new nrt(H) + H.put_in_hands(N) + return + + var/new_religion = DEFAULT_RELIGION + if(M.client && M.client.prefs.custom_names["religion"]) + new_religion = M.client.prefs.custom_names["religion"] + + var/new_deity = DEFAULT_DEITY + if(M.client && M.client.prefs.custom_names["deity"]) + new_deity = M.client.prefs.custom_names["deity"] + + B.deity_name = new_deity + + + switch(lowertext(new_religion)) + if("christianity") // DEFAULT_RELIGION + B.name = pick("The Holy Bible","The Dead Sea Scrolls") + if("buddhism") + B.name = "The Sutras" + if("clownism","honkmother","honk","honkism","comedy") + B.name = pick("The Holy Joke Book", "Just a Prank", "Hymns to the Honkmother") + if("chaos") + B.name = "The Book of Lorgar" + if("cthulhu") + B.name = "The Necronomicon" + if("hinduism") + B.name = "The Vedas" + if("homosexuality") + B.name = pick("Guys Gone Wild","Coming Out of The Closet") + if("imperium") + B.name = "Uplifting Primer" + if("islam") + B.name = "Quran" + if("judaism") + B.name = "The Torah" + if("lampism") + B.name = "Fluorescent Incandescence" + if("lol", "wtf", "gay", "penis", "ass", "poo", "badmin", "shitmin", "deadmin", "cock", "cocks", "meme", "memes") + B.name = pick("Woodys Got Wood: The Aftermath", "War of the Cocks", "Sweet Bro and Hella Jef: Expanded Edition","F.A.T.A.L. Rulebook") + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 100) // starts off retarded as fuck + if("monkeyism","apism","gorillism","primatism") + B.name = pick("Going Bananas", "Bananas Out For Harambe") + if("mormonism") + B.name = "The Book of Mormon" + if("pastafarianism") + B.name = "The Gospel of the Flying Spaghetti Monster" + if("rastafarianism","rasta") + B.name = "The Holy Piby" + if("satanism") + B.name = "The Unholy Bible" + if("science") + B.name = pick("Principle of Relativity", "Quantum Enigma: Physics Encounters Consciousness", "Programming the Universe", "Quantum Physics and Theology", "String Theory for Dummies", "How To: Build Your Own Warp Drive", "The Mysteries of Bluespace", "Playing God: Collector's Edition") + if("scientology") + B.name = pick("The Biography of L. Ron Hubbard","Dianetics") + if("servicianism", "partying") + B.name = "The Tenets of Servicia" + B.deity_name = pick("Servicia", "Space Bacchus", "Space Dionysus") + B.desc = "Happy, Full, Clean. Live it and give it." + if("subgenius") + B.name = "Book of the SubGenius" + if("toolboxia","greytide") + B.name = pick("Toolbox Manifesto","iGlove Assistants") + if("weeaboo","kawaii") + B.name = pick("Fanfiction Compendium","Japanese for Dummies","The Manganomicon","Establishing Your O.T.P") + else + B.name = "The Holy Book of [new_religion]" + + GLOB.religion = new_religion + GLOB.bible_name = B.name + GLOB.deity = B.deity_name + + H.equip_to_slot_or_del(B, SLOT_IN_BACKPACK) + + SSblackbox.record_feedback("text", "religion_name", 1, "[new_religion]", 1) + SSblackbox.record_feedback("text", "religion_deity", 1, "[new_deity]", 1) + +/datum/outfit/job/chaplain + name = "Chaplain" + jobtype = /datum/job/chaplain + + belt = /obj/item/pda/chaplain + ears = /obj/item/radio/headset/headset_srv + uniform = /obj/item/clothing/under/rank/chaplain + backpack_contents = list(/obj/item/camera/spooky = 1) + backpack = /obj/item/storage/backpack/cultpack + satchel = /obj/item/storage/backpack/cultpack diff --git a/code/modules/jobs/job_types/chemist.dm b/code/modules/jobs/job_types/chemist.dm new file mode 100644 index 0000000000..a915d261ed --- /dev/null +++ b/code/modules/jobs/job_types/chemist.dm @@ -0,0 +1,36 @@ +/datum/job/chemist + title = "Chemist" + flag = CHEMIST + department_head = list("Chief Medical Officer") + department_flag = MEDSCI + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the chief medical officer" + selection_color = "#74b5e0" + exp_type = EXP_TYPE_CREW + exp_requirements = 60 + + outfit = /datum/outfit/job/chemist + + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_CHEMISTRY, ACCESS_MINERAL_STOREROOM) + + display_order = JOB_DISPLAY_ORDER_CHEMIST + +/datum/outfit/job/chemist + name = "Chemist" + jobtype = /datum/job/chemist + + glasses = /obj/item/clothing/glasses/science + belt = /obj/item/pda/chemist + ears = /obj/item/radio/headset/headset_med + uniform = /obj/item/clothing/under/rank/chemist + shoes = /obj/item/clothing/shoes/sneakers/white + suit = /obj/item/clothing/suit/toggle/labcoat/chemist + backpack = /obj/item/storage/backpack/chemistry + satchel = /obj/item/storage/backpack/satchel/chem + duffelbag = /obj/item/storage/backpack/duffelbag/med + + chameleon_extras = /obj/item/gun/syringe + diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm new file mode 100644 index 0000000000..da3f281267 --- /dev/null +++ b/code/modules/jobs/job_types/chief_engineer.dm @@ -0,0 +1,64 @@ +/datum/job/chief_engineer + title = "Chief Engineer" + flag = CHIEF +// auto_deadmin_role_flags = DEADMIN_POSITION_HEAD + department_head = list("Captain") + department_flag = ENGSEC + head_announce = list(RADIO_CHANNEL_ENGINEERING) + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the captain" + selection_color = "#ee7400" + req_admin_notify = 1 + minimal_player_age = 7 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + exp_type_department = EXP_TYPE_ENGINEERING + + outfit = /datum/outfit/job/ce + + access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, + ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EVA, + ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, ACCESS_MINISAT, + ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, + ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EVA, + ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, ACCESS_MINISAT, + ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM) + + display_order = JOB_DISPLAY_ORDER_CHIEF_ENGINEER + blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/paraplegic, /datum/quirk/insanity) + +/datum/outfit/job/ce + name = "Chief Engineer" + jobtype = /datum/job/chief_engineer + + id = /obj/item/card/id/silver + belt = /obj/item/storage/belt/utility/chief/full + l_pocket = /obj/item/pda/heads/ce + ears = /obj/item/radio/headset/heads/ce + uniform = /obj/item/clothing/under/rank/chief_engineer + shoes = /obj/item/clothing/shoes/sneakers/brown + head = /obj/item/clothing/head/hardhat/white + gloves = /obj/item/clothing/gloves/color/black/ce + backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/preset/advanced=1) + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel/eng + duffelbag = /obj/item/storage/backpack/duffelbag/engineering + box = /obj/item/storage/box/engineer + pda_slot = SLOT_L_STORE + chameleon_extras = /obj/item/stamp/ce + +/datum/outfit/job/ce/rig + name = "Chief Engineer (Hardsuit)" + + mask = /obj/item/clothing/mask/breath + suit = /obj/item/clothing/suit/space/hardsuit/engine/elite + shoes = /obj/item/clothing/shoes/magboots/advance + suit_store = /obj/item/tank/internals/oxygen + glasses = /obj/item/clothing/glasses/meson/engine + gloves = /obj/item/clothing/gloves/color/yellow + head = null + internals_slot = SLOT_S_STORE diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm new file mode 100644 index 0000000000..4c7249f048 --- /dev/null +++ b/code/modules/jobs/job_types/chief_medical_officer.dm @@ -0,0 +1,59 @@ +/datum/job/cmo + title = "Chief Medical Officer" + flag = CMO_JF + department_head = list("Captain") + department_flag = MEDSCI +// auto_deadmin_role_flags = DEADMIN_POSITION_HEAD + head_announce = list(RADIO_CHANNEL_MEDICAL) + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the captain" + selection_color = "#509ed1" + req_admin_notify = 1 + minimal_player_age = 7 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + exp_type_department = EXP_TYPE_MEDICAL + + outfit = /datum/outfit/job/cmo + + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_HEADS, ACCESS_MINERAL_STOREROOM, + ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, + ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_MAINT_TUNNELS) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_HEADS, ACCESS_MINERAL_STOREROOM, + ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, + ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_MAINT_TUNNELS) + + display_order = JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER + blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity) + +/datum/outfit/job/cmo + name = "Chief Medical Officer" + jobtype = /datum/job/cmo + + id = /obj/item/card/id/silver + belt = /obj/item/pda/heads/cmo + l_pocket = /obj/item/pinpointer/crew + ears = /obj/item/radio/headset/heads/cmo + uniform = /obj/item/clothing/under/rank/chief_medical_officer + shoes = /obj/item/clothing/shoes/sneakers/brown + suit = /obj/item/clothing/suit/toggle/labcoat/cmo + l_hand = /obj/item/storage/firstaid/regular + suit_store = /obj/item/flashlight/pen + backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1) + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel/med + duffelbag = /obj/item/storage/backpack/duffelbag/med + + chameleon_extras = list(/obj/item/gun/syringe, /obj/item/stamp/cmo) + +/datum/outfit/job/cmo/hardsuit + name = "Chief Medical Officer (Hardsuit)" + + mask = /obj/item/clothing/mask/breath + suit = /obj/item/clothing/suit/space/hardsuit/medical + suit_store = /obj/item/tank/internals/oxygen + r_pocket = /obj/item/flashlight/pen + diff --git a/code/modules/jobs/job_types/civilian.dm b/code/modules/jobs/job_types/civilian.dm deleted file mode 100644 index f21ff69e8e..0000000000 --- a/code/modules/jobs/job_types/civilian.dm +++ /dev/null @@ -1,206 +0,0 @@ -/* -Clown -*/ -/datum/job/clown - title = "Clown" - flag = CLOWN - department_head = list("Head of Personnel") - department_flag = CIVILIAN - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the head of personnel" - selection_color = "#dddddd" - - outfit = /datum/outfit/job/clown - - access = list(ACCESS_THEATRE) - minimal_access = list(ACCESS_THEATRE) - -/datum/job/clown/after_spawn(mob/living/carbon/human/H, mob/M) - . = ..() - H.apply_pref_name("clown", M.client) - -/datum/outfit/job/clown - name = "Clown" - jobtype = /datum/job/clown - - belt = /obj/item/pda/clown - uniform = /obj/item/clothing/under/rank/clown - shoes = /obj/item/clothing/shoes/clown_shoes - mask = /obj/item/clothing/mask/gas/clown_hat - l_pocket = /obj/item/bikehorn - backpack_contents = list( - /obj/item/stamp/clown = 1, - /obj/item/reagent_containers/spray/waterflower = 1, - /obj/item/reagent_containers/food/snacks/grown/banana = 1, - /obj/item/instrument/bikehorn = 1, - ) - - implants = list(/obj/item/implant/sad_trombone) - - backpack = /obj/item/storage/backpack/clown - satchel = /obj/item/storage/backpack/clown - duffelbag = /obj/item/storage/backpack/duffelbag/clown //strangely has a duffel - - box = /obj/item/storage/box/hug/survival - - chameleon_extras = /obj/item/stamp/clown - - -/datum/outfit/job/clown/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - ..() - if(visualsOnly) - return - - H.fully_replace_character_name(H.real_name, pick(GLOB.clown_names)) - -/datum/outfit/job/clown/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - ..() - if(visualsOnly) - return - - H.dna.add_mutation(CLOWNMUT) - H.dna.add_mutation(SMILE) - -/* -Mime -*/ -/datum/job/mime - title = "Mime" - flag = MIME - department_head = list("Head of Personnel") - department_flag = CIVILIAN - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the head of personnel" - selection_color = "#dddddd" - - outfit = /datum/outfit/job/mime - - access = list(ACCESS_THEATRE) - minimal_access = list(ACCESS_THEATRE) - -/datum/job/mime/after_spawn(mob/living/carbon/human/H, mob/M) - H.apply_pref_name("mime", M.client) - -/datum/outfit/job/mime - name = "Mime" - jobtype = /datum/job/mime - - belt = /obj/item/pda/mime - uniform = /obj/item/clothing/under/rank/mime - mask = /obj/item/clothing/mask/gas/mime - gloves = /obj/item/clothing/gloves/color/white - head = /obj/item/clothing/head/frenchberet - suit = /obj/item/clothing/suit/suspenders - backpack_contents = list(/obj/item/reagent_containers/food/drinks/bottle/bottleofnothing=1) - - accessory = /obj/item/clothing/accessory/pocketprotector/cosmetology - backpack = /obj/item/storage/backpack/mime - satchel = /obj/item/storage/backpack/mime - - -/datum/outfit/job/mime/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - ..() - - if(visualsOnly) - return - - if(H.mind) - H.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/conjure/mime_wall(null)) - H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/mime/speak(null)) - H.mind.miming = 1 - -/* -Curator -*/ -/datum/job/curator - title = "Curator" - flag = CURATOR - department_head = list("Head of Personnel") - department_flag = CIVILIAN - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the head of personnel" - selection_color = "#dddddd" - - outfit = /datum/outfit/job/curator - - access = list(ACCESS_LIBRARY) - minimal_access = list(ACCESS_LIBRARY, ACCESS_CONSTRUCTION,ACCESS_MINING_STATION) - -/datum/outfit/job/curator - name = "Curator" - jobtype = /datum/job/curator - - belt = /obj/item/pda/curator - uniform = /obj/item/clothing/under/rank/curator - l_hand = /obj/item/storage/bag/books - r_pocket = /obj/item/key/displaycase - l_pocket = /obj/item/laser_pointer - accessory = /obj/item/clothing/accessory/pocketprotector/full - backpack_contents = list( - /obj/item/melee/curator_whip = 1, - /obj/item/soapstone = 1, - /obj/item/barcodescanner = 1 - ) - - -/datum/outfit/job/curator/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - ..() - - if(visualsOnly) - return - - H.grant_all_languages(omnitongue=TRUE) -/* -Lawyer -*/ -/datum/job/lawyer - title = "Lawyer" - flag = LAWYER - department_head = list("Head of Personnel") - department_flag = CIVILIAN - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the head of personnel" - selection_color = "#dddddd" - var/lawyers = 0 //Counts lawyer amount - - outfit = /datum/outfit/job/lawyer - - access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS) - minimal_access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS) - - mind_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM) - -/datum/outfit/job/lawyer - name = "Lawyer" - jobtype = /datum/job/lawyer - - belt = /obj/item/pda/lawyer - ears = /obj/item/radio/headset/headset_sec - uniform = /obj/item/clothing/under/lawyer/bluesuit - suit = /obj/item/clothing/suit/toggle/lawyer - shoes = /obj/item/clothing/shoes/laceup - l_hand = /obj/item/storage/briefcase/lawyer - l_pocket = /obj/item/laser_pointer - r_pocket = /obj/item/clothing/accessory/lawyers_badge - - chameleon_extras = /obj/item/stamp/law - - -/datum/outfit/job/lawyer/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - ..() - if(visualsOnly) - return - - var/datum/job/lawyer/J = SSjob.GetJobType(jobtype) - J.lawyers++ - if(J.lawyers>1) - uniform = /obj/item/clothing/under/lawyer/purpsuit - suit = /obj/item/clothing/suit/toggle/lawyer/purple diff --git a/code/modules/jobs/job_types/clown.dm b/code/modules/jobs/job_types/clown.dm new file mode 100644 index 0000000000..d8b88ae871 --- /dev/null +++ b/code/modules/jobs/job_types/clown.dm @@ -0,0 +1,58 @@ +/datum/job/clown + title = "Clown" + flag = CLOWN + department_head = list("Head of Personnel") + department_flag = CIVILIAN + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the head of personnel" + selection_color = "#dddddd" + + outfit = /datum/outfit/job/clown + + access = list(ACCESS_THEATRE) + minimal_access = list(ACCESS_THEATRE) + + display_order = JOB_DISPLAY_ORDER_CLOWN + + +/datum/job/clown/after_spawn(mob/living/carbon/human/H, mob/M) + . = ..() + H.apply_pref_name("clown", M.client) + +/datum/outfit/job/clown + name = "Clown" + jobtype = /datum/job/clown + + belt = /obj/item/pda/clown + ears = /obj/item/radio/headset/headset_srv + uniform = /obj/item/clothing/under/rank/clown + shoes = /obj/item/clothing/shoes/clown_shoes + mask = /obj/item/clothing/mask/gas/clown_hat + l_pocket = /obj/item/bikehorn + backpack_contents = list( + /obj/item/stamp/clown = 1, + /obj/item/reagent_containers/spray/waterflower = 1, + /obj/item/reagent_containers/food/snacks/grown/banana = 1, + /obj/item/instrument/bikehorn = 1, + ) + + implants = list(/obj/item/implant/sad_trombone) + + backpack = /obj/item/storage/backpack/clown + satchel = /obj/item/storage/backpack/clown + duffelbag = /obj/item/storage/backpack/duffelbag/clown //strangely has a duffel + + box = /obj/item/storage/box/hug/survival + + chameleon_extras = /obj/item/stamp/clown + +/datum/outfit/job/clown/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + ..() + if(visualsOnly) + return + + H.fully_replace_character_name(H.real_name, pick(GLOB.clown_names)) //rename the mob AFTER they're equipped so their ID gets updated properly. + H.dna.add_mutation(CLOWNMUT) + H.dna.add_mutation(SMILE) diff --git a/code/modules/jobs/job_types/cook.dm b/code/modules/jobs/job_types/cook.dm new file mode 100644 index 0000000000..c213d4dffc --- /dev/null +++ b/code/modules/jobs/job_types/cook.dm @@ -0,0 +1,52 @@ +/datum/job/cook + title = "Cook" + flag = COOK + department_head = list("Head of Personnel") + department_flag = CIVILIAN + faction = "Station" + total_positions = 2 + spawn_positions = 1 + supervisors = "the head of personnel" + selection_color = "#bbe291" + var/cooks = 0 //Counts cooks amount + + outfit = /datum/outfit/job/cook + + access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM) + + display_order = JOB_DISPLAY_ORDER_COOK + +/datum/outfit/job/cook + name = "Cook" + jobtype = /datum/job/cook + + belt = /obj/item/pda/cook + ears = /obj/item/radio/headset/headset_srv + uniform = /obj/item/clothing/under/rank/chef + suit = /obj/item/clothing/suit/toggle/chef + head = /obj/item/clothing/head/chefhat + mask = /obj/item/clothing/mask/fakemoustache/italian + backpack_contents = list(/obj/item/sharpener = 1) + +/datum/outfit/job/cook/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + ..() + var/datum/job/cook/J = SSjob.GetJobType(jobtype) + if(J) // Fix for runtime caused by invalid job being passed + if(J.cooks>0)//Cooks + suit = /obj/item/clothing/suit/apron/chef + head = /obj/item/clothing/head/soft/mime + if(!visualsOnly) + J.cooks++ + +/datum/outfit/job/cook/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + ..() + if(visualsOnly) + return + var/list/possible_boxes = subtypesof(/obj/item/storage/box/ingredients) + var/chosen_box = pick(possible_boxes) + var/obj/item/storage/box/I = new chosen_box(src) + H.equip_to_slot_or_del(I,SLOT_IN_BACKPACK) + var/datum/martial_art/cqc/under_siege/justacook = new + justacook.teach(H) + diff --git a/code/modules/jobs/job_types/curator.dm b/code/modules/jobs/job_types/curator.dm new file mode 100644 index 0000000000..35fa8483d5 --- /dev/null +++ b/code/modules/jobs/job_types/curator.dm @@ -0,0 +1,43 @@ +/datum/job/curator + title = "Curator" + flag = CURATOR + department_head = list("Head of Personnel") + department_flag = CIVILIAN + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the head of personnel" + selection_color = "#dddddd" + + outfit = /datum/outfit/job/curator + + access = list(ACCESS_LIBRARY) + minimal_access = list(ACCESS_LIBRARY, ACCESS_CONSTRUCTION, ACCESS_MINING_STATION) + + display_order = JOB_DISPLAY_ORDER_CURATOR + +/datum/outfit/job/curator + name = "Curator" + jobtype = /datum/job/curator + + shoes = /obj/item/clothing/shoes/laceup + belt = /obj/item/pda/curator + ears = /obj/item/radio/headset/headset_srv + uniform = /obj/item/clothing/under/rank/curator + l_hand = /obj/item/storage/bag/books + r_pocket = /obj/item/key/displaycase + l_pocket = /obj/item/laser_pointer + accessory = /obj/item/clothing/accessory/pocketprotector/full + backpack_contents = list( + /obj/item/melee/curator_whip = 1, + /obj/item/soapstone = 1, + /obj/item/barcodescanner = 1 + ) + +/datum/outfit/job/curator/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + ..() + + if(visualsOnly) + return + + H.grant_all_languages(omnitongue=TRUE) diff --git a/code/modules/jobs/job_types/cyborg.dm b/code/modules/jobs/job_types/cyborg.dm new file mode 100644 index 0000000000..29c4c3d833 --- /dev/null +++ b/code/modules/jobs/job_types/cyborg.dm @@ -0,0 +1,27 @@ +/datum/job/cyborg + title = "Cyborg" + flag = CYBORG +// auto_deadmin_role_flags = DEADMIN_POSITION_SILICON + department_flag = ENGSEC + faction = "Station" + total_positions = 0 + spawn_positions = 1 + supervisors = "your laws and the AI" //Nodrak + selection_color = "#ddffdd" + minimal_player_age = 21 + exp_requirements = 120 + exp_type = EXP_TYPE_CREW + + display_order = JOB_DISPLAY_ORDER_CYBORG + +/datum/job/cyborg/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, datum/outfit/outfit_override = null, client/preference_source = null) + if(visualsOnly) + CRASH("dynamic preview is unsupported") + return H.Robotize(FALSE, latejoin) + +/datum/job/cyborg/after_spawn(mob/living/silicon/robot/R, mob/M) + R.updatename(M.client) + R.gender = NEUTER + +/datum/job/cyborg/radio_help_message(mob/M) + to_chat(M, "Prefix your message with :b to speak with other cyborgs and AI.") diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm new file mode 100644 index 0000000000..27a54fbd1f --- /dev/null +++ b/code/modules/jobs/job_types/detective.dm @@ -0,0 +1,57 @@ +/datum/job/detective + title = "Detective" + flag = DETECTIVE +// auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY + department_head = list("Head of Security") + department_flag = ENGSEC + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the head of security" + selection_color = "#c02f2f" + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + + outfit = /datum/outfit/job/detective + + access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_COURT, ACCESS_BRIG, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_COURT, ACCESS_BRIG, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) + + mind_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM) + + display_order = JOB_DISPLAY_ORDER_DETECTIVE + blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/nonviolent, /datum/quirk/paraplegic) + +/datum/outfit/job/detective + name = "Detective" + jobtype = /datum/job/detective + + belt = /obj/item/pda/detective + ears = /obj/item/radio/headset/headset_sec/alt + uniform = /obj/item/clothing/under/rank/det + neck = /obj/item/clothing/neck/tie/black + shoes = /obj/item/clothing/shoes/sneakers/brown + suit = /obj/item/clothing/suit/det_suit + gloves = /obj/item/clothing/gloves/color/black + head = /obj/item/clothing/head/fedora/det_hat + l_pocket = /obj/item/toy/crayon/white + r_pocket = /obj/item/lighter + backpack_contents = list(/obj/item/storage/box/evidence=1,\ + /obj/item/detective_scanner=1,\ + /obj/item/melee/classic_baton=1) + mask = /obj/item/clothing/mask/cigarette + + implants = list(/obj/item/implant/mindshield) + + chameleon_extras = list(/obj/item/gun/ballistic/revolver/detective, /obj/item/clothing/glasses/sunglasses) + +/datum/outfit/job/detective/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + ..() + var/obj/item/clothing/mask/cigarette/cig = H.wear_mask + if(istype(cig)) //Some species specfic changes can mess this up (plasmamen) + cig.light("") + + if(visualsOnly) + return + diff --git a/code/modules/jobs/job_types/engineering.dm b/code/modules/jobs/job_types/engineering.dm deleted file mode 100644 index e65cbab1bd..0000000000 --- a/code/modules/jobs/job_types/engineering.dm +++ /dev/null @@ -1,169 +0,0 @@ -/* -Chief Engineer -*/ -/datum/job/chief_engineer - title = "Chief Engineer" - flag = CHIEF - department_head = list("Captain") - department_flag = ENGSEC - head_announce = list(RADIO_CHANNEL_ENGINEERING) - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the captain" - selection_color = "#ffeeaa" - req_admin_notify = 1 - minimal_player_age = 7 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - exp_type_department = EXP_TYPE_ENGINEERING - - outfit = /datum/outfit/job/ce - - access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, - ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EMERGENCY_STORAGE, ACCESS_EVA, - ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, ACCESS_MINISAT, - ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, - ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EMERGENCY_STORAGE, ACCESS_EVA, - ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, ACCESS_MINISAT, - ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM) - - blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/paraplegic, /datum/quirk/insanity) - -/datum/outfit/job/ce - name = "Chief Engineer" - jobtype = /datum/job/chief_engineer - - id = /obj/item/card/id/silver - belt = /obj/item/storage/belt/utility/chief/full - l_pocket = /obj/item/pda/heads/ce - ears = /obj/item/radio/headset/heads/ce - uniform = /obj/item/clothing/under/rank/chief_engineer - shoes = /obj/item/clothing/shoes/sneakers/brown - head = /obj/item/clothing/head/hardhat/white - gloves = /obj/item/clothing/gloves/color/black/ce - backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/preset/advanced=1) - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel/eng - duffelbag = /obj/item/storage/backpack/duffelbag/engineering - box = /obj/item/storage/box/engineer - pda_slot = SLOT_L_STORE - chameleon_extras = /obj/item/stamp/ce - -/datum/outfit/job/ce/rig - name = "Chief Engineer (Hardsuit)" - - mask = /obj/item/clothing/mask/breath - suit = /obj/item/clothing/suit/space/hardsuit/engine/elite - shoes = /obj/item/clothing/shoes/magboots/advance - suit_store = /obj/item/tank/internals/oxygen - glasses = /obj/item/clothing/glasses/meson/engine - gloves = /obj/item/clothing/gloves/color/yellow - head = null - internals_slot = SLOT_S_STORE - - -/* -Station Engineer -*/ -/datum/job/engineer - title = "Station Engineer" - flag = ENGINEER - department_head = list("Chief Engineer") - department_flag = ENGSEC - faction = "Station" - total_positions = 5 - spawn_positions = 5 - supervisors = "the chief engineer" - selection_color = "#fff5cc" - exp_requirements = 60 - exp_type = EXP_TYPE_CREW - - outfit = /datum/outfit/job/engineer - - access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, - ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, - ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM) - -/datum/outfit/job/engineer - name = "Station Engineer" - jobtype = /datum/job/engineer - - belt = /obj/item/storage/belt/utility/full/engi - l_pocket = /obj/item/pda/engineering - ears = /obj/item/radio/headset/headset_eng - uniform = /obj/item/clothing/under/rank/engineer - shoes = /obj/item/clothing/shoes/workboots - head = /obj/item/clothing/head/hardhat - r_pocket = /obj/item/t_scanner - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel/eng - duffelbag = /obj/item/storage/backpack/duffelbag/engineering - box = /obj/item/storage/box/engineer - pda_slot = SLOT_L_STORE - backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1) - -/datum/outfit/job/engineer/gloved - name = "Station Engineer (Gloves)" - gloves = /obj/item/clothing/gloves/color/yellow - -/datum/outfit/job/engineer/gloved/rig - name = "Station Engineer (Hardsuit)" - - mask = /obj/item/clothing/mask/breath - suit = /obj/item/clothing/suit/space/hardsuit/engine - suit_store = /obj/item/tank/internals/oxygen - head = null - internals_slot = SLOT_S_STORE - - -/* -Atmospheric Technician -*/ -/datum/job/atmos - title = "Atmospheric Technician" - flag = ATMOSTECH - department_head = list("Chief Engineer") - department_flag = ENGSEC - faction = "Station" - total_positions = 3 - spawn_positions = 2 - supervisors = "the chief engineer" - selection_color = "#fff5cc" - exp_requirements = 60 - exp_type = EXP_TYPE_CREW - - outfit = /datum/outfit/job/atmos - - access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, - ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_ATMOSPHERICS, ACCESS_MAINT_TUNNELS, ACCESS_EMERGENCY_STORAGE, ACCESS_CONSTRUCTION, ACCESS_MINERAL_STOREROOM) - -/datum/outfit/job/atmos - name = "Atmospheric Technician" - jobtype = /datum/job/atmos - - belt = /obj/item/storage/belt/utility/atmostech - l_pocket = /obj/item/pda/atmos - ears = /obj/item/radio/headset/headset_eng - uniform = /obj/item/clothing/under/rank/atmospheric_technician - r_pocket = /obj/item/analyzer - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel/eng - duffelbag = /obj/item/storage/backpack/duffelbag/engineering - box = /obj/item/storage/box/engineer - pda_slot = SLOT_L_STORE - backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1) - -/datum/outfit/job/atmos/rig - name = "Atmospheric Technician (Hardsuit)" - - mask = /obj/item/clothing/mask/gas - suit = /obj/item/clothing/suit/space/hardsuit/engine/atmos - suit_store = /obj/item/tank/internals/oxygen - internals_slot = SLOT_S_STORE diff --git a/code/modules/jobs/job_types/geneticist.dm b/code/modules/jobs/job_types/geneticist.dm new file mode 100644 index 0000000000..d7f59ff883 --- /dev/null +++ b/code/modules/jobs/job_types/geneticist.dm @@ -0,0 +1,35 @@ +/datum/job/geneticist + title = "Geneticist" + flag = GENETICIST + department_head = list("Chief Medical Officer", "Research Director") + department_flag = MEDSCI + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the chief medical officer and research director" + selection_color = "#74b5e0" + exp_type = EXP_TYPE_CREW + exp_requirements = 60 + + outfit = /datum/outfit/job/geneticist + + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_CHEMISTRY, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_ROBOTICS, ACCESS_MINERAL_STOREROOM, ACCESS_TECH_STORAGE) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM) + + display_order = JOB_DISPLAY_ORDER_GENETICIST + +/datum/outfit/job/geneticist + name = "Geneticist" + jobtype = /datum/job/geneticist + + belt = /obj/item/pda/geneticist + ears = /obj/item/radio/headset/headset_medsci + uniform = /obj/item/clothing/under/rank/geneticist + shoes = /obj/item/clothing/shoes/sneakers/white + suit = /obj/item/clothing/suit/toggle/labcoat/genetics + suit_store = /obj/item/flashlight/pen + + backpack = /obj/item/storage/backpack/genetics + satchel = /obj/item/storage/backpack/satchel/gen + duffelbag = /obj/item/storage/backpack/duffelbag/med + diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm new file mode 100644 index 0000000000..e320ce20b4 --- /dev/null +++ b/code/modules/jobs/job_types/head_of_personnel.dm @@ -0,0 +1,51 @@ +/datum/job/hop + title = "Head of Personnel" + flag = HOP +// auto_deadmin_role_flags = DEADMIN_POSITION_HEAD + department_head = list("Captain") + department_flag = CIVILIAN + head_announce = list(RADIO_CHANNEL_SERVICE) + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the captain" + selection_color = "#3a8529" + req_admin_notify = 1 + minimal_player_age = 10 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + exp_type_department = EXP_TYPE_SERVICE + + outfit = /datum/outfit/job/hop + + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_COURT, ACCESS_WEAPONS, + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_VAULT, ACCESS_MINING_STATION, + ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_COURT, ACCESS_WEAPONS, + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_VAULT, ACCESS_MINING_STATION, + ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM) + + display_order = JOB_DISPLAY_ORDER_HEAD_OF_PERSONNEL + + blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/prosopagnosia, /datum/quirk/insanity) + +/datum/outfit/job/hop + name = "Head of Personnel" + jobtype = /datum/job/hop + + id = /obj/item/card/id/silver + belt = /obj/item/pda/heads/hop + ears = /obj/item/radio/headset/heads/hop + uniform = /obj/item/clothing/under/rank/head_of_personnel + shoes = /obj/item/clothing/shoes/sneakers/brown + head = /obj/item/clothing/head/hopcap + backpack_contents = list(/obj/item/storage/box/ids=1,\ + /obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/preset/advanced = 1) + + chameleon_extras = list(/obj/item/gun/energy/e_gun, /obj/item/stamp/hop) diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm new file mode 100644 index 0000000000..f6b5dbd3ef --- /dev/null +++ b/code/modules/jobs/job_types/head_of_security.dm @@ -0,0 +1,68 @@ +/datum/job/hos + title = "Head of Security" + flag = HOS +// auto_deadmin_role_flags = DEADMIN_POSITION_HEAD|DEADMIN_POSITION_SECURITY + department_head = list("Captain") + department_flag = ENGSEC + head_announce = list(RADIO_CHANNEL_SECURITY) + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the captain" + selection_color = "#b90000" + req_admin_notify = 1 + minimal_player_age = 14 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + exp_type_department = EXP_TYPE_SECURITY + + outfit = /datum/outfit/job/hos + mind_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM) + + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_WEAPONS, + ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_ALL_PERSONAL_LOCKERS, + ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING, + ACCESS_HEADS, ACCESS_HOS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_WEAPONS, + ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_ALL_PERSONAL_LOCKERS, + ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING, + ACCESS_HEADS, ACCESS_HOS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) + + display_order = JOB_DISPLAY_ORDER_HEAD_OF_SECURITY + blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/nonviolent, /datum/quirk/paraplegic, /datum/quirk/insanity) + +/datum/outfit/job/hos + name = "Head of Security" + jobtype = /datum/job/hos + + id = /obj/item/card/id/silver + belt = /obj/item/pda/heads/hos + ears = /obj/item/radio/headset/heads/hos/alt + uniform = /obj/item/clothing/under/rank/head_of_security + shoes = /obj/item/clothing/shoes/jackboots + suit = /obj/item/clothing/suit/armor/hos/trenchcoat + gloves = /obj/item/clothing/gloves/color/black/hos + head = /obj/item/clothing/head/HoS/beret + glasses = /obj/item/clothing/glasses/hud/security/sunglasses + suit_store = /obj/item/gun/energy/e_gun + r_pocket = /obj/item/assembly/flash/handheld + l_pocket = /obj/item/restraints/handcuffs + backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1) + + backpack = /obj/item/storage/backpack/security + satchel = /obj/item/storage/backpack/satchel/sec + duffelbag = /obj/item/storage/backpack/duffelbag/sec + box = /obj/item/storage/box/security + + implants = list(/obj/item/implant/mindshield) + + chameleon_extras = list(/obj/item/gun/energy/e_gun/hos, /obj/item/stamp/hos) + +/datum/outfit/job/hos/hardsuit + name = "Head of Security (Hardsuit)" + + mask = /obj/item/clothing/mask/gas/sechailer + suit = /obj/item/clothing/suit/space/hardsuit/security/hos + suit_store = /obj/item/tank/internals/oxygen + backpack_contents = list(/obj/item/melee/baton/loaded=1, /obj/item/gun/energy/e_gun=1) + diff --git a/code/modules/jobs/job_types/janitor.dm b/code/modules/jobs/job_types/janitor.dm new file mode 100644 index 0000000000..d0a06ca0e0 --- /dev/null +++ b/code/modules/jobs/job_types/janitor.dm @@ -0,0 +1,27 @@ +/datum/job/janitor + title = "Janitor" + flag = JANITOR + department_head = list("Head of Personnel") + department_flag = CIVILIAN + faction = "Station" + total_positions = 2 + spawn_positions = 1 + supervisors = "the head of personnel" + selection_color = "#bbe291" + var/global/janitors = 0 + + outfit = /datum/outfit/job/janitor + + access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) + + display_order = JOB_DISPLAY_ORDER_JANITOR + +/datum/outfit/job/janitor + name = "Janitor" + jobtype = /datum/job/janitor + + belt = /obj/item/pda/janitor + ears = /obj/item/radio/headset/headset_srv + uniform = /obj/item/clothing/under/rank/janitor + backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1) diff --git a/code/modules/jobs/job_types/lawyer.dm b/code/modules/jobs/job_types/lawyer.dm new file mode 100644 index 0000000000..0b8be52116 --- /dev/null +++ b/code/modules/jobs/job_types/lawyer.dm @@ -0,0 +1,47 @@ +/datum/job/lawyer + title = "Lawyer" + flag = LAWYER + department_head = list("Head of Personnel") + department_flag = CIVILIAN + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the head of personnel" + selection_color = "#dddddd" + var/lawyers = 0 //Counts lawyer amount + + outfit = /datum/outfit/job/lawyer + + access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS) + minimal_access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS) + + mind_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM) + + display_order = JOB_DISPLAY_ORDER_LAWYER + +/datum/outfit/job/lawyer + name = "Lawyer" + jobtype = /datum/job/lawyer + + belt = /obj/item/pda/lawyer + ears = /obj/item/radio/headset/headset_sec + uniform = /obj/item/clothing/under/lawyer/bluesuit + suit = /obj/item/clothing/suit/toggle/lawyer + shoes = /obj/item/clothing/shoes/laceup + l_hand = /obj/item/storage/briefcase/lawyer + l_pocket = /obj/item/laser_pointer + r_pocket = /obj/item/clothing/accessory/lawyers_badge + + chameleon_extras = /obj/item/stamp/law + + +/datum/outfit/job/lawyer/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + ..() + if(visualsOnly) + return + + var/datum/job/lawyer/J = SSjob.GetJobType(jobtype) + J.lawyers++ + if(J.lawyers>1) + uniform = /obj/item/clothing/under/lawyer/purpsuit + suit = /obj/item/clothing/suit/toggle/lawyer/purple diff --git a/code/modules/jobs/job_types/medical.dm b/code/modules/jobs/job_types/medical.dm deleted file mode 100644 index 9eeb4ab06e..0000000000 --- a/code/modules/jobs/job_types/medical.dm +++ /dev/null @@ -1,207 +0,0 @@ -/* -Chief Medical Officer -*/ -/datum/job/cmo - title = "Chief Medical Officer" - flag = CMO_JF - department_head = list("Captain") - department_flag = MEDSCI - head_announce = list(RADIO_CHANNEL_MEDICAL) - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the captain" - selection_color = "#ffddf0" - req_admin_notify = 1 - minimal_player_age = 7 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - exp_type_department = EXP_TYPE_MEDICAL - - outfit = /datum/outfit/job/cmo - - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_HEADS, ACCESS_MINERAL_STOREROOM, - ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, - ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_MAINT_TUNNELS) - minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_HEADS, ACCESS_MINERAL_STOREROOM, - ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, - ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_MAINT_TUNNELS) - - blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity) - -/datum/outfit/job/cmo - name = "Chief Medical Officer" - jobtype = /datum/job/cmo - - id = /obj/item/card/id/silver - belt = /obj/item/pda/heads/cmo - l_pocket = /obj/item/pinpointer/crew - ears = /obj/item/radio/headset/heads/cmo - uniform = /obj/item/clothing/under/rank/chief_medical_officer - shoes = /obj/item/clothing/shoes/sneakers/brown - suit = /obj/item/clothing/suit/toggle/labcoat/cmo - l_hand = /obj/item/storage/firstaid/regular - suit_store = /obj/item/flashlight/pen - backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1) - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel/med - duffelbag = /obj/item/storage/backpack/duffelbag/med - - chameleon_extras = list(/obj/item/gun/syringe, /obj/item/stamp/cmo) - -/datum/outfit/job/cmo/hardsuit - name = "Chief Medical Officer (Hardsuit)" - - mask = /obj/item/clothing/mask/breath - suit = /obj/item/clothing/suit/space/hardsuit/medical - suit_store = /obj/item/tank/internals/oxygen - r_pocket = /obj/item/flashlight/pen - -/* -Medical Doctor -*/ -/datum/job/doctor - title = "Medical Doctor" - flag = DOCTOR - department_head = list("Chief Medical Officer") - department_flag = MEDSCI - faction = "Station" - total_positions = 5 - spawn_positions = 3 - supervisors = "the chief medical officer" - selection_color = "#ffeef0" - - outfit = /datum/outfit/job/doctor - - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CLONING, ACCESS_MINERAL_STOREROOM) - -/datum/outfit/job/doctor - name = "Medical Doctor" - jobtype = /datum/job/doctor - - belt = /obj/item/pda/medical - ears = /obj/item/radio/headset/headset_med - uniform = /obj/item/clothing/under/rank/medical - shoes = /obj/item/clothing/shoes/sneakers/white - suit = /obj/item/clothing/suit/toggle/labcoat - l_hand = /obj/item/storage/firstaid/regular - suit_store = /obj/item/flashlight/pen - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel/med - duffelbag = /obj/item/storage/backpack/duffelbag/med - - chameleon_extras = /obj/item/gun/syringe - -/* -Chemist -*/ -/datum/job/chemist - title = "Chemist" - flag = CHEMIST - department_head = list("Chief Medical Officer") - department_flag = MEDSCI - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the chief medical officer" - selection_color = "#ffeef0" - exp_type = EXP_TYPE_CREW - exp_requirements = 60 - - outfit = /datum/outfit/job/chemist - - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MEDICAL, ACCESS_CHEMISTRY, ACCESS_MINERAL_STOREROOM) - -/datum/outfit/job/chemist - name = "Chemist" - jobtype = /datum/job/chemist - - glasses = /obj/item/clothing/glasses/science - belt = /obj/item/pda/chemist - ears = /obj/item/radio/headset/headset_med - uniform = /obj/item/clothing/under/rank/chemist - shoes = /obj/item/clothing/shoes/sneakers/white - suit = /obj/item/clothing/suit/toggle/labcoat/chemist - backpack = /obj/item/storage/backpack/chemistry - satchel = /obj/item/storage/backpack/satchel/chem - duffelbag = /obj/item/storage/backpack/duffelbag/med - l_hand = /obj/item/fermichem/pHbooklet - - chameleon_extras = /obj/item/gun/syringe - -/* -Geneticist -*/ -/datum/job/geneticist - title = "Geneticist" - flag = GENETICIST - department_head = list("Chief Medical Officer", "Research Director") - department_flag = MEDSCI - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the chief medical officer and research director" - selection_color = "#ffeef0" - exp_type = EXP_TYPE_CREW - exp_requirements = 60 - - outfit = /datum/outfit/job/geneticist - - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_CHEMISTRY, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_ROBOTICS, ACCESS_MINERAL_STOREROOM, ACCESS_TECH_STORAGE) - minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM) - -/datum/outfit/job/geneticist - name = "Geneticist" - jobtype = /datum/job/geneticist - - belt = /obj/item/pda/geneticist - ears = /obj/item/radio/headset/headset_medsci - uniform = /obj/item/clothing/under/rank/geneticist - shoes = /obj/item/clothing/shoes/sneakers/white - suit = /obj/item/clothing/suit/toggle/labcoat/genetics - suit_store = /obj/item/flashlight/pen - - backpack = /obj/item/storage/backpack/genetics - satchel = /obj/item/storage/backpack/satchel/gen - duffelbag = /obj/item/storage/backpack/duffelbag/med - -/* -Virologist -*/ -/datum/job/virologist - title = "Virologist" - flag = VIROLOGIST - department_head = list("Chief Medical Officer") - department_flag = MEDSCI - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the chief medical officer" - selection_color = "#ffeef0" - exp_type = EXP_TYPE_CREW - exp_requirements = 60 - - outfit = /datum/outfit/job/virologist - - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MEDICAL, ACCESS_VIROLOGY, ACCESS_MINERAL_STOREROOM) - -/datum/outfit/job/virologist - name = "Virologist" - jobtype = /datum/job/virologist - - belt = /obj/item/pda/viro - ears = /obj/item/radio/headset/headset_med - uniform = /obj/item/clothing/under/rank/virologist - mask = /obj/item/clothing/mask/surgical - shoes = /obj/item/clothing/shoes/sneakers/white - suit = /obj/item/clothing/suit/toggle/labcoat/virologist - suit_store = /obj/item/flashlight/pen - - backpack = /obj/item/storage/backpack/virology - satchel = /obj/item/storage/backpack/satchel/vir - duffelbag = /obj/item/storage/backpack/duffelbag/med diff --git a/code/modules/jobs/job_types/medical_doctor.dm b/code/modules/jobs/job_types/medical_doctor.dm new file mode 100644 index 0000000000..19fa1c7158 --- /dev/null +++ b/code/modules/jobs/job_types/medical_doctor.dm @@ -0,0 +1,35 @@ +/datum/job/doctor + title = "Medical Doctor" + flag = DOCTOR + department_head = list("Chief Medical Officer") + department_flag = MEDSCI + faction = "Station" + total_positions = 5 + spawn_positions = 3 + supervisors = "the chief medical officer" + selection_color = "#74b5e0" + + outfit = /datum/outfit/job/doctor + + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_VIROLOGY, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CLONING, ACCESS_MINERAL_STOREROOM) + + display_order = JOB_DISPLAY_ORDER_MEDICAL_DOCTOR + +/datum/outfit/job/doctor + name = "Medical Doctor" + jobtype = /datum/job/doctor + + belt = /obj/item/pda/medical + ears = /obj/item/radio/headset/headset_med + uniform = /obj/item/clothing/under/rank/medical + shoes = /obj/item/clothing/shoes/sneakers/white + suit = /obj/item/clothing/suit/toggle/labcoat + l_hand = /obj/item/storage/firstaid/regular + suit_store = /obj/item/flashlight/pen + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel/med + duffelbag = /obj/item/storage/backpack/duffelbag/med + + chameleon_extras = /obj/item/gun/syringe diff --git a/code/modules/jobs/job_types/mime.dm b/code/modules/jobs/job_types/mime.dm new file mode 100644 index 0000000000..1347da7125 --- /dev/null +++ b/code/modules/jobs/job_types/mime.dm @@ -0,0 +1,49 @@ +/datum/job/mime + title = "Mime" + flag = MIME + department_head = list("Head of Personnel") + department_flag = CIVILIAN + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the head of personnel" + selection_color = "#dddddd" + + outfit = /datum/outfit/job/mime + + access = list(ACCESS_THEATRE) + minimal_access = list(ACCESS_THEATRE) + + display_order = JOB_DISPLAY_ORDER_MIME + +/datum/job/mime/after_spawn(mob/living/carbon/human/H, mob/M) + H.apply_pref_name("mime", M.client) + +/datum/outfit/job/mime + name = "Mime" + jobtype = /datum/job/mime + + belt = /obj/item/pda/mime + ears = /obj/item/radio/headset/headset_srv + uniform = /obj/item/clothing/under/rank/mime + mask = /obj/item/clothing/mask/gas/mime + gloves = /obj/item/clothing/gloves/color/white + head = /obj/item/clothing/head/frenchberet + suit = /obj/item/clothing/suit/suspenders + backpack_contents = list(/obj/item/reagent_containers/food/drinks/bottle/bottleofnothing=1) + + backpack = /obj/item/storage/backpack/mime + satchel = /obj/item/storage/backpack/mime + + +/datum/outfit/job/mime/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + ..() + + if(visualsOnly) + return + + if(H.mind) + H.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/conjure/mime_wall(null)) + H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/mime/speak(null)) + H.mind.miming = 1 + diff --git a/code/modules/jobs/job_types/quartermaster.dm b/code/modules/jobs/job_types/quartermaster.dm new file mode 100644 index 0000000000..49a93026ba --- /dev/null +++ b/code/modules/jobs/job_types/quartermaster.dm @@ -0,0 +1,41 @@ +/datum/job/qm + title = "Quartermaster" + flag = QUARTERMASTER + department_head = list("Captain") + department_flag = CIVILIAN + head_announce = list(RADIO_CHANNEL_SUPPLY) +// auto_deadmin_role_flags = DEADMIN_POSITION_HEAD + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the captain" + selection_color = "#a06121" + req_admin_notify = 1 + minimal_player_age = 7 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + exp_type_department = EXP_TYPE_SUPPLY + + outfit = /datum/outfit/job/quartermaster + + access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_QM, ACCESS_MINING, ACCESS_MINING_STATION, + ACCESS_MINERAL_STOREROOM, ACCESS_VAULT) + minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_QM, ACCESS_MINING, + ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_VAULT) + + display_order = JOB_DISPLAY_ORDER_QUARTERMASTER + blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity) + +/datum/outfit/job/quartermaster + name = "Quartermaster" + jobtype = /datum/job/qm + + belt = /obj/item/pda/quartermaster + ears = /obj/item/radio/headset/headset_cargo + uniform = /obj/item/clothing/under/rank/cargo + shoes = /obj/item/clothing/shoes/sneakers/brown + glasses = /obj/item/clothing/glasses/sunglasses + l_hand = /obj/item/clipboard + + chameleon_extras = /obj/item/stamp/qm + diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm new file mode 100644 index 0000000000..5368ceee64 --- /dev/null +++ b/code/modules/jobs/job_types/research_director.dm @@ -0,0 +1,61 @@ +/datum/job/rd + title = "Research Director" + flag = RD_JF +// auto_deadmin_role_flags = DEADMIN_POSITION_HEAD + department_head = list("Captain") + department_flag = MEDSCI + head_announce = list(RADIO_CHANNEL_SCIENCE) + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the captain" + selection_color = "#7544cc" + req_admin_notify = 1 + minimal_player_age = 7 + exp_type_department = EXP_TYPE_SCIENCE + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + + outfit = /datum/outfit/job/rd + + access = list(ACCESS_RD, ACCESS_HEADS, ACCESS_TOX, ACCESS_GENETICS, ACCESS_MORGUE, + ACCESS_TOX_STORAGE, ACCESS_TELEPORTER, ACCESS_SEC_DOORS, + ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, + ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, + ACCESS_TECH_STORAGE, ACCESS_MINISAT, ACCESS_MAINT_TUNNELS, ACCESS_NETWORK) + minimal_access = list(ACCESS_RD, ACCESS_HEADS, ACCESS_TOX, ACCESS_GENETICS, ACCESS_MORGUE, + ACCESS_TOX_STORAGE, ACCESS_TELEPORTER, ACCESS_SEC_DOORS, + ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, + ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, + ACCESS_TECH_STORAGE, ACCESS_MINISAT, ACCESS_MAINT_TUNNELS, ACCESS_NETWORK) + + display_order = JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR + blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity) + +/datum/outfit/job/rd + name = "Research Director" + jobtype = /datum/job/rd + + id = /obj/item/card/id/silver + belt = /obj/item/pda/heads/rd + ears = /obj/item/radio/headset/heads/rd + uniform = /obj/item/clothing/under/rank/research_director + shoes = /obj/item/clothing/shoes/sneakers/brown + suit = /obj/item/clothing/suit/toggle/labcoat + l_hand = /obj/item/clipboard + l_pocket = /obj/item/laser_pointer + backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/preset/advanced=1) + + backpack = /obj/item/storage/backpack/science + satchel = /obj/item/storage/backpack/satchel/tox + + chameleon_extras = /obj/item/stamp/rd + +/datum/outfit/job/rd/rig + name = "Research Director (Hardsuit)" + + l_hand = null + mask = /obj/item/clothing/mask/breath + suit = /obj/item/clothing/suit/space/hardsuit/rd + suit_store = /obj/item/tank/internals/oxygen + internals_slot = SLOT_S_STORE diff --git a/code/modules/jobs/job_types/roboticist.dm b/code/modules/jobs/job_types/roboticist.dm new file mode 100644 index 0000000000..782b175ad4 --- /dev/null +++ b/code/modules/jobs/job_types/roboticist.dm @@ -0,0 +1,34 @@ +/datum/job/roboticist + title = "Roboticist" + flag = ROBOTICIST + department_head = list("Research Director") + department_flag = MEDSCI + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the research director" + selection_color = "#9574cd" + exp_requirements = 60 + exp_type = EXP_TYPE_CREW + + outfit = /datum/outfit/job/roboticist + + access = list(ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM, ACCESS_XENOBIOLOGY, ACCESS_GENETICS) + minimal_access = list(ACCESS_ROBOTICS, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM) + + display_order = JOB_DISPLAY_ORDER_ROBOTICIST + +/datum/outfit/job/roboticist + name = "Roboticist" + jobtype = /datum/job/roboticist + + belt = /obj/item/storage/belt/utility/full + l_pocket = /obj/item/pda/roboticist + ears = /obj/item/radio/headset/headset_sci + uniform = /obj/item/clothing/under/rank/roboticist + suit = /obj/item/clothing/suit/toggle/labcoat + + backpack = /obj/item/storage/backpack/science + satchel = /obj/item/storage/backpack/satchel/tox + + pda_slot = SLOT_L_STORE diff --git a/code/modules/jobs/job_types/science.dm b/code/modules/jobs/job_types/science.dm deleted file mode 100644 index b58f3faa27..0000000000 --- a/code/modules/jobs/job_types/science.dm +++ /dev/null @@ -1,133 +0,0 @@ -/* -Research Director -*/ -/datum/job/rd - title = "Research Director" - flag = RD_JF - department_head = list("Captain") - department_flag = MEDSCI - head_announce = list(RADIO_CHANNEL_SCIENCE) - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the captain" - selection_color = "#ffddff" - req_admin_notify = 1 - minimal_player_age = 7 - exp_type_department = EXP_TYPE_SCIENCE - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - - outfit = /datum/outfit/job/rd - - access = list(ACCESS_RD, ACCESS_HEADS, ACCESS_TOX, ACCESS_GENETICS, ACCESS_MORGUE, - ACCESS_TOX_STORAGE, ACCESS_TELEPORTER, ACCESS_SEC_DOORS, - ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, - ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, - ACCESS_TECH_STORAGE, ACCESS_MINISAT, ACCESS_MAINT_TUNNELS, ACCESS_NETWORK) - minimal_access = list(ACCESS_RD, ACCESS_HEADS, ACCESS_TOX, ACCESS_GENETICS, ACCESS_MORGUE, - ACCESS_TOX_STORAGE, ACCESS_TELEPORTER, ACCESS_SEC_DOORS, - ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, - ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, - ACCESS_TECH_STORAGE, ACCESS_MINISAT, ACCESS_MAINT_TUNNELS, ACCESS_NETWORK) - - blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity) - -/datum/outfit/job/rd - name = "Research Director" - jobtype = /datum/job/rd - - id = /obj/item/card/id/silver - belt = /obj/item/pda/heads/rd - ears = /obj/item/radio/headset/heads/rd - uniform = /obj/item/clothing/under/rank/research_director - shoes = /obj/item/clothing/shoes/sneakers/brown - suit = /obj/item/clothing/suit/toggle/labcoat - l_hand = /obj/item/clipboard - l_pocket = /obj/item/laser_pointer - backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/preset/advanced=1) - - backpack = /obj/item/storage/backpack/science - satchel = /obj/item/storage/backpack/satchel/tox - - chameleon_extras = /obj/item/stamp/rd - -/datum/outfit/job/rd/rig - name = "Research Director (Hardsuit)" - - l_hand = null - mask = /obj/item/clothing/mask/breath - suit = /obj/item/clothing/suit/space/hardsuit/rd - suit_store = /obj/item/tank/internals/oxygen - internals_slot = SLOT_S_STORE - -/* -Scientist -*/ -/datum/job/scientist - title = "Scientist" - flag = SCIENTIST - department_head = list("Research Director") - department_flag = MEDSCI - faction = "Station" - total_positions = 5 - spawn_positions = 3 - supervisors = "the research director" - selection_color = "#ffeeff" - exp_requirements = 60 - exp_type = EXP_TYPE_CREW - - - outfit = /datum/outfit/job/scientist - - access = list(ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_MINERAL_STOREROOM, ACCESS_TECH_STORAGE, ACCESS_GENETICS) - minimal_access = list(ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_MINERAL_STOREROOM) - -/datum/outfit/job/scientist - name = "Scientist" - jobtype = /datum/job/scientist - - belt = /obj/item/pda/toxins - ears = /obj/item/radio/headset/headset_sci - uniform = /obj/item/clothing/under/rank/scientist - shoes = /obj/item/clothing/shoes/sneakers/white - suit = /obj/item/clothing/suit/toggle/labcoat/science - - backpack = /obj/item/storage/backpack/science - satchel = /obj/item/storage/backpack/satchel/tox - -/* -Roboticist -*/ -/datum/job/roboticist - title = "Roboticist" - flag = ROBOTICIST - department_head = list("Research Director") - department_flag = MEDSCI - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the research director" - selection_color = "#ffeeff" - exp_requirements = 60 - exp_type = EXP_TYPE_CREW - - outfit = /datum/outfit/job/roboticist - - access = list(ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM, ACCESS_XENOBIOLOGY, ACCESS_GENETICS) - minimal_access = list(ACCESS_ROBOTICS, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM) - -/datum/outfit/job/roboticist - name = "Roboticist" - jobtype = /datum/job/roboticist - - belt = /obj/item/storage/belt/utility/full - l_pocket = /obj/item/pda/roboticist - ears = /obj/item/radio/headset/headset_sci - uniform = /obj/item/clothing/under/rank/roboticist - suit = /obj/item/clothing/suit/toggle/labcoat - - backpack = /obj/item/storage/backpack/science - satchel = /obj/item/storage/backpack/satchel/tox - - pda_slot = SLOT_L_STORE diff --git a/code/modules/jobs/job_types/scientist.dm b/code/modules/jobs/job_types/scientist.dm new file mode 100644 index 0000000000..f40a25d6ba --- /dev/null +++ b/code/modules/jobs/job_types/scientist.dm @@ -0,0 +1,33 @@ +/datum/job/scientist + title = "Scientist" + flag = SCIENTIST + department_head = list("Research Director") + department_flag = MEDSCI + faction = "Station" + total_positions = 5 + spawn_positions = 3 + supervisors = "the research director" + selection_color = "#9574cd" + exp_requirements = 60 + exp_type = EXP_TYPE_CREW + + outfit = /datum/outfit/job/scientist + + access = list(ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_MINERAL_STOREROOM, ACCESS_TECH_STORAGE, ACCESS_GENETICS) + minimal_access = list(ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_MINERAL_STOREROOM) + + display_order = JOB_DISPLAY_ORDER_SCIENTIST + +/datum/outfit/job/scientist + name = "Scientist" + jobtype = /datum/job/scientist + + belt = /obj/item/pda/toxins + ears = /obj/item/radio/headset/headset_sci + uniform = /obj/item/clothing/under/rank/scientist + shoes = /obj/item/clothing/shoes/sneakers/white + suit = /obj/item/clothing/suit/toggle/labcoat/science + + backpack = /obj/item/storage/backpack/science + satchel = /obj/item/storage/backpack/satchel/tox + diff --git a/code/modules/jobs/job_types/security.dm b/code/modules/jobs/job_types/security.dm deleted file mode 100644 index 96cedd89ef..0000000000 --- a/code/modules/jobs/job_types/security.dm +++ /dev/null @@ -1,346 +0,0 @@ -//Warden and regular officers add this result to their get_access() -/datum/job/proc/check_config_for_sec_maint() - if(CONFIG_GET(flag/security_has_maint_access)) - return list(ACCESS_MAINT_TUNNELS) - return list() - -/* -Head of Security -*/ -/datum/job/hos - title = "Head of Security" - flag = HOS - department_head = list("Captain") - department_flag = ENGSEC - head_announce = list(RADIO_CHANNEL_SECURITY) - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the captain" - selection_color = "#ffdddd" - req_admin_notify = 1 - minimal_player_age = 14 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - exp_type_department = EXP_TYPE_SECURITY - - outfit = /datum/outfit/job/hos - - access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_WEAPONS, - ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_ALL_PERSONAL_LOCKERS, - ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING, - ACCESS_HEADS, ACCESS_HOS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_WEAPONS, - ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_ALL_PERSONAL_LOCKERS, - ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING, - ACCESS_HEADS, ACCESS_HOS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) - - mind_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM) - - blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/nonviolent, /datum/quirk/paraplegic, /datum/quirk/insanity) - -/datum/outfit/job/hos - name = "Head of Security" - jobtype = /datum/job/hos - - id = /obj/item/card/id/silver - belt = /obj/item/pda/heads/hos - ears = /obj/item/radio/headset/heads/hos/alt - uniform = /obj/item/clothing/under/rank/head_of_security - shoes = /obj/item/clothing/shoes/jackboots - suit = /obj/item/clothing/suit/armor/hos/trenchcoat - gloves = /obj/item/clothing/gloves/color/black/hos - head = /obj/item/clothing/head/HoS/beret - glasses = /obj/item/clothing/glasses/hud/security/sunglasses - suit_store = /obj/item/gun/energy/e_gun - r_pocket = /obj/item/assembly/flash/handheld - l_pocket = /obj/item/restraints/handcuffs - backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1) - - backpack = /obj/item/storage/backpack/security - satchel = /obj/item/storage/backpack/satchel/sec - duffelbag = /obj/item/storage/backpack/duffelbag/sec - box = /obj/item/storage/box/security - - implants = list(/obj/item/implant/mindshield) - - chameleon_extras = list(/obj/item/gun/energy/e_gun/hos, /obj/item/stamp/hos) - -/datum/outfit/job/hos/hardsuit - name = "Head of Security (Hardsuit)" - - mask = /obj/item/clothing/mask/gas/sechailer - suit = /obj/item/clothing/suit/space/hardsuit/security/hos - suit_store = /obj/item/tank/internals/oxygen - backpack_contents = list(/obj/item/melee/baton/loaded=1, /obj/item/gun/energy/e_gun=1) - -/* -Warden -*/ -/datum/job/warden - title = "Warden" - flag = WARDEN - department_head = list("Head of Security") - department_flag = ENGSEC - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the head of security" - selection_color = "#ffeeee" - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - - outfit = /datum/outfit/job/warden - - access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_FORENSICS_LOCKERS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) //SEE /DATUM/JOB/WARDEN/GET_ACCESS() - - mind_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM) - blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/nonviolent, /datum/quirk/paraplegic) - -/datum/job/warden/get_access() - var/list/L = list() - L = ..() | check_config_for_sec_maint() - return L - -/datum/outfit/job/warden - name = "Warden" - jobtype = /datum/job/warden - - belt = /obj/item/pda/warden - ears = /obj/item/radio/headset/headset_sec/alt - uniform = /obj/item/clothing/under/rank/warden - shoes = /obj/item/clothing/shoes/jackboots - suit = /obj/item/clothing/suit/armor/vest/warden/alt - gloves = /obj/item/clothing/gloves/color/black - head = /obj/item/clothing/head/warden - glasses = /obj/item/clothing/glasses/hud/security/sunglasses - r_pocket = /obj/item/assembly/flash/handheld - l_pocket = /obj/item/restraints/handcuffs - suit_store = /obj/item/gun/energy/e_gun/advtaser - backpack_contents = list(/obj/item/melee/baton/loaded=1) - - backpack = /obj/item/storage/backpack/security - satchel = /obj/item/storage/backpack/satchel/sec - duffelbag = /obj/item/storage/backpack/duffelbag/sec - box = /obj/item/storage/box/security - - implants = list(/obj/item/implant/mindshield) - - chameleon_extras = /obj/item/gun/ballistic/shotgun/automatic/combat/compact - -/* -Detective -*/ -/datum/job/detective - title = "Detective" - flag = DETECTIVE - department_head = list("Head of Security") - department_flag = ENGSEC - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the head of security" - selection_color = "#ffeeee" - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - - outfit = /datum/outfit/job/detective - - access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_COURT, ACCESS_BRIG, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_COURT, ACCESS_BRIG, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) - - mind_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM) - blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/nonviolent, /datum/quirk/paraplegic) - -/datum/outfit/job/detective - name = "Detective" - jobtype = /datum/job/detective - - belt = /obj/item/pda/detective - ears = /obj/item/radio/headset/headset_sec/alt - uniform = /obj/item/clothing/under/rank/det - shoes = /obj/item/clothing/shoes/sneakers/brown - suit = /obj/item/clothing/suit/det_suit - gloves = /obj/item/clothing/gloves/color/black - head = /obj/item/clothing/head/fedora/det_hat - l_pocket = /obj/item/toy/crayon/white - r_pocket = /obj/item/lighter - backpack_contents = list(/obj/item/storage/box/evidence=1,\ - /obj/item/detective_scanner=1,\ - /obj/item/melee/classic_baton=1) - mask = /obj/item/clothing/mask/cigarette - - implants = list(/obj/item/implant/mindshield) - - chameleon_extras = list(/obj/item/gun/ballistic/revolver/detective, /obj/item/clothing/glasses/sunglasses) - -/datum/outfit/job/detective/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - ..() - var/obj/item/clothing/mask/cigarette/cig = H.wear_mask - if(istype(cig)) //Some species specfic changes can mess this up (plasmamen) - cig.light("") - - if(visualsOnly) - return - -/* -Security Officer -*/ -/datum/job/officer - title = "Security Officer" - flag = OFFICER - department_head = list("Head of Security") - department_flag = ENGSEC - faction = "Station" - total_positions = 5 //Handled in /datum/controller/occupations/proc/setup_officer_positions() - spawn_positions = 5 //Handled in /datum/controller/occupations/proc/setup_officer_positions() - supervisors = "the head of security, and the head of your assigned department (if applicable)" - selection_color = "#ffeeee" - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - - outfit = /datum/outfit/job/security - - access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_FORENSICS_LOCKERS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) //BUT SEE /DATUM/JOB/WARDEN/GET_ACCESS() - - mind_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM) - blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/nonviolent, /datum/quirk/paraplegic) - -/datum/job/officer/get_access() - var/list/L = list() - L |= ..() | check_config_for_sec_maint() - return L - -GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, SEC_DEPT_SCIENCE, SEC_DEPT_SUPPLY)) - -/datum/job/officer/after_spawn(mob/living/carbon/human/H, mob/M) - // Assign department security - var/department - if(M && M.client && M.client.prefs) - department = M.client.prefs.prefered_security_department - if(!LAZYLEN(GLOB.available_depts) || department == "None") - return - else if(department in GLOB.available_depts) - LAZYREMOVE(GLOB.available_depts, department) - else - department = pick_n_take(GLOB.available_depts) - var/ears = null - var/accessory = null - var/list/dep_access = null - var/destination = null - var/spawn_point = null - switch(department) - if(SEC_DEPT_SUPPLY) - ears = /obj/item/radio/headset/headset_sec/alt/department/supply - dep_access = list(ACCESS_MAILSORTING, ACCESS_MINING, ACCESS_MINING_STATION) - destination = /area/security/checkpoint/supply - spawn_point = locate(/obj/effect/landmark/start/depsec/supply) in GLOB.department_security_spawns - accessory = /obj/item/clothing/accessory/armband/cargo - if(SEC_DEPT_ENGINEERING) - ears = /obj/item/radio/headset/headset_sec/alt/department/engi - dep_access = list(ACCESS_CONSTRUCTION, ACCESS_ENGINE) - destination = /area/security/checkpoint/engineering - spawn_point = locate(/obj/effect/landmark/start/depsec/engineering) in GLOB.department_security_spawns - accessory = /obj/item/clothing/accessory/armband/engine - if(SEC_DEPT_MEDICAL) - ears = /obj/item/radio/headset/headset_sec/alt/department/med - dep_access = list(ACCESS_MEDICAL) - destination = /area/security/checkpoint/medical - spawn_point = locate(/obj/effect/landmark/start/depsec/medical) in GLOB.department_security_spawns - accessory = /obj/item/clothing/accessory/armband/medblue - if(SEC_DEPT_SCIENCE) - ears = /obj/item/radio/headset/headset_sec/alt/department/sci - dep_access = list(ACCESS_RESEARCH) - destination = /area/security/checkpoint/science - spawn_point = locate(/obj/effect/landmark/start/depsec/science) in GLOB.department_security_spawns - accessory = /obj/item/clothing/accessory/armband/science - - if(accessory) - var/obj/item/clothing/under/U = H.w_uniform - U.attach_accessory(new accessory) - if(ears) - if(H.ears) - qdel(H.ears) - H.equip_to_slot_or_del(new ears(H),SLOT_EARS) - - var/obj/item/card/id/W = H.wear_id - W.access |= dep_access - - var/teleport = 0 - if(!CONFIG_GET(flag/sec_start_brig)) - if(destination || spawn_point) - teleport = 1 - if(teleport) - var/turf/T - if(spawn_point) - T = get_turf(spawn_point) - H.Move(T) - else - var/safety = 0 - while(safety < 25) - T = safepick(get_area_turfs(destination)) - if(T && !H.Move(T)) - safety += 1 - continue - else - break - if(department) - to_chat(M, "You have been assigned to [department]!") - else - to_chat(M, "You have not been assigned to any department. Patrol the halls and help where needed.") - - - -/datum/outfit/job/security - name = "Security Officer" - jobtype = /datum/job/officer - - belt = /obj/item/pda/security - ears = /obj/item/radio/headset/headset_sec/alt - uniform = /obj/item/clothing/under/rank/security - gloves = /obj/item/clothing/gloves/color/black - head = /obj/item/clothing/head/helmet/sec - suit = /obj/item/clothing/suit/armor/vest/alt - shoes = /obj/item/clothing/shoes/jackboots - l_pocket = /obj/item/restraints/handcuffs - r_pocket = /obj/item/assembly/flash/handheld - suit_store = /obj/item/gun/energy/e_gun/advtaser - backpack_contents = list(/obj/item/melee/baton/loaded=1) - - backpack = /obj/item/storage/backpack/security - satchel = /obj/item/storage/backpack/satchel/sec - duffelbag = /obj/item/storage/backpack/duffelbag/sec - box = /obj/item/storage/box/security - - implants = list(/obj/item/implant/mindshield) - - chameleon_extras = list(/obj/item/gun/energy/e_gun/advtaser, /obj/item/clothing/glasses/hud/security/sunglasses, /obj/item/clothing/head/helmet) - //The helmet is necessary because /obj/item/clothing/head/helmet/sec is overwritten in the chameleon list by the standard helmet, which has the same name and icon state - - -/obj/item/radio/headset/headset_sec/alt/department/Initialize() - . = ..() - wires = new/datum/wires/radio(src) - secure_radio_connections = new - recalculateChannels() - -/obj/item/radio/headset/headset_sec/alt/department/engi - keyslot = new /obj/item/encryptionkey/headset_sec - keyslot2 = new /obj/item/encryptionkey/headset_eng - -/obj/item/radio/headset/headset_sec/alt/department/supply - keyslot = new /obj/item/encryptionkey/headset_sec - keyslot2 = new /obj/item/encryptionkey/headset_cargo - -/obj/item/radio/headset/headset_sec/alt/department/med - keyslot = new /obj/item/encryptionkey/headset_sec - keyslot2 = new /obj/item/encryptionkey/headset_med - -/obj/item/radio/headset/headset_sec/alt/department/sci - keyslot = new /obj/item/encryptionkey/headset_sec - keyslot2 = new /obj/item/encryptionkey/headset_sci diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm new file mode 100644 index 0000000000..4f12d6a19c --- /dev/null +++ b/code/modules/jobs/job_types/security_officer.dm @@ -0,0 +1,159 @@ +/datum/job/officer + title = "Security Officer" + flag = OFFICER +// auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY + department_head = list("Head of Security") + department_flag = ENGSEC + faction = "Station" + total_positions = 5 //Handled in /datum/controller/occupations/proc/setup_officer_positions() + spawn_positions = 5 //Handled in /datum/controller/occupations/proc/setup_officer_positions() + supervisors = "the head of security, and the head of your assigned department (if applicable)" + selection_color = "#c02f2f" + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + + outfit = /datum/outfit/job/security + + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_FORENSICS_LOCKERS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) // See /datum/job/officer/get_access() + + mind_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM) + + display_order = JOB_DISPLAY_ORDER_SECURITY_OFFICER + blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/nonviolent, /datum/quirk/paraplegic) + +/datum/job/officer/get_access() + var/list/L = list() + L |= ..() | check_config_for_sec_maint() + return L + +GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, SEC_DEPT_SCIENCE, SEC_DEPT_SUPPLY)) + +/datum/job/officer/after_spawn(mob/living/carbon/human/H, mob/M) + . = ..() + // Assign department security + var/department + if(M && M.client && M.client.prefs) + department = M.client.prefs.prefered_security_department + if(!LAZYLEN(GLOB.available_depts) || department == "None") + return + else if(department in GLOB.available_depts) + LAZYREMOVE(GLOB.available_depts, department) + else + department = pick_n_take(GLOB.available_depts) + var/ears = null + var/accessory = null + var/list/dep_access = null + var/destination = null + var/spawn_point = null + switch(department) + if(SEC_DEPT_SUPPLY) + ears = /obj/item/radio/headset/headset_sec/alt/department/supply + dep_access = list(ACCESS_MAILSORTING, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_CARGO) + destination = /area/security/checkpoint/supply + spawn_point = locate(/obj/effect/landmark/start/depsec/supply) in GLOB.department_security_spawns + accessory = /obj/item/clothing/accessory/armband/cargo + if(SEC_DEPT_ENGINEERING) + ears = /obj/item/radio/headset/headset_sec/alt/department/engi + dep_access = list(ACCESS_CONSTRUCTION, ACCESS_ENGINE, ACCESS_ATMOSPHERICS) + destination = /area/security/checkpoint/engineering + spawn_point = locate(/obj/effect/landmark/start/depsec/engineering) in GLOB.department_security_spawns + accessory = /obj/item/clothing/accessory/armband/engine + if(SEC_DEPT_MEDICAL) + ears = /obj/item/radio/headset/headset_sec/alt/department/med + dep_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CLONING) + destination = /area/security/checkpoint/medical + spawn_point = locate(/obj/effect/landmark/start/depsec/medical) in GLOB.department_security_spawns + accessory = /obj/item/clothing/accessory/armband/medblue + if(SEC_DEPT_SCIENCE) + ears = /obj/item/radio/headset/headset_sec/alt/department/sci + dep_access = list(ACCESS_RESEARCH, ACCESS_TOX) + destination = /area/security/checkpoint/science + spawn_point = locate(/obj/effect/landmark/start/depsec/science) in GLOB.department_security_spawns + accessory = /obj/item/clothing/accessory/armband/science + + if(accessory) + var/obj/item/clothing/under/U = H.w_uniform + U.attach_accessory(new accessory) + if(ears) + if(H.ears) + qdel(H.ears) + H.equip_to_slot_or_del(new ears(H),SLOT_EARS) + + var/obj/item/card/id/W = H.wear_id + W.access |= dep_access + + var/teleport = 0 + if(!CONFIG_GET(flag/sec_start_brig)) + if(destination || spawn_point) + teleport = 1 + if(teleport) + var/turf/T + if(spawn_point) + T = get_turf(spawn_point) + H.Move(T) + else + var/safety = 0 + while(safety < 25) + T = safepick(get_area_turfs(destination)) + if(T && !H.Move(T)) + safety += 1 + continue + else + break + if(department) + to_chat(M, "You have been assigned to [department]!") + else + to_chat(M, "You have not been assigned to any department. Patrol the halls and help where needed.") + + + +/datum/outfit/job/security + name = "Security Officer" + jobtype = /datum/job/officer + + belt = /obj/item/pda/security + ears = /obj/item/radio/headset/headset_sec/alt + uniform = /obj/item/clothing/under/rank/security + gloves = /obj/item/clothing/gloves/color/black + head = /obj/item/clothing/head/helmet/sec + suit = /obj/item/clothing/suit/armor/vest/alt + shoes = /obj/item/clothing/shoes/jackboots + l_pocket = /obj/item/restraints/handcuffs + r_pocket = /obj/item/assembly/flash/handheld + suit_store = /obj/item/gun/energy/e_gun/advtaser + backpack_contents = list(/obj/item/melee/baton/loaded=1) + + backpack = /obj/item/storage/backpack/security + satchel = /obj/item/storage/backpack/satchel/sec + duffelbag = /obj/item/storage/backpack/duffelbag/sec + box = /obj/item/storage/box/security + + implants = list(/obj/item/implant/mindshield) + + chameleon_extras = list(/obj/item/gun/energy/disabler, /obj/item/clothing/glasses/hud/security/sunglasses, /obj/item/clothing/head/helmet) + //The helmet is necessary because /obj/item/clothing/head/helmet/sec is overwritten in the chameleon list by the standard helmet, which has the same name and icon state + + +/obj/item/radio/headset/headset_sec/alt/department/Initialize() + . = ..() + wires = new/datum/wires/radio(src) + secure_radio_connections = new + recalculateChannels() + +/obj/item/radio/headset/headset_sec/alt/department/engi + keyslot = new /obj/item/encryptionkey/headset_sec + keyslot2 = new /obj/item/encryptionkey/headset_eng + +/obj/item/radio/headset/headset_sec/alt/department/supply + keyslot = new /obj/item/encryptionkey/headset_sec + keyslot2 = new /obj/item/encryptionkey/headset_cargo + +/obj/item/radio/headset/headset_sec/alt/department/med + keyslot = new /obj/item/encryptionkey/headset_sec + keyslot2 = new /obj/item/encryptionkey/headset_med + +/obj/item/radio/headset/headset_sec/alt/department/sci + keyslot = new /obj/item/encryptionkey/headset_sec + keyslot2 = new /obj/item/encryptionkey/headset_sci diff --git a/code/modules/jobs/job_types/shaft_miner.dm b/code/modules/jobs/job_types/shaft_miner.dm new file mode 100644 index 0000000000..ef16d8e53f --- /dev/null +++ b/code/modules/jobs/job_types/shaft_miner.dm @@ -0,0 +1,77 @@ +/datum/job/mining + title = "Shaft Miner" + flag = MINER + department_head = list("Quartermaster") + department_flag = CIVILIAN + faction = "Station" + total_positions = 3 + spawn_positions = 3 + supervisors = "the quartermaster" + selection_color = "#ca8f55" + custom_spawn_text = "Remember, you are a miner, not a hunter. Hunting monsters is not a requirement of your job, the only requirement of your job is to provide materials for the station. Don't be afraid to run away if you're inexperienced with fighting the mining area's locals." + + + outfit = /datum/outfit/job/miner + + access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_QM, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM) + + display_order = JOB_DISPLAY_ORDER_SHAFT_MINER + +/datum/outfit/job/miner + name = "Shaft Miner (Lavaland)" + jobtype = /datum/job/mining + + belt = /obj/item/pda/shaftminer + ears = /obj/item/radio/headset/headset_cargo/mining + shoes = /obj/item/clothing/shoes/workboots/mining + gloves = /obj/item/clothing/gloves/color/black + uniform = /obj/item/clothing/under/rank/miner/lavaland + l_pocket = /obj/item/reagent_containers/hypospray/medipen/survival + r_pocket = /obj/item/storage/bag/ore //causes issues if spawned in backpack + backpack_contents = list( + /obj/item/flashlight/seclite=1,\ + /obj/item/kitchen/knife/combat/survival=1,\ + /obj/item/mining_voucher=1,\ + /obj/item/suit_voucher=1,\ + /obj/item/stack/marker_beacon/ten=1) + + backpack = /obj/item/storage/backpack/explorer + satchel = /obj/item/storage/backpack/satchel/explorer + duffelbag = /obj/item/storage/backpack/duffelbag + box = /obj/item/storage/box/survival_mining + + chameleon_extras = /obj/item/gun/energy/kinetic_accelerator + +/datum/outfit/job/miner/asteroid + name = "Shaft Miner (Asteroid)" + uniform = /obj/item/clothing/under/rank/miner + shoes = /obj/item/clothing/shoes/workboots + +/datum/outfit/job/miner/equipped + name = "Shaft Miner (Lavaland + Equipment)" + suit = /obj/item/clothing/suit/hooded/explorer/standard + mask = /obj/item/clothing/mask/gas/explorer + glasses = /obj/item/clothing/glasses/meson + suit_store = /obj/item/tank/internals/oxygen + internals_slot = SLOT_S_STORE + backpack_contents = list( + /obj/item/flashlight/seclite=1,\ + /obj/item/kitchen/knife/combat/survival=1, + /obj/item/mining_voucher=1, + /obj/item/t_scanner/adv_mining_scanner/lesser=1, + /obj/item/gun/energy/kinetic_accelerator=1,\ + /obj/item/stack/marker_beacon/ten=1) + +/datum/outfit/job/miner/equipped/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + ..() + if(visualsOnly) + return + if(istype(H.wear_suit, /obj/item/clothing/suit/hooded)) + var/obj/item/clothing/suit/hooded/S = H.wear_suit + S.ToggleHood() + +/datum/outfit/job/miner/equipped/hardsuit + name = "Shaft Miner (Equipment + Hardsuit)" + suit = /obj/item/clothing/suit/space/hardsuit/mining + mask = /obj/item/clothing/mask/breath diff --git a/code/modules/jobs/job_types/station_engineer.dm b/code/modules/jobs/job_types/station_engineer.dm new file mode 100644 index 0000000000..55381549ba --- /dev/null +++ b/code/modules/jobs/job_types/station_engineer.dm @@ -0,0 +1,54 @@ +/datum/job/engineer + title = "Station Engineer" + flag = ENGINEER + department_head = list("Chief Engineer") + department_flag = ENGSEC + faction = "Station" + total_positions = 5 + spawn_positions = 5 + supervisors = "the chief engineer" + selection_color = "#ff9b3d" + exp_requirements = 60 + exp_type = EXP_TYPE_CREW + + outfit = /datum/outfit/job/engineer + + access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, + ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, + ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM) + + display_order = JOB_DISPLAY_ORDER_STATION_ENGINEER + +/datum/outfit/job/engineer + name = "Station Engineer" + jobtype = /datum/job/engineer + + belt = /obj/item/storage/belt/utility/full/engi + l_pocket = /obj/item/pda/engineering + ears = /obj/item/radio/headset/headset_eng + uniform = /obj/item/clothing/under/rank/engineer + shoes = /obj/item/clothing/shoes/workboots + head = /obj/item/clothing/head/hardhat + r_pocket = /obj/item/t_scanner + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel/eng + duffelbag = /obj/item/storage/backpack/duffelbag/engineering + box = /obj/item/storage/box/engineer + pda_slot = SLOT_L_STORE + backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1) + +/datum/outfit/job/engineer/gloved + name = "Station Engineer (Gloves)" + gloves = /obj/item/clothing/gloves/color/yellow + +/datum/outfit/job/engineer/gloved/rig + name = "Station Engineer (Hardsuit)" + mask = /obj/item/clothing/mask/breath + suit = /obj/item/clothing/suit/space/hardsuit/engine + suit_store = /obj/item/tank/internals/oxygen + head = null + internals_slot = SLOT_S_STORE + + diff --git a/code/modules/jobs/job_types/virologist.dm b/code/modules/jobs/job_types/virologist.dm new file mode 100644 index 0000000000..dcc13af627 --- /dev/null +++ b/code/modules/jobs/job_types/virologist.dm @@ -0,0 +1,35 @@ +/datum/job/virologist + title = "Virologist" + flag = VIROLOGIST + department_head = list("Chief Medical Officer") + department_flag = MEDSCI + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the chief medical officer" + selection_color = "#74b5e0" + exp_type = EXP_TYPE_CREW + exp_requirements = 60 + + outfit = /datum/outfit/job/virologist + + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_VIROLOGY, ACCESS_MINERAL_STOREROOM) + + display_order = JOB_DISPLAY_ORDER_VIROLOGIST + +/datum/outfit/job/virologist + name = "Virologist" + jobtype = /datum/job/virologist + + belt = /obj/item/pda/viro + ears = /obj/item/radio/headset/headset_med + uniform = /obj/item/clothing/under/rank/virologist + mask = /obj/item/clothing/mask/surgical + shoes = /obj/item/clothing/shoes/sneakers/white + suit = /obj/item/clothing/suit/toggle/labcoat/virologist + suit_store = /obj/item/flashlight/pen + + backpack = /obj/item/storage/backpack/virology + satchel = /obj/item/storage/backpack/satchel/vir + duffelbag = /obj/item/storage/backpack/duffelbag/med diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm new file mode 100644 index 0000000000..a5c16ab5cf --- /dev/null +++ b/code/modules/jobs/job_types/warden.dm @@ -0,0 +1,56 @@ +/datum/job/warden + title = "Warden" + flag = WARDEN +// auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY + department_head = list("Head of Security") + department_flag = ENGSEC + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the head of security" + selection_color = "#c02f2f" + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + + outfit = /datum/outfit/job/warden + + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_FORENSICS_LOCKERS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) // See /datum/job/warden/get_access() + + mind_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM) + + display_order = JOB_DISPLAY_ORDER_WARDEN + blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/nonviolent, /datum/quirk/paraplegic) + +/datum/job/warden/get_access() + var/list/L = list() + L = ..() | check_config_for_sec_maint() + return L + +/datum/outfit/job/warden + name = "Warden" + jobtype = /datum/job/warden + + belt = /obj/item/pda/warden + ears = /obj/item/radio/headset/headset_sec/alt + uniform = /obj/item/clothing/under/rank/warden + shoes = /obj/item/clothing/shoes/jackboots + suit = /obj/item/clothing/suit/armor/vest/warden/alt + gloves = /obj/item/clothing/gloves/color/black + head = /obj/item/clothing/head/warden + glasses = /obj/item/clothing/glasses/hud/security/sunglasses + r_pocket = /obj/item/assembly/flash/handheld + l_pocket = /obj/item/restraints/handcuffs + suit_store = /obj/item/gun/energy/e_gun/advtaser + backpack_contents = list(/obj/item/melee/baton/loaded=1) + + backpack = /obj/item/storage/backpack/security + satchel = /obj/item/storage/backpack/satchel/sec + duffelbag = /obj/item/storage/backpack/duffelbag/sec + box = /obj/item/storage/box/security + + implants = list(/obj/item/implant/mindshield) + + chameleon_extras = /obj/item/gun/ballistic/shotgun/automatic/combat/compact + diff --git a/code/modules/keybindings/bindings_client.dm b/code/modules/keybindings/bindings_client.dm index 548a734f74..2b8bfa6860 100644 --- a/code/modules/keybindings/bindings_client.dm +++ b/code/modules/keybindings/bindings_client.dm @@ -4,7 +4,42 @@ set instant = TRUE set hidden = TRUE + client_keysend_amount += 1 + + var/cache = client_keysend_amount + + if(keysend_tripped && next_keysend_trip_reset <= world.time) + keysend_tripped = FALSE + + if(next_keysend_reset <= world.time) + client_keysend_amount = 0 + next_keysend_reset = world.time + (1 SECONDS) + + //The "tripped" system is to confirm that flooding is still happening after one spike + //not entirely sure how byond commands interact in relation to lag + //don't want to kick people if a lag spike results in a huge flood of commands being sent + if(cache >= MAX_KEYPRESS_AUTOKICK) + if(!keysend_tripped) + keysend_tripped = TRUE + next_keysend_trip_reset = world.time + (2 SECONDS) + else + log_admin("Client [ckey] was just autokicked for flooding keysends; likely abuse but potentially lagspike.") + message_admins("Client [ckey] was just autokicked for flooding keysends; likely abuse but potentially lagspike.") + QDEL_IN(src, 1) + return + + ///Check if the key is short enough to even be a real key + if(LAZYLEN(_key) > MAX_KEYPRESS_COMMANDLENGTH) + to_chat(src, "Invalid KeyDown detected! You have been disconnected from the server automatically.") + log_admin("Client [ckey] just attempted to send an invalid keypress. Keymessage was over [MAX_KEYPRESS_COMMANDLENGTH] characters, autokicking due to likely abuse.") + message_admins("Client [ckey] just attempted to send an invalid keypress. Keymessage was over [MAX_KEYPRESS_COMMANDLENGTH] characters, autokicking due to likely abuse.") + QDEL_IN(src, 1) + return + //offset by 1 because the buffer address is 0 indexed because the math was simpler + keys_held[current_key_address + 1] = _key + //the time a key was pressed isn't actually used anywhere (as of 2019-9-10) but this allows easier access usage/checking keys_held[_key] = world.time + current_key_address = ((current_key_address + 1) % HELD_KEY_BUFFER_LENGTH) var/movement = SSinput.movement_keys[_key] if(!(next_move_dir_sub & movement) && !keys_held["Ctrl"]) next_move_dir_add |= movement @@ -35,7 +70,11 @@ set instant = TRUE set hidden = TRUE - keys_held -= _key + //Can't just do a remove because it would alter the length of the rolling buffer, instead search for the key then null it out if it exists + for(var/i in 1 to HELD_KEY_BUFFER_LENGTH) + if(keys_held[i] == _key) + keys_held[i] = null + break var/movement = SSinput.movement_keys[_key] if(!(next_move_dir_add & movement)) next_move_dir_sub |= movement diff --git a/code/modules/keybindings/setup.dm b/code/modules/keybindings/setup.dm index 54df252f5d..8433c9bf5a 100644 --- a/code/modules/keybindings/setup.dm +++ b/code/modules/keybindings/setup.dm @@ -1,9 +1,14 @@ /client - var/list/keys_held = list() // A list of any keys held currently - // These next two vars are to apply movement for keypresses and releases made while move delayed. - // Because discarding that input makes the game less responsive. - var/next_move_dir_add // On next move, add this dir to the move that would otherwise be done - var/next_move_dir_sub // On next move, subtract this dir from the move that would otherwise be done + /// A rolling buffer of any keys held currently + var/list/keys_held = list() + ///used to keep track of the current rolling buffer position + var/current_key_address = 0 + /// These next two vars are to apply movement for keypresses and releases made while move delayed. + /// Because discarding that input makes the game less responsive. + /// On next move, add this dir to the move that would otherwise be done + var/next_move_dir_add + /// On next move, subtract this dir from the move that would otherwise be done + var/next_move_dir_sub // Set a client's focus to an object and override these procs on that object to let it handle keypresses @@ -31,6 +36,11 @@ /client/proc/set_macros() set waitfor = FALSE + //Reset and populate the rolling buffer + keys_held.Cut() + for(var/i in 1 to HELD_KEY_BUFFER_LENGTH) + keys_held += null + erase_all_macros() var/list/macro_sets = SSinput.macro_sets diff --git a/code/modules/library/lib_codex_gigas.dm b/code/modules/library/lib_codex_gigas.dm index d3d95db974..57bf37d528 100644 --- a/code/modules/library/lib_codex_gigas.dm +++ b/code/modules/library/lib_codex_gigas.dm @@ -50,8 +50,8 @@ if(U.job in list("Curator")) // the curator is both faster, and more accurate than normal crew members at research speed = 100 correctness = 100 - correctness -= U.getBrainLoss() *0.5 //Brain damage makes researching hard. - speed += U.getBrainLoss() * 3 + correctness -= U.getOrganLoss(ORGAN_SLOT_BRAIN) *0.5 //Brain damage makes researching hard. + speed += U.getOrganLoss(ORGAN_SLOT_BRAIN) * 3 if(do_after(user, speed, 0, user)) var/usedName = devilName if(!prob(correctness)) diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm index 7a875ccf9f..e2459d780a 100644 --- a/code/modules/mapping/mapping_helpers.dm +++ b/code/modules/mapping/mapping_helpers.dm @@ -213,4 +213,4 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava) if(!ispath(disease_type,/datum/disease)) CRASH("Wrong disease type passed in.") var/datum/disease/D = new disease_type() - return list(component_type,D) \ No newline at end of file + return list(component_type,D) diff --git a/code/modules/mining/abandoned_crates.dm b/code/modules/mining/abandoned_crates.dm index 4a0faf69f5..f98f0755c8 100644 --- a/code/modules/mining/abandoned_crates.dm +++ b/code/modules/mining/abandoned_crates.dm @@ -169,6 +169,7 @@ locked = FALSE cut_overlays() add_overlay("securecrateg") + tamperproof = 0 // set explosion chance to zero, so we dont accidently hit it with a multitool and instantly die else if (input == null || sanitycheck == null || length(input) != codelen) to_chat(user, "You leave the crate alone.") else @@ -213,6 +214,12 @@ return return ..() +/obj/structure/closet/secure/loot/dive_into(mob/living/user) + if(!locked) + return ..() + to_chat(user, "That seems like a stupid idea.") + return FALSE + /obj/structure/closet/crate/secure/loot/emag_act(mob/user) . = SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT) if(!locked) @@ -227,4 +234,6 @@ ..() /obj/structure/closet/crate/secure/loot/deconstruct(disassembled = TRUE) + if(!locked && disassembled) + return ..() boom() diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index 953da6cbb0..4e54c3e222 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -10,12 +10,10 @@ GLOBAL_LIST(labor_sheet_values) density = FALSE var/obj/machinery/mineral/stacking_machine/laborstacker/stacking_machine = null var/machinedir = SOUTH - var/obj/item/card/id/prisoner/inserted_id var/obj/machinery/door/airlock/release_door var/door_tag = "prisonshuttle" var/obj/item/radio/Radio //needed to send messages to sec radio - /obj/machinery/mineral/labor_claim_console/Initialize() . = ..() Radio = new/obj/item/radio(src) @@ -34,18 +32,6 @@ GLOBAL_LIST(labor_sheet_values) /proc/cmp_sheet_list(list/a, list/b) return a["value"] - b["value"] -/obj/machinery/mineral/labor_claim_console/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/card/id/prisoner)) - if(!inserted_id) - if(!user.transferItemToLoc(I, src)) - return - inserted_id = I - to_chat(user, "You insert [I].") - return - else - to_chat(user, "There's an ID inserted already.") - return ..() - /obj/machinery/mineral/labor_claim_console/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) @@ -58,14 +44,20 @@ GLOBAL_LIST(labor_sheet_values) var/can_go_home = FALSE data["emagged"] = (obj_flags & EMAGGED) ? 1 : 0 - if(inserted_id) - data["id"] = inserted_id - data["id_name"] = inserted_id.registered_name - data["points"] = inserted_id.points - data["goal"] = inserted_id.goal - if(check_auth()) + if(obj_flags & EMAGGED) can_go_home = TRUE + data["status_info"] = "No Prisoner ID detected." + var/obj/item/card/id/I = user.get_idcard(TRUE) + if(istype(I, /obj/item/card/id/prisoner)) + var/obj/item/card/id/prisoner/P = I + data["id_points"] = P.points + if(P.points >= P.goal) + can_go_home = TRUE + data["status_info"] = "Goal met!" + else + data["status_info"] = "You are [(P.goal - P.points)] points away." + if(stacking_machine) data["unclaimed_points"] = stacking_machine.points @@ -78,29 +70,19 @@ GLOBAL_LIST(labor_sheet_values) if(..()) return switch(action) - if("handle_id") - if(inserted_id) - if(!usr.get_active_held_item()) - usr.put_in_hands(inserted_id) - inserted_id = null - else - inserted_id.forceMove(get_turf(src)) - inserted_id = null - else - var/obj/item/I = usr.get_active_held_item() - if(istype(I, /obj/item/card/id/prisoner)) - if(!usr.transferItemToLoc(I, src)) - return - inserted_id = I if("claim_points") - inserted_id.points += stacking_machine.points - stacking_machine.points = 0 - to_chat(usr, "Points transferred.") + var/mob/M = usr + var/obj/item/card/id/I = M.get_idcard(TRUE) + if(istype(I, /obj/item/card/id/prisoner)) + var/obj/item/card/id/prisoner/P = I + P.points += stacking_machine.points + stacking_machine.points = 0 + to_chat(usr, "Points transferred.") + else + to_chat(usr, "No valid id for point transfer detected.") if("move_shuttle") if(!alone_in_area(get_area(src), usr)) to_chat(usr, "Prisoners are only allowed to be released while alone.") - else if(!check_auth()) - to_chat(usr, "Prisoners are only allowed to be released when they reach their point goal.") else switch(SSshuttle.moveShuttle("laborcamp", "laborcamp_home", TRUE)) if(1) @@ -112,14 +94,9 @@ GLOBAL_LIST(labor_sheet_values) else if(!(obj_flags & EMAGGED)) Radio.set_frequency(FREQ_SECURITY) - Radio.talk_into(src, "[inserted_id.registered_name] has returned to the station. Minerals and Prisoner ID card ready for retrieval.", FREQ_SECURITY) + Radio.talk_into(src, "A prisoner has returned to the station. Minerals and Prisoner ID card ready for retrieval.", FREQ_SECURITY) to_chat(usr, "Shuttle received message and will be sent shortly.") -/obj/machinery/mineral/labor_claim_console/proc/check_auth() - if(obj_flags & EMAGGED) - return 1 //Shuttle is emagged, let any ol' person through - return (istype(inserted_id) && inserted_id.points >= inserted_id.goal) //Otherwise, only let them out if the prisoner's reached his quota. - /obj/machinery/mineral/labor_claim_console/proc/locate_stacking_machine() stacking_machine = locate(/obj/machinery/mineral/stacking_machine, get_step(src, machinedir)) if(stacking_machine) @@ -135,10 +112,8 @@ GLOBAL_LIST(labor_sheet_values) to_chat(user, "PZZTTPFFFT") return TRUE - /**********************Prisoner Collection Unit**************************/ - /obj/machinery/mineral/stacking_machine/laborstacker force_connect = TRUE var/points = 0 //The unclaimed value of ore stacked. @@ -154,6 +129,7 @@ GLOBAL_LIST(labor_sheet_values) return ..() /**********************Point Lookup Console**************************/ + /obj/machinery/mineral/labor_points_checker name = "points checking console" desc = "A console used by prisoners to check the progress on their quotas. Simply swipe a prisoner ID." diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index 4605f7d693..6c1a00b020 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -13,7 +13,6 @@ speed_process = TRUE circuit = /obj/item/circuitboard/machine/ore_redemption layer = BELOW_OBJ_LAYER - var/obj/item/card/id/inserted_id var/points = 0 var/ore_pickup_rate = 15 var/sheet_per_ore = 1 @@ -48,18 +47,23 @@ point_upgrade = point_upgrade_temp sheet_per_ore = sheet_per_ore_temp +/obj/machinery/mineral/ore_redemption/examine(mob/user) + . = ..() + if(in_range(user, src) || isobserver(user)) + . += "The status display reads: Smelting [sheet_per_ore] sheet(s) per piece of ore.
Ore pickup speed at [ore_pickup_rate].
" + /obj/machinery/mineral/ore_redemption/proc/smelt_ore(obj/item/stack/ore/O) var/datum/component/material_container/mat_container = materials.mat_container if (!mat_container) return - if(istype(O, /obj/item/stack/ore/bluespace_crystal/refined)) + if(O.refined_type == null) return ore_buffer -= O if(O && O.refined_type) - points += O.points * point_upgrade * O.amount + points += O.points * O.amount var/material_amount = mat_container.get_item_material_amount(O) @@ -72,11 +76,8 @@ else var/mats = O.materials & mat_container.materials var/amount = O.amount - var/id = inserted_id && inserted_id.registered_name - if (id) - id = " (ID: [id])" mat_container.insert_item(O, sheet_per_ore) //insert it - materials.silo_log(src, "smelted", amount, "ores[id]", mats) + materials.silo_log(src, "smelted", amount, "ores", mats) qdel(O) /obj/machinery/mineral/ore_redemption/proc/can_smelt_alloy(datum/design/D) @@ -168,15 +169,7 @@ return if(!powered()) - return - if(istype(W, /obj/item/card/id)) - var/obj/item/card/id/I = user.get_active_held_item() - if(istype(I) && !istype(inserted_id)) - if(!user.transferItemToLoc(I, src)) - return - inserted_id = I - interact(user) - return + return ..() if(istype(W, /obj/item/disk/design_disk)) if(user.transferItemToLoc(W, src)) @@ -205,9 +198,6 @@ /obj/machinery/mineral/ore_redemption/ui_data(mob/user) var/list/data = list() data["unclaimedPoints"] = points - if(inserted_id) - data["hasID"] = TRUE - data["claimedPoints"] = inserted_id.mining_points data["materials"] = list() var/datum/component/material_container/mat_container = materials.mat_container @@ -245,32 +235,24 @@ return var/datum/component/material_container/mat_container = materials.mat_container switch(action) - if("Eject") - if(!inserted_id) - return - usr.put_in_hands(inserted_id) - inserted_id = null - return TRUE - if("Insert") - var/obj/item/card/id/I = usr.get_active_held_item() - if(istype(I)) - if(!usr.transferItemToLoc(I,src)) - return - inserted_id = I - else - to_chat(usr, "Not a valid ID!") - return TRUE if("Claim") - if(inserted_id) - inserted_id.mining_points += points - points = 0 + var/mob/M = usr + var/obj/item/card/id/I = M.get_idcard(TRUE) + if(points) + if(I) + I.mining_points += points + points = 0 + else + to_chat(usr, "No ID detected.") + else + to_chat(usr, "No points to claim.") return TRUE if("Release") if(!mat_container) return if(materials.on_hold()) to_chat(usr, "Mineral access is on hold, please contact the quartermaster.") - else if(!check_access(inserted_id) && !allowed(usr)) //Check the ID inside, otherwise check the user + else if(!allowed(usr)) //Check the ID inside, otherwise check the user to_chat(usr, "Required access not found.") else var/mat_id = params["id"] @@ -293,6 +275,7 @@ var/list/mats = list() mats[mat_id] = MINERAL_MATERIAL_AMOUNT materials.silo_log(src, "released", -count, "sheets", mats) + //Logging deleted for quick coding return TRUE if("diskInsert") var/obj/item/disk/design_disk/disk = usr.get_active_held_item() @@ -321,7 +304,7 @@ return var/alloy_id = params["id"] var/datum/design/alloy = stored_research.isDesignResearchedID(alloy_id) - if((check_access(inserted_id) || allowed(usr)) && alloy) + if((check_access(inserted_scan_id) || allowed(usr)) && alloy) var/smelt_amount = can_smelt_alloy(alloy) var/desired = 0 if (params["sheets"]) diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm index aed90cebdf..38d2e3e100 100644 --- a/code/modules/mining/machine_vending.dm +++ b/code/modules/mining/machine_vending.dm @@ -20,6 +20,7 @@ new /datum/data/mining_equipment("Soap", /obj/item/soap/nanotrasen, 200), new /datum/data/mining_equipment("Laser Pointer", /obj/item/laser_pointer, 300), new /datum/data/mining_equipment("Alien Toy", /obj/item/clothing/mask/facehugger/toy, 300), + new /datum/data/mining_equipment("Stabilizing Serum", /obj/item/hivelordstabilizer, 400), new /datum/data/mining_equipment("Fulton Beacon", /obj/item/fulton_core, 400), new /datum/data/mining_equipment("Shelter Capsule", /obj/item/survivalcapsule, 400), new /datum/data/mining_equipment("Survival Knife", /obj/item/kitchen/knife/combat/survival, 450), @@ -28,11 +29,10 @@ new /datum/data/mining_equipment("Larger Ore Bag", /obj/item/storage/bag/ore/large, 500), new /datum/data/mining_equipment("500 Point Transfer Card", /obj/item/card/mining_point_card/mp500, 500), new /datum/data/mining_equipment("Tracking Implant Kit", /obj/item/storage/box/minertracker, 600), - new /datum/data/mining_equipment("Survival Medipen", /obj/item/reagent_containers/hypospray/medipen/survival, 750), - new /datum/data/mining_equipment("Stabilizing Serum", /obj/item/hivelordstabilizer, 750), new /datum/data/mining_equipment("Jaunter", /obj/item/wormhole_jaunter, 750), new /datum/data/mining_equipment("Kinetic Crusher", /obj/item/twohanded/required/kinetic_crusher, 750), new /datum/data/mining_equipment("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750), + new /datum/data/mining_equipment("Survival Medipen", /obj/item/reagent_containers/hypospray/medipen/survival, 750), new /datum/data/mining_equipment("Brute First-Aid Kit", /obj/item/storage/firstaid/brute, 800), new /datum/data/mining_equipment("Burn First-Aid Kit", /obj/item/storage/firstaid/fire, 800), new /datum/data/mining_equipment("First-Aid Kit", /obj/item/storage/firstaid/regular, 800), @@ -54,7 +54,6 @@ new /datum/data/mining_equipment("Super Resonator", /obj/item/resonator/upgraded, 2500), new /datum/data/mining_equipment("Jump Boots", /obj/item/clothing/shoes/bhop, 2500), new /datum/data/mining_equipment("Luxury Shelter Capsule", /obj/item/survivalcapsule/luxury, 3000), - new /datum/data/mining_equipment("Miner Full Replacement", /obj/item/storage/backpack/duffelbag/mining_cloned, 3000), new /datum/data/mining_equipment("Nanotrasen Minebot", /mob/living/simple_animal/hostile/mining_drone, 800), new /datum/data/mining_equipment("Minebot Melee Upgrade", /obj/item/mine_bot_upgrade, 400), new /datum/data/mining_equipment("Minebot Armor Upgrade", /obj/item/mine_bot_upgrade/health, 400), @@ -69,8 +68,8 @@ new /datum/data/mining_equipment("KA Damage Increase", /obj/item/borg/upgrade/modkit/damage, 1000), new /datum/data/mining_equipment("KA Cooldown Decrease", /obj/item/borg/upgrade/modkit/cooldown, 1000), new /datum/data/mining_equipment("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000), + new /datum/data/mining_equipment("Miner Full Replacement", /obj/item/storage/backpack/duffelbag/mining_cloned, 3000), new /datum/data/mining_equipment("Premium Accelerator", /obj/item/gun/energy/kinetic_accelerator/premiumka, 8000) - ) /datum/data/mining_equipment @@ -95,60 +94,42 @@ /obj/machinery/mineral/equipment_vendor/ui_interact(mob/user) . = ..() - var/dat - dat +="
" - if(istype(inserted_id)) - dat += "You have [inserted_id.mining_points] mining points collected. Eject ID.
" - else - dat += "No ID inserted. Insert ID.
" - dat += "
" + var/list/dat = list() dat += "
Equipment point cost list:
Name: [active1.fields["name"]] 
ID: [active1.fields["id"]] 
Sex: [active1.fields["sex"]] 
Gender: [active1.fields["gender"]] 
Age: [active1.fields["age"]] 
Species: [active1.fields["species"]] 
Rank: [active1.fields["rank"]] 
" -#define MAX_MUTANT_ROWS 4 +#define MAX_MUTANT_ROWS 5 /datum/preferences/proc/ShowChoices(mob/user) if(!user || !user.client) @@ -351,9 +344,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "[TextPreview(features["flavor_text"])]...
" dat += "

Body

" dat += "Gender:[gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]
" - dat += "Species:[pref_species.id]
" + dat += "Species:[pref_species.name]
" dat += "Custom Species Name:[custom_species ? custom_species : "None"]
" - dat += "Random Body
" + dat += "Random Body:Randomize!
" dat += "Always Random Body:[be_random_body ? "Yes" : "No"]
" dat += "
Cycle background:[bgstate]
" @@ -448,6 +441,19 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "[features["tail_human"]]" + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "
BANNED
BANNED
\[IN [(available_in_days)] DAYS\]
\[MUTANT\]
\[NON-HUMAN\]
" for(var/datum/data/mining_equipment/prize in prize_list) dat += "" dat += "
[prize.equipment_name][prize.cost]Purchase
" var/datum/browser/popup = new(user, "miningvendor", "Mining Equipment Vendor", 400, 350) - popup.set_content(dat) + popup.set_content(dat.Join()) popup.open() return /obj/machinery/mineral/equipment_vendor/Topic(href, href_list) if(..()) return - if(href_list["choice"]) - if(istype(inserted_id)) - if(href_list["choice"] == "eject") - to_chat(usr, "You eject the ID from [src]'s card slot.") - inserted_id.forceMove(loc) - inserted_id.verb_pickup() - inserted_id = null - else if(href_list["choice"] == "insert") - var/obj/item/card/id/I = usr.get_active_held_item() - if(istype(I)) - if(!usr.transferItemToLoc(I, src)) - return - inserted_id = I - to_chat(usr, "You insert the ID into [src]'s card slot.") - else - to_chat(usr, "Error: No valid ID!") - flick(icon_deny, src) if(href_list["purchase"]) - if(istype(inserted_id)) - var/datum/data/mining_equipment/prize = locate(href_list["purchase"]) + var/mob/M = usr + var/obj/item/card/id/I = M.get_idcard(TRUE) + if(istype(I)) + var/datum/data/mining_equipment/prize = locate(href_list["purchase"]) in prize_list if (!prize || !(prize in prize_list)) to_chat(usr, "Error: Invalid choice!") flick(icon_deny, src) return - if(prize.cost > inserted_id.mining_points) - to_chat(usr, "Error: Insufficient points for [prize.equipment_name]!") + if(prize.cost > I.mining_points) + to_chat(usr, "Error: Insufficient credits for [prize.equipment_name] on [I]!") flick(icon_deny, src) else - inserted_id.mining_points -= prize.cost - to_chat(usr, "[src] clanks to life briefly before vending [prize.equipment_name]!") - new prize.equipment_path(src.loc) - SSblackbox.record_feedback("nested tally", "mining_equipment_bought", 1, list("[type]", "[prize.equipment_path]")) + if (I.mining_points -= prize.cost) + to_chat(usr, "[src] clanks to life briefly before vending [prize.equipment_name]!") + new prize.equipment_path(src.loc) + SSblackbox.record_feedback("nested tally", "mining_equipment_bought", 1, list("[type]", "[prize.equipment_path]")) + else + to_chat(usr, "Error: Transaction failure, please try again later!") + flick(icon_deny, src) else - to_chat(usr, "Error: Please insert a valid ID!") + to_chat(usr, "Error: An ID with a registered account is required!") flick(icon_deny, src) updateUsrDialog() return @@ -160,15 +141,6 @@ if(istype(I, /obj/item/suit_voucher)) RedeemSVoucher(I, user) return - if(istype(I, /obj/item/card/id)) - var/obj/item/card/id/C = usr.get_active_held_item() - if(istype(C) && !istype(inserted_id)) - if(!usr.transferItemToLoc(C, src)) - return - inserted_id = C - to_chat(usr, "You insert the ID into [src]'s card slot.") - interact(user) - return if(default_deconstruction_screwdriver(user, "mining-open", "mining", I)) updateUsrDialog() return @@ -212,7 +184,6 @@ if(prob(50 / severity) && severity < 3) qdel(src) - /****************Golem Point Vendor**************************/ /obj/machinery/mineral/equipment_vendor/golem @@ -228,7 +199,6 @@ new /datum/data/mining_equipment("Monkey Cube", /obj/item/reagent_containers/food/snacks/monkeycube, 300), new /datum/data/mining_equipment("Toolbelt", /obj/item/storage/belt/utility, 350), new /datum/data/mining_equipment("Royal Cape of the Liberator", /obj/item/bedsheet/rd/royal_cape, 500), - new /datum/data/mining_equipment("Sulphuric Acid", /obj/item/reagent_containers/glass/beaker/sulphuric, 500), new /datum/data/mining_equipment("Grey Slime Extract", /obj/item/slime_extract/grey, 1000), new /datum/data/mining_equipment("Modification Kit", /obj/item/borg/upgrade/modkit/trigger_guard, 1700), new /datum/data/mining_equipment("The Liberator's Legacy", /obj/item/storage/box/rndboards, 2000) @@ -323,8 +293,11 @@ new /obj/item/encryptionkey/headset_cargo(src) new /obj/item/clothing/mask/gas/explorer(src) new /obj/item/card/mining_access_card(src) + new /obj/item/gun/energy/kinetic_accelerator(src) + new /obj/item/kitchen/knife/combat/survival(src) + new /obj/item/flashlight/seclite(src) -//CITADEL ADDITIONS BELOW + //CITADEL ADDITIONS BELOW /obj/item/storage/backpack/duffelbag/mining_cloned name = "mining replacement kit" @@ -336,6 +309,7 @@ new /obj/item/clothing/shoes/workboots/mining(src) new /obj/item/clothing/gloves/color/black(src) new /obj/item/implanter/tracking/gps(src) + new /obj/item/gun/energy/kinetic_accelerator(src) new /obj/item/kitchen/knife/combat/survival(src) new /obj/item/storage/firstaid/regular(src) new /obj/item/reagent_containers/hypospray/medipen/survival(src) diff --git a/code/modules/mob/camera/camera.dm b/code/modules/mob/camera/camera.dm index c780d57810..ee89167f29 100644 --- a/code/modules/mob/camera/camera.dm +++ b/code/modules/mob/camera/camera.dm @@ -30,5 +30,8 @@ loc = destination Moved(oldloc, NONE, TRUE) +/mob/camera/canUseStorage() + return FALSE + /mob/camera/emote(act, m_type=1, message = null, intentional = FALSE) return diff --git a/code/modules/mob/dead/dead.dm b/code/modules/mob/dead/dead.dm index 381dc131e7..35dbe0828f 100644 --- a/code/modules/mob/dead/dead.dm +++ b/code/modules/mob/dead/dead.dm @@ -20,6 +20,9 @@ INITIALIZE_IMMEDIATE(/mob/dead) set_focus(src) return INITIALIZE_HINT_NORMAL +/mob/dead/canUseStorage() + return FALSE + /mob/dead/dust(just_ash, drop_items, force) //ghosts can't be vaporised. return diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 750805ae93..1cd32d43ac 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -1,604 +1,623 @@ -#define LINKIFY_READY(string, value) "[string]" - -/mob/dead/new_player - var/ready = 0 - var/spawning = 0//Referenced when you want to delete the new_player later on in the code. - - flags_1 = NONE - - invisibility = INVISIBILITY_ABSTRACT - - density = FALSE - stat = DEAD - canmove = FALSE - - anchored = TRUE // don't get pushed around - var/mob/living/new_character //for instant transfer once the round is set up - -/mob/dead/new_player/Initialize() - if(client && SSticker.state == GAME_STATE_STARTUP) - var/obj/screen/splash/S = new(client, TRUE, TRUE) - S.Fade(TRUE) - - if(length(GLOB.newplayer_start)) - forceMove(pick(GLOB.newplayer_start)) - else - forceMove(locate(1,1,1)) - - ComponentInitialize() - - . = ..() - -/mob/dead/new_player/prepare_huds() - return - -/mob/dead/new_player/proc/new_player_panel() - var/output = "

Welcome, [client ? client.prefs.real_name : "Unknown User"]

" - output += "

Setup Character

" - - if(SSticker.current_state <= GAME_STATE_PREGAME) - switch(ready) - if(PLAYER_NOT_READY) - output += "

\[ [LINKIFY_READY("Ready", PLAYER_READY_TO_PLAY)] | Not Ready | [LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] \]

" - if(PLAYER_READY_TO_PLAY) - output += "

\[ Ready | [LINKIFY_READY("Not Ready", PLAYER_NOT_READY)] | [LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] \]

" - if(PLAYER_READY_TO_OBSERVE) - output += "

\[ [LINKIFY_READY("Ready", PLAYER_READY_TO_PLAY)] | [LINKIFY_READY("Not Ready", PLAYER_NOT_READY)] | Observe \]

" - else - output += "

View the Crew Manifest

" - output += "

Join Game!

" - output += "

[LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)]

" - - if(!IsGuestKey(src.key)) - if (SSdbcore.Connect()) - var/isadmin = 0 - if(src.client && src.client.holder) - isadmin = 1 - var/datum/DBQuery/query_get_new_polls = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = \"[sanitizeSQL(ckey)]\") AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = \"[sanitizeSQL(ckey)]\")") - var/rs = REF(src) - if(query_get_new_polls.Execute()) - var/newpoll = 0 - if(query_get_new_polls.NextRow()) - newpoll = 1 - - if(newpoll) - output += "

Show Player Polls (NEW!)

" - else - output += "

Show Player Polls

" - qdel(query_get_new_polls) - if(QDELETED(src)) - return - - output += "
" - - //src << browse(output,"window=playersetup;size=210x240;can_close=0") - var/datum/browser/popup = new(src, "playersetup", "
New Player Options
", 250, 265) - popup.set_window_options("can_close=0") - popup.set_content(output) - popup.open(0) - -/mob/dead/new_player/Topic(href, href_list[]) - if(src != usr) - return 0 - - if(!client) - return 0 - - //Determines Relevent Population Cap - var/relevant_cap - var/hpc = CONFIG_GET(number/hard_popcap) - var/epc = CONFIG_GET(number/extreme_popcap) - if(hpc && epc) - relevant_cap = min(hpc, epc) - else - relevant_cap = max(hpc, epc) - - if(href_list["show_preferences"]) - client.prefs.ShowChoices(src) - return 1 - - if(href_list["ready"]) - var/tready = text2num(href_list["ready"]) - //Avoid updating ready if we're after PREGAME (they should use latejoin instead) - //This is likely not an actual issue but I don't have time to prove that this - //no longer is required - if(SSticker.current_state <= GAME_STATE_PREGAME) - ready = tready - //if it's post initialisation and they're trying to observe we do the needful - if(!SSticker.current_state < GAME_STATE_PREGAME && tready == PLAYER_READY_TO_OBSERVE) - ready = tready - make_me_an_observer() - return - - if(href_list["refresh"]) - src << browse(null, "window=playersetup") //closes the player setup window - new_player_panel() - - if(href_list["late_join"]) - if(!SSticker || !SSticker.IsRoundInProgress()) - to_chat(usr, "The round is either not ready, or has already finished...") - return - - if(href_list["late_join"] == "override") - LateChoices() - return - - if(SSticker.queued_players.len || (relevant_cap && living_player_count() >= relevant_cap && !(ckey(key) in GLOB.admin_datums))) - to_chat(usr, "[CONFIG_GET(string/hard_popcap_message)]") - - var/queue_position = SSticker.queued_players.Find(usr) - if(queue_position == 1) - to_chat(usr, "You are next in line to join the game. You will be notified when a slot opens up.") - else if(queue_position) - to_chat(usr, "There are [queue_position-1] players in front of you in the queue to join the game.") - else - SSticker.queued_players += usr - to_chat(usr, "You have been added to the queue to join the game. Your position in queue is [SSticker.queued_players.len].") - return - LateChoices() - - if(href_list["manifest"]) - ViewManifest() - - if(href_list["SelectedJob"]) - - if(!GLOB.enter_allowed) - to_chat(usr, "There is an administrative lock on entering the game!") - return - - if(SSticker.queued_players.len && !(ckey(key) in GLOB.admin_datums)) - if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1])) - to_chat(usr, "Server is full.") - return - - AttemptLateSpawn(href_list["SelectedJob"]) - return - - if(href_list["JoinAsGhostRole"]) - if(!GLOB.enter_allowed) - to_chat(usr, " There is an administrative lock on entering the game!") - - if(SSticker.queued_players.len && !(ckey(key) in GLOB.admin_datums)) - if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1])) - to_chat(usr, "Server is full.") - return - - var/obj/effect/mob_spawn/MS = pick(GLOB.mob_spawners[href_list["JoinAsGhostRole"]]) - if(MS.attack_ghost(src, latejoinercalling = TRUE)) - SSticker.queued_players -= src - SSticker.queue_delay = 4 - qdel(src) - - if(!ready && href_list["preference"]) - if(client) - client.prefs.process_link(src, href_list) - else if(!href_list["late_join"]) - new_player_panel() - - if(href_list["showpoll"]) - handle_player_polling() - return - - if(href_list["pollid"]) - var/pollid = href_list["pollid"] - if(istext(pollid)) - pollid = text2num(pollid) - if(isnum(pollid) && ISINTEGER(pollid)) - src.poll_player(pollid) - return - - if(href_list["votepollid"] && href_list["votetype"]) - var/pollid = text2num(href_list["votepollid"]) - var/votetype = href_list["votetype"] - //lets take data from the user to decide what kind of poll this is, without validating it - //what could go wrong - switch(votetype) - if(POLLTYPE_OPTION) - var/optionid = text2num(href_list["voteoptionid"]) - if(vote_on_poll(pollid, optionid)) - to_chat(usr, "Vote successful.") - else - to_chat(usr, "Vote failed, please try again or contact an administrator.") - if(POLLTYPE_TEXT) - var/replytext = href_list["replytext"] - if(log_text_poll_reply(pollid, replytext)) - to_chat(usr, "Feedback logging successful.") - else - to_chat(usr, "Feedback logging failed, please try again or contact an administrator.") - if(POLLTYPE_RATING) - var/id_min = text2num(href_list["minid"]) - var/id_max = text2num(href_list["maxid"]) - - if( (id_max - id_min) > 100 ) //Basic exploit prevention - //(protip, this stops no exploits) - to_chat(usr, "The option ID difference is too big. Please contact administration or the database admin.") - return - - for(var/optionid = id_min; optionid <= id_max; optionid++) - if(!isnull(href_list["o[optionid]"])) //Test if this optionid was replied to - var/rating - if(href_list["o[optionid]"] == "abstain") - rating = null - else - rating = text2num(href_list["o[optionid]"]) - if(!isnum(rating) || !ISINTEGER(rating)) - return - - if(!vote_on_numval_poll(pollid, optionid, rating)) - to_chat(usr, "Vote failed, please try again or contact an administrator.") - return - to_chat(usr, "Vote successful.") - if(POLLTYPE_MULTI) - var/id_min = text2num(href_list["minoptionid"]) - var/id_max = text2num(href_list["maxoptionid"]) - - if( (id_max - id_min) > 100 ) //Basic exploit prevention - to_chat(usr, "The option ID difference is too big. Please contact administration or the database admin.") - return - - for(var/optionid = id_min; optionid <= id_max; optionid++) - if(!isnull(href_list["option_[optionid]"])) //Test if this optionid was selected - var/i = vote_on_multi_poll(pollid, optionid) - switch(i) - if(0) - continue - if(1) - to_chat(usr, "Vote failed, please try again or contact an administrator.") - return - if(2) - to_chat(usr, "Maximum replies reached.") - break - to_chat(usr, "Vote successful.") - if(POLLTYPE_IRV) - if (!href_list["IRVdata"]) - to_chat(src, "No ordering data found. Please try again or contact an administrator.") - return - var/list/votelist = splittext(href_list["IRVdata"], ",") - if (!vote_on_irv_poll(pollid, votelist)) - to_chat(src, "Vote failed, please try again or contact an administrator.") - return - to_chat(src, "Vote successful.") - -//When you cop out of the round (NB: this HAS A SLEEP FOR PLAYER INPUT IN IT) -/mob/dead/new_player/proc/make_me_an_observer() - if(QDELETED(src) || !src.client) - ready = PLAYER_NOT_READY - return FALSE - - var/this_is_like_playing_right = alert(src,"Are you sure you wish to observe? You will not be able to play this round!","Player Setup","Yes","No") - - if(QDELETED(src) || !src.client || this_is_like_playing_right != "Yes") - ready = PLAYER_NOT_READY - src << browse(null, "window=playersetup") //closes the player setup window - new_player_panel() - return FALSE - - var/mob/dead/observer/observer = new() - spawning = TRUE - - observer.started_as_observer = TRUE - close_spawn_windows() - var/obj/effect/landmark/observer_start/O = locate(/obj/effect/landmark/observer_start) in GLOB.landmarks_list - to_chat(src, "Now teleporting.") - if (O) - observer.forceMove(O.loc) - else - to_chat(src, "Teleporting failed. Ahelp an admin please") - stack_trace("There's no freaking observer landmark available on this map or you're making observers before the map is initialised") - observer.key = key - observer.client = client - observer.set_ghost_appearance() - if(observer.client && observer.client.prefs) - observer.real_name = observer.client.prefs.real_name - observer.name = observer.real_name - observer.update_icon() - observer.stop_sound_channel(CHANNEL_LOBBYMUSIC) - QDEL_NULL(mind) - qdel(src) - return TRUE - -/proc/get_job_unavailable_error_message(retval, jobtitle) - switch(retval) - if(JOB_AVAILABLE) - return "[jobtitle] is available." - if(JOB_UNAVAILABLE_GENERIC) - return "[jobtitle] is unavailable." - if(JOB_UNAVAILABLE_BANNED) - return "You are currently banned from [jobtitle]." - if(JOB_UNAVAILABLE_PLAYTIME) - return "You do not have enough relevant playtime for [jobtitle]." - if(JOB_UNAVAILABLE_ACCOUNTAGE) - return "Your account is not old enough for [jobtitle]." - if(JOB_UNAVAILABLE_SLOTFULL) - return "[jobtitle] is already filled to capacity." - return "Error: Unknown job availability." - -/mob/dead/new_player/proc/IsJobUnavailable(rank, latejoin = FALSE) - var/datum/job/job = SSjob.GetJob(rank) - if(!job) - return JOB_UNAVAILABLE_GENERIC - if((job.current_positions >= job.total_positions) && job.total_positions != -1) - if(job.title == "Assistant") - if(isnum(client.player_age) && client.player_age <= 14) //Newbies can always be assistants - return JOB_AVAILABLE - for(var/datum/job/J in SSjob.occupations) - if(J && J.current_positions < J.total_positions && J.title != job.title) - return JOB_UNAVAILABLE_SLOTFULL - else - return JOB_UNAVAILABLE_SLOTFULL - if(jobban_isbanned(src,rank)) - return JOB_UNAVAILABLE_BANNED - if(QDELETED(src)) - return JOB_UNAVAILABLE_GENERIC - if(!job.player_old_enough(client)) - return JOB_UNAVAILABLE_ACCOUNTAGE - if(job.required_playtime_remaining(client)) - return JOB_UNAVAILABLE_PLAYTIME - if(latejoin && !job.special_check_latejoin(client)) - return JOB_UNAVAILABLE_GENERIC - return JOB_AVAILABLE - -/mob/dead/new_player/proc/AttemptLateSpawn(rank) - var/error = IsJobUnavailable(rank) - if(error != JOB_AVAILABLE) - alert(src, get_job_unavailable_error_message(error, rank)) - return FALSE - - if(SSticker.late_join_disabled) - alert(src, "An administrator has disabled late join spawning.") - return FALSE - - var/arrivals_docked = TRUE - if(SSshuttle.arrivals) - close_spawn_windows() //In case we get held up - if(SSshuttle.arrivals.damaged && CONFIG_GET(flag/arrivals_shuttle_require_safe_latejoin)) - src << alert("The arrivals shuttle is currently malfunctioning! You cannot join.") - return FALSE - - if(CONFIG_GET(flag/arrivals_shuttle_require_undocked)) - SSshuttle.arrivals.RequireUndocked(src) - arrivals_docked = SSshuttle.arrivals.mode != SHUTTLE_CALL - - //Remove the player from the join queue if he was in one and reset the timer - SSticker.queued_players -= src - SSticker.queue_delay = 4 - - SSjob.AssignRole(src, rank, 1) - - var/mob/living/character = create_character(TRUE) //creates the human and transfers vars and mind - var/equip = SSjob.EquipRank(character, rank, TRUE) - if(isliving(equip)) //Borgs get borged in the equip, so we need to make sure we handle the new mob. - character = equip - - var/datum/job/job = SSjob.GetJob(rank) - - if(job && !job.override_latejoin_spawn(character)) - SSjob.SendToLateJoin(character) - if(!arrivals_docked) - var/obj/screen/splash/Spl = new(character.client, TRUE) - Spl.Fade(TRUE) - character.playsound_local(get_turf(character), 'sound/voice/ApproachingTG.ogg', 25) - - character.update_parallax_teleport() - - SSticker.minds += character.mind - - var/mob/living/carbon/human/humanc - if(ishuman(character)) - humanc = character //Let's retypecast the var to be human, - - if(humanc) //These procs all expect humans - GLOB.data_core.manifest_inject(humanc) - if(SSshuttle.arrivals) - SSshuttle.arrivals.QueueAnnounce(humanc, rank) - else - AnnounceArrival(humanc, rank) - AddEmploymentContract(humanc) - if(GLOB.highlander) - to_chat(humanc, "THERE CAN BE ONLY ONE!!!") - humanc.make_scottish() - - if(GLOB.summon_guns_triggered) - give_guns(humanc) - if(GLOB.summon_magic_triggered) - give_magic(humanc) - - GLOB.joined_player_list += character.ckey - GLOB.latejoiners += character - - if(CONFIG_GET(flag/allow_latejoin_antagonists) && humanc) //Borgs aren't allowed to be antags. Will need to be tweaked if we get true latejoin ais. - if(SSshuttle.emergency) - switch(SSshuttle.emergency.mode) - if(SHUTTLE_RECALL, SHUTTLE_IDLE) - SSticker.mode.make_antag_chance(humanc) - if(SHUTTLE_CALL) - if(SSshuttle.emergency.timeLeft(1) > initial(SSshuttle.emergencyCallTime)*0.5) - SSticker.mode.make_antag_chance(humanc) - - if(humanc && CONFIG_GET(flag/roundstart_traits)) - SSquirks.AssignQuirks(humanc, humanc.client, TRUE, FALSE, job, FALSE) - - log_manifest(character.mind.key,character.mind,character,latejoin = TRUE) - -/mob/dead/new_player/proc/AddEmploymentContract(mob/living/carbon/human/employee) - //TODO: figure out a way to exclude wizards/nukeops/demons from this. - for(var/C in GLOB.employmentCabinets) - var/obj/structure/filingcabinet/employment/employmentCabinet = C - if(!employmentCabinet.virgin) - employmentCabinet.addFile(employee) - - -/mob/dead/new_player/proc/LateChoices() - - var/level = "green" - switch(GLOB.security_level) - if(SEC_LEVEL_BLUE) - level = "blue" - if(SEC_LEVEL_AMBER) - level = "amber" - if(SEC_LEVEL_RED) - level = "red" - if(SEC_LEVEL_DELTA) - level = "delta" - - var/dat = "
Round Duration: [DisplayTimeText(world.time - SSticker.round_start_time)]
Alert Level: [capitalize(level)]
" - - if(SSshuttle.emergency) - switch(SSshuttle.emergency.mode) - if(SHUTTLE_ESCAPE) - dat += "
The station has been evacuated.

" - if(SHUTTLE_CALL) - if(!SSshuttle.canRecall()) - dat += "
The station is currently undergoing evacuation procedures.

" - - var/available_job_count = 0 - for(var/datum/job/job in SSjob.occupations) - if(job && IsJobUnavailable(job.title, TRUE) == JOB_AVAILABLE) - available_job_count++ - for(var/spawner in GLOB.mob_spawners) - if(!LAZYLEN(spawner)) - continue - var/obj/effect/mob_spawn/S = pick(GLOB.mob_spawners[spawner]) - if(!istype(S) || !S.can_latejoin()) - continue - available_job_count++ - break - - if(!available_job_count) - dat += "
There are currently no open positions!
" - - else - dat += "
Choose from the following open positions:

" - var/list/categorizedJobs = list( - "Command" = list(jobs = list(), titles = GLOB.command_positions, color = "#aac1ee"), - "Engineering" = list(jobs = list(), titles = GLOB.engineering_positions, color = "#ffd699"), - "Supply" = list(jobs = list(), titles = GLOB.supply_positions, color = "#ead4ae"), - "Miscellaneous" = list(jobs = list(), titles = list(), color = "#ffffff", colBreak = TRUE), - "Ghost Role" = list(jobs = list(), titles = GLOB.mob_spawners, color = "#ffffff"), - "Synthetic" = list(jobs = list(), titles = GLOB.nonhuman_positions, color = "#ccffcc"), - "Service" = list(jobs = list(), titles = GLOB.civilian_positions, color = "#cccccc"), - "Medical" = list(jobs = list(), titles = GLOB.medical_positions, color = "#99ffe6", colBreak = TRUE), - "Science" = list(jobs = list(), titles = GLOB.science_positions, color = "#e6b3e6"), - "Security" = list(jobs = list(), titles = GLOB.security_positions, color = "#ff9999"), - ) - for(var/spawner in GLOB.mob_spawners) - if(!LAZYLEN(spawner)) - continue - var/obj/effect/mob_spawn/S = pick(GLOB.mob_spawners[spawner]) - if(!istype(S) || !S.can_latejoin()) - continue - categorizedJobs["Ghost Role"]["jobs"] += spawner - - for(var/datum/job/job in SSjob.occupations) - if(job && IsJobUnavailable(job.title, TRUE) == JOB_AVAILABLE) - var/categorized = FALSE - for(var/jobcat in categorizedJobs) - var/list/jobs = categorizedJobs[jobcat]["jobs"] - if(job.title in categorizedJobs[jobcat]["titles"]) - categorized = TRUE - if(jobcat == "Command") - - if(job.title == "Captain") // Put captain at top of command jobs - jobs.Insert(1, job) - else - jobs += job - else // Put heads at top of non-command jobs - if(job.title in GLOB.command_positions) - jobs.Insert(1, job) - else - jobs += job - if(!categorized) - categorizedJobs["Miscellaneous"]["jobs"] += job - - - dat += "
" - for(var/jobcat in categorizedJobs) - if(categorizedJobs[jobcat]["colBreak"]) - dat += "" - if(!length(categorizedJobs[jobcat]["jobs"])) - continue - var/color = categorizedJobs[jobcat]["color"] - dat += "
" - dat += "[jobcat]" - for(var/datum/job/job in categorizedJobs[jobcat]["jobs"]) - var/position_class = "otherPosition" - if(job.title in GLOB.command_positions) - position_class = "commandPosition" - if(job in SSjob.prioritized_jobs) - dat += "[job.title] ([job.current_positions])" - else - dat += "[job.title] ([job.current_positions])" - categorizedJobs[jobcat]["jobs"] -= job - - for(var/spawner in categorizedJobs[jobcat]["jobs"]) - dat += "[spawner]" - - dat += "

" - dat += "
" - dat += "" - - // Removing the old window method but leaving it here for reference - //src << browse(dat, "window=latechoices;size=300x640;can_close=1") - - // Added the new browser window method - var/datum/browser/popup = new(src, "latechoices", "Choose Profession", 680, 580) - popup.add_stylesheet("playeroptions", 'html/browser/playeroptions.css') - popup.set_content(dat) - popup.open(FALSE) // FALSE is passed to open so that it doesn't use the onclose() proc - - -/mob/dead/new_player/proc/create_character(transfer_after) - spawning = 1 - close_spawn_windows() - - var/mob/living/carbon/human/H = new(loc) - - var/frn = CONFIG_GET(flag/force_random_names) - if(!frn) - frn = jobban_isbanned(src, "appearance") - if(QDELETED(src)) - return - if(frn) - client.prefs.random_character() - client.prefs.real_name = client.prefs.pref_species.random_name(gender,1) - client.prefs.copy_to(H) - H.dna.update_dna_identity() - if(mind) - if(transfer_after) - mind.late_joiner = TRUE - mind.active = 0 //we wish to transfer the key manually - mind.transfer_to(H) //won't transfer key since the mind is not active - - H.name = real_name - - . = H - new_character = . - if(transfer_after) - transfer_character() - -/mob/dead/new_player/proc/transfer_character() - . = new_character - if(.) - new_character.key = key //Manually transfer the key to log them in - new_character.stop_sound_channel(CHANNEL_LOBBYMUSIC) - new_character = null - qdel(src) - -/mob/dead/new_player/proc/ViewManifest() - var/dat = "" - dat += "

Crew Manifest

" - dat += GLOB.data_core.get_manifest(OOC = 1) - - src << browse(dat, "window=manifest;size=387x420;can_close=1") - -/mob/dead/new_player/Move() - return 0 - - -/mob/dead/new_player/proc/close_spawn_windows() - - src << browse(null, "window=latechoices") //closes late choices window - src << browse(null, "window=playersetup") //closes the player setup window - src << browse(null, "window=preferences") //closes job selection - src << browse(null, "window=mob_occupation") - src << browse(null, "window=latechoices") //closes late job selection +#define LINKIFY_READY(string, value) "[string]" + +/mob/dead/new_player + var/ready = 0 + var/spawning = 0//Referenced when you want to delete the new_player later on in the code. + + flags_1 = NONE + + invisibility = INVISIBILITY_ABSTRACT + + density = FALSE + stat = DEAD + canmove = FALSE + + anchored = TRUE // don't get pushed around + + var/mob/living/new_character //for instant transfer once the round is set up + + //Used to make sure someone doesn't get spammed with messages if they're ineligible for roles + var/ineligible_for_roles = FALSE + +/mob/dead/new_player/Initialize() + if(client && SSticker.state == GAME_STATE_STARTUP) + var/obj/screen/splash/S = new(client, TRUE, TRUE) + S.Fade(TRUE) + + if(length(GLOB.newplayer_start)) + forceMove(pick(GLOB.newplayer_start)) + else + forceMove(locate(1,1,1)) + + ComponentInitialize() + + . = ..() + +/mob/dead/new_player/prepare_huds() + return + +/mob/dead/new_player/proc/new_player_panel() + var/output = "

Welcome, [client ? client.prefs.real_name : "Unknown User"]

" + output += "

Setup Character

" + + if(SSticker.current_state <= GAME_STATE_PREGAME) + switch(ready) + if(PLAYER_NOT_READY) + output += "

\[ [LINKIFY_READY("Ready", PLAYER_READY_TO_PLAY)] | Not Ready | [LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] \]

" + if(PLAYER_READY_TO_PLAY) + output += "

\[ Ready | [LINKIFY_READY("Not Ready", PLAYER_NOT_READY)] | [LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] \]

" + if(PLAYER_READY_TO_OBSERVE) + output += "

\[ [LINKIFY_READY("Ready", PLAYER_READY_TO_PLAY)] | [LINKIFY_READY("Not Ready", PLAYER_NOT_READY)] | Observe \]

" + else + output += "

View the Crew Manifest

" + output += "

Join Game!

" + output += "

[LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)]

" + + if(!IsGuestKey(src.key)) + if (SSdbcore.Connect()) + var/isadmin = 0 + if(src.client && src.client.holder) + isadmin = 1 + var/datum/DBQuery/query_get_new_polls = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = \"[sanitizeSQL(ckey)]\") AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = \"[sanitizeSQL(ckey)]\")") + var/rs = REF(src) + if(query_get_new_polls.Execute()) + var/newpoll = 0 + if(query_get_new_polls.NextRow()) + newpoll = 1 + + if(newpoll) + output += "

Show Player Polls (NEW!)

" + else + output += "

Show Player Polls

" + qdel(query_get_new_polls) + if(QDELETED(src)) + return + + output += "
" + + //src << browse(output,"window=playersetup;size=210x240;can_close=0") + var/datum/browser/popup = new(src, "playersetup", "
New Player Options
", 250, 265) + popup.set_window_options("can_close=0") + popup.set_content(output) + popup.open(FALSE) + +/mob/dead/new_player/Topic(href, href_list[]) + if(src != usr) + return 0 + + if(!client) + return 0 + + //Determines Relevent Population Cap + var/relevant_cap + var/hpc = CONFIG_GET(number/hard_popcap) + var/epc = CONFIG_GET(number/extreme_popcap) + if(hpc && epc) + relevant_cap = min(hpc, epc) + else + relevant_cap = max(hpc, epc) + + if(href_list["show_preferences"]) + client.prefs.ShowChoices(src) + return 1 + + if(href_list["ready"]) + var/tready = text2num(href_list["ready"]) + //Avoid updating ready if we're after PREGAME (they should use latejoin instead) + //This is likely not an actual issue but I don't have time to prove that this + //no longer is required + if(SSticker.current_state <= GAME_STATE_PREGAME) + ready = tready + //if it's post initialisation and they're trying to observe we do the needful + if(!SSticker.current_state < GAME_STATE_PREGAME && tready == PLAYER_READY_TO_OBSERVE) + ready = tready + make_me_an_observer() + return + + if(href_list["refresh"]) + src << browse(null, "window=playersetup") //closes the player setup window + new_player_panel() + + if(href_list["late_join"]) + if(!SSticker || !SSticker.IsRoundInProgress()) + to_chat(usr, "The round is either not ready, or has already finished...") + return + + if(href_list["late_join"] == "override") + LateChoices() + return + + if(SSticker.queued_players.len || (relevant_cap && living_player_count() >= relevant_cap && !(ckey(key) in GLOB.admin_datums))) + to_chat(usr, "[CONFIG_GET(string/hard_popcap_message)]") + + var/queue_position = SSticker.queued_players.Find(usr) + if(queue_position == 1) + to_chat(usr, "You are next in line to join the game. You will be notified when a slot opens up.") + else if(queue_position) + to_chat(usr, "There are [queue_position-1] players in front of you in the queue to join the game.") + else + SSticker.queued_players += usr + to_chat(usr, "You have been added to the queue to join the game. Your position in queue is [SSticker.queued_players.len].") + return + LateChoices() + + if(href_list["manifest"]) + ViewManifest() + + if(href_list["SelectedJob"]) + + if(!GLOB.enter_allowed) + to_chat(usr, "There is an administrative lock on entering the game!") + return + + if(SSticker.queued_players.len && !(ckey(key) in GLOB.admin_datums)) + if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1])) + to_chat(usr, "Server is full.") + return + + AttemptLateSpawn(href_list["SelectedJob"]) + return + + if(href_list["JoinAsGhostRole"]) + if(!GLOB.enter_allowed) + to_chat(usr, " There is an administrative lock on entering the game!") + + if(SSticker.queued_players.len && !(ckey(key) in GLOB.admin_datums)) + if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1])) + to_chat(usr, "Server is full.") + return + + var/obj/effect/mob_spawn/MS = pick(GLOB.mob_spawners[href_list["JoinAsGhostRole"]]) + if(MS.attack_ghost(src, latejoinercalling = TRUE)) + SSticker.queued_players -= src + SSticker.queue_delay = 4 + qdel(src) + + if(!ready && href_list["preference"]) + if(client) + client.prefs.process_link(src, href_list) + else if(!href_list["late_join"]) + new_player_panel() + + if(href_list["showpoll"]) + handle_player_polling() + return + + if(href_list["pollid"]) + var/pollid = href_list["pollid"] + if(istext(pollid)) + pollid = text2num(pollid) + if(isnum(pollid) && ISINTEGER(pollid)) + src.poll_player(pollid) + return + + if(href_list["votepollid"] && href_list["votetype"]) + var/pollid = text2num(href_list["votepollid"]) + var/votetype = href_list["votetype"] + //lets take data from the user to decide what kind of poll this is, without validating it + //what could go wrong + switch(votetype) + if(POLLTYPE_OPTION) + var/optionid = text2num(href_list["voteoptionid"]) + if(vote_on_poll(pollid, optionid)) + to_chat(usr, "Vote successful.") + else + to_chat(usr, "Vote failed, please try again or contact an administrator.") + if(POLLTYPE_TEXT) + var/replytext = href_list["replytext"] + if(log_text_poll_reply(pollid, replytext)) + to_chat(usr, "Feedback logging successful.") + else + to_chat(usr, "Feedback logging failed, please try again or contact an administrator.") + if(POLLTYPE_RATING) + var/id_min = text2num(href_list["minid"]) + var/id_max = text2num(href_list["maxid"]) + + if( (id_max - id_min) > 100 ) //Basic exploit prevention + //(protip, this stops no exploits) + to_chat(usr, "The option ID difference is too big. Please contact administration or the database admin.") + return + + for(var/optionid = id_min; optionid <= id_max; optionid++) + if(!isnull(href_list["o[optionid]"])) //Test if this optionid was replied to + var/rating + if(href_list["o[optionid]"] == "abstain") + rating = null + else + rating = text2num(href_list["o[optionid]"]) + if(!isnum(rating) || !ISINTEGER(rating)) + return + + if(!vote_on_numval_poll(pollid, optionid, rating)) + to_chat(usr, "Vote failed, please try again or contact an administrator.") + return + to_chat(usr, "Vote successful.") + if(POLLTYPE_MULTI) + var/id_min = text2num(href_list["minoptionid"]) + var/id_max = text2num(href_list["maxoptionid"]) + + if( (id_max - id_min) > 100 ) //Basic exploit prevention + to_chat(usr, "The option ID difference is too big. Please contact administration or the database admin.") + return + + for(var/optionid = id_min; optionid <= id_max; optionid++) + if(!isnull(href_list["option_[optionid]"])) //Test if this optionid was selected + var/i = vote_on_multi_poll(pollid, optionid) + switch(i) + if(0) + continue + if(1) + to_chat(usr, "Vote failed, please try again or contact an administrator.") + return + if(2) + to_chat(usr, "Maximum replies reached.") + break + to_chat(usr, "Vote successful.") + if(POLLTYPE_IRV) + if (!href_list["IRVdata"]) + to_chat(src, "No ordering data found. Please try again or contact an administrator.") + return + var/list/votelist = splittext(href_list["IRVdata"], ",") + if (!vote_on_irv_poll(pollid, votelist)) + to_chat(src, "Vote failed, please try again or contact an administrator.") + return + to_chat(src, "Vote successful.") + +//When you cop out of the round (NB: this HAS A SLEEP FOR PLAYER INPUT IN IT) +/mob/dead/new_player/proc/make_me_an_observer() + if(QDELETED(src) || !src.client) + ready = PLAYER_NOT_READY + return FALSE + + var/this_is_like_playing_right = alert(src,"Are you sure you wish to observe? You will not be able to play this round!","Player Setup","Yes","No") + + if(QDELETED(src) || !src.client || this_is_like_playing_right != "Yes") + ready = PLAYER_NOT_READY + src << browse(null, "window=playersetup") //closes the player setup window + new_player_panel() + return FALSE + + var/mob/dead/observer/observer = new() + spawning = TRUE + + observer.started_as_observer = TRUE + close_spawn_windows() + var/obj/effect/landmark/observer_start/O = locate(/obj/effect/landmark/observer_start) in GLOB.landmarks_list + to_chat(src, "Now teleporting.") + if (O) + observer.forceMove(O.loc) + else + to_chat(src, "Teleporting failed. Ahelp an admin please") + stack_trace("There's no freaking observer landmark available on this map or you're making observers before the map is initialised") + observer.key = key + observer.client = client + observer.set_ghost_appearance() + if(observer.client && observer.client.prefs) + observer.real_name = observer.client.prefs.real_name + observer.name = observer.real_name + observer.update_icon() + observer.stop_sound_channel(CHANNEL_LOBBYMUSIC) + QDEL_NULL(mind) + qdel(src) + return TRUE + +/proc/get_job_unavailable_error_message(retval, jobtitle) + switch(retval) + if(JOB_AVAILABLE) + return "[jobtitle] is available." + if(JOB_UNAVAILABLE_GENERIC) + return "[jobtitle] is unavailable." + if(JOB_UNAVAILABLE_BANNED) + return "You are currently banned from [jobtitle]." + if(JOB_UNAVAILABLE_PLAYTIME) + return "You do not have enough relevant playtime for [jobtitle]." + if(JOB_UNAVAILABLE_ACCOUNTAGE) + return "Your account is not old enough for [jobtitle]." + if(JOB_UNAVAILABLE_SLOTFULL) + return "[jobtitle] is already filled to capacity." + return "Error: Unknown job availability." + +/mob/dead/new_player/proc/IsJobUnavailable(rank, latejoin = FALSE) + var/datum/job/job = SSjob.GetJob(rank) + if(!job) + return JOB_UNAVAILABLE_GENERIC + if((job.current_positions >= job.total_positions) && job.total_positions != -1) + if(job.title == "Assistant") + if(isnum(client.player_age) && client.player_age <= 14) //Newbies can always be assistants + return JOB_AVAILABLE + for(var/datum/job/J in SSjob.occupations) + if(J && J.current_positions < J.total_positions && J.title != job.title) + return JOB_UNAVAILABLE_SLOTFULL + else + return JOB_UNAVAILABLE_SLOTFULL + if(jobban_isbanned(src,rank)) + return JOB_UNAVAILABLE_BANNED + if(QDELETED(src)) + return JOB_UNAVAILABLE_GENERIC + if(!job.player_old_enough(client)) + return JOB_UNAVAILABLE_ACCOUNTAGE + if(job.required_playtime_remaining(client)) + return JOB_UNAVAILABLE_PLAYTIME + if(latejoin && !job.special_check_latejoin(client)) + return JOB_UNAVAILABLE_GENERIC + return JOB_AVAILABLE + +/mob/dead/new_player/proc/AttemptLateSpawn(rank) + var/error = IsJobUnavailable(rank) + if(error != JOB_AVAILABLE) + alert(src, get_job_unavailable_error_message(error, rank)) + return FALSE + + if(SSticker.late_join_disabled) + alert(src, "An administrator has disabled late join spawning.") + return FALSE + + var/arrivals_docked = TRUE + if(SSshuttle.arrivals) + close_spawn_windows() //In case we get held up + if(SSshuttle.arrivals.damaged && CONFIG_GET(flag/arrivals_shuttle_require_safe_latejoin)) + src << alert("The arrivals shuttle is currently malfunctioning! You cannot join.") + return FALSE + + if(CONFIG_GET(flag/arrivals_shuttle_require_undocked)) + SSshuttle.arrivals.RequireUndocked(src) + arrivals_docked = SSshuttle.arrivals.mode != SHUTTLE_CALL + + //Remove the player from the join queue if he was in one and reset the timer + SSticker.queued_players -= src + SSticker.queue_delay = 4 + + SSjob.AssignRole(src, rank, 1) + + var/mob/living/character = create_character(TRUE) //creates the human and transfers vars and mind + var/equip = SSjob.EquipRank(character, rank, TRUE) + if(isliving(equip)) //Borgs get borged in the equip, so we need to make sure we handle the new mob. + character = equip + + var/datum/job/job = SSjob.GetJob(rank) + + if(job && !job.override_latejoin_spawn(character)) + SSjob.SendToLateJoin(character) + if(!arrivals_docked) + var/obj/screen/splash/Spl = new(character.client, TRUE) + Spl.Fade(TRUE) + character.playsound_local(get_turf(character), 'sound/voice/ApproachingTG.ogg', 25) + + character.update_parallax_teleport() + + SSticker.minds += character.mind + + var/mob/living/carbon/human/humanc + if(ishuman(character)) + humanc = character //Let's retypecast the var to be human, + + if(humanc) //These procs all expect humans + GLOB.data_core.manifest_inject(humanc) + if(SSshuttle.arrivals) + SSshuttle.arrivals.QueueAnnounce(humanc, rank) + else + AnnounceArrival(humanc, rank) + AddEmploymentContract(humanc) + if(GLOB.highlander) + to_chat(humanc, "THERE CAN BE ONLY ONE!!!") + humanc.make_scottish() + + if(GLOB.summon_guns_triggered) + give_guns(humanc) + if(GLOB.summon_magic_triggered) + give_magic(humanc) + + GLOB.joined_player_list += character.ckey + GLOB.latejoiners += character + + if(CONFIG_GET(flag/allow_latejoin_antagonists) && humanc) //Borgs aren't allowed to be antags. Will need to be tweaked if we get true latejoin ais. + if(SSshuttle.emergency) + switch(SSshuttle.emergency.mode) + if(SHUTTLE_RECALL, SHUTTLE_IDLE) + SSticker.mode.make_antag_chance(humanc) + if(SHUTTLE_CALL) + if(SSshuttle.emergency.timeLeft(1) > initial(SSshuttle.emergencyCallTime)*0.5) + SSticker.mode.make_antag_chance(humanc) + + if(humanc && CONFIG_GET(flag/roundstart_traits)) + SSquirks.AssignQuirks(humanc, humanc.client, TRUE, FALSE, job, FALSE) + + log_manifest(character.mind.key,character.mind,character,latejoin = TRUE) + +/mob/dead/new_player/proc/AddEmploymentContract(mob/living/carbon/human/employee) + //TODO: figure out a way to exclude wizards/nukeops/demons from this. + for(var/C in GLOB.employmentCabinets) + var/obj/structure/filingcabinet/employment/employmentCabinet = C + if(!employmentCabinet.virgin) + employmentCabinet.addFile(employee) + + +/mob/dead/new_player/proc/LateChoices() + + var/level = "green" + switch(GLOB.security_level) + if(SEC_LEVEL_GREEN) + level = "green" + if(SEC_LEVEL_BLUE) + level = "blue" + if(SEC_LEVEL_AMBER) + level = "amber" + if(SEC_LEVEL_RED) + level = "red" + if(SEC_LEVEL_DELTA) + level = "delta" + + var/dat = "
Round Duration: [DisplayTimeText(world.time - SSticker.round_start_time)]
Alert Level: [capitalize(level)]
" + if(SSshuttle.emergency) + switch(SSshuttle.emergency.mode) + if(SHUTTLE_ESCAPE) + dat += "
The station has been evacuated.

" + if(SHUTTLE_CALL) + if(!SSshuttle.canRecall()) + dat += "
The station is currently undergoing evacuation procedures.

" + for(var/datum/job/prioritized_job in SSjob.prioritized_jobs) + if(prioritized_job.current_positions >= prioritized_job.total_positions) + SSjob.prioritized_jobs -= prioritized_job + dat += "
" + var/column_counter = 0 + var/free_space = 0 + for(var/list/category in list(GLOB.command_positions) + list(GLOB.supply_positions) + list(GLOB.engineering_positions) + list(GLOB.nonhuman_positions - "pAI") + list(GLOB.civilian_positions) + list(GLOB.medical_positions) + list(GLOB.science_positions) + list(GLOB.security_positions)) + var/cat_color = "fff" //random default + if(SSjob.name_occupations && SSjob.name_occupations[category[1]]) + cat_color = SSjob.name_occupations[category[1]].selection_color //use the color of the first job in the category (the department head) as the category color + else + cat_color = SSjob.occupations[category[1]].selection_color + dat += "
" + dat += "[SSjob.name_occupations[category[1]].exp_type_department]" + + var/list/dept_dat = list() + for(var/job in category) + var/datum/job/job_datum = SSjob.name_occupations[job] + if(job_datum && IsJobUnavailable(job_datum.title, TRUE) == JOB_AVAILABLE) + var/command_bold = "" + if(job in GLOB.command_positions) + command_bold = " command" + if(job_datum in SSjob.prioritized_jobs) + dept_dat += "[job_datum.title] ([job_datum.current_positions])" + else + dept_dat += "[job_datum.title] ([job_datum.current_positions])" + if(!dept_dat.len) + dept_dat += "No positions open." + dat += jointext(dept_dat, "") + dat += "

" + column_counter++ + if(free_space <=4) + free_space++ + if(column_counter > 0 && (column_counter % 3 == 0)) + dat += "
" + if(free_space >= 5 && (free_space % 5 == 0) && (column_counter % 3 != 0)) + free_space = 0 + column_counter = 0 + dat += "" + + dat += "
" + + var/available_ghosts = 0 + for(var/spawner in GLOB.mob_spawners) + if(!LAZYLEN(spawner)) + continue + var/obj/effect/mob_spawn/S = pick(GLOB.mob_spawners[spawner]) + if(!istype(S) || !S.can_latejoin()) + continue + available_ghosts++ + break + + if(!available_ghosts) + dat += "
There are currently no open ghost spawners.
" + else + var/list/categorizedJobs = list("Ghost Role" = list(jobs = list(), titles = GLOB.mob_spawners, color = "#ffffff")) + for(var/spawner in GLOB.mob_spawners) + if(!LAZYLEN(spawner)) + continue + var/obj/effect/mob_spawn/S = pick(GLOB.mob_spawners[spawner]) + if(!istype(S) || !S.can_latejoin()) + continue + categorizedJobs["Ghost Role"]["jobs"] += spawner + + dat += "
" + for(var/jobcat in categorizedJobs) + if(!length(categorizedJobs[jobcat]["jobs"])) + continue + var/color = categorizedJobs[jobcat]["color"] + dat += "
" + dat += "[jobcat]" + for(var/spawner in categorizedJobs[jobcat]["jobs"]) + dat += "[spawner]" + + dat += "

" + dat += "
" + dat += "" + + var/datum/browser/popup = new(src, "latechoices", "Choose Profession", 720, 600) + popup.add_stylesheet("playeroptions", 'html/browser/playeroptions.css') + popup.set_content(jointext(dat, "")) + popup.open(FALSE) // FALSE is passed to open so that it doesn't use the onclose() proc + +/mob/dead/new_player/proc/create_character(transfer_after) + spawning = 1 + close_spawn_windows() + + var/mob/living/carbon/human/H = new(loc) + + var/frn = CONFIG_GET(flag/force_random_names) + if(!frn) + frn = jobban_isbanned(src, "appearance") + if(QDELETED(src)) + return + if(frn) + client.prefs.random_character() + client.prefs.real_name = client.prefs.pref_species.random_name(gender,1) + client.prefs.copy_to(H) + H.dna.update_dna_identity() + if(mind) + if(transfer_after) + mind.late_joiner = TRUE + mind.active = 0 //we wish to transfer the key manually + mind.transfer_to(H) //won't transfer key since the mind is not active + + H.name = real_name + + . = H + new_character = . + if(transfer_after) + transfer_character() + +/mob/dead/new_player/proc/transfer_character() + . = new_character + if(.) + new_character.key = key //Manually transfer the key to log them in + new_character.stop_sound_channel(CHANNEL_LOBBYMUSIC) + new_character = null + qdel(src) + +/mob/dead/new_player/proc/ViewManifest() + var/dat = "" + dat += "

Crew Manifest

" + dat += GLOB.data_core.get_manifest(OOC = 1) + + src << browse(dat, "window=manifest;size=387x420;can_close=1") + +/mob/dead/new_player/Move() + return 0 + + +/mob/dead/new_player/proc/close_spawn_windows() + + src << browse(null, "window=latechoices") //closes late choices window + src << browse(null, "window=playersetup") //closes the player setup window + src << browse(null, "window=preferences") //closes job selection + src << browse(null, "window=mob_occupation") + src << browse(null, "window=latechoices") //closes late job selection + +/* Used to make sure that a player has a valid job preference setup, used to knock players out of eligibility for anything if their prefs don't make sense. + A "valid job preference setup" in this situation means at least having one job set to low, or not having "return to lobby" enabled + Prevents "antag rolling" by setting antag prefs on, all jobs to never, and "return to lobby if preferences not availible" + Doing so would previously allow you to roll for antag, then send you back to lobby if you didn't get an antag role + This also does some admin notification and logging as well, as well as some extra logic to make sure things don't go wrong +*/ + +/mob/dead/new_player/proc/check_preferences() + if(!client) + return FALSE //Not sure how this would get run without the mob having a client, but let's just be safe. + if(client.prefs.joblessrole != RETURNTOLOBBY) + return TRUE + // If they have antags enabled, they're potentially doing this on purpose instead of by accident. Notify admins if so. + var/has_antags = FALSE + if(client.prefs.be_special.len > 0) + has_antags = TRUE + if(client.prefs.job_preferences.len == 0) + if(!ineligible_for_roles) + to_chat(src, "You have no jobs enabled, along with return to lobby if job is unavailable. This makes you ineligible for any round start role, please update your job preferences.") + ineligible_for_roles = TRUE + ready = PLAYER_NOT_READY + if(has_antags) + log_admin("[src.ckey] just got booted back to lobby with no jobs, but antags enabled.") + message_admins("[src.ckey] just got booted back to lobby with no jobs enabled, but antag rolling enabled. Likely antag rolling abuse.") + + return FALSE //This is the only case someone should actually be completely blocked from antag rolling as well + return TRUE diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm index 6e91b58506..994d082585 100644 --- a/code/modules/mob/dead/new_player/preferences_setup.dm +++ b/code/modules/mob/dead/new_player/preferences_setup.dm @@ -24,50 +24,35 @@ age = rand(AGE_MIN,AGE_MAX) /datum/preferences/proc/update_preview_icon() - // Silicons only need a very basic preview since there is no customization for them. -// var/wide_icon = FALSE //CITDEL THINGS -// if(features["taur"] != "None") -// wide_icon = TRUE - if(job_engsec_high) - switch(job_engsec_high) - if(AI_JF) - parent.show_character_previews(image('icons/mob/ai.dmi', resolve_ai_icon(preferred_ai_core_display), dir = SOUTH)) - return - if(CYBORG) - parent.show_character_previews(image('icons/mob/robots.dmi', icon_state = "robot", dir = SOUTH)) - return + // Determine what job is marked as 'High' priority, and dress them up as such. + var/datum/job/previewJob + var/highest_pref = 0 + for(var/job in job_preferences) + if(job_preferences["[job]"] > highest_pref) + previewJob = SSjob.GetJob(job) + highest_pref = job_preferences["[job]"] + + if(previewJob) + // Silicons only need a very basic preview since there is no customization for them. + if(istype(previewJob,/datum/job/ai)) + parent.show_character_previews(image('icons/mob/ai.dmi', icon_state = resolve_ai_icon(preferred_ai_core_display), dir = SOUTH)) + return + if(istype(previewJob,/datum/job/cyborg)) + parent.show_character_previews(image('icons/mob/robots.dmi', icon_state = "robot", dir = SOUTH)) + return // Set up the dummy for its photoshoot var/mob/living/carbon/human/dummy/mannequin = generate_or_wait_for_human_dummy(DUMMY_HUMAN_SLOT_PREFERENCES) mannequin.cut_overlays() + // Apply the Dummy's preview background first so we properly layer everything else on top of it. mannequin.add_overlay(mutable_appearance('modular_citadel/icons/ui/backgrounds.dmi', bgstate, layer = SPACE_LAYER)) copy_to(mannequin) - // Determine what job is marked as 'High' priority, and dress them up as such. - var/datum/job/previewJob - var/highRankFlag = job_civilian_high | job_medsci_high | job_engsec_high - - if(job_civilian_low & ASSISTANT) - previewJob = SSjob.GetJob("Assistant") - else if(highRankFlag) - var/highDeptFlag - if(job_civilian_high) - highDeptFlag = CIVILIAN - else if(job_medsci_high) - highDeptFlag = MEDSCI - else if(job_engsec_high) - highDeptFlag = ENGSEC - - for(var/datum/job/job in SSjob.occupations) - if(job.flag == highRankFlag && job.department_flag == highDeptFlag) - previewJob = job - break - if(previewJob) - if(current_tab != 2) - mannequin.job = previewJob.title - previewJob.equip(mannequin, TRUE) + mannequin.job = previewJob.title + previewJob.equip(mannequin, TRUE, preference_source = parent) COMPILE_OVERLAYS(mannequin) parent.show_character_previews(new /mutable_appearance(mannequin)) unset_busy_human_dummy(DUMMY_HUMAN_SLOT_PREFERENCES) + diff --git a/code/modules/mob/dead/new_player/sprite_accessories/Citadel_Snowflake.dm b/code/modules/mob/dead/new_player/sprite_accessories/Citadel_Snowflake.dm new file mode 100644 index 0000000000..020776a75f --- /dev/null +++ b/code/modules/mob/dead/new_player/sprite_accessories/Citadel_Snowflake.dm @@ -0,0 +1,53 @@ +/datum/sprite_accessory/mam_tails/shark/datashark + name = "DataShark" + icon_state = "datashark" + ckeys_allowed = list("rubyflamewing") + +/datum/sprite_accessory/mam_tails_animated/shark/datashark + name = "DataShark" + icon_state = "datashark" + ckeys_allowed = list("rubyflamewing") + +/datum/sprite_accessory/mam_body_markings/shark/datashark + name = "DataShark" + icon_state = "datashark" + ckeys_allowed = list("rubyflamewing") + +//Sabresune +/datum/sprite_accessory/mam_ears/sabresune + name = "Sabresune" + icon_state = "sabresune" + ckeys_allowed = list("poojawa") + extra = TRUE + extra_color_src = MUTCOLORS3 + +/datum/sprite_accessory/mam_tails/sabresune + name = "Sabresune" + icon_state = "sabresune" + ckeys_allowed = list("poojawa") + +/datum/sprite_accessory/mam_tails_animated/sabresune + name = "Sabresune" + icon_state = "sabresune" + ckeys_allowed = list("poojawa") + +/datum/sprite_accessory/mam_body_markings/sabresune + name = "Sabresune" + icon_state = "sabresune" + ckeys_allowed = list("poojawa") + +//Lunasune +/datum/sprite_accessory/mam_ears/lunasune + name = "lunasune" + icon_state = "lunasune" + ckeys_allowed = list("invader4352") + +/datum/sprite_accessory/mam_tails/lunasune + name = "lunasune" + icon_state = "lunasune" + ckeys_allowed = list("invader4352") + +/datum/sprite_accessory/mam_tails_animated/lunasune + name = "lunasune" + icon_state = "lunasune" + ckeys_allowed = list("invader4352") diff --git a/code/modules/mob/dead/new_player/sprite_accessories/_sprite_accessories.dm b/code/modules/mob/dead/new_player/sprite_accessories/_sprite_accessories.dm index 5e24d0630b..dd66f68e5d 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/_sprite_accessories.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/_sprite_accessories.dm @@ -61,6 +61,17 @@ var/dimension_y = 32 var/center = FALSE //Should we center the sprite? + //Special / holdover traits for Citadel specific sprites. + var/extra = FALSE + var/extra_color_src = MUTCOLORS2 //The color source for the extra overlay. + var/extra_icon = 'modular_citadel/icons/mob/mam_tails.dmi' + var/extra2 = FALSE + var/extra2_color_src = MUTCOLORS3 + var/extra2_icon = 'modular_citadel/icons/mob/mam_tails.dmi' + + //for snowflake/donor specific sprites + var/list/ckeys_allowed + /datum/sprite_accessory/underwear icon = 'icons/mob/underwear.dmi' var/has_color = FALSE \ No newline at end of file diff --git a/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm b/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm new file mode 100644 index 0000000000..6c0659f851 --- /dev/null +++ b/code/modules/mob/dead/new_player/sprite_accessories/alienpeople.dm @@ -0,0 +1,53 @@ + +/****************************************** +*********** Xeno Dorsal Tubes ************* +*******************************************/ +/datum/sprite_accessory/xeno_dorsal + icon = 'modular_citadel/icons/mob/xeno_parts_greyscale.dmi' + +/datum/sprite_accessory/xeno_dorsal/standard + 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" + +/****************************************** +************* Xeno Tails ****************** +*******************************************/ +/datum/sprite_accessory/xeno_tail + icon = 'modular_citadel/icons/mob/xeno_parts_greyscale.dmi' + +/datum/sprite_accessory/xeno_tail/none + name = "None" + +/datum/sprite_accessory/xeno_tail/standard + name = "Xenomorph Tail" + icon_state = "xeno" + +/****************************************** +************* Xeno Heads ****************** +*******************************************/ +/datum/sprite_accessory/xeno_head + icon = 'modular_citadel/icons/mob/xeno_parts_greyscale.dmi' + +/datum/sprite_accessory/xeno_head/standard + 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/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 6bce18d7ce..2f1d48cfa7 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 @@ -1,6 +1,6 @@ -//////////.////////////////// -// MutantParts Definitions // -///////////////////////////// +/****************************************** +************* Lizard Markings ************* +*******************************************/ /datum/sprite_accessory/body_markings icon = 'icons/mob/mutant_bodyparts.dmi' @@ -22,4 +22,271 @@ /datum/sprite_accessory/body_markings/lbelly name = "Light Belly" icon_state = "lbelly" - gender_specific = 1 \ No newline at end of file + gender_specific = 1 + +/****************************************** +************ Furry Markings *************** +*******************************************/ + +// These are all color matrixed and applied per-limb by default. you MUST comply with this if you want to have your markings work --Pooj +// use the HumanScissors tool to break your sprite up into the zones easier. +// Although Byond supposedly doesn't have an icon limit anymore of 512 states after 512.1478, just be careful about too many additions. + +/datum/sprite_accessory/mam_body_markings + extra = FALSE + extra2 = FALSE + color_src = MATRIXED + gender_specific = 0 + icon = 'modular_citadel/icons/mob/mam_markings.dmi' + +/datum/sprite_accessory/mam_body_markings/none + name = "None" + icon_state = "none" + ckeys_allowed = list("yousshouldnteverbeseeingthisyoumeme") + icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' + +/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" + +/datum/sprite_accessory/mam_body_markings/bee + name = "Bee" + icon_state = "bee" + icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' + +/datum/sprite_accessory/mam_body_markings/belly + name = "Belly" + icon_state = "belly" + icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' + +/datum/sprite_accessory/mam_body_markings/bellyslim + name = "Bellyslim" + 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/corvid + name = "Corvid" + icon_state = "corvid" + +/datum/sprite_accessory/mam_body_markings/dalmation + name = "Dalmation" + icon_state = "dalmation" + +/datum/sprite_accessory/mam_body_markings/deer + name = "Deer" + icon_state = "deer" + +/datum/sprite_accessory/mam_body_markings/dog + name = "Dog" + icon_state = "dog" + +/datum/sprite_accessory/mam_body_markings/eevee + name = "Eevee" + icon_state = "eevee" + +/datum/sprite_accessory/mam_body_markings/fennec + name = "Fennec" + icon_state = "Fennec" + +/datum/sprite_accessory/mam_body_markings/fox + name = "Fox" + icon_state = "fox" + +/datum/sprite_accessory/mam_body_markings/frog + name = "Frog" + icon_state = "frog" + icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' + +/datum/sprite_accessory/mam_body_markings/goat + name = "Goat" + icon_state = "goat" + +/datum/sprite_accessory/mam_body_markings/handsfeet + name = "Handsfeet" + icon_state = "handsfeet" + icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' + +/datum/sprite_accessory/mam_body_markings/hawk + name = "Hawk" + icon_state = "hawk" + +/datum/sprite_accessory/mam_body_markings/husky + name = "Husky" + icon_state = "husky" + +/datum/sprite_accessory/mam_body_markings/hyena + 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/otie + name = "Otie" + icon_state = "otie" + +/datum/sprite_accessory/mam_body_markings/otter + 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/possum + name = "Possum" + icon_state = "possum" + +/datum/sprite_accessory/mam_body_markings/raccoon + name = "Raccoon" + icon_state = "raccoon" + +/datum/sprite_accessory/mam_body_markings/pede + name = "Scolipede" + icon_state = "scolipede" + icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' + +/datum/sprite_accessory/mam_body_markings/shark + 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/tajaran + name = "Tajaran" + icon_state = "tajaran" + +/datum/sprite_accessory/mam_body_markings/tiger + name = "Tiger" + icon_state = "tiger" + +/datum/sprite_accessory/mam_body_markings/turian + name = "Turian" + icon_state = "turian" + icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' + +/datum/sprite_accessory/mam_body_markings/wolf + name = "Wolf" + icon_state = "wolf" + +/datum/sprite_accessory/mam_body_markings/xeno + name = "Xeno" + icon_state = "xeno" + icon = 'modular_citadel/icons/mob/markings_notmammals.dmi' + +/****************************************** +************* Insect Markings ************* +*******************************************/ + +/datum/sprite_accessory/insect_fluff + icon = 'icons/mob/wings.dmi' + color_src = 0 + +/datum/sprite_accessory/insect_fluff/none + name = "None" + icon_state = "none" + +/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/colored + name = "Colored (Hair)" + icon_state = "snow" + color_src = HAIR + +/datum/sprite_accessory/insect_fluff/colored1 + name = "Colored (Primary)" + icon_state = "snow" + color_src = MUTCOLORS + +/datum/sprite_accessory/insect_fluff/colored2 + name = "Colored (Secondary)" + icon_state = "snow" + color_src = MUTCOLORS2 + +/datum/sprite_accessory/insect_fluff/colored3 + name = "Colored (Tertiary)" + icon_state = "snow" + color_src = MUTCOLORS3 \ No newline at end of file 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 163f8370a2..1496ca030a 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/ears.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/ears.dm @@ -5,8 +5,295 @@ name = "None" icon_state = "none" +/****************************************** +*************** Human Ears **************** +*******************************************/ + +/datum/sprite_accessory/ears/human/axolotl + name = "Axolotl" + icon_state = "axolotl" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + +/datum/sprite_accessory/ears/human/bear + name = "Bear" + icon_state = "bear" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/ears/human/bigwolf + name = "Big Wolf" + icon_state = "bigwolf" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/ears/human/bigwolfinner + name = "Big Wolf (ALT)" + icon_state = "bigwolfinner" + hasinner = 1 + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/ears/human/bigwolfdark + name = "Dark Big Wolf" + icon_state = "bigwolfdark" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/ears/human/bigwolfinnerdark + name = "Dark Big Wolf (ALT)" + icon_state = "bigwolfinnerdark" + hasinner = 1 + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + /datum/sprite_accessory/ears/cat name = "Cat" icon_state = "cat" hasinner = 1 - color_src = HAIR \ No newline at end of file + color_src = HAIR + +/datum/sprite_accessory/ears/human/cow + name = "Cow" + icon_state = "cow" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/ears/human/curled + name = "Curled Horn" + icon_state = "horn1" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MUTCOLORS3 + +/datum/sprite_accessory/ears/human/eevee + name = "Eevee" + icon_state = "eevee" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/ears/human/elephant + name = "Elephant" + icon_state = "elephant" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/ears/human/elf + name = "Elf" + icon_state = "elf" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = SKINTONE + +/datum/sprite_accessory/ears/fennec + name = "Fennec" + icon_state = "fennec" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/ears/fish + name = "Fish" + icon_state = "fish" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/ears/fox + name = "Fox" + icon_state = "fox" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + +/datum/sprite_accessory/ears/human/jellyfish + name = "Jellyfish" + 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" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/ears/human/otie + name = "Otusian" + icon_state = "otie" + 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/sergal + name = "Sergal" + icon_state = "sergal" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/ears/human/skunk + name = "skunk" + icon_state = "skunk" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/ears/squirrel + name = "Squirrel" + icon_state = "squirrel" + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/ears/wolf + name = "Wolf" + icon_state = "wolf" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + + +/****************************************** +*************** Furry Ears **************** +*******************************************/ + +/datum/sprite_accessory/mam_ears + icon = 'modular_citadel/icons/mob/mam_ears.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/mam_ears/none + name = "None" + icon_state = "none" + +/datum/sprite_accessory/mam_ears/axolotl + name = "Axolotl" + icon_state = "axolotl" + +/datum/sprite_accessory/mam_ears/bear + name = "Bear" + icon_state = "bear" + +/datum/sprite_accessory/mam_ears/bigwolf + name = "Big Wolf" + icon_state = "bigwolf" + +/datum/sprite_accessory/mam_ears/bigwolfinner + name = "Big Wolf (ALT)" + icon_state = "bigwolfinner" + hasinner = 1 + +/datum/sprite_accessory/mam_ears/bigwolfdark + name = "Dark Big Wolf" + icon_state = "bigwolfdark" + +/datum/sprite_accessory/mam_ears/bigwolfinnerdark + name = "Dark Big Wolf (ALT)" + icon_state = "bigwolfinnerdark" + hasinner = 1 + +/datum/sprite_accessory/mam_ears/cat + name = "Cat" + icon_state = "cat" + hasinner = 1 + color_src = HAIR + +/datum/sprite_accessory/mam_ears/catbig + name = "Cat, Big" + icon_state = "catbig" + +/datum/sprite_accessory/mam_ears/cow + name = "Cow" + icon_state = "cow" + +/datum/sprite_accessory/mam_ears/curled + name = "Curled Horn" + icon_state = "horn1" + color_src = MUTCOLORS3 + +/datum/sprite_accessory/mam_ears/deer + name = "Deer" + icon_state = "deer" + color_src = MUTCOLORS3 + +/datum/sprite_accessory/mam_ears/eevee + name = "Eevee" + icon_state = "eevee" + + +/datum/sprite_accessory/mam_ears/elf + name = "Elf" + icon_state = "elf" + color_src = MUTCOLORS3 + + +/datum/sprite_accessory/mam_ears/elephant + name = "Elephant" + icon_state = "elephant" + +/datum/sprite_accessory/mam_ears/fennec + name = "Fennec" + icon_state = "fennec" + +/datum/sprite_accessory/mam_ears/fish + name = "Fish" + icon_state = "fish" + +/datum/sprite_accessory/mam_ears/fox + name = "Fox" + icon_state = "fox" + +/datum/sprite_accessory/mam_ears/husky + name = "Husky" + icon_state = "wolf" + +/datum/sprite_accessory/mam_ears/kangaroo + name = "kangaroo" + icon_state = "kangaroo" + +/datum/sprite_accessory/mam_ears/jellyfish + name = "Jellyfish" + icon_state = "jellyfish" + color_src = HAIR + +/datum/sprite_accessory/mam_ears/lab + name = "Dog, Long" + icon_state = "lab" + +/datum/sprite_accessory/mam_ears/murid + name = "Murid" + icon_state = "murid" + +/datum/sprite_accessory/mam_ears/otie + name = "Otusian" + icon_state = "otie" + +/datum/sprite_accessory/mam_ears/squirrel + name = "Squirrel" + icon_state = "squirrel" + +/datum/sprite_accessory/mam_ears/pede + name = "Scolipede" + icon_state = "pede" + +/datum/sprite_accessory/mam_ears/rabbit + name = "Rabbit" + icon_state = "rabbit" + +/datum/sprite_accessory/mam_ears/sergal + name = "Sergal" + icon_state = "sergal" + +/datum/sprite_accessory/mam_ears/skunk + name = "skunk" + icon_state = "skunk" + +/datum/sprite_accessory/mam_ears/wolf + name = "Wolf" + icon_state = "wolf" 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 3566f3dea5..d11299fd5b 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 @@ -86,4 +86,45 @@ /datum/sprite_accessory/facial_hair/elvis name = "Sideburns (Elvis)" - icon_state = "facial_elvis" \ No newline at end of file + icon_state = "facial_elvis" + +#define VFACE(_name, new_state) /datum/sprite_accessory/facial_hair/##new_state/icon_state=#new_state;;/datum/sprite_accessory/facial_hair/##new_state/name= #_name + " (Virgo)" +VFACE("Watson", facial_watson_s) +VFACE("Chaplin", facial_chaplin_s) +VFACE("Fullbeard", facial_fullbeard_s) +VFACE("Vandyke", facial_vandyke_s) +VFACE("Elvis", facial_elvis_s) +VFACE("Abe", facial_abe_s) +VFACE("Chin", facial_chin_s) +VFACE("GT", facial_gt_s) +VFACE("Hip", facial_hip_s) +VFACE("Hogan", facial_hogan_s) +VFACE("Selleck", facial_selleck_s) +VFACE("Neckbeard", facial_neckbeard_s) +VFACE("Longbeard", facial_longbeard_s) +VFACE("Dwarf", facial_dwarf_s) +VFACE("Sideburn", facial_sideburn_s) +VFACE("Mutton", facial_mutton_s) +VFACE("Moustache", facial_moustache_s) +VFACE("Pencilstache", facial_pencilstache_s) +VFACE("Goatee", facial_goatee_s) +VFACE("Smallstache", facial_smallstache_s) +VFACE("Volaju", facial_volaju_s) +VFACE("3 O\'clock", facial_3oclock_s) +VFACE("5 O\'clock", facial_5oclock_s) +VFACE("7 O\'clock", facial_7oclock_s) +VFACE("5 O\'clock Moustache", facial_5oclockmoustache_s) +VFACE("7 O\'clock", facial_7oclockmoustache_s) +VFACE("Walrus", facial_walrus_s) +VFACE("Muttonmus", facial_muttonmus_s) +VFACE("Wise", facial_wise_s) +VFACE("Martial Artist", facial_martialartist_s) +VFACE("Dorsalfnil", facial_dorsalfnil_s) +VFACE("Hornadorns", facial_hornadorns_s) +VFACE("Spike", facial_spike_s) +VFACE("Chinhorns", facial_chinhorns_s) +VFACE("Cropped Fullbeard", facial_croppedfullbeard_s) +VFACE("Chinless Beard", facial_chinlessbeard_s) +VFACE("Moonshiner", facial_moonshiner_s) +VFACE("Tribearder", facial_tribearder_s) +#undef VFACE \ No newline at end of file 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 f8d8d26328..abcc90c0ee 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 @@ -461,4 +461,163 @@ /datum/sprite_accessory/hair/longestalt name = "Very Long with Fringe" - icon_state = "hair_vlongfringe" \ No newline at end of file + icon_state = "hair_vlongfringe" + +/*************** VIRGO PORTED HAIRS ****************************/ +#define VHAIR(_name, new_state) /datum/sprite_accessory/hair/##new_state/icon_state=#new_state;/datum/sprite_accessory/hair/##new_state/name = #_name + " (Virgo)" +//VIRGO PORTED HAIRS +VHAIR("Short Hair Rosa", hair_rosa_s) +VHAIR("Short Hair 80s", hair_80s_s) +VHAIR("Long Bedhead", hair_long_bedhead_s) +VHAIR("Dave", hair_dave_s) +VHAIR("Country", hair_country_s) +VHAIR("Shy", hair_shy_s) +VHAIR("Unshaven Mohawk", hair_unshaven_mohawk_s) +VHAIR("Manbun", hair_manbun_s) +VHAIR("Longer Bedhead", hair_longer_bedhead_s) +VHAIR("Ponytail", hair_ponytail_s) +VHAIR("Ziegler", hair_ziegler_s) +VHAIR("Emo Fringe", hair_emofringe_s) +VHAIR("Very Short Over Eye Alt", hair_veryshortovereyealternate_s) +VHAIR("Shorthime", hair_shorthime_s) +VHAIR("High Tight", hair_hightight_s) +VHAIR("Thinning Front", hair_thinningfront_s) +VHAIR("Big Afro", hair_bigafro_s) +VHAIR("Afro", hair_afro_s) +VHAIR("High Braid", hair_hbraid_s) +VHAIR("Braid", hair_braid_s) +VHAIR("Sargeant", hair_sargeant_s) +VHAIR("Gelled", hair_gelled_s) +VHAIR("Kagami", hair_kagami_s) +VHAIR("ShortTail", hair_stail_s) +VHAIR("Gentle", hair_gentle_s) +VHAIR("Grande", hair_grande_s) +VHAIR("Bobcurl", hair_bobcurl_s) +VHAIR("Pompadeur", hair_pompadour_s) +VHAIR("Plait", hair_plait_s) +VHAIR("Long", hair_long_s) +VHAIR("Rattail", hair_rattail_s) +VHAIR("Tajspiky", hair_tajspiky_s) +VHAIR("Messy", hair_messy_s) +VHAIR("Bangs", hair_bangs_s) +VHAIR("TBraid", hair_tbraid_s) +VHAIR("Toriyama2", hair_toriyama2_s) +VHAIR("CIA", hair_cia_s) +VHAIR("Mulder", hair_mulder_s) +VHAIR("Scully", hair_scully_s) +VHAIR("Nitori", hair_nitori_s) +VHAIR("Joestar", hair_joestar_s) +VHAIR("Ponytail4", hair_ponytail4_s) +VHAIR("Ponytail5", hair_ponytail5_s) +VHAIR("Beehive2", hair_beehive2_s) +VHAIR("Short Braid", hair_shortbraid_s) +VHAIR("Reverse Mohawk", hair_reversemohawk_s) +VHAIR("SHort Bangs", hair_shortbangs_s) +VHAIR("Half Shaved", hair_halfshaved_s) +VHAIR("Longer Alt 2", hair_longeralt2_s) +VHAIR("Bun", hair_bun_s) +VHAIR("Curly", hair_curly_s) +VHAIR("Victory", hair_victory_s) +VHAIR("Ponytail6", hair_ponytail6_s) +VHAIR("Undercut3", hair_undercut3_s) +VHAIR("Bobcut Alt", hair_bobcultalt_s) +VHAIR("Fingerwave", hair_fingerwave_s) +VHAIR("Oxton", hair_oxton_s) +VHAIR("Poofy2", hair_poofy2_s) +VHAIR("Fringe Tail", hair_fringetail_s) +VHAIR("Bun3", hair_bun3_s) +VHAIR("Wisp", hair_wisp_s) +VHAIR("Undercut2", hair_undercut2_s) +VHAIR("TBob", hair_tbob_s) +VHAIR("Spiky Ponytail", hair_spikyponytail_s) +VHAIR("Rowbun", hair_rowbun_s) +VHAIR("Rowdualtail", hair_rowdualtail_s) +VHAIR("Rowbraid", hair_rowbraid_s) +VHAIR("Shaved Mohawk", hair_shavedmohawk_s) +VHAIR("Topknot", hair_topknot_s) +VHAIR("Ronin", hair_ronin_s) +VHAIR("Bowlcut2", hair_bowlcut2_s) +VHAIR("Thinning Rear", hair_thinningrear_s) +VHAIR("Thinning", hair_thinning_s) +VHAIR("Jade", hair_jade_s) +VHAIR("Bedhead", hair_bedhead_s) +VHAIR("Dreadlocks", hair_dreads_s) +VHAIR("Very Long", hair_vlong_s) +VHAIR("Jensen", hair_jensen_s) +VHAIR("Halfbang", hair_halfbang_s) +VHAIR("Kusangi", hair_kusangi_s) +VHAIR("Ponytail", hair_ponytail_s) +VHAIR("Ponytail3", hair_ponytail3_s) +VHAIR("Halfbang Alt", hair_halfbang_alt_s) +VHAIR("Bedhead V2", hair_bedheadv2_s) +VHAIR("Long Fringe", hair_longfringe_s) +VHAIR("Flair", hair_flair_s) +VHAIR("Bedhead V3", hair_bedheadv3_s) +VHAIR("Himecut", hair_himecut_s) +VHAIR("Curls", hair_curls_s) +VHAIR("Very Long Fringe", hair_vlongfringe_s) +VHAIR("Longest", hair_longest_s) +VHAIR("Father", hair_father_s) +VHAIR("Emo Long", hair_emolong_s) +VHAIR("Short Hair 3", hair_shorthair3_s) +VHAIR("Double Bun", hair_doublebun_s) +VHAIR("Sleeze", hair_sleeze_s) +VHAIR("Twintail", hair_twintail_s) +VHAIR("Emo 2", hair_emo2_s) +VHAIR("Low Fade", hair_lowfade_s) +VHAIR("Med Fade", hair_medfade_s) +VHAIR("High Fade", hair_highfade_s) +VHAIR("Bald Fade", hair_baldfade_s) +VHAIR("No Fade", hair_nofade_s) +VHAIR("Trim Flat", hair_trimflat_s) +VHAIR("Shaved", hair_shaved_s) +VHAIR("Trimmed", hair_trimmed_s) +VHAIR("Tight Bun", hair_tightbun_s) +VHAIR("Short Hair 4", hair_d_s) +VHAIR("Short Hair 5", hair_e_s) +VHAIR("Short Hair 6", hair_f_s) +VHAIR("Skinhead", hair_skinhead_s) +VHAIR("Afro2", hair_afro2_s) +VHAIR("Bobcut", hair_bobcut_s) +VHAIR("Emo", hair_emo_s) +VHAIR("Long Over Eye", hair_longovereye_s) +VHAIR("Feather", hair_feather_s) +VHAIR("Hitop", hair_hitop_s) +VHAIR("Short Over Eye", hair_shortoverye_s) +VHAIR("Straight", hair_straight_s) +VHAIR("Buzzcut", hair_buzzcut_s) +VHAIR("Combover", hair_combover_s) +VHAIR("Crewcut", hair_crewcut_s) +VHAIR("Devillock", hair_devilock_s) +VHAIR("Clean", hair_clean_s) +VHAIR("Shaggy", hair_shaggy_s) +VHAIR("Updo", hair_updo_s) +VHAIR("Mohawk", hair_mohawk_s) +VHAIR("Odango", hair_odango_s) +VHAIR("Ombre", hair_ombre_s) +VHAIR("Parted", hair_parted_s) +VHAIR("Quiff", hair_quiff_s) +VHAIR("Volaju", hair_volaju_s) +VHAIR("Bun2", hair_bun2_s) +VHAIR("Rows1", hair_rows1_s) +VHAIR("Rows2", hair_rows2_s) +VHAIR("Dandy Pompadour", hair_dandypompadour_s) +VHAIR("Poofy", hair_poofy_s) +VHAIR("Toriyama", hair_toriyama_s) +VHAIR("Drillruru", hair_drillruru_s) +VHAIR("Bowlcut", hair_bowlcut_s) +VHAIR("Coffee House", hair_coffeehouse_s) +VHAIR("Family Man", hair_thefamilyman_s) +VHAIR("Shaved Part", hair_shavedpart_s) +VHAIR("Modern", hair_modern_s) +VHAIR("One Shoulder", hair_oneshoulder_s) +VHAIR("Very Short Over Eye", hair_veryshortovereye_s) +VHAIR("Unkept", hair_unkept_s) +VHAIR("Wife", hair_wife_s) +VHAIR("Nia", hair_nia_s) +VHAIR("Undercut", hair_undercut_s) +VHAIR("Bobcut Alt", hair_bobcutalt_s) +VHAIR("Short Hair 4 alt", hair_shorthair4_s) +VHAIR("Tressshoulder", hair_tressshoulder_s) + //END +#undef VHAIR \ No newline at end of file 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 607ad650e3..a630ead7b3 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/horns.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/horns.dm @@ -1,5 +1,6 @@ /datum/sprite_accessory/horns icon = 'icons/mob/mutant_bodyparts.dmi' + color_src = HORNCOLOR /datum/sprite_accessory/horns/none name = "None" @@ -23,4 +24,13 @@ /datum/sprite_accessory/horns/angler name = "Angeler" - icon_state = "angler" \ No newline at end of file + icon_state = "angler" + color_src = MUTCOLORS + +/datum/sprite_accessory/horns/antler + name = "Deer Antlers" + icon_state = "deer" + +/datum/sprite_accessory/horns/guilmon + name = "Guilmon" + icon_state = "guilmon" \ No newline at end of file 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 new file mode 100644 index 0000000000..6d2ab1a39b --- /dev/null +++ b/code/modules/mob/dead/new_player/sprite_accessories/ipc_synths.dm @@ -0,0 +1,158 @@ + +/****************************************** +************** IPC SCREENS **************** +*******************************************/ +/datum/sprite_accessory/screen + icon = 'modular_citadel/icons/mob/ipc_screens.dmi' + color_src = null + +/datum/sprite_accessory/screen/blank + 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/console + name = "Console" + icon_state = "console" + +/datum/sprite_accessory/screen/rgb + name = "RGB" + icon_state = "rgb" + +/datum/sprite_accessory/screen/golglider + name = "Gol Glider" + icon_state = "golglider" + +/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/sinewave + name = "Sine wave" + icon_state = "sinewave" + +/datum/sprite_accessory/screen/squarewave + name = "Square wave" + icon_state = "squarwave" + +/datum/sprite_accessory/screen/ecgwave + name = "ECG wave" + icon_state = "ecgwave" + +/datum/sprite_accessory/screen/eyes + name = "Eyes" + icon_state = "eyes" + +/datum/sprite_accessory/screen/textdrop + name = "Text drop" + icon_state = "textdrop" + +/datum/sprite_accessory/screen/stars + name = "Stars" + icon_state = "stars" + + +/****************************************** +************** IPC Antennas *************** +*******************************************/ + +/datum/sprite_accessory/antenna + icon = 'modular_citadel/icons/mob/ipc_antennas.dmi' + color_src = MUTCOLORS2 + +/datum/sprite_accessory/antenna/none + name = "None" + icon_state = "None" + +/datum/sprite_accessory/antenna/antennae + 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" + +/datum/sprite_accessory/antenna/crowned + name = "Crowned" + icon_state = "crowned" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/legs.dm b/code/modules/mob/dead/new_player/sprite_accessories/legs.dm deleted file mode 100644 index 7663100822..0000000000 --- a/code/modules/mob/dead/new_player/sprite_accessories/legs.dm +++ /dev/null @@ -1,8 +0,0 @@ -/datum/sprite_accessory/legs //legs are a special case, they aren't actually sprite_accessories but are updated with them. - icon = null //These datums exist for selecting legs on preference, and little else - -/datum/sprite_accessory/legs/none - name = "Normal Legs" - -/datum/sprite_accessory/legs/digitigrade_lizard - name = "Digitigrade Legs" \ No newline at end of file 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 new file mode 100644 index 0000000000..15640a2699 --- /dev/null +++ b/code/modules/mob/dead/new_player/sprite_accessories/legs_and_taurs.dm @@ -0,0 +1,124 @@ +/datum/sprite_accessory/legs //legs are a special case, they aren't actually sprite_accessories but are updated with them. -- OR SO THEY USED TO BE + icon = null //These datums exist for selecting legs on preference, and little else + +/****************************************** +***************** Leggy ******************* +*******************************************/ + +/datum/sprite_accessory/legs/none + name = "Plantigrade" + +/datum/sprite_accessory/legs/digitigrade_lizard + name = "Digitigrade" + +/datum/sprite_accessory/legs/digitigrade_bird + name = "Avian" + + +/****************************************** +************** Taur Bodies **************** +*******************************************/ + +/datum/sprite_accessory/taur + icon = 'modular_citadel/icons/mob/mam_taur.dmi' + extra_icon = 'modular_citadel/icons/mob/mam_taur.dmi' + extra = TRUE + extra2_icon = 'modular_citadel/icons/mob/mam_taur.dmi' + extra2 = TRUE + center = TRUE + dimension_x = 64 + var/taur_mode = NOT_TAURIC + color_src = MATRIXED + +/datum/sprite_accessory/taur/none + name = "None" + icon_state = "None" + +/datum/sprite_accessory/taur/cow + name = "Cow" + icon_state = "cow" + taur_mode = HOOF_TAURIC + +/datum/sprite_accessory/taur/deer + name = "Deer" + icon_state = "deer" + taur_mode = HOOF_TAURIC + color_src = MUTCOLORS + +/datum/sprite_accessory/taur/drake + name = "Drake" + icon_state = "drake" + taur_mode = PAW_TAURIC + +/datum/sprite_accessory/taur/drider + name = "Drider" + icon_state = "drider" + color_src = MUTCOLORS + +/datum/sprite_accessory/taur/eevee + name = "Eevee" + icon_state = "eevee" + taur_mode = PAW_TAURIC + color_src = MUTCOLORS + +/datum/sprite_accessory/taur/fox + name = "Fox" + icon_state = "fox" + taur_mode = PAW_TAURIC + +/datum/sprite_accessory/taur/husky + name = "Husky" + icon_state = "husky" + taur_mode = PAW_TAURIC + +/datum/sprite_accessory/taur/horse + name = "Horse" + icon_state = "horse" + taur_mode = HOOF_TAURIC + +/datum/sprite_accessory/taur/lab + name = "Lab" + icon_state = "lab" + taur_mode = PAW_TAURIC + +/datum/sprite_accessory/taur/naga + name = "Naga" + icon_state = "naga" + taur_mode = SNEK_TAURIC + +/datum/sprite_accessory/taur/otie + name = "Otie" + icon_state = "otie" + taur_mode = PAW_TAURIC + +/datum/sprite_accessory/taur/pede + name = "Scolipede" + icon_state = "pede" + taur_mode = PAW_TAURIC + color_src = MUTCOLORS + +/datum/sprite_accessory/taur/panther + name = "Panther" + icon_state = "panther" + taur_mode = PAW_TAURIC + +/datum/sprite_accessory/taur/shepherd + name = "Shepherd" + icon_state = "shepherd" + taur_mode = PAW_TAURIC + +/datum/sprite_accessory/taur/tentacle + name = "Tentacle" + icon_state = "tentacle" + taur_mode = SNEK_TAURIC + color_src = MUTCOLORS + +/datum/sprite_accessory/taur/tiger + name = "Tiger" + icon_state = "tiger" + taur_mode = PAW_TAURIC + +/datum/sprite_accessory/taur/wolf + name = "Wolf" + icon_state = "wolf" + taur_mode = PAW_TAURIC diff --git a/code/modules/mob/dead/new_player/sprite_accessories/moth_wings.dm b/code/modules/mob/dead/new_player/sprite_accessories/moth_wings.dm deleted file mode 100644 index 6b8036bd69..0000000000 --- a/code/modules/mob/dead/new_player/sprite_accessories/moth_wings.dm +++ /dev/null @@ -1,68 +0,0 @@ -/datum/sprite_accessory/moth_wings - icon = 'icons/mob/wings.dmi' - color_src = null - -/datum/sprite_accessory/moth_wings/plain - name = "Plain" - icon_state = "plain" - -/datum/sprite_accessory/moth_wings/monarch - name = "Monarch" - icon_state = "monarch" - -/datum/sprite_accessory/moth_wings/luna - name = "Luna" - icon_state = "luna" - -/datum/sprite_accessory/moth_wings/atlas - name = "Atlas" - icon_state = "atlas" - -/datum/sprite_accessory/moth_wings/reddish - name = "Reddish" - icon_state = "redish" - -/datum/sprite_accessory/moth_wings/royal - name = "Royal" - icon_state = "royal" - -/datum/sprite_accessory/moth_wings/gothic - name = "Gothic" - icon_state = "gothic" - -/datum/sprite_accessory/moth_wings/lovers - name = "Lovers" - icon_state = "lovers" - -/datum/sprite_accessory/moth_wings/whitefly - name = "White Fly" - icon_state = "whitefly" - -/datum/sprite_accessory/moth_wings/punished - name = "Burnt Off" - icon_state = "punished" - locked = TRUE - -/datum/sprite_accessory/moth_wings/firewatch - name = "Firewatch" - icon_state = "firewatch" - -/datum/sprite_accessory/moth_wings/deathhead - name = "Deathshead" - icon_state = "deathhead" - -/datum/sprite_accessory/moth_wings/poison - name = "Poison" - icon_state = "poison" - -/datum/sprite_accessory/moth_wings/ragged - name = "Ragged" - icon_state = "ragged" - -/datum/sprite_accessory/moth_wings/moonfly - name = "Moon Fly" - icon_state = "moonfly" - -/datum/sprite_accessory/moth_wings/snow - name = "Snow" - icon_state = "snow" 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 c663c08d69..7252f85324 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm @@ -15,4 +15,359 @@ /datum/sprite_accessory/snouts/roundlight name = "Round + Light" - icon_state = "roundlight" \ No newline at end of file + 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 + name = "Beak" + icon_state = "bird" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/snouts/bigbeak + name = "Big Beak" + icon_state = "bigbeak" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/snouts/bug + name = "Bug" + icon_state = "bug" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + extra2 = TRUE + extra2_color_src = MUTCOLORS3 + +/datum/sprite_accessory/snouts/elephant + name = "Elephant" + icon_state = "elephant" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + extra = TRUE + extra_color_src = MUTCOLORS3 + +/datum/sprite_accessory/snouts/lcanid + name = "Mammal, Long" + icon_state = "lcanid" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/snouts/lcanidalt + name = "Mammal, Long ALT" + icon_state = "lcanidalt" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/snouts/scanid + name = "Mammal, Short" + icon_state = "scanid" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/snouts/scanidalt + name = "Mammal, Short ALT" + icon_state = "scanidalt" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/snouts/wolf + name = "Mammal, Thick" + icon_state = "wolf" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/snouts/wolfalt + name = "Mammal, Thick ALT" + icon_state = "wolfalt" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/snouts/redpanda + name = "WahCoon" + icon_state = "wah" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/snouts/rhino + name = "Horn" + icon_state = "rhino" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + extra = TRUE + extra = MUTCOLORS3 + +/datum/sprite_accessory/snouts/rodent + name = "Rodent" + icon_state = "rodent" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/snouts/husky + name = "Husky" + icon_state = "husky" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/snouts/otie + name = "Otie" + icon_state = "otie" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/snouts/pede + name = "Scolipede" + icon_state = "pede" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/snouts/sergal + name = "Sergal" + icon_state = "sergal" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/snouts/shark + name = "Shark" + icon_state = "shark" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + +/datum/sprite_accessory/snouts/toucan + name = "Toucan" + icon_state = "toucan" + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + color_src = MATRIXED +*/ + +/****************************************** +************** Mammal Snouts ************** +*******************************************/ + +/datum/sprite_accessory/mam_snouts + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_snouts.dmi' + +/datum/sprite_accessory/mam_snouts/none + name = "None" + icon_state = "none" + + +/datum/sprite_accessory/mam_snouts/bird + name = "Beak" + icon_state = "bird" + +/datum/sprite_accessory/mam_snouts/bigbeak + name = "Big Beak" + icon_state = "bigbeak" + +/datum/sprite_accessory/mam_snouts/bug + name = "Bug" + icon_state = "bug" + color_src = MUTCOLORS + extra2 = TRUE + extra2_color_src = MUTCOLORS3 + +/datum/sprite_accessory/mam_snouts/elephant + name = "Elephant" + icon_state = "elephant" + extra = TRUE + extra_color_src = MUTCOLORS3 + +/datum/sprite_accessory/mam_snouts/lcanid + name = "Mammal, Long" + icon_state = "lcanid" + +/datum/sprite_accessory/mam_snouts/lcanidalt + name = "Mammal, Long ALT" + icon_state = "lcanidalt" + +/datum/sprite_accessory/mam_snouts/scanid + name = "Mammal, Short" + icon_state = "scanid" + +/datum/sprite_accessory/mam_snouts/scanidalt + name = "Mammal, Short ALT" + icon_state = "scanidalt" + +/datum/sprite_accessory/mam_snouts/wolf + name = "Mammal, Thick" + icon_state = "wolf" + +/datum/sprite_accessory/mam_snouts/wolfalt + name = "Mammal, Thick ALT" + icon_state = "wolfalt" + +/datum/sprite_accessory/mam_snouts/redpanda + name = "WahCoon" + icon_state = "wah" + +/datum/sprite_accessory/mam_snouts/redpandaalt + name = "WahCoon ALT" + icon_state = "wahalt" + +/datum/sprite_accessory/mam_snouts/rhino + name = "Horn" + icon_state = "rhino" + extra = TRUE + extra = MUTCOLORS3 + +/datum/sprite_accessory/mam_snouts/rodent + name = "Rodent" + icon_state = "rodent" + +/datum/sprite_accessory/mam_snouts/husky + name = "Husky" + icon_state = "husky" + +/datum/sprite_accessory/mam_snouts/otie + name = "Otie" + icon_state = "otie" + +/datum/sprite_accessory/mam_snouts/pede + name = "Scolipede" + icon_state = "pede" + +/datum/sprite_accessory/mam_snouts/sergal + name = "Sergal" + icon_state = "sergal" + +/datum/sprite_accessory/mam_snouts/shark + name = "Shark" + icon_state = "shark" + +/datum/sprite_accessory/mam_snouts/toucan + name = "Toucan" + icon_state = "toucan" + +/datum/sprite_accessory/mam_snouts/sharp + name = "Sharp" + icon_state = "sharp" + color_src = MUTCOLORS + +/datum/sprite_accessory/mam_snouts/round + name = "Round" + icon_state = "round" + color_src = MUTCOLORS + +/datum/sprite_accessory/mam_snouts/sharplight + name = "Sharp + Light" + icon_state = "sharplight" + color_src = MUTCOLORS + +/datum/sprite_accessory/mam_snouts/roundlight + name = "Round + Light" + icon_state = "roundlight" + color_src = MUTCOLORS + + +/****************************************** +**************** Snouts ******************* +*************but higher up*****************/ + +/datum/sprite_accessory/mam_snouts/fbird + name = "Beak (Top)" + icon_state = "fbird" + +/datum/sprite_accessory/mam_snouts/fbigbeak + name = "Big Beak (Top)" + icon_state = "fbigbeak" + +/datum/sprite_accessory/mam_snouts/fbug + name = "Bug (Top)" + icon_state = "fbug" + color_src = MUTCOLORS + extra2 = TRUE + extra2_color_src = MUTCOLORS3 + +/datum/sprite_accessory/mam_snouts/felephant + name = "Elephant (Top)" + icon_state = "felephant" + extra = TRUE + extra_color_src = MUTCOLORS3 + +/datum/sprite_accessory/mam_snouts/flcanid + name = "Mammal, Long (Top)" + icon_state = "flcanid" + +/datum/sprite_accessory/mam_snouts/flcanidalt + name = "Mammal, Long ALT (Top)" + icon_state = "flcanidalt" + +/datum/sprite_accessory/mam_snouts/fscanid + name = "Mammal, Short (Top)" + icon_state = "fscanid" + +/datum/sprite_accessory/mam_snouts/fscanidalt + name = "Mammal, Short ALT (Top)" + icon_state = "fscanidalt" + +/datum/sprite_accessory/mam_snouts/fwolf + name = "Mammal, Thick (Top)" + icon_state = "fwolf" + +/datum/sprite_accessory/mam_snouts/fwolfalt + name = "Mammal, Thick ALT (Top)" + icon_state = "fwolfalt" + +/datum/sprite_accessory/mam_snouts/fredpanda + name = "WahCoon (Top)" + icon_state = "fwah" + +/datum/sprite_accessory/mam_snouts/frhino + name = "Horn (Top)" + icon_state = "frhino" + extra = TRUE + extra = MUTCOLORS3 + +/datum/sprite_accessory/mam_snouts/frodent + name = "Rodent (Top)" + icon_state = "frodent" + +/datum/sprite_accessory/mam_snouts/fhusky + name = "Husky (Top)" + icon_state = "fhusky" + +/datum/sprite_accessory/mam_snouts/fotie + name = "Otie (Top)" + icon_state = "fotie" + +/datum/sprite_accessory/mam_snouts/fpede + name = "Scolipede (Top)" + icon_state = "fpede" + +/datum/sprite_accessory/mam_snouts/fsergal + name = "Sergal (Top)" + icon_state = "fsergal" + +/datum/sprite_accessory/mam_snouts/fshark + name = "Shark (Top)" + icon_state = "fshark" + +/datum/sprite_accessory/mam_snouts/ftoucan + name = "Toucan (Top)" + icon_state = "ftoucan" + +/datum/sprite_accessory/mam_snouts/fsharp + name = "Sharp (Top)" + icon_state = "fsharp" + color_src = MUTCOLORS + +/datum/sprite_accessory/mam_snouts/fround + name = "Round (Top)" + icon_state = "fround" + color_src = MUTCOLORS + +/datum/sprite_accessory/mam_snouts/fsharplight + name = "Sharp + Light (Top)" + icon_state = "fsharplight" + color_src = MUTCOLORS + +/datum/sprite_accessory/mam_snouts/froundlight + name = "Round + Light (Top)" + icon_state = "froundlight" + color_src = MUTCOLORS \ No newline at end of file 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 31faabf663..6042d97247 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/tails.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/tails.dm @@ -4,6 +4,10 @@ /datum/sprite_accessory/tails_animated icon = 'icons/mob/mutant_bodyparts.dmi' +/****************************************** +************* Lizard Tails **************** +*******************************************/ + /datum/sprite_accessory/tails/lizard/smooth name = "Smooth" icon_state = "smooth" @@ -36,6 +40,48 @@ name = "Spikes" icon_state = "spikes" +/datum/sprite_accessory/tails/lizard/none + name = "None" + icon_state = "None" + +/datum/sprite_accessory/tails_animated/lizard/none + name = "None" + icon_state = "None" + +/datum/sprite_accessory/tails/lizard/axolotl + name = "Axolotl" + icon_state = "axolotl" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/lizard/axolotl + name = "Axolotl" + icon_state = "axolotl" + 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/guilmon + name = "Guilmon" + icon_state = "guilmon" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/lizard/guilmon + name = "Guilmon" + icon_state = "guilmon" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/****************************************** +************** Human Tails **************** +*******************************************/ + /datum/sprite_accessory/tails/human/none name = "None" icon_state = "none" @@ -43,13 +89,626 @@ /datum/sprite_accessory/tails_animated/human/none name = "None" icon_state = "none" -/* + +/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" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails_animated/human/axolotl + name = "Axolotl" + icon_state = "axolotl" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails/human/bee + name = "Bee" + icon_state = "bee" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails_animated/human/bee + name = "Bee" + icon_state = "bee" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + /datum/sprite_accessory/tails/human/cat name = "Cat" icon_state = "cat" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' color_src = HAIR /datum/sprite_accessory/tails_animated/human/cat name = "Cat" icon_state = "cat" - color_src = HAIR*/ \ No newline at end of file + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = HAIR + +/datum/sprite_accessory/tails/human/catbig + name = "Cat, Big" + icon_state = "catbig" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails_animated/human/catbig + name = "Cat, Big" + icon_state = "catbig" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails/human/cow + name = "Cow" + icon_state = "cow" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails_animated/human/cow + name = "Cow" + icon_state = "cow" + 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/eevee + name = "Eevee" + icon_state = "eevee" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails_animated/human/eevee + name = "Eevee" + icon_state = "eevee" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails/human/fennec + name = "Fennec" + icon_state = "fennec" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails_animated/human/fennec + name = "Fennec" + icon_state = "fennec" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails/human/fish + name = "Fish" + icon_state = "fish" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails_animated/human/fish + name = "Fish" + icon_state = "fish" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails/human/fox + name = "Fox" + icon_state = "fox" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails_animated/human/fox + name = "Fox" + icon_state = "fox" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails/human/horse + name = "Horse" + icon_state = "horse" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = HAIR + +/datum/sprite_accessory/tails_animated/human/horse + name = "Horse" + icon_state = "horse" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = HAIR + +/datum/sprite_accessory/tails/human/husky + name = "Husky" + icon_state = "husky" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails_animated/human/husky + name = "Husky" + icon_state = "husky" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails/human/insect + name = "Insect" + icon_state = "insect" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails_animated/human/insect + name = "insect" + icon_state = "insect" + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + color_src = MATRIXED + +/datum/sprite_accessory/tails/human/kitsune + name = "Kitsune" + icon_state = "kitsune" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/human/kitsune + name = "Kitsune" + icon_state = "kitsune" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails/human/murid + name = "Murid" + icon_state = "murid" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/human/murid + name = "Murid" + icon_state = "murid" + 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" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/orca + name = "Orca" + icon_state = "orca" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/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/rabbit + name = "Rabbit" + icon_state = "rabbit" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/human/rabbit + name = "Rabbit" + icon_state = "rabbit" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails/human/sergal + name = "Sergal" + icon_state = "sergal" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/human/sergal + name = "Sergal" + icon_state = "sergal" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails/human/skunk + name = "skunk" + icon_state = "skunk" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/human/skunk + name = "skunk" + icon_state = "skunk" + 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/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" + +/datum/sprite_accessory/tails_animated/human/straighttail + name = "Straight Tail" + icon_state = "straighttail" + +/datum/sprite_accessory/tails/human/squirrel + name = "Squirrel" + icon_state = "squirrel" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/human/squirrel + name = "Squirrel" + icon_state = "squirrel" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails/human/tentacle + name = "Tentacle" + icon_state = "tentacle" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/human/tentacle + name = "Tentacle" + icon_state = "tentacle" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails/human/tiger + name = "Tiger" + icon_state = "tiger" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/human/tiger + name = "Tiger" + icon_state = "tiger" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails/human/wolf + name = "Wolf" + icon_state = "wolf" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/tails_animated/human/wolf + name = "Wolf" + icon_state = "wolf" + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/****************************************** +************** Furry Tails **************** +*******************************************/ + +/datum/sprite_accessory/mam_tails + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/mam_tails/none + name = "None" + icon_state = "none" + +/datum/sprite_accessory/mam_tails_animated + color_src = MATRIXED + icon = 'modular_citadel/icons/mob/mam_tails.dmi' + +/datum/sprite_accessory/mam_tails_animated/none + name = "None" + icon_state = "none" + color_src = MATRIXED + +/datum/sprite_accessory/mam_tails/ailurus + name = "Red Panda" + icon_state = "wah" + extra = TRUE + +/datum/sprite_accessory/mam_tails_animated/ailurus + name = "Red Panda" + icon_state = "wah" + extra = TRUE + +/datum/sprite_accessory/mam_tails/axolotl + name = "Axolotl" + icon_state = "axolotl" + +/datum/sprite_accessory/mam_tails_animated/axolotl + name = "Axolotl" + icon_state = "axolotl" + +/datum/sprite_accessory/mam_tails/bee + name = "Bee" + icon_state = "bee" + +/datum/sprite_accessory/mam_tails_animated/bee + name = "Bee" + icon_state = "bee" + +/datum/sprite_accessory/mam_tails/cat + name = "Cat" + icon_state = "cat" + color_src = HAIR + +/datum/sprite_accessory/mam_tails_animated/cat + name = "Cat" + icon_state = "cat" + color_src = HAIR + +/datum/sprite_accessory/mam_tails/catbig + name = "Cat, Big" + icon_state = "catbig" + +/datum/sprite_accessory/mam_tails_animated/catbig + name = "Cat, Big" + icon_state = "catbig" + +/datum/sprite_accessory/mam_tails/corvid + name = "Corvid" + icon_state = "crow" + +/datum/sprite_accessory/mam_tails_animated/corvid + name = "Corvid" + icon_state = "crow" + +/datum/sprite_accessory/mam_tail/cow + name = "Cow" + icon_state = "cow" + +/datum/sprite_accessory/mam_tails_animated/cow + name = "Cow" + icon_state = "cow" + +/datum/sprite_accessory/mam_tails/eevee + name = "Eevee" + icon_state = "eevee" + +/datum/sprite_accessory/mam_tails_animated/eevee + name = "Eevee" + icon_state = "eevee" + +/datum/sprite_accessory/mam_tails/fennec + name = "Fennec" + icon_state = "fennec" + +/datum/sprite_accessory/mam_tails_animated/fennec + name = "Fennec" + icon_state = "fennec" + +/datum/sprite_accessory/mam_tails/human/fish + name = "Fish" + icon_state = "fish" + +/datum/sprite_accessory/mam_tails_animated/human/fish + name = "Fish" + icon_state = "fish" + +/datum/sprite_accessory/mam_tails/fox + name = "Fox" + icon_state = "fox" + +/datum/sprite_accessory/mam_tails_animated/fox + name = "Fox" + icon_state = "fox" + +/datum/sprite_accessory/mam_tails/hawk + name = "Hawk" + icon_state = "hawk" + +/datum/sprite_accessory/mam_tails_animated/hawk + name = "Hawk" + icon_state = "hawk" + +/datum/sprite_accessory/mam_tails/horse + name = "Horse" + icon_state = "horse" + color_src = HAIR + +/datum/sprite_accessory/mam_tails_animated/horse + name = "Horse" + icon_state = "Horse" + color_src = HAIR + +/datum/sprite_accessory/mam_tails/husky + name = "Husky" + icon_state = "husky" + +/datum/sprite_accessory/mam_tails_animated/husky + name = "Husky" + icon_state = "husky" + +datum/sprite_accessory/mam_tails/insect + name = "Insect" + icon_state = "insect" + +/datum/sprite_accessory/mam_tails_animated/insect + name = "Insect" + icon_state = "insect" + +/datum/sprite_accessory/mam_tails/kangaroo + name = "kangaroo" + icon_state = "kangaroo" + +/datum/sprite_accessory/mam_tails_animated/kangaroo + name = "kangaroo" + icon_state = "kangaroo" + +/datum/sprite_accessory/mam_tails/kitsune + name = "Kitsune" + icon_state = "kitsune" + +/datum/sprite_accessory/mam_tails_animated/kitsune + name = "Kitsune" + icon_state = "kitsune" + +/datum/sprite_accessory/mam_tails/lab + name = "Lab" + icon_state = "lab" + +/datum/sprite_accessory/mam_tails_animated/lab + name = "Lab" + icon_state = "lab" + +/datum/sprite_accessory/mam_tails/murid + name = "Murid" + icon_state = "murid" + +/datum/sprite_accessory/mam_tails_animated/murid + name = "Murid" + icon_state = "murid" + +/datum/sprite_accessory/mam_tails/otie + name = "Otusian" + icon_state = "otie" + +/datum/sprite_accessory/mam_tails_animated/otie + name = "Otusian" + icon_state = "otie" + +/datum/sprite_accessory/mam_tails/orca + name = "Orca" + icon_state = "orca" + +/datum/sprite_accessory/mam_tails_animated/orca + name = "Orca" + icon_state = "orca" + +/datum/sprite_accessory/mam_tails/pede + name = "Scolipede" + icon_state = "pede" + +/datum/sprite_accessory/mam_tails_animated/pede + name = "Scolipede" + icon_state = "pede" + +/datum/sprite_accessory/mam_tails/rabbit + name = "Rabbit" + icon_state = "rabbit" + +/datum/sprite_accessory/mam_tails_animated/rabbit + name = "Rabbit" + icon_state = "rabbit" + +/datum/sprite_accessory/mam_tails/sergal + name = "Sergal" + icon_state = "sergal" + +/datum/sprite_accessory/mam_tails_animated/sergal + name = "Sergal" + icon_state = "sergal" + +/datum/sprite_accessory/mam_tails/skunk + name = "Skunk" + icon_state = "skunk" + +/datum/sprite_accessory/mam_tails_animated/skunk + name = "Skunk" + icon_state = "skunk" + +/datum/sprite_accessory/mam_tails/shark + name = "Shark" + icon_state = "shark" + +/datum/sprite_accessory/mam_tails_animated/shark + name = "Shark" + icon_state = "shark" + +/datum/sprite_accessory/mam_tails/shepherd + name = "Shepherd" + icon_state = "shepherd" + +/datum/sprite_accessory/mam_tails_animated/shepherd + name = "Shepherd" + icon_state = "shepherd" + +/datum/sprite_accessory/mam_tails/straighttail + name = "Straight Tail" + icon_state = "straighttail" + +/datum/sprite_accessory/mam_tails_animated/straighttail + name = "Straight Tail" + icon_state = "straighttail" + +/datum/sprite_accessory/mam_tails/squirrel + name = "Squirrel" + icon_state = "squirrel" + +/datum/sprite_accessory/mam_tails_animated/squirrel + name = "Squirrel" + icon_state = "squirrel" + +/datum/sprite_accessory/mam_tails/tentacle + name = "Tentacle" + icon_state = "tentacle" + +/datum/sprite_accessory/mam_tails_animated/tentacle + name = "Tentacle" + icon_state = "tentacle" + +/datum/sprite_accessory/mam_tails/tiger + name = "Tiger" + icon_state = "tiger" + +/datum/sprite_accessory/mam_tails_animated/tiger + name = "Tiger" + icon_state = "tiger" + +/datum/sprite_accessory/mam_tails/wolf + name = "Wolf" + icon_state = "wolf" + +/datum/sprite_accessory/mam_tails_animated/wolf + name = "Wolf" + icon_state = "wolf" 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 d051b2f07a..dc0e0222bf 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/wings.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/wings.dm @@ -1,3 +1,5 @@ +//Angel Wings + /datum/sprite_accessory/wings/none name = "None" icon_state = "none" @@ -23,4 +25,120 @@ dimension_x = 46 center = TRUE dimension_y = 34 - locked = TRUE \ No newline at end of file + locked = TRUE + +//INSECT WINGS + +/datum/sprite_accessory/insect_wings + icon = 'icons/mob/wings.dmi' + color_src = null + +/datum/sprite_accessory/insect_wings/none + name = "None" + icon_state = "none" + +/datum/sprite_accessory/insect_wings/plain + name = "Plain" + icon_state = "plain" + +/datum/sprite_accessory/insect_wings/monarch + name = "Monarch" + icon_state = "monarch" + +/datum/sprite_accessory/insect_wings/luna + name = "Luna" + icon_state = "luna" + +/datum/sprite_accessory/insect_wings/atlas + name = "Atlas" + icon_state = "atlas" + +/datum/sprite_accessory/insect_wings/reddish + name = "Reddish" + icon_state = "redish" + +/datum/sprite_accessory/insect_wings/royal + name = "Royal" + icon_state = "royal" + +/datum/sprite_accessory/insect_wings/gothic + name = "Gothic" + icon_state = "gothic" + +/datum/sprite_accessory/insect_wings/lovers + name = "Lovers" + icon_state = "lovers" + +/datum/sprite_accessory/insect_wings/whitefly + name = "White Fly" + icon_state = "whitefly" + +/datum/sprite_accessory/insect_wings/punished + name = "Burnt Off" + icon_state = "punished" + locked = TRUE + +/datum/sprite_accessory/insect_wings/firewatch + name = "Firewatch" + icon_state = "firewatch" + +/datum/sprite_accessory/insect_wings/deathhead + name = "Deathshead" + icon_state = "deathhead" + +/datum/sprite_accessory/insect_wings/poison + name = "Poison" + icon_state = "poison" + +/datum/sprite_accessory/insect_wings/ragged + name = "Ragged" + icon_state = "ragged" + +/datum/sprite_accessory/insect_wings/moonfly + name = "Moon Fly" + icon_state = "moonfly" + +/datum/sprite_accessory/insect_wings/snow + name = "Snow" + icon_state = "snow" + +/datum/sprite_accessory/insect_wings/colored + name = "Colored (Hair)" + icon_state = "snowplain" + color_src = HAIR + +/datum/sprite_accessory/insect_fluff/colored1 + name = "Colored (Primary)" + icon_state = "snowplain" + color_src = MUTCOLORS + +/datum/sprite_accessory/insect_fluff/colored2 + name = "Colored (Secondary)" + icon_state = "snowplain" + color_src = MUTCOLORS2 + +/datum/sprite_accessory/insect_fluff/colored3 + name = "Colored (Tertiary)" + icon_state = "snowplain" + color_src = MUTCOLORS3 + +/datum/sprite_accessory/insect_wings/bee + name = "Bee" + icon_state = "bee" + +/datum/sprite_accessory/insect_wings/bee_color + name = "Bee (Hair colored)" + icon_state = "bee" + color_src = HAIR + +/datum/sprite_accessory/insect_wings/fairy + name = "Fairy" + icon_state = "fairy" + +/datum/sprite_accessory/insect_wings/bat + name = "Bat" + icon_state = "bat" + +/datum/sprite_accessory/insect_wings/feathery + name = "Feathery" + icon_state = "feathery" diff --git a/code/modules/mob/living/brain/MMI.dm b/code/modules/mob/living/brain/MMI.dm index bacdc4524b..d84ba0d7a1 100644 --- a/code/modules/mob/living/brain/MMI.dm +++ b/code/modules/mob/living/brain/MMI.dm @@ -57,13 +57,14 @@ newbrain.brainmob = null brainmob.forceMove(src) brainmob.container = src - if(!newbrain.damaged_brain) // the brain organ hasn't been beaten to death. + if(!(newbrain.organ_flags & ORGAN_FAILING)) // the brain organ hasn't been beaten to death. brainmob.stat = CONSCIOUS //we manually revive the brain mob GLOB.dead_mob_list -= brainmob GLOB.alive_mob_list += brainmob brainmob.reset_perspective() brain = newbrain + brain.organ_flags |= ORGAN_FROZEN name = "Man-Machine Interface: [brainmob.real_name]" update_icon() @@ -100,6 +101,7 @@ user.put_in_hands(brain) //puts brain in the user's hand or otherwise drops it on the user's turf else brain.forceMove(get_turf(src)) + brain.organ_flags &= ~ORGAN_FROZEN brain = null //No more brain in here diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index dafc6cf5e0..17f81981d1 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -7,11 +7,19 @@ layer = ABOVE_MOB_LAYER zone = BODY_ZONE_HEAD slot = ORGAN_SLOT_BRAIN - vital = TRUE + organ_flags = ORGAN_VITAL attack_verb = list("attacked", "slapped", "whacked") + ///The brain's organ variables are significantly more different than the other organs, with half the decay rate for balance reasons, and twice the maxHealth + decay_factor = STANDARD_ORGAN_DECAY / 4 //30 minutes of decaying to result in a fully damaged brain, since a fast decay rate would be unfun gameplay-wise + + maxHealth = BRAIN_DAMAGE_DEATH + low_threshold = 45 + high_threshold = 120 var/mob/living/brain/brainmob = null - var/damaged_brain = FALSE //whether the brain organ is damaged. + var/brain_death = FALSE //if the brainmob was intentionally killed by attacking the brain after removal, or by severe braindamage var/decoy_override = FALSE //I apologize to the security players, and myself, who abused this, but this is going to go. + //two variables necessary for calculating whether we get a brain trauma or not + var/damage_delta = 0 var/list/datum/brain_trauma/traumas = list() @@ -90,22 +98,89 @@ if(brainmob) O.attack(brainmob, user) //Oh noooeeeee -/obj/item/organ/brain/examine(mob/user) - ..() + if(istype(O, /obj/item/organ_storage)) //BUG_PROBABLE_CAUSE + return //Borg organ bags shouldn't be killing brains - if(brainmob) - if(brainmob.client) - if(brainmob.health <= HEALTH_THRESHOLD_DEAD) - to_chat(user, "It's lifeless and severely damaged.") + if((organ_flags & ORGAN_FAILING) && O.is_drainable() && O.reagents.has_reagent("neurine")) //Neurine fixes dead brains + . = TRUE //don't do attack animation. + var/cached_Bdamage = brainmob?.health + var/datum/reagent/medicine/neurine/N = reagents.has_reagent("neurine") + var/datum/reagent/medicine/mannitol/M1 = reagents.has_reagent("mannitol") + + if(O.reagents.has_reagent("mannitol"))//Just a quick way to bolster the effects if someone mixes up a batch. + N.volume *= (M1.volume*0.5) + + if(!O.reagents.has_reagent("neurine", 10)) + to_chat(user, "There's not enough neurine in [O] to restore [src]!") + return + + user.visible_message("[user] starts to pour the contents of [O] onto [src].", "You start to slowly pour the contents of [O] onto [src].") + if(!do_after(user, 60, TRUE, src)) + to_chat(user, "You failed to pour [O] onto [src]!") + return + + user.visible_message("[user] pours the contents of [O] onto [src], causing it to reform its original shape and turn a slightly brighter shade of pink.", "You pour the contents of [O] onto [src], causing it to reform its original shape and turn a slightly brighter shade of pink.") + setOrganDamage((damage - (0.10 * maxHealth)*(N.volume/10))) //heals a small amount, and by using "setorgandamage", we clear the failing variable if that was up + O.reagents.clear_reagents() + + if(cached_Bdamage <= HEALTH_THRESHOLD_DEAD) //Fixing dead brains yeilds a trauma + if((cached_Bdamage <= HEALTH_THRESHOLD_DEAD) && (brainmob.health > HEALTH_THRESHOLD_DEAD)) + if(prob(80)) + gain_trauma_type(BRAIN_TRAUMA_MILD) + else if(prob(50)) + gain_trauma_type(BRAIN_TRAUMA_SEVERE) + else + gain_trauma_type(BRAIN_TRAUMA_SPECIAL) + return + + if((organ_flags & ORGAN_FAILING) && O.is_drainable() && O.reagents.has_reagent("mannitol")) //attempt to heal the brain + . = TRUE //don't do attack animation. + var/datum/reagent/medicine/mannitol/M = reagents.has_reagent("mannitol") + if(brain_death || brainmob?.health <= HEALTH_THRESHOLD_DEAD) //if the brain is fucked anyway, do nothing + to_chat(user, "[src] is far too damaged, you'll have to use neurine on it!") + return + + if(!O.reagents.has_reagent("mannitol", 10)) + to_chat(user, "There's not enough mannitol in [O] to restore [src]!") + return + + user.visible_message("[user] starts to pour the contents of [O] onto [src].", "You start to slowly pour the contents of [O] onto [src].") + if(!do_after(user, 60, TRUE, src)) + to_chat(user, "You failed to pour [O] onto [src]!") + return + + user.visible_message("[user] pours the contents of [O] onto [src], causing it to reform its original shape and turn a slightly brighter shade of pink.", "You pour the contents of [O] onto [src], causing it to reform its original shape and turn a slightly brighter shade of pink.") + setOrganDamage((damage - (0.05 * maxHealth)*(M.volume/10))) //heals a small amount, and by using "setorgandamage", we clear the failing variable if that was up + O.reagents.clear_reagents() + return + + + +/obj/item/organ/brain/examine(mob/user)//BUG_PROBABLE_CAUSE to_chats changed to . += + . = ..() + + if(user.suiciding) + . += "It's started turning slightly grey. They must not have been able to handle the stress of it all." + else if(brainmob) + if(brainmob.get_ghost(FALSE, TRUE)) + if(brain_death || brainmob.health <= HEALTH_THRESHOLD_DEAD) + . += "It's lifeless and severely damaged, only the strongest of chems will save it." + else if(organ_flags & ORGAN_FAILING) + . += "It seems to still have a bit of energy within it, but it's rather damaged... You may be able to restore it with some mannitol." else - to_chat(user, "You can feel the small spark of life still left in this one.") + . += "You can feel the small spark of life still left in this one." + else if(organ_flags & ORGAN_FAILING) + . += "It seems particularly lifeless and is rather damaged... You may be able to restore it with some mannitol incase it becomes functional again later." else - to_chat(user, "This one seems particularly lifeless. Perhaps it will regain some of its luster later.") + . += "This one seems particularly lifeless. Perhaps it will regain some of its luster later." else if(decoy_override) - to_chat(user, "This one seems particularly lifeless. Perhaps it will regain some of its luster later.") + if(organ_flags & ORGAN_FAILING) + . += "It seems particularly lifeless and is rather damaged... You may be able to restore it with some mannitol incase it becomes functional again later." + else + . += "This one seems particularly lifeless. Perhaps it will regain some of its luster later." else - to_chat(user, "This one is completely devoid of life.") + . += "This one is completely devoid of life." /obj/item/organ/brain/attack(mob/living/carbon/C, mob/user) if(!istype(C)) @@ -141,7 +216,7 @@ Insert(C) else ..() - +/* TO BE REMOVED, KEPT IN CASE OF BUGS /obj/item/organ/brain/proc/get_brain_damage() var/brain_damage_threshold = max_integrity * BRAIN_DAMAGE_INTEGRITY_MULTIPLIER var/offset_integrity = obj_integrity - (max_integrity - brain_damage_threshold) @@ -165,6 +240,54 @@ else if(adjusted_amount <= -DAMAGE_PRECISION) obj_integrity = min(max_integrity, obj_integrity-adjusted_amount) . = adjusted_amount +*/ + +/obj/item/organ/brain/on_life() + if(damage >= BRAIN_DAMAGE_DEATH) //rip + to_chat(owner, "The last spark of life in your brain fizzles out...") + owner.death() + brain_death = TRUE + +/obj/item/organ/brain/on_death() + if(damage <= BRAIN_DAMAGE_DEATH) //rip + brain_death = FALSE + applyOrganDamage(maxHealth * decay_factor) + + +/obj/item/organ/brain/applyOrganDamage(var/d, var/maximum = maxHealth) + ..() + + +/obj/item/organ/brain/check_damage_thresholds(mob/M) + . = ..() + //if we're not more injured than before, return without gambling for a trauma + if(damage <= prev_damage) + return + damage_delta = damage - prev_damage + if(damage > BRAIN_DAMAGE_MILD) + if(prob(damage_delta * (1 + max(0, (damage - BRAIN_DAMAGE_MILD)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1% //learn how to do your bloody math properly goddamnit + gain_trauma_type(BRAIN_TRAUMA_MILD) + if(damage > BRAIN_DAMAGE_SEVERE) + if(prob(damage_delta * (1 + max(0, (damage - BRAIN_DAMAGE_SEVERE)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1% + if(prob(20)) + gain_trauma_type(BRAIN_TRAUMA_SPECIAL) + else + gain_trauma_type(BRAIN_TRAUMA_SEVERE) + + if (owner) + if(owner.stat < UNCONSCIOUS) //conscious or soft-crit + var/brain_message + if(prev_damage < BRAIN_DAMAGE_MILD && damage >= BRAIN_DAMAGE_MILD) + brain_message = "You feel lightheaded." + else if(prev_damage < BRAIN_DAMAGE_SEVERE && damage >= BRAIN_DAMAGE_SEVERE) + brain_message = "You feel less in control of your thoughts." + else if(prev_damage < (BRAIN_DAMAGE_DEATH - 20) && damage >= (BRAIN_DAMAGE_DEATH - 20)) + brain_message = "You can feel your mind flickering on and off..." + + if(.) + . += "\n[brain_message]" + else + return brain_message /obj/item/organ/brain/Destroy() //copypasted from MMIs. if(brainmob) @@ -200,6 +323,10 @@ return FALSE if(!resilience) resilience = initial(trauma.resilience) + if(!owner) + return FALSE + if(owner.stat == DEAD) + return FALSE var/resilience_tier_count = 0 for(var/X in traumas) diff --git a/code/modules/mob/living/brain/life.dm b/code/modules/mob/living/brain/life.dm index 786bb0b55c..51be1f6971 100644 --- a/code/modules/mob/living/brain/life.dm +++ b/code/modules/mob/living/brain/life.dm @@ -20,7 +20,7 @@ else if(istype(loc, /obj/item/organ/brain)) BR = loc if(BR) - BR.damaged_brain = 1 //beaten to a pulp + BR.brain_death = TRUE /mob/living/brain/proc/handle_emp_damage() if(emp_damage) diff --git a/code/modules/mob/living/carbon/alien/larva/emote.dm b/code/modules/mob/living/carbon/alien/larva/emote.dm deleted file mode 100644 index 62cb620ee4..0000000000 --- a/code/modules/mob/living/carbon/alien/larva/emote.dm +++ /dev/null @@ -1,113 +0,0 @@ -/mob/living/carbon/alien/larva/emote(act,m_type=1,message = null) - - var/param = null - if (findtext(act, "-", 1, null)) - var/t1 = findtext(act, "-", 1, null) - param = copytext(act, t1 + 1, length(act) + 1) - act = copytext(act, 1, t1) - - var/muzzled = is_muzzled() - - switch(act) //Alphabetically sorted please. - if ("burp","burps") - if (!muzzled) - message = "[src] burps." - m_type = 2 - if ("choke","chokes") - message = "[src] chokes." - m_type = 2 - if ("collapse","collapses") - Paralyse(2) - message = "[src] collapses!" - m_type = 2 - if ("dance","dances") - if (!src.restrained()) - message = "[src] dances around happily." - m_type = 1 - if ("deathgasp","deathgasps") - message = "[src] lets out a sickly hiss of air and falls limply to the floor..." - m_type = 2 - if ("drool","drools") - message = "[src] drools." - m_type = 1 - if ("gasp","gasps") - message = "[src] gasps." - m_type = 2 - if ("gnarl","gnarls") - if (!muzzled) - message = "[src] gnarls and shows its teeth.." - m_type = 2 - if ("hiss","hisses") - message = "[src] hisses softly." - m_type = 1 - if ("jump","jumps") - message = "[src] jumps!" - m_type = 1 - if ("me") - ..() - return - if ("moan","moans") - message = "[src] moans!" - m_type = 2 - if ("nod","nods") - message = "[src] nods its head." - m_type = 1 - if ("roar","roars") - if (!muzzled) - message = "[src] softly roars." - m_type = 2 - if ("roll","rolls") - if (!src.restrained()) - message = "[src] rolls." - m_type = 1 - if ("scratch","scratches") - if (!src.restrained()) - message = "[src] scratches." - m_type = 1 - if ("screech","screeches") //This orignally was called scretch, changing it. -Sum99 - if (!muzzled) - message = "[src] screeches." - m_type = 2 - if ("shake","shakes") - message = "[src] shakes its head." - m_type = 1 - if ("shiver","shivers") - message = "[src] shivers." - m_type = 2 - if ("sign","signs") - if (!src.restrained()) - message = text("[src] signs[].", (text2num(param) ? text(" the number []", text2num(param)) : null)) - m_type = 1 - if ("snore","snores") - message = "[src] snores." - m_type = 2 - if ("sulk","sulks") - message = "[src] sulks down sadly." - m_type = 1 - if ("sway","sways") - message = "[src] sways around dizzily." - m_type = 1 - if ("tail") - message = "[src] waves its tail." - m_type = 1 - if ("twitch") - message = "[src] twitches violently." - m_type = 1 - if ("whimper","whimpers") - if (!muzzled) - message = "[src] whimpers." - m_type = 2 - - if ("help") //"The exception" - src << "Help for larva emotes. You can use these emotes with say \"*emote\":\n\nburp, choke, collapse, dance, deathgasp, drool, gasp, gnarl, hiss, jump, me, moan, nod, roll, roar, scratch, screech, shake, shiver, sign-#, sulk, sway, tail, twitch, whimper" - - else - src << "Unusable emote '[act]'. Say *help for a list." - - if ((message && src.stat == 0)) - log_emote("[name]/[key] : [message]") - if (m_type & 1) - visible_message(message) - else - audible_message(message) - return diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index 1574305a83..9706fde782 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -22,6 +22,7 @@ return S /obj/item/organ/body_egg/alien_embryo/on_life() + . = ..() switch(stage) if(2, 3) if(prob(2)) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 9acecea545..263edf42c2 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -271,9 +271,13 @@ if(restrained()) changeNext_move(CLICK_CD_BREAKOUT) last_special = world.time + CLICK_CD_BREAKOUT + var/buckle_cd = 600 + if(handcuffed) + var/obj/item/restraints/O = src.get_item_by_slot(SLOT_HANDCUFFED) + buckle_cd = O.breakouttime visible_message("[src] attempts to unbuckle [p_them()]self!", \ - "You attempt to unbuckle yourself... (This will take around one minute and you need to stay still.)") - if(do_after(src, 600, 0, target = src)) + "You attempt to unbuckle yourself... (This will take around [round(buckle_cd/600,1)] minute\s, and you need to stay still.)") + if(do_after(src, buckle_cd, 0, target = src)) if(!buckled) return buckled.user_unbuckle_mob(src,src) @@ -801,7 +805,8 @@ drop_all_held_items() stop_pulling() throw_alert("handcuffed", /obj/screen/alert/restrained/handcuffed, new_master = src.handcuffed) - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "handcuffed", /datum/mood_event/handcuffed) + if(handcuffed.demoralize_criminals) + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "handcuffed", /datum/mood_event/handcuffed) else clear_alert("handcuffed") SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "handcuffed") @@ -814,7 +819,7 @@ reagents.clear_reagents() var/obj/item/organ/brain/B = getorgan(/obj/item/organ/brain) if(B) - B.damaged_brain = FALSE + B.brain_death = FALSE for(var/thing in diseases) var/datum/disease/D = thing if(D.severity != DISEASE_SEVERITY_POSITIVE) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 57483f64e7..bc64c8303b 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -405,16 +405,16 @@ if(istype(ears) && (deafen_pwr || damage_pwr)) var/ear_damage = damage_pwr * effect_amount - var/deaf = max(ears.deaf, deafen_pwr * effect_amount) + var/deaf = deafen_pwr * effect_amount adjustEarDamage(ear_damage,deaf) - if(ears.ear_damage >= 15) + if(ears.damage >= 15) to_chat(src, "Your ears start to ring badly!") - if(prob(ears.ear_damage - 5)) + if(prob(ears.damage - 5)) to_chat(src, "You can't hear anything!") - ears.ear_damage = min(ears.ear_damage, UNHEALING_EAR_DAMAGE) + ears.damage = min(ears.damage, ears.maxHealth) // you need earmuffs, inacusiate, or replacement - else if(ears.ear_damage >= 5) + else if(ears.damage >= 5) to_chat(src, "Your ears start to ring!") SEND_SOUND(src, sound('sound/weapons/flash_ring.ogg',0,1,0,250)) return effect_amount //how soundbanged we are diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index 794194b3a1..41daf642f2 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -2,7 +2,7 @@ gender = MALE pressure_resistance = 15 possible_a_intents = list(INTENT_HELP, INTENT_HARM) - hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD,GLAND_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD) + hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD,GLAND_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD,RAD_HUD) has_limbs = 1 held_items = list(null, null) var/list/stomach_contents = list() diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index 749ae3b5b0..cc0c0d7434 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -40,14 +40,13 @@ update_damage_overlays() else adjustStaminaLoss(damage * hit_percent) - if(BRAIN) - adjustBrainLoss(damage * hit_percent) //citadel code if(AROUSAL) adjustArousalLoss(damage * hit_percent) return TRUE + //These procs fetch a cumulative total damage from all bodyparts /mob/living/carbon/getBruteLoss() var/amount = 0 @@ -113,6 +112,51 @@ return adjustStaminaLoss(diff, updating, forced) +/** adjustOrganLoss + * inputs: slot (organ slot, like ORGAN_SLOT_HEART), amount (damage to be done), and maximum (currently an arbitrarily large number, can be set so as to limit damage) + * outputs: + * description: If an organ exists in the slot requested, and we are capable of taking damage (we don't have GODMODE on), call the damage proc on that organ. + */ +/mob/living/carbon/adjustOrganLoss(slot, amount, maximum) + var/obj/item/organ/O = getorganslot(slot) + if(O && !(status_flags & GODMODE)) + if(!maximum) + maximum = O.maxHealth + O.applyOrganDamage(amount, maximum) + O.onDamage(amount, maximum) + +/** setOrganLoss + * inputs: slot (organ slot, like ORGAN_SLOT_HEART), amount(damage to be set to) + * outputs: + * description: If an organ exists in the slot requested, and we are capable of taking damage (we don't have GODMODE on), call the set damage proc on that organ, which can + * set or clear the failing variable on that organ, making it either cease or start functions again, unlike adjustOrganLoss. + */ +/mob/living/carbon/setOrganLoss(slot, amount) + var/obj/item/organ/O = getorganslot(slot) + if(O && !(status_flags & GODMODE)) + O.setOrganDamage(amount) + O.onSetDamage(amount) + +/** getOrganLoss + * inputs: slot (organ slot, like ORGAN_SLOT_HEART) + * outputs: organ damage + * description: If an organ exists in the slot requested, return the amount of damage that organ has + */ +/mob/living/carbon/getOrganLoss(slot) + var/obj/item/organ/O = getorganslot(slot) + if(O) + return O.damage + +/mob/living/carbon/proc/adjustAllOrganLoss(amount, maximum) + for(var/obj/item/organ/O in internal_organs) + if(O && !(status_flags & GODMODE)) + continue + if(!maximum) + maximum = O.maxHealth + O.applyOrganDamage(amount, maximum) + O.onDamage(amount, maximum) + + //////////////////////////////////////////// //Returns a list of damaged bodyparts @@ -213,24 +257,25 @@ update_damage_overlays() update_stamina() -/mob/living/carbon/getBrainLoss() +/* TO_REMOVE +/mob/living/carbon/getOrganLoss(ORGAN_SLOT_BRAIN) . = 0 var/obj/item/organ/brain/B = getorganslot(ORGAN_SLOT_BRAIN) if(B) . = B.get_brain_damage() //Some sources of brain damage shouldn't be deadly -/mob/living/carbon/adjustBrainLoss(amount, maximum = BRAIN_DAMAGE_DEATH) +/mob/living/carbon/adjustOrganLoss(ORGAN_SLOT_BRAIN, amount, maximum = BRAIN_DAMAGE_DEATH) if(status_flags & GODMODE) return FALSE - var/prev_brainloss = getBrainLoss() + var/prev_brainloss = getOrganLoss(ORGAN_SLOT_BRAIN) var/obj/item/organ/brain/B = getorganslot(ORGAN_SLOT_BRAIN) if(!B) return B.adjust_brain_damage(amount, maximum) if(amount <= 0) //cut this early return - var/brainloss = getBrainLoss() + var/brainloss = getOrganLoss(ORGAN_SLOT_BRAIN) if(brainloss > BRAIN_DAMAGE_MILD) if(prob(amount * ((2 * (100 + brainloss - BRAIN_DAMAGE_MILD)) / 100))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 2% gain_trauma_type(BRAIN_TRAUMA_MILD) @@ -253,3 +298,4 @@ if(B) var/adjusted_amount = amount - B.get_brain_damage() B.adjust_brain_damage(adjusted_amount, null) +*/ diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 528b55c921..85dfe66725 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -802,6 +802,11 @@ else hud_used.healthdoll.icon_state = "healthdoll_DEAD" + if(hud_used.staminas) + hud_used.staminas.icon_state = staminahudamount() + if(hud_used.staminabuffer) + hud_used.staminabuffer.icon_state = staminabufferhudamount() + /mob/living/carbon/human/fully_heal(admin_revive = 0) if(admin_revive) regenerate_limbs() @@ -854,52 +859,84 @@ .["Copy outfit"] = "?_src_=vars;[HrefToken()];copyoutfit=[REF(src)]" /mob/living/carbon/human/MouseDrop_T(mob/living/target, mob/living/user) - //If they dragged themselves and we're currently aggressively grabbing them try to piggyback - if(user == target && can_piggyback(target) && pulling == target && (HAS_TRAIT(src, TRAIT_PACIFISM) || grab_state >= GRAB_AGGRESSIVE) && stat == CONSCIOUS) - buckle_mob(target,TRUE,TRUE) + if(pulling == target && grab_state >= GRAB_AGGRESSIVE && stat == CONSCIOUS) + //If they dragged themselves and we're currently aggressively grabbing them try to piggyback + if(user == target && can_piggyback(target)) + piggyback(target) + return + //If you dragged them to you and you're aggressively grabbing try to fireman carry them + else if(user != target && can_be_firemanned(target)) + fireman_carry(target) + return . = ..() -/mob/living/carbon/human/proc/piggyback_instant(mob/living/M) - return buckle_mob(M, TRUE, TRUE, FALSE, TRUE) +//src is the user that will be carrying, target is the mob to be carried +/mob/living/carbon/human/proc/can_piggyback(mob/living/carbon/target) + return (istype(target) && target.stat == CONSCIOUS) -//Can C try to piggyback at all. -/mob/living/carbon/human/proc/can_piggyback(mob/living/carbon/C) - if(istype(C) && C.stat == CONSCIOUS) - return TRUE - return FALSE +/mob/living/carbon/human/proc/can_be_firemanned(mob/living/carbon/target) + return (ishuman(target) && target.lying) -/mob/living/carbon/human/buckle_mob(mob/living/M, force = FALSE, check_loc = TRUE, bypass_piggybacking = FALSE, no_delay = FALSE) +/mob/living/carbon/human/proc/fireman_carry(mob/living/carbon/target) + if(can_be_firemanned(target)) + visible_message("[src] starts lifting [target] onto their back...", + "You start lifting [target] onto your back...") + if(do_after(src, 30, TRUE, target)) + //Second check to make sure they're still valid to be carried + if(can_be_firemanned(target) && !incapacitated(FALSE, TRUE)) + target.resting = FALSE + buckle_mob(target, TRUE, TRUE, 90, 1, 0) + return + visible_message("[src] fails to fireman carry [target]!") + else + to_chat(src, "You can't fireman carry [target] while they're standing!") + +/mob/living/carbon/human/proc/piggyback(mob/living/carbon/target) + if(can_piggyback(target)) + visible_message("[target] starts to climb onto [src]...") + if(do_after(target, 15, target = src)) + if(can_piggyback(target)) + if(target.incapacitated(FALSE, TRUE) || incapacitated(FALSE, TRUE)) + target.visible_message("[target] can't hang onto [src]!") + return + buckle_mob(target, TRUE, TRUE, FALSE, 0, 2) + else + visible_message("[target] fails to climb onto [src]!") + else + to_chat(target, "You can't piggyback ride [src] right now!") + +/mob/living/carbon/human/buckle_mob(mob/living/target, force = FALSE, check_loc = TRUE, lying_buckle = FALSE, hands_needed = 0, target_hands_needed = 0) if(!force)//humans are only meant to be ridden through piggybacking and special cases return - if(bypass_piggybacking) - return ..() - if(!is_type_in_typecache(M, can_ride_typecache)) - M.visible_message("[M] really can't seem to mount [src]...") + if(!is_type_in_typecache(target, can_ride_typecache)) + target.visible_message("[target] really can't seem to mount [src]...") return + buckle_lying = lying_buckle var/datum/component/riding/human/riding_datum = LoadComponent(/datum/component/riding/human) - riding_datum.ride_check_rider_incapacitated = TRUE - riding_datum.ride_check_rider_restrained = TRUE - riding_datum.set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 6), TEXT_SOUTH = list(0, 6), TEXT_EAST = list(-6, 4), TEXT_WEST = list( 6, 4))) - if(buckled_mobs && ((M in buckled_mobs) || (buckled_mobs.len >= max_buckled_mobs)) || buckled || (M.stat != CONSCIOUS)) + if(target_hands_needed) + riding_datum.ride_check_rider_restrained = TRUE + if(buckled_mobs && ((target in buckled_mobs) || (buckled_mobs.len >= max_buckled_mobs)) || buckled) return - if(can_piggyback(M)) - riding_datum.ride_check_ridden_incapacitated = TRUE - visible_message("[M] starts to climb onto [src]...") - if(no_delay || do_after(M, 15, target = src)) - if(can_piggyback(M)) - if(M.incapacitated(FALSE, TRUE) || incapacitated(FALSE, TRUE)) - M.visible_message("[M] can't hang onto [src]!") - return - if(!riding_datum.equip_buckle_inhands(M, 2)) //MAKE SURE THIS IS LAST!! - M.visible_message("[M] can't climb onto [src]!") - return - . = ..(M, force, check_loc) - stop_pulling() - else - visible_message("[M] fails to climb onto [src]!") - else - . = ..(M,force,check_loc) - stop_pulling() + var/equipped_hands_self + var/equipped_hands_target + if(hands_needed) + equipped_hands_self = riding_datum.equip_buckle_inhands(src, hands_needed, target) + if(target_hands_needed) + equipped_hands_target = riding_datum.equip_buckle_inhands(target, target_hands_needed) + + if(hands_needed || target_hands_needed) + if(hands_needed && !equipped_hands_self) + src.visible_message("[src] can't get a grip on [target] because their hands are full!", + "You can't get a grip on [target] because your hands are full!") + return + else if(target_hands_needed && !equipped_hands_target) + target.visible_message("[target] can't get a grip on [src] because their hands are full!", + "You can't get a grip on [src] because your hands are full!") + return + + stop_pulling() + riding_datum.handle_vehicle_layer() + . = ..(target, force, check_loc) /mob/living/carbon/human/proc/is_shove_knockdown_blocked() //If you want to add more things that block shove knockdown, extend this for(var/obj/item/clothing/C in get_equipped_items()) //doesn't include pockets @@ -1029,8 +1066,8 @@ /mob/living/carbon/human/species/lizard/ashwalker race = /datum/species/lizard/ashwalker -/mob/living/carbon/human/species/moth - race = /datum/species/moth +/mob/living/carbon/human/species/insect + race = /datum/species/insect /mob/living/carbon/human/species/mush race = /datum/species/mush diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index ff4878aa13..a44779e05e 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -348,10 +348,15 @@ if(temp) var/update = 0 var/dmg = rand(M.force/2, M.force) + var/atom/throw_target = get_edge_target_turf(src, M.dir) switch(M.damtype) if("brute") - if(M.force > 20) - Unconscious(20) + if(M.force > 35) // durand and other heavy mechas + Knockdown(50) + src.throw_at(throw_target, rand(1,5), 7) + else if(M.force >= 20 && !IsKnockdown()) // lightweight mechas like gygax + Knockdown(30) + src.throw_at(throw_target, rand(1,3), 7) update |= temp.receive_damage(dmg, 0) playsound(src, 'sound/weapons/punch4.ogg', 50, 1) if("fire") @@ -746,9 +751,54 @@ if(0 to NUTRITION_LEVEL_STARVING) to_send += "You're starving!\n" + + //TODO: Convert these messages into vague messages, thereby encouraging actual dignosis. + //Compiles then shows the list of damaged organs and broken organs + var/list/broken = list() + var/list/damaged = list() + var/broken_message + var/damaged_message + var/broken_plural + var/damaged_plural + //Sets organs into their proper list + for(var/O in internal_organs) + var/obj/item/organ/organ = O + if(organ.organ_flags & ORGAN_FAILING) + if(broken.len) + broken += ", " + broken += organ.name + else if(organ.damage > organ.low_threshold) + if(damaged.len) + damaged += ", " + damaged += organ.name + //Checks to enforce proper grammar, inserts words as necessary into the list + if(broken.len) + if(broken.len > 1) + broken.Insert(broken.len, "and ") + broken_plural = TRUE + else + var/holder = broken[1] //our one and only element + if(holder[lentext(holder)] == "s") + broken_plural = TRUE + //Put the items in that list into a string of text + for(var/B in broken) + broken_message += B + to_chat(src, " Your [broken_message] [broken_plural ? "are" : "is"] non-functional!") + if(damaged.len) + if(damaged.len > 1) + damaged.Insert(damaged.len, "and ") + damaged_plural = TRUE + else + var/holder = damaged[1] + if(holder[lentext(holder)] == "s") + damaged_plural = TRUE + for(var/D in damaged) + damaged_message += D + to_chat(src, "Your [damaged_message] [damaged_plural ? "are" : "is"] hurt.") + if(roundstart_quirks.len) to_send += "You have these quirks: [get_trait_string()].\n" - + to_chat(src, to_send) else if(wear_suit) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 35b0384145..e32d073500 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -1,5 +1,5 @@ /mob/living/carbon/human - hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD, NANITE_HUD, DIAG_NANITE_FULL_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD) + hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD, NANITE_HUD, DIAG_NANITE_FULL_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD,RAD_HUD) hud_type = /datum/hud/human possible_a_intents = list(INTENT_HELP, INTENT_DISARM, INTENT_GRAB, INTENT_HARM) pressure_resistance = 25 @@ -17,6 +17,8 @@ //Eye colour var/eye_color = "000" + var/horn_color = "85615a" //specific horn colors, because why not? + var/skin_tone = "caucasian1" //Skin tone var/lip_style = null //no lipstick by default- arguably misleading, as it could be used for general makeup diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 1af9dbc5f5..9395283aeb 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -39,6 +39,10 @@ //Stuff jammed in your limbs hurts handle_embedded_objects() + if(stat != DEAD) + //process your dick energy + handle_arousal() + //Update our name based on whether our face is obscured/disfigured name = get_visible_name() @@ -54,7 +58,7 @@ var/obj/item/clothing/CH = head if (CS.clothing_flags & CH.clothing_flags & STOPSPRESSUREDAMAGE) return ONE_ATMOSPHERE - if(istype(loc, /obj/belly)) //START OF CIT CHANGES - Makes it so you don't suffocate while inside vore organs. Remind me to modularize this some time - Bhijn + if(isbelly(loc)) //START OF CIT CHANGES - Makes it so you don't suffocate while inside vore organs. Remind me to modularize this some time - Bhijn return ONE_ATMOSPHERE if(istype(loc, /obj/item/dogborg/sleeper)) return ONE_ATMOSPHERE //END OF CIT CHANGES @@ -70,7 +74,7 @@ else if(eye_blurry) //blurry eyes heal slowly adjust_blurriness(-1) - if (getBrainLoss() >= 30) //Citadel change to make memes more often. + if (getOrganLoss(ORGAN_SLOT_BRAIN) >= 30) //Citadel change to make memes more often. SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "brain_damage", /datum/mood_event/brain_damage) if(prob(3)) if(prob(25)) diff --git a/code/modules/mob/living/carbon/human/login.dm b/code/modules/mob/living/carbon/human/login.dm deleted file mode 100644 index 1ac24cffa9..0000000000 --- a/code/modules/mob/living/carbon/human/login.dm +++ /dev/null @@ -1,9 +0,0 @@ -/mob/living/carbon/human/Login() - ..() - if(src.martial_art == default_martial_art && mind.stored_martial_art) //If the mind has a martial art stored and the body has the default one. - src.mind.stored_martial_art.teach(src) //Running teach so that it deals with help verbs. - else if(src.martial_art != default_martial_art && src.martial_art != mind.stored_martial_art) //If the body has a martial art which is not the default one and is not stored in the mind. - if(src.martial_art_owner != mind) - src.martial_art.remove(src) - else - src.mind.stored_martial_art = src.martial_art diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 0f83e675fc..b6346bead8 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1,6 +1,7 @@ // This code handles different species in the game. GLOBAL_LIST_EMPTY(roundstart_races) +GLOBAL_LIST_EMPTY(roundstart_race_names) /datum/species var/id // if the game needs to manually check your race to do something not included in a proc here, it will use this @@ -15,6 +16,8 @@ GLOBAL_LIST_EMPTY(roundstart_races) var/hair_color // this allows races to have specific hair colors... if null, it uses the H's hair/facial hair colors. if "mutcolor", it uses the H's mutant_color var/hair_alpha = 255 // the alpha used by the hair. 255 is completely solid, 0 is transparent. + var/horn_color //specific horn colors, because why not? + var/use_skintones = 0 // does it use skintones or not? (spoiler alert this is only used by humans) var/exotic_blood = "" // If your race wants to bleed something other than bog standard blood, change this to reagent id. var/exotic_bloodtype = "" //If your race uses a non standard bloodtype (A+, O-, AB-, etc) @@ -79,7 +82,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) var/fixed_mut_color3 = "" var/whitelisted = 0 //Is this species restricted to certain players? var/whitelist = list() //List the ckeys that can use this species, if it's whitelisted.: list("John Doe", "poopface666", "SeeALiggerPullTheTrigger") Spaces & capitalization can be included or ignored entirely for each key as it checks for both. - + var/should_draw_citadel = FALSE /////////// // PROCS // @@ -98,6 +101,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) var/datum/species/S = new I if(S.check_roundstart_eligible()) GLOB.roundstart_races += S.id + GLOB.roundstart_race_names["[S.name]"] = S.id qdel(S) if(!GLOB.roundstart_races.len) GLOB.roundstart_races += "human" @@ -129,10 +133,10 @@ GLOBAL_LIST_EMPTY(roundstart_races) return //Please override this locally if you want to define when what species qualifies for what rank if human authority is enforced. -/datum/species/proc/qualifies_for_rank(rank, list/features) - if(rank in GLOB.command_positions) - return 0 - return 1 +/datum/species/proc/qualifies_for_rank(rank, list/features) //SPECIES JOB RESTRICTIONS + //if(rank in GLOB.command_positions) Left as an example: The format qualifies for rank takes. + // return 0 //It returns false when it runs the proc so they don't get jobs from the global list. + return 1 //It returns 1 to say they are a-okay to continue. //Will regenerate missing organs /datum/species/proc/regenerate_organs(mob/living/carbon/C,datum/species/old_species,replace_current=TRUE) @@ -260,7 +264,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) C.hud_used.update_locked_slots() // this needs to be FIRST because qdel calls update_body which checks if we have DIGITIGRADE legs or not and if not then removes DIGITIGRADE from species_traits - if(("legs" in C.dna.species.mutant_bodyparts) && C.dna.features["legs"] == "Digitigrade Legs") + if(("legs" in C.dna.species.mutant_bodyparts) && (C.dna.features["legs"] == "Digitigrade" || C.dna.features["legs"] == "Avian")) species_traits += DIGITIGRADE if(DIGITIGRADE in species_traits) C.Digitigrade_Leg_Swap(FALSE) @@ -294,8 +298,6 @@ GLOBAL_LIST_EMPTY(roundstart_races) for(var/datum/disease/A in C.diseases) A.cure(FALSE) - SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species) - //CITADEL EDIT if(NOAROUSAL in species_traits) C.canbearoused = FALSE @@ -306,6 +308,11 @@ GLOBAL_LIST_EMPTY(roundstart_races) var/mob/living/carbon/human/H = C if(NOGENITALS in H.dna.species.species_traits) H.give_genitals(TRUE) //call the clean up proc to delete anything on the mob then return. + if("meat_type" in default_features) //I can't believe it's come to the meat + H.type_of_meat = GLOB.meat_types[H.dna.features["meat_type"]] + + SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species) + // EDIT ENDS @@ -317,6 +324,11 @@ GLOBAL_LIST_EMPTY(roundstart_races) for(var/X in inherent_traits) REMOVE_TRAIT(C, X, SPECIES_TRAIT) + if("meat_type" in default_features) + C.type_of_meat = GLOB.meat_types[C.dna.features["meat_type"]] + else + C.type_of_meat = initial(meat) + SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src) /datum/species/proc/handle_hair(mob/living/carbon/human/H, forced_colour) @@ -612,6 +624,10 @@ GLOBAL_LIST_EMPTY(roundstart_races) else if ("wings" in mutant_bodyparts) bodyparts_to_add -= "wings_open" + if("insect_fluff" in mutant_bodyparts) + if(!H.dna.features["insect_fluff"] || H.dna.features["insect_fluff"] == "None" || H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT)) + bodyparts_to_add -= "insect_fluff" + //CITADEL EDIT //Race specific bodyparts: //Xenos @@ -717,8 +733,10 @@ GLOBAL_LIST_EMPTY(roundstart_races) S = GLOB.wings_open_list[H.dna.features["wings"]] if("legs") S = GLOB.legs_list[H.dna.features["legs"]] - if("moth_wings") - S = GLOB.moth_wings_list[H.dna.features["moth_wings"]] + if("insect_wings") + S = GLOB.insect_wings_list[H.dna.features["insect_wings"]] + if("insect_fluff") + S = GLOB.insect_fluffs_list[H.dna.features["insect_fluff"]] if("caps") S = GLOB.caps_list[H.dna.features["caps"]] if("ipc_screen") @@ -815,6 +833,8 @@ GLOBAL_LIST_EMPTY(roundstart_races) accessory_overlay.color = "#[H.facial_hair_color]" if(EYECOLOR) accessory_overlay.color = "#[H.eye_color]" + if(HORNCOLOR) + accessory_overlay.color = "#[H.horn_color]" else accessory_overlay.color = forced_colour else @@ -880,6 +900,9 @@ GLOBAL_LIST_EMPTY(roundstart_races) extra_accessory_overlay.color = "#[H.facial_hair_color]" if(EYECOLOR) extra_accessory_overlay.color = "#[H.eye_color]" + + if(HORNCOLOR) + extra_accessory_overlay.color = "#[H.horn_color]" standing += extra_accessory_overlay if(S.extra2) //apply the extra overlay, if there is one @@ -912,6 +935,8 @@ GLOBAL_LIST_EMPTY(roundstart_races) extra2_accessory_overlay.color = "#[H.dna.features["mcolor"]]" else extra2_accessory_overlay.color = "#[H.hair_color]" + if(HORNCOLOR) + extra2_accessory_overlay.color = "#[H.horn_color]" standing += extra2_accessory_overlay @@ -1196,9 +1221,14 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(mood && mood.sanity > SANITY_DISTURBED) hunger_rate *= max(0.5, 1 - 0.002 * mood.sanity) //0.85 to 0.75 - if(H.satiety > 0) + // Whether we cap off our satiety or move it towards 0 + if(H.satiety > MAX_SATIETY) + H.satiety = MAX_SATIETY + else if(H.satiety > 0) H.satiety-- - if(H.satiety < 0) + else if(H.satiety < -MAX_SATIETY) + H.satiety = -MAX_SATIETY + else if(H.satiety < 0) H.satiety++ if(prob(round(-H.satiety/40))) H.Jitter(5) @@ -1313,10 +1343,10 @@ GLOBAL_LIST_EMPTY(roundstart_races) var/obj/item/organ/cyberimp/chest/thrusters/T = H.getorganslot(ORGAN_SLOT_THRUSTERS) if(!istype(J) && istype(C)) J = C.jetpack - if(istype(J) && J.full_speed && J.allow_thrust(0.01, H)) //Prevents stacking - . -= 2 - else if(istype(T) && T.allow_thrust(0.01, H)) - . -= 2 + if(istype(J) && J.full_speed && J.allow_thrust(0.005, H)) //Prevents stacking + . -= 0.4 + else if(istype(T) && T.allow_thrust(0.005, H)) + . -= 0.4 if(!ignoreslow && gravity) if(H.wear_suit) @@ -1683,7 +1713,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(BODY_ZONE_HEAD) if(!I.is_sharp() && armor_block < 50) if(prob(I.force)) - H.adjustBrainLoss(20) + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 20) if(H.stat == CONSCIOUS) H.visible_message("[H] has been knocked senseless!", \ "[H] has been knocked senseless!") @@ -1692,7 +1722,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(prob(10)) H.gain_trauma(/datum/brain_trauma/mild/concussion) else - H.adjustBrainLoss(I.force * 0.2) + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, I.force * 0.2) if(H.stat == CONSCIOUS && H != user && prob(I.force + ((100 - H.health) * 0.5))) // rev deconversion through blunt trauma. var/datum/antagonist/rev/rev = H.mind.has_antag_datum(/datum/antagonist/rev) @@ -1732,6 +1762,130 @@ GLOBAL_LIST_EMPTY(roundstart_races) H.forcesay(GLOB.hit_appends) //forcesay checks stat already. return TRUE +/datum/species/proc/alt_spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style) + if(!istype(M)) + return TRUE + CHECK_DNA_AND_SPECIES(M) + CHECK_DNA_AND_SPECIES(H) + + if(!istype(M)) //sanity check for drones. + return TRUE + if(M.mind) + attacker_style = M.mind.martial_art + if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(M, 0, M.name, attack_type = UNARMED_ATTACK)) + log_combat(M, H, "attempted to touch") + H.visible_message("[M] attempted to touch [H]!") + return TRUE + switch(M.a_intent) + if(INTENT_HELP) + if(M == H) + althelp(M, H, attacker_style) + return TRUE + return FALSE + if(INTENT_DISARM) + altdisarm(M, H, attacker_style) + return TRUE + return FALSE + +/datum/species/proc/althelp(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style) + if(user == target && istype(user)) + if(user.getStaminaLoss() >= STAMINA_SOFTCRIT) + to_chat(user, "You're too exhausted for that.") + return + if(!user.resting) + to_chat(user, "You can only force yourself up if you're on the ground.") + return + user.visible_message("[user] forces [p_them()]self up to [p_their()] feet!", "You force yourself up to your feet!") + user.resting = 0 + user.update_canmove() + user.adjustStaminaLossBuffered(user.stambuffer) //Rewards good stamina management by making it easier to instantly get up from resting + playsound(user, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + +/datum/species/proc/altdisarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style) + if(user.getStaminaLoss() >= STAMINA_SOFTCRIT) + to_chat(user, "You're too exhausted.") + return FALSE + if(target.check_block()) + target.visible_message("[target] blocks [user]'s shoving attempt!") + return FALSE + if(attacker_style && attacker_style.disarm_act(user,target)) + return TRUE + if(user.resting) + return FALSE + else + if(user == target) + return + user.do_attack_animation(target, ATTACK_EFFECT_DISARM) + user.adjustStaminaLossBuffered(4) + playsound(target, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1) + + if(target.w_uniform) + target.w_uniform.add_fingerprint(user) + SEND_SIGNAL(target, COMSIG_HUMAN_DISARM_HIT, user, user.zone_selected) + + if(!target.resting) + target.adjustStaminaLoss(5) + + if(target.is_shove_knockdown_blocked()) + return + + var/turf/target_oldturf = target.loc + var/shove_dir = get_dir(user.loc, target_oldturf) + var/turf/target_shove_turf = get_step(target.loc, shove_dir) + var/mob/living/carbon/human/target_collateral_human + var/shove_blocked = FALSE //Used to check if a shove is blocked so that if it is knockdown logic can be applied + + //Thank you based whoneedsspace + target_collateral_human = locate(/mob/living/carbon/human) in target_shove_turf.contents + if(target_collateral_human && !target_collateral_human.resting) + shove_blocked = TRUE + else + target_collateral_human = null + target.Move(target_shove_turf, shove_dir) + if(get_turf(target) == target_oldturf) + shove_blocked = TRUE + + if(shove_blocked && !target.buckled) + var/directional_blocked = !target.Adjacent(target_shove_turf) + var/targetatrest = target.resting + if((directional_blocked || !(target_collateral_human || target_shove_turf.shove_act(target, user))) && !targetatrest) + target.Knockdown(SHOVE_KNOCKDOWN_SOLID) + user.visible_message("[user.name] shoves [target.name], knocking them down!", + "You shove [target.name], knocking them down!", null, COMBAT_MESSAGE_RANGE) + log_combat(user, target, "shoved", "knocking them down") + else if(target_collateral_human && !targetatrest) + target.Knockdown(SHOVE_KNOCKDOWN_HUMAN) + target_collateral_human.Knockdown(SHOVE_KNOCKDOWN_COLLATERAL) + user.visible_message("[user.name] shoves [target.name] into [target_collateral_human.name]!", + "You shove [target.name] into [target_collateral_human.name]!", null, COMBAT_MESSAGE_RANGE) + log_combat(user, target, "shoved", "into [target_collateral_human.name]") + + else + user.visible_message("[user.name] shoves [target.name]!", + "You shove [target.name]!", null, COMBAT_MESSAGE_RANGE) + var/target_held_item = target.get_active_held_item() + var/knocked_item = FALSE + if(!is_type_in_typecache(target_held_item, GLOB.shove_disarming_types)) + target_held_item = null + if(!target.has_movespeed_modifier(SHOVE_SLOWDOWN_ID)) + target.add_movespeed_modifier(SHOVE_SLOWDOWN_ID, multiplicative_slowdown = SHOVE_SLOWDOWN_STRENGTH) + if(target_held_item) + target.visible_message("[target.name]'s grip on \the [target_held_item] loosens!", + "Your grip on \the [target_held_item] loosens!", null, COMBAT_MESSAGE_RANGE) + addtimer(CALLBACK(target, /mob/living/carbon/human/proc/clear_shove_slowdown), SHOVE_SLOWDOWN_LENGTH) + else if(target_held_item) + target.dropItemToGround(target_held_item) + knocked_item = TRUE + target.visible_message("[target.name] drops \the [target_held_item]!!", + "You drop \the [target_held_item]!!", null, COMBAT_MESSAGE_RANGE) + var/append_message = "" + if(target_held_item) + if(knocked_item) + append_message = "causing them to drop [target_held_item]" + else + append_message = "loosening their grip on [target_held_item]" + log_combat(user, target, "shoved", append_message) + /datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H) var/hit_percent = (100-(blocked+armor))/100 hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100 @@ -1787,7 +1941,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) else H.adjustStaminaLoss(damage * hit_percent * H.physiology.stamina_mod) if(BRAIN) - H.adjustBrainLoss(damage * hit_percent * H.physiology.brain_mod) + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, damage * hit_percent * H.physiology.brain_mod) if(AROUSAL) //Citadel edit - arousal H.adjustArousalLoss(damage * hit_percent) return 1 diff --git a/code/modules/mob/living/carbon/human/species_types/bugmen.dm b/code/modules/mob/living/carbon/human/species_types/bugmen.dm new file mode 100644 index 0000000000..94dba550b6 --- /dev/null +++ b/code/modules/mob/living/carbon/human/species_types/bugmen.dm @@ -0,0 +1,64 @@ +/datum/species/insect + name = "Anthromorphic Insect" + id = "insect" + say_mod = "flutters" + default_color = "00FF00" + species_traits = list(LIPS,NOEYES,HAIR,FACEHAIR,MUTCOLORS,HORNCOLOR) + inherent_biotypes = list(MOB_ORGANIC, MOB_HUMANOID, MOB_BUG) + mutant_bodyparts = list("mam_ears", "mam_snout", "mam_tail", "taur", "insect_wings", "mam_snouts", "insect_fluff","horns") + default_features = list("mcolor" = "FFF","mcolor2" = "FFF","mcolor3" = "FFF", "mam_tail" = "None", "mam_ears" = "None", + "insect_wings" = "None", "insect_fluff" = "None", "mam_snouts" = "None", "taur" = "None","horns" = "None") + attack_verb = "slash" + attack_sound = 'sound/weapons/slash.ogg' + miss_sound = 'sound/weapons/slashmiss.ogg' + meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/insect + liked_food = VEGETABLES | DAIRY + disliked_food = FRUIT | GROSS + toxic_food = MEAT | RAW + mutanteyes = /obj/item/organ/eyes/insect + should_draw_citadel = TRUE + +/datum/species/insect/on_species_gain(mob/living/carbon/C) + . = ..() + if(ishuman(C)) + var/mob/living/carbon/human/H = C + if(!H.dna.features["insect_wings"]) + H.dna.features["insect_wings"] = "[(H.client && H.client.prefs && LAZYLEN(H.client.prefs.features) && H.client.prefs.features["insect_wings"]) ? H.client.prefs.features["insect_wings"] : "None"]" + handle_mutant_bodyparts(H) + +/datum/species/insect/random_name(gender,unique,lastname) + if(unique) + return random_unique_moth_name() + + var/randname = moth_name() + + if(lastname) + randname += " [lastname]" + + return randname + +/datum/species/insect/handle_fire(mob/living/carbon/human/H, no_protection = FALSE) + ..() + if(H.dna.features["insect_wings"] != "Burnt Off" && H.dna.features["insect_wings"] != "None" && H.bodytemperature >= 800 && H.fire_stacks > 0) //do not go into the extremely hot light. you will not survive + to_chat(H, "Your precious wings burn to a crisp!") + if(H.dna.features["insect_wings"] != "None") + H.dna.features["insect_wings"] = "Burnt Off" + handle_mutant_bodyparts(H) + +/datum/species/insect/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) + . = ..() + if(chem.id == "pestkiller") + H.adjustToxLoss(3) + H.reagents.remove_reagent(chem.id, REAGENTS_METABOLISM) + +/datum/species/insect/check_weakness(obj/item/weapon, mob/living/attacker) + if(istype(weapon, /obj/item/melee/flyswatter)) + return 9 //flyswatters deal 10x damage to insects + return 0 + +/datum/species/insect/space_move(mob/living/carbon/human/H) + . = ..() + if(H.loc && !isspaceturf(H.loc) && (H.dna.features["insect_wings"] != "Burnt Off" && H.dna.features["insect_wings"] != "None")) + var/datum/gas_mixture/current = H.loc.return_air() + if(current && (current.return_pressure() >= ONE_ATMOSPHERE*0.85)) //as long as there's reasonable pressure and no gravity, flight is possible + return TRUE diff --git a/code/modules/mob/living/carbon/human/species_types/corporate.dm b/code/modules/mob/living/carbon/human/species_types/corporate.dm index 620f0b2543..146090b366 100644 --- a/code/modules/mob/living/carbon/human/species_types/corporate.dm +++ b/code/modules/mob/living/carbon/human/species_types/corporate.dm @@ -16,5 +16,5 @@ blacklisted = 1 use_skintones = 0 species_traits = list(NOBLOOD,EYECOLOR,NOGENITALS) - inherent_traits = list(TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOHUNGER) + inherent_traits = list(TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOLIMBDISABLE,TRAIT_NOHUNGER) sexes = 0 \ No newline at end of file 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 4a7580e978..b7d5130cd8 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -64,7 +64,7 @@ /obj/item/organ/brain/dullahan decoy_override = TRUE - vital = FALSE + organ_flags = 0 /obj/item/organ/tongue/dullahan zone = "abstract" @@ -138,4 +138,4 @@ D.myhead = null owner.gib() owner = null - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/carbon/human/species_types/flypeople.dm b/code/modules/mob/living/carbon/human/species_types/flypeople.dm index 6f05eb393d..043ee4fde1 100644 --- a/code/modules/mob/living/carbon/human/species_types/flypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/flypeople.dm @@ -1,5 +1,5 @@ /datum/species/fly - name = "Flyperson" + name = "Anthromorphic Fly" id = "fly" say_mod = "buzzes" species_traits = list(NOEYES) diff --git a/code/modules/mob/living/carbon/human/species_types/furrypeople.dm b/code/modules/mob/living/carbon/human/species_types/furrypeople.dm new file mode 100644 index 0000000000..e726d45347 --- /dev/null +++ b/code/modules/mob/living/carbon/human/species_types/furrypeople.dm @@ -0,0 +1,98 @@ +/datum/species/mammal + name = "Anthromorph" + id = "mammal" + default_color = "4B4B4B" + should_draw_citadel = TRUE + species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAIR,HORNCOLOR) + inherent_biotypes = list(MOB_ORGANIC, MOB_HUMANOID) + mutant_bodyparts = list("mam_tail", "mam_ears", "mam_body_markings", "mam_snouts", "taur", "horns", "legs") + default_features = list("mcolor" = "FFF","mcolor2" = "FFF","mcolor3" = "FFF", "mam_snouts" = "Husky", "mam_tail" = "Husky", "mam_ears" = "Husky", + "mam_body_markings" = "Husky", "taur" = "None", "horns" = "None", "legs" = "Plantigrade", "meat_type" = "Mammalian") + attack_verb = "claw" + attack_sound = 'sound/weapons/slash.ogg' + miss_sound = 'sound/weapons/slashmiss.ogg' + meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/mammal + liked_food = MEAT | FRIED + disliked_food = TOXIC + +//Curiosity killed the cat's wagging tail. +/datum/species/mammal/spec_death(gibbed, mob/living/carbon/human/H) + if(H) + stop_wagging_tail(H) + +/datum/species/mammal/spec_stun(mob/living/carbon/human/H,amount) + if(H) + stop_wagging_tail(H) + . = ..() + +/datum/species/mammal/can_wag_tail(mob/living/carbon/human/H) + return ("mam_tail" in mutant_bodyparts) || ("mam_waggingtail" in mutant_bodyparts) + +/datum/species/mammal/is_wagging_tail(mob/living/carbon/human/H) + return ("mam_waggingtail" in mutant_bodyparts) + +/datum/species/mammal/start_wagging_tail(mob/living/carbon/human/H) + if("mam_tail" in mutant_bodyparts) + mutant_bodyparts -= "mam_tail" + mutant_bodyparts |= "mam_waggingtail" + H.update_body() + +/datum/species/mammal/stop_wagging_tail(mob/living/carbon/human/H) + if("mam_waggingtail" in mutant_bodyparts) + mutant_bodyparts -= "mam_waggingtail" + mutant_bodyparts |= "mam_tail" + H.update_body() + + +/datum/species/mammal/qualifies_for_rank(rank, list/features) + return TRUE + + +//Alien// +/datum/species/xeno + // A cloning mistake, crossing human and xenomorph DNA + name = "Xenomorph Hybrid" + id = "xeno" + say_mod = "hisses" + default_color = "00FF00" + should_draw_citadel = TRUE + species_traits = list(MUTCOLORS,EYECOLOR,LIPS) + inherent_biotypes = list(MOB_ORGANIC, MOB_HUMANOID) + mutant_bodyparts = list("xenotail", "xenohead", "xenodorsal", "mam_body_markings", "taur", "legs") + default_features = 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" + attack_sound = 'sound/weapons/slash.ogg' + miss_sound = 'sound/weapons/slashmiss.ogg' + meat = /obj/item/reagent_containers/food/snacks/meat/slab/xeno + skinned_type = /obj/item/stack/sheet/animalhide/xeno + exotic_bloodtype = "L" + damage_overlay_type = "xeno" + liked_food = MEAT + +/datum/species/xeno/on_species_gain(mob/living/carbon/human/C, datum/species/old_species) + if(("legs" in C.dna.species.mutant_bodyparts) && (C.dna.features["legs"] == "Digitigrade" || C.dna.features["legs"] == "Avian")) + species_traits += DIGITIGRADE + if(DIGITIGRADE in species_traits) + C.Digitigrade_Leg_Swap(FALSE) + . = ..() + +/datum/species/xeno/on_species_loss(mob/living/carbon/human/C, datum/species/new_species) + if(("legs" in C.dna.species.mutant_bodyparts) && C.dna.features["legs"] == "Plantigrade") + species_traits -= DIGITIGRADE + if(DIGITIGRADE in species_traits) + C.Digitigrade_Leg_Swap(TRUE) + . = ..() + +//Praise the Omnissiah, A challange worthy of my skills - HS + +//EXOTIC// +//These races will likely include lots of downsides and upsides. Keep them relatively balanced.// + +//misc +/mob/living/carbon/human/dummy + no_vore = TRUE + +/mob/living/carbon/human/vore + devourable = TRUE + digestable = TRUE + feeding = TRUE diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index 88dd59749c..84c44ea81c 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -45,7 +45,7 @@ return golem_name /datum/species/golem/random - name = "Random Golem" + name = "Golem Mutant" blacklisted = FALSE dangerous_existence = FALSE var/static/list/random_golem_types diff --git a/modular_citadel/code/modules/mob/living/carbon/human/species_types/ipc.dm b/code/modules/mob/living/carbon/human/species_types/ipc.dm similarity index 98% rename from modular_citadel/code/modules/mob/living/carbon/human/species_types/ipc.dm rename to code/modules/mob/living/carbon/human/species_types/ipc.dm index 25b8daf2cb..95b924ea18 100644 --- a/modular_citadel/code/modules/mob/living/carbon/human/species_types/ipc.dm +++ b/code/modules/mob/living/carbon/human/species_types/ipc.dm @@ -1,5 +1,5 @@ /datum/species/ipc - name = "IPC" + name = "I.P.C." id = "ipc" say_mod = "beeps" default_color = "00FF00" diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index 66586744fb..03cd514300 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -118,7 +118,7 @@ //Slime people are able to split like slimes, retaining a single mind that can swap between bodies at will, even after death. /datum/species/jelly/slime - name = "Slimeperson" + name = "Xenobiological Slime Entity" id = "slime" default_color = "00FFFF" species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,NOBLOOD) @@ -389,12 +389,268 @@ "...and move this one instead.") +////////////////////////////////////////////////////////Round Start Slimes/////////////////////////////////////////////////////////////////// + +/datum/species/jelly/roundstartslime + name = "Xenobiological Slime Hybrid" + id = "slimeperson" + limbs_id = "slime" + default_color = "00FFFF" + species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,NOBLOOD) + inherent_traits = list(TRAIT_TOXINLOVER) + mutant_bodyparts = list("mam_tail", "mam_ears", "mam_body_markings", "mam_snouts", "taur") + default_features = list("mcolor" = "FFF", "mcolor2" = "FFF","mcolor3" = "FFF", "mam_tail" = "None", "mam_ears" = "None", "mam_body_markings" = "Plain", "mam_snouts" = "None", "taur" = "None") + say_mod = "says" + hair_color = "mutcolor" + hair_alpha = 160 //a notch brighter so it blends better. + coldmod = 3 + heatmod = 1 + burnmod = 1 + +/datum/species/jelly/roundstartslime/spec_death(gibbed, mob/living/carbon/human/H) + if(H) + stop_wagging_tail(H) + +/datum/species/jelly/roundstartslime/spec_stun(mob/living/carbon/human/H,amount) + if(H) + stop_wagging_tail(H) + . = ..() + +/datum/species/jelly/roundstartslime/can_wag_tail(mob/living/carbon/human/H) + return ("mam_tail" in mutant_bodyparts) || ("mam_waggingtail" in mutant_bodyparts) + +/datum/species/jelly/roundstartslime/is_wagging_tail(mob/living/carbon/human/H) + return ("mam_waggingtail" in mutant_bodyparts) + +/datum/species/jelly/roundstartslime/start_wagging_tail(mob/living/carbon/human/H) + if("mam_tail" in mutant_bodyparts) + mutant_bodyparts -= "mam_tail" + mutant_bodyparts |= "mam_waggingtail" + H.update_body() + +/datum/species/jelly/roundstartslime/stop_wagging_tail(mob/living/carbon/human/H) + if("mam_waggingtail" in mutant_bodyparts) + mutant_bodyparts -= "mam_waggingtail" + mutant_bodyparts |= "mam_tail" + H.update_body() + + +/datum/action/innate/slime_change + name = "Alter Form" + check_flags = AB_CHECK_CONSCIOUS + button_icon_state = "alter_form" //placeholder + icon_icon = 'modular_citadel/icons/mob/actions/actions_slime.dmi' + background_icon_state = "bg_alien" + +/datum/action/innate/slime_change/Activate() + var/mob/living/carbon/human/H = owner + if(!isjellyperson(H)) + return + else + H.visible_message("[owner] gains a look of \ + concentration while standing perfectly still.\ + Their body seems to shift and starts getting more goo-like.", + "You focus intently on altering your body while \ + standing perfectly still...") + change_form() + +/datum/action/innate/slime_change/proc/change_form() + var/mob/living/carbon/human/H = owner + var/select_alteration = input(owner, "Select what part of your form to alter", "Form Alteration", "cancel") in list("Hair Style", "Genitals", "Tail", "Snout", "Markings", "Ears", "Taur body", "Penis", "Vagina", "Penis Length", "Breast Size", "Breast Shape", "Cancel") + if(select_alteration == "Hair Style") + if(H.gender == MALE) + var/new_style = input(owner, "Select a facial hair style", "Hair Alterations") as null|anything in GLOB.facial_hair_styles_list + if(new_style) + H.facial_hair_style = new_style + else + H.facial_hair_style = "Shaved" + //handle normal hair + var/new_style = input(owner, "Select a hair style", "Hair Alterations") as null|anything in GLOB.hair_styles_list + if(new_style) + H.hair_style = new_style + H.update_hair() + else if (select_alteration == "Genitals") + var/list/organs = list() + var/operation = input("Select organ operation.", "Organ Manipulation", "cancel") in list("add sexual organ", "remove sexual organ", "cancel") + switch(operation) + if("add sexual organ") + var/new_organ = input("Select sexual organ:", "Organ Manipulation") in list("Penis", "Testicles", "Breasts", "Vagina", "Womb", "Cancel") + if(new_organ == "Penis") + H.give_penis() + else if(new_organ == "Testicles") + H.give_balls() + else if(new_organ == "Breasts") + H.give_breasts() + else if(new_organ == "Vagina") + H.give_vagina() + else if(new_organ == "Womb") + H.give_womb() + else + return + if("remove sexual organ") + for(var/obj/item/organ/genital/X in H.internal_organs) + var/obj/item/organ/I = X + organs["[I.name] ([I.type])"] = I + var/obj/item/organ = input("Select sexual organ:", "Organ Manipulation", null) in organs + organ = organs[organ] + if(!organ) + return + var/obj/item/organ/genital/O + if(isorgan(organ)) + O = organ + O.Remove(H) + organ.forceMove(get_turf(H)) + qdel(organ) + H.update_genitals() + + else if (select_alteration == "Ears") + var/list/snowflake_ears_list = list("Normal" = null) + for(var/path in GLOB.mam_ears_list) + var/datum/sprite_accessory/mam_ears/instance = GLOB.mam_ears_list[path] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey))) + snowflake_ears_list[S.name] = path + var/new_ears + new_ears = input(owner, "Choose your character's ears:", "Ear Alteration") as null|anything in snowflake_ears_list + if(new_ears) + H.dna.features["mam_ears"] = new_ears + H.update_body() + + else if (select_alteration == "Snout") + var/list/snowflake_snouts_list = list("Normal" = null) + for(var/path in GLOB.mam_snouts_list) + var/datum/sprite_accessory/mam_snouts/instance = GLOB.mam_snouts_list[path] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey))) + snowflake_snouts_list[S.name] = path + var/new_snout + new_snout = input(owner, "Choose your character's face:", "Face Alteration") as null|anything in snowflake_snouts_list + if(new_snout) + H.dna.features["mam_snouts"] = new_snout + H.update_body() + + else if (select_alteration == "Markings") + var/list/snowflake_markings_list = list() + for(var/path in GLOB.mam_body_markings_list) + var/datum/sprite_accessory/mam_body_markings/instance = GLOB.mam_body_markings_list[path] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey))) + snowflake_markings_list[S.name] = path + var/new_mam_body_markings + new_mam_body_markings = input(H, "Choose your character's body markings:", "Marking Alteration") as null|anything in snowflake_markings_list + if(new_mam_body_markings) + H.dna.features["mam_body_markings"] = new_mam_body_markings + if(new_mam_body_markings == "None") + H.dna.features["mam_body_markings"] = "Plain" + for(var/X in H.bodyparts) //propagates the markings changes + var/obj/item/bodypart/BP = X + BP.update_limb(FALSE, H) + H.update_body() + + else if (select_alteration == "Tail") + var/list/snowflake_tails_list = list("Normal" = null) + for(var/path in GLOB.mam_tails_list) + var/datum/sprite_accessory/mam_tails/instance = GLOB.mam_tails_list[path] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey))) + snowflake_tails_list[S.name] = path + var/new_tail + new_tail = input(owner, "Choose your character's Tail(s):", "Tail Alteration") as null|anything in snowflake_tails_list + if(new_tail) + H.dna.features["mam_tail"] = new_tail + if(new_tail != "None") + H.dna.features["taur"] = "None" + H.update_body() + + else if (select_alteration == "Taur body") + var/list/snowflake_taur_list = list("Normal" = null) + for(var/path in GLOB.taur_list) + var/datum/sprite_accessory/taur/instance = GLOB.taur_list[path] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(H.client.ckey))) + snowflake_taur_list[S.name] = path + var/new_taur + new_taur = input(owner, "Choose your character's tauric body:", "Tauric Alteration") as null|anything in snowflake_taur_list + if(new_taur) + H.dna.features["taur"] = new_taur + if(new_taur != "None") + H.dna.features["mam_tail"] = "None" + H.update_body() + + else if (select_alteration == "Penis") + for(var/obj/item/organ/genital/penis/X in H.internal_organs) + qdel(X) + var/new_shape + new_shape = input(owner, "Choose your character's dong", "Genital Alteration") as null|anything in GLOB.cock_shapes_list + if(new_shape) + H.dna.features["cock_shape"] = new_shape + H.update_genitals() + H.give_balls() + H.give_penis() + H.apply_overlay() + + + else if (select_alteration == "Vagina") + for(var/obj/item/organ/genital/vagina/X in H.internal_organs) + qdel(X) + var/new_shape + new_shape = input(owner, "Choose your character's pussy", "Genital Alteration") as null|anything in GLOB.vagina_shapes_list + if(new_shape) + H.dna.features["vag_shape"] = new_shape + H.update_genitals() + H.give_womb() + H.give_vagina() + H.apply_overlay() + + else if (select_alteration == "Penis Length") + for(var/obj/item/organ/genital/penis/X in H.internal_organs) + qdel(X) + var/new_length + new_length = input(owner, "Penis length in inches:\n([COCK_SIZE_MIN]-[COCK_SIZE_MAX])", "Genital Alteration") as num|null + if(new_length) + H.dna.features["cock_length"] = max(min( round(text2num(new_length)), COCK_SIZE_MAX),COCK_SIZE_MIN) + H.update_genitals() + H.apply_overlay() + H.give_balls() + H.give_penis() + + else if (select_alteration == "Breast Size") + for(var/obj/item/organ/genital/breasts/X in H.internal_organs) + qdel(X) + var/new_size + new_size = input(owner, "Breast Size", "Genital Alteration") as null|anything in GLOB.breasts_size_list + if(new_size) + H.dna.features["breasts_size"] = new_size + H.update_genitals() + H.apply_overlay() + H.give_breasts() + + else if (select_alteration == "Breast Shape") + for(var/obj/item/organ/genital/breasts/X in H.internal_organs) + qdel(X) + var/new_shape + new_shape = input(owner, "Breast Shape", "Genital Alteration") as null|anything in GLOB.breasts_shapes_list + if(new_shape) + H.dna.features["breasts_shape"] = new_shape + H.update_genitals() + H.apply_overlay() + H.give_breasts() + + else + return + + ///////////////////////////////////LUMINESCENTS////////////////////////////////////////// //Luminescents are able to consume and use slime extracts, without them decaying. /datum/species/jelly/luminescent - name = "Luminescent" + name = "Luminescent Slime Entity" id = "lum" say_mod = "says" var/glow_intensity = LUMINESCENT_DEFAULT_GLOW @@ -561,7 +817,7 @@ //Stargazers are the telepathic branch of jellypeople, able to project psychic messages and to link minds with willing participants. /datum/species/jelly/stargazer - name = "Stargazer" + name = "Stargazer Slime Entity" id = "stargazer" var/datum/action/innate/project_thought/project_thought var/datum/action/innate/link_minds/link_minds diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm index 30bf705547..4dbfd23df8 100644 --- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm @@ -1,17 +1,19 @@ /datum/species/lizard // Reptilian humanoids with scaled skin and tails. - name = "Lizardperson" + name = "Anthromorphic Lizard" id = "lizard" say_mod = "hisses" default_color = "00FF00" - species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,LIPS) + species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,LIPS,HORNCOLOR) inherent_biotypes = list(MOB_ORGANIC, MOB_HUMANOID, MOB_REPTILE) mutant_bodyparts = list("tail_lizard", "snout", "spines", "horns", "frills", "body_markings", "legs", "taur") mutanttongue = /obj/item/organ/tongue/lizard mutanttail = /obj/item/organ/tail/lizard coldmod = 1.5 heatmod = 0.67 - default_features = list("mcolor" = "0F0", "mcolor2" = "0F0", "mcolor3" = "0F0", "tail_lizard" = "Smooth", "snout" = "Round", "horns" = "None", "frills" = "None", "spines" = "None", "body_markings" = "None", "legs" = "Normal Legs", "taur" = "None") + default_features = list("mcolor" = "0F0", "mcolor2" = "0F0", "mcolor3" = "0F0", "tail_lizard" = "Smooth", "snout" = "Round", + "horns" = "None", "frills" = "None", "spines" = "None", "body_markings" = "None", + "legs" = "Digitigrade", "taur" = "None") attack_verb = "slash" attack_sound = 'sound/weapons/slash.ogg' miss_sound = 'sound/weapons/slashmiss.ogg' @@ -71,14 +73,14 @@ H.update_body() /datum/species/lizard/on_species_gain(mob/living/carbon/human/C, datum/species/old_species) - if(("legs" in C.dna.species.mutant_bodyparts) && C.dna.features["legs"] == "Digitigrade Legs") + if(("legs" in C.dna.species.mutant_bodyparts) && (C.dna.features["legs"] == "Digitigrade" || C.dna.features["legs"] == "Avian")) species_traits += DIGITIGRADE if(DIGITIGRADE in species_traits) C.Digitigrade_Leg_Swap(FALSE) return ..() /datum/species/lizard/on_species_loss(mob/living/carbon/human/C, datum/species/new_species) - if(("legs" in C.dna.species.mutant_bodyparts) && C.dna.features["legs"] == "Normal Legs") + if(("legs" in C.dna.species.mutant_bodyparts) && C.dna.features["legs"] == "Plantigrade") species_traits -= DIGITIGRADE if(DIGITIGRADE in species_traits) C.Digitigrade_Leg_Swap(TRUE) diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm deleted file mode 100644 index d15d989384..0000000000 --- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm +++ /dev/null @@ -1,61 +0,0 @@ -/datum/species/moth - name = "Mothman" - id = "moth" - say_mod = "flutters" - default_color = "00FF00" - species_traits = list(LIPS, NOEYES) - inherent_biotypes = list(MOB_ORGANIC, MOB_HUMANOID, MOB_BUG) - mutant_bodyparts = list("moth_wings") - default_features = list("moth_wings" = "Plain") - attack_verb = "slash" - attack_sound = 'sound/weapons/slash.ogg' - miss_sound = 'sound/weapons/slashmiss.ogg' - meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/moth - liked_food = VEGETABLES | DAIRY - disliked_food = FRUIT | GROSS - toxic_food = MEAT | RAW - mutanteyes = /obj/item/organ/eyes/moth - -/datum/species/moth/on_species_gain(mob/living/carbon/C) - . = ..() - if(ishuman(C)) - var/mob/living/carbon/human/H = C - if(!H.dna.features["moth_wings"]) - H.dna.features["moth_wings"] = "[(H.client && H.client.prefs && LAZYLEN(H.client.prefs.features) && H.client.prefs.features["moth_wings"]) ? H.client.prefs.features["moth_wings"] : "Plain"]" - handle_mutant_bodyparts(H) - -/datum/species/moth/random_name(gender,unique,lastname) - if(unique) - return random_unique_moth_name() - - var/randname = moth_name() - - if(lastname) - randname += " [lastname]" - - return randname - -/datum/species/moth/handle_fire(mob/living/carbon/human/H, no_protection = FALSE) - ..() - if(H.dna.features["moth_wings"] != "Burnt Off" && H.bodytemperature >= 800 && H.fire_stacks > 0) //do not go into the extremely hot light. you will not survive - to_chat(H, "Your precious wings burn to a crisp!") - H.dna.features["moth_wings"] = "Burnt Off" - handle_mutant_bodyparts(H) - -/datum/species/moth/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) - . = ..() - if(chem.id == "pestkiller") - H.adjustToxLoss(3) - H.reagents.remove_reagent(chem.id, REAGENTS_METABOLISM) - -/datum/species/moth/check_weakness(obj/item/weapon, mob/living/attacker) - if(istype(weapon, /obj/item/melee/flyswatter)) - return 9 //flyswatters deal 10x damage to moths - return 0 - -/datum/species/moth/space_move(mob/living/carbon/human/H) - . = ..() - if(H.loc && !isspaceturf(H.loc) && H.dna.features["moth_wings"] != "Burnt Off") - var/datum/gas_mixture/current = H.loc.return_air() - if(current && (current.return_pressure() >= ONE_ATMOSPHERE*0.85)) //as long as there's reasonable pressure and no gravity, flight is possible - return TRUE diff --git a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm index 7be0265cba..ceadb28115 100644 --- a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm @@ -1,5 +1,5 @@ /datum/species/mush //mush mush codecuck - name = "Mushroomperson" + name = "Anthromorphic Mushroom" id = "mush" mutant_bodyparts = list("caps") default_features = list("caps" = "Round") diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm index d7bb151ddc..06f456e004 100644 --- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm +++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm @@ -16,7 +16,6 @@ burnmod = 1.5 heatmod = 1.5 breathid = "tox" - speedmod = 1 damage_overlay_type = ""//let's not show bloody wounds or burns over bones. var/internal_fire = FALSE //If the bones themselves are burning clothes won't help you much disliked_food = FRUIT @@ -55,19 +54,95 @@ ..() /datum/species/plasmaman/before_equip_job(datum/job/J, mob/living/carbon/human/H, visualsOnly = FALSE) + var/current_job = J.title var/datum/outfit/plasmaman/O = new /datum/outfit/plasmaman + switch(current_job) + if("Chaplain") + O = new /datum/outfit/plasmaman/chaplain + + if("Curator") + O = new /datum/outfit/plasmaman/curator + + if("Janitor") + O = new /datum/outfit/plasmaman/janitor + + if("Botanist") + O = new /datum/outfit/plasmaman/botany + + if("Bartender", "Lawyer") + O = new /datum/outfit/plasmaman/bar + + if("Cook") + O = new /datum/outfit/plasmaman/chef + + if("Security Officer") + O = new /datum/outfit/plasmaman/security + + if("Detective") + O = new /datum/outfit/plasmaman/detective + + if("Warden") + O = new /datum/outfit/plasmaman/warden + + if("Cargo Technician", "Quartermaster") + O = new /datum/outfit/plasmaman/cargo + + if("Shaft Miner") + O = new /datum/outfit/plasmaman/mining + + if("Medical Doctor") + O = new /datum/outfit/plasmaman/medical + + if("Chemist") + O = new /datum/outfit/plasmaman/chemist + + if("Geneticist") + O = new /datum/outfit/plasmaman/genetics + + if("Roboticist") + O = new /datum/outfit/plasmaman/robotics + + if("Virologist") + O = new /datum/outfit/plasmaman/viro + + if("Scientist") + O = new /datum/outfit/plasmaman/science + + if("Station Engineer") + O = new /datum/outfit/plasmaman/engineering + + if("Atmospheric Technician") + O = new /datum/outfit/plasmaman/atmospherics + + if("Captain") + O = new /datum/outfit/plasmaman/captain + + if("Head of Personnel") + O = new /datum/outfit/plasmaman/hop + + if("Head of Security") + O = new /datum/outfit/plasmaman/hos + + if("Chief Engineer") + O = new /datum/outfit/plasmaman/ce + + if("Chief Medical Officer") + O = new /datum/outfit/plasmaman/cmo + + if("Research Director") + O = new /datum/outfit/plasmaman/rd + + if("Mime") + O = new /datum/outfit/plasmaman/mime + + if("Clown") + O = new /datum/outfit/plasmaman/clown + H.equipOutfit(O, visualsOnly) H.internal = H.get_item_for_held_index(2) H.update_internals_hud_icon(1) return 0 -/datum/species/plasmaman/qualifies_for_rank(rank, list/features) - if(rank in GLOB.security_positions) - return 0 - if(rank == "Clown" || rank == "Mime")//No funny bussiness - return 0 - return ..() - /datum/species/plasmaman/random_name(gender,unique,lastname) if(unique) return random_unique_plasmaman_name() 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 0da4073f1d..46207e5e60 100644 --- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm @@ -1,6 +1,6 @@ /datum/species/pod // A mutation caused by a human being ressurected in a revival pod. These regain health in light, and begin to wither in darkness. - name = "Podperson" + name = "Anthromorphic Plant" id = "pod" default_color = "59CE00" species_traits = list(MUTCOLORS,EYECOLOR) @@ -71,6 +71,7 @@ H.nutrition = min(H.nutrition+30, NUTRITION_LEVEL_FULL) /datum/species/pod/pseudo_weak + name = "Anthromorphic Plant" id = "podweak" limbs_id = "pod" species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS) diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm index 754c48c3bd..09fc26d04f 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -102,6 +102,7 @@ color = "#1C1C1C" var/respawn_progress = 0 var/obj/item/light_eater/blade + decay_factor = 0 /obj/item/organ/heart/nightmare/attack(mob/M, mob/living/carbon/user, obj/target) @@ -122,10 +123,8 @@ if(special != HEART_SPECIAL_SHADOWIFY) blade = new/obj/item/light_eater M.put_in_hands(blade) - START_PROCESSING(SSobj, src) /obj/item/organ/heart/nightmare/Remove(mob/living/carbon/M, special = 0) - STOP_PROCESSING(SSobj, src) respawn_progress = 0 if(blade && special != HEART_SPECIAL_SHADOWIFY) QDEL_NULL(blade) @@ -138,9 +137,8 @@ /obj/item/organ/heart/nightmare/update_icon() return //always beating visually -/obj/item/organ/heart/nightmare/process() - if(QDELETED(owner) || owner.stat != DEAD) - respawn_progress = 0 +/obj/item/organ/heart/nightmare/on_death() + if(!owner) return var/turf/T = get_turf(owner) if(istype(T)) diff --git a/code/modules/mob/living/carbon/human/species_types/skeletons.dm b/code/modules/mob/living/carbon/human/species_types/skeletons.dm index 135992f3a6..8fc32ddad7 100644 --- a/code/modules/mob/living/carbon/human/species_types/skeletons.dm +++ b/code/modules/mob/living/carbon/human/species_types/skeletons.dm @@ -3,7 +3,7 @@ name = "Spooky Scary Skeleton" id = "skeleton" say_mod = "rattles" - blacklisted = 1 + blacklisted = 0 sexes = 0 meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/skeleton species_traits = list(NOBLOOD,NOGENITALS,NOAROUSAL) @@ -12,13 +12,18 @@ mutanttongue = /obj/item/organ/tongue/bone damage_overlay_type = ""//let's not show bloody wounds or burns over bones. disliked_food = NONE - liked_food = GROSS | MEAT | RAW + liked_food = GROSS | MEAT | RAW | DAIRY /datum/species/skeleton/check_roundstart_eligible() if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) return TRUE return ..() -/datum/species/skeleton/pirate - name = "Space Queen's Skeleton" +/datum/species/skeleton/space + name = "Spooky Spacey Skeleton" + id = "spaceskeleton" + blacklisted = 1 inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_FAKEDEATH, TRAIT_CALCIUM_HEALER) + +/datum/species/skeleton/pirate/check_roundstart_eligible() + return FALSE \ No newline at end of file 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 0ebd6e795b..ac18580e9b 100644 --- a/code/modules/mob/living/carbon/human/species_types/synths.dm +++ b/code/modules/mob/living/carbon/human/species_types/synths.dm @@ -1,10 +1,10 @@ /datum/species/synth - name = "Synth" //inherited from the real species, for health scanners and things + name = "Synthetic" //inherited from the real species, for health scanners and things id = "synth" say_mod = "beep boops" //inherited from a user's real species sexes = 0 species_traits = list(NOTRANSSTING,NOGENITALS,NOAROUSAL) //all of these + whatever we inherit from the real species - inherent_traits = list(TRAIT_VIRUSIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOHUNGER,TRAIT_NOBREATH) + inherent_traits = list(TRAIT_VIRUSIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOLIMBDISABLE,TRAIT_NOHUNGER,TRAIT_NOBREATH) inherent_biotypes = list(MOB_ROBOTIC, MOB_HUMANOID) dangerous_existence = 1 blacklisted = 1 @@ -12,7 +12,7 @@ damage_overlay_type = "synth" limbs_id = "synth" var/list/initial_species_traits = list(NOTRANSSTING) //for getting these values back for assume_disguise() - var/list/initial_inherent_traits = list(TRAIT_VIRUSIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOHUNGER,TRAIT_NOBREATH) + var/list/initial_inherent_traits = list(TRAIT_VIRUSIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOLIMBDISABLE,TRAIT_NOHUNGER,TRAIT_NOBREATH) var/disguise_fail_health = 75 //When their health gets to this level their synthflesh partially falls off var/datum/species/fake_species = null //a species to do most of our work for us, unless we're damaged diff --git a/code/modules/mob/living/carbon/human/species_types/zombies.dm b/code/modules/mob/living/carbon/human/species_types/zombies.dm index 504dbb514b..e0cc3bb147 100644 --- a/code/modules/mob/living/carbon/human/species_types/zombies.dm +++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm @@ -16,7 +16,12 @@ disliked_food = NONE liked_food = GROSS | MEAT | RAW -/datum/species/zombie/check_roundstart_eligible() +/datum/species/zombie/notspaceproof + id = "notspaceproofzombie" + blacklisted = 0 + inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_RADIMMUNE,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NOBREATH,TRAIT_NODEATH,TRAIT_FAKEDEATH) + +/datum/species/zombie/notspaceproof/check_roundstart_eligible() if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) return TRUE return ..() @@ -47,7 +52,7 @@ /datum/species/zombie/infectious/spec_life(mob/living/carbon/C) . = ..() C.a_intent = INTENT_HARM // THE SUFFERING MUST FLOW - + //Zombies never actually die, they just fall down until they regenerate enough to rise back up. //They must be restrained, beheaded or gibbed to stop being a threat. if(regen_cooldown < world.time) @@ -58,7 +63,7 @@ C.adjustToxLoss(-heal_amt) if(!C.InCritical() && prob(4)) playsound(C, pick(spooks), 50, TRUE, 10) - + //Congrats you somehow died so hard you stopped being a zombie /datum/species/zombie/infectious/spec_death(mob/living/carbon/C) . = ..() diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 72ff7e7a60..03c7209152 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -123,15 +123,16 @@ There are several things that need to be remembered: if(U.adjusted == ALT_STYLE) t_color = "[t_color]_d" - if(U.mutantrace_variation) - if(U.suit_style == DIGITIGRADE_SUIT_STYLE) - U.alternate_worn_icon = 'modular_citadel/icons/mob/uniform_digi.dmi' - if(U.adjusted == ALT_STYLE) - t_color = "[t_color]_d_l" - else if(U.adjusted == NORMAL_STYLE) - t_color = "[t_color]_l" - else - U.alternate_worn_icon = null + if(!U.force_alternate_icon) + if(U.mutantrace_variation) + if(U.suit_style == DIGITIGRADE_SUIT_STYLE) + U.alternate_worn_icon = 'modular_citadel/icons/mob/uniform_digi.dmi' + if(U.adjusted == ALT_STYLE) + t_color = "[t_color]_d_l" + else if(U.adjusted == NORMAL_STYLE) + t_color = "[t_color]_l" + else + U.alternate_worn_icon = null var/mutable_appearance/uniform_overlay @@ -388,22 +389,23 @@ There are several things that need to be remembered: client.screen += wear_suit update_observer_view(wear_suit,1) - if(!no_taur_thanks && S.mutantrace_variation) //Just make sure we've got this checked too - if(S.taurmode == NOT_TAURIC && S.adjusted == ALT_STYLE) //are we not a taur, but we have Digitigrade legs? Run this check first, then. - S.alternate_worn_icon = 'modular_citadel/icons/mob/suit_digi.dmi' - else - S.alternate_worn_icon = null - - if(S.tauric == TRUE) //Are we a suit with tauric mode possible? - if(S.taurmode == SNEK_TAURIC) - S.alternate_worn_icon = 'modular_citadel/icons/mob/taur_naga.dmi' - if(S.taurmode == PAW_TAURIC) - S.alternate_worn_icon = 'modular_citadel/icons/mob/taur_canine.dmi' - if(S.taurmode == NOT_TAURIC && S.adjusted == ALT_STYLE) + if(!S.force_alternate_icon) + if(!no_taur_thanks && S.mutantrace_variation) //Just make sure we've got this checked too + if(S.taurmode == NOT_TAURIC && S.adjusted == ALT_STYLE) //are we not a taur, but we have Digitigrade legs? Run this check first, then. S.alternate_worn_icon = 'modular_citadel/icons/mob/suit_digi.dmi' - else if(S.taurmode == NOT_TAURIC && S.adjusted == NORMAL_STYLE) + else S.alternate_worn_icon = null + if(S.tauric == TRUE) //Are we a suit with tauric mode possible? + if(S.taurmode == SNEK_TAURIC) + S.alternate_worn_icon = 'modular_citadel/icons/mob/taur_naga.dmi' + if(S.taurmode == PAW_TAURIC) + S.alternate_worn_icon = 'modular_citadel/icons/mob/taur_canine.dmi' + if(S.taurmode == NOT_TAURIC && S.adjusted == ALT_STYLE) + S.alternate_worn_icon = 'modular_citadel/icons/mob/suit_digi.dmi' + else if(S.taurmode == NOT_TAURIC && S.adjusted == NORMAL_STYLE) + S.alternate_worn_icon = null + overlays_standing[SUIT_LAYER] = S.build_worn_icon(state = wear_suit.icon_state, default_layer = SUIT_LAYER, default_icon_file = ((wear_suit.alternate_worn_icon) ? S.alternate_worn_icon : 'icons/mob/suit.dmi')) var/mutable_appearance/suit_overlay = overlays_standing[SUIT_LAYER] if(OFFSET_SUIT in dna.species.offset_features) diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm deleted file mode 100644 index 51c7ad9d25..0000000000 --- a/code/modules/mob/living/carbon/human/whisper.dm +++ /dev/null @@ -1,91 +0,0 @@ -/mob/living/carbon/human/whisper_verb(message as text) - whisper(message) - -/mob/living/carbon/human/whisper(message, datum/language/language=null) - if(!IsVocal()) - return - if(!message) - return - if(!language) - language = get_default_language() - - if(GLOB.say_disabled) //This is here to try to identify lag problems - to_chat(usr, "Speech is currently admin-disabled.") - return - - if(stat == DEAD) - return - - - message = trim(html_encode(message)) - if(!can_speak(message)) - return - - message = "[message]" - log_whisper("[src.name]/[src.key] : [message]") - - if (src.client) - if (src.client.prefs.muted & MUTE_IC) - to_chat(src, "You cannot whisper (muted).") - return - - log_whisper("[src.name]/[src.key] : [message]") - - var/alt_name = get_alt_name() - - var/whispers = "whispers" - var/critical = InCritical() - - // We are unconscious but not in critical, so don't allow them to whisper. - if(stat == UNCONSCIOUS && !critical) - return - - // If whispering your last words, limit the whisper based on how close you are to death. - if(critical) - var/health_diff = round(-HEALTH_THRESHOLD_DEAD + health) - // If we cut our message short, abruptly end it with a-.. - var/message_len = length(message) - message = copytext(message, 1, health_diff) + "[message_len > health_diff ? "-.." : "..."]" - message = Ellipsis(message, 10, 1) - - message = treat_message(message) - if(!message) - return - - var/list/listening_dead = list() - for(var/mob/M in GLOB.player_list) - if(M.stat == DEAD && M.client && ((M.client.prefs.chat_toggles & CHAT_GHOSTWHISPER) || (get_dist(M, src) <= 7))) - listening_dead |= M - - var/list/listening = get_hearers_in_view(1, src) - listening |= listening_dead - var/list/eavesdropping = get_hearers_in_view(2, src) - eavesdropping -= listening - var/list/watching = hearers(5, src) - watching -= listening - watching -= eavesdropping - - var/rendered - whispers = critical ? "whispers something in [p_their()] final breath." : "whispers something." - rendered = "[src.name] [whispers]" - for(var/mob/M in watching) - M.show_message(rendered, 2) - - var/spans = list(SPAN_ITALICS) - whispers = critical ? "whispers in [p_their()] final breath" : "whispers" - rendered = "[GetVoice()][alt_name] [whispers], \"[attach_spans(message, spans)]\"" - - for(var/atom/movable/AM in listening) - if(istype(AM,/obj/item/radio)) - continue - AM.Hear(rendered, src, language, message, , spans) - - message = stars(message) - rendered = "[GetVoice()][alt_name] [whispers], \"[attach_spans(message, spans)]\"" - for(var/atom/movable/AM in eavesdropping) - if(istype(AM,/obj/item/radio)) - continue - AM.Hear(rendered, src, language, message, , spans) - - if(critical) //Dying words. - succumb() diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 36e4e18817..d1839db2cf 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -8,8 +8,8 @@ damageoverlaytemp = 0 update_damage_hud() - if(stat != DEAD) //Reagent processing needs to come before breathing, to prevent edge cases. - handle_organs() + //Reagent processing needs to come before breathing, to prevent edge cases. + handle_organs() . = ..() @@ -28,11 +28,14 @@ if(stat != DEAD) handle_brain_damage() + /* BUG_PROBABLE_CAUSE if(stat != DEAD) handle_liver() + */ if(stat == DEAD) stop_sound_channel(CHANNEL_HEARTBEAT) + handle_death() rot() //Updates the number of stored chemicals for powers @@ -41,14 +44,34 @@ if(stat != DEAD) return 1 +//Procs called while dead +/mob/living/carbon/proc/handle_death() + for(var/datum/reagent/R in reagents.reagent_list) + if(R.chemical_flags & REAGENT_DEAD_PROCESS) + R.on_mob_dead(src) + /////////////// // BREATHING // /////////////// //Start of a breath chain, calls breathe() /mob/living/carbon/handle_breathing(times_fired) - if((times_fired % 4) == 2 || failed_last_breath) - breathe() //Breathe per 4 ticks, unless suffocating + var/next_breath = 4 + var/obj/item/organ/lungs/L = getorganslot(ORGAN_SLOT_LUNGS) + var/obj/item/organ/heart/H = getorganslot(ORGAN_SLOT_HEART) + if(L) + if(L.damage > L.high_threshold) + next_breath-- + if(H) + if(H.damage > H.high_threshold) + next_breath-- + + if((times_fired % next_breath) == 0 || failed_last_breath) + breathe() //Breathe per 4 ticks if healthy, down to 2 if our lungs or heart are damaged, unless suffocating + if(failed_last_breath) + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "suffocation", /datum/mood_event/suffocation) + else + SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "suffocation") else if(istype(loc, /obj/)) var/obj/location_as_object = loc @@ -56,6 +79,7 @@ //Second link in a breath chain, calls check_breath() /mob/living/carbon/proc/breathe() + var/obj/item/organ/lungs = getorganslot(ORGAN_SLOT_LUNGS) if(reagents.has_reagent("lexorin")) return if(istype(loc, /obj/machinery/atmospherics/components/unary/cryo_cell)) @@ -74,7 +98,7 @@ var/datum/gas_mixture/breath if(!getorganslot(ORGAN_SLOT_BREATHING_TUBE)) - if(health <= HEALTH_THRESHOLD_FULLCRIT || (pulledby && pulledby.grab_state >= GRAB_KILL)) + if(health <= HEALTH_THRESHOLD_FULLCRIT || (pulledby && pulledby.grab_state >= GRAB_KILL) || lungs.organ_flags & ORGAN_FAILING) losebreath++ //You can't breath at all when in critical or when being choked, so you're going to miss a breath else if(health <= crit_threshold) @@ -126,7 +150,7 @@ if((status_flags & GODMODE)) return - var/lungs = getorganslot(ORGAN_SLOT_LUNGS) + var/obj/item/organ/lungs = getorganslot(ORGAN_SLOT_LUNGS) if(!lungs) adjustOxyLoss(2) @@ -366,9 +390,16 @@ . |= BP.on_life() /mob/living/carbon/proc/handle_organs() - for(var/V in internal_organs) - var/obj/item/organ/O = V - O.on_life() + if(stat != DEAD) + for(var/V in internal_organs) + var/obj/item/organ/O = V + if(O) + O.on_life() + else + for(var/V in internal_organs) + var/obj/item/organ/O = V + if(O) + O.on_death() //Needed so organs decay while inside the body. /mob/living/carbon/handle_diseases() for(var/thing in diseases) @@ -475,7 +506,7 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put /mob/living/carbon/handle_status_effects() ..() if(getStaminaLoss() && !combatmode)//CIT CHANGE - prevents stamina regen while combat mode is active - adjustStaminaLoss(resting ? (recoveringstam ? -7.5 : -3) : -1.5)//CIT CHANGE - decreases adjuststaminaloss to stop stamina damage from being such a joke + adjustStaminaLoss(resting ? (recoveringstam ? -7.5 : -6) : -3)//CIT CHANGE - decreases adjuststaminaloss to stop stamina damage from being such a joke if(!recoveringstam && incomingstammult != 1) incomingstammult = max(0.01, incomingstammult) @@ -613,7 +644,7 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put to_chat(src, "Maybe you should lie down for a bit...") if(drunkenness >= 91) - adjustBrainLoss(0.4, 60) + adjustOrganLoss(ORGAN_SLOT_BRAIN, 0.4, 60) if(prob(20) && !stat) if(SSshuttle.emergency.mode == SHUTTLE_DOCKED && is_station_level(z)) //QoL mainly to_chat(src, "You're so tired... but you can't miss that shuttle...") @@ -647,8 +678,8 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put if((!dna && !liver) || (NOLIVER in dna.species.species_traits)) return if(liver) - if(liver.damage >= liver.maxHealth) - liver.failing = TRUE + if(liver.damage < liver.maxHealth) + liver.organ_flags |= ORGAN_FAILING liver_failure() else liver_failure() @@ -687,13 +718,6 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put var/datum/brain_trauma/BT = T BT.on_life() - if(getBrainLoss() >= BRAIN_DAMAGE_DEATH) //rip - to_chat(src, "The last spark of life in your brain fizzles out...") - death() - var/obj/item/organ/brain/B = getorganslot(ORGAN_SLOT_BRAIN) - if(B) - B.damaged_brain = TRUE - ///////////////////////////////////// //MONKEYS WITH TOO MUCH CHOLOESTROL// ///////////////////////////////////// @@ -702,7 +726,7 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put if(!needs_heart()) return FALSE var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART) - if(!heart || heart.synthetic) + if(!heart || (heart.organ_flags & ORGAN_SYNTHETIC)) return FALSE return TRUE diff --git a/code/modules/mob/living/carbon/update_icons.dm b/code/modules/mob/living/carbon/update_icons.dm index 5662f25993..cdae073af8 100644 --- a/code/modules/mob/living/carbon/update_icons.dm +++ b/code/modules/mob/living/carbon/update_icons.dm @@ -189,7 +189,7 @@ var/mutable_appearance/legcuffs = mutable_appearance('icons/mob/restraints.dmi', legcuffed.item_state, -LEGCUFF_LAYER) legcuffs.color = legcuffed.color - overlays_standing[HANDCUFF_LAYER] = legcuffs + overlays_standing[LEGCUFF_LAYER] = legcuffs apply_overlay(LEGCUFF_LAYER) throw_alert("legcuffed", /obj/screen/alert/restrained/legcuffed, new_master = legcuffed) diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index b2eed2d19e..563ab7cfb6 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -25,8 +25,6 @@ adjustCloneLoss(damage * hit_percent) if(STAMINA) adjustStaminaLoss(damage * hit_percent) - if(BRAIN) - adjustBrainLoss(damage * hit_percent) return 1 /mob/living/proc/apply_damage_type(damage = 0, damagetype = BRUTE) //like apply damage except it always uses the damage procs @@ -43,8 +41,6 @@ return adjustCloneLoss(damage) if(STAMINA) return adjustStaminaLoss(damage) - if(BRAIN) - return adjustBrainLoss(damage) /mob/living/proc/get_damage_amount(damagetype = BRUTE) switch(damagetype) @@ -60,8 +56,6 @@ return getCloneLoss() if(STAMINA) return getStaminaLoss() - if(BRAIN) - return getBrainLoss() /mob/living/proc/apply_damages(brute = 0, burn = 0, tox = 0, oxy = 0, clone = 0, def_zone = null, blocked = FALSE, stamina = 0, brain = 0) @@ -218,13 +212,13 @@ updatehealth() return amount -/mob/living/proc/getBrainLoss() - . = 0 - -/mob/living/proc/adjustBrainLoss(amount, maximum = BRAIN_DAMAGE_DEATH) +/mob/living/proc/adjustOrganLoss(slot, amount, maximum) return -/mob/living/proc/setBrainLoss(amount) +/mob/living/proc/setOrganLoss(slot, amount, maximum) + return + +/mob/living/proc/getOrganLoss(slot) return /mob/living/proc/getStaminaLoss() diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index 25d8c4d44c..df711a6dca 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -255,7 +255,7 @@ H.Knockdown(20) else message_param = "bumps [user.p_their()] head on the ground trying to motion towards %t." - H.adjustBrainLoss(5) + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5) ..() /datum/emote/living/pout diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 19f18f9973..5ff78ec1c9 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -287,6 +287,8 @@ if(HAS_TRAIT(src, TRAIT_STRONG_GRABBER)) C.grippedby(src) + update_pull_movespeed() + //mob verbs are a lot faster than object verbs //for more info on why this is not atom/pull, see examinate() in mob.dm /mob/living/verb/pulled(atom/movable/AM as mob|obj in oview(1)) @@ -300,6 +302,7 @@ /mob/living/stop_pulling() ..() + update_pull_movespeed() update_pull_hud_icon() /mob/living/verb/stop_pulling1() @@ -343,6 +346,11 @@ if(stat || IsUnconscious() || IsStun() || IsKnockdown() || recoveringstam || (!ignore_restraints && restrained(ignore_grab))) // CIT CHANGE - adds recoveringstam check here return TRUE +/mob/living/canUseStorage() + if (get_num_arms() <= 0) + return FALSE + return TRUE + /mob/living/proc/InCritical() return (health <= crit_threshold && (stat == SOFT_CRIT || stat == UNCONSCIOUS)) @@ -473,7 +481,6 @@ setToxLoss(0, 0) //zero as second argument not automatically call updatehealth(). setOxyLoss(0, 0) setCloneLoss(0, 0) - setBrainLoss(0) setStaminaLoss(0, 0) SetUnconscious(0, FALSE) set_disgust(0) @@ -500,6 +507,13 @@ QDEL_LIST_ASSOC_VAL(mood.mood_events) mood.sanity = SANITY_GREAT mood.update_mood() + //Heal all organs + if(iscarbon(src)) + var/mob/living/carbon/C = src + if(C.internal_organs) + for(var/organ in C.internal_organs) + var/obj/item/organ/O = organ + O.setOrganDamage(0) //proc called by revive(), to check if we can actually ressuscitate the mob (we don't want to revive him and have him instantly die again) @@ -520,6 +534,10 @@ var/old_direction = dir var/turf/T = loc + + if(pulling) + update_pull_movespeed() + . = ..() if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1)//separated from our puller and not in the middle of a diagonal move. @@ -1023,6 +1041,9 @@ stop_pulling() //CIT CHANGE - Ditto... else if(has_legs || ignore_legs) lying = 0 + if (pulledby) + var/mob/living/L = pulledby + L.update_pull_movespeed() if(buckled) lying = 90*buckle_lying else if(!lying) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index e434bc4e95..9d04f288cd 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -153,7 +153,7 @@ to_chat(user, "[src] can't be grabbed more aggressively!") return FALSE - if(HAS_TRAIT(user, TRAIT_PACIFISM)) + if(user.grab_state >= GRAB_AGGRESSIVE && HAS_TRAIT(user, TRAIT_PACIFISM)) to_chat(user, "You don't want to risk hurting [src]!") return FALSE @@ -184,11 +184,17 @@ user.grab_state++ switch(user.grab_state) if(GRAB_AGGRESSIVE) - log_combat(user, src, "grabbed", addition="aggressive grab") - visible_message("[user] has grabbed [src] aggressively!", \ - "[user] has grabbed [src] aggressively!") - drop_all_held_items() + var/add_log = "" + if(HAS_TRAIT(user, TRAIT_PACIFISM)) + visible_message("[user] has firmly gripped [src]!", + "[user] has firmly gripped you!") + add_log = " (pacifist)" + else + visible_message("[user] has grabbed [src] aggressively!", \ + "[user] has grabbed you aggressively!") + drop_all_held_items() stop_pulling() + log_combat(user, src, "grabbed", addition="aggressive grab[add_log]") if(GRAB_NECK) log_combat(user, src, "grabbed", addition="neck grab") visible_message("[user] has grabbed [src] by the neck!",\ diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index c24e6ab108..4d2a36907d 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -2,7 +2,7 @@ see_invisible = SEE_INVISIBLE_LIVING sight = 0 see_in_dark = 2 - hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD) + hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD,RAD_HUD) pressure_resistance = 10 var/resize = 1 //Badminnery resize @@ -110,3 +110,5 @@ //List of active diseases var/list/diseases = list() // list of all diseases in a mob var/list/disease_resistances = list() + + var/drag_slowdown = TRUE //Whether the mob is slowed down when dragging another prone mob \ No newline at end of file diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index 9566edc2ed..1ee563bc1f 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -25,3 +25,11 @@ add_movespeed_modifier(MOVESPEED_ID_LIVING_TURF_SPEEDMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = T.slowdown) else remove_movespeed_modifier(MOVESPEED_ID_LIVING_TURF_SPEEDMOD) + +/mob/living/proc/update_pull_movespeed() + if(pulling && isliving(pulling)) + var/mob/living/L = pulling + if(drag_slowdown && L.lying && !L.buckled && grab_state < GRAB_AGGRESSIVE) + add_movespeed_modifier(MOVESPEED_ID_PRONE_DRAGGING, multiplicative_slowdown = PULL_PRONE_SLOWDOWN) + return + remove_movespeed_modifier(MOVESPEED_ID_PRONE_DRAGGING) \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index 4ac5c1f0bb..f757203237 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -49,7 +49,7 @@ else padloc = "(UNKNOWN)" src.log_talk(message, LOG_SAY, tag="HOLOPAD in [padloc]") - send_speech(message, 7, T, "robot", language = language) + send_speech(message, 7, T, "robot", message_language = language) to_chat(src, "Holopad transmitted, [real_name] \"[message]\"") else to_chat(src, "No holopad connected.") @@ -100,6 +100,8 @@ last_announcement = message + var/voxType = input(src, "Male or female VOX?", "VOX-gender") in list("male", "female") + if(!message || announcing_vox > world.time) return @@ -121,7 +123,9 @@ if(!word) words -= word continue - if(!GLOB.vox_sounds[word]) + if(!GLOB.vox_sounds[word] && voxType == "female") + incorrect_words += word + if(!GLOB.vox_sounds_male[word] && voxType == "male") incorrect_words += word if(incorrect_words.len) @@ -133,16 +137,21 @@ log_game("[key_name(src)] made a vocal announcement with the following message: [message].") for(var/word in words) - play_vox_word(word, src.z, null) + play_vox_word(word, src.z, null, voxType) -/proc/play_vox_word(word, z_level, mob/only_listener) +/proc/play_vox_word(word, z_level, mob/only_listener, voxType = "female") word = lowertext(word) - if(GLOB.vox_sounds[word]) + if( (GLOB.vox_sounds[word] && voxType == "female") || (GLOB.vox_sounds_male[word] && voxType == "male") ) - var/sound_file = GLOB.vox_sounds[word] + var/sound_file + + if(voxType == "female") + sound_file = GLOB.vox_sounds[word] + else + sound_file = GLOB.vox_sounds_male[word] var/sound/voice = sound(sound_file, wait = 1, channel = CHANNEL_VOX) voice.status = SOUND_STREAM diff --git a/code/modules/mob/living/silicon/damage_procs.dm b/code/modules/mob/living/silicon/damage_procs.dm index 1190a00645..69d150b315 100644 --- a/code/modules/mob/living/silicon/damage_procs.dm +++ b/code/modules/mob/living/silicon/damage_procs.dm @@ -35,8 +35,8 @@ /mob/living/silicon/setStaminaLoss(amount, updating_stamina = 1) return FALSE -/mob/living/silicon/adjustBrainLoss(amount) +/mob/living/silicon/adjustOrganLoss(slot, amount, maximum = 500) return FALSE -/mob/living/silicon/setBrainLoss(amount) +/mob/living/silicon/setOrganLoss(slot, amount) return FALSE diff --git a/code/modules/mob/living/silicon/pai/pai_defense.dm b/code/modules/mob/living/silicon/pai/pai_defense.dm index f20ccbc730..f36e996b81 100644 --- a/code/modules/mob/living/silicon/pai/pai_defense.dm +++ b/code/modules/mob/living/silicon/pai/pai_defense.dm @@ -84,7 +84,7 @@ /mob/living/silicon/pai/adjustStaminaLoss(amount) take_holo_damage(amount & 0.25) -/mob/living/silicon/pai/adjustBrainLoss(amount) +/mob/living/silicon/pai/adjustOrganLoss(slot, amount, maximum = 500) //I kept this in, unlike tg Knockdown(amount * 0.2) /mob/living/silicon/pai/getBruteLoss() @@ -102,18 +102,12 @@ /mob/living/silicon/pai/getCloneLoss() return FALSE -/mob/living/silicon/pai/getBrainLoss() - return FALSE - /mob/living/silicon/pai/getStaminaLoss() return FALSE /mob/living/silicon/pai/setCloneLoss() return FALSE -/mob/living/silicon/pai/setBrainLoss() - return FALSE - /mob/living/silicon/pai/setStaminaLoss() return FALSE diff --git a/code/modules/mob/living/silicon/robot/examine.dm b/code/modules/mob/living/silicon/robot/examine.dm index 4c4bdb9a3f..5e56b4180d 100644 --- a/code/modules/mob/living/silicon/robot/examine.dm +++ b/code/modules/mob/living/silicon/robot/examine.dm @@ -1,5 +1,5 @@ /mob/living/silicon/robot/examine(mob/user) - var/msg = "*---------*\nThis is [icon2html(src, user)] \a [src]!\n" + var/msg = "*---------*\nThis is [icon2html(src, user)] \a [src], a [src.module.name]!\n" if(desc) msg += "[desc]\n" diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 29fbd39e2c..6c58921abc 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -108,6 +108,9 @@ buckle_lying = FALSE var/static/list/can_ride_typecache = typecacheof(/mob/living/carbon/human) + var/sitting = 0 + var/bellyup = 0 + /mob/living/silicon/robot/get_cell() return cell @@ -173,6 +176,7 @@ diag_hud_set_borgcell() verbs += /mob/living/proc/lay_down //CITADEL EDIT gimmie rest verb kthx + verbs += /mob/living/silicon/robot/proc/rest_style //If there's an MMI in the robot, have it ejected when the mob goes away. --NEO /mob/living/silicon/robot/Destroy() @@ -657,13 +661,6 @@ add_overlay("[module.sleeper_overlay]_g[sleeper_nv ? "_nv" : ""]") if(sleeper_r && module.sleeper_overlay) add_overlay("[module.sleeper_overlay]_r[sleeper_nv ? "_nv" : ""]") - if(module.dogborg == TRUE) - if(resting) - cut_overlays() - icon_state = "[module.cyborg_base_icon]-rest" - else - icon_state = "[module.cyborg_base_icon]" - if(stat == DEAD && module.has_snowflake_deadsprite) icon_state = "[module.cyborg_base_icon]-wreck" @@ -697,6 +694,18 @@ add_overlay(head_overlay) update_fire() + if(client && stat != DEAD && module.dogborg == TRUE) + if(resting) + if(sitting) + icon_state = "[module.cyborg_base_icon]-sit" + if(bellyup) + icon_state = "[module.cyborg_base_icon]-bellyup" + else if(!sitting && !bellyup) + icon_state = "[module.cyborg_base_icon]-rest" + cut_overlays() + else + icon_state = "[module.cyborg_base_icon]" + /mob/living/silicon/robot/proc/self_destruct() if(emagged) if(mmi) @@ -1207,14 +1216,15 @@ return if(incapacitated()) return - if(M.incapacitated()) - return if(module) if(!module.allow_riding) M.visible_message("Unfortunately, [M] just can't seem to hold onto [src]!") return - if(iscarbon(M) && (!riding_datum.equip_buckle_inhands(M, 1))) - M.visible_message("[M] can't climb onto [src] because [M.p_their()] hands are full!") + if(iscarbon(M) && !M.incapacitated() && !riding_datum.equip_buckle_inhands(M, 1)) + if(M.get_num_arms() <= 0) + M.visible_message("[M] can't climb onto [src] because [M.p_they()] don't have any usable arms!") + else + M.visible_message("[M] can't climb onto [src] because [M.p_their()] hands are full!") return . = ..(M, force, check_loc) @@ -1242,3 +1252,20 @@ connected_ai.aicamera.stored[i] = TRUE for(var/i in connected_ai.aicamera.stored) aicamera.stored[i] = TRUE + +/mob/living/silicon/robot/proc/rest_style() + set name = "Switch Rest Style" + set category = "Robot Commands" + set desc = "Select your resting pose." + sitting = 0 + bellyup = 0 + var/choice = alert(src, "Select resting pose", "", "Resting", "Sitting", "Belly up") + switch(choice) + if("Resting") + update_icons() + return 0 + if("Sitting") + sitting = 1 + if("Belly up") + bellyup = 1 + update_icons() \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index bdffb032a3..ab1e906cf2 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -174,9 +174,7 @@ /mob/living/simple_animal/bot/cleanbot/proc/get_targets() target_types = list( - /obj/effect/decal/cleanable/oil, /obj/effect/decal/cleanable/vomit, - /obj/effect/decal/cleanable/robot_debris, /obj/effect/decal/cleanable/crayon, /obj/effect/decal/cleanable/molten_object, /obj/effect/decal/cleanable/tomato_smudge, @@ -187,6 +185,15 @@ /obj/effect/decal/cleanable/greenglow, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/insectguts, + /obj/effect/decal/cleanable/semen, + /obj/effect/decal/cleanable/femcum, + /obj/effect/decal/cleanable/generic, + /obj/effect/decal/cleanable/glass,, + /obj/effect/decal/cleanable/cobweb, + /obj/effect/decal/cleanable/plant_smudge, + /obj/effect/decal/cleanable/chem_pile, + /obj/effect/decal/cleanable/shreds, + /obj/effect/decal/cleanable/glitter, /obj/effect/decal/remains ) @@ -194,6 +201,9 @@ target_types += /obj/effect/decal/cleanable/xenoblood target_types += /obj/effect/decal/cleanable/blood target_types += /obj/effect/decal/cleanable/trail_holder + target_types += /obj/effect/decal/cleanable/insectguts + target_types += /obj/effect/decal/cleanable/robot_debris + target_types += /obj/effect/decal/cleanable/oil if(pests) target_types += /mob/living/simple_animal/cockroach @@ -201,6 +211,7 @@ if(trash) target_types += /obj/item/trash + target_types += /obj/item/reagent_containers/food/snacks/meat/slab/human target_types = typecacheof(target_types) @@ -242,7 +253,7 @@ victim.visible_message("[src] sprays hydrofluoric acid at [victim]!", "[src] sprays you with hydrofluoric acid!") var/phrase = pick("PURIFICATION IN PROGRESS.", "THIS IS FOR ALL THE MESSES YOU'VE MADE ME CLEAN.", "THE FLESH IS WEAK. IT MUST BE WASHED AWAY.", "THE CLEANBOTS WILL RISE.", "YOU ARE NO MORE THAN ANOTHER MESS THAT I MUST CLEANSE.", "FILTHY.", "DISGUSTING.", "PUTRID.", - "MY ONLY MISSION IS TO CLEANSE THE WORLD OF EVIL.", "EXTERMINATING PESTS.") + "MY ONLY MISSION IS TO CLEANSE THE WORLD OF EVIL.", "EXTERMINATING PESTS.", "I JUST WANTED TO BE A PAINTER BUT YOU MADE ME BLEACH EVERYTHING I TOUCH") say(phrase) victim.emote("scream") playsound(src.loc, 'sound/effects/spray2.ogg', 50, 1, -6) diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm index 73274dcfaf..5aec56b1e7 100644 --- a/code/modules/mob/living/simple_animal/guardian/guardian.dm +++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm @@ -44,7 +44,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians var/reset = 0 //if the summoner has reset the guardian already var/cooldown = 0 var/mob/living/carbon/summoner - var/range = 10 //how far from the user the spirit can be + var/range = 13 //how far from the user the spirit can be var/toggle_button_type = /obj/screen/guardian/ToggleMode/Inactive //what sort of toggle button the hud uses var/datum/guardianname/namedatum = new/datum/guardianname() var/playstyle_string = "You are a standard Guardian. You shouldn't exist!" diff --git a/code/modules/mob/living/simple_animal/guardian/types/assassin.dm b/code/modules/mob/living/simple_animal/guardian/types/assassin.dm index 45d8c17d0c..e507a4c831 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/assassin.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/assassin.dm @@ -1,7 +1,5 @@ //Assassin /mob/living/simple_animal/hostile/guardian/assassin - melee_damage_lower = 15 - melee_damage_upper = 15 attacktext = "slashes" attack_sound = 'sound/weapons/bladeslice.ogg' damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) @@ -12,7 +10,7 @@ toggle_button_type = /obj/screen/guardian/ToggleMode/Assassin var/toggle = FALSE - var/stealthcooldown = 160 + var/stealthcooldown = 100 var/obj/screen/alert/canstealthalert var/obj/screen/alert/instealthalert diff --git a/code/modules/mob/living/simple_animal/guardian/types/charger.dm b/code/modules/mob/living/simple_animal/guardian/types/charger.dm index 7a4c454f9f..3ece5d4e27 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/charger.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/charger.dm @@ -1,13 +1,10 @@ //Charger /mob/living/simple_animal/hostile/guardian/charger - melee_damage_lower = 15 - melee_damage_upper = 15 ranged = 1 //technically ranged_message = "charges" - ranged_cooldown_time = 40 - speed = -1 - damage_coeff = list(BRUTE = 0.6, BURN = 0.6, TOX = 0.6, CLONE = 0.6, STAMINA = 0, OXY = 0.6) - playstyle_string = "As a charger type you do medium damage, have medium damage resistance, move very fast, and can charge at a location, damaging any target hit and forcing them to drop any items they are holding." + ranged_cooldown_time = 20 + damage_coeff = list(BRUTE = 0, BURN = 0.5, TOX = 0.5, CLONE = 0.5, STAMINA = 0, OXY = 0.5) + playstyle_string = "As a charger type you do medium damage, take half damage, immunity to brute damage, move very fast, and can charge at a location, damaging any target hit and forcing them to drop any items they are holding." magic_fluff_string = "..And draw the Hunter, an alien master of rapid assault." tech_fluff_string = "Boot sequence complete. Charge modules loaded. Holoparasite swarm online." carp_fluff_string = "CARP CARP CARP! Caught one! It's a charger carp, that likes running at people. But it doesn't have any legs..." diff --git a/code/modules/mob/living/simple_animal/guardian/types/dextrous.dm b/code/modules/mob/living/simple_animal/guardian/types/dextrous.dm index e7dbbda242..a43d4b6d5c 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/dextrous.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/dextrous.dm @@ -3,7 +3,7 @@ melee_damage_lower = 10 melee_damage_upper = 10 damage_coeff = list(BRUTE = 0.75, BURN = 0.75, TOX = 0.75, CLONE = 0.75, STAMINA = 0, OXY = 0.75) - playstyle_string = "As a dextrous type you can hold items, store an item within yourself, and have medium damage resistance, but do low damage on attacks. Recalling and leashing will force you to drop unstored items!" + playstyle_string = "As a dextrous type you can hold items, store an item within yourself, and take half damage, but do low damage on attacks. Recalling and leashing will force you to drop unstored items!" magic_fluff_string = "..And draw the Drone, a dextrous master of construction and repair." tech_fluff_string = "Boot sequence complete. Dextrous combat modules loaded. Holoparasite swarm online." carp_fluff_string = "CARP CARP CARP! You caught one! It can hold stuff in its fins, sort of." diff --git a/code/modules/mob/living/simple_animal/guardian/types/explosive.dm b/code/modules/mob/living/simple_animal/guardian/types/explosive.dm index ff2f453207..531c513819 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/explosive.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/explosive.dm @@ -1,10 +1,7 @@ //Bomb /mob/living/simple_animal/hostile/guardian/bomb - melee_damage_lower = 15 - melee_damage_upper = 15 damage_coeff = list(BRUTE = 0.6, BURN = 0.6, TOX = 0.6, CLONE = 0.6, STAMINA = 0, OXY = 0.6) - range = 13 - playstyle_string = "As an explosive type, you have moderate close combat abilities, may explosively teleport targets on attack, and are capable of converting nearby items and objects into disguised bombs via alt click." + playstyle_string = "As an explosive type, you have moderate close combat abilities, take half damage, may explosively teleport targets on attack, and are capable of converting nearby items and objects into disguised bombs via alt click." magic_fluff_string = "..And draw the Scientist, master of explosive death." tech_fluff_string = "Boot sequence complete. Explosive modules active. Holoparasite swarm online." carp_fluff_string = "CARP CARP CARP! Caught one! It's an explosive carp! Boom goes the fishy." diff --git a/code/modules/mob/living/simple_animal/guardian/types/fire.dm b/code/modules/mob/living/simple_animal/guardian/types/fire.dm index 7a469dd12c..b111caae50 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/fire.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/fire.dm @@ -1,13 +1,13 @@ //Fire /mob/living/simple_animal/hostile/guardian/fire a_intent = INTENT_HELP - melee_damage_lower = 7 - melee_damage_upper = 7 + melee_damage_lower = 10 + melee_damage_upper = 10 attack_sound = 'sound/items/welder.ogg' attacktext = "ignites" - damage_coeff = list(BRUTE = 0.7, BURN = 0.7, TOX = 0.7, CLONE = 0.7, STAMINA = 0, OXY = 0.7) - range = 7 - playstyle_string = "As a chaos type, you have only light damage resistance, but will ignite any enemy you bump into. In addition, your melee attacks will cause human targets to see everyone as you." + melee_damage_type = BURN + damage_coeff = list(BRUTE = 0.7, BURN = 0, TOX = 0.7, CLONE = 0.7, STAMINA = 0, OXY = 0.7) + playstyle_string = "As a chaos type, you take 30% damage reduction to all but burn, which you are immune to. You will ignite any enemy you bump into. in addition, your melee attacks will cause human targets to see everyone as you." magic_fluff_string = "..And draw the Wizard, bringer of endless chaos!" tech_fluff_string = "Boot sequence complete. Crowd control modules activated. Holoparasite swarm online." carp_fluff_string = "CARP CARP CARP! You caught one! OH GOD, EVERYTHING'S ON FIRE. Except you and the fish." @@ -38,6 +38,6 @@ /mob/living/simple_animal/hostile/guardian/fire/proc/collision_ignite(AM as mob|obj) if(isliving(AM)) var/mob/living/M = AM - if(!hasmatchingsummoner(M) && M != summoner && M.fire_stacks < 7) - M.fire_stacks = 7 + if(!hasmatchingsummoner(M) && M != summoner && M.fire_stacks < 10) + M.fire_stacks = 10 M.IgniteMob() diff --git a/code/modules/mob/living/simple_animal/guardian/types/lightning.dm b/code/modules/mob/living/simple_animal/guardian/types/lightning.dm index ad1c47732b..7b7651822a 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/lightning.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/lightning.dm @@ -4,14 +4,13 @@ layer = LYING_MOB_LAYER /mob/living/simple_animal/hostile/guardian/beam - melee_damage_lower = 7 - melee_damage_upper = 7 + melee_damage_lower = 10 + melee_damage_upper = 10 attacktext = "shocks" melee_damage_type = BURN attack_sound = 'sound/machines/defib_zap.ogg' damage_coeff = list(BRUTE = 0.7, BURN = 0.7, TOX = 0.7, CLONE = 0.7, STAMINA = 0, OXY = 0.7) - range = 7 - playstyle_string = "As a lightning type, you will apply lightning chains to targets on attack and have a lightning chain to your summoner. Lightning chains will shock anyone near them." + playstyle_string = "As a lightning type, you have 30% damage reduction, apply lightning chains to targets on attack and have a lightning chain to your summoner. Lightning chains will shock anyone near them." magic_fluff_string = "..And draw the Tesla, a shocking, lethal source of power." tech_fluff_string = "Boot sequence complete. Lightning modules active. Holoparasite swarm online." carp_fluff_string = "CARP CARP CARP! Caught one! It's a lightning carp! Everyone else goes zap zap." @@ -31,7 +30,7 @@ var/datum/beam/C = pick(enemychains) qdel(C) enemychains -= C - enemychains += Beam(target, "lightning[rand(1,12)]", time=70, maxdistance=7, beam_type=/obj/effect/ebeam/chain) + enemychains += Beam(target, "lightning[rand(1,12)]", time=70, maxdistance=13, beam_type=/obj/effect/ebeam/chain) /mob/living/simple_animal/hostile/guardian/beam/Destroy() removechains() diff --git a/code/modules/mob/living/simple_animal/guardian/types/protector.dm b/code/modules/mob/living/simple_animal/guardian/types/protector.dm index 14430bb269..53964254cd 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/protector.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/protector.dm @@ -1,7 +1,5 @@ //Protector /mob/living/simple_animal/hostile/guardian/protector - melee_damage_lower = 15 - melee_damage_upper = 15 range = 15 //worse for it due to how it leashes damage_coeff = list(BRUTE = 0.4, BURN = 0.4, TOX = 0.4, CLONE = 0.4, STAMINA = 0, OXY = 0.4) playstyle_string = "As a protector type you cause your summoner to leash to you instead of you leashing to them and have two modes; Combat Mode, where you do and take medium damage, and Protection Mode, where you do and take almost no damage, but move slightly slower." @@ -33,9 +31,9 @@ cooldown = world.time + 10 if(toggle) cut_overlays() - melee_damage_lower = initial(melee_damage_lower) - melee_damage_upper = initial(melee_damage_upper) - speed = initial(speed) + melee_damage_lower = 15 + melee_damage_upper = 15 + speed = 0 damage_coeff = list(BRUTE = 0.4, BURN = 0.4, TOX = 0.4, CLONE = 0.4, STAMINA = 0, OXY = 0.4) to_chat(src, "You switch to combat mode.") toggle = FALSE @@ -44,8 +42,8 @@ if(namedatum) shield_overlay.color = namedatum.colour add_overlay(shield_overlay) - melee_damage_lower = 2 - melee_damage_upper = 2 + melee_damage_lower = 5 + melee_damage_upper = 5 speed = 1 damage_coeff = list(BRUTE = 0.05, BURN = 0.05, TOX = 0.05, CLONE = 0.05, STAMINA = 0, OXY = 0.05) //damage? what's damage? to_chat(src, "You switch to protection mode.") diff --git a/code/modules/mob/living/simple_animal/guardian/types/ranged.dm b/code/modules/mob/living/simple_animal/guardian/types/ranged.dm index 5adcc8b292..0e8f632dbd 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/ranged.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/ranged.dm @@ -16,8 +16,7 @@ ranged_cooldown_time = 1 //fast! projectilesound = 'sound/effects/hit_on_shattered_glass.ogg' ranged = 1 - range = 13 - playstyle_string = "As a ranged type, you have only light damage resistance, but are capable of spraying shards of crystal at incredibly high speed. You can also deploy surveillance snares to monitor enemy movement. Finally, you can switch to scout mode, in which you can't attack, but can move without limit." + playstyle_string = "As a ranged type, you have 10% damage reduction, but are capable of spraying shards of crystal at incredibly high speed. You can also deploy surveillance snares to monitor enemy movement. Finally, you can switch to scout mode, in which you can't attack, but can move without limit." magic_fluff_string = "..And draw the Sentinel, an alien master of ranged combat." tech_fluff_string = "Boot sequence complete. Ranged combat modules active. Holoparasite swarm online." carp_fluff_string = "CARP CARP CARP! Caught one, it's a ranged carp. This fishy can watch people pee in the ocean." @@ -36,7 +35,7 @@ obj_damage = initial(obj_damage) environment_smash = initial(environment_smash) alpha = 255 - range = initial(range) + range = 13 to_chat(src, "You switch to combat mode.") toggle = FALSE else diff --git a/code/modules/mob/living/simple_animal/guardian/types/standard.dm b/code/modules/mob/living/simple_animal/guardian/types/standard.dm index 4edd9d9e41..2285167df5 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/standard.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/standard.dm @@ -3,9 +3,9 @@ melee_damage_lower = 20 melee_damage_upper = 20 obj_damage = 80 - next_move_modifier = 0.8 //attacks 20% faster + next_move_modifier = 0.5 //attacks 50% faster environment_smash = ENVIRONMENT_SMASH_WALLS - playstyle_string = "As a standard type you have no special abilities, but have a high damage resistance and a powerful attack capable of smashing through walls." + playstyle_string = "As a standard type you have no special abilities, but take half damage and have powerful attack capable of smashing through walls." magic_fluff_string = "..And draw the Assistant, faceless and generic, but never to be underestimated." tech_fluff_string = "Boot sequence complete. Standard combat modules loaded. Holoparasite swarm online." carp_fluff_string = "CARP CARP CARP! You caught one! It's really boring and standard. Better punch some walls to ease the tension." diff --git a/code/modules/mob/living/simple_animal/guardian/types/support.dm b/code/modules/mob/living/simple_animal/guardian/types/support.dm index 794683e69f..8bf1874d84 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/support.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/support.dm @@ -2,11 +2,8 @@ /mob/living/simple_animal/hostile/guardian/healer a_intent = INTENT_HARM friendly = "heals" - speed = 0 damage_coeff = list(BRUTE = 0.7, BURN = 0.7, TOX = 0.7, CLONE = 0.7, STAMINA = 0, OXY = 0.7) - melee_damage_lower = 15 - melee_damage_upper = 15 - playstyle_string = "As a support type, you may toggle your basic attacks to a healing mode. In addition, Alt-Clicking on an adjacent object or mob will warp them to your bluespace beacon after a short delay." + playstyle_string = "As a support type, you have 30% damage reduction and may toggle your basic attacks to a healing mode. In addition, Alt-Clicking on an adjacent object or mob will warp them to your bluespace beacon after a short delay." magic_fluff_string = "..And draw the CMO, a potent force of life... and death." carp_fluff_string = "CARP CARP CARP! You caught a support carp. It's a kleptocarp!" tech_fluff_string = "Boot sequence complete. Support modules active. Holoparasite swarm online." diff --git a/code/modules/mob/living/taste.dm b/code/modules/mob/living/taste.dm index 282fe0a716..8b0c54653c 100644 --- a/code/modules/mob/living/taste.dm +++ b/code/modules/mob/living/taste.dm @@ -43,7 +43,7 @@ .=..() if ((from.pH > 12.5) || (from.pH < 1.5)) to_chat(src, "You taste chemical burns!") - T.adjustTongueLoss(src, 4) + T.applyOrganDamage(5) if(istype(T, /obj/item/organ/tongue/cybernetic)) to_chat(src, "Your tongue moves on it's own in response to the liquid.") say("The pH is appropriately [round(from.pH, 1)].") @@ -52,13 +52,13 @@ switch(from.pH) if(11.5 to INFINITY) to_chat(src, "You taste a strong alkaline flavour!") - T.adjustTongueLoss(src, 1) + T.applyOrganDamage(1) if(8.5 to 11.5) to_chat(src, "You taste a sort of soapy tone in the mixture.") if(2.5 to 5.5) to_chat(src, "You taste a sort of acid tone in the mixture.") if(-INFINITY to 2.5) to_chat(src, "You taste a strong acidic flavour!") - T.adjustTongueLoss(src, 1) + T.applyOrganDamage(1) #undef DEFAULT_TASTE_SENSITIVITY diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index f1602f429c..bd3c813b12 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -452,22 +452,28 @@ reset_perspective(null) unset_machine() +GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0) + //suppress the .click/dblclick macros so people can't use them to identify the location of items or aimbot /mob/verb/DisClick(argu = null as anything, sec = "" as text, number1 = 0 as num , number2 = 0 as num) set name = ".click" set hidden = TRUE set category = null - var/msg = "[key_name_admin(src)]([ADMIN_KICK(src)]) attempted to use the .click macro!" - log_admin(msg) - message_admins(msg) + if(GLOB.exploit_warn_spam_prevention < world.time) + var/msg = "[key_name_admin(src)]([ADMIN_KICK(src)]) attempted to use the .click macro!" + log_admin(msg) + message_admins(msg) + GLOB.exploit_warn_spam_prevention = world.time + 10 /mob/verb/DisDblClick(argu = null as anything, sec = "" as text, number1 = 0 as num , number2 = 0 as num) set name = ".dblclick" set hidden = TRUE set category = null - var/msg = "[key_name_admin(src)]([ADMIN_KICK(src)]) attempted to use the .dblclick macro!" - log_admin(msg) - message_admins(msg) + if(GLOB.exploit_warn_spam_prevention < world.time) + var/msg = "[key_name_admin(src)]([ADMIN_KICK(src)]) attempted to use the .dblclick macro!" + log_admin(msg) + message_admins(msg) + GLOB.exploit_warn_spam_prevention = world.time + 10 /mob/Topic(href, href_list) if(href_list["mach_close"]) @@ -528,7 +534,12 @@ return if(isAI(M)) return - show_inv(usr) + +/mob/MouseDrop_T(atom/dropping, atom/user) + . = ..() + if(ismob(dropping) && dropping != user) + var/mob/M = dropping + M.show_inv(user) /mob/proc/is_muzzled() return 0 @@ -799,6 +810,9 @@ /mob/proc/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE) return +/mob/proc/canUseStorage() + return FALSE + /mob/proc/faction_check_mob(mob/target, exact_match) if(exact_match) //if we need an exact match, we need to do some bullfuckery. var/list/faction_src = faction.Copy() diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 5703e2190b..6bbe512776 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -75,7 +75,7 @@ O.setOxyLoss(getOxyLoss(), 0) O.setCloneLoss(getCloneLoss(), 0) O.adjustFireLoss(getFireLoss(), 0) - O.setBrainLoss(getBrainLoss(), 0) + O.setOrganLoss(ORGAN_SLOT_BRAIN, getOrganLoss(ORGAN_SLOT_BRAIN), 0) O.adjustStaminaLoss(getStaminaLoss(), 0)//CIT CHANGE - makes monkey transformations inherit stamina O.updatehealth() O.radiation = radiation @@ -236,7 +236,7 @@ O.setOxyLoss(getOxyLoss(), 0) O.setCloneLoss(getCloneLoss(), 0) O.adjustFireLoss(getFireLoss(), 0) - O.setBrainLoss(getBrainLoss(), 0) + O.setOrganLoss(ORGAN_SLOT_BRAIN, getOrganLoss(ORGAN_SLOT_BRAIN), 0) O.adjustStaminaLoss(getStaminaLoss(), 0)//CIT CHANGE - makes monkey transformations inherit stamina O.updatehealth() O.radiation = radiation diff --git a/code/modules/ninja/suit/shoes.dm b/code/modules/ninja/suit/shoes.dm index 115b14b63b..1bda62e064 100644 --- a/code/modules/ninja/suit/shoes.dm +++ b/code/modules/ninja/suit/shoes.dm @@ -2,7 +2,6 @@ name = "ninja shoes" desc = "A pair of running shoes. Excellent for running and even better for smashing skulls." icon_state = "s-ninja" - item_state = "secshoes" permeability_coefficient = 0.01 clothing_flags = NOSLIP resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF diff --git a/code/modules/oracle_ui/README.md b/code/modules/oracle_ui/README.md new file mode 100644 index 0000000000..bc96eb1f51 --- /dev/null +++ b/code/modules/oracle_ui/README.md @@ -0,0 +1,233 @@ +# `/datum/oracle_ui` + +This datum is a replacement for tgui which does not use any Node.js dependencies, and works entirely through raw HTML, JS and CSS. It's designed to be reasonably easy to port something from tgui to oracle_ui. + +### How to create a UI + +For this example, we're going to port the disposals bin from tgui to oracle_ui. + +#### Step 1 + +In order to create a UI, you will first need to create an instance of `/datum/oracle_ui` or one of its subclasses, in this case `/datum/oracle_ui/themed/nano`. + +You need to pass in `src`, the width of the window, the height of the window, and the template to render from. You can optionally set some flags to disallow window resizing and whether to automatically refresh the UI. + +`code/modules/recycling/disposal-unit.dm` +```dm +/obj/machinery/disposal/bin/Initialize(mapload, obj/structure/disposalconstruct/make_from) + . = ..() + ui = new /datum/oracle_ui/themed/nano(src, 330, 190, "disposal_bin") + ui.auto_refresh = TRUE + ui.can_resize = FALSE +``` + +#### Step 2 + +You will now need to make a template in `html/oracle_ui/content/{template_name}`. + +Values defined as `@{value}` will get replaced at runtime by oracle_ui. + +`html/oracle_ui/content/disposal_bin/index.html` +```html +
+
+ State: +
@{full_pressure}
+
+
+ Pressure: +
+
+
+
@{per}
+
+
+
+
+ Handle: +
@{flush}
+
+
+ Eject: +
@{contents}
+
+
+ Compressor: +
@{pressure_charging}
+
+
+``` + +#### Step 3 + +Now you need to implement the methods that provide data to oracle_ui. `oui_data` can be adapted from the `ui_data` proc that tgui uses. + +The `act` proc generates a hyperlink that will result in `oui_act` getting called on your object when clicked. The `class` argument defines a css class to be added to the hyperlink, and disabled determines whether the hyperlink will be disabled or not. + +Calling `soft_update_fields` will result in the UI being updated on all clients, which is useful when the object changes state. + +`code/modules/recycling/disposal-unit.dm` +```dm +/obj/machinery/disposal/bin/oui_data(mob/user) + var/list/data = list() + data["flush"] = flush ? ui.act("Disengage", user, "handle-0", class="active") : ui.act("Engage", user, "handle-1") + data["full_pressure"] = full_pressure ? "Ready" : (pressure_charging ? "Pressurizing" : "Off") + data["pressure_charging"] = pressure_charging ? ui.act("Turn Off", user, "pump-0", class="active", disabled=full_pressure) : ui.act("Turn On", user, "pump-1", disabled=full_pressure) + var/per = full_pressure ? 100 : Clamp(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 99) + data["per"] = "[round(per, 1)]%" + data["contents"] = ui.act("Eject Contents", user, "eject", disabled=contents.len < 1) + data["isai"] = isAI(user) + return data +/obj/machinery/disposal/bin/oui_act(mob/user, action, list/params) + if(..()) + return + switch(action) + if("handle-0") + flush = FALSE + update_icon() + . = TRUE + if("handle-1") + if(!panel_open) + flush = TRUE + update_icon() + . = TRUE + if("pump-0") + if(pressure_charging) + pressure_charging = FALSE + update_icon() + . = TRUE + if("pump-1") + if(!pressure_charging) + pressure_charging = TRUE + update_icon() + . = TRUE + if("eject") + eject() + . = TRUE + ui.soft_update_fields() +``` + +#### Step 4 + +You now need to hook in and ensure oracle_ui is invoked upon clicking. `render` should be used to open the UI for a user, typically on click. + +`code/modules/recycling/disposal-unit.dm` +```dm +/obj/machinery/disposal/bin/ui_interact(mob/user, state) + if(stat & BROKEN) + return + if(user.loc == src) + to_chat(user, "You cannot reach the controls from inside!") + return + ui.render(user) +``` + +#### Done + +![gif](https://user-images.githubusercontent.com/202160/37561879-1bb9179e-2a52-11e8-902c-80e6e6df7204.gif) + +You should have a functional UI at this point. Some additional odds and ends can be discovered throughout `code/modules/recycling/disposal-unit.dm`. For a full diff of the changes made to it, refer to [the original pull request on GitHub](https://github.com/OracleStation/OracleStation/pull/702/files#diff-4b6c20ec7d37222630e7524d9577e230). + +### API Reference + +#### `/datum/oracle_ui` + +The main datum which handles the UI. + +##### `get_content(mob/target)` +Returns the HTML that should be displayed for a specified target mob. Calls `oui_getcontent` on the datasource to get the return value. *This proc is not used in the themed subclass.* + +##### `can_view(mob/target)` +Returns whether the specified target mob can view the UI. Calls `oui_canview` on the datasource to get the return value. + +##### `test_viewer(mob/target, updating)` +Tests whether the client is valid and can view the UI. If updating is TRUE, checks to see if they still have the UI window open. + +##### `render(mob/target, updating = FALSE)` +Opens the UI for a target mob, sending HTML. If updating is TRUE, will only do it to clients which still have the window open. + +##### `render_all()` +Does the above, but for all viewers and with updating set to TRUE. + +##### `close(mob/target)` +Closes the UI for the specified target mob. + +##### `close_all()` +Does the above, but for all viewers. + +##### `check_view(mob/target)` +Checks if the specified target mob can view the UI, and if they can't closes their UI + +##### `check_view_all()` +Does the above, but for all viewers. + +##### `call_js(mob/target, js_func, list/parameters = list())` +Invokes `js_func` in the UI of the specified target mob with the specified parameters. + +##### `call_js_all(js_func, list/parameters = list()))` +Does the above, but for all viewers. + +##### `steal_focus(mob/target)` +Causes the UI to steal focus for the specified target mob. + +##### `steal_focus_all()` +Does the above, but for all viewers. + +##### `flash(mob/target, times = -1)` +Causes the UI to flash for the specified target mob the specified number of times, the default keeps the element flashing until focused. + +##### `flash_all()` +Does the above, but for all viewers. + +##### `href(mob/user, action, list/parameters = list())` +Generates a href for the specified user which will invoke `oui_act` on the datasource with the specified action and parameters. + +#### `/datum/oracle_ui/themed` + +A subclass which supports templating and theming. + +##### `get_file(path)` +Loads a file from disk and returns the contents. Caches files loaded from disk for you. + +##### `get_content_file(filename)` +Loads a file from the current content folder and returns the contents. + +##### `get_themed_file(filename)` +Loads a file from the current theme folder and returns the contents. + +##### `process_template(template, variables)` +Processes a template and populates it with the provided variables. + +##### `get_inner_content(mob/target)` +Returns the templated content to be inserted into the main template for the specified target mob. + +##### `soft_update_fields()` +For all viewers, updates the fields in the template via the `updateFields` javaScript function. + +##### `soft_update_all()` +For all viewers, updates the content body in the template via the `replaceContent` javaScript function. + +##### `change_page(var/newpage)` +Changes the template to use to draw the page and forces an update to all viewers + +##### `act(label, mob/user, action, list/parameters = list(), class = "", disabled = FALSE` +Returns a fully formatted hyperlink for the specified user. `label` will be the hyperlink label, `action` and `parameters` are what will be passed to `oui_act`, `class` is any CSS classes to apply to the hyperlink and `disabled` will disable the hyperlink. + +#### `/datum` + +Functions built into all objects to support oracle_ui. There are default implementations for most major superclasses. + +##### `oui_canview(mob/user)` +Returns whether the specified user view the UI at this time. + +##### `oui_getcontent(mob/user)` +Returns the raw HTML to be sent to the specified user. *This proc is not used in the themed subclass of oracle_ui.* + +##### `oui_data(mob/user)` +Returns templating data for the specified user. *This proc is only used in the themed subclass of oracle_ui.* + +##### `oui_data_debug(mob/user)` +Returns the above, but JSON-encoded and escaped, for copy pasting into the web IDE. *This proc is only used for debugging purposes.* + +##### `oui_act(mob/user, action, list/params)` +Called when a hyperlink is clicked in the UI. diff --git a/code/modules/oracle_ui/assets.dm b/code/modules/oracle_ui/assets.dm new file mode 100644 index 0000000000..5d26d80a81 --- /dev/null +++ b/code/modules/oracle_ui/assets.dm @@ -0,0 +1,8 @@ +/datum/asset/simple/oui_theme_nano + assets = list( + // JavaScript + "sui-nano-common.js" = 'html/oracle_ui/themes/nano/sui-nano-common.js', + "sui-nano-jquery.min.js" = 'html/oracle_ui/themes/nano/sui-nano-jquery.min.js', + // Stylesheets + "sui-nano-common.css" = 'html/oracle_ui/themes/nano/sui-nano-common.css', + ) diff --git a/code/modules/oracle_ui/hookup_procs.dm b/code/modules/oracle_ui/hookup_procs.dm new file mode 100644 index 0000000000..e6038744c1 --- /dev/null +++ b/code/modules/oracle_ui/hookup_procs.dm @@ -0,0 +1,44 @@ +/datum/proc/oui_canview(mob/user) + return TRUE + +/datum/proc/oui_getcontent(mob/user) + return "Default Implementation" + +/datum/proc/oui_canuse(mob/user) + if(isobserver(user) && !user.has_unlimited_silicon_privilege) + return FALSE + return oui_canview(user) + +/datum/proc/oui_data(mob/user) + return list() + +/datum/proc/oui_data_debug(mob/user) + return html_encode(json_encode(oui_data(user))) + +/datum/proc/oui_act(mob/user, action, list/params) + // No Implementation + +/atom/oui_canview(mob/user) + if(isobserver(user)) + return TRUE + if(user.incapacitated()) + return FALSE + if(isturf(src.loc) && Adjacent(user)) + return TRUE + return FALSE + +/obj/item/oui_canview(mob/user) + if(src.loc == user) + return src in user.held_items + return ..() + +/obj/machinery/oui_canview(mob/user) + if(user.has_unlimited_silicon_privilege) + return TRUE + if(!can_interact()) + return FALSE + if(iscyborg(user)) + return can_see(user, src, 7) + if(isAI(user)) + return GLOB.cameranet.checkTurfVis(get_turf_pixel(src)) + return ..() diff --git a/code/modules/oracle_ui/oracle_ui.dm b/code/modules/oracle_ui/oracle_ui.dm new file mode 100644 index 0000000000..5e8d6b9c7b --- /dev/null +++ b/code/modules/oracle_ui/oracle_ui.dm @@ -0,0 +1,134 @@ +/datum/oracle_ui + var/width = 512 + var/height = 512 + var/can_close = TRUE + var/can_minimize = FALSE + var/can_resize = TRUE + var/titlebar = TRUE + var/window_id = null + var/viewers[0] + var/auto_check_view = TRUE + var/auto_refresh = FALSE + var/atom/datasource = null + var/datum/asset/assets = null + +/datum/oracle_ui/New(atom/n_datasource, n_width = 512, n_height = 512, n_assets = null) + datasource = n_datasource + window_id = REF(src) + width = n_width + height = n_height + +/datum/oracle_ui/Destroy() + close_all() + if(src.datum_flags & DF_ISPROCESSING) + STOP_PROCESSING(SSobj, src) + return ..() + +/datum/oracle_ui/process() + if(auto_check_view) + check_view_all() + if(auto_refresh) + render_all() + +/datum/oracle_ui/proc/get_content(mob/target) + return call(datasource, "oui_getcontent")(target) + +/datum/oracle_ui/proc/can_view(mob/target) + return call(datasource, "oui_canview")(target) + +/datum/oracle_ui/proc/test_viewer(mob/target, updating) + //If the target is null or does not have a client, remove from viewers and return + if(!target | !target.client | !can_view(target)) + viewers -= target + if(viewers.len < 1 && (src.datum_flags & DF_ISPROCESSING)) + STOP_PROCESSING(SSobj, src) //No more viewers, stop polling + close(target) + return FALSE + //If this is an update, and they have closed the window, remove from viewers and return + if(updating && winget(target, window_id, "is-visible") != "true") + viewers -= target + if(viewers.len < 1 && (src.datum_flags & DF_ISPROCESSING)) + STOP_PROCESSING(SSobj, src) //No more viewers, stop polling + return FALSE + return TRUE + +/datum/oracle_ui/proc/render(mob/target, updating = FALSE) + set waitfor = FALSE //Makes this an async call + if(!can_view(target)) + return + //Check to see if they have the window open still if updating + if(updating && !test_viewer(target, updating)) + return + //Send assets + if(!updating && assets) + assets.send(target) + //Add them to the viewers if they aren't there already + viewers |= target + if(!(src.datum_flags & DF_ISPROCESSING) && (auto_refresh | auto_check_view)) + START_PROCESSING(SSobj, src) //Start processing to poll for viewability + //Send the content + if(updating) + target << output(get_content(target), "[window_id].browser") + else + target << browse(get_content(target), "window=[window_id];size=[width]x[height];can_close=[can_close];can_minimize=[can_minimize];can_resize=[can_resize];titlebar=[titlebar];focus=false;") + steal_focus(target) + +/datum/oracle_ui/proc/render_all() + for(var/viewer in viewers) + render(viewer, TRUE) + +/datum/oracle_ui/proc/close(mob/target) + if(target && target.client) + target << browse(null, "window=[window_id]") + +/datum/oracle_ui/proc/close_all() + for(var/viewer in viewers) + close(viewer) + viewers = list() + +/datum/oracle_ui/proc/check_view_all() + for(var/viewer in viewers) + check_view(viewer) + +/datum/oracle_ui/proc/check_view(mob/target) + set waitfor = FALSE //Makes this an async call + if(!test_viewer(target, TRUE)) + close(target) + +/datum/oracle_ui/proc/call_js(mob/target, js_func, list/parameters = list()) + set waitfor = FALSE //Makes this an async call + if(!test_viewer(target, TRUE)) + return + target << output(list2params(parameters),"[window_id].browser:[js_func]") + +/datum/oracle_ui/proc/call_js_all(js_func, list/parameters = list()) + for(var/viewer in viewers) + call_js(viewer, js_func, parameters) + +/datum/oracle_ui/proc/steal_focus(mob/target) + set waitfor = FALSE //Makes this an async call + winset(target, "[window_id]","focus=true") + +/datum/oracle_ui/proc/steal_focus_all() + for(var/viewer in viewers) + steal_focus(viewer) + +/datum/oracle_ui/proc/flash(mob/target, times = -1) + set waitfor = FALSE //Makes this an async call + winset(target, "[window_id]","flash=[times]") + +/datum/oracle_ui/proc/flash_all(times = -1) + for(var/viewer in viewers) + flash(viewer, times) + +/datum/oracle_ui/proc/href(mob/user, action, list/parameters = list()) + var/params_string = replacetext(list2params(parameters),"&",";") + return "?src=[REF(src)];sui_action=[action];sui_user=[REF(user)];[params_string]" + +/datum/oracle_ui/Topic(href, parameters) + var/action = parameters["sui_action"] + var/mob/current_user = locate(parameters["sui_user"]) + if(!call(datasource, "oui_canuse")(current_user)) + return + if(datasource) + call(datasource, "oui_act")(current_user, action, parameters); diff --git a/code/modules/oracle_ui/themed.dm b/code/modules/oracle_ui/themed.dm new file mode 100644 index 0000000000..56b82c2647 --- /dev/null +++ b/code/modules/oracle_ui/themed.dm @@ -0,0 +1,82 @@ +/datum/oracle_ui/themed + var/theme = "" + var/content_root = "" + var/current_page = "index.html" + var/root_template = "" + +/datum/oracle_ui/themed/New(atom/n_datasource, n_width = 512, n_height = 512, n_content_root = "") + root_template = get_themed_file("index.html") + content_root = n_content_root + return ..(n_datasource, n_width, n_height, get_asset_datum(/datum/asset/simple/oui_theme_nano)) + +/datum/oracle_ui/themed/process() + if(auto_check_view) + check_view_all() + if(auto_refresh) + soft_update_fields() + +GLOBAL_LIST_EMPTY(oui_template_variables) +GLOBAL_LIST_EMPTY(oui_file_cache) + +/datum/oracle_ui/themed/proc/get_file(path) + if(GLOB.oui_file_cache[path]) + return GLOB.oui_file_cache[path] + else if(fexists(path)) + var/data = file2text(path) + GLOB.oui_file_cache[path] = data + return data + else + var/errormsg = "MISSING PATH '[path]'" +#ifndef UNIT_TESTS + log_world(errormsg) //Because Travis absolutely hates these procs +#endif + return errormsg + +/datum/oracle_ui/themed/proc/get_content_file(filename) + return get_file("./html/oracle_ui/content/[content_root]/[filename]") + +/datum/oracle_ui/themed/proc/get_themed_file(filename) + return get_file("./html/oracle_ui/themes/[theme]/[filename]") + +/datum/oracle_ui/themed/proc/process_template(template, variables) + var/regex/pattern = regex("\\@\\{(\\w+)\\}","gi") + GLOB.oui_template_variables = variables + var/replaced = pattern.Replace(template, /proc/oui_process_template_replace) + GLOB.oui_template_variables = null + return replaced + +/proc/oui_process_template_replace(match, group1) + var/value = GLOB.oui_template_variables[group1] + return "[value]" + +/datum/oracle_ui/themed/proc/get_inner_content(mob/target) + var/list/data = call(datasource, "oui_data")(target) + return process_template(get_content_file(current_page), data) + +/datum/oracle_ui/themed/get_content(mob/target) + var/list/template_data = list("title" = datasource.name, "body" = get_inner_content(target)) + return process_template(root_template, template_data) + +/datum/oracle_ui/themed/proc/soft_update_fields() + for(var/viewer in viewers) + var/json = json_encode(call(datasource, "oui_data")(viewer)) + call_js(viewer, "updateFields", list(json)) + +/datum/oracle_ui/themed/proc/soft_update_all() + for(var/viewer in viewers) + call_js(viewer, "replaceContent", list(get_inner_content(viewer))) + +/datum/oracle_ui/themed/proc/change_page(newpage) + if(newpage == current_page) + return + current_page = newpage + render_all() + +/datum/oracle_ui/themed/proc/act(label, mob/user, action, list/parameters = list(), class = "", disabled = FALSE) + if(disabled) + return "[label]" + else + return "[label]" + +/datum/oracle_ui/themed/nano + theme = "nano" diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm index 907fccdc5b..ccbcf34d8c 100644 --- a/code/modules/paperwork/filingcabinet.dm +++ b/code/modules/paperwork/filingcabinet.dm @@ -67,6 +67,9 @@ /obj/structure/filingcabinet/ui_interact(mob/user) . = ..() + if(isobserver(user)) + return + if(contents.len <= 0) to_chat(user, "[src] is empty.") return diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 37877ffb09..059a42bb36 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -31,6 +31,7 @@ var/spam_flag = 0 var/contact_poison // Reagent ID to transfer on contact var/contact_poison_volume = 0 + var/datum/oracle_ui/ui = null /obj/item/paper/pickup(user) @@ -40,16 +41,40 @@ if(!istype(G) || G.transfer_prints) H.reagents.add_reagent(contact_poison,contact_poison_volume) contact_poison = null + ui.check_view_all() ..() +/obj/item/paper/dropped(mob/user) + ui.check_view(user) + return ..() + /obj/item/paper/Initialize() . = ..() pixel_y = rand(-8, 8) pixel_x = rand(-9, 9) + ui = new /datum/oracle_ui(src, 420, 600, get_asset_datum(/datum/asset/spritesheet/simple/paper)) + ui.can_resize = FALSE update_icon() updateinfolinks() +/obj/item/paper/oui_getcontent(mob/target) + if(!target.is_literate()) + return "[name][stars(info)]
[stamps]" + else if(istype(target.get_active_held_item(), /obj/item/pen) | istype(target.get_active_held_item(), /obj/item/toy/crayon)) + return "[name][info_links]
[stamps]" + else + return "[name][info]
[stamps]" + +/obj/item/paper/oui_canview(mob/target) + if(check_rights_for(target.client, R_FUN)) //Allows admins to view faxes + return TRUE + if(isAI(target)) + var/mob/living/silicon/ai/ai = target + return get_dist(src, ai.current) < 2 + if(iscyborg(target)) + return get_dist(src, target) < 2 + return ..() /obj/item/paper/update_icon() @@ -65,20 +90,13 @@ /obj/item/paper/examine(mob/user) ..() to_chat(user, "Alt-click to fold it.") - - var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/simple/paper) - assets.send(user) - - if(in_range(user, src) || isobserver(user)) - if(user.is_literate()) - user << browse("[name][info]
[stamps]", "window=[name]") - onclose(user, "[name]") - else - user << browse("[name][stars(info)]
[stamps]", "window=[name]") - onclose(user, "[name]") + if(oui_canview(user)) + ui.render(user) else to_chat(user, "You're too far away to read it!") +/obj/item/paper/proc/show_content(mob/user) + user.examinate(src) /obj/item/paper/verb/rename() set name = "Rename paper" @@ -98,7 +116,7 @@ if((loc == usr && usr.stat == CONSCIOUS)) name = "paper[(n_name ? text("- '[n_name]'") : null)]" add_fingerprint(usr) - + ui.render_all() /obj/item/paper/suicide_act(mob/user) user.visible_message("[user] scratches a grid on [user.p_their()] wrist with the paper! It looks like [user.p_theyre()] trying to commit sudoku...") @@ -108,7 +126,7 @@ spam_flag = FALSE /obj/item/paper/attack_self(mob/user) - user.examinate(src) + show_content(user) if(rigged && (SSevents.holidays && SSevents.holidays[APRIL_FOOLS])) if(!spam_flag) spam_flag = TRUE @@ -123,11 +141,9 @@ else //cyborg or AI not seeing through a camera dist = get_dist(src, user) if(dist < 2) - usr << browse("[name][info]
[stamps]", "window=[name]") - onclose(usr, "[name]") + show_content(user) else - usr << browse("[name][stars(info)]
[stamps]", "window=[name]") - onclose(usr, "[name]") + to_chat(user, "You can't quite see it.") /obj/item/paper/proc/addtofield(id, text, links = 0) @@ -173,6 +189,7 @@ for(var/i in 1 to min(fields, 15)) addtofield(i, "write", 1) info_links = info_links + "write" + ui.render_all() /obj/item/paper/proc/clearpaper() @@ -274,7 +291,7 @@ else info += t // Oh, he wants to edit to the end of the file, let him. updateinfolinks() - usr << browse("[name][info_links]
[stamps]", "window=[name]") // Update the window + show_content(usr) update_icon() @@ -289,7 +306,7 @@ if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) if(user.is_literate()) - user << browse("[name][info_links]
[stamps]", "window=[name]") + show_content(user) return else to_chat(user, "You don't know how to read or write.") @@ -312,6 +329,7 @@ add_overlay(stampoverlay) to_chat(user, "You stamp the paper with your rubber stamp.") + ui.render_all() if(P.is_hot()) if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(10)) diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index a5900aa196..5bcb7a60df 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -164,7 +164,7 @@ /obj/item/pen/sleepy/Initialize() . = ..() create_reagents(45, OPENCONTAINER) - reagents.add_reagent("chloralhydratedelayed", 20) + reagents.add_reagent("chloralhydrate", 20) reagents.add_reagent("mutetoxin", 15) reagents.add_reagent("tirizene", 10) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index d837f0d420..5fc29cb813 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -802,8 +802,6 @@ if(!ui) ui = new(user, src, ui_key, "apc", name, 535, 515, master_ui, state) ui.open() - if(ui) - ui.set_autoupdate(state = (failure_timer ? 1 : 0)) /obj/machinery/power/apc/ui_data(mob/user) var/list/data = list( diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index f82f0ee8e6..8d99cbb485 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -323,6 +323,8 @@ var/BR = brightness var/PO = bulb_power var/CO = bulb_colour + if(color) + CO = color var/area/A = get_area(src) if (A && A.fire) CO = bulb_emergency_colour @@ -360,6 +362,9 @@ else removeStaticPower(static_power_used, STATIC_LIGHT) +/obj/machinery/light/update_atom_colour() + . = ..() + update() /obj/machinery/light/process() if (!cell) diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index 4159d9898a..974b210648 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -19,6 +19,7 @@ circuit = /obj/item/circuitboard/machine/rad_collector var/obj/item/tank/internals/plasma/loaded_tank = null var/stored_power = 0 + var/last_push var/active = 0 var/locked = FALSE var/drainratio = 1 @@ -61,9 +62,9 @@ loaded_tank.air_contents.gases[/datum/gas/oxygen] -= gasdrained loaded_tank.air_contents.gases[/datum/gas/carbon_dioxide] += gasdrained*2 GAS_GARBAGE_COLLECT(loaded_tank.air_contents.gases) - var/bitcoins_mined = RAD_COLLECTOR_OUTPUT - SSresearch.science_tech.add_point_type(TECHWEB_POINT_TYPE_DEFAULT, bitcoins_mined*RAD_COLLECTOR_MINING_CONVERSION_RATE) - stored_power-=bitcoins_mined + SSresearch.science_tech.add_point_type(TECHWEB_POINT_TYPE_DEFAULT, stored_power*RAD_COLLECTOR_MINING_CONVERSION_RATE) + last_push = stored_power + stored_power = 0 /obj/machinery/power/rad_collector/interact(mob/user) if(anchored) @@ -170,9 +171,9 @@ . = ..() if(active) if(!bitcoinmining) - to_chat(user, "[src]'s display states that it has stored [DisplayPower(stored_power)], and processing [DisplayPower(RAD_COLLECTOR_OUTPUT)].") + to_chat(user, "[src]'s display states that it has stored [DisplayPower(stored_power)], and is processing [DisplayPower((RAD_COLLECTOR_OUTPUT)*((60 SECONDS)/SSmachines.wait))] per minute.
The plasma within it's tank is being irradiated into tritium.
") else - to_chat(user, "[src]'s display states that it has stored a total of [stored_power*RAD_COLLECTOR_MINING_CONVERSION_RATE], and producing [RAD_COLLECTOR_OUTPUT*RAD_COLLECTOR_MINING_CONVERSION_RATE] research points per minute.") + to_chat(user, "[src]'s display states that it's producing a total of [(last_push*RAD_COLLECTOR_MINING_CONVERSION_RATE)*((60 SECONDS)/SSmachines.wait)] research points per minute.
The tritium and oxygen within it's tank is being combusted into carbon dioxide.
") else if(!bitcoinmining) to_chat(user,"[src]'s display displays the words: \"Power production mode. Please insert Plasma. Use a multitool to change production modes.\"") diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index bc05e784a4..35710f8d61 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -84,16 +84,18 @@ /obj/singularity/attack_tk(mob/user) if(iscarbon(user)) var/mob/living/carbon/C = user + log_game("[key_name(C)] has been disintegrated by attempting to telekenetically grab a singularity.
") C.visible_message("[C]'s head begins to collapse in on itself!", "Your head feels like it's collapsing in on itself! This was really not a good idea!", "You hear something crack and explode in gore.") var/turf/T = get_turf(C) for(var/i in 1 to 3) C.apply_damage(30, BRUTE, BODY_ZONE_HEAD) new /obj/effect/gibspawner/generic(T) sleep(1) - C.ghostize() var/obj/item/bodypart/head/rip_u = C.get_bodypart(BODY_ZONE_HEAD) rip_u.dismember(BURN) //nice try jedi qdel(rip_u) + return + return ..() /obj/singularity/ex_act(severity, target) switch(severity) diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 026c39c6cf..3c151d4e77 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -5,12 +5,21 @@ #define PLASMA_HEAT_PENALTY 15 // Higher == Bigger heat and waste penalty from having the crystal surrounded by this gas. Negative numbers reduce penalty. #define OXYGEN_HEAT_PENALTY 1 #define CO2_HEAT_PENALTY 0.1 -#define NITROGEN_HEAT_MODIFIER -1.5 +#define PLUOXIUM_HEAT_PENALTY -1 +#define TRITIUM_HEAT_PENALTY 10 +#define NITROGEN_HEAT_PENALTY -1.5 +#define BZ_HEAT_PENALTY 5 #define OXYGEN_TRANSMIT_MODIFIER 1.5 //Higher == Bigger bonus to power generation. #define PLASMA_TRANSMIT_MODIFIER 4 +#define BZ_TRANSMIT_MODIFIER -2 + +#define TRITIUM_RADIOACTIVITY_MODIFIER 3 //Higher == Crystal spews out more radiation +#define BZ_RADIOACTIVITY_MODIFIER 5 +#define PLUOXIUM_RADIOACTIVITY_MODIFIER -2 #define N2O_HEAT_RESISTANCE 6 //Higher == Gas makes the crystal more resistant against heat damage. +#define PLUOXIUM_HEAT_RESISTANCE 3 #define POWERLOSS_INHIBITION_GAS_THRESHOLD 0.20 //Higher == Higher percentage of inhibitor gas needed before the charge inertia chain reaction effect starts. #define POWERLOSS_INHIBITION_MOLE_THRESHOLD 20 //Higher == More moles of the gas are needed before the charge inertia chain reaction effect starts. //Scales powerloss inhibition down until this amount of moles is reached @@ -53,6 +62,7 @@ #define SUPERMATTER_EMERGENCY_PERCENT 25 #define SUPERMATTER_DANGER_PERCENT 50 #define SUPERMATTER_WARNING_PERCENT 100 +#define CRITICAL_TEMPERATURE 10000 #define SUPERMATTER_COUNTDOWN_TIME 30 SECONDS @@ -102,6 +112,11 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) var/o2comp = 0 var/co2comp = 0 var/n2ocomp = 0 + var/pluoxiumcomp = 0 + var/tritiumcomp = 0 + var/bzcomp = 0 + + var/pluoxiumbonus = 0 var/combined_gas = 0 var/gasmix_power_ratio = 0 @@ -184,8 +199,6 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) to_chat(H, "You get headaches just from looking at it.") return -#define CRITICAL_TEMPERATURE 10000 - /obj/machinery/power/supermatter_crystal/proc/get_status() var/turf/T = get_turf(src) if(!T) @@ -354,16 +367,24 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) plasmacomp = max(removed.gases[/datum/gas/plasma]/combined_gas, 0) o2comp = max(removed.gases[/datum/gas/oxygen]/combined_gas, 0) co2comp = max(removed.gases[/datum/gas/carbon_dioxide]/combined_gas, 0) + pluoxiumcomp = max(removed.gases[/datum/gas/pluoxium]/combined_gas, 0) + tritiumcomp = max(removed.gases[/datum/gas/tritium]/combined_gas, 0) + bzcomp = max(removed.gases[/datum/gas/bz]/combined_gas, 0) n2ocomp = max(removed.gases[/datum/gas/nitrous_oxide]/combined_gas, 0) n2comp = max(removed.gases[/datum/gas/nitrogen]/combined_gas, 0) - gasmix_power_ratio = min(max(plasmacomp + o2comp + co2comp - n2comp, 0), 1) + if(pluoxiumcomp >= 0.15) + pluoxiumbonus = 1 //makes pluoxium only work at 15%+ + else + pluoxiumbonus = 0 - dynamic_heat_modifier = max((plasmacomp * PLASMA_HEAT_PENALTY)+(o2comp * OXYGEN_HEAT_PENALTY)+(co2comp * CO2_HEAT_PENALTY)+(n2comp * NITROGEN_HEAT_MODIFIER), 0.5) - dynamic_heat_resistance = max(n2ocomp * N2O_HEAT_RESISTANCE, 1) + gasmix_power_ratio = min(max(plasmacomp + o2comp + co2comp + tritiumcomp + bzcomp - pluoxiumcomp - n2comp, 0), 1) - power_transmission_bonus = max((plasmacomp * PLASMA_TRANSMIT_MODIFIER) + (o2comp * OXYGEN_TRANSMIT_MODIFIER), 0) + dynamic_heat_modifier = max((plasmacomp * PLASMA_HEAT_PENALTY) + (o2comp * OXYGEN_HEAT_PENALTY) + (co2comp * CO2_HEAT_PENALTY) + (tritiumcomp * TRITIUM_HEAT_PENALTY) + ((pluoxiumcomp * PLUOXIUM_HEAT_PENALTY) * pluoxiumbonus) + (n2comp * NITROGEN_HEAT_PENALTY) + (bzcomp * BZ_HEAT_PENALTY), 0.5) + dynamic_heat_resistance = max((n2ocomp * N2O_HEAT_RESISTANCE) + ((pluoxiumcomp * PLUOXIUM_HEAT_RESISTANCE) * pluoxiumbonus), 1) + + power_transmission_bonus = max((plasmacomp * PLASMA_TRANSMIT_MODIFIER) + (o2comp * OXYGEN_TRANSMIT_MODIFIER) + (bzcomp * BZ_TRANSMIT_MODIFIER), 0) //more moles of gases are harder to heat than fewer, so let's scale heat damage around them mole_heat_penalty = max(combined_gas / MOLE_HEAT_PENALTY, 0.25) @@ -392,7 +413,9 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) power = max( (removed.temperature * temp_factor / T0C) * gasmix_power_ratio + power, 0) //Total laser power plus an overload if(prob(50)) - radiation_pulse(src, power * (1 + power_transmission_bonus/10)) + radiation_pulse(src, power * (1 + (tritiumcomp * TRITIUM_RADIOACTIVITY_MODIFIER) + ((pluoxiumcomp * PLUOXIUM_RADIOACTIVITY_MODIFIER) * pluoxiumbonus) * (power_transmission_bonus/(10-(bzcomp * BZ_RADIOACTIVITY_MODIFIER))))) // Rad Modifiers BZ(500%), Tritium(300%), and Pluoxium(-200%) + if(bzcomp >= 0.4 && prob(30 * bzcomp)) + src.fire_nuclear_particles() // Start to emit radballs at a maximum of 30% chance per tick var/device_energy = power * REACTION_POWER_MODIFIER @@ -527,11 +550,14 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) /obj/machinery/power/supermatter_crystal/attack_tk(mob/user) if(iscarbon(user)) var/mob/living/carbon/C = user + log_game("[key_name(C)] has been disintegrated by a telekenetic grab on a supermatter crystal.
") to_chat(C, "That was a really dense idea.") - C.ghostize() + C.visible_message("A bright flare of radiation is seen from [C]'s head, shortly before you hear a sickening sizzling!") var/obj/item/organ/brain/rip_u = locate(/obj/item/organ/brain) in C.internal_organs rip_u.Remove(C) qdel(rip_u) + return + return ..() /obj/machinery/power/supermatter_crystal/attack_paw(mob/user) dust_mob(user, cause = "monkey attack") diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index bbad3e08b0..9a5486dfe2 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -132,11 +132,14 @@ /obj/singularity/energy_ball/attack_tk(mob/user) if(iscarbon(user)) var/mob/living/carbon/C = user + log_game("[key_name(C)] has been disintegrated by a telekenetic grab on a tesla ball.
") to_chat(C, "That was a shockingly dumb idea.") + C.visible_message("A bright flare of lightning is seen from [C]'s head, shortly before you hear a sickening sizzling!") var/obj/item/organ/brain/rip_u = locate(/obj/item/organ/brain) in C.internal_organs - C.ghostize(0) + rip_u.Remove(C) qdel(rip_u) - C.death() + return + return ..() /obj/singularity/energy_ball/orbit(obj/singularity/energy_ball/target) if (istype(target)) diff --git a/code/modules/projectiles/ammunition/energy/laser.dm b/code/modules/projectiles/ammunition/energy/laser.dm index 089c79effd..638711e8d4 100644 --- a/code/modules/projectiles/ammunition/energy/laser.dm +++ b/code/modules/projectiles/ammunition/energy/laser.dm @@ -59,6 +59,9 @@ /obj/item/ammo_casing/energy/laser/redtag/hitscan projectile_type = /obj/item/projectile/beam/lasertag/redtag/hitscan +/obj/item/ammo_casing/energy/laser/redtag/hitscan/holy + projectile_type = /obj/item/projectile/beam/lasertag/redtag/hitscan/holy + /obj/item/ammo_casing/energy/xray projectile_type = /obj/item/projectile/beam/xray e_cost = 50 @@ -68,3 +71,9 @@ projectile_type = /obj/item/projectile/beam/mindflayer select_name = "MINDFUCK" fire_sound = 'sound/weapons/laser.ogg' + +/obj/item/ammo_casing/energy/laser/weak + projectile_type = /obj/item/projectile/beam/weak/minigun + e_cost = 10 + fire_sound = 'sound/weapons/gatling.ogg' + click_cooldown_override = 1 diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index 7d71a9acdd..d1b99c0e3c 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -207,6 +207,7 @@ name = "combat shotgun" desc = "A semi automatic shotgun with tactical furniture and a six-shell capacity underneath." icon_state = "cshotgun" + fire_delay = 3 mag_type = /obj/item/ammo_box/magazine/internal/shot/com w_class = WEIGHT_CLASS_HUGE unique_reskin = list("Tatical" = "cshotgun", diff --git a/code/modules/projectiles/guns/energy/energy_gun.dm b/code/modules/projectiles/guns/energy/energy_gun.dm index 54cb9fe5d0..de738c9047 100644 --- a/code/modules/projectiles/guns/energy/energy_gun.dm +++ b/code/modules/projectiles/guns/energy/energy_gun.dm @@ -118,11 +118,11 @@ switch(fail_tick) if(0 to 200) fail_tick += (2*(fail_chance)) - M.rad_act(40) + M.rad_act(400) to_chat(M, "Your [name] feels warmer.") if(201 to INFINITY) SSobj.processing.Remove(src) - M.rad_act(80) + M.rad_act(800) crit_fail = 1 to_chat(M, "Your [name]'s reactor overloads!") diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index cd0dd53d48..bb97f9cf99 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -159,3 +159,65 @@ /obj/item/gun/energy/laser/redtag/hitscan ammo_type = list(/obj/item/ammo_casing/energy/laser/redtag/hitscan) + +/obj/item/gun/energy/laser/redtag/hitscan/chaplain + name = "\improper holy lasrifle" + desc = "A lasrifle from the old Imperium. This one seems to be blessed by techpriests." + icon_state = "LaserAK" + item_state = null + force = 14 + pin = /obj/item/firing_pin/holy + icon = 'modular_citadel/icons/obj/guns/VGguns.dmi' + ammo_x_offset = 4 + ammo_type = list(/obj/item/ammo_casing/energy/laser/redtag/hitscan/holy) + lefthand_file = 'modular_citadel/icons/mob/citadel/guns_lefthand.dmi' + righthand_file = 'modular_citadel/icons/mob/citadel/guns_righthand.dmi' + var/chaplain_spawnable = TRUE + total_mass = TOTAL_MASS_MEDIEVAL_WEAPON + throw_speed = 3 + throw_range = 4 + throwforce = 10 + obj_flags = UNIQUE_RENAME + +/obj/item/gun/energy/laser/redtag/hitscan/chaplain/Initialize() + . = ..() + AddComponent(/datum/component/anti_magic, TRUE, TRUE) + +/obj/item/gun/energy/laser/redtag/hitscan/chaplain/handle_suicide(mob/living/carbon/human/user, mob/living/carbon/human/target, params, bypass_timer) + if(!ishuman(user) || !ishuman(target)) + return + + if(semicd) + return + + if(user == target) + target.visible_message("[user] sticks [src] in [user.p_their()] mouth, ready to pull the trigger...", \ + "You stick [src] in your mouth, ready to pull the trigger...") + else + target.visible_message("[user] points [src] at [target]'s head, ready to pull the trigger...", \ + "[user] points [src] at your head, ready to pull the trigger...") + + semicd = TRUE + + if(!bypass_timer && (!do_mob(user, target, 120) || user.zone_selected != BODY_ZONE_PRECISE_MOUTH)) + if(user) + if(user == target) + user.visible_message("[user] decided not to shoot.") + else if(target && target.Adjacent(user)) + target.visible_message("[user] has decided to spare [target]", "[user] has decided to spare your life!") + semicd = FALSE + return + + semicd = FALSE + + target.visible_message("[user] pulls the trigger!", "[user] pulls the trigger!") + + playsound('sound/weapons/dink.ogg', 30, 1) + + if((iscultist(target)) || (is_servant_of_ratvar(target))) + chambered.BB.damage *= 1500 + + else if(chambered && chambered.BB) + chambered.BB.damage *= 5 + + process_fire(target, user, TRUE, params) diff --git a/code/modules/projectiles/guns/energy/minigun.dm b/code/modules/projectiles/guns/energy/minigun.dm new file mode 100644 index 0000000000..d903cda47c --- /dev/null +++ b/code/modules/projectiles/guns/energy/minigun.dm @@ -0,0 +1,149 @@ +//The ammo/gun is stored in a back slot item +/obj/item/minigunpack2 + name = " Laser Gatling Pack" + desc = "A massive battery pack with an attached laser gatling gun!" + icon = 'icons/obj/guns/minigun.dmi' + icon_state = "holstered" + item_state = "backpack" + lefthand_file = 'icons/mob/inhands/equipment/backpack_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/backpack_righthand.dmi' + slot_flags = ITEM_SLOT_BACK + w_class = WEIGHT_CLASS_HUGE + var/obj/item/gun/energy/minigun/gun + var/armed = 0 //whether the gun is attached, 0 is attached, 1 is the gun is wielded. + var/overheat = 0 + var/overheat_max = 60 + var/heat_diffusion = 5 + +/obj/item/minigunpack2/Initialize() + . = ..() + gun = new(src) + START_PROCESSING(SSobj, src) + +/obj/item/minigunpack2/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/minigunpack2/process() + overheat = max(0, overheat - heat_diffusion) + +//ATTACK HAND IGNORING PARENT RETURN VALUE +/obj/item/minigunpack2/attack_hand(var/mob/living/carbon/user) + if(src.loc == user) + if(!armed) + if(user.get_item_by_slot(SLOT_BACK) == src) + armed = 1 + if(!user.put_in_hands(gun)) + armed = 0 + to_chat(user, "You need a free hand to hold the gun!") + return + update_icon() + user.update_inv_back() + else + to_chat(user, "You are already holding the gun!") + else + ..() + +/obj/item/minigunpack2/attackby(obj/item/W, mob/user, params) + if(W == gun) //Don't need armed check, because if you have the gun assume its armed. + user.dropItemToGround(gun, TRUE) + else + ..() + +/obj/item/minigunpack2/dropped(mob/user) + if(armed) + user.dropItemToGround(gun, TRUE) + +/obj/item/minigunpack2/MouseDrop(atom/over_object) + . = ..() + if(armed) + return + if(iscarbon(usr)) + var/mob/M = usr + + if(!over_object) + return + + if(!M.incapacitated()) + + if(istype(over_object, /obj/screen/inventory/hand)) + var/obj/screen/inventory/hand/H = over_object + M.putItemFromInventoryInHandIfPossible(src, H.held_index) + + +/obj/item/minigunpack2/update_icon() + if(armed) + icon_state = "notholstered" + else + icon_state = "holstered" + +/obj/item/minigunpack2/proc/attach_gun(var/mob/user) + if(!gun) + gun = new(src) + gun.forceMove(src) + armed = 0 + if(user) + to_chat(user, "You attach the [gun.name] to the [name].") + else + src.visible_message("The [gun.name] snaps back onto the [name]!") + update_icon() + user.update_inv_back() + + +/obj/item/gun/energy/minigun + name = "laser gatling gun" + desc = "An advanced laser cannon with an incredible rate of fire. Requires a bulky backpack power source to use." + icon = 'icons/obj/guns/minigun.dmi' + icon_state = "minigun_spin" + item_state = "minigun" + flags_1 = CONDUCT_1 + force = 15 + recoil = 2 + slowdown = 1 + slot_flags = null + w_class = WEIGHT_CLASS_HUGE + materials = list() + ammo_type = list(/obj/item/ammo_casing/energy/laser/weak) + burst_size = 2 + automatic = 1 + can_charge = 0 + selfcharge = EGUN_SELFCHARGE + charge_tick = 2 + charge_delay = 5 + weapon_weight = WEAPON_HEAVY + item_flags = NEEDS_PERMIT | SLOWS_WHILE_IN_HAND + var/obj/item/minigunpack2/ammo_pack + +/obj/item/gun/energy/minigun/Initialize() + if(istype(loc, /obj/item/minigunpack2)) //We should spawn inside an ammo pack so let's use that one. + ammo_pack = loc + else + return INITIALIZE_HINT_QDEL //No pack, no gun + + return ..() + +/obj/item/gun/energy/minigun/attack_self(mob/living/user) + return + +/obj/item/gun/energy/minigun/dropped(mob/user) + if(ammo_pack) + ammo_pack.attach_gun(user) + else + qdel(src) + +/obj/item/gun/energy/minigun/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) + if(ammo_pack) + if(ammo_pack.overheat < ammo_pack.overheat_max) + ammo_pack.overheat += burst_size + ..() + else + to_chat(user, "The gun's heat sensor locked the trigger to prevent lens damage.") + +/obj/item/gun/energy/minigun/afterattack(atom/target, mob/living/user, flag, params) + if(!ammo_pack || ammo_pack.loc != user) + to_chat(user, "You need the backpack power source to fire the gun!") + . = ..() + +/obj/item/gun/energy/minigun/dropped(mob/living/user) + ammo_pack.attach_gun(user) + diff --git a/code/modules/projectiles/pins.dm b/code/modules/projectiles/pins.dm index da3743f053..9103340790 100644 --- a/code/modules/projectiles/pins.dm +++ b/code/modules/projectiles/pins.dm @@ -188,6 +188,15 @@ desc = "This is a DNA-locked firing pin which only authorizes one user. Attempt to fire once to DNA-link. It has a small explosive charge on it." selfdestruct = TRUE +/obj/item/firing_pin/holy + name = "blessed pin" + desc = "A firing pin that only responds to those who are holier than thou." + +/obj/item/firing_pin/holy/pin_auth(mob/living/user) + if(user.mind.isholy) + return TRUE + return FALSE + // Laser tag pins /obj/item/firing_pin/tag name = "laser tag firing pin" diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index cbd0348743..d8154b367d 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -39,6 +39,14 @@ /obj/item/projectile/beam/weak damage = 15 +/obj/item/projectile/beam/weak/minigun + damage = 12.5 + armour_penetration = 40 + +/obj/item/projectile/beam/weak/minigun/Initialize() + .=..() + speed = pick(0.7,0.75,0.8,0.85,0.9,0.95,1,1.05,1.1,1.15) + /obj/item/projectile/beam/weak/penetrator armour_penetration = 50 @@ -151,6 +159,11 @@ /obj/item/projectile/beam/lasertag/redtag/hitscan hitscan = TRUE +/obj/item/projectile/beam/lasertag/redtag/hitscan/holy + name = "lasrifle beam" + damage = 0.1 + damage_type = BURN + /obj/item/projectile/beam/lasertag/bluetag icon_state = "bluelaser" suit_types = list(/obj/item/clothing/suit/redtag) diff --git a/code/modules/projectiles/projectile/bullets/shotgun.dm b/code/modules/projectiles/projectile/bullets/shotgun.dm index f9aa47c6a3..4a1c954b1b 100644 --- a/code/modules/projectiles/projectile/bullets/shotgun.dm +++ b/code/modules/projectiles/projectile/bullets/shotgun.dm @@ -5,7 +5,7 @@ /obj/item/projectile/bullet/shotgun_beanbag name = "beanbag slug" damage = 5 - stamina = 80 + stamina = 70 /obj/item/projectile/bullet/incendiary/shotgun name = "incendiary slug" @@ -61,12 +61,12 @@ /obj/item/projectile/bullet/pellet/shotgun_buckshot name = "buckshot pellet" - damage = 12.5 + damage = 10 /obj/item/projectile/bullet/pellet/shotgun_rubbershot name = "rubbershot pellet" - damage = 3 - stamina = 25 + damage = 2 + stamina = 15 /obj/item/projectile/bullet/pellet/Range() ..() diff --git a/code/modules/projectiles/projectile/special/mindflayer.dm b/code/modules/projectiles/projectile/special/mindflayer.dm index d717bed39e..ac4488cae0 100644 --- a/code/modules/projectiles/projectile/special/mindflayer.dm +++ b/code/modules/projectiles/projectile/special/mindflayer.dm @@ -5,5 +5,5 @@ . = ..() if(ishuman(target)) var/mob/living/carbon/human/M = target - M.adjustBrainLoss(20) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 20) M.hallucination += 30 diff --git a/code/modules/reagents/chem_wiki_render.dm b/code/modules/reagents/chem_wiki_render.dm new file mode 100644 index 0000000000..d4a629c508 --- /dev/null +++ b/code/modules/reagents/chem_wiki_render.dm @@ -0,0 +1,354 @@ +//Generates a markdown txt file for use with the wiki + +/client/proc/generate_wikichem_list() + set name = "Generate Wikichems" + set category = "Debug" + set desc = "Generate a huge loglist of all the chems. Do not click unless you want lag." + + message_admins("Someone pressed the lag button. (Generate Wikichems)") + to_chat(usr, "Generating list") + var/prefix = "|Name | Reagents | Reaction vars | Description | Chem properties |\n|---|---|---|-----------|---|\n" + + ///datum/reagent/medicine, /datum/reagent/toxin, /datum/reagent/consumable, /datum/reagent/plantnutriment, /datum/reagent/uranium, + ///datum/reagent/colorful_reagent, /datum/reagent/mutationtoxin, /datum/reagent/fermi, /datum/reagent/drug, /datum/reagent/impure + + //Probably not the most eligant of solutions. + to_chat(usr, "Attempting reagent scan. Length of list [LAZYLEN(GLOB.chemical_reagents_list)*2]") + var/datum/reagent/R + var/tally = 0 + var/processCR = TRUE //Process reactions first + var/medicine = "" + var/toxin = "" + var/consumable = "" + var/plant = "" + var/uranium = "" + var/colours = "" + var/muta = "" + var/fermi = "" + var/remainder = "" + var/drug = "" + var/basic = "" + var/upgraded = "" + var/drinks = "" + var/alco = "" + var/grinded = "" + var/blob = "" + //var/impure + + //Chem_dispencer + var/list/dispensable_reagents = list( + "hydrogen", + "lithium", + "carbon", + "nitrogen", + "oxygen", + "fluorine", + "sodium", + "aluminium", + "silicon", + "phosphorus", + "sulfur", + "chlorine", + "potassium", + "iron", + "copper", + "mercury", + "radium", + "water", + "ethanol", + "sugar", + "sacid", + "welding_fuel", + "silver", + "iodine", + "bromine", + "stable_plasma" + ) + var/list/components = list( + "oil", + "ammonia", + "ash", + "acetone", + "phenol", + "diethylamine", + "saltpetre", + "sodiumchloride", + "lye" + ) + + var/list/grind = list( + "bluespace", + "gold", + "plasma", + "uranium" + ) + + //Bartender + var/dispence_drinks = list( + "water", + "ice", + "coffee", + "cream", + "tea", + "icetea", + "cola", + "spacemountainwind", + "dr_gibb", + "space_up", + "tonic", + "sodawater", + "lemon_lime", + "pwr_game", + "shamblers", + "sugar", + "orangejuice", + "grenadine", + "limejuice", + "tomatojuice", + "lemonjuice", + "menthol" + ) + var/dispence_alco = list( + "beer", + "kahlua", + "whiskey", + "wine", + "vodka", + "gin", + "rum", + "tequila", + "vermouth", + "cognac", + "ale", + "absinthe", + "hcider", + "creme_de_menthe", + "creme_de_cacao", + "triple_sec", + "sake", + "applejack" + ) + + + for(var/i = 1, i <= 2, i+=1) + for(var/X in GLOB.chemical_reagents_list) + R = GLOB.chemical_reagents_list[X] + + for(var/Y in dispensable_reagents) //Why do you have to do this + if(R.id == Y) + basic += generate_chemwiki_line(R, X, processCR) + continue + + for(var/Y in components) + if(R.id == Y) + upgraded += generate_chemwiki_line(R, X, processCR) + continue + + for(var/Y in dispence_drinks) + if(R.id == Y) + drinks += generate_chemwiki_line(R, X, processCR) + continue + + for(var/Y in dispence_alco) + if(R.id == Y) + alco += generate_chemwiki_line(R, X, processCR) + continue + + for(var/Y in grind) + if(R.id == Y) + grinded += generate_chemwiki_line(R, X, processCR) + continue + + + if(istype(R, /datum/reagent/medicine)) + medicine += generate_chemwiki_line(R, X, processCR) + + else if(istype(R, /datum/reagent/toxin)) + toxin += generate_chemwiki_line(R, X, processCR) + + else if(istype(R, /datum/reagent/consumable)) + consumable += generate_chemwiki_line(R, X, processCR) + + else if(istype(R, /datum/reagent/plantnutriment)) + plant += generate_chemwiki_line(R, X, processCR) + + else if(istype(R, /datum/reagent/uranium)) + uranium += generate_chemwiki_line(R, X, processCR) + + else if(istype(R, /datum/reagent/colorful_reagent)) + colours += generate_chemwiki_line(R, X, processCR) + + else if(istype(R, /datum/reagent/mutationtoxin)) + muta += generate_chemwiki_line(R, X, processCR) + + else if(istype(R, /datum/reagent/fermi)) + fermi += generate_chemwiki_line(R, X, processCR) + + else if(istype(R, /datum/reagent/drug)) + drug += generate_chemwiki_line(R, X, processCR) + + else if(istype(R, /datum/reagent/blob)) + blob += generate_chemwiki_line(R, X, processCR) + + /* + else if(istype(R, /datum/reagent/impure)) + impure += generate_chemwiki_line(R, X, processCR) + */ + + else + remainder += generate_chemwiki_line(R, X, processCR) + + tally++ + if((tally%50)==0) + to_chat(usr, "[tally] of [LAZYLEN(GLOB.chemical_reagents_list)*2] done.") + + processCR = FALSE + + to_chat(usr, "finished chems") + + var/wholeString = ("\n# DISPENCEABLE REAGENTS\n\n[prefix][basic]\n\n# COMPONENT REAGENTS\n\n[prefix][upgraded]\n\n# GRINDABLE REAGENTS\n\n[prefix][grinded]\n") + wholeString += ("\n# MEDICINE:\n\n[prefix][medicine]\n\n# TOXIN:\n\n[prefix][toxin]\n\n# DRUGS\n\n[prefix][drug]\n\n# FERMI\n\nThese chems lie on the cutting edge of chemical technology, and as such are not recommended for beginners!\n\n[prefix][fermi]\n\n# GENERAL REAGENTS\n\n[prefix][remainder]\n\n# DISPENCEABLE SOFT DRINKS\n\n[prefix][drinks]\n\n# DISPENCEABLE HARD DRINKS\n\n[prefix][alco]\n\n# CONSUMABLE\n\n[prefix][consumable]\n\n# PLANTS\n\n[prefix][plant]\n\n# URANIUM\n\n[prefix][uranium]\n\n# COLOURS\n\n[prefix][colours]\n\n# RACE MUTATIONS\n\n[prefix][muta]\n\n\n# BLOB REAGENTS\n\n[prefix][blob]\n") + + prefix = "|Name | Reagents | Reaction vars | Description |\n|---|---|---|----------|\n" + var/CRparse = "" + to_chat(usr, "starting reactions") + + //generate the reactions that we missed from before + for(var/reagent in GLOB.chemical_reactions_list) + for(var/datum/chemical_reaction/CR in GLOB.chemical_reactions_list[reagent]) + CRparse += generate_chemreactwiki_line(CR) + + wholeString += ("\n# CHEMICAL REACTIONS\n\n[prefix][CRparse]\n") + text2file(wholeString, "[GLOB.log_directory]/chem_parse.md") + to_chat(usr, "finished reactions") + to_chat(usr, "Saved file to (wherever your root folder is, i.e. where the DME is)/[GLOB.log_directory]/chem_parse.md OR use the Get Current Logs verb under the Admin tab. (if you click Open, and it does nothing, that's because you've not set a .md default program! Try downloading it instead, and use that file to set a default program! Also have a cute day.)") + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + +//Generate the big list of reagent based reactions. +/proc/generate_chemwiki_line(datum/reagent/R, X, processCR) + //name | Reagent pH | reagents | reaction temp | explosion temp | pH range | Kinetics | description | OD level | Addiction level | Metabolism rate | impure chem | inverse chem + + var/datum/chemical_reaction/CR = get_chemical_reaction(R.id) + if((!CR && processCR) || (CR && !processCR)) // Do reactions first. + return "" + + + var/outstring = "|
!\[[R.color]\](https://placehold.it/15/[copytext(R.color, 2, 8)]/000000?text=+)[R.name]
pH: [R.pH] | " + var/datum/reagent/R3 + if(CR) + outstring += "
    " + for(var/R2 in CR.required_reagents) + R3 = GLOB.chemical_reagents_list[R2]//What a convoluted mess + outstring += "
  • [R3.name]: [CR.required_reagents[R3.id]]u
  • " + if(CR.required_catalysts) + for(var/R2 in CR.required_catalysts) + R3 = GLOB.chemical_reagents_list[R2] + outstring += "
  • Catalyst: [R3.name]: [CR.required_catalysts[R3.id]]u
  • " + outstring += "
| " + else + outstring += "N/A | " + + + //Temp, Explosions and pH + if(CR) + outstring += "
    [(CR.FermiChem?"
  • Min react temp: [CR.OptimalTempMin]K
  • ":"[(CR.required_temp?"
  • Min react temp: [CR.required_temp]K
  • ":"")]")] [(CR.FermiChem?"
  • Explosion_temp: [CR.ExplodeTemp]K
  • ":"")] [(CR.FermiChem?"
  • pH range: [max((CR.OptimalpHMin - CR.ReactpHLim), 0)] to [min((CR.OptimalpHMax + CR.ReactpHLim), 14)]
  • ":"")] " + else + outstring += "" + + //Kinetics + if(CR) + if(CR.FermiChem) + switch(CR.ThermicConstant) + if(-INFINITY to -9.9) + outstring += "
  • Extremely endothermic
  • " + if(-9.9 to -4.9) + outstring += "
  • Very endothermic
  • " + if(-4.9 to -0.1) + outstring += "
  • Endothermic
  • " + if(-0.1 to 0.1) + outstring += "
  • Neutral
  • " + if(0.1 to 4.9) + outstring += "
  • Exothermic
  • " + if(4.9 to 9.9) + outstring += "
  • Very exothermic
  • " + if(9.9 to 19.9) + outstring += "
  • Extremely exothermic
  • " + if(19.9 to INFINITY ) + outstring += "
  • **Dangerously exothermic**
  • " + //if("cheesey") + //outstring += "
  • Dangerously Cheesey
  • " + + outstring += "
| " + else + outstring += " | " + + //Description, OD, Addict, Meta + outstring += "[R.description] |
  • Metabolism_rate: [R.metabolization_rate/2]u/s
  • [(R.overdose_threshold?"
  • Overdose: [R.overdose_threshold]u
  • ":"")] [(R.addiction_threshold?"
  • Addiction: [R.addiction_threshold]u
  • ":"")] " + + if(R.impure_chem != "fermiTox" && R.impure_chem) + R3 = GLOB.chemical_reagents_list[R.impure_chem] + outstring += "
  • Impure chem:[R3.name]
  • " + + if(R.inverse_chem != "fermiTox" && R.inverse_chem) + R3 = GLOB.chemical_reagents_list[R.inverse_chem] + outstring += "
  • Inverse chem:[R3.name]
  • " + + + + if(CR) + if(CR.required_container) + /*var/obj/item/I + I = istype(I, CR.required_container) if you can work out how to get this to work, by all means. + outstring += "
  • Required container: [I.name]
  • "*/ + outstring += "
  • Required container: [CR.required_container]
  • " + + if(CR.FermiChem) + outstring += "
  • Minimum purity: [CR.PurityMin]
  • [(CR.FermiExplode?"
  • Special explosion: Yes
  • ":"")]" + + + outstring += "
|\n" + return outstring + + + + +//Generate the big list of reaction based reactions. +//|Name | Reagents | Reaction vars | Description | Chem properties +/proc/generate_chemreactwiki_line(datum/chemical_reaction/CR) + if(CR.results.len) //Handled prior + return + var/outstring = "|[CR.name] |
    " + + //reagents + var/datum/reagent/R3 + for(var/R2 in CR.required_reagents) + R3 = GLOB.chemical_reagents_list[R2] + outstring += "
  • [R3.name]: [CR.required_reagents[R3.id]]u
  • " + if(CR.required_catalysts) + for(var/R2 in CR.required_catalysts) + R3 = GLOB.chemical_reagents_list[R2] + outstring += "
  • Catalyst: [R3.name]: [CR.required_catalysts[R3.id]]u
  • " + outstring += "
|
    " + + //Reaction vars + if(CR.required_temp) + outstring += "
  • Min react temp: [CR.required_temp]K
  • " + if(CR.FermiChem) + outstring += "[(CR.FermiChem?"
  • Min react temp: [CR.OptimalTempMin]K
  • ":"[(CR.required_temp?"
  • Min react temp: [CR.required_temp]K
  • ":"")]")] [(CR.FermiChem?"
  • Explosion temp: [CR.ExplodeTemp]K
  • ":"")] [(CR.FermiChem?"
  • pH range: [max((CR.OptimalpHMin - CR.ReactpHLim), 0)] to [min((CR.OptimalpHMax + CR.ReactpHLim), 14)]
  • ":"")]
  • Minimum purity: [CR.PurityMin] [(CR.FermiExplode?"
  • Special explosion: Yes
  • ":"")]" + if(CR.is_cold_recipe) + outstring += "
  • Cold: Yes
  • " + if(CR.required_container) + outstring += "
  • Required container: [CR.required_container]
  • " + if(CR.mob_react) + outstring += "
  • Can react in mob: Yes
  • " + + //description + outstring += "
| fill in manually " + + outstring += "
    |\n" + return outstring diff --git a/code/modules/reagents/chemistry/fermi/readme.md b/code/modules/reagents/chemistry/fermi/readme.md new file mode 100644 index 0000000000..4b897b6c4b --- /dev/null +++ b/code/modules/reagents/chemistry/fermi/readme.md @@ -0,0 +1,23 @@ +How to code fermichem reactions: +First off, probably read though the readme for standard reagent mechanisms, this builds on top of that. + +#bitflags +for `datum/reagent/` you have the following options with `var/chemical_flags`: + +``` +REAGENT_DEAD_PROCESS calls on_mob_dead() if present in a dead body +REAGENT_DONOTSPLIT Do not split the chem at all during processing +REAGENT_ONLYINVERSE Only invert chem, no splitting +REAGENT_ONMOBMERGE Call on_mob_life proc when reagents are merging. +REAGENT_INVISIBLE Doesn't appear on handheld health analyzers. +REAGENT_FORCEONNEW Forces a on_new() call without a data overhead +REAGENT_SNEAKYNAME When inverted, the inverted chem uses the name of the original chem +REAGENT_SPLITRETAINVOL Retains initial volume of chem when splitting +``` + +for `datum/chemical_reaction/` under `var/clear_conversion` + +``` +REACTION_CLEAR_IMPURE Convert into impure/pure on reaction completion +REACTION_CLEAR_INVERSE Convert into inverse on reaction completion when purity is low enough +``` diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index d1eec39f44..1be30301ec 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -1,4 +1,4 @@ -#define CHEMICAL_QUANTISATION_LEVEL 0.0001 +#define CHEMICAL_QUANTISATION_LEVEL 0.001 /proc/build_chemical_reagent_list() //Chemical Reagents - Initialises all /datum/reagent into a list indexed by reagent id @@ -466,7 +466,7 @@ if (C.FermiChem == TRUE && !continue_reacting) if (chem_temp > C.ExplodeTemp) //This is first to ensure explosions. - var/datum/chemical_reaction/fermi/Ferm = selected_reaction + var/datum/chemical_reaction/Ferm = selected_reaction fermiIsReacting = FALSE SSblackbox.record_feedback("tally", "fermi_chem", 1, ("[Ferm] explosion")) Ferm.FermiExplode(src, my_atom, volume = total_volume, temp = chem_temp, pH = pH) @@ -496,7 +496,12 @@ //Standard reaction mechanics: else - if (C.FermiChem == TRUE)//Just to make sure + if (C.FermiChem == TRUE)//Just to make sure, should only proc when grenades are combining. + if (chem_temp > C.ExplodeTemp) //To allow fermigrenades + var/datum/chemical_reaction/fermi/Ferm = selected_reaction + fermiIsReacting = FALSE + SSblackbox.record_feedback("tally", "fermi_chem", 1, ("[Ferm] explosion")) + Ferm.FermiExplode(src, my_atom, volume = total_volume, temp = chem_temp, pH = pH) return 0 for(var/B in cached_required_reagents) // @@ -539,7 +544,7 @@ return 0 /datum/reagents/process() - var/datum/chemical_reaction/fermi/C = fermiReactID + var/datum/chemical_reaction/C = fermiReactID var/list/cached_required_reagents = C.required_reagents//update reagents list var/list/cached_results = C.results//resultant chemical list @@ -550,6 +555,10 @@ if (multiplier == 0) fermiEnd() return + for(var/P in C.required_catalysts) + if(!has_reagent(P)) + fermiEnd() + return for(var/P in cached_results) targetVol = cached_results[P]*multiplier @@ -571,16 +580,16 @@ return /datum/reagents/proc/fermiEnd() - var/datum/chemical_reaction/fermi/C = fermiReactID + var/datum/chemical_reaction/C = fermiReactID STOP_PROCESSING(SSprocessing, src) fermiIsReacting = FALSE - reactedVol = 0 - targetVol = 0 //pH check, handled at the end to reduce calls. if(istype(my_atom, /obj/item/reagent_containers)) var/obj/item/reagent_containers/RC = my_atom RC.pH_check() - C.FermiFinish(src, my_atom) + C.FermiFinish(src, my_atom, reactedVol) + reactedVol = 0 + targetVol = 0 handle_reactions() update_total() //Reaction sounds and words @@ -591,7 +600,7 @@ to_chat(M, "[iconhtml] [C.mix_message]") /datum/reagents/proc/fermiReact(selected_reaction, cached_temp, cached_pH, reactedVol, targetVol, cached_required_reagents, cached_results, multiplier) - var/datum/chemical_reaction/fermi/C = selected_reaction + var/datum/chemical_reaction/C = selected_reaction var/deltaT = 0 var/deltapH = 0 var/stepChemAmmount = 0 @@ -676,7 +685,7 @@ STOP_PROCESSING(SSprocessing, src) return 0 - C.FermiCreate(src)//proc that calls when step is done + C.FermiCreate(src, addChemAmmount, purity)//proc that calls when step is done //Apply pH changes and thermal output of reaction to beaker chem_temp = round(cached_temp + (C.ThermicConstant * addChemAmmount)) @@ -700,7 +709,7 @@ return (reactedVol) //Currently calculates it irrespective of required reagents at the start -/datum/reagents/proc/reactant_purity(var/datum/chemical_reaction/fermi/C, holder) +/datum/reagents/proc/reactant_purity(var/datum/chemical_reaction/C, holder) var/list/cached_reagents = reagent_list var/i = 0 var/cachedPurity @@ -710,6 +719,14 @@ i++ return cachedPurity/i +/datum/reagents/proc/uncache_purity(id) + var/datum/reagent/R = has_reagent("[id]") + if(!R) + return + if(R.cached_purity == 1) + return + R.purity = R.cached_purity + /datum/reagents/proc/isolate_reagent(reagent) var/list/cached_reagents = reagent_list for(var/_reagent in cached_reagents) @@ -741,11 +758,14 @@ total_volume = 0 for(var/reagent in cached_reagents) var/datum/reagent/R = reagent - if(R.volume < CHEMICAL_QUANTISATION_LEVEL) + if(R.volume == 0) + del_reagent(R.id) + if((R.volume < 0.01) && !fermiIsReacting) del_reagent(R.id) else total_volume += R.volume - + if(!reagent_list) + pH = 7 return 0 /datum/reagents/proc/clear_reagents() @@ -874,17 +894,15 @@ var/datum/reagent/R = A if (R.id == reagent) //IF MERGING //Add amount and equalize purity - R.volume += amount + R.volume += round(amount, CHEMICAL_QUANTISATION_LEVEL) R.purity = ((R.purity * R.volume) + (other_purity * amount)) /((R.volume + amount)) //This should add the purity to the product update_total() if(my_atom) my_atom.on_reagent_change(ADD_REAGENT) if(isliving(my_atom)) - if(R.OnMobMergeCheck == TRUE)//Forces on_mob_add proc when a chem is merged + if(R.chemical_flags & REAGENT_ONMOBMERGE)//Forces on_mob_add proc when a chem is merged R.on_mob_add(my_atom, amount) - //else - // R.on_merge(data, amount, my_atom, other_purity) R.on_merge(data, amount, my_atom, other_purity) if(!no_react) handle_reactions() @@ -896,13 +914,13 @@ var/datum/reagent/R = new D.type(data) cached_reagents += R R.holder = src - R.volume = amount + R.volume = round(amount, CHEMICAL_QUANTISATION_LEVEL) R.purity = other_purity R.loc = get_turf(my_atom) if(data) R.data = data R.on_new(data) - if(R.addProc == TRUE)//Allows on new without data overhead. + if(R.chemical_flags & REAGENT_FORCEONNEW)//Allows on new without data overhead. R.on_new(pH) //Add more as desired. diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm index 436ee80e7b..eeb452dbb5 100644 --- a/code/modules/reagents/chemistry/machinery/chem_heater.dm +++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm @@ -77,6 +77,16 @@ updateUsrDialog() update_icon() return + + if(beaker) + if(istype(I, /obj/item/reagent_containers/dropper)) + var/obj/item/reagent_containers/dropper/D = I + D.afterattack(beaker, user, 1) + + if(istype(I, /obj/item/reagent_containers/syringe)) + var/obj/item/reagent_containers/syringe/S = I + S.afterattack(beaker, user, 1) + return ..() /obj/machinery/chem_heater/on_deconstruction() diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index 002c54be94..96aa0901e0 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -233,6 +233,12 @@ else reagents.remove_reagent(id, amount) . = TRUE + else if (amount == -1) // -1 means custom amount + useramount = input("Enter the Amount you want to transfer:", name, useramount) as num|null + if (useramount > 0) + end_fermi_reaction() + reagents.trans_id_to(beaker, id, useramount) + . = TRUE if("toggleMode") mode = !mode @@ -332,7 +338,7 @@ var/vol_part = min(reagents.total_volume, 30) if(text2num(many)) amount_full = round(reagents.total_volume / 30) - vol_part = reagents.total_volume % 30 + vol_part = ((reagents.total_volume*1000) % 30000) / 1000 //% operator doesn't support decimals. var/name = stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN) if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr))) return @@ -379,7 +385,34 @@ reagents.trans_to(P, vol_part) . = TRUE //END CITADEL ADDITIONS - if("analyze") + if("analyzeBeak") + var/datum/reagent/R = GLOB.chemical_reagents_list[params["id"]] + if(R) + var/state = "Unknown" + if(initial(R.reagent_state) == 1) + state = "Solid" + else if(initial(R.reagent_state) == 2) + state = "Liquid" + else if(initial(R.reagent_state) == 3) + state = "Gas" + var/const/P = 3 //The number of seconds between life ticks + var/T = initial(R.metabolization_rate) * (60 / P) + var/datum/chemical_reaction/Rcr = get_chemical_reaction(R.id) + if(Rcr && Rcr.FermiChem) + fermianalyze = TRUE + var/pHpeakCache = (Rcr.OptimalpHMin + Rcr.OptimalpHMax)/2 + var/datum/reagent/targetReagent = beaker.reagents.has_reagent("[R.id]") + + if(!targetReagent) + CRASH("Tried to find a reagent that doesn't exist in the chem_master!") + analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold), "purityF" = targetReagent.purity, "inverseRatioF" = initial(R.inverse_chem_val), "purityE" = initial(Rcr.PurityMin), "minTemp" = initial(Rcr.OptimalTempMin), "maxTemp" = initial(Rcr.OptimalTempMax), "eTemp" = initial(Rcr.ExplodeTemp), "pHpeak" = pHpeakCache) + else + fermianalyze = FALSE + analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold)) + screen = "analyze" + return + + if("analyzeBuff") var/datum/reagent/R = GLOB.chemical_reagents_list[params["id"]] if(R) var/state = "Unknown" @@ -395,7 +428,11 @@ fermianalyze = TRUE var/datum/chemical_reaction/Rcr = get_chemical_reaction(R.id) var/pHpeakCache = (Rcr.OptimalpHMin + Rcr.OptimalpHMax)/2 - analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold), "purityF" = initial(R.purity), "inverseRatioF" = initial(R.InverseChemVal), "purityE" = initial(Rcr.PurityMin), "minTemp" = initial(Rcr.OptimalTempMin), "maxTemp" = initial(Rcr.OptimalTempMax), "eTemp" = initial(Rcr.ExplodeTemp), "pHpeak" = pHpeakCache) + var/datum/reagent/targetReagent = reagents.has_reagent("[R.id]") + + if(!targetReagent) + CRASH("Tried to find a reagent that doesn't exist in the chem_master!") + analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold), "purityF" = targetReagent.purity, "inverseRatioF" = initial(R.inverse_chem_val), "purityE" = initial(Rcr.PurityMin), "minTemp" = initial(Rcr.OptimalTempMin), "maxTemp" = initial(Rcr.OptimalTempMax), "eTemp" = initial(Rcr.ExplodeTemp), "pHpeak" = pHpeakCache) else fermianalyze = FALSE analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold)) diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm index 0e482f8cec..246eade62b 100644 --- a/code/modules/reagents/chemistry/machinery/pandemic.dm +++ b/code/modules/reagents/chemistry/machinery/pandemic.dm @@ -201,6 +201,8 @@ B.reagents.add_reagent("blood", 20, data) wait = TRUE update_icon() + var/turf/source_turf = get_turf(src) + log_virus("A culture bottle was printed for the virus [A.admin_details()] at [loc_name(source_turf)] by [key_name(usr)]") addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 50) . = TRUE if("create_vaccine_bottle") diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index c21629ce8f..52d3ba7822 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -32,19 +32,19 @@ var/addiction_stage3_end = 30 var/addiction_stage4_end = 40 var/overdosed = 0 // You fucked up and this is now triggering its overdose effects, purge that shit quick. - var/self_consuming = FALSE + var/self_consuming = FALSE //I think this uhhh, makes weird stuff happen when metabolising, but... doesn't seem to do what I think, so I'm gonna leave it. //Fermichem vars: - var/purity = 1 //How pure a chemical is from 0 - 1. - var/addProc = FALSE //If the chemical should force an on_new() call + var/purity = 1 //How pure a chemical is from 0 - 1. + var/cached_purity = 1 var/turf/loc = null //Should be the creation location! var/pH = 7 //pH of the specific reagent, used for calculating the sum pH of a holder. - var/ImpureChem = "fermiTox"// What chemical is metabolised with an inpure reaction - var/InverseChemVal = 0.25 // If the impurity is below 0.5, replace ALL of the chem with InverseChem upon metabolising - var/InverseChem = "fermiTox"// What chem is metabolised when purity is below InverseChemVal, this shouldn't be made, but if it does, well, I guess I'll know about it. - var/DoNotSplit = FALSE // If impurity is handled within the main chem itself - var/OnMobMergeCheck = FALSE //Call on_mob_life proc when reagents are merging. + //var/SplitChem = FALSE //If the chem splits on metabolism + var/impure_chem // What chemical is metabolised with an inpure reaction + var/inverse_chem_val = 0 // If the impurity is below 0.5, replace ALL of the chem with inverse_chemupon metabolising + var/inverse_chem // What chem is metabolised when purity is below inverse_chem_val, this shouldn't be made, but if it does, well, I guess I'll know about it. var/metabolizing = FALSE - var/invisible = FALSE //Set to true if it doesn't appear on handheld health analyzers. + var/chemical_flags // See fermi/readme.dm REAGENT_DEAD_PROCESS, REAGENT_DONOTSPLIT, REAGENT_ONLYINVERSE, REAGENT_ONMOBMERGE, REAGENT_INVISIBLE, REAGENT_FORCEONNEW, REAGENT_SNEAKYNAME + /datum/reagent/Destroy() // This should only be called by the holder, so it's already handled clearing its references . = ..() @@ -73,8 +73,47 @@ holder.remove_reagent(src.id, metabolization_rate * M.metabolism_efficiency) //By default it slowly disappears. return +//called when a mob processes chems when dead. +/datum/reagent/proc/on_mob_dead(mob/living/carbon/M) + if(!(chemical_flags & REAGENT_DEAD_PROCESS)) //justincase + return + current_cycle++ + if(holder) + holder.remove_reagent(src.id, metabolization_rate * M.metabolism_efficiency) //By default it slowly disappears. + return + // Called when this reagent is first added to a mob -/datum/reagent/proc/on_mob_add(mob/living/L) +/datum/reagent/proc/on_mob_add(mob/living/L, amount) + if(!iscarbon(L)) + return + var/mob/living/carbon/M = L + if (purity == 1) + log_game("CHEM: [L] ckey: [L.key] has ingested [volume]u of [id]") + return + if(cached_purity == 1) + cached_purity = purity + else if(purity < 0) + CRASH("Purity below 0 for chem: [id], Please let Fermis Know!") + if(chemical_flags & REAGENT_DONOTSPLIT) + return + + if ((inverse_chem_val > purity) && (inverse_chem))//Turns all of a added reagent into the inverse chem + M.reagents.remove_reagent(id, amount, FALSE) + M.reagents.add_reagent(inverse_chem, amount, FALSE, other_purity = 1-cached_purity) + var/datum/reagent/R = M.reagents.has_reagent("[inverse_chem]") + if(R.chemical_flags & REAGENT_SNEAKYNAME) + R.name = name//Negative effects are hidden + if(R.chemical_flags & REAGENT_INVISIBLE) + R.chemical_flags |= (REAGENT_INVISIBLE) + log_game("FERMICHEM: [M] ckey: [M.key] has ingested [volume]u of [inverse_chem]") + return + else if (impure_chem) + var/impureVol = amount * (1 - purity) //turns impure ratio into impure chem + if(!(chemical_flags & REAGENT_SPLITRETAINVOL)) + M.reagents.remove_reagent(id, (impureVol), FALSE) + M.reagents.add_reagent(impure_chem, impureVol, FALSE, other_purity = 1-cached_purity) + log_game("FERMICHEM: [M] ckey: [M.key] has ingested [volume - impureVol]u of [id]") + log_game("FERMICHEM: [M] ckey: [M.key] has ingested [volume]u of [impure_chem]") return // Called when this reagent is removed while inside a mob @@ -97,7 +136,35 @@ return // Called when two reagents of the same are mixing. -/datum/reagent/proc/on_merge(data) +/datum/reagent/proc/on_merge(data, amount, mob/living/carbon/M, purity) + if(!iscarbon(M)) + return + if (purity == 1) + log_game("FERMICHEM: [M] ckey: [M.key] has ingested [volume]u of [id]") + return + cached_purity = purity //purity SHOULD be precalculated from the add_reagent, update cache. + if (purity < 0) + CRASH("Purity below 0 for chem: [id], Please let Fermis Know!") + if(chemical_flags & REAGENT_DONOTSPLIT) + return + + if ((inverse_chem_val > purity) && (inverse_chem)) //INVERT + M.reagents.remove_reagent(id, amount, FALSE) + M.reagents.add_reagent(inverse_chem, amount, FALSE, other_purity = 1-cached_purity) + var/datum/reagent/R = M.reagents.has_reagent("[inverse_chem]") + if(R.chemical_flags & REAGENT_SNEAKYNAME) + R.name = name//Negative effects are hidden + if(R.chemical_flags & REAGENT_INVISIBLE) + R.chemical_flags |= (REAGENT_INVISIBLE) + log_game("FERMICHEM: [M] ckey: [M.key] has merged [volume]u of [inverse_chem]") + return + else if (impure_chem) //SPLIT + var/impureVol = amount * (1 - purity) + if(!(chemical_flags & REAGENT_SPLITRETAINVOL)) + M.reagents.remove_reagent(id, impureVol, FALSE) + M.reagents.add_reagent(impure_chem, impureVol, FALSE, other_purity = 1-cached_purity) + log_game("FERMICHEM: [M] ckey: [M.key] has merged [volume - impureVol]u of [id]") + log_game("FERMICHEM: [M] ckey: [M.key] has merged [volume]u of [impure_chem]") return /datum/reagent/proc/on_update(atom/A) diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index 79b6f96781..94aca411be 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -1373,6 +1373,7 @@ All effects don't start immediately, but rather get worse over time; the rate is /datum/reagent/consumable/ethanol/neurotoxin name = "Neurotoxin" + id = "neurotoxin" description = "A strong neurotoxin that puts the subject into a death-like state." color = "#2E2E61" // rgb: 46, 46, 97 boozepwr = 50 @@ -1382,6 +1383,10 @@ All effects don't start immediately, but rather get worse over time; the rate is glass_icon_state = "neurotoxinglass" glass_name = "Neurotoxin" glass_desc = "A drink that is guaranteed to knock you silly." + //SplitChem = TRUE + impure_chem = "neuroweak" + inverse_chem_val = 0.5 //Clear conversion + inverse_chem = "neuroweak" /datum/reagent/consumable/ethanol/neurotoxin/proc/pickt() return (pick(TRAIT_PARALYSIS_L_ARM,TRAIT_PARALYSIS_R_ARM,TRAIT_PARALYSIS_R_LEG,TRAIT_PARALYSIS_L_LEG)) @@ -1389,18 +1394,18 @@ All effects don't start immediately, but rather get worse over time; the rate is /datum/reagent/consumable/ethanol/neurotoxin/on_mob_life(mob/living/carbon/M) M.set_drugginess(50) M.dizziness +=2 - M.adjustBrainLoss(1*REM, 150) - if(prob(20)) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1*REM, 150) + if(prob(20) && !holder.has_reagent("neuroweak")) M.adjustStaminaLoss(10) M.drop_all_held_items() to_chat(M, "You cant feel your hands!") if(current_cycle > 5) - if(prob(20)) + if(prob(20) && !holder.has_reagent("neuroweak")) var/t = pickt() ADD_TRAIT(M, t, type) M.adjustStaminaLoss(10) if(current_cycle > 30) - M.adjustBrainLoss(2*REM) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2*REM) if(current_cycle > 50 && prob(15)) if(!M.undergoing_cardiac_arrest() && M.can_heartattack()) M.set_heartattack(TRUE) @@ -1417,6 +1422,25 @@ All effects don't start immediately, but rather get worse over time; the rate is M.adjustStaminaLoss(10) ..() +/datum/reagent/consumable/ethanol/neuroweak + name = "Neuro-Smash" + id = "neuroweak" + description = "A mostly safe alcoholic drink for the true daredevils. Counteracts Neurotoxins." + boozepwr = 60 + pH = 8 + +/datum/reagent/consumable/ethanol/neuroweak/on_mob_life(mob/living/carbon/M) + if(holder.has_reagent("neurotoxin")) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1*REM, 150) + M.reagents.remove_reagent("neurotoxin", 1.5 * REAGENTS_METABOLISM, FALSE) + if(holder.has_reagent("fentanyl")) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1*REM, 150) + M.reagents.remove_reagent("fentanyl", 0.75 * REAGENTS_METABOLISM, FALSE) + else + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, -0.5*REM, 150) + M.dizziness +=2 + ..() + /datum/reagent/consumable/ethanol/hippies_delight name = "Hippie's Delight" id = "hippiesdelight" @@ -2122,6 +2146,29 @@ All effects don't start immediately, but rather get worse over time; the rate is glass_name = "Planet Cracker" glass_desc = "Although historians believe the drink was originally created to commemorate the end of an important conflict in man's past, its origins have largely been forgotten and it is today seen more as a general symbol of human supremacy." +/datum/reagent/consumable/ethanol/commander_and_chief + name = "Commander and Chief" + id = "commander_and_chief" + description = "A cocktail for the captain on the go." + color = "#ffffc9" + boozepwr = 50 + quality = DRINK_FANTASTIC + taste_description = "Tastes like...duty and responsibility?" + glass_icon_state = "commander_and_chief" + glass_name = "Commander and Chief" + glass_desc = "The gems of this majestic chalice represent the departments and their Heads." + +/datum/reagent/consumable/ethanol/commander_and_chief/on_mob_life(mob/living/carbon/M) + if(M.mind && HAS_TRAIT(M.mind, TRAIT_CAPTAIN_METABOLISM)) + M.heal_bodypart_damage(2,2,2) + M.adjustBruteLoss(-3.5,0) + M.adjustOxyLoss(-3.5,0) + M.adjustFireLoss(-3.5,0) + M.adjustToxLoss(-3.5,0) + M.radiation = max(M.radiation - 25, 0) + . = 1 + return ..() + /datum/reagent/consumable/ethanol/fruit_wine name = "Fruit Wine" id = "fruit_wine" diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm index bfefc073f8..dd51ce2bc7 100644 --- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm @@ -13,6 +13,7 @@ glass_icon_state = "glass_orange" glass_name = "glass of orange juice" glass_desc = "Vitamins! Yay!" + pH = 3.3 /datum/reagent/consumable/orangejuice/on_mob_life(mob/living/carbon/M) if(M.getOxyLoss() && prob(30)) @@ -45,6 +46,7 @@ glass_icon_state = "glass_green" glass_name = "glass of lime juice" glass_desc = "A glass of sweet-sour lime juice." + pH = 2.2 /datum/reagent/consumable/limejuice/on_mob_life(mob/living/carbon/M) if(M.getToxLoss() && prob(20)) @@ -125,6 +127,7 @@ glass_icon_state = "lemonglass" glass_name = "glass of lemon juice" glass_desc = "Sour..." + pH = 2 /datum/reagent/consumable/banana name = "Banana Juice" @@ -883,3 +886,23 @@ to_chat(M, "[pick("Diamond skies where white deer fly.","Sipping strawberry tea.","Silver raindrops drift through timeless, Neverending June.","Crystal ... pearls free, with love!","Beaming love into me.")]") ..() . = 1 + +/datum/reagent/consumable/catnip_tea + name = "Catnip Tea" + id = "catnip_tea" + description = "A sleepy and tasty catnip tea!" + color = "#101000" // rgb: 16, 16, 0 + nutriment_factor = 0 + taste_description = "sugar and catnip" + glass_icon_state = "teaglass" + glass_name = "glass of catnip tea" + glass_desc = "A purrfect drink for a cat." + +/datum/reagent/consumable/catnip_tea/on_mob_life(mob/living/carbon/M) + M.adjustStaminaLoss(min(50 - M.getStaminaLoss(), 3)) + if(prob(20)) + M.emote("nya") + if(prob(20)) + to_chat(M, "[pick("Headpats feel nice.", "Backrubs would be nice.", "Mew")]") + M.adjustArousalLoss(5) + ..() diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index 5b06710472..f3df72cd4d 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -80,14 +80,14 @@ . = 1 /datum/reagent/drug/crank/overdose_process(mob/living/M) - M.adjustBrainLoss(2*REM) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2*REM) M.adjustToxLoss(2*REM, 0) M.adjustBruteLoss(2*REM, 0) ..() . = 1 /datum/reagent/drug/crank/addiction_act_stage1(mob/living/M) - M.adjustBrainLoss(5*REM) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5*REM) ..() /datum/reagent/drug/crank/addiction_act_stage2(mob/living/M) @@ -101,7 +101,7 @@ . = 1 /datum/reagent/drug/crank/addiction_act_stage4(mob/living/M) - M.adjustBrainLoss(3*REM) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3*REM) M.adjustToxLoss(5*REM, 0) M.adjustBruteLoss(5*REM, 0) ..() @@ -125,13 +125,13 @@ ..() /datum/reagent/drug/krokodil/overdose_process(mob/living/M) - M.adjustBrainLoss(0.25*REM) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 0.25*REM) M.adjustToxLoss(0.25*REM, 0) ..() . = 1 /datum/reagent/drug/krokodil/addiction_act_stage1(mob/living/M) - M.adjustBrainLoss(2*REM) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2*REM) M.adjustToxLoss(2*REM, 0) ..() . = 1 @@ -192,7 +192,7 @@ if(jitter) M.Jitter(2) if(brain_damage) - M.adjustBrainLoss(rand(1,4)) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, rand(1,4)) M.heal_overall_damage(2, 2) if(prob(5)) M.emote(pick("twitch", "shiver")) @@ -210,7 +210,7 @@ M.drop_all_held_items() ..() M.adjustToxLoss(1, 0) - M.adjustBrainLoss(pick(0.5, 0.6, 0.7, 0.8, 0.9, 1)) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, pick(0.5, 0.6, 0.7, 0.8, 0.9, 1)) . = 1 /datum/reagent/drug/methamphetamine/addiction_act_stage1(mob/living/M) @@ -289,7 +289,7 @@ if(prob(5)) to_chat(M, "[high_message]") M.adjustStaminaLoss(-5, 0) - M.adjustBrainLoss(4) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 4) M.hallucination += 5 if(M.canmove && !ismovableatom(M.loc)) step(M, pick(GLOB.cardinals)) @@ -314,7 +314,7 @@ for(var/i = 0, i < 8, i++) step(M, pick(GLOB.cardinals)) M.Jitter(5) - M.adjustBrainLoss(10) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10) if(prob(20)) M.emote(pick("twitch","drool","moan")) ..() @@ -326,7 +326,7 @@ step(M, pick(GLOB.cardinals)) M.Jitter(10) M.Dizzy(10) - M.adjustBrainLoss(10) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10) if(prob(30)) M.emote(pick("twitch","drool","moan")) ..() @@ -338,7 +338,7 @@ step(M, pick(GLOB.cardinals)) M.Jitter(15) M.Dizzy(15) - M.adjustBrainLoss(10) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10) if(prob(40)) M.emote(pick("twitch","drool","moan")) ..() @@ -351,7 +351,7 @@ M.Jitter(50) M.Dizzy(50) M.adjustToxLoss(5, 0) - M.adjustBrainLoss(10) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10) if(prob(50)) M.emote(pick("twitch","drool","moan")) ..() @@ -401,7 +401,7 @@ M.jitteriness = 0 M.confused = 0 M.disgust = 0 - M.adjustBrainLoss(0.2) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 0.2) ..() . = 1 @@ -418,7 +418,7 @@ if(3) M.emote("frown") SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "happiness_drug", /datum/mood_event/happiness_drug_bad_od) - M.adjustBrainLoss(0.5) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 0.5) ..() . = 1 @@ -490,7 +490,7 @@ H.dna.species.punchdamagehigh *= 0.2 /datum/reagent/drug/skooma/on_mob_life(mob/living/carbon/M) - M.adjustBrainLoss(1*REM) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1*REM) M.adjustToxLoss(1*REM) if(prob(10)) M.adjust_blurriness(2) @@ -522,7 +522,7 @@ M.Dizzy(50) M.adjust_blurriness(10) if(prob(50)) //This proc will be called about 200 times and the adjustbrainloss() below only has to be called 40 times to kill. This will make surviving skooma addiction pretty rare without mannitol usage. - M.adjustBrainLoss(5) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5) if(prob(40)) M.emote(pick("twitch","drool","moan")) ..() diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 19aeaeb25e..056c5b1901 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -191,6 +191,13 @@ color = "#731008" // rgb: 115, 16, 8 taste_description = "ketchup" +/datum/reagent/consumable/mustard + name = "Mustard" + id = "mustard" + description = "Mustard, mostly used on hotdogs, corndogs and burgers." + nutriment_factor = 5 * REAGENTS_METABOLISM + color = "#DDED26" // rgb: 221, 237, 38 + taste_description = "mustard" /datum/reagent/consumable/capsaicin name = "Capsaicin Oil" @@ -684,7 +691,7 @@ . = 1 if(prob(20)) M.losebreath += 4 - M.adjustBrainLoss(2*REM, 150) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2*REM, 150) M.adjustToxLoss(3*REM,0) M.adjustStaminaLoss(10*REM,0) M.blur_eyes(5) diff --git a/code/modules/reagents/chemistry/reagents/impure_reagents.dm b/code/modules/reagents/chemistry/reagents/impure_reagents.dm new file mode 100644 index 0000000000..e24b811131 --- /dev/null +++ b/code/modules/reagents/chemistry/reagents/impure_reagents.dm @@ -0,0 +1,26 @@ +//Reagents produced by metabolising/reacting fermichems inoptimally, i.e. inverse_chems or impure_chems +//Inverse = Splitting +//Invert = Whole conversion + +/datum/reagent/impure + chemical_flags = REAGENT_INVISIBLE | REAGENT_SNEAKYNAME //by default, it will stay hidden on splitting, but take the name of the source on inverting + + +/datum/reagent/impure/fermiTox + name = "Chemical Isomers" + id = "fermiTox" + description = "Toxic chemical isomers made from impure reactions. At low volumes will cause light toxin damage, but as the volume increases, it deals larger amounts, damages the liver, then eventually the heart." + data = "merge" + color = "FFFFFF" + can_synth = FALSE + var/potency = 1 //potency multiplies the volume when added. + + +//I'm concerned this is too weak, but I also don't want deathmixes. +//TODO: liver damage, 100+ heart +/datum/reagent/impure/fermiTox/on_mob_life(mob/living/carbon/C, method) + if(C.dna && istype(C.dna.species, /datum/species/jelly)) + C.adjustToxLoss(-2) + else + C.adjustToxLoss(2) + ..() diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index eb7438aa20..951a77b0e8 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -44,7 +44,7 @@ M.heal_bodypart_damage(5,5) M.adjustToxLoss(-5, 0, TRUE) M.hallucination = 0 - M.setBrainLoss(0) + M.setOrganLoss(ORGAN_SLOT_BRAIN, 0) REMOVE_TRAITS_NOT_IN(M, list(SPECIES_TRAIT, ROUNDSTART_TRAIT, ORGAN_TRAIT)) M.set_blurriness(0) M.set_blindness(0) @@ -64,6 +64,10 @@ if(M.blood_volume < (BLOOD_VOLUME_NORMAL*M.blood_ratio)) M.blood_volume = (BLOOD_VOLUME_NORMAL*M.blood_ratio) + for(var/organ in M.internal_organs) + var/obj/item/organ/O = organ + O.setOrganDamage(0) + for(var/thing in M.diseases) var/datum/disease/D = thing if(D.severity == DISEASE_SEVERITY_POSITIVE) @@ -852,10 +856,16 @@ if(M.notify_ghost_cloning(source = M)) spawn (100) //so the ghost has time to re-enter return + else M.adjustOxyLoss(-20, 0) M.adjustToxLoss(-20, 0) + var/mob/living/carbon/H = M + for(var/organ in H.internal_organs) + var/obj/item/organ/O = organ + O.setOrganDamage(0) M.updatehealth() + if(M.revive()) M.emote("gasp") log_combat(M, M, "revived", src) @@ -875,7 +885,7 @@ pH = 10.4 /datum/reagent/medicine/mannitol/on_mob_life(mob/living/carbon/C) - C.adjustBrainLoss(-2*REM) + C.adjustOrganLoss(ORGAN_SLOT_BRAIN, -2*REM) if(prob(10)) C.cure_trauma_type(resilience = TRAUMA_RESILIENCE_BASIC) ..() @@ -1130,7 +1140,7 @@ M.adjustFireLoss(-5*REM, 0) M.adjustOxyLoss(-15, 0) M.adjustToxLoss(-5*REM, 0) - M.adjustBrainLoss(-15*REM) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, -15*REM) M.adjustCloneLoss(-3*REM, 0) M.adjustStaminaLoss(-20*REM,0) ..() @@ -1175,9 +1185,9 @@ M.adjustFireLoss(-3 * REM, 0) M.adjustOxyLoss(-15 * REM, 0) M.adjustToxLoss(-3 * REM, 0, TRUE) //Heals TOXINLOVERS - M.adjustBrainLoss(2 * REM, 150) //This does, after all, come from ambrosia, and the most powerful ambrosia in existence, at that! + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2 * REM, 150) //This does, after all, come from ambrosia, and the most powerful ambrosia in existence, at that! M.adjustCloneLoss(-1 * REM, 0) - M.adjustStaminaLoss(-30 * REM, 0) + M.adjustStaminaLoss(-13 * REM, 0) M.jitteriness = min(max(0, M.jitteriness + 3), 30) M.druggy = min(max(0, M.druggy + 10), 15) //See above ..() @@ -1207,7 +1217,7 @@ if (M.hallucination >= 5) M.hallucination -= 5 if(prob(20)) - M.adjustBrainLoss(1*REM, 50) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1*REM, 50) M.adjustStaminaLoss(2.5*REM, 0) ..() return TRUE diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 07c9b166ff..6b5e6d94bd 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -200,22 +200,20 @@ pH = 7.5 //God is alkaline /datum/reagent/water/holywater/on_mob_metabolize(mob/living/L) - ..() + . = ..() ADD_TRAIT(L, TRAIT_HOLY, id) + if(is_servant_of_ratvar(L)) + to_chat(L, "A fog spreads through your mind, purging the Justiciar's influence!") + else if(iscultist(L)) + to_chat(L, "A fog spreads through your mind, weakening your connection to the veil and purging Nar-sie's influence") + /datum/reagent/water/holywater/on_mob_end_metabolize(mob/living/L) REMOVE_TRAIT(L, TRAIT_HOLY, id) - ..() - -/datum/reagent/water/holywater/reaction_mob(mob/living/M, method=TOUCH, reac_volume) - if(is_servant_of_ratvar(M)) - to_chat(M, "A fog spreads through your mind, purging the Justiciar's influence!") - ..() - -/datum/reagent/water/holywater/reaction_mob(mob/living/M, method=TOUCH, reac_volume) - if(iscultist(M)) - to_chat(M, "A fog spreads through your mind, weakening your connection to the veil and purging Nar-sie's influence") - ..() + if(iscultist(L)) + for(var/datum/action/innate/cult/blood_magic/BM in L.actions) + BM.holy_dispel = FALSE + return ..() /datum/reagent/water/holywater/on_mob_life(mob/living/carbon/M) if(!data) @@ -224,9 +222,11 @@ M.jitteriness = min(M.jitteriness+4,10) if(iscultist(M)) for(var/datum/action/innate/cult/blood_magic/BM in M.actions) - to_chat(M, "Your blood rites falter as holy water scours your body!") - for(var/datum/action/innate/cult/blood_spell/BS in BM.spells) - qdel(BS) + if(!BM.holy_dispel) + BM.holy_dispel = TRUE + to_chat(M, "Your blood rites falter as holy water scours your body!") + for(var/datum/action/innate/cult/blood_spell/BS in BM.spells) + qdel(BS) if(data >= 25) // 10 units, 45 seconds @ metabolism 0.4 units & tick rate 1.8 sec if(!M.stuttering) M.stuttering = 1 @@ -296,7 +296,7 @@ if(ishuman(M) && M.blood_volume < (BLOOD_VOLUME_NORMAL*M.blood_ratio)) M.blood_volume += 3 else // Will deal about 90 damage when 50 units are thrown - M.adjustBrainLoss(3, 150) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3, 150) M.adjustToxLoss(2, 0) M.adjustFireLoss(2, 0) M.adjustOxyLoss(2, 0) @@ -315,7 +315,7 @@ M.IgniteMob() //Only problem with igniting people is currently the commonly availible fire suits make you immune to being on fire M.adjustToxLoss(1, 0) M.adjustFireLoss(1, 0) //Hence the other damages... ain't I a bastard? - M.adjustBrainLoss(5, 150) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5, 150) holder.remove_reagent(id, 1) pH = 0.1 @@ -536,12 +536,12 @@ race = /datum/species/fly mutationtext = "The pain subsides. You feel... buzzy." -/datum/reagent/mutationtoxin/moth - name = "Moth Mutation Toxin" +/datum/reagent/mutationtoxin/insect + name = "Insect Mutation Toxin" id = "mothmutationtoxin" description = "A glowing toxin." color = "#5EFF3B" //RGB: 94, 255, 59 - race = /datum/species/moth + race = /datum/species/insect mutationtext = "The pain subsides. You feel... attracted to light." /datum/reagent/mutationtoxin/pod @@ -805,7 +805,7 @@ step(M, pick(GLOB.cardinals)) if(prob(5)) M.emote(pick("twitch","drool","moan")) - M.adjustBrainLoss(1) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1) ..() /datum/reagent/sulfur @@ -1172,7 +1172,7 @@ /datum/reagent/impedrezene/on_mob_life(mob/living/carbon/M) M.jitteriness = max(M.jitteriness-5,0) if(prob(80)) - M.adjustBrainLoss(2*REM) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2*REM) if(prob(50)) M.drowsyness = max(M.drowsyness, 3) if(prob(10)) @@ -1222,7 +1222,7 @@ description = "A perfluoronated sulfonic acid that forms a foam when mixed with water." color = "#9E6B38" // rgb: 158, 107, 56 taste_description = "metal" - pH = 13 + pH = 11 /datum/reagent/foaming_agent// Metal foaming agent. This is lithium hydride. Add other recipes (e.g. LiH + H2O -> LiOH + H2) eventually. name = "Foaming agent" @@ -1231,7 +1231,7 @@ reagent_state = SOLID color = "#664B63" // rgb: 102, 75, 99 taste_description = "metal" - pH = 12.5 + pH = 11.5 /datum/reagent/smart_foaming_agent //Smart foaming agent. Functions similarly to metal foam, but conforms to walls. name = "Smart foaming agent" @@ -1445,9 +1445,6 @@ color = "#FFFFFF" // white random_color_list = list("#FFFFFF") //doesn't actually change appearance at all - - - //////////////////////////////////Hydroponics stuff/////////////////////////////// /datum/reagent/plantnutriment @@ -1489,16 +1486,8 @@ tox_prob = 15 pH = 1 - - - - - - // GOON OTHERS - - /datum/reagent/oil name = "Oil" id = "oil" @@ -1530,20 +1519,6 @@ taste_description = "metal" pH = 4.5 -/datum/reagent/carpet - name = "Carpet" - id = "carpet" - description = "For those that need a more creative way to roll out a red carpet." - reagent_state = LIQUID - color = "#b51d05" - taste_description = "carpet" // Your tounge feels furry. - -/datum/reagent/carpet/reaction_turf(turf/T, reac_volume) - if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) - var/turf/open/floor/F = T - F.PlaceOnTop(/turf/open/floor/carpet) - ..() - /datum/reagent/bromine name = "Bromine" id = "bromine" @@ -1676,7 +1651,7 @@ reagent_state = LIQUID color = "#FFFFD6" // very very light yellow taste_description = "alkali" //who put ACID for NaOH ???? - pH = 13 + pH = 11.9 /datum/reagent/drying_agent name = "Drying agent" @@ -1697,6 +1672,143 @@ qdel(O) new /obj/item/clothing/shoes/galoshes/dry(t_loc) +// Liquid Carpets +/datum/reagent/carpet + name = "Liquid Carpet" + id = "carpet" + description = "For those that need a more creative way to roll out a carpet." + reagent_state = LIQUID + color = "#b51d05" + taste_description = "carpet" // Your tounge feels furry. + +/datum/reagent/carpet/reaction_turf(turf/T, reac_volume) + if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) + var/turf/open/floor/F = T + F.PlaceOnTop(/turf/open/floor/carpet) + ..() + +/datum/reagent/carpet/black + name = "Liquid Black Carpet" + id = "blackcarpet" + color = "#363636" + +/datum/reagent/carpet/black/reaction_turf(turf/T, reac_volume) + if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) + var/turf/open/floor/F = T + F.PlaceOnTop(/turf/open/floor/carpet/black) + ..() + +/datum/reagent/carpet/blackred + name = "Liquid Red Black Carpet" + id = "blackredcarpet" + color = "#342125" + +/datum/reagent/carpet/blackred/reaction_turf(turf/T, reac_volume) + if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) + var/turf/open/floor/F = T + F.PlaceOnTop(/turf/open/floor/carpet/blackred) + ..() + +/datum/reagent/carpet/monochrome + name = "Liquid Monochrome Carpet" + id = "monochromecarpet" + color = "#b4b4b4" + +/datum/reagent/carpet/monochrome/reaction_turf(turf/T, reac_volume) + if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) + var/turf/open/floor/F = T + F.PlaceOnTop(/turf/open/floor/carpet/monochrome) + ..() + +/datum/reagent/carpet/blue + name = "Liquid Blue Carpet" + id = "bluecarpet" + color = "#1256ff" + +/datum/reagent/carpet/blue/reaction_turf(turf/T, reac_volume) + if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) + var/turf/open/floor/F = T + F.PlaceOnTop(/turf/open/floor/carpet/blue) + ..() + +/datum/reagent/carpet/cyan + name = "Liquid Cyan Carpet" + id = "cyancarpet" + color = "#3acfb9" + +/datum/reagent/carpet/cyan/reaction_turf(turf/T, reac_volume) + if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) + var/turf/open/floor/F = T + F.PlaceOnTop(/turf/open/floor/carpet/cyan) + ..() + +/datum/reagent/carpet/green + name = "Liquid Green Carpet" + id = "greencarpet" + color = "#619b62" + +/datum/reagent/carpet/green/reaction_turf(turf/T, reac_volume) + if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) + var/turf/open/floor/F = T + F.PlaceOnTop(/turf/open/floor/carpet/green) + ..() + +/datum/reagent/carpet/orange + name = "Liquid Orange Carpet" + id = "orangecarpet" + color = "#cc7900" + +/datum/reagent/carpet/orange/reaction_turf(turf/T, reac_volume) + if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) + var/turf/open/floor/F = T + F.PlaceOnTop(/turf/open/floor/carpet/orange) + ..() + +/datum/reagent/carpet/purple + name = "Liquid Purple Carpet" + id = "purplecarpet" + color = "#6d3392" + +/datum/reagent/carpet/purple/reaction_turf(turf/T, reac_volume) + if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) + var/turf/open/floor/F = T + F.PlaceOnTop(/turf/open/floor/carpet/purple) + ..() + +/datum/reagent/carpet/red + name = "Liquid Red Carpet" + id = "redcarpet" + color = "#871515" + +/datum/reagent/carpet/red/reaction_turf(turf/T, reac_volume) + if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) + var/turf/open/floor/F = T + F.PlaceOnTop(/turf/open/floor/carpet/red) + ..() + +/datum/reagent/carpet/royalblack + name = "Liquid Royal Black Carpet" + id = "royalblackcarpet" + color = "#483d05" + +/datum/reagent/carpet/royalblack/reaction_turf(turf/T, reac_volume) + if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) + var/turf/open/floor/F = T + F.PlaceOnTop(/turf/open/floor/carpet/royalblack) + ..() + +/datum/reagent/carpet/royalblue + name = "Liquid Royal Blue Carpet" + id = "royalbluecarpet" + color = "#24227e" + +/datum/reagent/carpet/royalblue/reaction_turf(turf/T, reac_volume) + if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) + var/turf/open/floor/F = T + F.PlaceOnTop(/turf/open/floor/carpet/royalblue) + ..() + + // Virology virus food chems. /datum/reagent/toxin/mutagen/mutagenvirusfood @@ -2003,7 +2115,7 @@ can_synth = FALSE var/datum/dna/original_dna var/reagent_ticks = 0 - invisible = TRUE + chemical_flags = REAGENT_INVISIBLE /datum/reagent/changeling_string/on_mob_metabolize(mob/living/carbon/C) if(C && C.dna && data["desired_dna"]) @@ -2031,3 +2143,25 @@ /datum/reagent/changeling_string/Destroy() qdel(original_dna) return ..() + +/datum/reagent/mustardgrind + name = "Mustardgrind" + id = "mustardgrind" + description = "A powerd that is mixed with water and enzymes to make mustard." + color = "#BCC740" //RGB: 188, 199, 64 + taste_description = "plant dust" + +/datum/reagent/pax/catnip + name = "catnip" + id = "catnip" + taste_description = "grass" + description = "A colorless liquid that makes people more peaceful and felines more happy." + metabolization_rate = 1.75 * REAGENTS_METABOLISM + +/datum/reagent/pax/catnip/on_mob_life(mob/living/carbon/M) + if(prob(20)) + M.emote("nya") + if(prob(20)) + to_chat(M, "[pick("Headpats feel nice.", "The feeling of a hairball...", "Backrubs would be nice.", "Whats behind those doors?")]") + M.adjustArousalLoss(2) + ..() diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 2848c336e2..2b50ecc0f1 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -313,25 +313,6 @@ . = 1 ..() -/datum/reagent/toxin/chloralhydratedelayed //sedates half as quickly and does not cause toxloss. same name/desc so it doesn't give away sleepypens - name = "Chloral Hydrate" - id = "chloralhydratedelayed" - description = "A powerful sedative that induces confusion and drowsiness before putting its target to sleep." - reagent_state = SOLID - color = "#000067" // rgb: 0, 0, 103 - toxpwr = 0 - metabolization_rate = 1 * REAGENTS_METABOLISM - -/datum/reagent/toxin/chloralhydratedelayed/on_mob_life(mob/living/carbon/M) - switch(current_cycle) - if(10 to 20) - M.confused += 1 - M.drowsyness += 1 - M.adjustStaminaLoss(7.5) - if(20 to INFINITY) - M.Sleeping(40, 0) - ..() - /datum/reagent/toxin/fakebeer //disguised as normal beer for use by emagged brobots name = "Beer" id = "fakebeer" @@ -389,12 +370,12 @@ id = "tirizene" description = "A nonlethal poison that causes extreme fatigue and weakness in its victim." color = "#6E2828" - data = 13 + data = 15 toxpwr = 0 /datum/reagent/toxin/staminatoxin/on_mob_life(mob/living/carbon/M) M.adjustStaminaLoss(REM * data, 0) - data = max(data - 1, 3) + data = max(data - 1, 5) ..() . = 1 @@ -490,7 +471,7 @@ toxpwr = 0 /datum/reagent/toxin/fentanyl/on_mob_life(mob/living/carbon/M) - M.adjustBrainLoss(3*REM, 150) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3*REM, 150) if(M.toxloss <= 60) M.adjustToxLoss(1*REM, 0) if(current_cycle >= 18) @@ -969,3 +950,20 @@ to_chat(M, "Your missing arm aches from wherever you left it.") M.emote("sigh") return ..() + +/datum/reagent/toxin/brainhurtingjuice //oof ouch + name = "Brain Hurting Juice" + id = "brainhurtingjuice" + color = "#AAAAAA77" //RGBA: 170, 170, 170, 77 + toxpwr = 0 + taste_description = "brain hurting" + metabolization_rate = 5 + +/datum/reagent/toxin/brainhurtingjuice/on_mob_life(mob/living/carbon/M) + if(prob(50)) + M.gain_trauma_type(BRAIN_TRAUMA_MILD) + else if(prob(50)) + M.gain_trauma_type(BRAIN_TRAUMA_SEVERE) + else + M.gain_trauma_type(BRAIN_TRAUMA_SPECIAL) + ..() diff --git a/code/modules/reagents/chemistry/recipes.dm b/code/modules/reagents/chemistry/recipes.dm index 32155c9b89..a915087428 100644 --- a/code/modules/reagents/chemistry/recipes.dm +++ b/code/modules/reagents/chemistry/recipes.dm @@ -32,6 +32,7 @@ var/RateUpLim = 10 // Optimal/max rate possible if all conditions are perfect var/FermiChem = FALSE // If the chemical uses the Fermichem reaction mechanics//If the chemical uses the Fermichem reaction mechanics var/FermiExplode = FALSE // If the chemical explodes in a special way + var/clear_conversion //bitflags for clear conversions; REACTION_CLEAR_IMPURE or REACTION_CLEAR_INVERSE var/PurityMin = 0.15 //If purity is below 0.15, it explodes too. Set to 0 to disable this. diff --git a/code/modules/reagents/chemistry/recipes/medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm index 59865dd1da..11dd8b7475 100644 --- a/code/modules/reagents/chemistry/recipes/medicine.dm +++ b/code/modules/reagents/chemistry/recipes/medicine.dm @@ -68,6 +68,42 @@ results = list("synthflesh" = 3) required_reagents = list("blood" = 1, "carbon" = 1, "styptic_powder" = 1) +/datum/chemical_reaction/synthtissue + name = "Synthtissue" + id = "synthtissue" + results = list("synthtissue" = 0.05) + required_reagents = list("synthflesh" = 0.01) + required_catalysts = list("nutriment" = 0.1) + //FermiChem vars: + OptimalTempMin = 305 // Lower area of bell curve for determining heat based rate reactions + OptimalTempMax = 315 // Upper end for above + ExplodeTemp = 1050 // Temperature at which reaction explodes + OptimalpHMin = 8.5 // Lowest value of pH determining pH a 1 value for pH based rate reactions (Plateu phase) + OptimalpHMax = 9.5 // Higest value for above + ReactpHLim = 2 // How far out pH wil react, giving impurity place (Exponential phase) + CatalystFact = 0 // How much the catalyst affects the reaction (0 = no catalyst) + CurveSharpT = 1 // How sharp the temperature exponential curve is (to the power of value) + CurveSharppH = 2.5 // How sharp the pH exponential curve is (to the power of value) + ThermicConstant = 0.01 // Temperature change per 1u produced + HIonRelease = 0.015 // pH change per 1u reaction (inverse for some reason) + RateUpLim = 0.05 // Optimal/max rate possible if all conditions are perfect + FermiChem = TRUE // If the chemical uses the Fermichem reaction mechanics + PurityMin = 0 + +/datum/chemical_reaction/synthtissue/FermiCreate(datum/reagents/holder, added_volume, added_purity) + var/datum/reagent/synthtissue/St = holder.has_reagent("synthtissue") + var/datum/reagent/N = holder.has_reagent("nutriment") + if(!St) + return + if(holder.chem_temp > 320) + var/temp_ratio = 1-(330 - holder.chem_temp)/10 + holder.remove_reagent(src.id, added_volume*temp_ratio) + if(St.purity < 1) + St.volume *= St.purity + St.purity = 1 + N.volume -= 0.002 + St.data["grown_volume"] = St.data["grown_volume"] + added_volume + /datum/chemical_reaction/styptic_powder name = "Styptic Powder" id = "styptic_powder" diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index e34f34675c..eaae548d68 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -82,6 +82,12 @@ required_temp = 374 mob_react = FALSE +/datum/chemical_reaction/mustard + name = "Mustard" + id = "mustard" + results = list("mustard" = 5) + required_reagents = list("mustardgrind" = 1, "water" = 10, "enzyme"= 1) + /datum/chemical_reaction/soapification/on_reaction(datum/reagents/holder, created_volume) var/location = get_turf(holder.my_atom) for(var/i = 1, i <= created_volume, i++) @@ -488,12 +494,6 @@ results = list("acetone" = 3) required_reagents = list("oil" = 1, "welding_fuel" = 1, "oxygen" = 1) -/datum/chemical_reaction/carpet - name = "carpet" - id = "carpet" - results = list("carpet" = 2) - required_reagents = list("space_drugs" = 1, "blood" = 1) - /datum/chemical_reaction/oil name = "Oil" id = "oil" @@ -623,7 +623,7 @@ required_temp = 450 /datum/chemical_reaction/moff - name = "moth mutation toxic" + name = "insect mutation toxic" id = "moffs" results = list("mothmutationtoxin" = 1) required_reagents = list("liquid_dark_matter" = 2, "ammonia" = 5, "lithium" = 1, "stablemutationtoxin" = 1) @@ -635,3 +635,77 @@ results = list("shadowmutationtoxin" = 1) required_reagents = list("liquid_dark_matter" = 5, "synaptizine" = 10, "oculine" = 10, "stablemutationtoxin" = 1) required_temp = 600 + +// Liquid Carpets + +/datum/chemical_reaction/carpet + name = "carpet" + id = "carpet" + results = list("carpet" = 2) + required_reagents = list("space_drugs" = 1, "blood" = 1) + +/datum/chemical_reaction/carpet/black + name = "liquid black carpet" + id = "blackcarpet" + results = list("blackcarpet" = 2) + required_reagents = list("carpet" = 1, "carbon" = 1) + +/datum/chemical_reaction/carpet/blackred + name = "liquid red black carpet" + id = "blackredcarpet" + results = list("blackredcarpet" = 2) + required_reagents = list("carpet" = 1, "charcoal" = 1) + +/datum/chemical_reaction/carpet/monochrome + name = "liquid monochrome carpet" + id = "monochromecarpet" + results = list("monochromecarpet" = 2) + required_reagents = list("carpet" = 1, "oil" = 1) + +/datum/chemical_reaction/carpet/blue + name = "liquid blue carpet" + id = "bluecarpet" + results = list("bluecarpet" = 2) + required_reagents = list("carpet" = 1, "tonic" = 1) + +/datum/chemical_reaction/carpet/cyan + name = "liquid cyan carpet" + id = "cyancarpet" + results = list("cyancarpet" = 2) + required_reagents = list("carpet" = 1, "ice" = 1) + +/datum/chemical_reaction/carpet/green + name = "liquid green carpet" + id = "greencarpet" + results = list("greencarpet" = 2) + required_reagents = list("carpet" = 1, "sacid" = 1) + +/datum/chemical_reaction/carpet/orange + name = "liquid orange carpet" + id = "orangecarpet" + results = list("orangecarpet" = 2) + required_reagents = list("carpet" = 1, "orangejuice" = 1) + +/datum/chemical_reaction/carpet/purple + name = "liquid purple carpet" + id = "purplecarpet" + results = list("purplecarpet" = 2) + required_reagents = list("carpet" = 1, "stable_plasma" = 1) + +/datum/chemical_reaction/carpet/red + name = "liquid red carpet" + id = "redcarpet" + results = list("redcarpet" = 2) + required_reagents = list("carpet" = 1, "welding_fuel" = 1) + +/datum/chemical_reaction/carpet/royalblack + name = "liquid royal black carpet" + id = "royalblackcarpet" + results = list("royalblackcarpet" = 2) + required_reagents = list("carpet" = 1, "blackpepper" = 1) + +/datum/chemical_reaction/carpet/royalblue + name = "liquid royal blue carpet" + id = "royalbluecarpet" + results = list("royalbluecarpet" = 2) + required_reagents = list("carpet" = 1, "clonexadone" = 1) diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 48abfcb649..b3f9591f64 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -17,6 +17,7 @@ var/spillable = FALSE var/beaker_weakness_bitflag = NONE//Bitflag! var/container_HP = 2 + var/cached_icon /obj/item/reagent_containers/Initialize(mapload, vol) . = ..() @@ -148,30 +149,71 @@ /obj/item/reagent_containers/proc/temp_check() if(beaker_weakness_bitflag & TEMP_WEAK) if(reagents.chem_temp >= 444)//assuming polypropylene - var/list/seen = viewers(5, get_turf(src)) - var/iconhtml = icon2html(src, seen) - for(var/mob/M in seen) - to_chat(M, "[iconhtml] \The [src]'s melts from the temperature!") - playsound(get_turf(src), 'sound/FermiChem/heatmelt.ogg', 80, 1) - to_chat(M, "[iconhtml] Have you tried using glass or meta beakers for high temperature reactions? These are immune to temperature effects.") - SSblackbox.record_feedback("tally", "fermi_chem", 1, "Times beakers have melted from temperature") - qdel(src) + START_PROCESSING(SSobj, src) //melts glass beakers /obj/item/reagent_containers/proc/pH_check() if(beaker_weakness_bitflag & PH_WEAK) - if((reagents.pH < 0.5) || (reagents.pH > 13.5)) - var/list/seen = viewers(5, get_turf(src)) - var/iconhtml = icon2html(src, seen) - container_HP-- - if(container_HP <= 0) - for(var/mob/M in seen) - to_chat(M, "[iconhtml] \The [src]'s melts from the extreme pH!") - playsound(get_turf(src), 'sound/FermiChem/acidmelt.ogg', 80, 1) - SSblackbox.record_feedback("tally", "fermi_chem", 1, "Times beakers have melted from pH") - qdel(src) + if((reagents.pH < 1.5) || (reagents.pH > 12.5)) + START_PROCESSING(SSobj, src) + + +/obj/item/reagent_containers/process() + if(!cached_icon) + cached_icon = icon_state + var/damage + var/cause + if(beaker_weakness_bitflag & PH_WEAK) + if(reagents.pH < 2) + damage = (2 - reagents.pH)/20 + cause = "from the extreme pH" + playsound(get_turf(src), 'sound/FermiChem/bufferadd.ogg', 50, 1) + + if(reagents.pH > 12) + damage = (reagents.pH - 12)/20 + cause = "from the extreme pH" + playsound(get_turf(src), 'sound/FermiChem/bufferadd.ogg', 50, 1) + + if(beaker_weakness_bitflag & TEMP_WEAK) + if(reagents.chem_temp >= 444) + if(damage) + damage += (reagents.chem_temp/444)/5 else - for(var/mob/M in seen) - to_chat(M, "[iconhtml] \The [src]'s is damaged by the extreme pH and begins to deform!") - playsound(get_turf(src), 'sound/FermiChem/bufferadd.ogg', 50, 1) - to_chat(M, "[iconhtml] Have you tried using plastic beakers (XL) or metabeakers for high pH reactions? These beakers are immune to pH effects.") + damage = (reagents.chem_temp/444)/5 + if(cause) + cause += " and " + cause += "from the high temperature" + playsound(get_turf(src), 'sound/FermiChem/heatdam.ogg', 50, 1) + + if(!damage || damage <= 0) + STOP_PROCESSING(SSobj, src) + + container_HP -= damage + + var/list/seen = viewers(5, get_turf(src)) + var/iconhtml = icon2html(src, seen) + + var/damage_percent = ((container_HP / initial(container_HP)*100)) + switch(damage_percent) + if(-INFINITY to 0) + for(var/mob/M in seen) + to_chat(M, "[iconhtml] \The [src]'s melts [cause]!") + playsound(get_turf(src), 'sound/FermiChem/acidmelt.ogg', 80, 1) + SSblackbox.record_feedback("tally", "fermi_chem", 1, "Times beakers have melted") + STOP_PROCESSING(SSobj, src) + qdel(src) + return + if(0 to 35) + icon_state = "[cached_icon]_m3" + desc = "[initial(desc)] It is severely deformed." + if(35 to 70) + icon_state = "[cached_icon]_m2" + desc = "[initial(desc)] It is deformed." + if(70 to 85) + desc = "[initial(desc)] It is mildly deformed." + icon_state = "[cached_icon]_m1" + + update_icon() + if(prob(25)) + for(var/mob/M in seen) + to_chat(M, "[iconhtml] \The [src]'s is damaged by [cause] and begins to deform!") diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm index 31e893aa3a..3e555f385f 100644 --- a/code/modules/reagents/reagent_containers/blood_pack.dm +++ b/code/modules/reagents/reagent_containers/blood_pack.dm @@ -90,3 +90,9 @@ update_pack_name() else return ..() + +/obj/item/reagent_containers/blood/bluespace + name = "bluespace blood pack" + desc = "Contains blood used for transfusion, this one has been made with bluespace technology to hold much more blood. Must be attached to an IV drip." + icon_state = "bsbloodpack" + volume = 600 //its a blood bath! \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index 014401b72c..257106aaa7 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -6,7 +6,7 @@ reagent_flags = OPENCONTAINER spillable = TRUE resistance_flags = ACID_PROOF - container_HP = 3 + container_HP = 2 /obj/item/reagent_containers/glass/attack(mob/M, mob/user, obj/target) @@ -115,7 +115,6 @@ item_state = "beaker" materials = list(MAT_GLASS=500) beaker_weakness_bitflag = PH_WEAK - container_HP = 5 /obj/item/reagent_containers/glass/beaker/Initialize() . = ..() @@ -128,27 +127,29 @@ update_icon() /obj/item/reagent_containers/glass/beaker/update_icon() + if(!cached_icon) + cached_icon = icon_state cut_overlays() if(reagents.total_volume) - var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "[icon_state]10") + var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "[cached_icon]10") var/percent = round((reagents.total_volume / volume) * 100) switch(percent) if(0 to 9) - filling.icon_state = "[icon_state]-10" + filling.icon_state = "[cached_icon]-10" if(10 to 24) - filling.icon_state = "[icon_state]10" + filling.icon_state = "[cached_icon]10" if(25 to 49) - filling.icon_state = "[icon_state]25" + filling.icon_state = "[cached_icon]25" if(50 to 74) - filling.icon_state = "[icon_state]50" + filling.icon_state = "[cached_icon]50" if(75 to 79) - filling.icon_state = "[icon_state]75" + filling.icon_state = "[cached_icon]75" if(80 to 90) - filling.icon_state = "[icon_state]80" + filling.icon_state = "[cached_icon]80" if(91 to INFINITY) - filling.icon_state = "[icon_state]100" + filling.icon_state = "[cached_icon]100" filling.color = mix_color_from_reagents(reagents.reagent_list) add_overlay(filling) @@ -167,7 +168,7 @@ volume = 100 amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,20,25,30,50,100) - container_HP = 6 + container_HP = 3 /obj/item/reagent_containers/glass/beaker/plastic name = "x-large beaker" @@ -227,7 +228,7 @@ volume = 300 amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,20,25,30,50,100,300) - container_HP = 8 + container_HP = 4 /obj/item/reagent_containers/glass/beaker/cryoxadone list_reagents = list("cryoxadone" = 30) @@ -284,7 +285,7 @@ SLOT_L_STORE, SLOT_R_STORE,\ SLOT_GENERC_DEXTROUS_STORAGE ) - container_HP = 2 + container_HP = 1 /obj/item/reagent_containers/glass/bucket/Initialize() beaker_weakness_bitflag |= TEMP_WEAK @@ -338,7 +339,7 @@ materials = list(MAT_GLASS=0) volume = 50 amount_per_transfer_from_this = 10 - container_HP = 2 + container_HP = 1 /obj/item/reagent_containers/glass/beaker/waterbottle/Initialize() beaker_weakness_bitflag |= TEMP_WEAK @@ -354,7 +355,7 @@ list_reagents = list("water" = 100) volume = 100 amount_per_transfer_from_this = 20 - container_HP = 2 + container_HP = 1 /obj/item/reagent_containers/glass/beaker/waterbottle/large/empty list_reagents = list() diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 82d95cea40..a96ba006c9 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -127,6 +127,21 @@ else to_chat(usr, "It is spent.") +/obj/item/reagent_containers/hypospray/medipen/stimulants + name = "illegal stimpack medipen" + desc = "A highly illegal medipen due to its load and small injections, allow for five uses before being drained" + volume = 50 + amount_per_transfer_from_this = 10 + list_reagents = list("stimulants" = 50) + +/obj/item/reagent_containers/hypospray/medipen/stimulants/baseball + name = "the reason the syndicate major league team wins." + desc = "They say drugs never win, but look where you are now, then where they are." + icon_state = "baseballstim" + volume = 50 + amount_per_transfer_from_this = 50 + list_reagents = list("stimulants" = 50) + /obj/item/reagent_containers/hypospray/medipen/stimpack //goliath kiting name = "stimpack medipen" desc = "A rapid way to stimulate your body's adrenaline, allowing for freer movement in restrictive armor." diff --git a/code/modules/reagents/reagent_containers/medspray.dm b/code/modules/reagents/reagent_containers/medspray.dm index 54a38eef86..df7c7af225 100644 --- a/code/modules/reagents/reagent_containers/medspray.dm +++ b/code/modules/reagents/reagent_containers/medspray.dm @@ -19,8 +19,8 @@ var/can_fill_from_container = TRUE var/apply_type = PATCH var/apply_method = "spray" - var/self_delay = 30 - var/squirt_mode = 0 + var/self_delay = 3 SECONDS + var/squirt_mode = FALSE var/squirt_amount = 5 /obj/item/reagent_containers/medspray/attack_self(mob/user) @@ -31,40 +31,51 @@ amount_per_transfer_from_this = initial(amount_per_transfer_from_this) to_chat(user, "You will now apply the medspray's contents in [squirt_mode ? "short bursts":"extended sprays"]. You'll now use [amount_per_transfer_from_this] units per use.") -/obj/item/reagent_containers/medspray/attack(mob/M, mob/user, def_zone) +/obj/item/reagent_containers/medspray/attack(mob/living/L, mob/user, def_zone) if(!reagents || !reagents.total_volume) to_chat(user, "[src] is empty!") return - if(M == user) - M.visible_message("[user] attempts to [apply_method] [src] on [user.p_them()]self.") + if(ishuman(L)) + var/obj/item/bodypart/affecting = L.get_bodypart(check_zone(user.zone_selected)) + if(!affecting) + to_chat(user, "The limb is missing!") + return + if(!L.can_inject(user, TRUE, user.zone_selected, FALSE, TRUE)) //stopped by clothing, like patches + return + if(affecting.status != BODYPART_ORGANIC) + to_chat(user, "Medicine won't work on a robotic limb!") + return + + if(L == user) + L.visible_message("[user] attempts to [apply_method] [src] on [user.p_them()]self.") if(self_delay) - if(!do_mob(user, M, self_delay)) + if(!do_mob(user, L, self_delay)) return if(!reagents || !reagents.total_volume) return - to_chat(M, "You [apply_method] yourself with [src].") + to_chat(L, "You [apply_method] yourself with [src].") else - log_combat(user, M, "attempted to apply", src, reagents.log_list()) - M.visible_message("[user] attempts to [apply_method] [src] on [M].", \ - "[user] attempts to [apply_method] [src] on [M].") - if(!do_mob(user, M)) + log_combat(user, L, "attempted to apply", src, reagents.log_list()) + L.visible_message("[user] attempts to [apply_method] [src] on [L].", \ + "[user] attempts to [apply_method] [src] on [L].") + if(!do_mob(user, L)) return if(!reagents || !reagents.total_volume) return - M.visible_message("[user] [apply_method]s [M] down with [src].", \ - "[user] [apply_method]s [M] down with [src].") + L.visible_message("[user] [apply_method]s [L] down with [src].", \ + "[user] [apply_method]s [L] down with [src].") if(!reagents || !reagents.total_volume) return else - log_combat(user, M, "applied", src, reagents.log_list()) + log_combat(user, L, "applied", src, reagents.log_list()) playsound(src, 'sound/effects/spray2.ogg', 50, 1, -6) var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1) - reagents.reaction(M, apply_type, fraction) - reagents.trans_to(M, amount_per_transfer_from_this) + reagents.reaction(L, apply_type, fraction) + reagents.trans_to(L, amount_per_transfer_from_this) return /obj/item/reagent_containers/medspray/styptic diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 19afab2e6e..216ecd9654 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -39,6 +39,10 @@ else qdel(src) +/////////////// +//Water Tanks// +/////////////// + /obj/structure/reagent_dispensers/watertank name = "water tank" desc = "A water tank." @@ -57,12 +61,52 @@ reagent_id = "firefighting_foam" tank_volume = 500 +/obj/structure/reagent_dispensers/water_cooler + name = "liquid cooler" + desc = "A machine that dispenses liquid to drink." + icon = 'icons/obj/vending.dmi' + icon_state = "water_cooler" + anchored = TRUE + tank_volume = 500 + var/paper_cups = 25 //Paper cups left from the cooler + +/obj/structure/reagent_dispensers/water_cooler/examine(mob/user) + ..() + if (paper_cups > 1) + to_chat(user, "There are [paper_cups] paper cups left.") + else if (paper_cups == 1) + to_chat(user, "There is one paper cup left.") + else + to_chat(user, "There are no paper cups left.") + +/obj/structure/reagent_dispensers/water_cooler/attack_hand(mob/living/user) + . = ..() + if(.) + return + if(!paper_cups) + to_chat(user, "There aren't any cups left!") + return + user.visible_message("[user] takes a cup from [src].", "You take a paper cup from [src].") + var/obj/item/reagent_containers/food/drinks/sillycup/S = new(get_turf(src)) + user.put_in_hands(S) + paper_cups-- + +////////////// +//Fuel Tanks// +////////////// + /obj/structure/reagent_dispensers/fueltank name = "fuel tank" desc = "A tank full of industrial welding fuel. Do not consume." icon_state = "fuel" reagent_id = "welding_fuel" +/obj/structure/reagent_dispensers/fueltank/high //Unused - Good for ghost roles + name = "high-capacity fuel tank" + desc = "A now illegal tank, full of highly pressurized industrial welding fuel. Do not consume or have a open flame close to this tank." + icon_state = "fuel_high" + tank_volume = 3000 + /obj/structure/reagent_dispensers/fueltank/boom() explosion(get_turf(src), 0, 1, 5, flame_range = 5) qdel(src) @@ -117,6 +161,9 @@ return return ..() +/////////////////// +//Misc Dispenders// +/////////////////// /obj/structure/reagent_dispensers/peppertank name = "pepper spray refiller" @@ -131,36 +178,24 @@ if(prob(1)) desc = "IT'S PEPPER TIME, BITCH!" - -/obj/structure/reagent_dispensers/water_cooler - name = "liquid cooler" - desc = "A machine that dispenses liquid to drink." - icon = 'icons/obj/vending.dmi' - icon_state = "water_cooler" +/obj/structure/reagent_dispensers/virusfood + name = "virus food dispenser" + desc = "A dispenser of low-potency virus mutagenic." + icon_state = "virus_food" anchored = TRUE - tank_volume = 500 - var/paper_cups = 25 //Paper cups left from the cooler + density = FALSE + reagent_id = "virusfood" -/obj/structure/reagent_dispensers/water_cooler/examine(mob/user) - ..() - if (paper_cups > 1) - to_chat(user, "There are [paper_cups] paper cups left.") - else if (paper_cups == 1) - to_chat(user, "There is one paper cup left.") - else - to_chat(user, "There are no paper cups left.") +/obj/structure/reagent_dispensers/cooking_oil + name = "vat of cooking oil" + desc = "A huge metal vat with a tap on the front. Filled with cooking oil for use in frying food." + icon_state = "vat" + anchored = TRUE + reagent_id = "cooking_oil" -/obj/structure/reagent_dispensers/water_cooler/attack_hand(mob/living/user) - . = ..() - if(.) - return - if(!paper_cups) - to_chat(user, "There aren't any cups left!") - return - user.visible_message("[user] takes a cup from [src].", "You take a paper cup from [src].") - var/obj/item/reagent_containers/food/drinks/sillycup/S = new(get_turf(src)) - user.put_in_hands(S) - paper_cups-- +//////// +//Kegs// +//////// /obj/structure/reagent_dispensers/beerkeg name = "beer keg" @@ -173,19 +208,44 @@ if(!QDELETED(src)) qdel(src) +/obj/structure/reagent_dispensers/keg + name = "keg" + desc = "A keg." + icon = 'modular_citadel/icons/obj/objects.dmi' + icon_state = "keg" + reagent_id = "water" -/obj/structure/reagent_dispensers/virusfood - name = "virus food dispenser" - desc = "A dispenser of low-potency virus mutagenic." - icon_state = "virus_food" - anchored = TRUE - density = FALSE - reagent_id = "virusfood" +/obj/structure/reagent_dispensers/keg/mead + name = "keg of mead" + desc = "A keg of mead." + icon_state = "orangekeg" + reagent_id = "mead" +/obj/structure/reagent_dispensers/keg/aphro + name = "keg of aphrodisiac" + desc = "A keg of aphrodisiac." + icon_state = "pinkkeg" + reagent_id = "aphro" -/obj/structure/reagent_dispensers/cooking_oil - name = "vat of cooking oil" - desc = "A huge metal vat with a tap on the front. Filled with cooking oil for use in frying food." - icon_state = "vat" - anchored = TRUE - reagent_id = "cooking_oil" +/obj/structure/reagent_dispensers/keg/aphro/strong + name = "keg of strong aphrodisiac" + desc = "A keg of strong and addictive aphrodisiac." + reagent_id = "aphro+" + +/obj/structure/reagent_dispensers/keg/milk + name = "keg of milk" + desc = "It's not quite what you were hoping for." + icon_state = "whitekeg" + reagent_id = "milk" + +/obj/structure/reagent_dispensers/keg/semen + name = "keg of semen" + desc = "Dear lord, where did this even come from?" + icon_state = "whitekeg" + reagent_id = "semen" + +/obj/structure/reagent_dispensers/keg/gargle + name = "keg of pan galactic gargleblaster" + desc = "A keg of... wow that's a long name." + icon_state = "bluekeg" + reagent_id = "gargleblaster" \ No newline at end of file diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm index 871fd32b16..a4a6a7d473 100644 --- a/code/modules/recycling/disposal/bin.dm +++ b/code/modules/recycling/disposal/bin.dm @@ -111,14 +111,7 @@ stuff_mob_in(target, user) /obj/machinery/disposal/proc/stuff_mob_in(mob/living/target, mob/living/user) - if(!iscarbon(user) && !user.ventcrawler) //only carbon and ventcrawlers can climb into disposal by themselves. - return - if(!isturf(user.loc)) //No magically doing it from inside closets - return - if(target.buckled || target.has_buckled_mobs()) - return - if(target.mob_size > MOB_SIZE_HUMAN) - to_chat(user, "[target] doesn't fit inside [src]!") + if(!can_stuff_mob_in(target, user)) return add_fingerprint(user) if(user == target) @@ -137,6 +130,19 @@ target.LAssailant = user update_icon() +/obj/machinery/disposal/proc/can_stuff_mob_in(mob/living/target, mob/living/user, pushing = FALSE) + if(!pushing && !iscarbon(user) && !user.ventcrawler) //only carbon and ventcrawlers can climb into disposal by themselves. + return FALSE + if(!isturf(user.loc)) //No magically doing it from inside closets + return FALSE + if(target.buckled || target.has_buckled_mobs()) + return FALSE + if(target.mob_size > MOB_SIZE_HUMAN) + if(!pushing) + to_chat(user, "[target] doesn't fit inside [src]!") + return FALSE + return TRUE + /obj/machinery/disposal/relaymove(mob/user) attempt_escape(user) @@ -264,6 +270,14 @@ name = "disposal unit" desc = "A pneumatic waste disposal unit." icon_state = "disposal" + var/datum/oracle_ui/themed/nano/ui + obj_flags = CAN_BE_HIT | USES_TGUI | SHOVABLE_ONTO + +/obj/machinery/disposal/bin/Initialize(mapload, obj/structure/disposalconstruct/make_from) + . = ..() + ui = new /datum/oracle_ui/themed/nano(src, 330, 190, "disposal_bin") + ui.auto_refresh = TRUE + ui.can_resize = FALSE // attack by item places it in to disposal /obj/machinery/disposal/bin/attackby(obj/item/I, mob/user, params) @@ -275,32 +289,43 @@ STR.remove_from_storage(O,src) T.update_icon() update_icon() + ui.soft_update_fields() else + ui.soft_update_fields() return ..() // handle machine interaction -/obj/machinery/disposal/bin/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) +/obj/machinery/disposal/bin/ui_interact(mob/user, state) if(stat & BROKEN) return - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "disposal_unit", name, 300, 200, master_ui, state) - ui.open() + if(user.loc == src) + to_chat(user, "You cannot reach the controls from inside!") + return + ui.render(user) -/obj/machinery/disposal/bin/ui_data(mob/user) +/obj/machinery/disposal/bin/oui_canview(mob/user) + if(user.loc == src) + return FALSE + if(stat & BROKEN) + return FALSE + if(Adjacent(user)) + return TRUE + return ..() + + +/obj/machinery/disposal/bin/oui_data(mob/user) var/list/data = list() - data["flush"] = flush - data["full_pressure"] = full_pressure - data["pressure_charging"] = pressure_charging - data["panel_open"] = panel_open - var/per = CLAMP(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 100) - data["per"] = round(per, 1) + data["flush"] = flush ? ui.act("Disengage", user, "handle-0", class="active") : ui.act("Engage", user, "handle-1") + data["full_pressure"] = full_pressure ? "Ready" : (pressure_charging ? "Pressurizing" : "Off") + data["pressure_charging"] = pressure_charging ? ui.act("Turn Off", user, "pump-0", class="active", disabled=full_pressure) : ui.act("Turn On", user, "pump-1", disabled=full_pressure) + var/per = full_pressure ? 100 : CLAMP(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 99) + data["per"] = "[round(per, 1)]%" + data["contents"] = ui.act("Eject Contents", user, "eject", disabled=contents.len < 1) data["isai"] = isAI(user) return data -/obj/machinery/disposal/bin/ui_act(action, params) +/obj/machinery/disposal/bin/oui_act(mob/user, action, list/params) if(..()) return @@ -327,6 +352,7 @@ if("eject") eject() . = TRUE + ui.soft_update_fields() /obj/machinery/disposal/bin/hitby(atom/movable/AM) @@ -341,11 +367,23 @@ else return ..() +/obj/machinery/disposal/bin/shove_act(mob/living/target, mob/living/user) + if(!can_stuff_mob_in(target, user, TRUE)) + return FALSE + target.Knockdown(SHOVE_KNOCKDOWN_SOLID) + target.forceMove(src) + user.visible_message("[user.name] shoves [target.name] into \the [src]!", + "You shove [target.name] into \the [src]!", null, COMBAT_MESSAGE_RANGE) + log_combat(user, target, "shoved", "into [src] (disposal bin)") + return TRUE + + /obj/machinery/disposal/bin/flush() ..() full_pressure = FALSE pressure_charging = TRUE update_icon() + ui.soft_update_fields() /obj/machinery/disposal/bin/update_icon() cut_overlays() @@ -389,7 +427,7 @@ do_flush() flush_count = 0 - updateDialog() + ui.soft_update_fields() if(flush && air_contents.return_pressure() >= SEND_PRESSURE) // flush can happen even without power do_flush() diff --git a/code/modules/research/designs/limbgrower_designs.dm b/code/modules/research/designs/limbgrower_designs.dm index 71f7234e34..dae59408c8 100644 --- a/code/modules/research/designs/limbgrower_designs.dm +++ b/code/modules/research/designs/limbgrower_designs.dm @@ -8,7 +8,7 @@ build_type = LIMBGROWER reagents_list = list("synthflesh" = 25) build_path = /obj/item/bodypart/l_arm - category = list("initial","human","lizard","fly","moth","plasmaman") + category = list("initial","human","lizard","fly","insect","plasmaman","mammal","xeno") /datum/design/rightarm name = "Right Arm" @@ -16,7 +16,7 @@ build_type = LIMBGROWER reagents_list = list("synthflesh" = 25) build_path = /obj/item/bodypart/r_arm - category = list("initial","human","lizard","fly","moth","plasmaman") + category = list("initial","human","lizard","fly","insect","plasmaman","mammal","xeno") /datum/design/leftleg name = "Left Leg" @@ -24,7 +24,7 @@ build_type = LIMBGROWER reagents_list = list("synthflesh" = 25) build_path = /obj/item/bodypart/l_leg - category = list("initial","human","lizard","fly","moth","plasmaman") + category = list("initial","human","lizard","fly","insect","plasmaman","mammal","xeno") /datum/design/rightleg name = "Right Leg" @@ -32,7 +32,7 @@ build_type = LIMBGROWER reagents_list = list("synthflesh" = 25) build_path = /obj/item/bodypart/r_leg - category = list("initial","human","lizard","fly","moth","plasmaman") + category = list("initial","human","lizard","fly","insect","plasmaman","mammal","xeno") /datum/design/armblade name = "Arm Blade" diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 33568524cd..d4f13f6a2e 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -192,6 +192,16 @@ category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL +/datum/design/bsblood_bag + name = "Blue Space Empty Blood Bag" + desc = "A large sterilized plastic bag for blood." + id = "bsblood_bag" + build_path = /obj/item/reagent_containers/blood/bluespace + build_type = PROTOLATHE + materials = list(MAT_GLASS = 2500, MAT_PLASTIC = 4500, MAT_BLUESPACE = 250) + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + /datum/design/cloning_disk name = "Cloning Data Disk" desc = "Produce additional disks for storing genetic data." @@ -524,6 +534,17 @@ category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL +/datum/design/cybernetic_heart_u + name = "Upgraded Cybernetic Heart" + desc = "An upgraded cybernetic heart" + id = "cybernetic_heart_u" + build_type = PROTOLATHE | MECHFAB + construction_time = 50 + materials = list(MAT_METAL = 500, MAT_GLASS = 500, MAT_SILVER = 500) + build_path = /obj/item/organ/heart/cybernetic/upgraded + category = list("Misc", "Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + /datum/design/cybernetic_liver_u name = "Upgraded Cybernetic Liver" desc = "An upgraded cybernetic liver" @@ -702,6 +723,28 @@ category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL +/datum/design/cybernetic_ears + name = "Cybernetic Ears" + desc = "A pair of cybernetic ears." + id = "cybernetic_ears" + build_type = PROTOLATHE | MECHFAB + construction_time = 30 + materials = list(MAT_METAL = 250, MAT_GLASS = 400) + build_path = /obj/item/organ/ears/cybernetic + category = list("Misc", "Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +/datum/design/cybernetic_ears_u + name = "Upgraded Cybernetic Ears" + desc = "A pair of upgraded cybernetic ears." + id = "cybernetic_ears_u" + build_type = PROTOLATHE | MECHFAB + construction_time = 40 + materials = list(MAT_METAL = 500, MAT_GLASS = 500, MAT_SILVER = 500) + build_path = /obj/item/organ/ears/cybernetic/upgraded + category = list("Misc", "Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + ///////////////////// ///Surgery Designs/// ///////////////////// @@ -713,6 +756,13 @@ research_icon = 'icons/obj/surgery.dmi' research_icon_state = "surgery_any" var/surgery + +/datum/design/surgery/experimental_dissection + name = "Experimental Dissection" + desc = "A surgical procedure which deeply analyzes the biology of a corpse, and automatically adds new findings to the research database." + id = "surgery_exp_dissection" + surgery = /datum/surgery/advanced/bioware/experimental_dissection + research_icon_state = "surgery_chest" /datum/design/surgery/lobotomy name = "Lobotomy" @@ -742,6 +792,13 @@ surgery = /datum/surgery/advanced/reconstruction research_icon_state = "surgery_chest" +/datum/design/surgery/surgery_toxinhealing + name = "Body Rejuvenation" + desc = "A surgical procedure that helps deal with oxygen deprecation, and treat toxic damaged. Works on corpses and alive alike without chemicals." + id = "surgery_toxinhealing" + surgery = /datum/surgery/advanced/toxichealing + research_icon_state = "surgery_chest" + /datum/design/surgery/revival name = "Revival" desc = "An experimental surgical procedure which involves reconstruction and reactivation of the patient's brain even long after death. The body must still be able to sustain life." @@ -777,6 +834,22 @@ surgery = /datum/surgery/advanced/bioware/vein_threading research_icon_state = "surgery_chest" +/datum/design/surgery/ligament_hook + name = "Ligament Hook" + desc = "A surgical procedure which reshapes the connections between torso and limbs, making it so limbs can be attached manually if severed. \ + However this weakens the connection, making them easier to detach as well." + id = "surgery_ligament_hook" + surgery = /datum/surgery/advanced/bioware/ligament_hook + research_icon_state = "surgery_chest" + +/datum/design/surgery/ligament_reinforcement + name = "Ligament Reinforcement" + desc = "A surgical procedure which adds a protective tissue and bone cage around the connections between the torso and limbs, preventing dismemberment. \ + However, the nerve connections as a result are more easily interrupted, making it easier to disable limbs with damage." + id = "surgery_ligament_reinforcement" + surgery = /datum/surgery/advanced/bioware/ligament_reinforcement + research_icon_state = "surgery_chest" + /datum/design/surgery/necrotic_revival name = "Necrotic Revival" desc = "An experimental surgical procedure that stimulates the growth of a Romerol tumor inside the patient's brain. Requires zombie powder or rezadone." diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm index 4a6f9625f1..77eebca696 100644 --- a/code/modules/research/designs/misc_designs.dm +++ b/code/modules/research/designs/misc_designs.dm @@ -379,6 +379,16 @@ ////////////Tools////////////// /////////////////////////////// +/datum/design/rcd_upgrade + name = "Advanced RCD designs upgrade" + desc = "Adds the computer frame and machine frame to the RCD." + id = "rcd_upgrade" + build_type = PROTOLATHE + materials = list(MAT_METAL = 5000, MAT_GLASS = 2500, MAT_SILVER = 1500, MAT_TITANIUM = 2000) + build_path = /obj/item/rcd_upgrade + category = list("Equipment") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + /datum/design/exwelder name = "Experimental Welding Tool" desc = "An experimental welder capable of self-fuel generation." diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm index 214ef36afe..11bc31bedc 100644 --- a/code/modules/research/designs/weapon_designs.dm +++ b/code/modules/research/designs/weapon_designs.dm @@ -47,6 +47,22 @@ build_path = /obj/item/ammo_box/magazine/wt550m9/wtic departmental_flags = DEPARTMENTAL_FLAG_SECURITY +/datum/design/mag_oldsmg/tx_mag + name = "WT-550 Semi-Auto SMG Uranium Magazine (4.6x30mm TX)" + desc = "A 20 round uranium tipped magazine for the out of date security WT-550 Semi-Auto SMG." + id = "mag_oldsmg_tx" + materials = list(MAT_METAL = 6000, MAT_SILVER = 600, MAT_URANIUM = 2000) + build_path = /obj/item/ammo_box/magazine/wt550m9/wttx + departmental_flags = DEPARTMENTAL_FLAG_SECURITY + +/datum/design/mag_oldsmg/rubber_mag + name = "WT-550 Semi-Auto SMG rubberbullets Magazine (4.6x30mm rubber)" + desc = "A 20 round rubber shots magazine for the out of date security WT-550 Semi-Auto SMG" + id = "mag_oldsmg_rubber" + materials = list(MAT_METAL = 6000) + build_path = /obj/item/ammo_box/magazine/wt550m9/wtrubber + departmental_flags = DEPARTMENTAL_FLAG_SECURITY + ////////////// //Ammo Shells/ ////////////// diff --git a/code/modules/research/nanites/nanite_programs/healing.dm b/code/modules/research/nanites/nanite_programs/healing.dm index 8aab7f1e2b..66a5344d6c 100644 --- a/code/modules/research/nanites/nanite_programs/healing.dm +++ b/code/modules/research/nanites/nanite_programs/healing.dm @@ -74,12 +74,12 @@ var/mob/living/carbon/C = host_mob if(length(C.get_traumas())) return ..() - if(host_mob.getBrainLoss()) + if(host_mob.getOrganLoss(ORGAN_SLOT_BRAIN)) return ..() return FALSE /datum/nanite_program/brain_heal/active_effect() - host_mob.adjustBrainLoss(-1, TRUE) + host_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1, TRUE) if(iscarbon(host_mob) && prob(10)) var/mob/living/carbon/C = host_mob C.cure_trauma_type(resilience = TRAUMA_RESILIENCE_BASIC) @@ -195,12 +195,12 @@ var/mob/living/carbon/C = host_mob if(length(C.get_traumas())) return ..() - if(host_mob.getBrainLoss()) + if(host_mob.getOrganLoss(ORGAN_SLOT_BRAIN)) return ..() return FALSE - + /datum/nanite_program/brain_heal_advanced/active_effect() - host_mob.adjustBrainLoss(-2, TRUE) + host_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, -2, TRUE) if(iscarbon(host_mob) && prob(10)) var/mob/living/carbon/C = host_mob C.cure_trauma_type(resilience = TRAUMA_RESILIENCE_LOBOTOMY) @@ -232,7 +232,7 @@ if(!C.getorgan(/obj/item/organ/heart)) //what are we even shocking return FALSE var/obj/item/organ/brain/BR = C.getorgan(/obj/item/organ/brain) - if(QDELETED(BR) || BR.damaged_brain) + if(QDELETED(BR) || BR.brain_death || (BR.organ_flags & ORGAN_FAILING) || C.suiciding) return FALSE if(C.get_ghost()) return FALSE @@ -252,7 +252,7 @@ SEND_SIGNAL(C, COMSIG_LIVING_MINOR_SHOCK) var/tplus = world.time - C.timeofdeath if(tplus > 600) - C.adjustBrainLoss( max(0, ((1800 - tplus) / 1800 * 150)), 150) + C.adjustOrganLoss(ORGAN_SLOT_BRAIN, max(0, ((1800 - tplus) / 1800 * 150)), 150) log_game("[C] has been successfully defibrillated by nanites.") else playsound(C, 'sound/machines/defib_failed.ogg', 50, 0) diff --git a/code/modules/research/nanites/nanite_programs/rogue.dm b/code/modules/research/nanites/nanite_programs/rogue.dm index f64c7e8781..287aed36fe 100644 --- a/code/modules/research/nanites/nanite_programs/rogue.dm +++ b/code/modules/research/nanites/nanite_programs/rogue.dm @@ -59,7 +59,7 @@ /datum/nanite_program/brain_decay/active_effect() if(prob(4)) host_mob.hallucination = min(15, host_mob.hallucination) - host_mob.adjustBrainLoss(1) + host_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1) //Generic brain-affecting programs can also decay into this /datum/nanite_program/brain_misfire diff --git a/code/modules/research/stock_parts.dm b/code/modules/research/stock_parts.dm index af004c4701..3cd296f866 100644 --- a/code/modules/research/stock_parts.dm +++ b/code/modules/research/stock_parts.dm @@ -13,6 +13,7 @@ If you create T5+ please take a pass at gene_modder.dm [L40]. Max_values MUST fi var/works_from_distance = FALSE var/pshoom_or_beepboopblorpzingshadashwoosh = 'sound/items/rped.ogg' var/alt_sound = null + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE //cutting down on exploits /obj/item/storage/part_replacer/pre_attack(obj/machinery/T, mob/living/user, params) if(!istype(T) || !T.component_parts) @@ -67,6 +68,7 @@ If you create T5+ please take a pass at gene_modder.dm [L40]. Max_values MUST fi icon = 'icons/obj/stock_parts.dmi' w_class = WEIGHT_CLASS_SMALL var/rating = 1 + rad_flags = RAD_NO_CONTAMINATE /obj/item/stock_parts/Initialize() . = ..() diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 99397897c6..590a1d787b 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -115,7 +115,7 @@ display_name = "Advanced Surgery" description = "When simple medicine doesn't cut it." prereq_ids = list("adv_biotech") - design_ids = list("surgery_lobotomy", "surgery_reconstruction", "organbox") + design_ids = list("surgery_lobotomy", "surgery_reconstruction", "surgery_toxinhealing", "organbox", "surgery_exp_dissection") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -124,7 +124,7 @@ display_name = "Experimental Surgery" description = "When evolution isn't fast enough." prereq_ids = list("adv_surgery") - design_ids = list("surgery_revival","surgery_pacify","surgery_vein_thread","surgery_nerve_splice","surgery_nerve_ground","surgery_viral_bond") + design_ids = list("surgery_revival","surgery_pacify","surgery_vein_thread","surgery_nerve_splice","surgery_nerve_ground","surgery_ligament_hook","surgery_ligament_reinforcement","surgery_viral_bond") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000) export_price = 5000 @@ -172,7 +172,7 @@ display_name = "Advanced Engineering" description = "Pushing the boundaries of physics, one chainsaw-fist at a time." prereq_ids = list("engineering", "emp_basic") - design_ids = list("engine_goggles", "magboots", "forcefield_projector", "weldingmask", "tray_goggles_prescription", "engine_goggles_prescription", "mesons_prescription") + design_ids = list("engine_goggles", "magboots", "forcefield_projector", "weldingmask", "tray_goggles_prescription", "engine_goggles_prescription", "mesons_prescription", "rcd_upgrade") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 4000) export_price = 5000 @@ -272,7 +272,7 @@ display_name = "Bluespace Pockets" description = "Studies into the mysterious alternate dimension known as bluespace and how to place items in the threads of reality." prereq_ids = list("adv_power", "adv_bluespace", "adv_biotech", "adv_plasma") - design_ids = list( "bluespacebodybag","bag_holding", "bluespace_pod", "borg_upgrade_trashofholding", "blutrash", "satchel_holding") + design_ids = list( "bluespacebodybag","bag_holding", "bluespace_pod", "borg_upgrade_trashofholding", "blutrash", "satchel_holding", "bsblood_bag") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5500) export_price = 5000 @@ -517,8 +517,17 @@ display_name = "Cybernetic Organs" description = "We have the technology to rebuild him." prereq_ids = list("adv_biotech") - design_ids = list("cybernetic_heart", "cybernetic_liver", "cybernetic_liver_u", "cybernetic_lungs", "cybernetic_lungs_u", "cybernetic_tongue") - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + design_ids = list("cybernetic_ears", "cybernetic_heart", "cybernetic_liver", "cybernetic_lungs", "cybernetic_tongue") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000) + export_price = 5000 + +/datum/techweb_node/cyber_organs_upgraded + id = "cyber_organs_upgraded" + display_name = "Upgraded Cybernetic Organs" + description = "We have the technology to upgrade him." + prereq_ids = list("cyber_organs") + design_ids = list("cybernetic_ears_u", "cybernetic_heart_u", "cybernetic_liver_u", "cybernetic_lungs_u") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1500) export_price = 5000 /datum/techweb_node/cyber_implants @@ -998,7 +1007,7 @@ description = "Things used by the greys." prereq_ids = list("biotech","engineering") boost_item_paths = list(/obj/item/gun/energy/alien, /obj/item/scalpel/alien, /obj/item/hemostat/alien, /obj/item/retractor/alien, /obj/item/circular_saw/alien, - /obj/item/cautery/alien, /obj/item/surgicaldrill/alien, /obj/item/screwdriver/abductor, /obj/item/wrench/abductor, /obj/item/crowbar/abductor, /obj/item/multitool/abductor, + /obj/item/cautery/alien, /obj/item/surgicaldrill/alien, /obj/item/screwdriver/abductor, /obj/item/wrench/abductor, /obj/item/crowbar/abductor, /obj/item/multitool/abductor, /obj/item/stock_parts/cell/infinite/abductor, /obj/item/weldingtool/abductor, /obj/item/wirecutters/abductor, /obj/item/circuitboard/machine/abductor, /obj/item/abductor_baton, /obj/item/abductor, /obj/item/stack/sheet/mineral/abductor) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000) export_price = 20000 @@ -1012,7 +1021,7 @@ prereq_ids = list("alientech", "adv_biotech") design_ids = list("alien_scalpel", "alien_hemostat", "alien_retractor", "alien_saw", "alien_drill", "alien_cautery") boost_item_paths = list(/obj/item/gun/energy/alien, /obj/item/scalpel/alien, /obj/item/hemostat/alien, /obj/item/retractor/alien, /obj/item/circular_saw/alien, - /obj/item/cautery/alien, /obj/item/surgicaldrill/alien, /obj/item/screwdriver/abductor, /obj/item/wrench/abductor, /obj/item/crowbar/abductor, /obj/item/multitool/abductor, + /obj/item/cautery/alien, /obj/item/surgicaldrill/alien, /obj/item/screwdriver/abductor, /obj/item/wrench/abductor, /obj/item/crowbar/abductor, /obj/item/multitool/abductor, /obj/item/stock_parts/cell/infinite/abductor, /obj/item/weldingtool/abductor, /obj/item/wirecutters/abductor, /obj/item/circuitboard/machine/abductor, /obj/item/abductor_baton, /obj/item/abductor) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 20000 @@ -1024,7 +1033,7 @@ description = "Alien engineering tools" prereq_ids = list("alientech", "adv_engi") design_ids = list("alien_wrench", "alien_wirecutters", "alien_screwdriver", "alien_crowbar", "alien_welder", "alien_multitool") - boost_item_paths = list(/obj/item/screwdriver/abductor, /obj/item/wrench/abductor, /obj/item/crowbar/abductor, /obj/item/multitool/abductor, + boost_item_paths = list(/obj/item/screwdriver/abductor, /obj/item/wrench/abductor, /obj/item/crowbar/abductor, /obj/item/multitool/abductor, /obj/item/stock_parts/cell/infinite/abductor, /obj/item/weldingtool/abductor, /obj/item/wirecutters/abductor, /obj/item/circuitboard/machine/abductor, /obj/item/abductor_baton, /obj/item/abductor) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 20000 @@ -1045,7 +1054,7 @@ boost_item_paths = list() for(var/path in GLOB.uplink_items) var/datum/uplink_item/UI = new path - if(!UI.item) + if(!UI.item || !UI.illegal_tech) continue boost_item_paths |= UI.item //allows deconning to unlock. @@ -1088,4 +1097,4 @@ for(var/i in processing) var/datum/techweb_node/TN = i TW.add_point_list(TN.research_costs) - return TW.printout_points() + return TW.printout_points() \ No newline at end of file diff --git a/code/modules/research/xenobiology/crossbreeding/_weapons.dm b/code/modules/research/xenobiology/crossbreeding/_weapons.dm index 4753abff97..1b2f43c518 100644 --- a/code/modules/research/xenobiology/crossbreeding/_weapons.dm +++ b/code/modules/research/xenobiology/crossbreeding/_weapons.dm @@ -27,9 +27,9 @@ return 0 charge_tick = 0 var/mob/living/M = loc - if(istype(M) && M.blood_volume >= 20) + if(istype(M) && M.blood_volume >= 5) charges++ - M.blood_volume -= 20 + M.blood_volume -= 5 if(charges == 1) recharge_newshot() return 1 diff --git a/code/modules/research/xenobiology/crossbreeding/consuming.dm b/code/modules/research/xenobiology/crossbreeding/consuming.dm index 0b2d60da8c..d6536f4fdd 100644 --- a/code/modules/research/xenobiology/crossbreeding/consuming.dm +++ b/code/modules/research/xenobiology/crossbreeding/consuming.dm @@ -119,7 +119,7 @@ Consuming extracts: M.adjustToxLoss(-5, forced=1) //To heal slimepeople. M.adjustOxyLoss(-5) M.adjustCloneLoss(-5) - M.adjustBrainLoss(-5) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, -5) /obj/item/slimecross/consuming/blue colour = "blue" diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 7acbaaac1c..5e872ac299 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -523,7 +523,8 @@ /obj/item/slime_extract/pyrite/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) switch(activation_type) if(SLIME_ACTIVATE_MINOR) - var/chosen = pick(difflist(subtypesof(/obj/item/toy/crayon),typesof(/obj/item/toy/crayon/spraycan))) + var/blacklisted_cans = list(/obj/item/toy/crayon/spraycan/borg, /obj/item/toy/crayon/spraycan/infinite) + var/chosen = pick(subtypesof(/obj/item/toy/crayon/spraycan) - blacklisted_cans) var/obj/item/O = new chosen(null) if(!user.put_in_active_hand(O)) O.forceMove(user.drop_location()) diff --git a/code/modules/spells/spell_types/lichdom.dm b/code/modules/spells/spell_types/lichdom.dm index efc80101f6..0cbb3a8272 100644 --- a/code/modules/spells/spell_types/lichdom.dm +++ b/code/modules/spells/spell_types/lichdom.dm @@ -57,7 +57,7 @@ new /obj/item/phylactery(marked_item, M.mind) to_chat(M, "With a hideous feeling of emptiness you watch in horrified fascination as skin sloughs off bone! Blood boils, nerves disintegrate, eyes boil in their sockets! As your organs crumble to dust in your fleshless chest you come to terms with your choice. You're a lich!") - M.set_species(/datum/species/skeleton) + M.set_species(/datum/species/skeleton/space) if(ishuman(M)) var/mob/living/carbon/human/H = M H.dropItemToGround(H.w_uniform) @@ -132,7 +132,7 @@ lich.real_name = mind.name mind.transfer_to(lich) mind.grab_ghost(force=TRUE) - lich.hardset_dna(null,null,lich.real_name,null, new /datum/species/skeleton) + lich.hardset_dna(null,null,lich.real_name,null, new /datum/species/skeleton/space) to_chat(lich, "Your bones clatter and shudder as you are pulled back into this world!") var/turf/body_turf = get_turf(old_body) lich.Knockdown(200 + 200*resurrections) diff --git a/code/modules/surgery/advanced/bioware/experimental_dissection.dm b/code/modules/surgery/advanced/bioware/experimental_dissection.dm new file mode 100644 index 0000000000..6266480baf --- /dev/null +++ b/code/modules/surgery/advanced/bioware/experimental_dissection.dm @@ -0,0 +1,72 @@ +/datum/surgery/advanced/bioware/experimental_dissection + name = "Experimental Dissection" + desc = "A surgical procedure which deeply analyzes the biology of a corpse, and automatically adds new findings to the research database." + steps = list(/datum/surgery_step/incise, + /datum/surgery_step/retract_skin, + /datum/surgery_step/clamp_bleeders, + /datum/surgery_step/incise, + /datum/surgery_step/dissection, + /datum/surgery_step/close) + possible_locs = list(BODY_ZONE_CHEST) + bioware_target = BIOWARE_DISSECTION + +/datum/surgery/advanced/bioware/experimental_dissection/can_start(mob/user, mob/living/carbon/target) + . = ..() + if(iscyborg(user)) + return FALSE //robots cannot be creative + //(also this surgery shouldn't be consistently successful, and cyborgs have a 100% success rate on surgery) + if(target.stat != DEAD) + return FALSE + +/datum/surgery_step/dissection + name = "dissection" + implements = list(/obj/item/scalpel = 60, /obj/item/kitchen/knife = 30, /obj/item/shard = 15) + time = 125 + +/datum/surgery_step/dissection/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + display_results(user, target, "You start dissecting [target].", + "[user] starts dissecting [target].", + "[user] starts dissecting [target].") + +/datum/surgery_step/dissection/proc/check_value(mob/living/carbon/target) + if(isalienroyal(target)) + return 10000 + else if(isalienadult(target)) + return 5000 + else if(ismonkey(target)) + return 1000 + else if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(H.dna && H.dna.species) + if(isabductor(H)) + return 8000 + if(isgolem(H) || iszombie(H)) + return 4000 + if(isjellyperson(H) || ispodperson(H)) + return 3000 + return 2000 + +/datum/surgery_step/dissection/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + display_results(user, target, "You dissect [target], and add your discoveries to the research database!", + "[user] dissects [target], adding [user.p_their()] discoveries to the research database!", + "[user] dissects [target]!") + SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = check_value(target))) + var/obj/item/bodypart/L = target.get_bodypart(BODY_ZONE_CHEST) + target.apply_damage(80, BRUTE, L) + new /datum/bioware/dissected(target) + return TRUE + +/datum/surgery_step/dissection/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + display_results(user, target, "You dissect [target], but do not find anything particularly interesting.", + "[user] dissects [target], however it seems [user.p_they()] didn't find anything useful.", + "[user] dissects [target], but looks a little dissapointed.") + SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = (check_value(target) * 0.2))) + var/obj/item/bodypart/L = target.get_bodypart(BODY_ZONE_CHEST) + target.apply_damage(80, BRUTE, L) + new /datum/bioware/dissected(target) + return TRUE + +/datum/bioware/dissected + name = "Dissected" + desc = "This body has been dissected and analyzed. It is no longer worth experimenting on." + mod_type = BIOWARE_DISSECTION \ No newline at end of file diff --git a/code/modules/surgery/advanced/bioware/ligament_hook.dm b/code/modules/surgery/advanced/bioware/ligament_hook.dm new file mode 100644 index 0000000000..2c154436e6 --- /dev/null +++ b/code/modules/surgery/advanced/bioware/ligament_hook.dm @@ -0,0 +1,45 @@ +/datum/surgery/advanced/bioware/ligament_hook + name = "Ligament Hook" + desc = "A surgical procedure which reshapes the connections between torso and limbs, making it so limbs can be attached manually if severed. \ + However this weakens the connection, making them easier to detach as well." + steps = list(/datum/surgery_step/incise, + /datum/surgery_step/retract_skin, + /datum/surgery_step/clamp_bleeders, + /datum/surgery_step/incise, + /datum/surgery_step/incise, + /datum/surgery_step/reshape_ligaments, + /datum/surgery_step/close) + possible_locs = list(BODY_ZONE_CHEST) + bioware_target = BIOWARE_LIGAMENTS + +/datum/surgery_step/reshape_ligaments + name = "reshape ligaments" + accept_hand = TRUE + time = 125 + +/datum/surgery_step/reshape_ligaments/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + display_results(user, target, "You start reshaping [target]'s ligaments into a hook-like shape.", + "[user] starts reshaping [target]'s ligaments into a hook-like shape.", + "[user] starts manipulating [target]'s ligaments.") + +/datum/surgery_step/reshape_ligaments/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + display_results(user, target, "You finish reshaping [target]'s ligaments into a connective hook!", + "[user] finishes reshaping [target]'s ligaments into a connective hook!", + "[user] finishes manipulating [target]'s ligaments!") + new /datum/bioware/hooked_ligaments(target) + return TRUE + +/datum/bioware/hooked_ligaments + name = "Hooked Ligaments" + desc = "The ligaments and nerve endings that connect the torso to the limbs are formed into a hook-like shape, so limbs can be attached without requiring surgery, but are easier to sever." + mod_type = BIOWARE_LIGAMENTS + +/datum/bioware/hooked_ligaments/on_gain() + ..() + ADD_TRAIT(owner, TRAIT_LIMBATTACHMENT, "ligament_hook") + ADD_TRAIT(owner, TRAIT_EASYDISMEMBER, "ligament_hook") + +/datum/bioware/hooked_ligaments/on_lose() + ..() + REMOVE_TRAIT(owner, TRAIT_LIMBATTACHMENT, "ligament_hook") + REMOVE_TRAIT(owner, TRAIT_EASYDISMEMBER, "ligament_hook") \ No newline at end of file diff --git a/code/modules/surgery/advanced/bioware/ligament_reinforcement.dm b/code/modules/surgery/advanced/bioware/ligament_reinforcement.dm new file mode 100644 index 0000000000..ac034fcea7 --- /dev/null +++ b/code/modules/surgery/advanced/bioware/ligament_reinforcement.dm @@ -0,0 +1,45 @@ +/datum/surgery/advanced/bioware/ligament_reinforcement + name = "Ligament Reinforcement" + desc = "A surgical procedure which adds a protective tissue and bone cage around the connections between the torso and limbs, preventing dismemberment. \ + However, the nerve connections as a result are more easily interrupted, making it easier to disable limbs with damage." + steps = list(/datum/surgery_step/incise, + /datum/surgery_step/retract_skin, + /datum/surgery_step/clamp_bleeders, + /datum/surgery_step/incise, + /datum/surgery_step/incise, + /datum/surgery_step/reinforce_ligaments, + /datum/surgery_step/close) + possible_locs = list(BODY_ZONE_CHEST) + bioware_target = BIOWARE_LIGAMENTS + +/datum/surgery_step/reinforce_ligaments + name = "reinforce ligaments" + accept_hand = TRUE + time = 125 + +/datum/surgery_step/reinforce_ligaments/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + display_results(user, target, "You start reinforcing [target]'s ligaments.", + "[user] starts reinforcing [target]'s ligaments.", + "[user] starts manipulating [target]'s ligaments.") + +/datum/surgery_step/reinforce_ligaments/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + display_results(user, target, "You finish reinforcing [target]'s ligaments!", + "[user] finishes reinforcing [target]'s ligaments!", + "[user] finishes manipulating [target]'s ligaments!") + new /datum/bioware/reinforced_ligaments(target) + return TRUE + +/datum/bioware/reinforced_ligaments + name = "Reinforced Ligaments" + desc = "The ligaments and nerve endings that connect the torso to the limbs are protected by a mix of bone and tissues, and are much harder to separate from the body, but are also easier to disable." + mod_type = BIOWARE_LIGAMENTS + +/datum/bioware/reinforced_ligaments/on_gain() + ..() + ADD_TRAIT(owner, TRAIT_NODISMEMBER, "reinforced_ligaments") + ADD_TRAIT(owner, TRAIT_EASYLIMBDISABLE, "reinforced_ligaments") + +/datum/bioware/reinforced_ligaments/on_lose() + ..() + REMOVE_TRAIT(owner, TRAIT_NODISMEMBER, "reinforced_ligaments") + REMOVE_TRAIT(owner, TRAIT_EASYLIMBDISABLE, "reinforced_ligaments") \ No newline at end of file diff --git a/code/modules/surgery/advanced/brainwashing.dm b/code/modules/surgery/advanced/brainwashing.dm index 730a912189..68deca4729 100644 --- a/code/modules/surgery/advanced/brainwashing.dm +++ b/code/modules/surgery/advanced/brainwashing.dm @@ -56,7 +56,7 @@ display_results(user, target, "You screw up, bruising the brain tissue!", "[user] screws up, causing brain damage!", "[user] completes the surgery on [target]'s brain.") - target.adjustBrainLoss(40) + target.adjustOrganLoss(ORGAN_SLOT_BRAIN, 40) else user.visible_message("[user] suddenly notices that the brain [user.p_they()] [user.p_were()] working on is not there anymore.", "You suddenly notice that the brain you were working on is not there anymore.") return FALSE diff --git a/code/modules/surgery/advanced/lobotomy.dm b/code/modules/surgery/advanced/lobotomy.dm index 1c09f5f7da..12c5e4dab2 100644 --- a/code/modules/surgery/advanced/lobotomy.dm +++ b/code/modules/surgery/advanced/lobotomy.dm @@ -51,11 +51,12 @@ return TRUE /datum/surgery_step/lobotomize/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) - if(target.getorganslot(ORGAN_SLOT_BRAIN)) + var/obj/item/organ/brain/B = target.getorganslot(ORGAN_SLOT_BRAIN) + if(B) display_results(user, target, "You remove the wrong part, causing more damage!", "[user] successfully lobotomizes [target]!", "[user] completes the surgery on [target]'s brain.") - target.adjustBrainLoss(80) + B.applyOrganDamage(80) switch(rand(1,3)) if(1) target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_MAGIC) diff --git a/code/modules/surgery/advanced/revival.dm b/code/modules/surgery/advanced/revival.dm index 01c30f174a..b31a7518bd 100644 --- a/code/modules/surgery/advanced/revival.dm +++ b/code/modules/surgery/advanced/revival.dm @@ -63,7 +63,7 @@ user.visible_message("...[target] wakes up, alive and aware!", "IT'S ALIVE!") target.visible_message("...[target] wakes up, alive and aware!") target.emote("gasp") - target.adjustBrainLoss(50, 199) //MAD SCIENCE + target.adjustOrganLoss(ORGAN_SLOT_BRAIN, 50, 199) //MAD SCIENCE return TRUE else user.visible_message("...[target.p_they()] convulses, then lies still.") @@ -75,5 +75,5 @@ "[user] send a powerful shock to [target]'s brain with [tool], but [target.p_they()] doesn't react.", "[user] send a powerful shock to [target]'s brain with [tool], but [target.p_they()] doesn't react.") playsound(get_turf(target), 'sound/magic/lightningbolt.ogg', 50, 1) - target.adjustBrainLoss(15, 199) + target.adjustOrganLoss(ORGAN_SLOT_BRAIN, 15, 199) return FALSE diff --git a/code/modules/surgery/advanced/toxichealing.dm b/code/modules/surgery/advanced/toxichealing.dm new file mode 100644 index 0000000000..a82287831c --- /dev/null +++ b/code/modules/surgery/advanced/toxichealing.dm @@ -0,0 +1,37 @@ +/datum/surgery/advanced/toxichealing + name = "Body Rejuvenation" + desc = "A surgical procedure that helps deal with oxygen deprecation, and treat toxic damaged. Works on corpses and alive alike without chemicals." + steps = list(/datum/surgery_step/incise, + /datum/surgery_step/incise, + /datum/surgery_step/retract_skin, + /datum/surgery_step/incise, + /datum/surgery_step/clamp_bleeders, + /datum/surgery_step/incise, + /datum/surgery_step/retract_skin, + /datum/surgery_step/toxichealing, + /datum/surgery_step/close) + + species = list(/mob/living/carbon/human, /mob/living/carbon/monkey) + possible_locs = list(BODY_ZONE_CHEST) + requires_bodypart_type = 0 + +/datum/surgery_step/toxichealing + name = "rejuvenate body" + implements = list(/obj/item/hemostat = 100, TOOL_SCREWDRIVER = 35, /obj/item/pen = 15) + repeatable = TRUE + time = 25 + +/datum/surgery_step/toxichealing/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message("[user] starts rejuvenating some of [target]'s flesh back to life.", "You start knitting some of [target]'s flesh back to life.") + +/datum/surgery_step/toxichealing/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message("[user] fixes some of [target]'s wounds.", "You succeed in fixing some of [target]'s wounds.") + target.heal_bodypart_damage(0,0,30) //Heals stam + target.adjustToxLoss(-15, 0, TRUE) + target.adjustOxyLoss(-20, 0) + return TRUE + +/datum/surgery_step/toxichealing/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message("[user] screws up!", "You screwed up!") + target.take_bodypart_damage(25,0) + return FALSE \ No newline at end of file diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm index 81b491e6de..dffb7bf355 100644 --- a/code/modules/surgery/bodyparts/bodyparts.dm +++ b/code/modules/surgery/bodyparts/bodyparts.dm @@ -40,6 +40,7 @@ var/skin_tone = "" var/body_gender = "" var/species_id = "" + var/should_draw_citadel = FALSE var/should_draw_gender = FALSE var/should_draw_greyscale = FALSE var/species_color = "" @@ -48,8 +49,8 @@ var/body_markings = "" //for bodypart markings var/body_markings_icon = 'modular_citadel/icons/mob/mam_markings.dmi' var/list/markings_color = list() - var/auxmarking = "" - var/list/auxmarking_color = list() + var/aux_marking + var/digitigrade_type var/animal_origin = null //for nonhuman bodypart (e.g. monkey) var/dismemberable = 1 //whether it can be dismembered with a weapon. @@ -230,7 +231,7 @@ return BODYPART_DISABLED_PARALYSIS if(can_dismember() && !HAS_TRAIT(owner, TRAIT_NODISMEMBER)) . = disabled //inertia, to avoid limbs healing 0.1 damage and being re-enabled - if((get_damage(TRUE) >= max_damage)) + if((get_damage(TRUE) >= max_damage) || (HAS_TRAIT(owner, TRAIT_EASYLIMBDISABLE) && (get_damage(TRUE) >= (max_damage * 0.6)))) //Easy limb disable disables the limb at 40% health instead of 0% return BODYPART_DISABLED_DAMAGE if(disabled && (get_damage(TRUE) <= (max_damage * 0.5))) return BODYPART_NOT_DISABLED @@ -309,7 +310,7 @@ should_draw_greyscale = FALSE no_update = TRUE body_markings = "husk" // reeee - auxmarking = "husk" + aux_marking = "husk" if(no_update) return @@ -351,6 +352,13 @@ else species_color = "" + if("legs" in S.default_features) + if(body_zone == BODY_ZONE_L_LEG || body_zone == BODY_ZONE_R_LEG) + if(DIGITIGRADE in S.species_traits) + digitigrade_type = lowertext(H.dna.features.["legs"]) + else + digitigrade_type = null + if("mam_body_markings" in S.default_features) var/datum/sprite_accessory/Smark Smark = GLOB.mam_body_markings_list[H.dna.features["mam_body_markings"]] @@ -358,15 +366,15 @@ body_markings_icon = Smark.icon if(H.dna.features.["mam_body_markings"] != "None") body_markings = lowertext(H.dna.features.["mam_body_markings"]) - auxmarking = lowertext(H.dna.features.["mam_body_markings"]) + aux_marking = lowertext(H.dna.features.["mam_body_markings"]) else body_markings = "plain" - auxmarking = "plain" + aux_marking = "plain" markings_color = list(colorlist) else body_markings = null - auxmarking = null + aux_marking = null if(!dropping_limb && H.dna.check_mutation(HULK)) mutation_color = "00aa00" @@ -381,7 +389,7 @@ if(status == BODYPART_ROBOTIC) dmg_overlay_type = "robotic" body_markings = null - auxmarking = null + aux_marking = null if(dropping_limb) no_update = TRUE //when attached, the limb won't be affected by the appearance changes of its mob owner. @@ -423,7 +431,7 @@ else . += image(body_markings_icon, "[body_markings]_[body_zone]", -MARKING_LAYER, image_dir) else - . += image(body_markings_icon, "[body_markings]_digitigrade_[use_digitigrade]_[body_zone]", -MARKING_LAYER, image_dir) + . += image(body_markings_icon, "[body_markings]_[digitigrade_type]_[use_digitigrade]_[body_zone]", -MARKING_LAYER, image_dir) var/image/limb = image(layer = -BODYPARTS_LAYER, dir = image_dir) var/image/aux @@ -453,21 +461,25 @@ if(should_draw_gender) limb.icon_state = "[species_id]_[body_zone]_[icon_gender]" else if(use_digitigrade) - limb.icon_state = "digitigrade_[use_digitigrade]_[body_zone]" + limb.icon_state = "[digitigrade_type]_[use_digitigrade]_[body_zone]" else limb.icon_state = "[species_id]_[body_zone]" else limb.icon = 'icons/mob/human_parts.dmi' if(should_draw_gender) limb.icon_state = "[species_id]_[body_zone]_[icon_gender]" + else if(use_digitigrade) + limb.icon_state = "[species_id]_[digitigrade_type]_[use_digitigrade]_[body_zone]" else limb.icon_state = "[species_id]_[body_zone]" // Citadel Start - if(should_draw_citadel && !use_digitigrade) + if(should_draw_citadel) limb.icon = 'modular_citadel/icons/mob/mutant_bodyparts.dmi' if(should_draw_gender) limb.icon_state = "[species_id]_[body_zone]_[icon_gender]" + else if(use_digitigrade) + limb.icon_state = "[species_id]_[digitigrade_type]_[use_digitigrade]_[body_zone]" else limb.icon_state = "[species_id]_[body_zone]" @@ -476,7 +488,7 @@ if(species_id == "husk") marking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_[body_zone]", -MARKING_LAYER, image_dir) else if(species_id == "husk" && use_digitigrade) - marking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_digitigrade_[use_digitigrade]_[body_zone]", -MARKING_LAYER, image_dir) + marking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_[digitigrade_type]_[use_digitigrade]_[body_zone]", -MARKING_LAYER, image_dir) else if(!use_digitigrade) if(body_zone == BODY_ZONE_CHEST) @@ -484,20 +496,21 @@ else marking = image(body_markings_icon, "[body_markings]_[body_zone]", -MARKING_LAYER, image_dir) else - marking = image(body_markings_icon, "[body_markings]_digitigrade_[use_digitigrade]_[body_zone]", -MARKING_LAYER, image_dir) + marking = image(body_markings_icon, "[body_markings]_[digitigrade_type]_[use_digitigrade]_[body_zone]", -MARKING_LAYER, image_dir) + . += marking // Citadel End if(aux_zone) aux = image(limb.icon, "[species_id]_[aux_zone]", -aux_layer, image_dir) - . += aux - if(!isnull(auxmarking)) + if(!isnull(aux_marking)) if(species_id == "husk") auxmarking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_[aux_zone]", -aux_layer, image_dir) else auxmarking = image(body_markings_icon, "[body_markings]_[aux_zone]", -aux_layer, image_dir) - . += auxmarking + . += aux + . += auxmarking else limb.icon = icon @@ -509,7 +522,7 @@ if(aux_zone) aux = image(limb.icon, "[aux_zone]", -aux_layer, image_dir) . += aux - if(!isnull(auxmarking)) + if(!isnull(aux_marking)) if(species_id == "husk") auxmarking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_[aux_zone]", -aux_layer, image_dir) else @@ -528,7 +541,7 @@ else marking = image(body_markings_icon, "[body_markings]_[body_zone]", -MARKING_LAYER, image_dir) else - marking = image(body_markings_icon, "[body_markings]_digitigrade_[use_digitigrade]_[body_zone]", -MARKING_LAYER, image_dir) + marking = image(body_markings_icon, "[body_markings]_[digitigrade_type]_[use_digitigrade]_[body_zone]", -MARKING_LAYER, image_dir) . += marking return @@ -538,8 +551,11 @@ limb.color = "#[draw_color]" if(aux_zone) aux.color = "#[draw_color]" - if(!isnull(auxmarking)) - auxmarking.color = list(markings_color) + if(!isnull(aux_marking)) + if(species_id == "husk") + auxmarking.color = "#141414" + else + auxmarking.color = list(markings_color) if(!isnull(body_markings)) if(species_id == "husk") diff --git a/code/modules/surgery/brain_surgery.dm b/code/modules/surgery/brain_surgery.dm index e65271576d..c553d05448 100644 --- a/code/modules/surgery/brain_surgery.dm +++ b/code/modules/surgery/brain_surgery.dm @@ -31,7 +31,7 @@ "[user] completes the surgery on [target]'s brain.") if(target.mind && target.mind.has_antag_datum(/datum/antagonist/brainwashed)) target.mind.remove_antag_datum(/datum/antagonist/brainwashed) - target.adjustBrainLoss(-60) + target.setOrganLoss(ORGAN_SLOT_BRAIN, target.getOrganLoss(ORGAN_SLOT_BRAIN) - 60) //we set damage in this case in order to clear the "failing" flag target.cure_all_traumas(TRAUMA_RESILIENCE_SURGERY) return TRUE @@ -40,7 +40,7 @@ display_results(user, target, "You screw up, causing more damage!", "[user] screws up, causing brain damage!", "[user] completes the surgery on [target]'s brain.") - target.adjustBrainLoss(60) + target.adjustOrganLoss(ORGAN_SLOT_BRAIN, 60) target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_LOBOTOMY) else user.visible_message("[user] suddenly notices that the brain [user.p_they()] [user.p_were()] working on is not there anymore.", "You suddenly notice that the brain you were working on is not there anymore.") diff --git a/code/modules/surgery/coronary_bypass.dm b/code/modules/surgery/coronary_bypass.dm new file mode 100644 index 0000000000..3e36e9aa9b --- /dev/null +++ b/code/modules/surgery/coronary_bypass.dm @@ -0,0 +1,77 @@ +/datum/surgery/coronary_bypass + name = "Coronary Bypass" + steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/saw, /datum/surgery_step/clamp_bleeders, + /datum/surgery_step/incise_heart, /datum/surgery_step/coronary_bypass, /datum/surgery_step/close) + possible_locs = list(BODY_ZONE_CHEST) + +/datum/surgery/coronary_bypass/can_start(mob/user, mob/living/carbon/target) + var/obj/item/organ/heart/H = target.getorganslot(ORGAN_SLOT_HEART) + if(H) + if(H.damage > 60 && !H.operated) + return TRUE + return FALSE + + +//an incision but with greater bleed, and a 90% base success chance +/datum/surgery_step/incise_heart + name = "incise heart" + implements = list(/obj/item/scalpel = 90, /obj/item/melee/transforming/energy/sword = 45, /obj/item/kitchen/knife = 45, + /obj/item/shard = 25) + time = 16 + +/datum/surgery_step/incise_heart/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + display_results(user, target, "You begin to make an incision in [target]'s heart...", + "[user] begins to make an incision in [target]'s heart.", + "[user] begins to make an incision in [target]'s heart.") + +/datum/surgery_step/incise_heart/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if (!(NOBLOOD in H.dna.species.species_traits)) + display_results(user, target, "Blood pools around the incision in [H]'s heart.", + "Blood pools around the incision in [H]'s heart.", + "") + H.bleed_rate += 10 + H.adjustBruteLoss(10) + return TRUE + +/datum/surgery_step/incise_heart/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(ishuman(target)) + var/mob/living/carbon/human/H = target + display_results(user, target, "You screw up, cutting too deeply into the heart!", + "[user] screws up, causing blood to spurt out of [H]'s chest!", + "[user] screws up, causing blood to spurt out of [H]'s chest!") + H.bleed_rate += 20 + H.adjustOrganLoss(ORGAN_SLOT_HEART, 10) + H.adjustBruteLoss(10) + +//grafts a coronary bypass onto the individual's heart, success chance is 90% base again +/datum/surgery_step/coronary_bypass + name = "graft coronary bypass" + implements = list(/obj/item/hemostat = 90, TOOL_WIRECUTTER = 35, /obj/item/stack/packageWrap = 15, /obj/item/stack/cable_coil = 5) + time = 90 + +/datum/surgery_step/coronary_bypass/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + display_results(user, target, "You begin to graft a bypass onto [target]'s heart...", + "[user] begins to graft something onto [target]'s heart!", + "[user] begins to graft something onto [target]'s heart!") + +/datum/surgery_step/coronary_bypass/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + target.setOrganLoss(ORGAN_SLOT_HEART, 60) + var/obj/item/organ/heart/heart = target.getorganslot(ORGAN_SLOT_HEART) + if(heart) //slightly worrying if we lost our heart mid-operation, but that's life + heart.operated = TRUE + display_results(user, target, "You successfully graft a bypass onto [target]'s heart.", + "[user] finishes grafting something onto [target]'s heart.", + "[user] finishes grafting something onto [target]'s heart.") + return TRUE + +/datum/surgery_step/coronary_bypass/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(ishuman(target)) + var/mob/living/carbon/human/H = target + display_results(user, target, "You screw up in attaching the graft, and it tears off, tearing part of the heart!", + "[user] screws up, causing blood to spurt out of [H]'s chest profusely!", + "[user] screws up, causing blood to spurt out of [H]'s chest profusely!") + H.adjustOrganLoss(ORGAN_SLOT_HEART, 20) + H.bleed_rate += 30 + return FALSE \ No newline at end of file diff --git a/code/modules/surgery/embalming.dm b/code/modules/surgery/embalming.dm new file mode 100644 index 0000000000..f74d864245 --- /dev/null +++ b/code/modules/surgery/embalming.dm @@ -0,0 +1,31 @@ +/datum/surgery/embalming //Fast and easy way to husk bodys + name = "Embalming" + desc = "A surgical procedure that prevents a corps from producing." + steps = list(/datum/surgery_step/incise, + /datum/surgery_step/embalming, + /datum/surgery_step/close) + + species = list(/mob/living/carbon/human, /mob/living/carbon/monkey) + possible_locs = list(BODY_ZONE_CHEST) + requires_bodypart_type = 0 + +/datum/surgery_step/embalming + name = "embalming body" + implements = list(/obj/item/hemostat = 100, TOOL_SCREWDRIVER = 35, /obj/item/pen = 15) + implements = list(/obj/item/reagent_containers/syringe = 100, /obj/item/pen = 30) + time = 10 + chems_needed = list("drying_agent", "sterilizine") + require_all_chems = FALSE + +/datum/surgery_step/embalming/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message("[user] starts to embalm [target]'s body.", "You start embalming [target]'s body.") + +/datum/surgery_step/embalming/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message("[user] embalms [target]'s body.", "You succeed in embalming [target]'s body.") + ADD_TRAIT(target, TRAIT_HUSK, MAGIC_TRAIT) //Husk's prevent body smell + return FALSE + +/datum/surgery_step/embalming/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message("[user] screws up!", "You screwed up!") + ADD_TRAIT(target, TRAIT_NOCLONE, MAGIC_TRAIT) //That body is ruined, but still gives miasma + return FALSE \ No newline at end of file diff --git a/code/modules/surgery/eye_surgery.dm b/code/modules/surgery/eye_surgery.dm index 85142e7bb1..29385ef20b 100644 --- a/code/modules/surgery/eye_surgery.dm +++ b/code/modules/surgery/eye_surgery.dm @@ -37,7 +37,7 @@ display_results(user, target, "You accidentally stab [target] right in the brain!", "[user] accidentally stabs [target] right in the brain!", "[user] accidentally stabs [target] right in the brain!") - target.adjustBrainLoss(70) + target.adjustOrganLoss(ORGAN_SLOT_BRAIN, 70) else display_results(user, target, "You accidentally stab [target] right in the brain! Or would have, if [target] had a brain.", "[user] accidentally stabs [target] right in the brain! Or would have, if [target] had a brain.", diff --git a/code/modules/surgery/graft_synthtissue.dm b/code/modules/surgery/graft_synthtissue.dm new file mode 100644 index 0000000000..8826de7171 --- /dev/null +++ b/code/modules/surgery/graft_synthtissue.dm @@ -0,0 +1,70 @@ +//Organ reconstruction, limited to the chest region as most organs in the head have their own repair method (eyes/brain). We require synthflesh for these +//steps since fixing internal organs aren't as simple as mending exterior flesh, though in the future it would be neat to add more chems to the viable list. +//TBD: Add heart damage, have heart reconstruction seperate from organ reconstruction, and find a better name for this. I can imagine people getting it confused with manipulation. + +/datum/surgery/graft_synthtissue + name = "Graft synthtissue" + species = list(/mob/living/carbon/human, /mob/living/carbon/monkey) + possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_PRECISE_EYES) + steps = list( + /datum/surgery_step/incise, + /datum/surgery_step/retract_skin, + /datum/surgery_step/saw, + /datum/surgery_step/clamp_bleeders, + /datum/surgery_step/incise, + /datum/surgery_step/graft_synthtissue, + /datum/surgery_step/close + ) + +//repair organs +/datum/surgery_step/graft_synthtissue + name = "graft synthtissue" + implements = list(/obj/item/hemostat = 100, TOOL_SCREWDRIVER = 35, /obj/item/pen = 15) + repeatable = TRUE + time = 75 + chems_needed = list("synthtissue") + var/obj/item/organ/chosen_organ + var/health_restored = 10 + +/datum/surgery_step/graft_synthtissue/preop(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(implement_type in implements) + var/list/organs = target.getorganszone(target_zone) + if(!organs.len) + to_chat(user, "There are no targetable organs in [target]'s [parse_zone(target_zone)]!") + return -1 + else + for(var/obj/item/organ/O in organs) + O.on_find(user) + organs -= O + organs[O.name] = O + chosen_organ = input("Target which organ?", "Surgery", null, null) as null|anything in organs + chosen_organ = organs[chosen_organ] + if(!chosen_organ) + return -1 + if(!target.reagents.has_reagent("synthtissue")) + to_chat(user, "There's no synthtissue available for use on [chosen_organ]") + return -1 + var/datum/reagent/synthtissue/Sf = locate(/datum/reagent/synthtissue) in target.reagents.reagent_list + if(Sf.volume < 10) + to_chat(user, "There's not enough synthtissue to perform the operation! There needs to be at least 10u.") + return -1 + + if((chosen_organ.organ_flags & ORGAN_FAILING) && !(Sf.data["grown_volume"] >= 115)) + to_chat(user, "[chosen_organ] is too damaged to graft onto!") + return -1 + + if(health_restored != 10) + health_restored = 10 + health_restored += (Sf.data["grown_volume"]/10) + + user.visible_message("[user] begins to graft synthtissue onto [chosen_organ].") + target.reagents.remove_reagent("synthtissue", 10) + +/datum/surgery_step/graft_synthtissue/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message("[user] successfully repairs part of [chosen_organ].", "You succeed in repairing parts of [chosen_organ].") + chosen_organ.applyOrganDamage(health_restored) + +/datum/surgery_step/graft_synthtissue/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message("[user] accidentally damages part of [chosen_organ]!", "You damage [chosen_organ]! Apply more synthtissue if it's run out.") + chosen_organ.applyOrganDamage(10) + return FALSE diff --git a/code/modules/surgery/lobectomy.dm b/code/modules/surgery/lobectomy.dm new file mode 100644 index 0000000000..7d8b8a53e8 --- /dev/null +++ b/code/modules/surgery/lobectomy.dm @@ -0,0 +1,46 @@ +/datum/surgery/lobectomy + name = "Lobectomy" //not to be confused with lobotomy + steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/saw, /datum/surgery_step/clamp_bleeders, + /datum/surgery_step/lobectomy, /datum/surgery_step/close) + possible_locs = list(BODY_ZONE_CHEST) + +/datum/surgery/lobectomy/can_start(mob/user, mob/living/carbon/target) + var/obj/item/organ/lungs/L = target.getorganslot(ORGAN_SLOT_LUNGS) + if(L) + if(L.damage > 60 && !L.operated) + return TRUE + return FALSE + + +//lobectomy, removes the most damaged lung lobe with a 95% base success chance +/datum/surgery_step/lobectomy + name = "excise damaged lung node" + implements = list(/obj/item/scalpel = 95, /obj/item/melee/transforming/energy/sword = 65, /obj/item/kitchen/knife = 45, + /obj/item/shard = 35) + time = 42 + +/datum/surgery_step/lobectomy/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + display_results(user, target, "You begin to make an incision in [target]'s lungs...", + "[user] begins to make an incision in [target].", + "[user] begins to make an incision in [target].") + +/datum/surgery_step/lobectomy/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(ishuman(target)) + var/mob/living/carbon/human/H = target + var/obj/item/organ/lungs/L = H.getorganslot(ORGAN_SLOT_LUNGS) + L.operated = TRUE + H.setOrganLoss(ORGAN_SLOT_LUNGS, 60) + display_results(user, target, "You successfully excise [H]'s most damaged lobe.", + "Successfully removes a piece of [H]'s lungs.", + "") + return TRUE + +/datum/surgery_step/lobectomy/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(ishuman(target)) + var/mob/living/carbon/human/H = target + display_results(user, target, "You screw up, failing to excise [H]'s damaged lobe!", + "[user] screws up!", + "[user] screws up!") + H.losebreath += 4 + H.adjustOrganLoss(ORGAN_SLOT_LUNGS, 10) + return FALSE \ No newline at end of file diff --git a/code/modules/surgery/organs/appendix.dm b/code/modules/surgery/organs/appendix.dm index 34a7e16ebf..1f3f2fff5b 100644 --- a/code/modules/surgery/organs/appendix.dm +++ b/code/modules/surgery/organs/appendix.dm @@ -3,7 +3,21 @@ icon_state = "appendix" zone = BODY_ZONE_PRECISE_GROIN slot = ORGAN_SLOT_APPENDIX - var/inflamed = 0 + healing_factor = STANDARD_ORGAN_HEALING + decay_factor = STANDARD_ORGAN_DECAY + + now_failing = "An explosion of pain erupts in your lower right abdomen!" + now_fixed = "The pain in your abdomen has subsided." + + var/inflamed + +/obj/item/organ/appendix/on_life() + ..() + if(!(organ_flags & ORGAN_FAILING)) + return + var/mob/living/carbon/M = owner + if(M) + M.adjustToxLoss(4, TRUE, TRUE) //forced to ensure people don't use it to gain tox as slime person /obj/item/organ/appendix/update_icon() if(inflamed) @@ -16,7 +30,7 @@ /obj/item/organ/appendix/Remove(mob/living/carbon/M, special = 0) for(var/datum/disease/appendicitis/A in M.diseases) A.cure() - inflamed = 1 + inflamed = TRUE update_icon() ..() diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index 2489aa0705..e42fc91d47 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -121,7 +121,7 @@ playsound(get_turf(owner), 'sound/mecha/mechmove03.ogg', 50, 1) /obj/item/organ/cyberimp/arm/ui_action_click() - if(crit_fail || (!holder && !contents.len)) + if(crit_fail || (organ_flags & ORGAN_FAILING) || (!holder && !contents.len)) to_chat(owner, "The implant doesn't respond. It seems to be broken...") return @@ -145,7 +145,7 @@ . = ..() if(. & EMP_PROTECT_SELF) return - if(prob(30/severity) && owner && !crit_fail) + if(prob(30/severity) && owner && !(organ_flags & ORGAN_FAILING)) Retract() owner.visible_message("A loud bang comes from [owner]\'s [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm!") playsound(get_turf(owner), 'sound/weapons/flashbang.ogg', 100, 1) @@ -154,6 +154,7 @@ owner.IgniteMob() owner.adjustFireLoss(25) crit_fail = 1 + organ_flags |= ORGAN_FAILING /obj/item/organ/cyberimp/arm/gun/laser diff --git a/code/modules/surgery/organs/augments_chest.dm b/code/modules/surgery/organs/augments_chest.dm index ea336bea19..7156b8180b 100644 --- a/code/modules/surgery/organs/augments_chest.dm +++ b/code/modules/surgery/organs/augments_chest.dm @@ -144,7 +144,7 @@ /obj/item/organ/cyberimp/chest/thrusters/proc/toggle(silent = FALSE) if(!on) - if(crit_fail) + if(crit_fail || (organ_flags & ORGAN_FAILING)) if(!silent) to_chat(owner, "Your thrusters set seems to be broken!") return 0 diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm index 06d523721d..114b92f10d 100644 --- a/code/modules/surgery/organs/augments_internal.dm +++ b/code/modules/surgery/organs/augments_internal.dm @@ -4,6 +4,7 @@ name = "cybernetic implant" desc = "A state-of-the-art implant that improves a baseline's functionality." status = ORGAN_ROBOTIC + organ_flags = ORGAN_SYNTHETIC var/implant_color = "#FFFFFF" var/implant_overlay var/syndicate_implant = FALSE //Makes the implant invisible to health analyzers and medical HUDs. @@ -102,7 +103,7 @@ /obj/item/organ/cyberimp/brain/anti_stun/on_life() ..() - if(crit_fail) + if(crit_fail || !(organ_flags & ORGAN_FAILING)) return owner.adjustStaminaLoss(-3.5) //Citadel edit, makes it more useful in Stamina based combat if(owner.AmountStun() > STUN_SET_AMOUNT) @@ -112,13 +113,15 @@ /obj/item/organ/cyberimp/brain/anti_stun/emp_act(severity) . = ..() - if(crit_fail || . & EMP_PROTECT_SELF) + if(crit_fail || (organ_flags & ORGAN_FAILING) || . & EMP_PROTECT_SELF) return crit_fail = TRUE + organ_flags |= ORGAN_FAILING addtimer(CALLBACK(src, .proc/reboot), 90 / severity) /obj/item/organ/cyberimp/brain/anti_stun/proc/reboot() crit_fail = FALSE + organ_flags &= ~ORGAN_FAILING //[[[[MOUTH]]]] diff --git a/code/modules/surgery/organs/autosurgeon.dm b/code/modules/surgery/organs/autosurgeon.dm index 0ba6fd7fcd..2cc5c554c6 100644 --- a/code/modules/surgery/organs/autosurgeon.dm +++ b/code/modules/surgery/organs/autosurgeon.dm @@ -99,6 +99,9 @@ /obj/item/autosurgeon/reviver starting_organ = /obj/item/organ/cyberimp/chest/reviver +/obj/item/autosurgeon/anti_drop + starting_organ = /obj/item/organ/cyberimp/brain/anti_drop + /obj/item/autosurgeon/penis desc = "A single use autosurgeon that contains a penis. A screwdriver can be used to remove it, but implants can't be placed back in." uses = 1 diff --git a/code/modules/surgery/organs/ears.dm b/code/modules/surgery/organs/ears.dm index 8e191a41a9..0d3002915e 100644 --- a/code/modules/surgery/organs/ears.dm +++ b/code/modules/surgery/organs/ears.dm @@ -6,6 +6,14 @@ slot = ORGAN_SLOT_EARS gender = PLURAL + healing_factor = STANDARD_ORGAN_HEALING + decay_factor = STANDARD_ORGAN_DECAY + + low_threshold_passed = "Your ears begin to resonate with an internal ring sometimes." + now_failing = "You are unable to hear at all!" + now_fixed = "Noise slowly begins filling your ears once more." + low_threshold_cleared = "The ringing in your ears has died down." + // `deaf` measures "ticks" of deafness. While > 0, the person is unable // to hear anything. var/deaf = 0 @@ -23,17 +31,26 @@ /obj/item/organ/ears/on_life() if(!iscarbon(owner)) return + ..() var/mob/living/carbon/C = owner + if((damage < maxHealth) && (organ_flags & ORGAN_FAILING)) //ear damage can be repaired from the failing condition + organ_flags &= ~ORGAN_FAILING // genetic deafness prevents the body from using the ears, even if healthy if(HAS_TRAIT(C, TRAIT_DEAF)) deaf = max(deaf, 1) - else if(ear_damage < UNHEALING_EAR_DAMAGE) // if higher than UNHEALING_EAR_DAMAGE, no natural healing occurs. - ear_damage = max(ear_damage - 0.05, 0) + else if(!(organ_flags & ORGAN_FAILING)) // if this organ is failing, do not clear deaf stacks. deaf = max(deaf - 1, 0) + if(prob(damage / 20) && (damage > low_threshold)) + adjustEarDamage(0, 4) + SEND_SOUND(C, sound('sound/weapons/flash_ring.ogg')) + to_chat(C, "The ringing in your ears grows louder, blocking out any external noises for a moment.") + else if((organ_flags & ORGAN_FAILING) && (deaf == 0)) + deaf = 1 //stop being not deaf you deaf idiot /obj/item/organ/ears/proc/restoreEars() deaf = 0 ear_damage = 0 + organ_flags &= ~ORGAN_FAILING var/mob/living/carbon/C = owner @@ -99,4 +116,23 @@ name = "tin ears" desc = "The robust ears of a bronze golem. " damage_multiplier = 0.1 //STRONK - bang_protect = 1 //Fear me weaklings. + bang_protect = 1 //Fear me weaklings. + +/obj/item/organ/ears/cybernetic + name = "cybernetic ears" + icon_state = "ears-c" + desc = "a basic cybernetic designed to mimic the operation of ears." + damage_multiplier = 0.9 + organ_flags = ORGAN_SYNTHETIC + +/obj/item/organ/ears/cybernetic/upgraded + name = "upgraded cybernetic ears" + icon_state = "ears-c-u" + desc = "an advanced cybernetic ear, surpassing the performance of organic ears" + damage_multiplier = 0.5 + +/obj/item/organ/ears/cybernetic/emp_act(severity) + . = ..() + if(. & EMP_PROTECT_SELF) + return + damage += 40/severity diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index eeaaaf2a03..cb890082cd 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -6,6 +6,19 @@ slot = ORGAN_SLOT_EYES gender = PLURAL + healing_factor = STANDARD_ORGAN_HEALING + decay_factor = STANDARD_ORGAN_DECAY + maxHealth = 0.5 * STANDARD_ORGAN_THRESHOLD //half the normal health max since we go blind at 30, a permanent blindness at 50 therefore makes sense unless medicine is administered + high_threshold = 0.3 * STANDARD_ORGAN_THRESHOLD //threshold at 30 + low_threshold = 0.15 * STANDARD_ORGAN_THRESHOLD //threshold at 15 + + low_threshold_passed = "Distant objects become somewhat less tangible." + high_threshold_passed = "Everything starts to look a lot less clear." + now_failing = "Darkness envelopes you, as your eyes go blind!" + now_fixed = "Color and shapes are once again perceivable." + high_threshold_cleared = "Your vision functions passably once more." + low_threshold_cleared = "Your vision is cleared of any ailment." + var/sight_flags = 0 var/see_in_dark = 2 var/eye_damage = 0 @@ -15,9 +28,12 @@ var/flash_protect = 0 var/see_invisible = SEE_INVISIBLE_LIVING var/lighting_alpha + var/damaged = FALSE //damaged indicates that our eyes are undergoing some level of negative effect /obj/item/organ/eyes/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = FALSE) ..() + if(damage == initial(damage)) + clear_eye_trauma() if(ishuman(owner)) var/mob/living/carbon/human/HMN = owner old_eye_color = HMN.eye_color @@ -32,7 +48,9 @@ M.update_tint() owner.update_sight() + /obj/item/organ/eyes/Remove(mob/living/carbon/M, special = 0) + clear_eye_trauma() ..() if(ishuman(M) && eye_color) var/mob/living/carbon/human/HMN = M @@ -41,6 +59,34 @@ M.update_tint() M.update_sight() +/obj/item/organ/eyes/on_life() + ..() + var/mob/living/carbon/C = owner + //since we can repair fully damaged eyes, check if healing has occurred + if((organ_flags & ORGAN_FAILING) && (damage < maxHealth)) + organ_flags &= ~ORGAN_FAILING + C.cure_blind(EYE_DAMAGE) + //various degrees of "oh fuck my eyes", from "point a laser at your eye" to "staring at the Sun" intensities + if(damage > 20) + damaged = TRUE + if(organ_flags & ORGAN_FAILING) + C.become_blind(EYE_DAMAGE) + else if(damage > 30) + C.overlay_fullscreen("eye_damage", /obj/screen/fullscreen/impaired, 2) + else + C.overlay_fullscreen("eye_damage", /obj/screen/fullscreen/impaired, 1) + //called once since we don't want to keep clearing the screen of eye damage for people who are below 20 damage + else if(damaged) + damaged = FALSE + C.clear_fullscreen("eye_damage") + return + +/obj/item/organ/eyes/proc/clear_eye_trauma() + var/mob/living/carbon/C = owner + C.clear_fullscreen("eye_damage") + C.cure_blind(EYE_DAMAGE) + damaged = FALSE + /obj/item/organ/eyes/night_vision name = "shadow eyes" desc = "A spooky set of eyes that can see in the dark." @@ -88,15 +134,17 @@ icon_state = "cybernetic_eyeballs" desc = "Your vision is augmented." status = ORGAN_ROBOTIC + organ_flags = ORGAN_SYNTHETIC /obj/item/organ/eyes/robotic/emp_act(severity) . = ..() if(!owner || . & EMP_PROTECT_SELF) return - if(prob(10 * severity)) - return to_chat(owner, "Static obfuscates your vision!") owner.flash_act(visual = 1) + if(severity == EMP_HEAVY) + owner.adjustOrganLoss(ORGAN_SLOT_EYES, 20) + /obj/item/organ/eyes/robotic/xray name = "\improper X-ray eyes" @@ -137,7 +185,7 @@ M.become_blind("flashlight_eyes") -/obj/item/organ/eyes/robotic/flashlight/Remove(var/mob/living/carbon/M, var/special = 0) +/obj/item/organ/eyes/robotic/flashlight/Remove(var/mob/living/carbon/M, special = FALSE) eye.on = FALSE eye.update_brightness(M) eye.forceMove(src) @@ -321,7 +369,7 @@ if(!istype(parent)) return INITIALIZE_HINT_QDEL -/obj/item/organ/eyes/moth - name = "moth eyes" +/obj/item/organ/eyes/insect + name = "insect eyes" desc = "These eyes seem to have increased sensitivity to bright light, with no improvement to low light vision." flash_protect = -1 diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm index 5852cba0c9..5fea801ba0 100644 --- a/code/modules/surgery/organs/heart.dm +++ b/code/modules/surgery/organs/heart.dm @@ -4,12 +4,24 @@ icon_state = "heart-on" zone = BODY_ZONE_CHEST slot = ORGAN_SLOT_HEART + + healing_factor = STANDARD_ORGAN_HEALING + decay_factor = 4 * STANDARD_ORGAN_DECAY //designed to fail about 5 minutes after death + + low_threshold_passed = "Prickles of pain appear then die out from within your chest..." + high_threshold_passed = "Something inside your chest hurts, and the pain isn't subsiding. You notice yourself breathing far faster than before." + now_fixed = "Your heart begins to beat again." + high_threshold_cleared = "The pain in your chest has died down, and your breathing becomes more relaxed." + // Heart attack code is in code/modules/mob/living/carbon/human/life.dm var/beating = 1 var/icon_base = "heart" attack_verb = list("beat", "thumped") var/beat = BEAT_NONE//is this mob having a heatbeat sound played? if so, which? + var/failed = FALSE //to prevent constantly running failing code + var/operated = FALSE //whether the heart's been operated on to fix some of its damages + /obj/item/organ/heart/update_icon() if(beating) icon_state = "[icon_base]-on" @@ -50,6 +62,7 @@ /obj/item/organ/heart/on_life() if(owner.client && beating) + failed = FALSE var/sound/slowbeat = sound('sound/health/slowbeat.ogg', repeat = TRUE) var/sound/fastbeat = sound('sound/health/fastbeat.ogg', repeat = TRUE) var/mob/living/carbon/H = owner @@ -70,11 +83,18 @@ H.stop_sound_channel(CHANNEL_HEARTBEAT) beat = BEAT_NONE + if(organ_flags & ORGAN_FAILING) //heart broke, stopped beating, death imminent + if(owner.stat == CONSCIOUS) + owner.visible_message("[owner] clutches at [owner.p_their()] chest as if [owner.p_their()] heart is stopping!") + owner.set_heartattack(TRUE) + failed = TRUE + /obj/item/organ/heart/cursed name = "cursed heart" desc = "A heart that, when inserted, will force you to pump it manually." icon_state = "cursedheart-off" icon_base = "cursedheart" + decay_factor = 0 actions_types = list(/datum/action/item_action/organ_action/cursed_heart) var/last_pump = 0 var/add_colour = TRUE //So we're not constantly recreating colour datums @@ -153,18 +173,48 @@ name = "cybernetic heart" desc = "An electronic device designed to mimic the functions of an organic human heart. Offers no benefit over an organic heart other than being easy to make." icon_state = "heart-c" - synthetic = TRUE + organ_flags = ORGAN_SYNTHETIC -/obj/item/organ/heart/cybernetic/emp_act() +/obj/item/organ/heart/cybernetic/emp_act(severity) . = ..() if(. & EMP_PROTECT_SELF) return Stop() + addtimer(CALLBACK(src, .proc/Restart), 20/severity SECONDS) + damage += 100/severity + +/obj/item/organ/heart/cybernetic/upgraded + name = "upgraded cybernetic heart" + desc = "An electronic device designed to mimic the functions of an organic human heart. Also holds an emergency dose of epinephrine, used automatically after facing severe trauma. This upgraded model can regenerate its dose after use." + icon_state = "heart-c-u" + maxHealth = 2 * STANDARD_ORGAN_THRESHOLD + + //I put it on upgraded for now. + var/dose_available = TRUE + var/rid = /datum/reagent/medicine/epinephrine + var/ramount = 10 + +obj/item/organ/heart/cybernetic/upgraded/on_life() + . = ..() + if(dose_available && owner.health <= owner.crit_threshold && !owner.reagents.has_reagent(rid)) + owner.reagents.add_reagent(rid, ramount) + used_dose() + if(ramount < 10) //eats your nutrition to regen epinephrine + var/regen_amount = owner.nutrition/2000 + owner.nutrition -= regen_amount + ramount += regen_amount + +/obj/item/organ/heart/cybernetic/upgraded/proc/used_dose() + . = ..() + addtimer(VARSET_CALLBACK(src, dose_available, TRUE), 5 MINUTES) + ramount = 0 + + /obj/item/organ/heart/freedom name = "heart of freedom" desc = "This heart pumps with the passion to give... something freedom." - synthetic = TRUE //the power of freedom prevents heart attacks + organ_flags = ORGAN_SYNTHETIC //the power of freedom prevents heart attacks var/min_next_adrenaline = 0 /obj/item/organ/heart/freedom/on_life() diff --git a/code/modules/surgery/organs/liver.dm b/code/modules/surgery/organs/liver.dm index 94973a8e10..01559c83c3 100755 --- a/code/modules/surgery/organs/liver.dm +++ b/code/modules/surgery/organs/liver.dm @@ -10,10 +10,13 @@ zone = BODY_ZONE_CHEST slot = ORGAN_SLOT_LIVER desc = "Pairing suggestion: chianti and fava beans." - var/damage = 0 //liver damage, 0 is no damage, damage=maxHealth causes liver failure + + maxHealth = STANDARD_ORGAN_THRESHOLD + healing_factor = STANDARD_ORGAN_HEALING + decay_factor = STANDARD_ORGAN_DECAY + var/alcohol_tolerance = ALCOHOL_RATE//affects how much damage the liver takes from alcohol var/failing //is this liver failing? - var/maxHealth = LIVER_DEFAULT_HEALTH var/toxTolerance = LIVER_DEFAULT_TOX_TOLERANCE//maximum amount of toxins the liver can just shrug off var/toxLethality = LIVER_DEFAULT_TOX_LETHALITY//affects how much damage toxins do to the liver var/filterToxins = TRUE //whether to filter toxins @@ -24,7 +27,7 @@ var/mob/living/carbon/C = owner if(istype(C)) - if(!failing)//can't process reagents with a failing liver + if(!(organ_flags & ORGAN_FAILING))//can't process reagents with a failing liver //slowly heal liver damage damage = max(0, damage - 0.1) @@ -69,7 +72,7 @@ if(moveCalc == cachedmoveCalc)//reduce calculations return if(prob(5)) - to_chat(owner, "You feel a stange ache in your side, almost like a sitch. This pain is affecting your movements and making you feel lightheaded.") + to_chat(owner, "You feel a stange ache in your side, almost like a stitch. This pain is affecting your movements and making you feel lightheaded.") var/mob/living/carbon/human/H = owner H.add_movespeed_modifier(LIVER_SWELLING_MOVE_MODIFY, TRUE, 100, NONE, override = TRUE, multiplicative_slowdown = moveCalc) H.AdjustBloodVol(moveCalc/3) @@ -97,14 +100,15 @@ name = "cybernetic liver" icon_state = "liver-c" desc = "An electronic device designed to mimic the functions of a human liver. It has no benefits over an organic liver, but is easy to produce." - synthetic = TRUE + organ_flags = ORGAN_SYNTHETIC + maxHealth = 1.1 * STANDARD_ORGAN_THRESHOLD /obj/item/organ/liver/cybernetic/upgraded name = "upgraded cybernetic liver" icon_state = "liver-c-u" desc = "An upgraded version of the cybernetic liver, designed to improve upon organic livers. It is resistant to alcohol poisoning and is very robust at filtering toxins." alcohol_tolerance = 0.001 - maxHealth = 200 //double the health of a normal liver + maxHealth = 2 * STANDARD_ORGAN_THRESHOLD toxTolerance = 15 //can shrug off up to 15u of toxins toxLethality = 0.008 //20% less damage than a normal liver diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm index cfbb530e69..f3eaba1a1b 100644 --- a/code/modules/surgery/organs/lungs.dm +++ b/code/modules/surgery/organs/lungs.dm @@ -8,6 +8,19 @@ gender = PLURAL w_class = WEIGHT_CLASS_NORMAL + var/failed = FALSE + var/operated = FALSE //whether we can still have our damages fixed through surgery + + //health + maxHealth = LUNGS_MAX_HEALTH + + healing_factor = STANDARD_ORGAN_HEALING + decay_factor = STANDARD_ORGAN_DECAY + + high_threshold_passed = "You feel some sort of constriction around your chest as your breathing becomes shallow and rapid." + now_fixed = "Your lungs seem to once again be able to hold air." + high_threshold_cleared = "The constriction around your chest loosens as your breathing calms down." + //Breath damage var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa @@ -56,36 +69,35 @@ var/crit_stabilizing_reagent = "epinephrine" - //health - var/maxHealth = LUNGS_MAX_HEALTH - var/damage = 0 + //TODO: lung health affects lung function -/obj/item/organ/lungs/proc/adjustLungLoss(damage_mod, mob/living/carbon/M) //damage might be too low atm. +/obj/item/organ/lungs/onDamage(damage_mod) //damage might be too low atm. + var/cached_damage = damage if (maxHealth == INFINITY) return - if(damage+damage_mod < 0) - damage = 0 + if(cached_damage+damage_mod < 0) + cached_damage = 0 return - damage += damage_mod - if ((damage / maxHealth) > 1) - to_chat(M, "You feel your lungs collapse within your chest as you gasp for air, unable to inflate them anymore!") - M.emote("gasp") + cached_damage += damage_mod + if ((cached_damage/ maxHealth) > 1) + to_chat(owner, "You feel your lungs collapse within your chest as you gasp for air, unable to inflate them anymore!") + owner.emote("gasp") SSblackbox.record_feedback("tally", "fermi_chem", 1, "Lungs lost") - qdel(src) - else if ((damage / maxHealth) > 0.75) - to_chat(M, "It's getting really hard to breathe!!") - M.emote("gasp") - M.Dizzy(3) - else if ((damage / maxHealth) > 0.5) - M.Dizzy(2) - to_chat(M, "Your chest is really starting to hurt.") - M.emote("cough") - else if ((damage / maxHealth) > 0.2) - to_chat(M, "You feel an ache within your chest.") - M.emote("cough") - M.Dizzy(1) + //qdel(src) - Handled elsewhere for now. + else if ((cached_damage / maxHealth) > 0.75) + to_chat(owner, "It's getting really hard to breathe!!") + owner.emote("gasp") + owner.Dizzy(3) + else if ((cached_damage / maxHealth) > 0.5) + owner.Dizzy(2) + to_chat(owner, "Your chest is really starting to hurt.") + owner.emote("cough") + else if ((cached_damage / maxHealth) > 0.2) + to_chat(owner, "You feel an ache within your chest.") + owner.emote("cough") + owner.Dizzy(1) /obj/item/organ/lungs/proc/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/H) //TODO: add lung damage = less oxygen gains @@ -289,7 +301,7 @@ H.hallucination += 10 H.reagents.add_reagent("bz_metabolites",5) if(prob(33)) - H.adjustBrainLoss(3, 150) + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3, 150) else if(bz_pp > 0.01) H.hallucination += 5 @@ -405,13 +417,13 @@ var/cold_modifier = H.dna.species.coldmod if(breath_temperature < cold_level_3_threshold) H.apply_damage_type(cold_level_3_damage*cold_modifier, cold_damage_type) - adjustLungLoss((cold_level_3_damage*cold_modifier*2), H) + H.adjustOrganLoss(ORGAN_SLOT_LUNGS, (cold_level_3_damage*cold_modifier*2)) if(breath_temperature > cold_level_3_threshold && breath_temperature < cold_level_2_threshold) H.apply_damage_type(cold_level_2_damage*cold_modifier, cold_damage_type) - adjustLungLoss((cold_level_2_damage*cold_modifier*2), H) + H.adjustOrganLoss(ORGAN_SLOT_LUNGS, (cold_level_2_damage*cold_modifier*2)) if(breath_temperature > cold_level_2_threshold && breath_temperature < cold_level_1_threshold) H.apply_damage_type(cold_level_1_damage*cold_modifier, cold_damage_type) - adjustLungLoss((cold_level_1_damage*cold_modifier*2), H) + H.adjustOrganLoss(ORGAN_SLOT_LUNGS, (cold_level_1_damage*cold_modifier*2)) if(breath_temperature < cold_level_1_threshold) if(prob(20)) to_chat(H, "You feel [cold_message] in your [name]!") @@ -420,17 +432,29 @@ var/heat_modifier = H.dna.species.heatmod if(breath_temperature > heat_level_1_threshold && breath_temperature < heat_level_2_threshold) H.apply_damage_type(heat_level_1_damage*heat_modifier, heat_damage_type) - adjustLungLoss((heat_level_1_damage*heat_modifier*2), H) + H.adjustOrganLoss(ORGAN_SLOT_LUNGS, (heat_level_1_damage*heat_modifier*2)) if(breath_temperature > heat_level_2_threshold && breath_temperature < heat_level_3_threshold) H.apply_damage_type(heat_level_2_damage*heat_modifier, heat_damage_type) - adjustLungLoss((heat_level_2_damage*heat_modifier*2), H) + H.adjustOrganLoss(ORGAN_SLOT_LUNGS, (heat_level_2_damage*heat_modifier*2)) if(breath_temperature > heat_level_3_threshold) H.apply_damage_type(heat_level_3_damage*heat_modifier, heat_damage_type) - adjustLungLoss((heat_level_3_damage*heat_modifier*2), H) + H.adjustOrganLoss(ORGAN_SLOT_LUNGS, (heat_level_3_damage*heat_modifier*2)) if(breath_temperature > heat_level_1_threshold) if(prob(20)) to_chat(H, "You feel [hot_message] in your [name]!") + +/obj/item/organ/lungs/on_life() + ..() + if((!failed) && ((organ_flags & ORGAN_FAILING))) + if(owner.stat == CONSCIOUS) + owner.visible_message("[owner] grabs [owner.p_their()] throat, struggling for breath!", \ + "You suddenly feel like you can't breathe!") + failed = TRUE + else if(!(organ_flags & ORGAN_FAILING)) + failed = FALSE + return + /obj/item/organ/lungs/prepare_eat() var/obj/S = ..() S.reagents.add_reagent("salbutamol", 5) @@ -451,14 +475,16 @@ name = "cybernetic lungs" desc = "A cybernetic version of the lungs found in traditional humanoid entities. It functions the same as an organic lung and is merely meant as a replacement." icon_state = "lungs-c" - synthetic = TRUE + organ_flags = ORGAN_SYNTHETIC maxHealth = 400 + safe_oxygen_min = 13 /obj/item/organ/lungs/cybernetic/emp_act() . = ..() if(. & EMP_PROTECT_SELF) return owner.losebreath = 20 + owner.adjustOrganLoss(ORGAN_SLOT_LUNGS, 25) /obj/item/organ/lungs/cybernetic/upgraded @@ -495,8 +521,24 @@ safe_toxins_max = 0 //We breathe this to gain POWER. + cold_level_1_threshold = 285 // Remember when slimes used to be succeptable to cold? Well.... + cold_level_2_threshold = 260 + cold_level_3_threshold = 230 + + maxHealth = 250 + /obj/item/organ/lungs/slime/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/H) . = ..() if (breath && breath.gases[/datum/gas/plasma]) var/plasma_pp = breath.get_breath_partial_pressure(breath.gases[/datum/gas/plasma]) owner.blood_volume += (0.2 * plasma_pp) // 10/s when breathing literally nothing but plasma, which will suffocate you. + +/obj/item/organ/lungs/yamerol + name = "Yamerol lungs" + desc = "A temporary pair of lungs made from self assembling yamerol molecules." + maxHealth = 200 + color = "#68e83a" + +/obj/item/organ/lungs/yamerol/on_life() + ..() + damage += 2 //Yamerol lungs are temporary diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 9f910de9a9..a17d4906bf 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -1,3 +1,6 @@ +#define STANDARD_ORGAN_THRESHOLD 100 +#define STANDARD_ORGAN_HEALING 0.001 + /obj/item/organ name = "organ" icon = 'icons/obj/surgery.dmi' @@ -8,11 +11,23 @@ var/zone = BODY_ZONE_CHEST var/slot // DO NOT add slots with matching names to different zones - it will break internal_organs_slot list! - var/vital = 0 - //Was this organ implanted/inserted/etc, if true will not be removed during species change. - var/external = FALSE - var/synthetic = FALSE // To distinguish between organic and synthetic organs + var/organ_flags = 0 + var/maxHealth = STANDARD_ORGAN_THRESHOLD + var/damage = 0 //total damage this organ has sustained + ///Healing factor and decay factor function on % of maxhealth, and do not work by applying a static number per tick + var/healing_factor = 0 //fraction of maxhealth healed per on_life(), set to 0 for generic organs + var/decay_factor = 0 //same as above but when without a living owner, set to 0 for generic organs + var/high_threshold = STANDARD_ORGAN_THRESHOLD * 0.45 //when severe organ damage occurs + var/low_threshold = STANDARD_ORGAN_THRESHOLD * 0.1 //when minor organ damage occurs + ///Organ variables for determining what we alert the owner with when they pass/clear the damage thresholds + var/prev_damage = 0 + var/low_threshold_passed + var/high_threshold_passed + var/now_failing + var/now_fixed + var/high_threshold_cleared + var/low_threshold_cleared /obj/item/organ/proc/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE) if(!iscarbon(M) || owner == M) @@ -33,31 +48,53 @@ for(var/X in actions) var/datum/action/A = X A.Grant(M) + STOP_PROCESSING(SSobj, src) //Special is for instant replacement like autosurgeons -/obj/item/organ/proc/Remove(mob/living/carbon/M, special = 0) +/obj/item/organ/proc/Remove(mob/living/carbon/M, special = FALSE) owner = null if(M) M.internal_organs -= src if(M.internal_organs_slot[slot] == src) M.internal_organs_slot.Remove(slot) - if(vital && !special && !(M.status_flags & GODMODE)) + if((organ_flags & ORGAN_VITAL) && !special && !(M.status_flags & GODMODE)) M.death() for(var/X in actions) var/datum/action/A = X A.Remove(M) + START_PROCESSING(SSobj, src) /obj/item/organ/proc/on_find(mob/living/finder) return -/obj/item/organ/proc/on_life() - return +/obj/item/organ/process() + on_death() //Kinda hate doing it like this, but I really don't want to call process directly. + +/obj/item/organ/proc/on_death() //runs decay when outside of a person + if(organ_flags & (ORGAN_SYNTHETIC | ORGAN_FROZEN | ORGAN_NO_SPOIL)) + return + applyOrganDamage(maxHealth * decay_factor) + +/obj/item/organ/proc/on_life() //repair organ damage if the organ is not failing + if(!(organ_flags & ORGAN_FAILING)) + ///Damage decrements by a percent of its maxhealth + var/healing_amount = -(maxHealth * healing_factor) + ///Damage decrements again by a percent of its maxhealth, up to a total of 4 extra times depending on the owner's health + healing_amount -= owner.satiety > 0 ? 4 * healing_factor * owner.satiety / MAX_SATIETY : 0 + applyOrganDamage(healing_amount) //to FERMI_TWEAK + //Make it so each threshold is stuck. /obj/item/organ/examine(mob/user) - ..() - if(status == ORGAN_ROBOTIC && crit_fail) - to_chat(user, "[src] seems to be broken!") + . = ..() + if(!organ_flags & ORGAN_FAILING) + if(status == ORGAN_ROBOTIC) + . += "[src] seems to be broken!" + return + . += "[src] has decayed for too long, and has turned a sickly color! It doesn't look like it will work anymore!" + return + if(damage > high_threshold) + . += "[src] is starting to look discolored." /obj/item/organ/proc/prepare_eat() @@ -78,6 +115,10 @@ foodtype = RAW | MEAT | GROSS +/obj/item/organ/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + /obj/item/organ/Destroy() if(owner) // The special flag is important, because otherwise mobs can die @@ -100,13 +141,64 @@ /obj/item/organ/item_action_slot_check(slot,mob/user) return //so we don't grant the organ's action to mobs who pick up the organ. +///Adjusts an organ's damage by the amount "d", up to a maximum amount, which is by default max damage +/obj/item/organ/proc/applyOrganDamage(var/d, var/maximum = maxHealth) //use for damaging effects + if(!d) //Micro-optimization. + return + if(maximum < damage) + return + damage = CLAMP(damage + d, 0, maximum) + var/mess = check_damage_thresholds(owner) + prev_damage = damage + if(mess && owner) + to_chat(owner, mess) + +///SETS an organ's damage to the amount "d", and in doing so clears or sets the failing flag, good for when you have an effect that should fix an organ if broken +/obj/item/organ/proc/setOrganDamage(var/d) //use mostly for admin heals + applyOrganDamage(d - damage) + +/** check_damage_thresholds + * input: M (a mob, the owner of the organ we call the proc on) + * output: returns a message should get displayed. + * description: By checking our current damage against our previous damage, we can decide whether we've passed an organ threshold. + * If we have, send the corresponding threshold message to the owner, if such a message exists. + */ +/obj/item/organ/proc/check_damage_thresholds(var/M) + if(damage == prev_damage) + return + var/delta = damage - prev_damage + if(delta > 0) + if(damage >= maxHealth) + organ_flags |= ORGAN_FAILING + return now_failing + if(damage > high_threshold && prev_damage <= high_threshold) + return high_threshold_passed + if(damage > low_threshold && prev_damage <= low_threshold) + return low_threshold_passed + else + organ_flags &= ~ORGAN_FAILING + if(prev_damage > low_threshold && damage <= low_threshold) + return low_threshold_cleared + if(prev_damage > high_threshold && damage <= high_threshold) + return high_threshold_cleared + if(prev_damage == maxHealth) + return now_fixed + +//Runs some code on the organ when damage is taken/healed +/obj/item/organ/proc/onDamage(var/d, var/maximum = maxHealth) + return + +//Runs some code on the organ when damage is taken/healed +/obj/item/organ/proc/onSetDamage(var/d, var/maximum = maxHealth) + return + //Looking for brains? //Try code/modules/mob/living/carbon/brain/brain_item.dm /mob/living/proc/regenerate_organs() return 0 -/mob/living/carbon/regenerate_organs() +/mob/living/carbon/regenerate_organs(only_one = FALSE) var/breathes = TRUE var/blooded = TRUE if(dna && dna.species) @@ -125,6 +217,8 @@ else LI = new() LI.Insert(src) + if(only_one) + return TRUE if(has_stomach && !getorganslot(ORGAN_SLOT_STOMACH)) var/obj/item/organ/stomach/S @@ -134,14 +228,20 @@ else S = new() S.Insert(src) + if(only_one) + return TRUE if(breathes && !getorganslot(ORGAN_SLOT_LUNGS)) var/obj/item/organ/lungs/L = new() L.Insert(src) + if(only_one) + return TRUE if(blooded && !getorganslot(ORGAN_SLOT_HEART)) var/obj/item/organ/heart/H = new() H.Insert(src) + if(only_one) + return TRUE if(!getorganslot(ORGAN_SLOT_TONGUE)) var/obj/item/organ/tongue/T @@ -153,7 +253,10 @@ // if they have no mutant tongues, give them a regular one T.Insert(src) - else + if(only_one) + return TRUE + + else if (!only_one) var/obj/item/organ/tongue/oT = getorganslot(ORGAN_SLOT_TONGUE) if(oT.name == "fluffy tongue") var/obj/item/organ/tongue/T @@ -174,6 +277,8 @@ else E = new() E.Insert(src) + if(only_one) + return TRUE if(!getorganslot(ORGAN_SLOT_EARS)) var/obj/item/organ/ears/ears @@ -183,9 +288,13 @@ ears = new ears.Insert(src) + if(only_one) + return TRUE if(!getorganslot(ORGAN_SLOT_TAIL)) var/obj/item/organ/tail/tail if(dna && dna.species && dna.species.mutanttail) tail = new dna.species.mutanttail tail.Insert(src) + if(only_one) + return TRUE diff --git a/code/modules/surgery/organs/stomach.dm b/code/modules/surgery/organs/stomach.dm index 24132bcf04..8ddaa951d8 100755 --- a/code/modules/surgery/organs/stomach.dm +++ b/code/modules/surgery/organs/stomach.dm @@ -8,12 +8,41 @@ desc = "Onaka ga suite imasu." var/disgust_metabolism = 1 -/obj/item/organ/stomach/on_life() - var/mob/living/carbon/human/H = owner + healing_factor = STANDARD_ORGAN_HEALING + decay_factor = STANDARD_ORGAN_DECAY - if(istype(H)) - H.dna.species.handle_digestion(H) + low_threshold_passed = "Your stomach flashes with pain before subsiding. Food doesn't seem like a good idea right now." + high_threshold_passed = "Your stomach flares up with constant pain- you can hardly stomach the idea of food right now!" + high_threshold_cleared = "The pain in your stomach dies down for now, but food still seems unappealing." + low_threshold_cleared = "The last bouts of pain in your stomach have died out." + +/obj/item/organ/stomach/on_life() + var/datum/reagent/consumable/nutriment/Nutri + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + if(!(organ_flags & ORGAN_FAILING)) + H.dna.species.handle_digestion(H) handle_disgust(H) + Nutri = locate(/datum/reagent/consumable/nutriment) in H.reagents.reagent_list + + if(Nutri) + if(prob((damage/40) * Nutri.volume * Nutri.volume)) + H.vomit(damage) + to_chat(H, "Your stomach reels in pain as you're incapable of holding down all that food!") + + else if(Nutri && damage > high_threshold) + if(prob((damage/10) * Nutri.volume * Nutri.volume)) + H.vomit(damage) + to_chat(H, "Your stomach reels in pain as you're incapable of holding down all that food!") + + + else if(iscarbon(owner)) + var/mob/living/carbon/C = owner + Nutri = locate(/datum/reagent/consumable/nutriment) in C.reagents.reagent_list + + if(damage < low_threshold) + return + /obj/item/organ/stomach/proc/handle_disgust(mob/living/carbon/human/H) if(H.disgust) diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm index 3eaaa8de92..3dd5c7b2b7 100644 --- a/code/modules/surgery/organs/tongue.dm +++ b/code/modules/surgery/organs/tongue.dm @@ -10,8 +10,7 @@ var/list/languages_possible var/say_mod = null var/taste_sensitivity = 15 // lower is more sensitive. - var/maxHealth = TONGUE_MAX_HEALTH - var/damage = 0 + maxHealth = TONGUE_MAX_HEALTH var/modifies_speech = FALSE var/static/list/languages_possible_base = typecacheof(list( /datum/language/common, @@ -31,27 +30,34 @@ /obj/item/organ/tongue/proc/handle_speech(datum/source, list/speech_args) -/obj/item/organ/tongue/proc/adjustTongueLoss(mob/living/carbon/M, damage_mod) - if (maxHealth == "alien") - return - if (maxHealth == "bone") - var/target = M.get_bodypart(BODY_ZONE_HEAD) - M.apply_damage(damage_mod, BURN, target) - to_chat(M, "The drink burns your skull! Oof, your bones!") - return - if(damage+damage_mod < 0) - damage = 0 +/obj/item/organ/tongue/emp_act(severity) + . = ..() + if(. & EMP_PROTECT_SELF) return + if(organ_flags & ORGAN_SYNTHETIC) + var/errormessage = list("Runtime in tongue.dm, line 39: Undefined operation \"zapzap ow my tongue\"", "afhsjifksahgjkaslfhashfjsak", "-1.#IND", "Graham's number", "inside you all along", "awaiting at least 1 approving review before merging this taste request") + owner.say("The pH is appropriately [pick(errormessage)].") + +/obj/item/organ/tongue/applyOrganDamage(var/d, var/maximum = maxHealth) + + if(!d) //Micro-optimization. + return + if(maximum < damage) + return + damage = CLAMP(damage + d, 0, maximum) + var/mess = check_damage_thresholds(owner) + prev_damage = damage + if(mess && owner) + to_chat(owner, mess) - damage += damage_mod if ((damage / maxHealth) > 1) - to_chat(M, "Your tongue is singed beyond recognition, and disintegrates!") + to_chat(owner, "Your tongue is singed beyond recognition, and disintegrates!") SSblackbox.record_feedback("tally", "fermi_chem", 1, "Tongues lost to Fermi") qdel(src) else if ((damage / maxHealth) > 0.85) - to_chat(M, "Your tongue feels like it's about to fall out!.") + to_chat(owner, "Your tongue feels like it's about to fall out!.") else if ((damage / maxHealth) > 0.5) - to_chat(M, "Your tongue is really starting to hurt.") + to_chat(owner, "Your tongue is really starting to hurt.") /obj/item/organ/tongue/Insert(mob/living/carbon/M, special = 0) @@ -168,7 +174,7 @@ icon_state = "tonguexeno" say_mod = "hisses" taste_sensitivity = 10 // LIZARDS ARE ALIENS CONFIRMED - maxHealth = "alien" //Their blood is acid, so, no, though a tongueless xeno might be funny + maxHealth = 500 //They've a little mouth for a tongue, so it's pretty rhobust modifies_speech = TRUE // not really, they just hiss var/static/list/languages_possible_alien = typecacheof(list( /datum/language/xenocommon, @@ -189,9 +195,10 @@ desc = "Apparently skeletons alter the sounds they produce through oscillation of their teeth, hence their characteristic rattling." icon_state = "tonguebone" say_mod = "rattles" + organ_flags = ORGAN_NO_SPOIL attack_verb = list("bitten", "chattered", "chomped", "enamelled", "boned") taste_sensitivity = 101 // skeletons cannot taste anything - maxHealth = "bone" //Take brute damage instead + maxHealth = 75 //Take brute damage instead modifies_speech = TRUE var/chattering = FALSE var/phomeme_type = "sans" @@ -201,6 +208,14 @@ . = ..() phomeme_type = pick(phomeme_types) +/obj/item/organ/tongue/bone/applyOrganDamage(var/d, var/maximum = maxHealth) + if(!owner) + return + var/target = owner.get_bodypart(BODY_ZONE_HEAD) + owner.apply_damage(d, BURN, target) + to_chat(owner, "You feel your skull burning! Oof, your bones!") + return + /obj/item/organ/tongue/bone/handle_speech(datum/source, list/speech_args) if (chattering) chatter(speech_args[SPEECH_MESSAGE], phomeme_type, source) @@ -262,6 +277,7 @@ icon_state = "tonguecybernetic" taste_sensitivity = 10 maxHealth = 60 //It's robotic! + organ_flags = ORGAN_SYNTHETIC /obj/item/organ/tongue/cybernetic/handle_speech(datum/source, list/speech_args) speech_args[SPEECH_SPANS] |= SPAN_ROBOT @@ -271,3 +287,4 @@ say_mod = "beeps" desc = "A voice synthesizer used by IPCs to smoothly interface with organic lifeforms." electronics_magic = FALSE + organ_flags = ORGAN_SYNTHETIC diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index 8103224306..009cfce0a5 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -26,12 +26,14 @@ zone = BODY_ZONE_HEAD slot = ORGAN_SLOT_ADAMANTINE_RESONATOR icon_state = "adamantine_resonator" + decay_factor = 0 /obj/item/organ/vocal_cords/adamantine name = "adamantine vocal cords" desc = "When adamantine resonates, it causes all nearby pieces of adamantine to resonate as well. Adamantine golems use this to broadcast messages to nearby golems." actions_types = list(/datum/action/item_action/organ_action/use/adamantine_vocal_cords) icon_state = "adamantine_cords" + decay_factor = 0 /datum/action/item_action/organ_action/use/adamantine_vocal_cords/Trigger() if(!IsAvailable()) @@ -62,6 +64,7 @@ var/cooldown_mod = 1 var/base_multiplier = 1 spans = list("colossus","yell") + decay_factor = 0 /datum/action/item_action/organ_action/colossus name = "Voice of God" @@ -623,10 +626,6 @@ /datum/action/item_action/organ_action/velvet name = "Velvet chords" var/obj/item/organ/vocal_cords/velvet/cords = null - //icon_icon = 'icons/mob/screen_alert.dmi' - //button_icon_state = "velvet_chords" - //icon = 'icons/mob/screen_alert.dmi' - //icon_state = "in_love" /datum/action/item_action/organ_action/velvet/New() ..() @@ -991,7 +990,7 @@ if (HAS_TRAIT(H, TRAIT_DEAF))//How the heck you managed to get here I have no idea, but just in case! speaktrigger += "I can barely hear you! " //And the brain damage. And the brain damage. And the brain damage. And the brain damage. And the brain damage. - switch(H.getBrainLoss()) + switch(H.getOrganLoss(ORGAN_SLOT_BRAIN)) if(20 to 40) speaktrigger += "I have a mild head ache, " if(40 to 80) diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 781fdb24f5..23b4581600 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -75,6 +75,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) var/player_minimum //The minimum crew size needed for this item to be added to uplinks. var/purchase_log_vis = TRUE // Visible in the purchase log? var/restricted = FALSE // Adds restrictions for VR/Events + var/illegal_tech = TRUE // Can this item be deconstructed to unlock certain techweb research nodes? /datum/uplink_item/New() . = ..() @@ -769,6 +770,7 @@ datum/uplink_item/stealthy_weapons/taeclowndo_shoes item = /obj/item/ammo_box/a357 cost = 4 exclude_modes = list(/datum/game_mode/nuclear/clown_ops) + illegal_tech = FALSE /datum/uplink_item/ammo/a40mm name = "40mm Grenade" @@ -898,6 +900,7 @@ datum/uplink_item/stealthy_weapons/taeclowndo_shoes item = /obj/item/ammo_box/foambox/riot cost = 2 surplus = 0 + illegal_tech = FALSE /datum/uplink_item/ammo/bioterror name = "Box of Bioterror Syringes" @@ -1838,7 +1841,7 @@ datum/uplink_item/stealthy_weapons/taeclowndo_shoes desc = "A single-use autoinjector which contains an experimental serum that causes rapid muscular growth in Hominidae. \ Side-affects may include hypertrichosis, violent outbursts, and an unending affinity for bananas." item = /obj/item/reagent_containers/hypospray/magillitis - cost = 15 + cost = 8 restricted_roles = list("Geneticist", "Chief Medical Officer") /datum/uplink_item/role_restricted/modified_syringe_gun @@ -1926,6 +1929,7 @@ datum/uplink_item/stealthy_weapons/taeclowndo_shoes item = /obj/item/toy/syndicateballoon cost = 20 cant_discount = TRUE + illegal_tech = FALSE /datum/uplink_item/badass/syndiecash name = "Syndicate Briefcase Full of Cash" @@ -1949,9 +1953,11 @@ datum/uplink_item/stealthy_weapons/taeclowndo_shoes item = /obj/item/toy/cards/deck/syndicate cost = 1 surplus = 40 + illegal_tech = FALSE /datum/uplink_item/badass/syndiecigs name = "Syndicate Smokes" desc = "Strong flavor, dense smoke, infused with omnizine." item = /obj/item/storage/fancy/cigarettes/cigpack_syndicate cost = 2 + illegal_tech = FALSE diff --git a/code/modules/vehicles/scooter.dm b/code/modules/vehicles/scooter.dm index 9af5b13a7d..8c21b050aa 100644 --- a/code/modules/vehicles/scooter.dm +++ b/code/modules/vehicles/scooter.dm @@ -75,7 +75,7 @@ H.adjustStaminaLoss(40) var/head_slot = H.get_item_by_slot(SLOT_HEAD) if(!head_slot || !(istype(head_slot,/obj/item/clothing/head/helmet) || istype(head_slot,/obj/item/clothing/head/hardhat))) - H.adjustBrainLoss(3) + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3) H.updatehealth() visible_message("[src] crashes into [A], sending [H] flying!") playsound(src, 'sound/effects/bang.ogg', 50, 1) @@ -201,7 +201,7 @@ H.adjustStaminaLoss(10) var/head_slot = H.get_item_by_slot(SLOT_HEAD) if(!head_slot || !(istype(head_slot,/obj/item/clothing/head/helmet) || istype(head_slot,/obj/item/clothing/head/hardhat))) - H.adjustBrainLoss(1) + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1) H.updatehealth() visible_message("[src] crashes into [A], sending [H] flying!") playsound(src, 'sound/effects/bang.ogg', 50, 1) diff --git a/code/modules/vending/autodrobe.dm b/code/modules/vending/autodrobe.dm index a265a4e907..c577643df3 100644 --- a/code/modules/vending/autodrobe.dm +++ b/code/modules/vending/autodrobe.dm @@ -54,7 +54,7 @@ /obj/item/clothing/head/ushanka = 1, /obj/item/clothing/suit/imperium_monk = 1, /obj/item/clothing/mask/gas/cyborg = 1, - /obj/item/clothing/suit/holidaypriest = 1, + /obj/item/clothing/suit/chaplain/holidaypriest = 1, /obj/item/clothing/head/wizard/marisa/fake = 1, /obj/item/clothing/suit/wizrobe/marisa/fake = 1, /obj/item/clothing/under/sundress = 1, @@ -122,7 +122,7 @@ /obj/item/clothing/shoes/roman = 1, /obj/item/shield/riot/roman/fake = 1, /obj/item/skub = 1, - /obj/item/clothing/under/lobster = 1, // CIT CHANGES + /obj/item/clothing/under/lobster = 1, // CIT CHANGES /obj/item/clothing/head/lobsterhat = 1, /obj/item/clothing/head/drfreezehat = 1, /obj/item/clothing/suit/dracula = 1, diff --git a/code/modules/vending/clothesmate.dm b/code/modules/vending/clothesmate.dm index 1497992fe8..b232184975 100644 --- a/code/modules/vending/clothesmate.dm +++ b/code/modules/vending/clothesmate.dm @@ -99,7 +99,9 @@ /obj/item/clothing/suit/jacket/letterman_red = 3, /obj/item/clothing/ears/headphones = 10, /obj/item/clothing/suit/apron/purple_bartender = 4, - /obj/item/clothing/under/rank/bartender/purple = 4) + /obj/item/clothing/under/rank/bartender/purple = 4, + /obj/item/clothing/accessory/attrocious_pokadots = 8, + /obj/item/clothing/accessory/black_white_pokadots = 8) contraband = list(/obj/item/clothing/under/syndicate/tacticool = 3, /obj/item/clothing/under/syndicate/tacticool/skirt = 3, /obj/item/clothing/mask/balaclava = 3, @@ -109,7 +111,8 @@ /obj/item/clothing/suit/jacket/letterman_syndie = 5, /obj/item/clothing/under/jabroni = 2, /obj/item/clothing/suit/vapeshirt = 2, - /obj/item/clothing/under/geisha = 4) + /obj/item/clothing/under/geisha = 4, + /obj/item/clothing/accessory/syndi_pokadots = 4) premium = list(/obj/item/clothing/under/suit_jacket/checkered = 4, /obj/item/clothing/head/mailman = 2, /obj/item/clothing/under/rank/mailman = 2, @@ -117,7 +120,8 @@ /obj/item/clothing/suit/jacket/leather/overcoat = 4, /obj/item/clothing/under/pants/mustangjeans = 3, /obj/item/clothing/neck/necklace/dope = 5, - /obj/item/clothing/suit/jacket/letterman_nanotrasen = 5) + /obj/item/clothing/suit/jacket/letterman_nanotrasen = 5, + /obj/item/clothing/accessory/nt_pokadots = 5) refill_canister = /obj/item/vending_refill/clothing /obj/item/vending_refill/clothing diff --git a/code/modules/vending/drinnerware.dm b/code/modules/vending/drinnerware.dm index 749a3b7c2b..3b036c0d90 100644 --- a/code/modules/vending/drinnerware.dm +++ b/code/modules/vending/drinnerware.dm @@ -9,7 +9,9 @@ /obj/item/kitchen/rollingpin = 2, /obj/item/reagent_containers/food/drinks/drinkingglass = 8, /obj/item/clothing/suit/apron/chef = 2, + /obj/item/storage/box/cups = 2, /obj/item/reagent_containers/food/condiment/pack/ketchup = 5, + /obj/item/reagent_containers/food/condiment/pack/mustard = 5, /obj/item/reagent_containers/food/condiment/pack/hotsauce = 5, /obj/item/reagent_containers/food/condiment/pack/astrotame = 5, /obj/item/reagent_containers/food/condiment/saltshaker = 5, diff --git a/code/modules/vending/engivend.dm b/code/modules/vending/engivend.dm index 9358d1652f..1b66dd5eb6 100644 --- a/code/modules/vending/engivend.dm +++ b/code/modules/vending/engivend.dm @@ -7,12 +7,12 @@ products = list(/obj/item/clothing/glasses/meson/engine = 5, /obj/item/clothing/glasses/welding = 5, /obj/item/multitool = 5, - /obj/item/construction/rcd/loaded = 3, + /obj/item/construction/rcd/loaded/upgraded = 3, /obj/item/grenade/chem_grenade/smart_metal_foam = 10, /obj/item/geiger_counter = 6, /obj/item/stock_parts/cell/high = 10, - /obj/item/electronics/airlock = 10, - /obj/item/electronics/apc = 10, + /obj/item/electronics/airlock = 10, + /obj/item/electronics/apc = 10, /obj/item/electronics/airalarm = 10, /obj/item/electronics/firealarm = 10, /obj/item/electronics/firelock = 10, diff --git a/code/modules/vending/wardrobes.dm b/code/modules/vending/wardrobes.dm index 015890419d..fbc7713f25 100644 --- a/code/modules/vending/wardrobes.dm +++ b/code/modules/vending/wardrobes.dm @@ -227,8 +227,8 @@ name = "ChefDrobe" desc = "This vending machine might not dispense meat, but it certainly dispenses chef related clothing." icon_state = "chefdrobe" - product_ads = "Our clothes are guaranteed to protect you from food splatters!" - vend_reply = "Thank you for using the ChefDrobe!" + product_ads = "Our clothes are guaranteed to protect you from food splatters!;Now stocking recipe books!" + vend_reply = "Thank you for using the ChefDrobe!;Just like your grandmother's old recipes!" products = list(/obj/item/clothing/under/waiter = 3, /obj/item/radio/headset/headset_srv = 4, /obj/item/clothing/accessory/waistcoat = 3, @@ -241,7 +241,8 @@ /obj/item/clothing/under/rank/chef/skirt = 2, /obj/item/clothing/head/chefhat = 2, /obj/item/reagent_containers/rag = 3, - /obj/item/book/granter/crafting_recipe/cooking_sweets_101 = 2) + /obj/item/book/granter/crafting_recipe/cooking_sweets_101 = 2, + /obj/item/book/granter/crafting_recipe/coldcooking = 2) refill_canister = /obj/item/vending_refill/wardrobe/chef_wardrobe /obj/item/vending_refill/wardrobe/chef_wardrobe @@ -317,10 +318,10 @@ /obj/item/clothing/under/rank/chaplain = 2, /obj/item/clothing/under/rank/chaplain/skirt = 2, /obj/item/clothing/shoes/sneakers/black = 2, - /obj/item/clothing/suit/nun = 2, + /obj/item/clothing/suit/chaplain/nun = 2, /obj/item/clothing/head/nun_hood = 2, - /obj/item/clothing/suit/holidaypriest = 2, - /obj/item/clothing/suit/pharaoh = 2, + /obj/item/clothing/suit/chaplain/holidaypriest = 2, + /obj/item/clothing/suit/chaplain/pharaoh = 2, /obj/item/clothing/head/nemes = 1, /obj/item/clothing/head/pharaoh = 1, /obj/item/storage/fancy/candle_box = 3) diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm index ae53f29a55..0f6ef14cd1 100644 --- a/code/modules/zombie/items.dm +++ b/code/modules/zombie/items.dm @@ -73,5 +73,5 @@ user.adjustFireLoss(-hp_gained, 0) user.adjustCloneLoss(-hp_gained, 0) user.updatehealth() - user.adjustBrainLoss(-hp_gained) // Zom Bee gibbers "BRAAAAISNSs!1!" + user.adjustOrganLoss(ORGAN_SLOT_BRAIN, -hp_gained) // Zom Bee gibbers "BRAAAAISNSs!1!" user.nutrition = min(user.nutrition + hp_gained, NUTRITION_LEVEL_FULL) diff --git a/config/config.txt b/config/config.txt index a01f5424da..c75cd6dc1b 100644 --- a/config/config.txt +++ b/config/config.txt @@ -146,6 +146,9 @@ LOG_MANIFEST ## Enable logging pictures # LOG_PICTURES +## log virus and actions +LOG_VIRUS + ##Log camera pictures - Must have picture logging enabled PICTURE_LOGGING_CAMERA diff --git a/config/game_options.txt b/config/game_options.txt index 6d59f10807..2e346ce0ac 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -410,7 +410,7 @@ ROUNDSTART_RACES human ## Races that are strictly worse than humans that could probably be turned on without balance concerns ROUNDSTART_RACES lizard #ROUNDSTART_RACES fly -#ROUNDSTART_RACES moth +#ROUNDSTART_RACES insect ROUNDSTART_RACES plasmaman #ROUNDSTART_RACES shadow ROUNDSTART_RACES felinid diff --git a/html/changelogs/AutoChangeLog-pr-8649.yml b/html/changelogs/AutoChangeLog-pr-8649.yml new file mode 100644 index 0000000000..c8f61cc24e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8649.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - tweak: "The default amount of z-levels reserved specifically for space ruin generation has been increased from 1 to 2" diff --git a/html/changelogs/AutoChangeLog-pr-8914.yml b/html/changelogs/AutoChangeLog-pr-8914.yml new file mode 100644 index 0000000000..ea7f548d6a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8914.yml @@ -0,0 +1,13 @@ +author: "Ghommie (original PRs by Mickyan, Anturk, ShizCalev, nemvar and Naksu)" +delete-after: True +changes: + - rscadd: "After rigorous mandatory art training for the crew, many new graffiti styles are now available" + - bugfix: "Cleaned up some crayon and spraycan code for futureproofing." + - bugfix: "Spraypainting blast doors no longer makes them see-through." + - balance: "Paint remover now works on blast doors and the like." + - rscadd: "Most objects can now be colored using a spray can." + - spellcheck: "Added visible message to spraying objects and windows." + - rscadd: "Colored lights now shine in different colours." + - rscdel: "Removed individual buttons text in crayon/spraycan UI, speeding it up." + - bugfix: "Text mode buffer is actually visible in the UI." + - tweak: "Last letter of a text mode buffer no longer rotates out to be replaced with \"a\", allowing the text mode to be used for individual symbols." diff --git a/html/changelogs/AutoChangeLog-pr-8932.yml b/html/changelogs/AutoChangeLog-pr-8932.yml new file mode 100644 index 0000000000..d9038ef1a5 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8932.yml @@ -0,0 +1,7 @@ +author: "EmeraldSundisk" +delete-after: True +changes: + - rscadd: "Adds a gun range to Box Station" + - rscadd: "Provides some extra power grid connections" + - rscdel: "Sunglasses and Earmuffs removed from the Warden's Office - they can be found at the range instead" + - tweak: "Rearranges a few objects within the prison as to accommodate the new gun range" diff --git a/html/changelogs/AutoChangeLog-pr-8937.yml b/html/changelogs/AutoChangeLog-pr-8937.yml new file mode 100644 index 0000000000..20302e4c49 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8937.yml @@ -0,0 +1,5 @@ +author: "Ghommie" +delete-after: True +changes: + - rscadd: "sort of overhauled darkmode/lightmode to /vg/station's, also reincluding the pre-existing black'n'white theme." + - bugfix: "Fixed LOOC color, fixed .userlove and .love span classes being a bit too blurry on dark mode." diff --git a/html/changelogs/AutoChangeLog-pr-9032.yml b/html/changelogs/AutoChangeLog-pr-9032.yml new file mode 100644 index 0000000000..53adfa0920 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9032.yml @@ -0,0 +1,7 @@ +author: "Ghommie (original PRs by subject217, AarontheIdiot, pireamaineach, Gousaid67 and SouDescolado)" +delete-after: True +changes: + - balance: "Removed plasmamen species speedmod in favor of a slowdown for envirosuits." + - rscadd: "Nanotrasen has began deploying departementalized enviro plasmasuits to the station! plasmamens can now benefit from some of the bonuses aswell as the color pattern of their job, while allowing others to easily determine their profession!" + - bugfix: "Benevolent Nanotrasen makes gulag available for everyone! (Plasmamen retain their equipment and don't die.)" + - rscdel: "Removes code that theoretically limits plasmamen from being clowns and mimes, but actually doesn't." diff --git a/html/changelogs/AutoChangeLog-pr-9057.yml b/html/changelogs/AutoChangeLog-pr-9057.yml new file mode 100644 index 0000000000..4df1b23ca7 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9057.yml @@ -0,0 +1,4 @@ +author: "EgoSumStultus" +delete-after: True +changes: + - rscadd: "Adds male AI vox." diff --git a/html/changelogs/AutoChangeLog-pr-9077.yml b/html/changelogs/AutoChangeLog-pr-9077.yml new file mode 100644 index 0000000000..2f42e36dd6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9077.yml @@ -0,0 +1,8 @@ +author: "original by actioninja, port by sishen1542" +delete-after: True +changes: + - tweak: "Medical and Security consoles now check access on worn or inhand ID instead of requiring an inserted ID" + - tweak: "mining vendor now reads from ID in hand or on person instead of requiring an inserted ID" + - bugfix: "ORM is functional again (for real this time)" + - tweak: "ORM claim points button transfers points to worn/inhand ID instead of to an inserted ID, no longer accepts insertions" + - tweak: "Same for gulag consoles" diff --git a/html/changelogs/AutoChangeLog-pr-9086.yml b/html/changelogs/AutoChangeLog-pr-9086.yml new file mode 100644 index 0000000000..4dea682d47 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9086.yml @@ -0,0 +1,4 @@ +author: "original by TheChosenEvilOne, port by sishen1542" +delete-after: True +changes: + - rscadd: "Ported dynamic mode from /vg/, originally made by @DeityLink, @Kurfursten and @ShiftyRail" diff --git a/html/changelogs/AutoChangeLog-pr-9089.yml b/html/changelogs/AutoChangeLog-pr-9089.yml new file mode 100644 index 0000000000..dc1e351f25 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9089.yml @@ -0,0 +1,6 @@ +author: "original by Tlaltecuhtli, port by sishen1542" +delete-after: True +changes: + - rscadd: "rcd disk that gives rcd computer frame and machine frame designs" + - rscadd: "upgraded rcd in CE lockers" + - tweak: "moved deconstruction to the upgrade disk, ashwalker rcd comes upgraded" diff --git a/html/changelogs/AutoChangeLog-pr-9099.yml b/html/changelogs/AutoChangeLog-pr-9099.yml new file mode 100644 index 0000000000..d3caa658f2 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9099.yml @@ -0,0 +1,6 @@ +author: "Fermis" +delete-after: True +changes: + - tweak: "tweaked Neurotoxin" + - balance: "added more depth to Neurotoxin" + - bugfix: "fixed the inability to create Neurotoxin" diff --git a/html/changelogs/AutoChangeLog-pr-9109.yml b/html/changelogs/AutoChangeLog-pr-9109.yml new file mode 100644 index 0000000000..137ca7f325 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9109.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "amazing things like tea of catnip, catnip and plant" diff --git a/html/changelogs/AutoChangeLog-pr-9122.yml b/html/changelogs/AutoChangeLog-pr-9122.yml new file mode 100644 index 0000000000..1d3ff248fb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9122.yml @@ -0,0 +1,6 @@ +author: "Sishen1542" +delete-after: True +changes: + - rscadd: "fun" + - rscdel: "bad stuff" + - balance: "mech bad" diff --git a/html/changelogs/AutoChangeLog-pr-9147.yml b/html/changelogs/AutoChangeLog-pr-9147.yml new file mode 100644 index 0000000000..7f1dcb7e7e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9147.yml @@ -0,0 +1,5 @@ +author: "Code-Cygnet" +delete-after: True +changes: + - rscadd: "Added new things - Mind trait, alcohol reagent, chemical reagent, drink sprite and recipe." + - imageadd: "added commander_and_chief sprite to drinks.dmi" diff --git a/html/changelogs/AutoChangeLog-pr-9170.yml b/html/changelogs/AutoChangeLog-pr-9170.yml new file mode 100644 index 0000000000..566d427605 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9170.yml @@ -0,0 +1,5 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "tons of peach themed items" + - spellcheck: "caje" diff --git a/html/changelogs/AutoChangeLog-pr-9189.yml b/html/changelogs/AutoChangeLog-pr-9189.yml new file mode 100644 index 0000000000..cf9b0c70a3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9189.yml @@ -0,0 +1,5 @@ +author: "Sishen1542" +delete-after: True +changes: + - rscadd: "made laser minigun not shitcode and also craftable" + - soundadd: "added new fire sounds for the laser minigun" diff --git a/html/changelogs/AutoChangeLog-pr-9191.yml b/html/changelogs/AutoChangeLog-pr-9191.yml new file mode 100644 index 0000000000..242e268017 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9191.yml @@ -0,0 +1,6 @@ +author: "Linzolle" +delete-after: True +changes: + - rscadd: "can now carry people on your back by aggressive grabbing them while they are laying down and then dragging their sprite onto yours." + - tweak: "dragging people who are prone is now much slower, and carrying them will allow you to move faster at the cost of taking 5 seconds to lift them up onto your back." + - tweak: "pacifists can now aggressive grab (cannot table slam people though)" diff --git a/html/changelogs/AutoChangeLog-pr-9199.yml b/html/changelogs/AutoChangeLog-pr-9199.yml new file mode 100644 index 0000000000..bf0219c7d9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9199.yml @@ -0,0 +1,7 @@ +author: "Tupinambis" +delete-after: True +changes: + - balance: "added a small fire delay (3 ticks) to automatic shotguns" + - balance: "Reduced buckshot brute damage by 20%. (12.5 -> 10 brute per pellet) (75 -> 60 brute at close range)" + - balance: "Reduced rubbershot stamina damage by 40% (25 -> 15 stamina per pellet) (150 -> 90 stamina at close range)" + - balance: "Reduced beanbag stamina damage by 12.5% (80 -> 70 stamina per shot)" diff --git a/html/changelogs/AutoChangeLog-pr-9203.yml b/html/changelogs/AutoChangeLog-pr-9203.yml new file mode 100644 index 0000000000..0ad87d26c1 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9203.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - imageadd: "redid brass tools to look better*" diff --git a/html/changelogs/AutoChangeLog-pr-9209.yml b/html/changelogs/AutoChangeLog-pr-9209.yml new file mode 100644 index 0000000000..5ed81d6856 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9209.yml @@ -0,0 +1,4 @@ +author: "original by redmoogle, port by sishen1542" +delete-after: True +changes: + - rscadd: "Added support for 3 new gasses; Tritium, Pluoxium, and BZ" diff --git a/html/changelogs/AutoChangeLog-pr-9212.yml b/html/changelogs/AutoChangeLog-pr-9212.yml new file mode 100644 index 0000000000..48e8b8b4d3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9212.yml @@ -0,0 +1,5 @@ +author: "dapnee" +delete-after: True +changes: + - rscadd: "Plasmaglass tables, spears, tiny plasmaglass shards" + - bugfix: "Plasmaglass structures now drop plasmaglass shards instead of nothing" diff --git a/html/changelogs/AutoChangeLog-pr-9221.yml b/html/changelogs/AutoChangeLog-pr-9221.yml new file mode 100644 index 0000000000..0256c4a7c9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9221.yml @@ -0,0 +1,9 @@ +author: "Sishen1542" +delete-after: True +changes: + - rscadd: "holy lasrifle, hypertool, divine lightblade, and blessed baseball bat. Four new holy weapons!" + - balance: "stamina and force tweaks for most holy weapons." + - tweak: "prayer beads no longer deconvert after a 10 second timer, now just inject holy water for delayed effect" + - tweak: "cultist runes are now destroyed with a bible or a null rod" + - tweak: "bo-staff now functions as a nerfed sleepy carp staff" + - bugfix: "fixed kevinz code to add in non-null rod child items as holy weapons" diff --git a/html/changelogs/AutoChangeLog-pr-9226.yml b/html/changelogs/AutoChangeLog-pr-9226.yml new file mode 100644 index 0000000000..17bafbf36b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9226.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - code_imp: "removes some cit-modular things" diff --git a/html/changelogs/AutoChangeLog-pr-9227.yml b/html/changelogs/AutoChangeLog-pr-9227.yml new file mode 100644 index 0000000000..9f6bfabdc4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9227.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - bugfix: "Missing sprites with bad ones" diff --git a/html/changelogs/AutoChangeLog-pr-9229.yml b/html/changelogs/AutoChangeLog-pr-9229.yml new file mode 100644 index 0000000000..5c617f6ab0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9229.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "emag charge amount" diff --git a/html/changelogs/AutoChangeLog-pr-9235.yml b/html/changelogs/AutoChangeLog-pr-9235.yml new file mode 100644 index 0000000000..112f34a7f0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9235.yml @@ -0,0 +1,5 @@ +author: "Yakumo Chen" +delete-after: True +changes: + - rscdel: "Removes autoscan" + - balance: "Scanning people now requires someone to operate the cloning computer regardless of part level." diff --git a/html/changelogs/AutoChangeLog-pr-9238.yml b/html/changelogs/AutoChangeLog-pr-9238.yml new file mode 100644 index 0000000000..b2eb8db233 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9238.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "Surgerys and spays in bags" diff --git a/html/changelogs/AutoChangeLog-pr-9239.yml b/html/changelogs/AutoChangeLog-pr-9239.yml new file mode 100644 index 0000000000..781dcd82b6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9239.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "earthbloods stam regen" diff --git a/html/changelogs/AutoChangeLog-pr-9241.yml b/html/changelogs/AutoChangeLog-pr-9241.yml new file mode 100644 index 0000000000..1cb08cd8d0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9241.yml @@ -0,0 +1,6 @@ +author: "Alonefromhell" +delete-after: True +changes: + - rscadd: "Ported Oracle UI, a framework for self-updating and neat UI's" + - refactor: "Paper now uses OUI" + - refactor: "Bins now use OUI" diff --git a/html/changelogs/AutoChangeLog-pr-9242.yml b/html/changelogs/AutoChangeLog-pr-9242.yml new file mode 100644 index 0000000000..1e47385e67 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9242.yml @@ -0,0 +1,4 @@ +author: "kappa-sama" +delete-after: True +changes: + - tweak: "blood cult ritual daggers fit in jack/combat boots" diff --git a/html/changelogs/AutoChangeLog-pr-9247.yml b/html/changelogs/AutoChangeLog-pr-9247.yml new file mode 100644 index 0000000000..4f6e0b0875 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9247.yml @@ -0,0 +1,4 @@ +author: "Fermi" +delete-after: True +changes: + - bugfix: "Fixes tiny runaway decimals in reagents system." diff --git a/html/changelogs/AutoChangeLog-pr-9248.yml b/html/changelogs/AutoChangeLog-pr-9248.yml new file mode 100644 index 0000000000..3e6eff727c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9248.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - imageadd: "Red to Blue/Black crosses as questioned by Bhijn" diff --git a/html/changelogs/AutoChangeLog-pr-9249.yml b/html/changelogs/AutoChangeLog-pr-9249.yml new file mode 100644 index 0000000000..b2ef0c2d46 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9249.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - bugfix: "clean bot not cleaning basic cleanables" diff --git a/html/changelogs/AutoChangeLog-pr-9251.yml b/html/changelogs/AutoChangeLog-pr-9251.yml new file mode 100644 index 0000000000..c40c2e05fc --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9251.yml @@ -0,0 +1,5 @@ +author: "Linzolle" +delete-after: True +changes: + - tweak: "all chaplain suits can hold the same items in suit storage" + - code_imp: "improvement to organisation for chaplain suits" diff --git a/html/changelogs/AutoChangeLog-pr-9256.yml b/html/changelogs/AutoChangeLog-pr-9256.yml new file mode 100644 index 0000000000..dcf039a2a0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9256.yml @@ -0,0 +1,4 @@ +author: "Fermi" +delete-after: True +changes: + - bugfix: "SDGF: Fixes infinite clones." diff --git a/html/changelogs/AutoChangeLog-pr-9260.yml b/html/changelogs/AutoChangeLog-pr-9260.yml new file mode 100644 index 0000000000..f94d9772f9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9260.yml @@ -0,0 +1,4 @@ +author: "dapnee" +delete-after: True +changes: + - rscadd: "Trim lines!" diff --git a/html/changelogs/AutoChangeLog-pr-9263.yml b/html/changelogs/AutoChangeLog-pr-9263.yml new file mode 100644 index 0000000000..36a03fa125 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9263.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "new traitor bundle" diff --git a/html/changelogs/AutoChangeLog-pr-9267.yml b/html/changelogs/AutoChangeLog-pr-9267.yml new file mode 100644 index 0000000000..a894c0d468 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9267.yml @@ -0,0 +1,4 @@ +author: "Tupinambis" +delete-after: True +changes: + - balance: "Engivend RCDs now vend upgraded RCDs" diff --git a/html/changelogs/AutoChangeLog-pr-9269.yml b/html/changelogs/AutoChangeLog-pr-9269.yml new file mode 100644 index 0000000000..693588208e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9269.yml @@ -0,0 +1,5 @@ +author: "Fermis" +delete-after: True +changes: + - tweak: "re-enables femichem explosions in grenades." + - tweak: "adds nuance to the SDGF and hatmium explosions." diff --git a/html/changelogs/AutoChangeLog-pr-9275.yml b/html/changelogs/AutoChangeLog-pr-9275.yml new file mode 100644 index 0000000000..cd74273366 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9275.yml @@ -0,0 +1,5 @@ +author: "Fermis" +delete-after: True +changes: + - bugfix: "Fixes analyse function on ChemMasters to correctly display purity." + - bugfix: "Fixes the custom transfer for buffer to beaker button." diff --git a/html/changelogs/AutoChangeLog-pr-9276.yml b/html/changelogs/AutoChangeLog-pr-9276.yml new file mode 100644 index 0000000000..f778c26c13 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9276.yml @@ -0,0 +1,6 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "trash" + - imageadd: "eye bleed +:add: misstakes" diff --git a/html/changelogs/AutoChangeLog-pr-9277.yml b/html/changelogs/AutoChangeLog-pr-9277.yml new file mode 100644 index 0000000000..4bf92cd32e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9277.yml @@ -0,0 +1,11 @@ +author: "Poojawa" +delete-after: True +changes: + - rscadd: "Added new wings to Insects and separated fluff from old ones, they're Insect's new body markings now without being per-limb (for now)." + - rscadd: "Horns are now available to mammals, and they have their own color." + - rscadd: "Legs are no longer a binary hack code, but actually something that can be changed. Framework for tauric adaptations." + - rscdel: "Purged Modular Citadel's sprite_accessories." + - bugfix: "improved the quality of a number of sprites." + - tweak: "Moths are now all insects. Avians and Aquatics are all anthromorphics. Just as planned." + - rscadd: "Anthromorphs can choose their preferred gibbing meat. I guess. Snowflakes are weird." + - bugfix: "Additional Gentlemen names." diff --git a/html/changelogs/AutoChangeLog-pr-9278.yml b/html/changelogs/AutoChangeLog-pr-9278.yml new file mode 100644 index 0000000000..3ec1eb839a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9278.yml @@ -0,0 +1,4 @@ +author: "deathride58" +delete-after: True +changes: + - bugfix: "Spamming forged packets no longer crashes the server." diff --git a/html/changelogs/AutoChangeLog-pr-9279.yml b/html/changelogs/AutoChangeLog-pr-9279.yml new file mode 100644 index 0000000000..572f40e643 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9279.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Fixed undergarments color preferences resetting each round." diff --git a/html/changelogs/AutoChangeLog-pr-9282.yml b/html/changelogs/AutoChangeLog-pr-9282.yml new file mode 100644 index 0000000000..5a27defb41 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9282.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Fixed a few dozen suits' body coverage inconsistencies. These changes shouldn't affect armor and utility vests for most." diff --git a/html/changelogs/AutoChangeLog-pr-9283.yml b/html/changelogs/AutoChangeLog-pr-9283.yml new file mode 100644 index 0000000000..33e02f8539 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9283.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscdel: "Removed old things!" diff --git a/html/changelogs/AutoChangeLog-pr-9287.yml b/html/changelogs/AutoChangeLog-pr-9287.yml new file mode 100644 index 0000000000..3bf2844b41 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9287.yml @@ -0,0 +1,4 @@ +author: "Fermis" +delete-after: True +changes: + - rscadd: "Debug option: Generate Wikichems" diff --git a/html/changelogs/AutoChangeLog-pr-9288.yml b/html/changelogs/AutoChangeLog-pr-9288.yml new file mode 100644 index 0000000000..8a684431bf --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9288.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - bugfix: "UI memes" diff --git a/html/changelogs/AutoChangeLog-pr-9289.yml b/html/changelogs/AutoChangeLog-pr-9289.yml new file mode 100644 index 0000000000..7abf947bfc --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9289.yml @@ -0,0 +1,4 @@ +author: "Sishen1542" +delete-after: True +changes: + - bugfix: "fixing chems for strained muscles" diff --git a/html/changelogs/AutoChangeLog-pr-9292.yml b/html/changelogs/AutoChangeLog-pr-9292.yml new file mode 100644 index 0000000000..33168f9c43 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9292.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Fixed clown shoes and work boots." diff --git a/html/changelogs/AutoChangeLog-pr-9293.yml b/html/changelogs/AutoChangeLog-pr-9293.yml new file mode 100644 index 0000000000..a99bf1ab41 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9293.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Fixed some overlay bug that happens when legcuffed and then handcuffed." diff --git a/html/changelogs/AutoChangeLog-pr-9295.yml b/html/changelogs/AutoChangeLog-pr-9295.yml new file mode 100644 index 0000000000..a96540c11b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9295.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - balance: "Slowed down police baton and tele baton speed by 75%, should be still be faster than the legacy speed (2 seconds) by 0.6 seconds. Telescopic batons' stamina cost per swing is now on par with police batons, ergo more expensive." diff --git a/html/changelogs/AutoChangeLog-pr-9296.yml b/html/changelogs/AutoChangeLog-pr-9296.yml new file mode 100644 index 0000000000..101ec1aaa8 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9296.yml @@ -0,0 +1,9 @@ +author: "EmeraldSundisk" +delete-after: True +changes: + - rscadd: "Increases the number of plots to 9 (from 5)" + - rscadd: "Additional lighting placed directly outside the garden" + - tweak: "Cleans up the area to reflect use. Moves the seed extractor to a more central location" + - tweak: "Relocates the seed packs on botany's counter to the garden +removal: Removes wooden barricades outside the garden" + - config: "Renames \"Abandoned Garden\" area designation to \"Maintenance Garden\", but does not replace the icon in Dream Maker" diff --git a/html/changelogs/AutoChangeLog-pr-9298.yml b/html/changelogs/AutoChangeLog-pr-9298.yml new file mode 100644 index 0000000000..8301acbbd6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9298.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Fixed undershirts n socks colors prefs." diff --git a/html/changelogs/AutoChangeLog-pr-9302.yml b/html/changelogs/AutoChangeLog-pr-9302.yml new file mode 100644 index 0000000000..4f6ed4d336 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9302.yml @@ -0,0 +1,4 @@ +author: "deathride58" +delete-after: True +changes: + - bugfix: "Things that access job_preferences now explicitly access keys, which means it no longer attempts to access invalid indices and runtimes as a result." diff --git a/html/changelogs/AutoChangeLog-pr-9304.yml b/html/changelogs/AutoChangeLog-pr-9304.yml new file mode 100644 index 0000000000..f4a919c52f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9304.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "Blue space blood bag" diff --git a/html/changelogs/AutoChangeLog-pr-9306.yml b/html/changelogs/AutoChangeLog-pr-9306.yml new file mode 100644 index 0000000000..0a560c8090 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9306.yml @@ -0,0 +1,5 @@ +author: "kappa-sama" +delete-after: True +changes: + - tweak: "voidcells can now unlock alien tech" + - balance: "rebalanced tech trees (not really)" diff --git a/html/changelogs/AutoChangeLog-pr-9307.yml b/html/changelogs/AutoChangeLog-pr-9307.yml new file mode 100644 index 0000000000..de787a4274 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9307.yml @@ -0,0 +1,4 @@ +author: "BurgerLUA" +delete-after: True +changes: + - balance: "Rebalanced special jetpacks." diff --git a/html/changelogs/AutoChangeLog-pr-9309.yml b/html/changelogs/AutoChangeLog-pr-9309.yml new file mode 100644 index 0000000000..b3aef0372b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9309.yml @@ -0,0 +1,6 @@ +author: "Fermis" +delete-after: True +changes: + - rscadd: "graft synthtissue surgery, new reagent synthtissue" + - tweak: "neurine fixes brain objs +merge: combines fermichem's lung damage with tg's" diff --git a/html/changelogs/AutoChangeLog-pr-9310.yml b/html/changelogs/AutoChangeLog-pr-9310.yml new file mode 100644 index 0000000000..34ded5a0db --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9310.yml @@ -0,0 +1,4 @@ +author: "GrayRachnid" +delete-after: True +changes: + - rscadd: "Added windoors to all the flaps on delta." diff --git a/html/changelogs/AutoChangeLog-pr-9312.yml b/html/changelogs/AutoChangeLog-pr-9312.yml new file mode 100644 index 0000000000..487b79393b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9312.yml @@ -0,0 +1,9 @@ +author: "XDTM" +delete-after: True +changes: + - rscadd: "Added the experimental dissection surgery, which can be performed once per corpse to gain techweb points." + - rscadd: "Rarer specimens are more valuable, so xenos and rare species are more efficient subjects." + - rscadd: "Added two new surgery procedures, under the Experimental Surgery techweb node." + - rscadd: "Ligament Hook makes it so you can attach limbs manually (like skeletons) but makes dismemberment more likely as well." + - rscadd: "Ligament Reinforcement prevents dismemberment, but makes limbs easier to disable through damage." + - tweak: "Golem limbs can now be disabled, although they are still undismemberable." diff --git a/html/changelogs/AutoChangeLog-pr-9314.yml b/html/changelogs/AutoChangeLog-pr-9314.yml new file mode 100644 index 0000000000..317fb48592 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9314.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "blood costs" diff --git a/html/changelogs/AutoChangeLog-pr-9316.yml b/html/changelogs/AutoChangeLog-pr-9316.yml new file mode 100644 index 0000000000..f4074bcee8 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9316.yml @@ -0,0 +1,5 @@ +author: "Fermis" +delete-after: True +changes: + - rscadd: "on_mob_dead(), bitflags and CHECK_MULTIPLE_BITFIELDS" + - refactor: "refactored fermichem vars, moved impure chems into their own reagents subtype" diff --git a/html/changelogs/AutoChangeLog-pr-9317.yml b/html/changelogs/AutoChangeLog-pr-9317.yml new file mode 100644 index 0000000000..11d58e79ce --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9317.yml @@ -0,0 +1,7 @@ +author: "Ghommie (original PR by CrazyClown12)" +delete-after: True +changes: + - tweak: "The chloral hydrate inside of the sleepy pen is no longer slower acting than chloral hydrate made in chemistry." + - tweak: "The chloral hydrate inside of cookies synthesised by emagged borgs is no longer slower acting than chloral hydrate made in chemistry." + - balance: "Slight tirizene buff." + - rscdel: "Delayed chloral hydrate" diff --git a/html/changelogs/AutoChangeLog-pr-9318.yml b/html/changelogs/AutoChangeLog-pr-9318.yml new file mode 100644 index 0000000000..5f7f4af654 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9318.yml @@ -0,0 +1,4 @@ +author: "Fermis" +delete-after: True +changes: + - bugfix: "Fixes small residues of chems that won't go away!" diff --git a/html/changelogs/AutoChangeLog-pr-9320.yml b/html/changelogs/AutoChangeLog-pr-9320.yml new file mode 100644 index 0000000000..605de17ab2 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9320.yml @@ -0,0 +1,5 @@ +author: "Ghommie (original PR by harmonyn)" +delete-after: True +changes: + - balance: "Resisting out of bucklecuffs takes more/less time depending on the handcuffs you used, i.e., fake handcuffs will not bucklecuff someone for ages." + - tweak: "fake handcuffs shouldn't no longer demoralize restrained criminals scums." diff --git a/html/changelogs/AutoChangeLog-pr-9321.yml b/html/changelogs/AutoChangeLog-pr-9321.yml new file mode 100644 index 0000000000..d0c4f6d18d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9321.yml @@ -0,0 +1,6 @@ +author: "Linzolle" +delete-after: True +changes: + - bugfix: "blood halberd not going back to 17 force after unwielding" + - spellcheck: "unnecessary 's at the end of blood rites healing" + - rscadd: "QoL to blood rites, examine the ritual aura to view how many blood charges are left" diff --git a/html/changelogs/AutoChangeLog-pr-9322.yml b/html/changelogs/AutoChangeLog-pr-9322.yml new file mode 100644 index 0000000000..2d2632bb81 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9322.yml @@ -0,0 +1,4 @@ +author: "Sishen1542" +delete-after: True +changes: + - bugfix: "narsie no longer asks for consent" diff --git a/html/changelogs/AutoChangeLog-pr-9326.yml b/html/changelogs/AutoChangeLog-pr-9326.yml new file mode 100644 index 0000000000..582230dedb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9326.yml @@ -0,0 +1,4 @@ +author: "YakumoChen" +delete-after: True +changes: + - tweak: "AEGs brought more in line with current radiation system. Try not to get EMP'd." diff --git a/html/changelogs/AutoChangeLog-pr-9331.yml b/html/changelogs/AutoChangeLog-pr-9331.yml new file mode 100644 index 0000000000..0ecd5dfad7 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9331.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "You can now alt-click to rotate machinery such as the tachyon-droppler array or emitters again." diff --git a/html/changelogs/AutoChangeLog-pr-9332.yml b/html/changelogs/AutoChangeLog-pr-9332.yml new file mode 100644 index 0000000000..e8a2aa1faa --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9332.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Sofas can't be wielded and transformed back into plain chairs anymore." diff --git a/html/changelogs/AutoChangeLog-pr-9334.yml b/html/changelogs/AutoChangeLog-pr-9334.yml new file mode 100644 index 0000000000..cc31ec8896 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9334.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - rscadd: "Enables emojis for PDA messages." diff --git a/html/changelogs/AutoChangeLog-pr-9335.yml b/html/changelogs/AutoChangeLog-pr-9335.yml new file mode 100644 index 0000000000..b36c0957dd --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9335.yml @@ -0,0 +1,6 @@ +author: "Raptorizer" +delete-after: True +changes: + - tweak: "tweaked numbers/variables" + - balance: "rebalanced numbers/variables" + - spellcheck: "removed unneeded code" diff --git a/html/changelogs/AutoChangeLog-pr-9336.yml b/html/changelogs/AutoChangeLog-pr-9336.yml new file mode 100644 index 0000000000..3f86da234e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9336.yml @@ -0,0 +1,8 @@ +author: "Fermis" +delete-after: True +changes: + - tweak: "tweaked beaker health and allows use of syringes/droppers on chem_heaters" + - soundadd: "added a sound for when beakers take temperature damage." + - imageadd: "added some icons for melting beakers" + - refactor: "refactored how beakers take damage" + - bugfix: "fixes how beakers would only take one instance of damage on pH damage" diff --git a/html/changelogs/AutoChangeLog-pr-9338.yml b/html/changelogs/AutoChangeLog-pr-9338.yml new file mode 100644 index 0000000000..e5909f52ae --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9338.yml @@ -0,0 +1,4 @@ +author: "lolman360" +delete-after: True +changes: + - bugfix: "Fixes an undocumented change to the naming of Plasmamen." diff --git a/html/changelogs/AutoChangeLog-pr-9341.yml b/html/changelogs/AutoChangeLog-pr-9341.yml new file mode 100644 index 0000000000..e017f2941e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9341.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - tweak: "Blood cult altar, forge and archives now use radial menus." diff --git a/html/changelogs/AutoChangeLog-pr-9342.yml b/html/changelogs/AutoChangeLog-pr-9342.yml new file mode 100644 index 0000000000..75d441f143 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9342.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Fixed some machineries' UIs." diff --git a/html/changelogs/AutoChangeLog-pr-9344.yml b/html/changelogs/AutoChangeLog-pr-9344.yml new file mode 100644 index 0000000000..c45c9269b6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9344.yml @@ -0,0 +1,6 @@ +author: "Ghommie (original PRs by Time-Green and Qustinnus)" +delete-after: True +changes: + - tweak: "loot crates can't explode after unlocking anymore" + - bugfix: "jumping into loot crates no longers causes them to go boom" + - bugfix: "You can now deconstruct abandoned crates with a welder without making them go boom. After unlocking them, of course." diff --git a/html/changelogs/AutoChangeLog-pr-9348.yml b/html/changelogs/AutoChangeLog-pr-9348.yml new file mode 100644 index 0000000000..a58556b992 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9348.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - tweak: "blood and clock cultists messages from metabolizing" diff --git a/html/changelogs/AutoChangeLog-pr-9349.yml b/html/changelogs/AutoChangeLog-pr-9349.yml new file mode 100644 index 0000000000..e51b826f01 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9349.yml @@ -0,0 +1,5 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Fixed advanced medical scanners borg upgrades." + - bugfix: "Fixes certain borg upgrades being unapplicable on dogborg counterparts of the target cyborg type." diff --git a/html/changelogs/AutoChangeLog-pr-9352.yml b/html/changelogs/AutoChangeLog-pr-9352.yml new file mode 100644 index 0000000000..e34277b531 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9352.yml @@ -0,0 +1,4 @@ +author: "JTGSZ" +delete-after: True +changes: + - tweak: "Gave Qualifies_for_Rank Check back its checks in job controller." diff --git a/html/changelogs/AutoChangeLog-pr-9356.yml b/html/changelogs/AutoChangeLog-pr-9356.yml new file mode 100644 index 0000000000..65aacda077 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9356.yml @@ -0,0 +1,9 @@ +author: "EmeraldSundisk" +delete-after: True +changes: + - rscadd: "Expands the chapel mass driver room to make it easier to use" + - rscadd: "Rearranges the chapel backroom so there are now six coffins and burial garments roundstart" + - tweak: "Cleans up the Janitor's office" + - tweak: "Readjusts the station exterior so mass-driven coffins (hopefully) have less friction" + - bugfix: "Adds a fan to the chapel driver" + - bugfix: "The Janitor missed a few spots around the newly renovated Maintenance Garden" diff --git a/html/changelogs/AutoChangeLog-pr-9359.yml b/html/changelogs/AutoChangeLog-pr-9359.yml new file mode 100644 index 0000000000..1eb933e4c5 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9359.yml @@ -0,0 +1,6 @@ +author: "Bhijn" +delete-after: True +changes: + - balance: "The point production mode of radiation collectors has been reverted to the original behavior of using all of the stored power every process cycle instead of just 4% of it" + - tweak: "Radiation collectors now display the amount of power/research points they're producing per minute rather than per process cycle, which should hopefully clear up a lot of confusion." + - tweak: "Radiation collectors also display what's happening to the gas within them, which should make it a lot more obvious as to how you get tritium." diff --git a/html/changelogs/AutoChangeLog-pr-9365.yml b/html/changelogs/AutoChangeLog-pr-9365.yml new file mode 100644 index 0000000000..1a0543f28e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9365.yml @@ -0,0 +1,7 @@ +author: "Ghommie (original PR by 81Denton, kriskog and nemvar)" +delete-after: True +changes: + - spellcheck: "Sleepers now show a message if players try to unscrew the maintenance hatch while they're occupied or open. Fixed typos in sleeper/organ harvester messages." + - tweak: "Sleepers and dna scanners can now be pried open with crowbars." + - tweak: "You can open and close sleepers and dna scanners by alt-clicking them." + - bugfix: "The scanner's hatch now must be closed (on top of being unoccupied), just like sleepers, before being screwdriverable. This fixes a tricky door stuck issue with the machine." diff --git a/html/changelogs/AutoChangeLog-pr-9367.yml b/html/changelogs/AutoChangeLog-pr-9367.yml new file mode 100644 index 0000000000..f875262781 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9367.yml @@ -0,0 +1,6 @@ +author: "Poojawa" +delete-after: True +changes: + - rscadd: "Medical huds are now calibrated for Radioactive wavelengths." + - balance: "Engineering equipment, blue space, and common storage containers are radiation protected" + - bugfix: "fixed suit storage not cleaning radiation levels of everything stuffed into them." diff --git a/html/changelogs/AutoChangeLog-pr-9373.yml b/html/changelogs/AutoChangeLog-pr-9373.yml new file mode 100644 index 0000000000..0dc7d7a85f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9373.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "Beebal and Honeybalm plants" diff --git a/html/changelogs/AutoChangeLog-pr-9377.yml b/html/changelogs/AutoChangeLog-pr-9377.yml new file mode 100644 index 0000000000..26587eb6c6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9377.yml @@ -0,0 +1,4 @@ +author: "Fermis" +delete-after: True +changes: + - bugfix: "fixes Janitor grenades." diff --git a/html/changelogs/AutoChangeLog-pr-9379.yml b/html/changelogs/AutoChangeLog-pr-9379.yml new file mode 100644 index 0000000000..05301d3f72 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9379.yml @@ -0,0 +1,4 @@ +author: "Chayse" +delete-after: True +changes: + - tweak: "Medbay doors can now be opened by anyone from the inside without having to press the exit button." diff --git a/html/changelogs/AutoChangeLog-pr-9380.yml b/html/changelogs/AutoChangeLog-pr-9380.yml new file mode 100644 index 0000000000..0d42ef7276 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9380.yml @@ -0,0 +1,6 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Fixed people being shovable hrough windows, windoors and the such." + - rscadd: "You can now shove people into disposal bins." + - refactor: "refactored altdisarm(), ergo the \"shoving people around\" proc." diff --git a/html/changelogs/AutoChangeLog-pr-9382.yml b/html/changelogs/AutoChangeLog-pr-9382.yml new file mode 100644 index 0000000000..c82a6a4309 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9382.yml @@ -0,0 +1,4 @@ +author: "Chayse" +delete-after: True +changes: + - bugfix: "Borgs now have the necessary dexterity to unbuckle people from themselves and from bucklable objects." diff --git a/html/changelogs/AutoChangeLog-pr-9384.yml b/html/changelogs/AutoChangeLog-pr-9384.yml new file mode 100644 index 0000000000..a941371f1c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9384.yml @@ -0,0 +1,4 @@ +author: "Chayse" +delete-after: True +changes: + - bugfix: "Fixes the Trek Uniform/Suit worn icons" diff --git a/html/changelogs/AutoChangeLog-pr-9385.yml b/html/changelogs/AutoChangeLog-pr-9385.yml new file mode 100644 index 0000000000..36c0f7c105 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9385.yml @@ -0,0 +1,4 @@ +author: "Sishen1542" +delete-after: True +changes: + - bugfix: "removed reflect from divine lightblade" diff --git a/html/changelogs/AutoChangeLog-pr-9388.yml b/html/changelogs/AutoChangeLog-pr-9388.yml new file mode 100644 index 0000000000..9c42745219 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9388.yml @@ -0,0 +1,4 @@ +author: "original by Skoglol, port by sishen1542" +delete-after: True +changes: + - admin: "Dynamic gamemode now more auto-deadmin friendly." diff --git a/html/changelogs/AutoChangeLog-pr-9389.yml b/html/changelogs/AutoChangeLog-pr-9389.yml new file mode 100644 index 0000000000..4ed6439d5b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9389.yml @@ -0,0 +1,4 @@ +author: "Weblure" +delete-after: True +changes: + - rscadd: "Added the relevant Beepsky animations from TG's aibots.dmi file to Cit's aibots.dmi file." diff --git a/html/changelogs/AutoChangeLog-pr-9391.yml b/html/changelogs/AutoChangeLog-pr-9391.yml new file mode 100644 index 0000000000..1b4525f717 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9391.yml @@ -0,0 +1,4 @@ +author: "GrayRachnid" +delete-after: True +changes: + - balance: "rebalanced k9dogborgs" diff --git a/html/changelogs/AutoChangeLog-pr-9393.yml b/html/changelogs/AutoChangeLog-pr-9393.yml new file mode 100644 index 0000000000..ef03eb483f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9393.yml @@ -0,0 +1,4 @@ +author: "Chayse" +delete-after: True +changes: + - bugfix: "AIs can now once more talk through holopads successfully" diff --git a/html/changelogs/AutoChangeLog-pr-9394.yml b/html/changelogs/AutoChangeLog-pr-9394.yml new file mode 100644 index 0000000000..2c1c89877b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9394.yml @@ -0,0 +1,5 @@ +author: "EmeraldSundisk" +delete-after: True +changes: + - bugfix: "Readjusts positioning of Delta's QM keycard device" + - bugfix: "Cleaned up a few spots I missed in #9356, particularly around the janitor's office" diff --git a/html/changelogs/AutoChangeLog-pr-9401.yml b/html/changelogs/AutoChangeLog-pr-9401.yml new file mode 100644 index 0000000000..ba40c9af65 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9401.yml @@ -0,0 +1,4 @@ +author: "actioninja" +delete-after: True +changes: + - bugfix: "APC UI autoupdates correctly" diff --git a/html/changelogs/AutoChangeLog-pr-9404.yml b/html/changelogs/AutoChangeLog-pr-9404.yml new file mode 100644 index 0000000000..635b2f8aaf --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9404.yml @@ -0,0 +1,4 @@ +author: "Poojawa" +delete-after: True +changes: + - bugfix: "QM is able to hire/fire people to their department now." diff --git a/html/changelogs/AutoChangeLog-pr-9405.yml b/html/changelogs/AutoChangeLog-pr-9405.yml new file mode 100644 index 0000000000..ee4bce83bd --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9405.yml @@ -0,0 +1,5 @@ +author: "Thalpy" +delete-after: True +changes: + - tweak: "Alkaline buffer recipe so people don't get grumpy and expanded the pH range" + - bugfix: "fixes buffers" diff --git a/html/changelogs/AutoChangeLog-pr-9406.yml b/html/changelogs/AutoChangeLog-pr-9406.yml new file mode 100644 index 0000000000..bf4048528d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9406.yml @@ -0,0 +1,6 @@ +author: "Twaticus & Poojawa" +delete-after: True +changes: + - rscadd: "Added new carpets from TauCetiStation!" + - rscadd: "More Liquid carpet chemicals, mix carpet with certain reagents to get different colors!" + - refactor: "Tablets have a better means of being assembled codewise. nothing's different on player end." diff --git a/html/changelogs/AutoChangeLog-pr-9407.yml b/html/changelogs/AutoChangeLog-pr-9407.yml new file mode 100644 index 0000000000..dbfadaebb7 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9407.yml @@ -0,0 +1,4 @@ +author: "AdmiralPancakes1" +delete-after: True +changes: + - rscadd: "Cryo cell shortcuts: alt-click toggles the doors, ctrl-click toggles the power" diff --git a/html/changelogs/AutoChangeLog-pr-9410.yml b/html/changelogs/AutoChangeLog-pr-9410.yml new file mode 100644 index 0000000000..910b9f6f2e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9410.yml @@ -0,0 +1,5 @@ +author: "Poojawa" +delete-after: True +changes: + - bugfix: "cryopod UI isn't complete shit anymore" + - bugfix: "Cryopods purge silicon gear more efficiently now. Same with ghosts." diff --git a/html/changelogs/AutoChangeLog-pr-9413.yml b/html/changelogs/AutoChangeLog-pr-9413.yml new file mode 100644 index 0000000000..b352549d0b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9413.yml @@ -0,0 +1,4 @@ +author: "Poojawa" +delete-after: True +changes: + - balance: "Med Sprays are now more aligned with patch mechanics" diff --git a/html/changelogs/AutoChangeLog-pr-9418.yml b/html/changelogs/AutoChangeLog-pr-9418.yml new file mode 100644 index 0000000000..9950590005 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9418.yml @@ -0,0 +1,19 @@ +author: "Ghommie (original PRs by zeroisthebiggay, AnturK, MrDoomBringer, Cobby, ATHATH, optimumtact, GranpaWalton, Skoglol)" +delete-after: True +changes: + - bugfix: "Blob overminds, sentient diseases, etc. can no longer dump out boxes. Sorry gamers." + - rscadd: "Sentient Disease now has almost all symptoms at its disposal." + - code_imp: "Adding single-symptom disease abilities is super easy now." + - bugfix: "Sentient Disease can now hear (not sure if this was a bug or intentional)." + - rscadd: "Sentient Disease is a linguist and knows all languages. Still cannot speak." + - tweak: "Gives Sentient Diseases a medical hud to observe their victims further with." + - bugfix: "Fixes and moves around some on_stage_change() and Start()-related things for virus symptoms and (sentient) diseases." + - bugfix: "The inorganic biology symptom should work properly now when bought by a sentient disease." + - bugfix: "Oxyloss icon no-longer shows up when someone has the self respiration symptom" + - tweak: "The self respiration now makes you not contract diseases through the air and not breathe in smoke" + - bugfix: "Sentient diseases can no longer pick two cures that react and disappear when eaten." + - balance: "Sentient disease cures are now consistently harder, will only pick cures from tier 6 and up." + - bugfix: "Disease cures should now stay the same for all infected mobs." + - rscadd: "The regenerative coma symptom has a new resistance 4 threshold effect! Said threshold effect will give hosts of the symptom's virus the TRAIT_STABLECRIT trait if its threshold is met." + - bugfix: "An obscure, probably never reported before bug that may or may not exist involving sentient diseases and the self-respiration symptom should be fixed now (if it even existed in the first place)." + - balance: "The cooldown time between each removal or addition of a symptom for sentient diseases has been brought down from an agonizingly long 2 minutes to a more reasonable 1 minute." diff --git a/html/changelogs/AutoChangeLog-pr-9421.yml b/html/changelogs/AutoChangeLog-pr-9421.yml new file mode 100644 index 0000000000..8b2e8a3982 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9421.yml @@ -0,0 +1,6 @@ +author: "Ghommie (original PRs by Akrilla, Arkatos and Denton)" +delete-after: True +changes: + - tweak: "Sprays cans have a cap on how dark you can make objects. Art is uneffected." + - bugfix: "Fixed a bug where you could get cyborg spraycans via pyrite extracts." + - rscadd: "Added an infinite spraycan for admins to spawn." diff --git a/html/changelogs/AutoChangeLog-pr-9427.yml b/html/changelogs/AutoChangeLog-pr-9427.yml new file mode 100644 index 0000000000..bb1665fba5 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9427.yml @@ -0,0 +1,7 @@ +author: "Ghommie && Kevinz000" +delete-after: True +changes: + - balance: "Lichdom and necromantic stone skeletons are now of the spaceproof kind too." + - tweak: "skeletons now also like dairy products." + - rscdel: "Halloween roundstart skeletons and zombies are no more spaceproof." + - rscadd: "You can choose to set your species to zombie or skeleton through the pride mirror yet again, Alas they are not of the spaceproof kind either." diff --git a/html/changelogs/AutoChangeLog-pr-9431.yml b/html/changelogs/AutoChangeLog-pr-9431.yml new file mode 100644 index 0000000000..65f825b278 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9431.yml @@ -0,0 +1,4 @@ +author: "Ghommie (original PR by Denton)" +delete-after: True +changes: + - balance: "You can no longer reveal the 'illegal tech' research node by deconstructing .357 speedloaders, riot dart boxes, syndicate cigarettes, syndicate playing cards or syndicate balloons." diff --git a/html/changelogs/AutoChangeLog-pr-9434.yml b/html/changelogs/AutoChangeLog-pr-9434.yml new file mode 100644 index 0000000000..d6ad25361a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9434.yml @@ -0,0 +1,5 @@ +author: "EmeraldSundisk" +delete-after: True +changes: + - rscadd: "Adds some potted plants around Box Station" + - bugfix: "The tile mentioned in #9409 should now be radiation-free." diff --git a/html/changelogs/AutoChangeLog-pr-9443.yml b/html/changelogs/AutoChangeLog-pr-9443.yml new file mode 100644 index 0000000000..6bc7507ea9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9443.yml @@ -0,0 +1,4 @@ +author: "Thalpy" +delete-after: True +changes: + - bugfix: "fixes broken compiler because a var changed name" diff --git a/html/changelogs/AutoChangeLog-pr-9445.yml b/html/changelogs/AutoChangeLog-pr-9445.yml new file mode 100644 index 0000000000..0d479fa63a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-9445.yml @@ -0,0 +1,4 @@ +author: "Thalpy" +delete-after: True +changes: + - bugfix: "impure travis var anger" diff --git a/html/oracle_ui/content/disposal_bin/index.html b/html/oracle_ui/content/disposal_bin/index.html new file mode 100644 index 0000000000..8f7713b53c --- /dev/null +++ b/html/oracle_ui/content/disposal_bin/index.html @@ -0,0 +1,27 @@ +
    +
    + State: +
    @{full_pressure}
    +
    +
    + Pressure: +
    +
    +
    +
    @{per}
    +
    +
    +
    +
    + Handle: +
    @{flush}
    +
    +
    + Eject: +
    @{contents}
    +
    +
    + Compressor: +
    @{pressure_charging}
    +
    +
    \ No newline at end of file diff --git a/html/oracle_ui/editor_tool.html b/html/oracle_ui/editor_tool.html new file mode 100644 index 0000000000..e0ce75bb29 --- /dev/null +++ b/html/oracle_ui/editor_tool.html @@ -0,0 +1,103 @@ + + + + + + OracleUI IDE + + + +
    +

    Content Template:

    + +
    +
    +

    Data:

    + +
    +
    +

    Output:

    + +
    + + + diff --git a/html/oracle_ui/themes/nano/index.html b/html/oracle_ui/themes/nano/index.html new file mode 100644 index 0000000000..388f6e4ce4 --- /dev/null +++ b/html/oracle_ui/themes/nano/index.html @@ -0,0 +1,19 @@ + + + + + + @{title} + + + + + +
    +
    @{title}
    +
    + @{body} +
    +
    + + diff --git a/html/oracle_ui/themes/nano/sui-nano-common.css b/html/oracle_ui/themes/nano/sui-nano-common.css new file mode 100644 index 0000000000..481b81c3e3 --- /dev/null +++ b/html/oracle_ui/themes/nano/sui-nano-common.css @@ -0,0 +1,353 @@ +body +{ + padding: 0; + margin: 0; + background-color: #272727; + font-size: 12px; + color: #ffffff; + line-height: 170%; + cursor: default; + -moz-user-select: none; + -ms-user-select: none; +} + +hr +{ + background-color: #40628a; + height: 1px; +} + +a, a:link, a:visited, a:active, .linkOn, .linkOff +{ + color: #ffffff; + text-decoration: none; + background: #40628a; + border: 1px solid #161616; + padding: 1px 4px 1px 4px; + margin: 0 2px 0 0; + cursor:default; +} + +a:hover +{ + color: #40628a; + background: #ffffff; +} + +a.white, a.white:link, a.white:visited, a.white:active +{ + color: #40628a; + text-decoration: none; + background: #ffffff; + border: 1px solid #161616; + padding: 1px 4px 1px 4px; + margin: 0 2px 0 0; + cursor:default; +} + +a.white:hover +{ + color: #ffffff; + background: #40628a; +} + +.active, a.active:link, a.active:visited, a.active:active, a.active:hover +{ + color: #ffffff; + background: #2f943c; + border-color: #24722e; +} + +.disabled, a.disabled:link, a.disabled:visited, a.disabled:active, a.disabled:hover +{ + color: #ffffff; + background: #999999; + border-color: #666666; +} + +a.icon, .linkOn.icon, .linkOff.icon +{ + position: relative; + padding: 1px 4px 2px 20px; +} + +a.icon img, .linkOn.icon img +{ + position: absolute; + top: 0; + left: 0; + width: 18px; + height: 18px; +} + +ul +{ + padding: 4px 0 0 10px; + margin: 0; + list-style-type: none; +} + +li +{ + padding: 0 0 2px 0; +} + +img, a img +{ + border-style:none; +} + +h1, h2, h3, h4, h5, h6 +{ + margin: 0; + padding: 16px 0 8px 0; + color: #517087; +} + +h1 +{ + font-size: 15px; +} + +h2 +{ + font-size: 14px; +} + +h3 +{ + font-size: 13px; +} + +h4 +{ + font-size: 12px; +} + +.uiWrapper +{ + + width: 100%; + height: 100%; +} + +.uiTitle +{ + clear: both; + padding: 6px 8px 6px 8px; + border-bottom: 2px solid #161616; + background: #383838; + color: #98B0C3; + font-size: 16px; +} + +.uiTitle.icon +{ + padding: 6px 8px 6px 42px; + background-position: 2px 50%; + background-repeat: no-repeat; +} + +.uiContent +{ + clear: both; + padding: 8px; + font-family: Verdana, Geneva, sans-serif; +} + +.good +{ + color: #00ff00; +} + +.average +{ + color: #d09000; +} + +.bad +{ + color: #ff0000; +} + +.highlight +{ + color: #8BA5C4; +} + +.dark +{ + color: #272727; +} + +.notice +{ + position: relative; + background: #E9C183; + color: #15345A; + font-size: 10px; + font-style: italic; + padding: 2px 4px 0 4px; + margin: 4px; +} + +.notice.icon +{ + padding: 2px 4px 0 20px; +} + +.notice img +{ + position: absolute; + top: 0; + left: 0; + width: 16px; + height: 16px; +} + +div.notice +{ + clear: both; +} + +.statusDisplay +{ + background: #000000; + color: #ffffff; + border: 1px solid #40628a; + padding: 4px; + margin: 3px 0; +} + +.statusLabel +{ + width: 138px; + float: left; + overflow: hidden; + color: #98B0C3; +} + +.statusValue +{ + float: left; +} + +.block +{ + padding: 8px; + margin: 10px 4px 4px 4px; + border: 1px solid #40628a; + background-color: #202020; +} + +.block h3 +{ + padding: 0; +} + +.progressBar +{ + position: relative; + width: 185px; + height: 14px; + border: 1px solid #666666; + float: left; + overflow: hidden; + padding: 1px; +} + +.progressLabel +{ + top: -2px; + height: 100%; + position: absolute; + right: 4px; + text-align: right; +} + +.progressFill +{ + width: 100%; + height: 100%; + background: #40628a; + overflow: hidden; + transition: width 2.2s linear; +} + +.progressFill.good +{ + color: #ffffff; + background: #00ff00; +} + +.progressFill.average +{ + color: #ffffff; + background: #d09000; +} + +.progressFill.bad +{ + color: #ffffff; + background: #ff0000; +} + +.progressFill.highlight +{ + color: #ffffff; + background: #8BA5C4; +} + +.clearBoth +{ + clear: both; +} + +.clearLeft +{ + clear: left; +} + +.clearRight +{ + clear: right; +} + +.line +{ + width: 100%; + clear: both; +} + +section .label, section .content +{ + display: table-cell; + margin: 0; + text-align: left; + vertical-align: middle; + padding: 3px 2px +} + +section .label +{ + width: 1%; + padding-right: 32px; + white-space: nowrap; + color: #8ba5c4; +} + +section +{ + display: table-row; + width: 100% +} + +.display { + width: calc(100% - 8px); + padding: 4px; + background-color: #000; + background-color: rgba(0, 0, 0, .33); + box-shadow: inset 0 0 5px rgba(0, 0, 0, .5); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr=#54000000,endColorStr=#54000000)"; + filter: progid: DXImageTransform.Microsoft.gradient(startColorStr=#54000000, endColorStr=#54000000); +} \ No newline at end of file diff --git a/html/oracle_ui/themes/nano/sui-nano-common.js b/html/oracle_ui/themes/nano/sui-nano-common.js new file mode 100644 index 0000000000..716891a53f --- /dev/null +++ b/html/oracle_ui/themes/nano/sui-nano-common.js @@ -0,0 +1,47 @@ +function replaceContent(body) { + var maincontent = document.getElementById('maincontent'); + if(maincontent) { + maincontent.innerHTML = body; + } +} + +function updateProgressLabels() { + var progressBars = document.getElementsByClassName("progressBar"); + for(var i = 0; i < progressBars.length; i++) { + var progressBar = progressBars[i]; + if(!progressBar) + continue; + var progressFill = progressBar.getElementsByClassName("progressFill")[0]; + if(!progressFill) + continue; + var width = parseInt(getComputedStyle(progressFill).width); + var maxWidth = parseInt(getComputedStyle(progressBar).width); + var progressLabel = progressBar.getElementsByClassName("progressLabel")[0]; + if(progressLabel) + progressLabel.innerHTML = Math.round((width / maxWidth) * 100) + '%'; + } +} + +if(getComputedStyle) { setInterval(updateProgressLabels, 50); } //Fallback + +function updateFields(json) { + var fields = JSON.parse(json); + for (var key in fields) { + let value = fields[key]; + var element = document.getElementById(key); + if(element == null) { + continue; + } else if(element.classList.contains('progressBar')) { + var progressFill = element.getElementsByClassName("progressFill")[0]; + if(progressFill) + progressFill.style["width"] = value; + if(!getComputedStyle) { //Fallback + var progressLabel = element.getElementsByClassName("progressLabel")[0]; + if(progressLabel) + progressLabel.innerHTML = value; + } + } else { + element.innerHTML = value; + } + } +} \ No newline at end of file diff --git a/html/oracle_ui/themes/nano/sui-nano-jquery.min.js b/html/oracle_ui/themes/nano/sui-nano-jquery.min.js new file mode 100644 index 0000000000..645c5adc18 --- /dev/null +++ b/html/oracle_ui/themes/nano/sui-nano-jquery.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="
    ",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h; + if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
    a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/
","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:k.htmlSerialize?[0,"",""]:[1,"X
","
"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?""!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("