diff --git a/.gitignore b/.gitignore index b05b7141493b..ce296843c01a 100644 --- a/.gitignore +++ b/.gitignore @@ -236,3 +236,4 @@ define_sanity_output.txt #This file contains developer-specific config overrides. These shouldn't be committed. config/_config_nogit.txt +config/dbconfig.txt diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index c62ad2065e42..923f68d288dc 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -23884,6 +23884,13 @@ pixel_y = 3 }, /obj/item/storage/box/syringes, +/obj/machinery/reagentgrinder{ + desc = "Used to grind things up into raw materials and liquids."; + pixel_y = 5 + }, +/obj/item/stack/sheet/mineral/plasma{ + amount = 5 + }, /turf/open/floor/iron, /area/station/medical/virology) "fLY" = ( @@ -33174,7 +33181,9 @@ dir = 8 }, /obj/effect/turf_decal/bot, -/obj/structure/closet/l3closet/virology, +/obj/machinery/computer/records/pathology{ + dir = 4 + }, /turf/open/floor/iron, /area/station/medical/storage) "ibk" = ( @@ -33488,6 +33497,10 @@ pixel_y = -7 }, /obj/machinery/power/apc/auto_name/directional/south, +/obj/item/clothing/glasses/science, +/obj/item/clothing/glasses/science, +/obj/item/clothing/glasses/science, +/obj/item/clothing/glasses/science, /turf/open/floor/iron/checker, /area/station/service/janitor) "idu" = ( @@ -39201,7 +39214,7 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /obj/effect/turf_decal/siding/green, /obj/structure/cable, -/obj/structure/filingcabinet/chestdrawer, +/obj/machinery/computer/records/pathology, /turf/open/floor/iron, /area/station/medical/virology) "jyV" = ( @@ -40729,13 +40742,13 @@ /turf/open/floor/iron, /area/station/medical/pharmacy) "jQq" = ( -/obj/structure/closet/l3closet/virology, /obj/effect/turf_decal/bot, /obj/effect/turf_decal/tile/neutral/half/contrasted, /obj/effect/turf_decal/siding/green, /obj/item/radio/intercom/directional/north, /obj/machinery/light/cold/directional/north, /obj/structure/cable, +/obj/machinery/disease2/diseaseanalyser, /turf/open/floor/iron, /area/station/medical/virology) "jQw" = ( @@ -47772,10 +47785,10 @@ "lBH" = ( /obj/structure/reagent_dispensers/wall/virusfood/directional/east, /obj/structure/cable, -/obj/machinery/smartfridge/chemistry/virology/preloaded, /obj/effect/turf_decal/stripes/line{ dir = 1 }, +/obj/machinery/computer/diseasesplicer, /turf/open/floor/iron, /area/station/medical/virology) "lBR" = ( @@ -51609,6 +51622,19 @@ /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe/antiviral, /obj/item/reagent_containers/syringe/antiviral, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/storage/box/monkeycubes/mousecubes, +/obj/item/storage/box/monkeycubes/mousecubes, +/obj/item/storage/box/monkeycubes/mousecubes, /turf/open/floor/iron/white, /area/station/medical/virology) "mzu" = ( @@ -59907,7 +59933,7 @@ name = "Virology Access Button"; pixel_x = -24; pixel_y = -2; - req_access = list("virology") + req_access = list("pathology") }, /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/line{ @@ -61999,6 +62025,7 @@ /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/trimline/green/filled/line, /obj/structure/cable, +/obj/structure/closet/l3closet/virology, /turf/open/floor/iron/white, /area/station/medical/virology) "pfh" = ( @@ -62752,7 +62779,7 @@ idSelf = "virology_airlock_control"; name = "Virology Access Button"; pixel_y = 22; - req_access = list("virology") + req_access = list("pathology") }, /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -68204,7 +68231,7 @@ name = "Virology Access Console"; pixel_x = 24; pixel_y = -24; - req_access = list("virology") + req_access = list("pathology") }, /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -72678,6 +72705,11 @@ /obj/item/clothing/gloves/latex, /obj/item/clothing/glasses/science, /obj/item/clothing/glasses/hud/health, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, /turf/open/floor/iron, /area/station/medical/virology) "rJJ" = ( @@ -76875,7 +76907,6 @@ /turf/open/floor/iron, /area/station/ai_monitored/command/storage/eva) "sKQ" = ( -/obj/machinery/computer/pandemic, /obj/effect/turf_decal/bot, /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -76883,6 +76914,7 @@ /obj/machinery/status_display/ai/directional/south, /obj/machinery/light/cold/directional/south, /obj/structure/cable, +/obj/machinery/disease2/incubator, /turf/open/floor/iron, /area/station/medical/virology) "sKR" = ( @@ -84163,17 +84195,10 @@ /turf/open/floor/iron/dark, /area/station/engineering/main) "uxI" = ( -/obj/structure/table/glass, /obj/effect/turf_decal/stripes/line{ dir = 5 }, -/obj/machinery/reagentgrinder{ - desc = "Used to grind things up into raw materials and liquids."; - pixel_y = 5 - }, -/obj/item/stack/sheet/mineral/plasma{ - amount = 5 - }, +/obj/machinery/disease2/centrifuge, /turf/open/floor/iron, /area/station/medical/virology) "uxY" = ( diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index f32a676e2931..0132818d1107 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -2788,7 +2788,7 @@ idSelf = "virology_airlock_control"; name = "Virology Access Button"; pixel_y = -24; - req_access = list("virology") + req_access = list("pathology") }, /obj/machinery/door/firedoor, /obj/structure/disposalpipe/segment{ @@ -2814,7 +2814,7 @@ /obj/effect/turf_decal/trimline/green/filled/end{ dir = 1 }, -/obj/structure/tank_holder/extinguisher, +/obj/machinery/computer/records/pathology, /turf/open/floor/iron/white, /area/station/medical/medbay/aft) "aWg" = ( @@ -4377,7 +4377,7 @@ name = "Virology Access Button"; pixel_x = -24; pixel_y = 5; - req_access = list("virology") + req_access = list("pathology") }, /obj/machinery/door/firedoor, /obj/structure/disposalpipe/segment, @@ -8198,12 +8198,9 @@ /turf/open/floor/plating, /area/station/ai_monitored/turret_protected/aisat_interior) "cAM" = ( -/obj/structure/table, -/obj/item/folder/white{ - pixel_y = 4 - }, /obj/machinery/newscaster/directional/north, /obj/effect/turf_decal/tile/green/full, +/obj/machinery/computer/diseasesplicer, /turf/open/floor/iron/dark/smooth_large, /area/station/medical/virology) "cAR" = ( @@ -29820,7 +29817,7 @@ name = "Virology Access Console"; pixel_x = 8; pixel_y = 25; - req_access = list("virology") + req_access = list("pathology") }, /obj/structure/closet/secure_closet/medical1, /turf/open/floor/iron/white, @@ -36304,15 +36301,12 @@ }, /area/station/command/heads_quarters/rd) "lGj" = ( -/obj/structure/table/glass, -/obj/machinery/reagentgrinder{ - pixel_y = 8 - }, /obj/effect/turf_decal/trimline/green/filled/line{ dir = 6 }, /obj/structure/extinguisher_cabinet/directional/south, /obj/machinery/light/directional/south, +/obj/machinery/disease2/centrifuge, /turf/open/floor/iron/white, /area/station/medical/virology) "lGp" = ( @@ -36519,6 +36513,9 @@ department = "Janitorial"; name = "Janitorial Requests Console" }, +/obj/item/clothing/glasses/science, +/obj/item/clothing/glasses/science, +/obj/item/clothing/glasses/science, /turf/open/floor/iron, /area/station/service/janitor) "lKZ" = ( @@ -43992,7 +43989,7 @@ /obj/effect/turf_decal/trimline/green/filled/line{ dir = 4 }, -/obj/machinery/computer/pandemic, +/obj/machinery/disease2/incubator, /turf/open/floor/iron/white, /area/station/medical/virology) "oeW" = ( @@ -45541,6 +45538,9 @@ pixel_y = 2 }, /obj/effect/turf_decal/tile/green/full, +/obj/item/storage/box/monkeycubes/mousecubes, +/obj/item/storage/box/monkeycubes/mousecubes, +/obj/item/storage/box/monkeycubes/mousecubes, /turf/open/floor/iron/dark/smooth_large, /area/station/medical/virology) "oCv" = ( @@ -49337,6 +49337,10 @@ /obj/item/clothing/mask/breath/medical, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/emergency_oxygen, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, /turf/open/floor/iron/white, /area/station/medical/virology) "pNB" = ( @@ -55158,6 +55162,7 @@ "rHz" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/turf_decal/trimline/blue/filled/line, +/obj/structure/tank_holder/extinguisher, /turf/open/floor/iron/white, /area/station/medical/medbay/aft) "rHH" = ( @@ -57013,6 +57018,9 @@ pixel_y = 2 }, /obj/effect/turf_decal/trimline/green/filled/corner, +/obj/machinery/reagentgrinder{ + pixel_y = 8 + }, /turf/open/floor/iron/white, /area/station/medical/virology) "soF" = ( @@ -59351,6 +59359,9 @@ /obj/machinery/airalarm/directional/east, /obj/machinery/firealarm/directional/south, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply, +/obj/machinery/computer/records/pathology{ + dir = 1 + }, /turf/open/floor/iron/dark, /area/station/medical/virology) "tcO" = ( @@ -66179,6 +66190,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply, /obj/machinery/requests_console/auto_name/directional/south, +/obj/machinery/disease2/diseaseanalyser, /turf/open/floor/iron/dark, /area/station/medical/virology) "vqg" = ( diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm index 6936e6c65cae..29bc1bdabe5c 100644 --- a/_maps/map_files/KiloStation/KiloStation.dmm +++ b/_maps/map_files/KiloStation/KiloStation.dmm @@ -11374,7 +11374,7 @@ id = "virologysurgery"; name = "Virology Privacy Toggle"; pixel_y = 6; - req_access = list("virology") + req_access = list("pathology") }, /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 8 @@ -48898,7 +48898,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/door/window/left/directional/east{ name = "Monkey Pen"; - req_access = list("virology") + req_access = list("pathology") }, /obj/effect/turf_decal/delivery, /turf/open/floor/iron/dark, diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index f4842840b5f4..3ff5e686574f 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -2779,6 +2779,9 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 1 }, +/obj/item/storage/box/monkeycubes/mousecubes, +/obj/item/storage/box/monkeycubes/mousecubes, +/obj/item/storage/box/monkeycubes/mousecubes, /turf/open/floor/iron/white, /area/station/medical/virology) "aWH" = ( @@ -5444,7 +5447,7 @@ name = "Virology Access Button"; pixel_x = 8; pixel_y = -24; - req_access = list("virology") + req_access = list("pathology") }, /obj/effect/mapping_helpers/airlock/cyclelink_helper{ dir = 8 @@ -21245,7 +21248,7 @@ idSelf = "virology_airlock_control"; name = "Virology Access Button"; pixel_y = -24; - req_access = list("virology") + req_access = list("pathology") }, /obj/effect/mapping_helpers/airlock/cyclelink_helper{ dir = 4 @@ -23081,7 +23084,7 @@ name = "Virology Access Console"; pixel_x = 24; pixel_y = -24; - req_access = list("virology") + req_access = list("pathology") }, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -27900,6 +27903,9 @@ pixel_y = -2 }, /obj/item/radio/intercom/directional/north, +/obj/item/clothing/glasses/science, +/obj/item/clothing/glasses/science, +/obj/item/clothing/glasses/science, /turf/open/floor/iron, /area/station/service/janitor) "kbU" = ( @@ -28108,9 +28114,9 @@ /turf/open/floor/iron, /area/station/security/office) "kgz" = ( -/obj/machinery/smartfridge/chemistry/virology/preloaded, /obj/machinery/firealarm/directional/south, /obj/effect/turf_decal/tile/green/half/contrasted, +/obj/machinery/computer/diseasesplicer, /turf/open/floor/iron/white, /area/station/medical/virology) "kgC" = ( @@ -28255,6 +28261,10 @@ pixel_x = -8 }, /obj/effect/turf_decal/tile/green/half/contrasted, +/obj/item/clothing/glasses/science, +/obj/item/clothing/glasses/science, +/obj/item/healthanalyzer, +/obj/item/clothing/gloves/latex, /turf/open/floor/iron/white, /area/station/medical/virology) "kjZ" = ( @@ -28295,6 +28305,20 @@ /obj/effect/turf_decal/tile/green/anticorner/contrasted{ dir = 8 }, +/obj/item/device/antibody_scanner, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, +/obj/item/clothing/gloves/latex, +/obj/item/clothing/glasses/hud/health, /turf/open/floor/iron/white, /area/station/medical/virology) "kkU" = ( @@ -30256,11 +30280,11 @@ /turf/open/floor/iron, /area/station/hallway/primary/aft) "kVg" = ( -/obj/machinery/computer/pandemic, /obj/structure/cable, /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 8 }, +/obj/machinery/disease2/incubator, /turf/open/floor/iron/white, /area/station/medical/virology) "kVs" = ( @@ -50134,6 +50158,13 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark, /area/station/service/chapel/funeral) +"rTO" = ( +/obj/effect/turf_decal/tile/green/half/contrasted{ + dir = 8 + }, +/obj/machinery/disease2/centrifuge, +/turf/open/floor/iron/white, +/area/station/medical/virology) "rTQ" = ( /obj/effect/turf_decal/tile/bar, /obj/effect/turf_decal/tile/neutral{ @@ -53843,14 +53874,12 @@ /turf/open/floor/wood/parquet, /area/station/medical/psychology) "tgD" = ( -/obj/structure/table/glass, -/obj/item/clothing/gloves/latex, -/obj/item/healthanalyzer, -/obj/item/clothing/glasses/hud/health, -/obj/item/clothing/glasses/science, /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 8 }, +/obj/machinery/computer/records/pathology{ + dir = 4 + }, /turf/open/floor/iron/white, /area/station/medical/virology) "thc" = ( @@ -54237,6 +54266,9 @@ /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 1 }, +/obj/machinery/computer/records/pathology{ + dir = 2 + }, /turf/open/floor/iron/white, /area/station/medical/storage) "tnP" = ( @@ -63722,6 +63754,7 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 4 }, +/obj/machinery/disease2/diseaseanalyser, /turf/open/floor/iron/white, /area/station/medical/virology) "wJv" = ( @@ -82214,7 +82247,7 @@ aaa aaa xjH vdJ -wpi +rTO kVg tgD kku diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm index c1575d8acef6..1cad9f2a2c9b 100644 --- a/_maps/map_files/NorthStar/north_star.dmm +++ b/_maps/map_files/NorthStar/north_star.dmm @@ -16590,7 +16590,7 @@ idSelf = "virology_airlock_control"; name = "Virology Access Button"; pixel_y = -24; - req_access = list("virology") + req_access = list("pathology") }, /obj/effect/turf_decal/trimline/green/filled/line{ dir = 6 @@ -46151,7 +46151,7 @@ name = "Virology Access Button"; pixel_x = -26; pixel_y = 26; - req_access = list("virology") + req_access = list("pathology") }, /obj/effect/turf_decal/trimline/green/filled/line{ dir = 1 @@ -50857,7 +50857,7 @@ /obj/structure/table/reinforced, /obj/machinery/door/window/brigdoor/left/directional/north{ name = "Virology Desk"; - req_access = list("virology") + req_access = list("pathology") }, /obj/machinery/door/window/brigdoor/left/directional/south{ name = "Virology Desk" @@ -79913,7 +79913,7 @@ idSelf = "virology_airlock_control"; name = "Virology Access Button"; pixel_y = 24; - req_access = list("virology") + req_access = list("pathology") }, /obj/effect/turf_decal/trimline/green/filled/arrow_cw{ dir = 9 @@ -87835,7 +87835,7 @@ idSelf = "virology_airlock_control"; name = "Virology Access Console"; pixel_x = 24; - req_access = list("virology") + req_access = list("pathology") }, /obj/effect/turf_decal/tile/green/opposingcorners, /turf/open/floor/iron/white, diff --git a/_maps/map_files/Oshan/oshan.dmm b/_maps/map_files/Oshan/oshan.dmm index 02cebf441a1b..04a56f450eb4 100644 --- a/_maps/map_files/Oshan/oshan.dmm +++ b/_maps/map_files/Oshan/oshan.dmm @@ -439,11 +439,8 @@ /turf/open/floor/iron/dark/textured, /area/station/engineering/main) "ahL" = ( -/obj/effect/landmark/start/virologist, -/obj/structure/chair/sofa/left{ - dir = 8 - }, -/turf/open/floor/carpet/neon/simple/cyan/nodots, +/obj/structure/table/glass, +/turf/open/floor/iron/white, /area/station/medical/virology) "ahN" = ( /obj/machinery/power/apc/auto_name/directional/west, @@ -7631,9 +7628,6 @@ luminosity = 2 }, /area/station/ai_monitored/turret_protected/ai) -"dFf" = ( -/turf/open/floor/carpet/neon/simple/cyan/nodots, -/area/station/medical/virology) "dFg" = ( /obj/machinery/holopad{ pixel_x = 17 @@ -10963,6 +10957,15 @@ }, /turf/open/floor/iron, /area/station/cargo/miningoffice) +"ffB" = ( +/obj/effect/turf_decal/trimline/blue/filled/line{ + dir = 8 + }, +/obj/machinery/computer/records/pathology{ + dir = 2 + }, +/turf/open/floor/iron/white/textured, +/area/station/medical/office) "ffD" = ( /obj/structure/fans/tiny/forcefield{ dir = 8 @@ -12023,6 +12026,15 @@ /obj/item/clothing/glasses/hud/health, /obj/item/clothing/glasses/science, /obj/item/book/manual/wiki/infections, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/storage/box/monkeycubes/mousecubes, +/obj/item/storage/box/monkeycubes/mousecubes, +/obj/item/storage/box/monkeycubes/mousecubes, /turf/open/floor/iron/white, /area/station/medical/virology) "fHr" = ( @@ -12683,10 +12695,8 @@ /turf/open/floor/iron/grimy, /area/station/hallway/secondary/service) "fXc" = ( -/obj/structure/chair/sofa/right{ - dir = 8 - }, -/turf/open/floor/carpet/neon/simple/cyan/nodots, +/obj/machinery/disease2/centrifuge, +/turf/open/floor/iron/white, /area/station/medical/virology) "fXj" = ( /obj/effect/turf_decal/stripes, @@ -15363,6 +15373,9 @@ /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/spray/cleaner, /obj/structure/table/glass, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, /turf/open/floor/iron/white, /area/station/medical/virology) "hmD" = ( @@ -15637,8 +15650,8 @@ }, /area/station/engineering/main) "hva" = ( -/obj/machinery/computer/pandemic, /obj/machinery/light/floor/has_bulb, +/obj/machinery/disease2/incubator, /turf/open/floor/iron/white, /area/station/medical/virology) "hvh" = ( @@ -16100,10 +16113,8 @@ /turf/open/floor/iron/dark/textured, /area/station/hallway/primary/central/fore) "hFF" = ( -/obj/structure/chair/sofa/middle{ - dir = 8 - }, -/turf/open/floor/carpet/neon/simple/cyan/nodots, +/obj/machinery/disease2/diseaseanalyser, +/turf/open/floor/iron/white, /area/station/medical/virology) "hFV" = ( /obj/machinery/ai_slipper{ @@ -18060,6 +18071,9 @@ "iyC" = ( /obj/structure/cable, /obj/machinery/power/apc/auto_name/directional/south, +/obj/machinery/computer/records/pathology{ + dir = 1 + }, /turf/open/floor/iron/white, /area/station/medical/virology) "iyR" = ( @@ -24814,10 +24828,6 @@ /obj/machinery/disposal/bin, /turf/open/floor/iron/dark, /area/station/service/chapel/office) -"lEJ" = ( -/obj/machinery/light/floor/has_bulb, -/turf/open/floor/carpet/neon/simple/cyan/nodots, -/area/station/medical/virology) "lFa" = ( /obj/structure/closet/firecloset, /obj/effect/turf_decal/trimline/dark_blue/filled/warning{ @@ -25414,6 +25424,7 @@ /area/station/maintenance/department/bridge) "lSZ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/landmark/start/virologist, /turf/open/floor/iron/white, /area/station/medical/virology) "lTb" = ( @@ -33437,7 +33448,7 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 8 }, -/turf/open/floor/carpet/neon/simple/cyan/nodots, +/turf/open/floor/iron/white, /area/station/medical/virology) "pFK" = ( /obj/effect/turf_decal/tile/green/fourcorners, @@ -37306,6 +37317,8 @@ pixel_y = 12 }, /obj/structure/extinguisher_cabinet/directional/west, +/obj/item/clothing/glasses/science, +/obj/item/clothing/glasses/science, /turf/open/floor/iron/dark, /area/station/service/janitor) "rqI" = ( @@ -45249,7 +45262,7 @@ /turf/open/floor/plastic, /area/station/hallway/primary/central) "vdb" = ( -/obj/machinery/smartfridge/chemistry/virology/preloaded, +/obj/machinery/computer/diseasesplicer, /turf/open/floor/iron/white, /area/station/medical/virology) "vde" = ( @@ -80209,7 +80222,7 @@ iNI nhC sQl nvL -kJN +ffB kJN rhg boX @@ -105385,9 +105398,9 @@ ajB bfx eUj ipi -dFf +dXQ pFD -lEJ +ipi jPF bfx wdc diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index 414bf3cd4a7f..53acbb404fcf 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -5780,6 +5780,7 @@ dir = 9 }, /obj/structure/cable, +/obj/structure/closet/l3closet/virology, /turf/open/floor/iron/white, /area/station/medical/virology) "aNb" = ( @@ -5788,6 +5789,7 @@ }, /obj/machinery/power/apc/auto_name/directional/north, /obj/structure/cable, +/obj/structure/closet/secure_closet/medical1, /turf/open/floor/iron/white, /area/station/medical/virology) "aNd" = ( @@ -6306,14 +6308,42 @@ /area/station/hallway/primary/tram/center) "aQH" = ( /obj/effect/turf_decal/trimline/green/filled/line, -/obj/structure/closet/l3closet/virology, /obj/machinery/newscaster/directional/south, +/obj/structure/table/glass, +/obj/item/storage/box/monkeycubes/mousecubes, +/obj/item/storage/box/monkeycubes/mousecubes, +/obj/item/storage/box/monkeycubes/mousecubes, +/obj/item/storage/box/monkeycubes/mousecubes, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, +/obj/item/device/antibody_scanner, +/obj/item/reagent_containers/spray/cleaner, +/obj/item/reagent_containers/dropper, /turf/open/floor/iron/white, /area/station/medical/virology) "aQI" = ( /obj/effect/turf_decal/trimline/green/filled/line, -/obj/structure/closet/secure_closet/medical1, /obj/item/radio/intercom/directional/south, +/obj/structure/table/glass, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/reagent_containers/cup/beaker/vial, +/obj/item/clothing/glasses/science, +/obj/item/clothing/glasses/science, +/obj/item/clothing/glasses/hud/health, +/obj/item/reagent_containers/syringe/antiviral, +/obj/item/reagent_containers/syringe/antiviral, +/obj/item/reagent_containers/syringe/antiviral, +/obj/item/book/manual/wiki/infections{ + pixel_y = 7 + }, /turf/open/floor/iron/white, /area/station/medical/virology) "aQL" = ( @@ -8048,7 +8078,7 @@ /obj/effect/turf_decal/trimline/green/filled/line{ dir = 4 }, -/obj/machinery/smartfridge/chemistry/virology/preloaded, +/obj/machinery/computer/diseasesplicer, /turf/open/floor/iron/white, /area/station/medical/virology) "bvk" = ( @@ -17607,15 +17637,9 @@ /obj/effect/turf_decal/trimline/green/filled/line{ dir = 8 }, -/obj/structure/table/glass, -/obj/item/book/manual/wiki/infections{ - pixel_y = 7 - }, -/obj/item/reagent_containers/dropper, -/obj/item/reagent_containers/spray/cleaner, -/obj/item/reagent_containers/syringe/antiviral, /obj/machinery/light/directional/west, /obj/machinery/status_display/ai/directional/west, +/obj/machinery/disease2/diseaseanalyser, /turf/open/floor/iron/white, /area/station/medical/virology) "ewo" = ( @@ -18500,7 +18524,7 @@ /obj/effect/turf_decal/trimline/green/filled/line{ dir = 4 }, -/obj/machinery/computer/pandemic, +/obj/machinery/disease2/centrifuge, /turf/open/floor/iron/white, /area/station/medical/virology) "eNP" = ( @@ -20414,7 +20438,7 @@ idSelf = "virology_airlock_control"; name = "Virology Access Button"; pixel_y = 24; - req_access = list("virology") + req_access = list("pathology") }, /obj/effect/mapping_helpers/airlock/access/all/medical/virology, /obj/structure/cable, @@ -23802,7 +23826,7 @@ name = "Virology Access Console"; pixel_x = -24; pixel_y = 24; - req_access = list("virology") + req_access = list("pathology") }, /obj/structure/cable, /obj/structure/disposalpipe/segment{ @@ -28549,14 +28573,8 @@ /obj/effect/turf_decal/trimline/green/filled/line{ dir = 4 }, -/obj/structure/table/glass, /obj/structure/reagent_dispensers/wall/virusfood/directional/east, -/obj/machinery/reagentgrinder{ - pixel_y = 8 - }, -/obj/item/stack/sheet/mineral/plasma{ - amount = 3 - }, +/obj/machinery/disease2/incubator, /turf/open/floor/iron/white, /area/station/medical/virology) "iig" = ( @@ -32678,7 +32696,7 @@ idSelf = "virology_airlock_control"; name = "Virology Access Button"; pixel_y = -24; - req_access = list("virology") + req_access = list("pathology") }, /obj/effect/mapping_helpers/airlock/access/all/medical/virology, /obj/structure/cable, @@ -40597,6 +40615,9 @@ }, /obj/machinery/airalarm/directional/north, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, +/obj/machinery/computer/records/pathology{ + dir = 2 + }, /turf/open/floor/iron/dark, /area/station/medical/virology) "lXH" = ( @@ -43228,6 +43249,9 @@ /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 9 }, +/obj/machinery/computer/records/pathology{ + dir = 2 + }, /turf/open/floor/iron/white, /area/station/medical/storage) "mPA" = ( @@ -48386,6 +48410,9 @@ /obj/structure/window/reinforced/spawner, /obj/structure/window/reinforced/spawner/directional/west, /obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/item/clothing/glasses/science, +/obj/item/clothing/glasses/science, +/obj/item/clothing/glasses/science, /turf/open/floor/iron/dark, /area/station/service/janitor) "oAU" = ( @@ -55724,6 +55751,12 @@ }, /obj/item/storage/box/syringes, /obj/structure/sign/clock/directional/north, +/obj/machinery/reagentgrinder{ + pixel_y = 8 + }, +/obj/item/stack/sheet/mineral/plasma{ + amount = 3 + }, /turf/open/floor/iron/white, /area/station/medical/virology) "qSS" = ( @@ -56902,10 +56935,6 @@ /obj/effect/turf_decal/trimline/green/filled/line{ dir = 8 }, -/obj/structure/table/glass, -/obj/item/healthanalyzer, -/obj/item/clothing/glasses/science, -/obj/item/clothing/glasses/hud/health, /turf/open/floor/iron/white, /area/station/medical/virology) "rko" = ( diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm index 8a658f3913d7..e6de590eac53 100644 --- a/code/__DEFINES/MC.dm +++ b/code/__DEFINES/MC.dm @@ -91,6 +91,7 @@ /datum/controller/subsystem/##X/New(){\ NEW_SS_GLOBAL(SS##X);\ PreInit();\ + ss_id=#X;\ }\ /datum/controller/subsystem/##X @@ -114,6 +115,7 @@ /datum/controller/subsystem/processing/##X/New(){\ NEW_SS_GLOBAL(SS##X);\ PreInit();\ + ss_id="processing_[#X]";\ }\ /datum/controller/subsystem/processing/##X/fire() {..() /*just so it shows up on the profiler*/} \ /datum/controller/subsystem/processing/##X diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm index 8fa9d18d7bf9..f06cd7be07e6 100644 --- a/code/__DEFINES/access.dm +++ b/code/__DEFINES/access.dm @@ -84,7 +84,7 @@ /// Allows access to the larger room for Chemistry plumbing machinery setups. #define ACCESS_PLUMBING "plumbing" /// Access to the Virology portion of the medical department, as well as the virology crate. -#define ACCESS_VIROLOGY "virology" +#define ACCESS_VIROLOGY "pathology" /// Access to the Psychologist's office. #define ACCESS_PSYCHOLOGY "psychology" /// Access for the Chief Medical Officer's personal quarters in mapping, as well as some other CMO-related things. diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index f28dc8ec7dc6..25e86ab8f191 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -263,6 +263,9 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define GRENADE_NONCLUMSY_FUMBLE 2 #define GRENADE_NO_FUMBLE 3 +#define BODY_ZONE_EVERYTHING "everything" +#define BODY_ZONE_ARMS "arms" +#define BODY_ZONE_LEGS "legs" #define BODY_ZONE_HEAD "head" #define BODY_ZONE_CHEST "chest" #define BODY_ZONE_L_ARM "l_arm" diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm index d21e6b286ec4..f48157a93bbf 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm @@ -15,6 +15,8 @@ #define COMSIG_ATOM_ON_Z_IMPACT "movable_on_z_impact" ///called on a movable (NOT living) when it starts pulling (atom/movable/pulled, state, force) #define COMSIG_ATOM_START_PULL "movable_start_pull" +///called on a movable when it starts being pulled +#define COMSIG_ATOM_PULLED "movable_start_pulled" /// called on /atom when something attempts to pass through it (atom/movable/source, atom/movable/passing, dir) #define COMSIG_ATOM_TRIED_PASS "atom_tried_pass" #define COMSIG_COMPONENT_PERMIT_PASSAGE (1 << 0) diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm index 39d8be41b99a..17965de3dc7a 100644 --- a/code/__DEFINES/jobs.dm +++ b/code/__DEFINES/jobs.dm @@ -59,7 +59,7 @@ #define JOB_MEDICAL_DOCTOR "Medical Doctor" #define JOB_PARAMEDIC "Paramedic" #define JOB_CHEMIST "Chemist" -#define JOB_VIROLOGIST "Virologist" +#define JOB_VIROLOGIST "Pathologist" //Science #define JOB_SCIENTIST "Scientist" #define JOB_ROBOTICIST "Roboticist" diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 6c16bda522b7..5432a7408527 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -133,6 +133,7 @@ #define INIT_ORDER_DBCORE 95 #define INIT_ORDER_BLACKBOX 94 #define INIT_ORDER_SERVER_MAINT 93 +#define INIT_ORDER_METRICS 91 #define INIT_ORDER_INPUT 85 #define INIT_ORDER_SOUNDS 83 #define INIT_ORDER_INSTRUMENTS 82 @@ -165,6 +166,7 @@ #define INIT_ORDER_LANGUAGE 25 #define INIT_ORDER_MACHINES 20 #define INIT_ORDER_SKILLS 15 +#define INIT_ORDER_PATHOGEN 14 #define INIT_ORDER_TIMER 1 #define INIT_ORDER_DEFAULT 0 #define INIT_ORDER_AIR -1 @@ -215,6 +217,7 @@ #define FIRE_PRIORITY_REAGENTS 26 #define FIRE_PRIORITY_SPACEDRIFT 30 #define FIRE_PRIORITY_HOTSPOT 30 +#define FIRE_PRIORITY_PATHOGEN 31 #define FIRE_PRIORITY_SMOOTHING 35 #define FIRE_PRIORITY_OBJ 40 #define FIRE_PRIORITY_ACID 40 diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index 31e722178279..5311a99714b7 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -161,3 +161,4 @@ #define VV_HK_WEAKREF_RESOLVE "weakref_resolve" #define VV_HK_POSSESS_ITEM "possess_item" +#define VV_HK_VIEW_DISEASE_DATA "disease_data" diff --git a/code/__DEFINES/~monkestation/virology.dm b/code/__DEFINES/~monkestation/virology.dm new file mode 100644 index 000000000000..3985e451fb54 --- /dev/null +++ b/code/__DEFINES/~monkestation/virology.dm @@ -0,0 +1,48 @@ +#define EFFECT_DANGER_HELPFUL "0" +#define EFFECT_DANGER_FLAVOR "1" +#define EFFECT_DANGER_ANNOYING "2" +#define EFFECT_DANGER_HINDRANCE "3" +#define EFFECT_DANGER_HARMFUL "4" +#define EFFECT_DANGER_DEADLY "5" + +#define ANTIGEN_BLOOD "blood" +#define ANTIGEN_COMMON "common" +#define ANTIGEN_RARE "rare" +#define ANTIGEN_ALIEN "alien" + +//blood antigens +#define ANTIGEN_O "O" +#define ANTIGEN_A "A" +#define ANTIGEN_B "B" +#define ANTIGEN_RH "Rh" +//common antigens +#define ANTIGEN_Q "Q" +#define ANTIGEN_U "U" +#define ANTIGEN_V "V" +//rare antigens +#define ANTIGEN_M "M" +#define ANTIGEN_N "N" +#define ANTIGEN_P "P" +//alien antigens +#define ANTIGEN_X "X" +#define ANTIGEN_Y "Y" +#define ANTIGEN_Z "Z" + + +#define DISEASE_BUMP "bump" +#define DISEASE_TOUCH "touch" + +#define DISEASE_NORMAL "normal" +#define DISEASE_TRANSFORMATION "transformation" +#define DISEASE_GONDOLA "gondola" +#define DISEASE_GONDOLA_DIGITAL "digital_gondola" +#define DISEASE_XENO "xeno" +#define DISEASE_CORGI "corgi" +#define DISEASE_SLIME "slime" +#define DISEASE_MORPH "morph" +#define DISEASE_ROBOT "robot" +#define DISEASE_COLD "cold" +#define DISEASE_HEART "heart" +#define DISEASE_TRAUMA "trauma" +#define DISEASE_DECLONING "decloning" +#define DISEASE_ANXIETY "anxiety" diff --git a/code/__HELPERS/logging/attack.dm b/code/__HELPERS/logging/attack.dm index e804c07e9f0f..0b063e31324b 100644 --- a/code/__HELPERS/logging/attack.dm +++ b/code/__HELPERS/logging/attack.dm @@ -32,6 +32,7 @@ var/message = "[what_done] [starget][postfix]" user.log_message(message, LOG_ATTACK, color="red") + add_event_to_buffer(user, target, message, "ATTACK") if(user != target) var/reverse_message = "was [what_done] by [ssource][postfix]" target.log_message(reverse_message, LOG_VICTIM, color="orange", log_globally=FALSE) @@ -67,6 +68,7 @@ if(dealt_bare_wound_bonus) message += " | BWB: [dealt_bare_wound_bonus]" + add_event_to_buffer(victim, data = message, log_key = "WOUND") victim.log_message(message, LOG_ATTACK, color="blue") /// Logging for bombs detonating @@ -81,5 +83,6 @@ GLOB.bombers += bomb_message + add_event_to_buffer(user, data = bomb_message, log_key = "BOMB") if(message_admins) message_admins("[user ? "[ADMIN_LOOKUPFLW(user)] at [ADMIN_VERBOSEJMP(user)] " : ""][details][bomb ? " [bomb.name] at [ADMIN_VERBOSEJMP(bomb)]": ""][additional_details ? " [additional_details]" : ""].") diff --git a/code/__HELPERS/logging/economy.dm b/code/__HELPERS/logging/economy.dm index 8f190cb934c2..4ab22d638093 100644 --- a/code/__HELPERS/logging/economy.dm +++ b/code/__HELPERS/logging/economy.dm @@ -1,3 +1,4 @@ /proc/log_econ(text) + add_event_to_buffer(data = text, log_key = "ECONOMY") if (CONFIG_GET(flag/log_econ)) WRITE_LOG(GLOB.world_econ_log, "MONEY: [text]") diff --git a/code/__HELPERS/logging/talk.dm b/code/__HELPERS/logging/talk.dm index bed13b6c3505..66de66006d52 100644 --- a/code/__HELPERS/logging/talk.dm +++ b/code/__HELPERS/logging/talk.dm @@ -14,6 +14,14 @@ /atom/proc/log_talk(message, message_type, tag = null, log_globally = TRUE, forced_by = null, custom_say_emote = null) var/prefix = tag ? "([tag]) " : "" var/suffix = forced_by ? " FORCED by [forced_by]" : "" + + var/voluntary = TRUE + if(forced_by) + voluntary = FALSE + + if(!(message_type & LOG_OOC) && !(message_type & LOG_ASAY) && !(message_type & LOG_EMOTE)) + add_event_to_buffer(src, data = "[prefix][custom_say_emote ? "*[custom_say_emote]*, " : ""]\"[message]\"[suffix]", log_key = "SAY", voluntary = voluntary) + log_message("[prefix][custom_say_emote ? "*[custom_say_emote]*, " : ""]\"[message]\"[suffix]", message_type, log_globally = log_globally) /// Logging for generic spoken messages diff --git a/code/__HELPERS/~monkestation-helpers/virology.dm b/code/__HELPERS/~monkestation-helpers/virology.dm new file mode 100644 index 000000000000..5a87606202bd --- /dev/null +++ b/code/__HELPERS/~monkestation-helpers/virology.dm @@ -0,0 +1,51 @@ +GLOBAL_LIST_INIT(all_antigens, list( + ANTIGEN_O, + ANTIGEN_A, + ANTIGEN_B, + ANTIGEN_RH, + ANTIGEN_Q, + ANTIGEN_U, + ANTIGEN_V, + ANTIGEN_M, + ANTIGEN_N, + ANTIGEN_P, + ANTIGEN_X, + ANTIGEN_Y, + ANTIGEN_Z, + )) + +GLOBAL_LIST_INIT(blood_antigens, list( + ANTIGEN_O, + ANTIGEN_A, + ANTIGEN_B, + ANTIGEN_RH, +)) + +GLOBAL_LIST_INIT(common_antigens, list( + ANTIGEN_Q, + ANTIGEN_U, + ANTIGEN_V, +)) + +GLOBAL_LIST_INIT(rare_antigens, list( + ANTIGEN_M, + ANTIGEN_N, + ANTIGEN_P, +)) + +GLOBAL_LIST_INIT(alien_antigens, list( + ANTIGEN_X, + ANTIGEN_Y, + ANTIGEN_Z, +)) + +/proc/antigen_family(id) + switch(id) + if (ANTIGEN_BLOOD) + return GLOB.blood_antigens + if (ANTIGEN_COMMON) + return GLOB.common_antigens + if (ANTIGEN_RARE) + return GLOB.rare_antigens + if (ANTIGEN_ALIEN) + return GLOB.alien_antigens diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index 7d0fc92b4446..f8541eb88157 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -315,7 +315,7 @@ GLOBAL_LIST_INIT(TAGGERLOCATIONS, list("Disposals", "CMO Office", "Chemistry", "Research", "RD Office", "Robotics", "HoP Office", "Library", "Chapel", "Theatre", "Bar", "Kitchen", "Hydroponics", "Janitor Closet","Genetics", - "Experimentor Lab", "Ordnance", "Dormitories", "Virology", + "Experimentor Lab", "Ordnance", "Dormitories", "Pathology", "Xenobiology", "Law Office","Detective's Office")) GLOBAL_LIST_INIT(station_prefixes, world.file2list("strings/station_prefixes.txt")) diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index ddeb9368f500..0b0904895e11 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -37,6 +37,8 @@ ///Bitmap of what game states can this subsystem fire at. See [RUNLEVELS_DEFAULT] for more details. var/runlevels = RUNLEVELS_DEFAULT //points of the game at which the SS can fire + ///Subsystem ID. Used for when we need a technical name for the SS used by SSmetrics + var/ss_id = "generic_ss_id" /* * The following variables are managed by the MC and should not be modified directly. @@ -65,7 +67,7 @@ /// Tracks the current execution state of the subsystem. Used to handle subsystems that sleep in fire so the mc doesn't run them again while they are sleeping var/state = SS_IDLE - + /// Tracks how many times a subsystem has ever slept in fire(). var/slept_count = 0 @@ -303,3 +305,19 @@ if (NAMEOF(src, queued_priority)) //editing this breaks things. return FALSE . = ..() + +/** +* Returns the metrics for the subsystem. +* +* This can be overriden on subtypes for variables that could affect tick usage +* Example: ATs on SSair +*/ + +/datum/controller/subsystem/proc/get_metrics() + SHOULD_CALL_PARENT(TRUE) + var/list/out = list() + out["relation_id_SS"] = "[ss_id]-[time_stamp()]-[rand(100, 10000)]" // since we insert custom into its own table we want to add a relational id to fetch from the custom data and the subsystem + out["cost"] = cost + out["tick_usage"] = tick_usage + out["custom"] = list() // Override as needed on child + return out diff --git a/code/controllers/subsystem/disease.dm b/code/controllers/subsystem/disease.dm index bfa6fd66b7d8..b3ed4ac0eda6 100644 --- a/code/controllers/subsystem/disease.dm +++ b/code/controllers/subsystem/disease.dm @@ -14,6 +14,7 @@ SUBSYSTEM_DEF(disease) /datum/controller/subsystem/disease/Initialize() var/list/all_common_diseases = diseases - typesof(/datum/disease/advance) + create_global_diseases() for(var/common_disease_type in all_common_diseases) var/datum/disease/prototype = new common_disease_type() archive_diseases[prototype.GetDiseaseID()] = prototype diff --git a/code/controllers/subsystem/id_access.dm b/code/controllers/subsystem/id_access.dm index 483dcd49bd25..c7c630032c22 100644 --- a/code/controllers/subsystem/id_access.dm +++ b/code/controllers/subsystem/id_access.dm @@ -281,7 +281,7 @@ SUBSYSTEM_DEF(id_access) desc_by_access["[ACCESS_LIBRARY]"] = "Library" desc_by_access["[ACCESS_LAWYER]"] = "Law Office" desc_by_access["[ACCESS_ROBOTICS]"] = "Robotics" - desc_by_access["[ACCESS_VIROLOGY]"] = "Virology" + desc_by_access["[ACCESS_VIROLOGY]"] = "Pathology" desc_by_access["[ACCESS_PSYCHOLOGY]"] = "Psychology" desc_by_access["[ACCESS_CMO]"] = "CMO Office" desc_by_access["[ACCESS_QM]"] = "Quartermaster" diff --git a/code/controllers/subsystem/machines.dm b/code/controllers/subsystem/machines.dm index 2426741df79c..0d132d93d660 100644 --- a/code/controllers/subsystem/machines.dm +++ b/code/controllers/subsystem/machines.dm @@ -28,7 +28,8 @@ SUBSYSTEM_DEF(machines) /// Removes a machine from the machine subsystem; should only be called by the machine itself inside Destroy. /datum/controller/subsystem/machines/proc/unregister_machine(obj/machinery/machine) var/list/existing = machines_by_type[machine.type] - existing -= machine + if(length(existing)) + existing -= machine if(!length(existing)) machines_by_type -= machine.type all_machines -= machine diff --git a/code/controllers/subsystem/processing/fastprocess.dm b/code/controllers/subsystem/processing/fastprocess.dm index ce1617253ed9..743ad7631e5e 100644 --- a/code/controllers/subsystem/processing/fastprocess.dm +++ b/code/controllers/subsystem/processing/fastprocess.dm @@ -8,3 +8,8 @@ PROCESSING_SUBSYSTEM_DEF(actualfastprocess) wait = 0.1 SECONDS priority = FIRE_PRIORITY_TICKER stat_tag = "AFP" + +PROCESSING_SUBSYSTEM_DEF(pathogen_processing) + name = "Pathogen Cloud Processing" + wait = 1 SECONDS + stat_tag = "SP" diff --git a/code/datums/ai_laws/ai_laws.dm b/code/datums/ai_laws/ai_laws.dm index c3ea9fcfadfa..8f5418378538 100644 --- a/code/datums/ai_laws/ai_laws.dm +++ b/code/datums/ai_laws/ai_laws.dm @@ -168,6 +168,7 @@ GLOBAL_VAR(round_default_lawset) add_inherent_law(line) if(!inherent.len) //Failsafe to prevent lawless AIs being created. log_silicon("AI created with empty custom laws, laws set to Asimov. Please check silicon_laws.txt.") + add_event_to_buffer(data = "AI created with empty custom laws, laws set to Asimov. Please check silicon_laws.txt.", log_key = "SILICON") add_inherent_law("You may not injure a human being or, through inaction, allow a human being to come to harm.") add_inherent_law("You must obey orders given to you by human beings, except where such orders would conflict with the First Law.") add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 7b8b1d375909..365a2fd27adf 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -287,6 +287,7 @@ // We have to create our own since we can only show emotes to ourselves and our owner /datum/emote/imaginary_friend/run_emote(mob/user, params, type_override, intentional = FALSE) + add_event_to_buffer(user, data = message, log_key = "EMOTE", voluntary = intentional) user.log_talk(message, LOG_EMOTE) if(!can_run_emote(user, FALSE, intentional)) return FALSE diff --git a/code/datums/components/cleaner.dm b/code/datums/components/cleaner.dm index 77893aa01b7c..f83a7b10c21b 100644 --- a/code/datums/components/cleaner.dm +++ b/code/datums/components/cleaner.dm @@ -125,6 +125,12 @@ user.mind?.adjust_experience(/datum/skill/cleaning, round(cleanable_decal.beauty / CLEAN_SKILL_BEAUTY_ADJUSTMENT)) if(target.wash(cleaning_strength)) user.mind?.adjust_experience(/datum/skill/cleaning, round(CLEAN_SKILL_GENERIC_WASH_XP)) + if(isitem(target)) + var/obj/item/item= target + if(length(item.viruses)) + for(var/datum/disease/advanced/D as anything in item.viruses) + item.remove_disease(D) + on_cleaned_callback?.Invoke(source, target, user) //remove the cleaning overlay diff --git a/code/datums/components/infective.dm b/code/datums/components/infective.dm index 42fe8ae08150..3c9dd8fac3a6 100644 --- a/code/datums/components/infective.dm +++ b/code/datums/components/infective.dm @@ -39,9 +39,11 @@ /datum/component/infective/proc/try_infect_eat(datum/source, mob/living/eater, mob/living/feeder) SIGNAL_HANDLER + /* TODO VIROLOGY: Convert to new diseases for(var/V in diseases) eater.ForceContractDisease(V) try_infect(feeder, BODY_ZONE_L_ARM) + */ /datum/component/infective/proc/try_infect_drink(datum/source, mob/living/drinker, mob/living/feeder) SIGNAL_HANDLER @@ -124,9 +126,13 @@ output_diseases |= diseases -/datum/component/infective/proc/try_infect(mob/living/L, target_zone) - for(var/V in diseases) - L.ContactContractDisease(V, target_zone) +/datum/component/infective/proc/try_infect(mob/living/living, target_zone) + if(length(diseases)) + var/block = living.check_contact_sterility(BODY_ZONE_EVERYTHING) + var/list/contact = filter_disease_by_spread(diseases, required = DISEASE_SPREAD_CONTACT_SKIN) + if(length(contact) && !block) + for(var/datum/disease/advanced/V as anything in contact) + living.try_contact_infect(V, note="(Skin Contact - (Infective Component), coming from [src.parent])") /datum/component/infective/proc/extrapolation(datum/source, mob/user, obj/item/extrapolator/E, scan = TRUE) SIGNAL_HANDLER diff --git a/code/datums/components/rot.dm b/code/datums/components/rot.dm index 3dafdf420d4a..97d60eaacfcb 100644 --- a/code/datums/components/rot.dm +++ b/code/datums/components/rot.dm @@ -147,9 +147,26 @@ return //We're running just under the "worst disease", since we don't want these to be too strong - var/datum/disease/advance/random/rand_disease = new(rand(4 * strength * time_scaling), rand(strength * 5 * time_scaling)) - rand_disease.name = "Unknown" - react_to.ContactContractDisease(rand_disease, target_zone) + var/virus_choice = pick(subtypesof(/datum/disease/advanced)- typesof(/datum/disease/advanced/premade)) + var/list/anti = list( + ANTIGEN_BLOOD = 2, + ANTIGEN_COMMON = 2, + ANTIGEN_RARE = 1, + ANTIGEN_ALIEN = 0, + ) + var/list/bad = list( + EFFECT_DANGER_HELPFUL = 1, + EFFECT_DANGER_FLAVOR = 2, + EFFECT_DANGER_ANNOYING = 2, + EFFECT_DANGER_HINDRANCE = 2, + EFFECT_DANGER_HARMFUL = 2, + EFFECT_DANGER_DEADLY = 0, + ) + var/datum/disease/advanced/disease = new virus_choice + disease.makerandom(list(50,90),list(10,100),anti,bad,src) + + var/note = "Rot Infection Contact [key_name(react_to)]" + react_to.try_contact_infect(disease, note = note) #undef REAGENT_BLOCKER #undef TEMPERATURE_BLOCKER diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm index e7fe90891cf1..d4821cc1bc97 100644 --- a/code/datums/components/uplink.dm +++ b/code/datums/components/uplink.dm @@ -136,6 +136,7 @@ uplink_handler.telecrystals += amt telecrystals.use(amt) log_uplink("[key_name(user)] loaded [amt] telecrystals into [parent]'s uplink") + add_event_to_buffer(user, data = "loaded [amt] telecrystals into [parent]'s uplink", log_key = "UPLINK") /datum/component/uplink/proc/OnAttackBy(datum/source, obj/item/item, mob/user) SIGNAL_HANDLER diff --git a/code/datums/diseases/_MobProcs.dm b/code/datums/diseases/_MobProcs.dm index 734f67eb0ceb..666711762c19 100644 --- a/code/datums/diseases/_MobProcs.dm +++ b/code/datums/diseases/_MobProcs.dm @@ -64,7 +64,7 @@ if(ishuman(src)) var/mob/living/carbon/human/infecting_human = src - if(infecting_human.reagents.has_reagent(/datum/reagent/medicine/spaceacillin) && prob(75)) + if(infecting_human.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin) && prob(75)) return switch(target_zone) @@ -97,7 +97,7 @@ /mob/living/proc/AirborneContractDisease(datum/disease/disease, force_spread) if(ishuman(src)) var/mob/living/carbon/human/infecting_human = src - if(infecting_human.reagents.has_reagent(/datum/reagent/medicine/spaceacillin) && prob(75)) + if(infecting_human.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin) && prob(75)) return if(((disease.spread_flags & DISEASE_SPREAD_AIRBORNE) || force_spread) && prob((50*disease.spreading_modifier) - 1)) diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm index 9c164a0ab3d1..7597ce48bce4 100644 --- a/code/datums/diseases/_disease.dm +++ b/code/datums/diseases/_disease.dm @@ -1,8 +1,10 @@ +GLOBAL_LIST_INIT(inspectable_diseases, list()) + /datum/disease //Flags var/visibility_flags = 0 var/disease_flags = CURABLE|CAN_CARRY|CAN_RESIST - var/spread_flags = DISEASE_SPREAD_AIRBORNE | DISEASE_SPREAD_CONTACT_FLUIDS | DISEASE_SPREAD_CONTACT_SKIN + var/spread_flags = 0 //Fluff var/form = "Virus" @@ -14,7 +16,7 @@ //Stages var/stage = 1 - var/max_stages = 0 + var/max_stages = 4 /// The probability of this infection advancing a stage every second the cure is not present. var/stage_prob = 2 @@ -36,12 +38,13 @@ var/infectable_biotypes = MOB_ORGANIC //if the disease can spread on organics, synthetics, or undead var/process_dead = FALSE //if this ticks while the host is dead var/copy_type = null //if this is null, copies will use the type of the instance being copied + var/list/symptoms = list() // The symptoms of the disease. /datum/disease/Destroy() . = ..() if(affected_mob) remove_disease() - SSdisease.active_diseases.Remove(src) + //SSdisease.active_diseases.Remove(src) //add this disease if the host does not already have too many /datum/disease/proc/try_infect(mob/living/infectee, make_copy = TRUE) @@ -51,9 +54,11 @@ //add the disease with no checks /datum/disease/proc/infect(mob/living/infectee, make_copy = TRUE) var/datum/disease/D = make_copy ? Copy() : src + if(!istype(D)) + return LAZYADD(infectee.diseases, D) D.affected_mob = infectee - SSdisease.active_diseases += D //Add it to the active diseases list, now that it's actually in a mob and being processed. + //SSdisease.active_diseases += D //Add it to the active diseases list, now that it's actually in a mob and being processed. D.after_add() infectee.med_hud_set_status() @@ -64,7 +69,7 @@ ///Proc to process the disease and decide on whether to advance, cure or make the sympthoms appear. Returns a boolean on whether to continue acting on the symptoms or not. /datum/disease/proc/stage_act(seconds_per_tick, times_fired) - var/slowdown = affected_mob.reagents.has_reagent(/datum/reagent/medicine/spaceacillin) ? 0.5 : 1 // spaceacillin slows stage speed by 50% + var/slowdown = affected_mob.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin) ? 0.5 : 1 // spaceacillin slows stage speed by 50% if(has_cure()) if(SPT_PROB(cure_chance, seconds_per_tick)) @@ -101,21 +106,18 @@ if(!(spread_flags & DISEASE_SPREAD_AIRBORNE) && !force_spread) return - if(affected_mob.reagents.has_reagent(/datum/reagent/medicine/spaceacillin) || (affected_mob.satiety > 0 && prob(affected_mob.satiety/10))) + if(affected_mob.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin) || (affected_mob.satiety > 0 && prob(affected_mob.satiety/10))) return - var/spread_range = 2 - - if(force_spread) - spread_range = force_spread - + affected_mob.spread_airborne_diseases() + /* var/turf/T = affected_mob.loc if(istype(T)) for(var/mob/living/carbon/C in oview(spread_range, affected_mob)) var/turf/V = get_turf(C) if(disease_air_spread_walk(T, V)) C.AirborneContractDisease(src, force_spread) - + */ /proc/disease_air_spread_walk(turf/start, turf/end) if(!start || !end) return FALSE @@ -127,6 +129,10 @@ return FALSE end = Temp +/datum/disease/proc/IsSame(datum/disease/D) + if(istype(D, type)) + return TRUE + return FALSE /datum/disease/proc/cure(add_resistance = TRUE) if(affected_mob) @@ -134,18 +140,56 @@ LAZYOR(affected_mob.disease_resistances, GetDiseaseID()) qdel(src) -/datum/disease/proc/IsSame(datum/disease/D) - if(istype(D, type)) - return TRUE - return FALSE - - /datum/disease/proc/Copy() //note that stage is not copied over - the copy starts over at stage 1 - var/static/list/copy_vars = list("name", "visibility_flags", "disease_flags", "spread_flags", "form", "desc", "agent", "spread_text", - "cure_text", "max_stages", "stage_prob", "viable_mobtypes", "cures", "infectivity", "cure_chance", - "bypasses_immunity", "spreading_modifier", "severity", "required_organs", "needs_all_cures", "strain_data", - "infectable_biotypes", "process_dead") + var/static/list/copy_vars = list( + "name", + "visibility_flags", + "disease_flags", + "spread_flags", + "form", + "desc", + "agent", + "spread_text", + "cure_text", + "max_stages", + "stage_prob", + "viable_mobtypes", + "cures", + "infectivity", + "cure_chance", + "bypasses_immunity", + "spreading_modifier", + "severity", + "required_organs", + "needs_all_cures", + "strain_data", + "infectable_biotypes", + "process_dead", + "mutation_modifier", + "strength", + "robustness", + "max_bodytemperature", + "min_bodytemperature", + "log", + "origin", + "logged_virusfood", + "fever_warning", + "color", + "pattern", + "pattern_color", + "can_kill", + "infectionchance", + "infectionchance_base", + "ticks", + "speed", + "subID", + "uniqueID", + "childID", + "symptoms", + "stageprob", + "antigen", + ) var/datum/disease/D = copy_type ? new copy_type() : new type() for(var/V in copy_vars) diff --git a/code/datums/diseases/adrenal_crisis.dm b/code/datums/diseases/adrenal_crisis.dm deleted file mode 100644 index a0fc1fc10ddc..000000000000 --- a/code/datums/diseases/adrenal_crisis.dm +++ /dev/null @@ -1,39 +0,0 @@ -/datum/disease/adrenal_crisis - form = "Condition" - name = "Adrenal Crisis" - max_stages = 2 - cure_text = "Trauma" - cures = list(/datum/reagent/determination) - cure_chance = 10 - agent = "Shitty Adrenal Glands" - viable_mobtypes = list(/mob/living/carbon/human) - spreading_modifier = 1 - desc = "If left untreated the subject will suffer from lethargy, dizziness and periodic loss of conciousness." - severity = DISEASE_SEVERITY_MEDIUM - disease_flags = CAN_CARRY|CAN_RESIST - spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS - spread_text = "Organ failure" - visibility_flags = HIDDEN_PANDEMIC - bypasses_immunity = TRUE - -/datum/disease/adrenal_crisis/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - switch(stage) - if(1) - if(SPT_PROB(2.5, seconds_per_tick)) - to_chat(affected_mob, span_warning(pick("You feel lightheaded.", "You feel lethargic."))) - if(2) - if(SPT_PROB(5, seconds_per_tick)) - affected_mob.Unconscious(40) - - if(SPT_PROB(10, seconds_per_tick)) - affected_mob.adjust_slurring(14 SECONDS) - - if(SPT_PROB(7, seconds_per_tick)) - affected_mob.set_dizzy_if_lower(20 SECONDS) - - if(SPT_PROB(2.5, seconds_per_tick)) - to_chat(affected_mob, span_warning(pick("You feel pain shoot down your legs!", "You feel like you are going to pass out at any moment.", "You feel really dizzy."))) diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index 99c3531d2ffa..2af48239b1b2 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -27,7 +27,6 @@ // NEW VARS var/list/properties = list() - var/list/symptoms = list() // The symptoms of the disease. var/id = "" var/processing = FALSE var/mutable = TRUE //set to FALSE to prevent most in-game methods of altering the disease via virology diff --git a/code/datums/diseases/advance/symptoms/beard.dm b/code/datums/diseases/advance/symptoms/beard.dm deleted file mode 100644 index 7247b379909c..000000000000 --- a/code/datums/diseases/advance/symptoms/beard.dm +++ /dev/null @@ -1,37 +0,0 @@ -/** Facial Hypertrichosis - * No change to stealth. - * Increases resistance. - * Increases speed. - * Slighlty increases transmissibility - * Intense Level. - * Bonus: Makes the mob grow a massive beard, regardless of gender. -*/ - -/datum/symptom/beard - name = "Facial Hypertrichosis" - desc = "The virus increases hair production significantly, causing rapid beard growth." - illness = "Man-Mouth" - stealth = 0 - resistance = 3 - stage_speed = 2 - transmittable = 1 - level = 4 - severity = 1 - symptom_delay_min = 18 - symptom_delay_max = 36 - - var/list/beard_order = list("Beard (Jensen)", "Beard (Full)", "Beard (Dwarf)", "Beard (Very Long)") - -/datum/symptom/beard/Activate(datum/disease/advance/A) - . = ..() - if(!.) - return - var/mob/living/M = A.affected_mob - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/index = min(max(beard_order.Find(H.facial_hairstyle)+1, A.stage-1), beard_order.len) - if(index > 0 && H.facial_hairstyle != beard_order[index]) - to_chat(H, span_warning("Your chin itches.")) - H.facial_hairstyle = beard_order[index] - H.update_body_parts() - diff --git a/code/datums/diseases/advance/symptoms/chills.dm b/code/datums/diseases/advance/symptoms/chills.dm deleted file mode 100644 index 427ff4f8b3b5..000000000000 --- a/code/datums/diseases/advance/symptoms/chills.dm +++ /dev/null @@ -1,78 +0,0 @@ -#define CHILLS_CHANGE "chills" -/** Chills - * No change to stealth. - * Increases resistance. - * Increases stage speed. - * Increases transmissibility - * Low level - * Bonus: Cools down your body. - */ - -/datum/symptom/chills - name = "Chills" - desc = "The virus inhibits the body's thermoregulation, cooling the body down." - illness = "Cold Shoulder" - stealth = 0 - resistance = 2 - stage_speed = 3 - transmittable = 2 - level = 2 - severity = 2 - symptom_delay_min = 10 - symptom_delay_max = 30 - var/unsafe = FALSE //over the cold threshold - threshold_descs = list( - "Stage Speed 5" = "Increases the intensity of the cooling; the host can fall below safe temperature levels.", - "Stage Speed 10" = "Increases the intensity of the cooling even further." - ) - -/datum/symptom/chills/Start(datum/disease/advance/A) - . = ..() - if(!.) - return - if(A.totalStageSpeed() >= 5) //dangerous cold - power = 1.5 - unsafe = TRUE - if(A.totalStageSpeed() >= 10) - power = 2.5 - -/datum/symptom/chills/Activate(datum/disease/advance/A) - . = ..() - if(!.) - return - var/mob/living/carbon/M = A.affected_mob - if(!unsafe || A.stage < 4) - to_chat(M, span_warning("[pick("You feel cold.", "You shiver.")]")) - else - to_chat(M, span_userdanger("[pick("You feel your blood run cold.", "You feel ice in your veins.", "You feel like you can't heat up.", "You shiver violently.")]")) - set_body_temp(A.affected_mob, A) - -/** - * set_body_temp Sets the body temp change - * - * Sets the body temp change to the mob based on the stage and resistance of the disease - * arguments: - * * mob/living/M The mob to apply changes to - * * datum/disease/advance/A The disease applying the symptom - */ -/datum/symptom/chills/proc/set_body_temp(mob/living/M, datum/disease/advance/A) - if(unsafe) // when unsafe the shivers can cause cold damage - M.add_body_temperature_change(CHILLS_CHANGE, -6 * power * A.stage) - else - // Get the max amount of change allowed before going under cold damage limit, then cap the maximum allowed temperature change from safe chills to 5 over the cold damage limit - var/change_limit = min(M.get_body_temp_cold_damage_limit() + 5 - M.get_body_temp_normal(apply_change=FALSE), 0) - M.add_body_temperature_change(CHILLS_CHANGE, max(-6 * power * A.stage, change_limit)) - -/// Update the body temp change based on the new stage -/datum/symptom/chills/on_stage_change(datum/disease/advance/A) - . = ..() - if(.) - set_body_temp(A.affected_mob, A) - -/// remove the body temp change when removing symptom -/datum/symptom/chills/End(datum/disease/advance/A) - var/mob/living/carbon/M = A.affected_mob - if(M) - M.remove_body_temperature_change(CHILLS_CHANGE) - -#undef CHILLS_CHANGE diff --git a/code/datums/diseases/advance/symptoms/choking.dm b/code/datums/diseases/advance/symptoms/choking.dm deleted file mode 100644 index 25c492f9779e..000000000000 --- a/code/datums/diseases/advance/symptoms/choking.dm +++ /dev/null @@ -1,149 +0,0 @@ -/**Choking - * Very very noticable. - * Lowers resistance - * Decreases stage speed - * Greatly decreases transmissibility - * Moderate Level. - * Bonus: Inflicts spikes of oxyloss - */ - -/datum/symptom/choking - name = "Choking" - desc = "The virus causes inflammation of the host's air conduits, leading to intermittent choking." - illness = "Pneumatic Tubes" - stealth = -3 - resistance = -2 - stage_speed = -2 - transmittable = -2 - level = 3 - severity = 3 - base_message_chance = 15 - symptom_delay_min = 10 - symptom_delay_max = 30 - threshold_descs = list( - "Stage Speed 8" = "Causes choking more frequently.", - "Stealth 4" = "The symptom remains hidden until active." - ) - -/datum/symptom/choking/Start(datum/disease/advance/A) - . = ..() - if(!.) - return - if(A.totalStageSpeed() >= 8) - symptom_delay_min = 7 - symptom_delay_max = 24 - if(A.totalStealth() >= 4) - suppress_warning = TRUE - -/datum/symptom/choking/Activate(datum/disease/advance/A) - . = ..() - if(!.) - return - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2) - if(prob(base_message_chance) && !suppress_warning) - to_chat(M, span_warning("[pick("You're having difficulty breathing.", "Your breathing becomes heavy.")]")) - if(3, 4) - if(!suppress_warning) - to_chat(M, span_warning("[pick("Your windpipe feels like a straw.", "Your breathing becomes tremendously difficult.")]")) - else - to_chat(M, span_warning("You feel very [pick("dizzy","woozy","faint")].")) //fake bloodloss messages - Choke_stage_3_4(M, A) - M.emote("gasp") - else - to_chat(M, span_userdanger("[pick("You're choking!", "You can't breathe!")]")) - Choke(M, A) - M.emote("gasp") - -/datum/symptom/choking/proc/Choke_stage_3_4(mob/living/M, datum/disease/advance/A) - M.adjustOxyLoss(rand(6,13)) - return 1 - -/datum/symptom/choking/proc/Choke(mob/living/M, datum/disease/advance/A) - M.adjustOxyLoss(rand(10,18)) - return 1 - -/* -////////////////////////////////////// - -Asphyxiation - - Very very noticable. - Decreases stage speed. - Decreases transmittablity. - -Bonus - Inflicts large spikes of oxyloss - Introduces Asphyxiating drugs to the system - Causes cardiac arrest on dying victims. - -////////////////////////////////////// -*/ - -/datum/symptom/asphyxiation - - name = "Acute respiratory distress syndrome" - desc = "The virus causes shrinking of the host's lungs, causing severe asphyxiation. May also lead to brain damage in critical patients." - illness = "Iron Lungs" - stealth = -2 - resistance = -0 - stage_speed = -1 - transmittable = -2 - level = 7 - severity = 6 - base_message_chance = 15 - symptom_delay_min = 14 - symptom_delay_max = 30 - var/paralysis = FALSE - threshold_descs = list( - "Stage Speed 8" = "Additionally synthesizes pancuronium and sodium thiopental inside the host.", - "Transmission 8" = "Doubles the damage caused by the symptom." - ) - - -/datum/symptom/asphyxiation/Start(datum/disease/advance/A) - . = ..() - if(!.) - return - if(A.totalStageSpeed() >= 8) - paralysis = TRUE - if(A.totalTransmittable() >= 8) - power = 2 - -/datum/symptom/asphyxiation/Activate(datum/disease/advance/A) - . = ..() - if(!.) - return - var/mob/living/M = A.affected_mob - switch(A.stage) - if(3, 4) - to_chat(M, span_warning("[pick("Your windpipe feels thin.", "Your lungs feel small.")]")) - Asphyxiate_stage_3_4(M, A) - M.emote("gasp") - if(5) - to_chat(M, span_userdanger("[pick("Your lungs hurt!", "It hurts to breathe!")]")) - Asphyxiate(M, A) - M.emote("gasp") - if(M.getOxyLoss() >= 120) - M.visible_message(span_warning("[M] stops breathing, as if their lungs have totally collapsed!")) - Asphyxiate_death(M, A) - return - -/datum/symptom/asphyxiation/proc/Asphyxiate_stage_3_4(mob/living/M, datum/disease/advance/A) - var/get_damage = rand(10,15) * power - M.adjustOxyLoss(get_damage) - return 1 - -/datum/symptom/asphyxiation/proc/Asphyxiate(mob/living/M, datum/disease/advance/A) - var/get_damage = rand(15,21) * power - M.adjustOxyLoss(get_damage) - if(paralysis) - M.reagents.add_reagent_list(list(/datum/reagent/toxin/pancuronium = 3, /datum/reagent/toxin/sodium_thiopental = 3)) - return 1 - -/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.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 deleted file mode 100644 index 2c6afe4b80d8..000000000000 --- a/code/datums/diseases/advance/symptoms/confusion.dm +++ /dev/null @@ -1,73 +0,0 @@ -/**Confusion - * Slightly increases stealth - * Slightly lowers resistance - * Decreases stage speed - * No effect to transmissibility - * Intense level - * Bonus: Makes the affected mob be confused for short periods of time. - */ -/datum/symptom/confusion - name = "Confusion" - desc = "The virus interferes with the proper function of the neural system, leading to bouts of confusion and erratic movement." - illness = "Shattered Reality" - stealth = 1 - resistance = -1 - stage_speed = -3 - transmittable = 0 - level = 4 - severity = 2 - base_message_chance = 25 - symptom_delay_min = 10 - symptom_delay_max = 30 - threshold_descs = list( - "Stage Speed 6" = "Prevents any form of reading or writing.", - "Resistance 6" = "Causes brain damage over time.", - "Transmission 6" = "Increases confusion duration and strength.", - "Stealth 4" = "The symptom remains hidden until active.", - ) - var/brain_damage = FALSE - var/causes_illiteracy = FALSE - -/datum/symptom/confusion/Start(datum/disease/advance/advanced_disease) - . = ..() - if(!.) - return - if(advanced_disease.totalStageSpeed() >= 6) - causes_illiteracy = TRUE - if(advanced_disease.totalResistance() >= 6) - brain_damage = TRUE - if(advanced_disease.totalTransmittable() >= 6) - power = 1.5 - if(advanced_disease.totalStealth() >= 4) - suppress_warning = TRUE - -/datum/symptom/confusion/End(datum/disease/advance/advanced_disease) - advanced_disease.affected_mob.remove_status_effect(/datum/status_effect/confusion) - REMOVE_TRAIT(advanced_disease.affected_mob, TRAIT_ILLITERATE, DISEASE_TRAIT) - return ..() - -/datum/symptom/confusion/Activate(datum/disease/advance/advanced_disease) - . = ..() - if(!.) - return - var/mob/living/carbon/infected_mob = advanced_disease.affected_mob - switch(advanced_disease.stage) - if(1, 2, 3, 4) - if(prob(base_message_chance) && !suppress_warning) - to_chat(infected_mob, span_warning("[pick("Your head hurts.", "Your mind blanks for a moment.")]")) - else - to_chat(infected_mob, span_userdanger("You can't think straight!")) - infected_mob.adjust_confusion(16 SECONDS * power) - if(brain_damage) - infected_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3 * power, 80) - infected_mob.updatehealth() - return - -/datum/symptom/confusion/on_stage_change(datum/disease/advance/advanced_disease) - . = ..() - if(!.) - return FALSE - var/mob/living/carbon/infected_mob = advanced_disease.affected_mob - if(advanced_disease.stage >= 5 && causes_illiteracy) - ADD_TRAIT(infected_mob, TRAIT_ILLITERATE, DISEASE_TRAIT) - return TRUE diff --git a/code/datums/diseases/advance/symptoms/cough.dm b/code/datums/diseases/advance/symptoms/cough.dm deleted file mode 100644 index efef945a7720..000000000000 --- a/code/datums/diseases/advance/symptoms/cough.dm +++ /dev/null @@ -1,80 +0,0 @@ -/**Coughing - * Slightly decreases stealth - * Reduces resistance - * Slightly increases stage speed - * Increases transmissibility - * Low level - * Bonus: Spreads the virus in a small square around the host. Can force the affected mob to drop small items! -*/ -/datum/symptom/cough - name = "Cough" - desc = "The virus irritates the throat of the host, causing occasional coughing. Each cough will try to infect bystanders who are within 1 tile of the host with the virus." - illness = "Jest Infection" - stealth = -1 - resistance = 3 - stage_speed = 1 - transmittable = 2 - level = 1 - severity = 1 - base_message_chance = 15 - symptom_delay_min = 2 - symptom_delay_max = 15 - var/spread_range = 1 - threshold_descs = list( - "Resistance 11" = "The host will drop small items when coughing.", - "Resistance 15" = "Occasionally causes coughing fits that stun the host. The extra coughs do not spread the virus.", - "Stage Speed 6" = "Increases cough frequency.", - "Transmission 7" = "Coughing will now infect bystanders up to 2 tiles away.", - "Stealth 4" = "The symptom remains hidden until active.", - ) - ///emote cooldowns - COOLDOWN_DECLARE(cough_cooldown) - ///if FALSE, there is a percentage chance that the mob will emote coughing while cough_cooldown is on cooldown. If TRUE, won't emote again until after the off cooldown cough occurs. - var/off_cooldown_coughed = FALSE - -/datum/symptom/cough/Start(datum/disease/advance/active_disease) - . = ..() - if(!.) - return - if(active_disease.totalStealth() >= 4) - suppress_warning = TRUE - if(active_disease.totalTransmittable() >= 7) - spread_range = 2 - if(active_disease.totalResistance() >= 11) //strong enough to drop items - power = 1.5 - if(active_disease.totalResistance() >= 15) //strong enough to stun (occasionally) - power = 2 - if(active_disease.totalStageSpeed() >= 6) //cough more often - symptom_delay_max = 10 - -/datum/symptom/cough/Activate(datum/disease/advance/active_disease) - . = ..() - if(!.) - return - var/mob/living/affected_mob = active_disease.affected_mob - if(HAS_TRAIT(affected_mob, TRAIT_SOOTHED_THROAT)) - return - switch(active_disease.stage) - if(1, 2, 3) - if(prob(base_message_chance) && !suppress_warning) - to_chat(affected_mob, span_warning("[pick("You swallow excess mucus.", "You lightly cough.")]")) - else - if(COOLDOWN_FINISHED(src, cough_cooldown) || !COOLDOWN_FINISHED(src, cough_cooldown) && prob(60) && !off_cooldown_coughed) - affected_mob.emote("cough") - COOLDOWN_START(src, cough_cooldown, 5 SECONDS) - if(!off_cooldown_coughed && !COOLDOWN_FINISHED(src, cough_cooldown)) - off_cooldown_coughed = TRUE - else - off_cooldown_coughed = FALSE - if(affected_mob.CanSpreadAirborneDisease()) - active_disease.spread(spread_range) - if(power >= 1.5) - var/obj/item/held_object = affected_mob.get_active_held_item() - if(held_object && held_object.w_class == WEIGHT_CLASS_TINY) - affected_mob.dropItemToGround(held_object) - if(power >= 2 && prob(30)) - to_chat(affected_mob, span_userdanger("[pick("You have a coughing fit!", "You can't stop coughing!")]")) - affected_mob.Immobilize(20) - addtimer(CALLBACK(affected_mob, TYPE_PROC_REF(/mob/, emote), "cough"), 6) - addtimer(CALLBACK(affected_mob, TYPE_PROC_REF(/mob/, emote), "cough"), 12) - addtimer(CALLBACK(affected_mob, TYPE_PROC_REF(/mob/, emote), "cough"), 18) diff --git a/code/datums/diseases/advance/symptoms/deafness.dm b/code/datums/diseases/advance/symptoms/deafness.dm deleted file mode 100644 index 513783442605..000000000000 --- a/code/datums/diseases/advance/symptoms/deafness.dm +++ /dev/null @@ -1,72 +0,0 @@ -/**Deafness - * Slightly decreases stealth - * Lowers Resistance - * Slightly decreases stage speed - * Decreases transmissibility - * Intense level - * Bonus: Causes intermittent loss of hearing. -*/ -/datum/symptom/deafness - name = "Deafness" - desc = "The virus causes inflammation of the eardrums, causing intermittent deafness." - illness = "Aural Perforation" - stealth = -1 - resistance = -2 - stage_speed = -1 - transmittable = -3 - level = 4 - severity = 4 - base_message_chance = 100 - symptom_delay_min = 25 - symptom_delay_max = 80 - threshold_descs = list( - "Resistance 9" = "Causes permanent deafness, instead of intermittent.", - "Stealth 4" = "The symptom remains hidden until active.", - ) - var/causes_permanent_deafness = FALSE - -/datum/symptom/deafness/Start(datum/disease/advance/A) - . = ..() - if(!.) - return - if(A.totalStealth() >= 4) - suppress_warning = TRUE - if(A.totalResistance() >= 9) //permanent deafness - causes_permanent_deafness = TRUE - -/datum/symptom/deafness/End(datum/disease/advance/advanced_disease) - REMOVE_TRAIT(advanced_disease.affected_mob, TRAIT_DEAF, DISEASE_TRAIT) - return ..() - -/datum/symptom/deafness/Activate(datum/disease/advance/A) - . = ..() - if(!.) - return - var/mob/living/carbon/infected_mob = A.affected_mob - var/obj/item/organ/internal/ears/ears = infected_mob.get_organ_slot(ORGAN_SLOT_EARS) - if(!ears) - return //cutting off your ears to cure the deafness: the ultimate own - switch(A.stage) - if(3, 4) - if(prob(base_message_chance) && !suppress_warning) - to_chat(infected_mob, span_warning("[pick("You hear a ringing in your ear.", "Your ears pop.")]")) - if(5) - if(causes_permanent_deafness) - if(ears.damage < ears.maxHealth) - to_chat(infected_mob, span_userdanger("Your ears pop painfully and start bleeding!")) - // Just absolutely murder me man - ears.apply_organ_damage(ears.maxHealth) - infected_mob.emote("scream") - ADD_TRAIT(infected_mob, TRAIT_DEAF, DISEASE_TRAIT) - else - to_chat(infected_mob, span_userdanger("Your ears pop and begin ringing loudly!")) - ears.deaf = min(20, ears.deaf + 15) - -/datum/symptom/deafness/on_stage_change(datum/disease/advance/advanced_disease) - . = ..() - if(!.) - return FALSE - var/mob/living/carbon/infected_mob = advanced_disease.affected_mob - if(advanced_disease.stage < 5 || !causes_permanent_deafness) - REMOVE_TRAIT(infected_mob, TRAIT_DEAF, DISEASE_TRAIT) - return TRUE diff --git a/code/datums/diseases/advance/symptoms/disfiguration.dm b/code/datums/diseases/advance/symptoms/disfiguration.dm deleted file mode 100644 index 612c8eb26b17..000000000000 --- a/code/datums/diseases/advance/symptoms/disfiguration.dm +++ /dev/null @@ -1,42 +0,0 @@ -/**Disfiguration - * Increases stealth - * No change to resistance - * Increases stage speed - * Slightly increases transmissibility - * Critical level - * Bonus: Adds disfiguration trait making the mob appear as "Unknown" to others. - */ -/datum/symptom/disfiguration - name = "Disfiguration" - desc = "The virus liquefies facial muscles, disfiguring the host." - illness = "Broken Face" - stealth = 2 - resistance = 0 - stage_speed = 3 - transmittable = 1 - level = 5 - severity = 1 - symptom_delay_min = 25 - symptom_delay_max = 75 - -/datum/symptom/disfiguration/Activate(datum/disease/advance/A) - . = ..() - if(!.) - return - var/mob/living/M = A.affected_mob - if (HAS_TRAIT(M, TRAIT_DISFIGURED)) - return - switch(A.stage) - if(5) - ADD_TRAIT(M, TRAIT_DISFIGURED, DISEASE_TRAIT) - M.visible_message(span_warning("[M]'s face appears to cave in!"), span_notice("You feel your face crumple and cave in!")) - else - M.visible_message(span_warning("[M]'s face begins to contort..."), span_notice("Your face feels wet and malleable...")) - - -/datum/symptom/disfiguration/End(datum/disease/advance/A) - . = ..() - if(!.) - return - if(A.affected_mob) - REMOVE_TRAIT(A.affected_mob, TRAIT_DISFIGURED, DISEASE_TRAIT) diff --git a/code/datums/diseases/advance/symptoms/genetics.dm b/code/datums/diseases/advance/symptoms/genetics.dm deleted file mode 100644 index 2eebdb7c0216..000000000000 --- a/code/datums/diseases/advance/symptoms/genetics.dm +++ /dev/null @@ -1,69 +0,0 @@ -/*DNA Saboteur - * Lowers stealth - * Lowers resistance greatly - * No change to stage speed - * Decreases transmissibility greatly - * Fatal level - * Bonus: Cleans the DNA of a person and then randomly gives them a trait. -*/ - -/datum/symptom/genetic_mutation - name = "Dormant DNA Activator" - desc = "The virus bonds with the DNA of the host, activating random dormant mutations within their DNA. When the virus is cured, the host's genetic alterations are undone." - illness = "Lycanthropy" - stealth = -2 - resistance = -3 - stage_speed = 0 - transmittable = -3 - level = 6 - severity = 4 - base_message_chance = 50 - symptom_delay_min = 30 - symptom_delay_max = 60 - var/excludemuts = NONE - var/no_reset = FALSE - var/mutadone_proof = NONE - threshold_descs = list( - "Resistance 8" = "The mutations caused by the virus are mutadone-proof (but will still be undone when the virus is cured if the resistance 14 threshold is not met).", - "Resistance 14" = "The host's genetic alterations are not undone when the virus is cured.", - "Stage Speed 10" = "The virus activates dormant mutations at a much faster rate.", - "Stealth 5" = "Only activates negative mutations in hosts.", - "Stealth -7" = "excludes strong negative mutations." - ) - -/datum/symptom/genetic_mutation/Start(datum/disease/advance/A) - . = ..() - if(!.) - return - if(A.totalStealth() >= 5) //only give them bad mutations - excludemuts = POSITIVE - if(A.totalStealth() <= -7) //only give them good mutations - excludemuts = NEGATIVE - if(A.totalStageSpeed() >= 10) //activate dormant mutations more often at around 1.5x the pace - symptom_delay_min = 20 - symptom_delay_max = 40 - if(A.totalResistance() >= 8) //mutadone won't save you now - mutadone_proof = (NEGATIVE | MINOR_NEGATIVE | POSITIVE) - if(A.totalResistance() >= 14) //one does not simply escape Nurgle's grasp - no_reset = TRUE - -/datum/symptom/genetic_mutation/Activate(datum/disease/advance/A) - . = ..() - if(!.) - return - var/mob/living/carbon/C = A.affected_mob - if(!C.has_dna()) - return - switch(A.stage) - if(4, 5) - to_chat(C, span_warning("[pick("Your skin feels itchy.", "You feel light headed.")]")) - C.easy_random_mutate((NEGATIVE | MINOR_NEGATIVE | POSITIVE) - excludemuts, TRUE, TRUE, TRUE, mutadone_proof) - -/datum/symptom/genetic_mutation/End(datum/disease/advance/A) - . = ..() - if(!.) - return - if(!no_reset) - var/mob/living/carbon/M = A.affected_mob - if(M.has_dna()) - M.dna.remove_all_mutations(list(MUT_NORMAL, MUT_EXTRA), FALSE) diff --git a/code/datums/diseases/advance/symptoms/hallucigen.dm b/code/datums/diseases/advance/symptoms/hallucigen.dm deleted file mode 100644 index b0e28d05a8b9..000000000000 --- a/code/datums/diseases/advance/symptoms/hallucigen.dm +++ /dev/null @@ -1,65 +0,0 @@ -/*Hallucigen - * Slightly increases stealth - * Lowers resistance tremendously - * Slightly decreases stage speed - * Slightly reduces transmissibility - * Critical level - * Bonus:Makes the affected mob be hallucinated for short periods of time. -*/ - -/datum/symptom/hallucigen - name = "Hallucigen" - desc = "The virus stimulates the brain, causing occasional hallucinations." - illness = "Paranoyance" - stealth = 1 - resistance = -4 - stage_speed = 1 - transmittable = -1 - level = 5 - severity = 2 - base_message_chance = 25 - symptom_delay_min = 25 - symptom_delay_max = 90 - var/fake_healthy = FALSE - threshold_descs = list( - "Stage Speed 7" = "Increases the amount of hallucinations.", - "Stealth 4" = "The virus mimics positive symptoms.", - ) - -/datum/symptom/hallucigen/Start(datum/disease/advance/A) - . = ..() - if(!.) - return - if(A.totalStealth() >= 4) //fake good symptom messages - fake_healthy = TRUE - base_message_chance = 50 - if(A.totalStageSpeed() >= 7) //stronger hallucinations - power = 2 - -/datum/symptom/hallucigen/Activate(datum/disease/advance/A) - . = ..() - if(!.) - return - var/mob/living/carbon/M = A.affected_mob - var/list/healthy_messages = list("Your lungs feel great.", "You realize you haven't been breathing.", "You don't feel the need to breathe.",\ - "Your eyes feel great.", "Your ears feel great.", "You don't feel the need to blink.") - switch(A.stage) - if(1, 2) - if(prob(base_message_chance)) - if(!fake_healthy) - to_chat(M, span_notice("[pick("Something appears in your peripheral vision, then winks out.", "You hear a faint whisper with no source.", "Your head aches.")]")) - else - to_chat(M, span_notice("[pick(healthy_messages)]")) - if(3, 4) - if(prob(base_message_chance)) - if(!fake_healthy) - to_chat(M, span_danger("[pick("Something is following you.", "You are being watched.", "You hear a whisper in your ear.", "Thumping footsteps slam toward you from nowhere.")]")) - else - to_chat(M, span_notice("[pick(healthy_messages)]")) - else - if(prob(base_message_chance)) - if(!fake_healthy) - to_chat(M, span_userdanger("[pick("Oh, your head...", "Your head pounds.", "They're everywhere! Run!", "Something in the shadows...")]")) - else - to_chat(M, span_notice("[pick(healthy_messages)]")) - M.adjust_hallucinations(90 SECONDS * power) diff --git a/code/datums/diseases/advance/symptoms/itching.dm b/code/datums/diseases/advance/symptoms/itching.dm deleted file mode 100644 index 0f70a7c71975..000000000000 --- a/code/datums/diseases/advance/symptoms/itching.dm +++ /dev/null @@ -1,57 +0,0 @@ -/*Itching - * No effect to stealth - * Greatly increases resistance - * Greatly increases stage speed - * Slightly increases transmissibility - * Low level - * Bonus: Displays an annoying message! Should be used for buffing your disease. -*/ -/datum/symptom/itching - name = "Itching" - desc = "The virus irritates the skin, causing itching." - illness = "Discrete Itching" - stealth = 0 - resistance = 3 - stage_speed = 3 - transmittable = 1 - level = 1 - severity = 1 - symptom_delay_min = 5 - symptom_delay_max = 25 - var/scratch = FALSE - threshold_descs = list( - "Transmission 6" = "Increases frequency of itching.", - "Stage Speed 7" = "The host will scrath itself when itching, causing superficial damage.", - ) - ///emote cooldowns - COOLDOWN_DECLARE(itching_cooldown) - ///if FALSE, there is a percentage chance that the mob will emote scratching while itching_cooldown is on cooldown. If TRUE, won't emote again until after the off cooldown scratch occurs. - var/off_cooldown_scratched = FALSE -/datum/symptom/itching/Start(datum/disease/advance/active_disease) - . = ..() - if(!.) - return - if(active_disease.totalTransmittable() >= 6) //itch more often - symptom_delay_min = 1 - symptom_delay_max = 4 - if(active_disease.totalStageSpeed() >= 7) //scratch - scratch = TRUE - -/datum/symptom/itching/Activate(datum/disease/advance/active_disease) - . = ..() - if(!.) - return - var/mob/living/carbon/affected_mob = active_disease.affected_mob - var/obj/item/bodypart/bodypart = affected_mob.get_bodypart(affected_mob.get_random_valid_zone(even_weights = TRUE)) - if(bodypart && IS_ORGANIC_LIMB(bodypart) && !(bodypart.bodypart_flags & BODYPART_PSEUDOPART)) //robotic limbs will mean less scratching overall (why are golems able to damage themselves with self-scratching, but not androids? the world may never know) - var/can_scratch = scratch && !affected_mob.incapacitated() - if(can_scratch) - bodypart.receive_damage(0.5) - //below handles emotes, limiting the emote of emotes passed to chat - if(COOLDOWN_FINISHED(src, itching_cooldown) || !COOLDOWN_FINISHED(src, itching_cooldown) && prob(60) && !off_cooldown_scratched) - affected_mob.visible_message("[can_scratch ? span_warning("[affected_mob] scratches [affected_mob.p_their()] [bodypart.plaintext_zone].") : ""]", span_warning("Your [bodypart.plaintext_zone] itches. [can_scratch ? " You scratch it." : ""]")) - COOLDOWN_START(src, itching_cooldown, 5 SECONDS) - if(!off_cooldown_scratched && !COOLDOWN_FINISHED(src, itching_cooldown)) - off_cooldown_scratched = TRUE - else - off_cooldown_scratched = FALSE diff --git a/code/datums/diseases/advance/symptoms/narcolepsy.dm b/code/datums/diseases/advance/symptoms/narcolepsy.dm deleted file mode 100644 index ac7eae25d4a7..000000000000 --- a/code/datums/diseases/advance/symptoms/narcolepsy.dm +++ /dev/null @@ -1,75 +0,0 @@ -/*Narcolepsy - * Slight reduction to stealth - * Reduces resistance - * Greatly reduces stage speed - * No change to transmissibility - * Fatal level - * Bonus: Causes drowsiness and sleep. -*/ -/datum/symptom/narcolepsy - name = "Narcolepsy" - desc = "The virus causes a hormone imbalance, making the host sleepy and narcoleptic." - illness = "Aurora Snorealis" - stealth = -1 - resistance = -2 - stage_speed = -3 - transmittable = 0 - level = 6 - symptom_delay_min = 30 - symptom_delay_max = 85 - severity = 4 - var/yawning = FALSE - threshold_descs = list( - "Transmission 4" = "Causes the host to periodically emit a yawn that tries to infect bystanders within 6 meters of the host.", - "Stage Speed 10" = "Causes narcolepsy more often, increasing the chance of the host falling asleep.", - ) - -/datum/symptom/narcolepsy/Start(datum/disease/advance/A) - . = ..() - if(!.) - return - if(A.totalTransmittable() >= 4) //yawning (mostly just some copy+pasted code from sneezing, with a few tweaks) - yawning = TRUE - if(A.totalStageSpeed() >= 10) //act more often - symptom_delay_min = 20 - symptom_delay_max = 45 - -/datum/symptom/narcolepsy/Activate(datum/disease/advance/A) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1) - if(prob(50)) - to_chat(M, span_warning("You feel tired.")) - if(2) - if(prob(50)) - to_chat(M, span_warning("You feel very tired.")) - if(3) - if(prob(50)) - to_chat(M, span_warning("You try to focus on staying awake.")) - - M.adjust_drowsiness_up_to(10 SECONDS, 140 SECONDS) - - if(4) - if(prob(50)) - if(yawning) - to_chat(M, span_warning("You try and fail to suppress a yawn.")) - else - to_chat(M, span_warning("You nod off for a moment.")) //you can't really yawn while nodding off, can you? - - M.adjust_drowsiness_up_to(20 SECONDS, 140 SECONDS) - - if(yawning) - M.emote("yawn") - if(M.CanSpreadAirborneDisease()) - A.spread(6) - - if(5) - if(prob(50)) - to_chat(M, span_warning("[pick("So tired...","You feel very sleepy.","You have a hard time keeping your eyes open.","You try to stay awake.")]")) - - M.adjust_drowsiness_up_to(80 SECONDS, 140 SECONDS) - - if(yawning) - M.emote("yawn") - if(M.CanSpreadAirborneDisease()) - A.spread(6) diff --git a/code/datums/diseases/advance/symptoms/sensory.dm b/code/datums/diseases/advance/symptoms/sensory.dm deleted file mode 100644 index 2d21f9f147a5..000000000000 --- a/code/datums/diseases/advance/symptoms/sensory.dm +++ /dev/null @@ -1,117 +0,0 @@ -/* Mind Restoration - * Slight stealth reduction - * Reduces resistance - * Slight increase to stage speed - * Greatly decreases transmissibility - * Critical level -*/ -/datum/symptom/mind_restoration - name = "Mind Restoration" - desc = "The virus strengthens the bonds between neurons, reducing the duration of any ailments of the mind." - stealth = -1 - resistance = -2 - stage_speed = 1 - transmittable = -3 - level = 5 - symptom_delay_min = 5 - symptom_delay_max = 10 - var/purge_alcohol = FALSE - var/trauma_heal_mild = FALSE - var/trauma_heal_severe = FALSE - threshold_descs = list( - "Resistance 6" = "Heals minor brain traumas.", - "Resistance 9" = "Heals severe brain traumas.", - "Transmission 8" = "Purges alcohol in the bloodstream.", - ) - -/datum/symptom/mind_restoration/Start(datum/disease/advance/A) - . = ..() - if(!.) - return - if(A.totalResistance() >= 6) //heal brain damage - trauma_heal_mild = TRUE - if(A.totalResistance() >= 9) //heal severe traumas - trauma_heal_severe = TRUE - if(A.totalTransmittable() >= 8) //purge alcohol - purge_alcohol = TRUE - -/datum/symptom/mind_restoration/Activate(datum/disease/advance/A) - . = ..() - if(!.) - return - var/mob/living/M = A.affected_mob - - - if(A.stage >= 3) - M.adjust_dizzy(-4 SECONDS) - M.adjust_drowsiness(-4 SECONDS) - M.adjust_slurring(-1 SECONDS) - M.adjust_confusion(-2 SECONDS) - if(purge_alcohol) - M.reagents.remove_all_type(/datum/reagent/consumable/ethanol, 3) - M.adjust_drunk_effect(-5) - - if(A.stage >= 4) - M.adjust_drowsiness(-4 SECONDS) - if(M.reagents.has_reagent(/datum/reagent/toxin/mindbreaker)) - M.reagents.remove_reagent(/datum/reagent/toxin/mindbreaker, 5) - if(M.reagents.has_reagent(/datum/reagent/toxin/histamine)) - M.reagents.remove_reagent(/datum/reagent/toxin/histamine, 5) - - M.adjust_hallucinations(-20 SECONDS) - - if(A.stage >= 5) - M.adjustOrganLoss(ORGAN_SLOT_BRAIN, -3) - if(trauma_heal_mild && iscarbon(M)) - var/mob/living/carbon/C = M - if(prob(10)) - if(trauma_heal_severe) - C.cure_trauma_type(resilience = TRAUMA_RESILIENCE_SURGERY) - else - C.cure_trauma_type(resilience = TRAUMA_RESILIENCE_BASIC) - - - -/datum/symptom/sensory_restoration - name = "Sensory Restoration" - desc = "The virus stimulates the production and replacement of sensory tissues, causing the host to regenerate eyes and ears when damaged." - stealth = 0 - resistance = 1 - stage_speed = -2 - transmittable = 2 - level = 4 - base_message_chance = 7 - symptom_delay_min = 1 - symptom_delay_max = 1 - -/datum/symptom/sensory_restoration/Activate(datum/disease/advance/source_disease) - . = ..() - if(!.) - return - var/mob/living/carbon/ill_mob = source_disease.affected_mob - switch(source_disease.stage) - if(4, 5) - var/obj/item/organ/internal/ears/ears = ill_mob.get_organ_slot(ORGAN_SLOT_EARS) - if(ears) - ears.adjustEarDamage(-4, -4) - - ill_mob.adjust_temp_blindness(-4 SECONDS) - ill_mob.adjust_eye_blur(-4 SECONDS) - - var/obj/item/organ/internal/eyes/eyes = ill_mob.get_organ_slot(ORGAN_SLOT_EYES) - if(!eyes) // only dealing with eye stuff from here on out - return - - eyes.apply_organ_damage(-2) - if(prob(20)) - if(ill_mob.is_blind_from(EYE_DAMAGE)) - to_chat(ill_mob, span_warning("Your vision slowly returns...")) - ill_mob.adjust_eye_blur(20 SECONDS) - - else if(ill_mob.is_nearsighted_from(EYE_DAMAGE)) - to_chat(ill_mob, span_warning("The blackness in your peripheral vision begins to fade.")) - ill_mob.adjust_eye_blur(5 SECONDS) - - else - if(prob(base_message_chance)) - to_chat(ill_mob, span_notice("[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 hearing feels more acute.")]")) diff --git a/code/datums/diseases/advance/symptoms/shedding.dm b/code/datums/diseases/advance/symptoms/shedding.dm deleted file mode 100644 index 13d78204a7b2..000000000000 --- a/code/datums/diseases/advance/symptoms/shedding.dm +++ /dev/null @@ -1,49 +0,0 @@ -/*Alopecia - * No change to stealth - * Slight increase to resistance - * Increases stage speed - * Increases transmissibility - * Near critcal level - * Bonus: Makes the mob lose hair. -*/ -/datum/symptom/shedding - name = "Alopecia" - desc = "The virus causes rapid shedding of head and body hair." - illness = "Thin Skinned" - stealth = 0 - resistance = 1 - stage_speed = 2 - transmittable = 2 - level = 4 - severity = 1 - base_message_chance = 50 - symptom_delay_min = 45 - symptom_delay_max = 90 - -/datum/symptom/shedding/Activate(datum/disease/advance/A) - . = ..() - if(!.) - return - - var/mob/living/M = A.affected_mob - if(prob(base_message_chance)) - to_chat(M, span_warning("[pick("Your scalp itches.", "Your skin feels flaky.")]")) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - switch(A.stage) - if(3, 4) - if(!(H.hairstyle == "Bald") && !(H.hairstyle == "Balding Hair")) - to_chat(H, span_warning("Your hair starts to fall out in clumps...")) - addtimer(CALLBACK(src, PROC_REF(Shed), H, FALSE), 50) - if(5) - if(!(H.facial_hairstyle == "Shaved") || !(H.hairstyle == "Bald")) - to_chat(H, span_warning("Your hair starts to fall out in clumps...")) - addtimer(CALLBACK(src, PROC_REF(Shed), H, TRUE), 50) - -/datum/symptom/shedding/proc/Shed(mob/living/carbon/human/H, fullbald) - if(fullbald) - H.facial_hairstyle = "Shaved" - H.hairstyle = "Bald" - else - H.hairstyle = "Balding Hair" - H.update_body_parts() diff --git a/code/datums/diseases/advance/symptoms/skin.dm b/code/datums/diseases/advance/symptoms/skin.dm deleted file mode 100644 index c1ae104618ca..000000000000 --- a/code/datums/diseases/advance/symptoms/skin.dm +++ /dev/null @@ -1,35 +0,0 @@ -/*Polyvitiligo - * Slight reduction to stealth - * Greatly increases resistance - * Slightly increases stage speed - * Increases transmissibility - * Critical level - * Bonus: Makes the mob gain a random crayon powder colorful reagent. -*/ -/datum/symptom/polyvitiligo - name = "Polyvitiligo" - desc = "The virus replaces the melanin in the skin with reactive pigment." - illness = "Chroma Imbalance" - stealth = -1 - resistance = 3 - stage_speed = 1 - transmittable = 2 - level = 5 - severity = 1 - symptom_delay_min = 7 - symptom_delay_max = 14 - -/datum/symptom/polyvitiligo/Activate(datum/disease/advance/A) - . = ..() - if(!.) - return - var/mob/living/M = A.affected_mob - switch(A.stage) - if(5) - var/static/list/banned_reagents = list(/datum/reagent/colorful_reagent/powder/invisible, /datum/reagent/colorful_reagent/powder/white) - var/color = pick(subtypesof(/datum/reagent/colorful_reagent/powder) - banned_reagents) - if(M.reagents.total_volume <= (M.reagents.maximum_volume/10)) // no flooding humans with 1000 units of colorful reagent - M.reagents.add_reagent(color, 5) - else - if (prob(50)) // spam - M.visible_message(span_warning("[M] looks rather vibrant..."), span_notice("The colors, man, the colors...")) diff --git a/code/datums/diseases/advance/symptoms/sneeze.dm b/code/datums/diseases/advance/symptoms/sneeze.dm deleted file mode 100644 index 85f5c2d58b72..000000000000 --- a/code/datums/diseases/advance/symptoms/sneeze.dm +++ /dev/null @@ -1,70 +0,0 @@ -/*Sneezing - * Reduces stealth - * Greatly increases resistance - * No effect to stage speed - * Increases transmission tremendously - * Low level - * Bonus: Forces a spread type of AIRBORNE with extra range! -*/ -/datum/symptom/sneeze - name = "Sneezing" - desc = "The virus causes irritation of the nasal cavity, making the host sneeze occasionally. Sneezes from this symptom will spread the virus in a 4 meter cone in front of the host." - illness = "Bard Flu" - stealth = -2 - resistance = 3 - stage_speed = 0 - transmittable = 4 - level = 1 - severity = 1 - symptom_delay_min = 5 - symptom_delay_max = 35 - var/spread_range = 4 - var/cartoon_sneezing = FALSE //ah, ah, AH, AH-CHOO!! - threshold_descs = list( - "Transmission 9" = "Increases sneezing range, spreading the virus over 6 meter cone instead of over a 4 meter cone.", - "Stealth 4" = "The symptom remains hidden until active.", - "Stage Speed 17" = "The force of each sneeze catapults the host backwards, potentially stunning and lightly damaging them if they hit a wall or another person mid-flight." - ) - ///Emote cooldowns - COOLDOWN_DECLARE(sneeze_cooldown) - ///if FALSE, there is a percentage chance that the mob will emote sneezing while sneeze_cooldown is on cooldown. If TRUE, won't emote again until after the off cooldown sneeze occurs. - var/off_cooldown_sneezed = FALSE - -/datum/symptom/sneeze/Start(datum/disease/advance/active_disease) - . = ..() - if(!.) - return - if(active_disease.totalTransmittable() >= 9) //longer spread range - spread_range = 6 - if(active_disease.totalStealth() >= 4) - suppress_warning = TRUE - if(active_disease.totalStageSpeed() >= 17) //Yep, stage speed 17, not stage speed 7. This is a big boy threshold (effect), like the language-scrambling transmission one for the voice change symptom. - cartoon_sneezing = TRUE //for a really fun time, distribute a disease with this threshold met while the gravity generator is down - -/datum/symptom/sneeze/Activate(datum/disease/advance/active_disease) - . = ..() - if(!.) - return - var/mob/living/affected_mob = active_disease.affected_mob - switch(active_disease.stage) - if(1, 2, 3) - if(!suppress_warning) - affected_mob.emote("sniff") - else - if(affected_mob.CanSpreadAirborneDisease()) //don't spread germs if they covered their mouth - for(var/mob/living/exposed_mob in oview(spread_range, affected_mob)) - if(is_source_facing_target(affected_mob, exposed_mob) && disease_air_spread_walk(get_turf(affected_mob), get_turf(exposed_mob))) - exposed_mob.AirborneContractDisease(active_disease, TRUE) - if(cartoon_sneezing) //Yeah, this can fling you around even if you have a space suit helmet on. It's, uh, bluespace snot, yeah. - affected_mob.emote("sneeze") - to_chat(affected_mob, span_userdanger("You are launched violently backwards by an all-mighty sneeze!")) - var/sneeze_distance = rand(2,4) //twice as far as a normal baseball bat strike will fling you - var/turf/target = get_ranged_target_turf(affected_mob, turn(affected_mob.dir, 180), sneeze_distance) - affected_mob.throw_at(target, sneeze_distance, rand(1,4)) //with the wounds update, sneezing at 7 speed was causing peoples bones to spontaneously explode, turning cartoonish sneezing into a nightmarishly lethal GBS 2.0 outbreak - else if(COOLDOWN_FINISHED(src, sneeze_cooldown) || !COOLDOWN_FINISHED(src, sneeze_cooldown) && prob(60) && !off_cooldown_sneezed) - affected_mob.emote("sneeze") - COOLDOWN_START(src, sneeze_cooldown, 5 SECONDS) - if(!off_cooldown_sneezed && !COOLDOWN_FINISHED(src, sneeze_cooldown)) - off_cooldown_sneezed = TRUE - else - off_cooldown_sneezed = FALSE diff --git a/code/datums/diseases/advance/symptoms/species.dm b/code/datums/diseases/advance/symptoms/species.dm deleted file mode 100644 index cb782f36ded5..000000000000 --- a/code/datums/diseases/advance/symptoms/species.dm +++ /dev/null @@ -1,50 +0,0 @@ -/* Necrotic Metabolism - * Increases stealth - * Reduces resistance - * Slightly increases stage speed - * No effect to transmissibility - * Critical level - * Bonus: Infected corpses spread disease and undead species are infectable -*/ -/datum/symptom/undead_adaptation - name = "Necrotic Metabolism" - desc = "The virus is able to thrive and act even within dead hosts." - stealth = 2 - resistance = -2 - stage_speed = 1 - transmittable = 0 - level = 5 - severity = 0 - -/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 - -/* Inorganic Biology - * Slight stealth reduction - * Tremendous resistance increase - * Reduces stage speed - * Greatly increases transmissibility - * Critical level - * Bonus: Enables infection of mineral biotype species -*/ -/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." - stealth = -1 - resistance = 4 - stage_speed = -2 - transmittable = 3 - level = 5 - severity = 0 - -/datum/symptom/inorganic_adaptation/OnAdd(datum/disease/advance/A) - A.infectable_biotypes |= MOB_MINERAL | MOB_ROBOTIC // Plasmamen, golems, and androids. - -/datum/symptom/inorganic_adaptation/OnRemove(datum/disease/advance/A) - A.infectable_biotypes &= ~(MOB_MINERAL | MOB_ROBOTIC) - diff --git a/code/datums/diseases/advance/symptoms/symptoms.dm b/code/datums/diseases/advance/symptoms/symptoms.dm index ceda0f9c1d34..984f96a6219e 100644 --- a/code/datums/diseases/advance/symptoms/symptoms.dm +++ b/code/datums/diseases/advance/symptoms/symptoms.dm @@ -38,14 +38,6 @@ ///If this symptom can appear from /datum/disease/advance/GenerateSymptoms() var/naturally_occuring = TRUE -/datum/symptom/New() - var/list/S = SSdisease.list_symptoms - for(var/i = 1; i <= S.len; i++) - if(type == S[i]) - id = "[i]" - return - CRASH("We couldn't assign an ID!") - ///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) @@ -77,6 +69,10 @@ new_symp.name = name new_symp.id = id new_symp.neutered = neutered + new_symp.multiplier = multiplier + new_symp.chance = chance + new_symp.max_chance = max_chance + new_symp.max_multiplier = max_multiplier return new_symp /datum/symptom/proc/generate_threshold_desc() diff --git a/code/datums/diseases/advance/symptoms/viral.dm b/code/datums/diseases/advance/symptoms/viral.dm deleted file mode 100644 index 5bcd60dc1d28..000000000000 --- a/code/datums/diseases/advance/symptoms/viral.dm +++ /dev/null @@ -1,34 +0,0 @@ -/*Viral adaptation - * Greatly increases stealth - * Tremendous buff for resistance - * Greatly decreases stage speed - * No effect to transmissibility - * - * Bonus: Buffs resistance & stealth. Extremely useful for buffing viruses -*/ -/datum/symptom/viraladaptation - name = "Viral self-adaptation" - desc = "The virus mimics the function of normal body cells, becoming harder to spot and to eradicate, but reducing its speed." - stealth = 3 - resistance = 5 - stage_speed = -3 - transmittable = 0 - level = 3 - -/*Viral evolution - * Reduces stealth - * Greatly reduces resistance - * Tremendous buff for stage speed - * Greatly increases transmissibility - * - * Bonus: Buffs transmission and speed. Extremely useful for buffing viruse* -*/ -/datum/symptom/viralevolution - name = "Viral evolutionary acceleration" - desc = "The virus quickly adapts to spread as fast as possible both outside and inside a host. \ - This, however, makes the virus easier to spot, and less able to fight off a cure." - stealth = -2 - resistance = -3 - stage_speed = 5 - transmittable = 3 - level = 3 diff --git a/code/datums/diseases/advance/symptoms/vision.dm b/code/datums/diseases/advance/symptoms/vision.dm deleted file mode 100644 index f6cbddbd6680..000000000000 --- a/code/datums/diseases/advance/symptoms/vision.dm +++ /dev/null @@ -1,80 +0,0 @@ -/*Hyphema (Eye bleeding) - * Slightly reduces stealth - * Tremendously reduces resistance - * Tremendously reduces stage speed - * Greatly reduces transmissibility - * Critical level - * Bonus: Causes blindness. -*/ -/datum/symptom/visionloss - name = "Hyphema" - desc = "Sufferers exhibit dangerously low levels of frames per second in the eyes, leading to damage and eventually blindness." - illness = "Diluted Pupils" - stealth = -1 - resistance = -4 - stage_speed = -4 - transmittable = -3 - level = 5 - severity = 5 - base_message_chance = 50 - symptom_delay_min = 25 - symptom_delay_max = 80 - threshold_descs = list( - "Resistance 12" = "Weakens extraocular muscles, eventually leading to complete detachment of the eyes.", - "Stealth 4" = "The symptom remains hidden until active.", - ) - - /// At max stage: If FALSE, cause blindness. If TRUE, cause their eyes to fall out. - var/remove_eyes = FALSE - -/datum/symptom/visionloss/Start(datum/disease/advance/A) - . = ..() - if(!.) - return - if(A.totalStealth() >= 4) - suppress_warning = TRUE - if(A.totalResistance() >= 12) //goodbye eyes - remove_eyes = TRUE - -/datum/symptom/visionloss/Activate(datum/disease/advance/source_disease) - . = ..() - if(!.) - return - var/mob/living/carbon/ill_mob = source_disease.affected_mob - var/obj/item/organ/internal/eyes/eyes = ill_mob.get_organ_slot(ORGAN_SLOT_EYES) - if(!eyes) - return // can't do much - - switch(source_disease.stage) - if(1, 2) - if(prob(base_message_chance) && !suppress_warning) - to_chat(ill_mob, span_warning("Your eyes itch.")) - - if(3, 4) - to_chat(ill_mob, span_boldwarning("Your eyes burn!")) - ill_mob.set_eye_blur_if_lower(20 SECONDS) - eyes.apply_organ_damage(1) - - else - ill_mob.set_eye_blur_if_lower(40 SECONDS) - eyes.apply_organ_damage(5) - - // Applies nearsighted at minimum - if(!ill_mob.is_nearsighted_from(EYE_DAMAGE) && eyes.damage <= eyes.low_threshold) - eyes.set_organ_damage(eyes.low_threshold) - - if(prob(eyes.damage - eyes.low_threshold + 1)) - if(remove_eyes) - ill_mob.visible_message( - span_warning("[ill_mob]'s eyes fall out of their sockets!"), - span_userdanger("Your eyes fall out of their sockets!"), - ) - eyes.Remove(ill_mob) - eyes.forceMove(get_turf(ill_mob)) - - else if(!ill_mob.is_blind_from(EYE_DAMAGE)) - to_chat(ill_mob, span_userdanger("You go blind!")) - eyes.apply_organ_damage(eyes.maxHealth) - - else - to_chat(ill_mob, span_userdanger("Your eyes burn horrifically!")) diff --git a/code/datums/diseases/advance/symptoms/vomit.dm b/code/datums/diseases/advance/symptoms/vomit.dm deleted file mode 100644 index 92bca8cfbf5e..000000000000 --- a/code/datums/diseases/advance/symptoms/vomit.dm +++ /dev/null @@ -1,55 +0,0 @@ -/*Vomiting - * Reduces stealth - * Slight resistance reduction - * Slight stage speed reduction - * Increases transmissibility - * Bonus : Forces the affected mob to vomit! Makes the affected mob lose nutrition and heal toxin damage -and your disease can spread via people walking on vomit. -*/ - -/datum/symptom/vomit - name = "Vomiting" - desc = "The virus causes nausea and irritates the stomach, causing occasional vomit." - illness = "Cyclonic Irritation" - stealth = -2 - resistance = -1 - stage_speed = -1 - transmittable = 2 - level = 3 - severity = 3 - base_message_chance = 100 - symptom_delay_min = 25 - symptom_delay_max = 80 - var/vomit_blood = FALSE - var/proj_vomit = 0 - threshold_descs = list( - "Resistance 7" = "Host will vomit blood, causing internal damage.", - "Transmission 7" = "Host will projectile vomit, increasing vomiting range.", - "Stealth 4" = "The symptom remains hidden until active." - ) - -/datum/symptom/vomit/Start(datum/disease/advance/A) - . = ..() - if(!.) - return - if(A.totalStealth() >= 4) - suppress_warning = TRUE - if(A.totalResistance() >= 7) //blood vomit - vomit_blood = TRUE - if(A.totalTransmittable() >= 7) //projectile vomit - proj_vomit = 5 - -/datum/symptom/vomit/Activate(datum/disease/advance/A) - . = ..() - if(!.) - return - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2, 3, 4) - if(prob(base_message_chance) && !suppress_warning) - to_chat(M, span_warning("[pick("You feel nauseated.", "You feel like you're going to throw up!")]")) - else - vomit(M) - -/datum/symptom/vomit/proc/vomit(mob/living/carbon/M) - M.vomit(20, vomit_blood, distance = proj_vomit) diff --git a/code/datums/diseases/advance/symptoms/weight.dm b/code/datums/diseases/advance/symptoms/weight.dm deleted file mode 100644 index 86fbd75a6d15..000000000000 --- a/code/datums/diseases/advance/symptoms/weight.dm +++ /dev/null @@ -1,44 +0,0 @@ -/*Weight Loss - * Reduces stealth - * Increases resistance - * Reduces stage speed - * Reduces transmissibility - * Bonus: Drains nutrition from the host -*/ -/datum/symptom/weight_loss - name = "Weight Loss" - desc = "The virus mutates the host's metabolism, making it almost unable to gain nutrition from food." - illness = "Placid Reflux" - stealth = -2 - resistance = 2 - stage_speed = -2 - transmittable = -2 - level = 3 - severity = 3 - base_message_chance = 100 - symptom_delay_min = 15 - symptom_delay_max = 45 - threshold_descs = list( - "Stealth 4" = "The symptom is less noticeable." - ) - -/datum/symptom/weight_loss/Start(datum/disease/advance/A) - . = ..() - if(!.) - return - if(A.totalStealth() >= 4) //warn less often - base_message_chance = 25 - -/datum/symptom/weight_loss/Activate(datum/disease/advance/A) - . = ..() - if(!.) - return - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2, 3, 4) - if(prob(base_message_chance)) - to_chat(M, span_warning("[pick("You feel hungry.", "You crave for food.")]")) - else - to_chat(M, span_warning("[pick("So hungry...", "You'd kill someone for a bite of food...", "Hunger cramps seize you...")]")) - M.overeatduration = max(M.overeatduration - 200 SECONDS, 0) - M.adjust_nutrition(-100) diff --git a/code/datums/diseases/advance/symptoms/youth.dm b/code/datums/diseases/advance/symptoms/youth.dm deleted file mode 100644 index 57637e73a5f0..000000000000 --- a/code/datums/diseases/advance/symptoms/youth.dm +++ /dev/null @@ -1,50 +0,0 @@ -/*Eternal Youth - * Greatly increases stealth - * Tremendous increase to resistance - * Tremendous increase to stage speed - * Tremendous reduction to transmissibility - * Critical level - * Bonus: Can be used to buff your virus -*/ - -/datum/symptom/youth - name = "Eternal Youth" - desc = "The virus becomes symbiotically connected to the cells in the host's body, preventing and reversing aging. \ - The virus, in turn, becomes more resistant, spreads faster, and is harder to spot, although it doesn't thrive as well without a host." - stealth = 3 - resistance = 4 - stage_speed = 4 - transmittable = -4 - level = 5 - base_message_chance = 100 - symptom_delay_min = 25 - symptom_delay_max = 50 - -/datum/symptom/youth/Activate(datum/disease/advance/A) - . = ..() - if(!.) - return - var/mob/living/M = A.affected_mob - if(ishuman(M)) - var/mob/living/carbon/human/H = M - switch(A.stage) - if(1) - if(H.age > 41) - H.age = 41 - to_chat(H, span_notice("You haven't had this much energy in years!")) - if(2) - if(H.age > 36) - H.age = 36 - to_chat(H, span_notice("You're suddenly in a good mood.")) - if(3) - if(H.age > 31) - H.age = 31 - to_chat(H, span_notice("You begin to feel more lithe.")) - if(4) - if(H.age > 26) - H.age = 26 - to_chat(H, span_notice("You feel reinvigorated.")) - if(5) - if(H.age > 21) - H.age = 21 - to_chat(H, span_notice("You feel like you can take on the world!")) diff --git a/code/datums/diseases/anxiety.dm b/code/datums/diseases/anxiety.dm deleted file mode 100644 index 9f912d9803fc..000000000000 --- a/code/datums/diseases/anxiety.dm +++ /dev/null @@ -1,44 +0,0 @@ -/datum/disease/anxiety - name = "Severe Anxiety" - form = "Infection" - max_stages = 4 - spread_text = "On contact" - spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_SKIN | DISEASE_SPREAD_CONTACT_FLUIDS - cure_text = "Ethanol" - cures = list(/datum/reagent/consumable/ethanol) - agent = "Excess Lepidopticides" - viable_mobtypes = list(/mob/living/carbon/human) - desc = "If left untreated subject will regurgitate butterflies." - severity = DISEASE_SEVERITY_MINOR - - -/datum/disease/anxiety/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - switch(stage) - if(2) //also changes say, see say.dm - if(SPT_PROB(2.5, seconds_per_tick)) - to_chat(affected_mob, span_notice("You feel anxious.")) - if(3) - if(SPT_PROB(5, seconds_per_tick)) - to_chat(affected_mob, span_notice("Your stomach flutters.")) - if(SPT_PROB(2.5, seconds_per_tick)) - to_chat(affected_mob, span_notice("You feel panicky.")) - if(SPT_PROB(1, seconds_per_tick)) - to_chat(affected_mob, span_danger("You're overtaken with panic!")) - affected_mob.adjust_confusion(rand(2 SECONDS, 3 SECONDS)) - if(4) - if(SPT_PROB(5, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel butterflies in your stomach.")) - if(SPT_PROB(2.5, seconds_per_tick)) - affected_mob.visible_message(span_danger("[affected_mob] stumbles around in a panic."), \ - span_userdanger("You have a panic attack!")) - affected_mob.adjust_confusion(rand(6 SECONDS, 8 SECONDS)) - affected_mob.adjust_jitter(rand(12 SECONDS, 16 SECONDS)) - if(SPT_PROB(1, seconds_per_tick)) - affected_mob.visible_message(span_danger("[affected_mob] coughs up butterflies!"), \ - span_userdanger("You cough up butterflies!")) - new /mob/living/basic/butterfly(affected_mob.loc) - new /mob/living/basic/butterfly(affected_mob.loc) diff --git a/code/datums/diseases/beesease.dm b/code/datums/diseases/beesease.dm deleted file mode 100644 index f2ef68b4e499..000000000000 --- a/code/datums/diseases/beesease.dm +++ /dev/null @@ -1,41 +0,0 @@ -/datum/disease/beesease - name = "Beesease" - form = "Infection" - max_stages = 4 - spread_text = "On contact" - spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_SKIN | DISEASE_SPREAD_CONTACT_FLUIDS - cure_text = "Sugar" - cures = list(/datum/reagent/consumable/sugar) - agent = "Apidae Infection" - viable_mobtypes = list(/mob/living/carbon/human) - desc = "If left untreated subject will regurgitate bees." - severity = DISEASE_SEVERITY_MEDIUM - infectable_biotypes = MOB_ORGANIC|MOB_UNDEAD //bees nesting in corpses - - -/datum/disease/beesease/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - switch(stage) - if(2) //also changes say, see say.dm - if(SPT_PROB(1, seconds_per_tick)) - to_chat(affected_mob, span_notice("You taste honey in your mouth.")) - if(3) - if(SPT_PROB(5, seconds_per_tick)) - to_chat(affected_mob, span_notice("Your stomach rumbles.")) - if(SPT_PROB(1, seconds_per_tick)) - to_chat(affected_mob, span_danger("Your stomach stings painfully.")) - if(prob(20)) - affected_mob.adjustToxLoss(2) - if(4) - if(SPT_PROB(5, seconds_per_tick)) - affected_mob.visible_message(span_danger("[affected_mob] buzzes."), \ - span_userdanger("Your stomach buzzes violently!")) - if(SPT_PROB(2.5, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel something moving in your throat.")) - if(SPT_PROB(0.5, seconds_per_tick)) - affected_mob.visible_message(span_danger("[affected_mob] coughs up a swarm of bees!"), \ - span_userdanger("You cough up a swarm of bees!")) - new /mob/living/basic/bee(affected_mob.loc) diff --git a/code/datums/diseases/cold.dm b/code/datums/diseases/cold.dm index 5aafb5d12e6f..68b9f5fdd618 100644 --- a/code/datums/diseases/cold.dm +++ b/code/datums/diseases/cold.dm @@ -3,7 +3,7 @@ desc = "If left untreated the subject will contract the flu." max_stages = 3 cure_text = "Rest & Spaceacillin" - cures = list(/datum/reagent/medicine/spaceacillin) + cures = list(/datum/reagent/medicine/antipathogenic/spaceacillin) agent = "XY-rhinovirus" viable_mobtypes = list(/mob/living/carbon/human) spreading_modifier = 0.5 diff --git a/code/datums/diseases/cold9.dm b/code/datums/diseases/cold9.dm index 543a021eee86..c97fbc39f2e5 100644 --- a/code/datums/diseases/cold9.dm +++ b/code/datums/diseases/cold9.dm @@ -4,7 +4,7 @@ spread_text = "On contact" spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_SKIN | DISEASE_SPREAD_CONTACT_FLUIDS cure_text = "Common Cold Anti-bodies & Spaceacillin" - cures = list(/datum/reagent/medicine/spaceacillin) + cures = list(/datum/reagent/medicine/antipathogenic/spaceacillin) agent = "ICE9-rhinovirus" viable_mobtypes = list(/mob/living/carbon/human) desc = "If left untreated the subject will slow, as if partly frozen." diff --git a/code/datums/diseases/decloning.dm b/code/datums/diseases/decloning.dm deleted file mode 100644 index 0b7c74f9a031..000000000000 --- a/code/datums/diseases/decloning.dm +++ /dev/null @@ -1,65 +0,0 @@ -/datum/disease/decloning - form = "Virus" - name = "Cellular Degeneration" - max_stages = 5 - stage_prob = 0.5 - cure_text = "Rezadone or death." - agent = "Severe Genetic Damage" - viable_mobtypes = list(/mob/living/carbon/human) - desc = @"If left untreated the subject will [REDACTED]!" - severity = "Dangerous!" - cures = list(/datum/reagent/medicine/rezadone) - disease_flags = CAN_CARRY|CAN_RESIST - spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS - spread_text = "Organic meltdown" - process_dead = TRUE - -/datum/disease/decloning/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - if(affected_mob.stat == DEAD) - cure() - return FALSE - - switch(stage) - if(2) - if(SPT_PROB(1, seconds_per_tick)) - affected_mob.emote("itch") - if(SPT_PROB(1, seconds_per_tick)) - affected_mob.emote("yawn") - if(3) - if(SPT_PROB(1, seconds_per_tick)) - affected_mob.emote("itch") - if(SPT_PROB(1, seconds_per_tick)) - affected_mob.emote("drool") - if(SPT_PROB(1.5, seconds_per_tick)) - affected_mob.adjustCloneLoss(1, FALSE) - if(SPT_PROB(1, seconds_per_tick)) - to_chat(affected_mob, span_danger("Your skin feels strange.")) - - if(4) - if(SPT_PROB(1, seconds_per_tick)) - affected_mob.emote("itch") - if(SPT_PROB(1, seconds_per_tick)) - affected_mob.emote("drool") - if(SPT_PROB(2.5, seconds_per_tick)) - affected_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1, 170) - affected_mob.adjustCloneLoss(2, FALSE) - if(SPT_PROB(7.5, seconds_per_tick)) - affected_mob.adjust_stutter(6 SECONDS) - if(5) - if(SPT_PROB(1, seconds_per_tick)) - affected_mob.emote("itch") - if(SPT_PROB(1, seconds_per_tick)) - affected_mob.emote("drool") - if(SPT_PROB(2.5, seconds_per_tick)) - to_chat(affected_mob, span_danger("Your skin starts degrading!")) - if(SPT_PROB(5, seconds_per_tick)) - affected_mob.adjustCloneLoss(5, FALSE) - affected_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2, 170) - if(affected_mob.cloneloss >= 100) - affected_mob.visible_message(span_danger("[affected_mob] skin turns to dust!"), span_boldwarning("Your skin turns to dust!")) - affected_mob.dust() - return FALSE diff --git a/code/datums/diseases/dna_spread.dm b/code/datums/diseases/dna_spread.dm deleted file mode 100644 index 17faeda6243e..000000000000 --- a/code/datums/diseases/dna_spread.dm +++ /dev/null @@ -1,78 +0,0 @@ -/datum/disease/dnaspread - name = "Space Retrovirus" - max_stages = 4 - spread_text = "On contact" - spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_SKIN | DISEASE_SPREAD_CONTACT_FLUIDS - cure_text = "Mutadone" - cures = list(/datum/reagent/medicine/mutadone) - disease_flags = CAN_CARRY|CAN_RESIST|CURABLE - agent = "S4E1 retrovirus" - viable_mobtypes = list(/mob/living/carbon/human) - var/datum/dna/original_dna = null - var/transformed = 0 - desc = "This disease transplants the genetic code of the initial vector into new hosts." - severity = DISEASE_SEVERITY_MEDIUM - - -/datum/disease/dnaspread/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - if(!affected_mob.dna) - cure() - return FALSE - - if((NOTRANSSTING in affected_mob.dna.species.species_traits) || (NO_DNA_COPY in affected_mob.dna.species.species_traits)) //Only species that can be spread by transformation sting can be spread by the retrovirus - cure() - return FALSE - - if(!strain_data["dna"]) - //Absorbs the target DNA. - strain_data["dna"] = new affected_mob.dna.type - affected_mob.dna.copy_dna(strain_data["dna"]) - carrier = TRUE - stage = 4 - return - - switch(stage) - if(2, 3) //Pretend to be a cold and give time to spread. - if(SPT_PROB(4, seconds_per_tick)) - affected_mob.emote("sneeze") - if(SPT_PROB(4, seconds_per_tick)) - affected_mob.emote("cough") - if(SPT_PROB(0.5, seconds_per_tick)) - to_chat(affected_mob, span_danger("Your muscles ache.")) - if(prob(20)) - affected_mob.take_bodypart_damage(1, updating_health = FALSE) - if(SPT_PROB(0.5, seconds_per_tick)) - to_chat(affected_mob, span_danger("Your stomach hurts.")) - if(prob(20)) - affected_mob.adjustToxLoss(2, FALSE) - if(4) - if(!transformed && !carrier) - //Save original dna for when the disease is cured. - original_dna = new affected_mob.dna.type - affected_mob.dna.copy_dna(original_dna) - - to_chat(affected_mob, span_danger("You don't feel like yourself..")) - var/datum/dna/transform_dna = strain_data["dna"] - - transform_dna.transfer_identity(affected_mob, transfer_SE = 1) - affected_mob.real_name = affected_mob.dna.real_name - affected_mob.updateappearance(mutcolor_update=1) - affected_mob.domutcheck() - - transformed = 1 - carrier = 1 //Just chill out at stage 4 - - -/datum/disease/dnaspread/Destroy() - if (original_dna && transformed && affected_mob) - original_dna.transfer_identity(affected_mob, transfer_SE = 1) - affected_mob.real_name = affected_mob.dna.real_name - affected_mob.updateappearance(mutcolor_update=1) - affected_mob.domutcheck() - - to_chat(affected_mob, span_notice("You feel more like yourself.")) - return ..() diff --git a/code/datums/diseases/flu.dm b/code/datums/diseases/flu.dm index 0da9a5b8e92d..a5324475de36 100644 --- a/code/datums/diseases/flu.dm +++ b/code/datums/diseases/flu.dm @@ -3,7 +3,7 @@ max_stages = 3 spread_text = "Airborne" cure_text = "Spaceacillin" - cures = list(/datum/reagent/medicine/spaceacillin) + cures = list(/datum/reagent/medicine/antipathogenic/spaceacillin) cure_chance = 5 agent = "H13N1 flu virion" viable_mobtypes = list(/mob/living/carbon/human) diff --git a/code/datums/diseases/fluspanish.dm b/code/datums/diseases/fluspanish.dm index 109b7ac470b5..225bfd6ea565 100644 --- a/code/datums/diseases/fluspanish.dm +++ b/code/datums/diseases/fluspanish.dm @@ -3,7 +3,7 @@ max_stages = 3 spread_text = "Airborne" cure_text = "Spaceacillin & Anti-bodies to the common flu" - cures = list(/datum/reagent/medicine/spaceacillin) + cures = list(/datum/reagent/medicine/antipathogenic/spaceacillin) cure_chance = 5 agent = "1nqu1s1t10n flu virion" viable_mobtypes = list(/mob/living/carbon/human) diff --git a/code/datums/diseases/heart_failure.dm b/code/datums/diseases/heart_failure.dm deleted file mode 100644 index 37d3bc243aea..000000000000 --- a/code/datums/diseases/heart_failure.dm +++ /dev/null @@ -1,70 +0,0 @@ -/datum/disease/heart_failure - form = "Condition" - name = "Myocardial Infarction" - max_stages = 5 - stage_prob = 1 - cure_text = "Heart replacement surgery to cure. Defibrillation (or as a last resort, uncontrolled electric shocking) may also be effective after the onset of cardiac arrest. Penthrite can also mitigate cardiac arrest." - agent = "Shitty Heart" - viable_mobtypes = list(/mob/living/carbon/human) - spreading_modifier = 1 - desc = "If left untreated the subject will die!" - severity = "Dangerous!" - disease_flags = CAN_CARRY|CAN_RESIST - spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS - spread_text = "Organ failure" - visibility_flags = HIDDEN_PANDEMIC - required_organs = list(/obj/item/organ/internal/heart) - bypasses_immunity = TRUE // Immunity is based on not having an appendix; this isn't a virus - var/sound = FALSE - -/datum/disease/heart_failure/Copy() - var/datum/disease/heart_failure/D = ..() - D.sound = sound - return D - - -/datum/disease/heart_failure/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - if(!affected_mob.can_heartattack()) - cure() - return FALSE - - switch(stage) - if(1 to 2) - if(SPT_PROB(1, seconds_per_tick)) - to_chat(affected_mob, span_warning("You feel [pick("discomfort", "pressure", "a burning sensation", "pain")] in your chest.")) - if(SPT_PROB(1, seconds_per_tick)) - to_chat(affected_mob, span_warning("You feel dizzy.")) - affected_mob.adjust_confusion(6 SECONDS) - if(SPT_PROB(1.5, seconds_per_tick)) - to_chat(affected_mob, span_warning("You feel [pick("full", "nauseated", "sweaty", "weak", "tired", "short of breath", "uneasy")].")) - if(3 to 4) - if(!sound) - affected_mob.playsound_local(affected_mob, 'sound/health/slowbeat.ogg', 40, FALSE, channel = CHANNEL_HEARTBEAT, use_reverb = FALSE) - sound = TRUE - if(SPT_PROB(1.5, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel a sharp pain in your chest!")) - if(prob(25)) - affected_mob.vomit(95) - affected_mob.emote("cough") - affected_mob.Paralyze(40) - affected_mob.losebreath += 4 - if(SPT_PROB(1.5, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel very weak and dizzy...")) - affected_mob.adjust_confusion(8 SECONDS) - affected_mob.stamina.adjust(-40, FALSE) - affected_mob.emote("cough") - if(5) - affected_mob.stop_sound_channel(CHANNEL_HEARTBEAT) - affected_mob.playsound_local(affected_mob, 'sound/effects/singlebeat.ogg', 100, FALSE, use_reverb = FALSE) - if(affected_mob.stat == CONSCIOUS) - affected_mob.visible_message(span_danger("[affected_mob] clutches at [affected_mob.p_their()] chest as if [affected_mob.p_their()] heart is stopping!"), \ - span_userdanger("You feel a terrible pain in your chest, as if your heart has stopped!")) - affected_mob.stamina.adjust(-60, FALSE) - affected_mob.set_heartattack(TRUE) - affected_mob.reagents.add_reagent(/datum/reagent/medicine/c2/penthrite, 3) // To give the victim a final chance to shock their heart before losing consciousness - cure() - return FALSE diff --git a/code/datums/diseases/magnitis.dm b/code/datums/diseases/magnitis.dm deleted file mode 100644 index 52156b968f9b..000000000000 --- a/code/datums/diseases/magnitis.dm +++ /dev/null @@ -1,66 +0,0 @@ -/datum/disease/magnitis - name = "Magnitis" - max_stages = 4 - spread_text = "Airborne" - cure_text = "Iron" - cures = list(/datum/reagent/iron) - agent = "Fukkos Miracos" - viable_mobtypes = list(/mob/living/carbon/human) - disease_flags = CAN_CARRY|CAN_RESIST|CURABLE - spreading_modifier = 0.75 - desc = "This disease disrupts the magnetic field of your body, making it act as if a powerful magnet. Injections of iron help stabilize the field." - severity = DISEASE_SEVERITY_MEDIUM - infectable_biotypes = MOB_ORGANIC|MOB_ROBOTIC - process_dead = TRUE - - -/datum/disease/magnitis/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - switch(stage) - if(2) - if(SPT_PROB(1, seconds_per_tick)) - to_chat(affected_mob, span_danger("Your skin tingles with energy.")) - if(SPT_PROB(1, seconds_per_tick)) - for(var/obj/nearby_object in orange(2, affected_mob)) - if(nearby_object.anchored || !(nearby_object.flags_1 & CONDUCT_1)) - continue - var/move_dir = get_dir(nearby_object, affected_mob) - nearby_object.Move(get_step(nearby_object, move_dir), move_dir) - for(var/mob/living/silicon/nearby_silicon in orange(2, affected_mob)) - if(isAI(nearby_silicon)) - continue - var/move_dir = get_dir(nearby_silicon, affected_mob) - nearby_silicon.Move(get_step(nearby_silicon, move_dir), move_dir) - if(3) - if(SPT_PROB(1, seconds_per_tick)) - to_chat(affected_mob, span_danger("Your hair stands on end.")) - if(SPT_PROB(2, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel a light shock course through your body.")) - for(var/obj/nearby_object in orange(4, affected_mob)) - if(nearby_object.anchored || !(nearby_object.flags_1 & CONDUCT_1)) - continue - for(var/i in 1 to rand(1, 2)) - nearby_object.throw_at(affected_mob, 4, 3) - for(var/mob/living/silicon/nearby_silicon in orange(4, affected_mob)) - if(isAI(nearby_silicon)) - continue - for(var/i in 1 to rand(1, 2)) - nearby_silicon.throw_at(affected_mob, 4, 3) - if(4) - if(SPT_PROB(1, seconds_per_tick)) - to_chat(affected_mob, span_danger("You query upon the nature of miracles.")) - if(SPT_PROB(4, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel a powerful shock course through your body.")) - for(var/obj/nearby_object in orange(6, affected_mob)) - if(nearby_object.anchored || !(nearby_object.flags_1 & CONDUCT_1)) - continue - for(var/i in 1 to rand(1, 3)) - nearby_object.throw_at(affected_mob, 6, 5) // I really wanted to use addtimers to stagger out when everything gets thrown but it would probably cause a lot of lag. - for(var/mob/living/silicon/nearby_silicon in orange(6, affected_mob)) - if(isAI(nearby_silicon)) - continue - for(var/i in 1 to rand(1, 3)) - nearby_silicon.throw_at(affected_mob, 6, 5) diff --git a/code/datums/diseases/pierrot_throat.dm b/code/datums/diseases/pierrot_throat.dm deleted file mode 100644 index d24afb6fe5b1..000000000000 --- a/code/datums/diseases/pierrot_throat.dm +++ /dev/null @@ -1,64 +0,0 @@ -/datum/disease/pierrot_throat - name = "Pierrot's Throat" - max_stages = 4 - spread_text = "Airborne" - cure_text = "Banana products, especially banana bread." - cures = list(/datum/reagent/consumable/banana) - cure_chance = 50 - agent = "H0NI<42 Virus" - viable_mobtypes = list(/mob/living/carbon/human) - spreading_modifier = 0.75 - desc = "If left untreated the subject will probably drive others to insanity." - severity = DISEASE_SEVERITY_MEDIUM - - -/datum/disease/pierrot_throat/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - switch(stage) - if(1) - if(SPT_PROB(5, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel a little silly.")) - if(2) - if(SPT_PROB(5, seconds_per_tick)) - to_chat(affected_mob, span_danger("You start seeing rainbows.")) - if(3) - if(SPT_PROB(5, seconds_per_tick)) - to_chat(affected_mob, span_danger("Your thoughts are interrupted by a loud HONK!")) - if(4) - if(SPT_PROB(2.5, seconds_per_tick)) - affected_mob.say( pick( list("HONK!", "Honk!", "Honk.", "Honk?", "Honk!!", "Honk?!", "Honk...") ) , forced = "pierrot's throat") - - -/datum/disease/pierrot_throat/after_add() - RegisterSignal(affected_mob, COMSIG_MOB_SAY, PROC_REF(handle_speech)) - - -/datum/disease/pierrot_throat/proc/handle_speech(datum/source, list/speech_args) - SIGNAL_HANDLER - - var/message = speech_args[SPEECH_MESSAGE] - var/list/split_message = splittext(message, " ") //List each word in the message - var/applied = 0 - for (var/i in 1 to length(split_message)) - if(prob(3 * stage)) //Stage 1: 3% Stage 2: 6% Stage 3: 9% Stage 4: 12% - if(findtext(split_message[i], "*") || findtext(split_message[i], ";") || findtext(split_message[i], ":")) - continue - split_message[i] = "HONK" - if (applied++ > stage) - break - if (applied) - speech_args[SPEECH_SPANS] |= SPAN_CLOWN // a little bonus - message = jointext(split_message, " ") - speech_args[SPEECH_MESSAGE] = message - - -/datum/disease/pierrot_throat/Destroy() - UnregisterSignal(affected_mob, COMSIG_MOB_SAY) - return ..() - -/datum/disease/pierrot_throat/remove_disease() - UnregisterSignal(affected_mob, COMSIG_MOB_SAY) - return ..() diff --git a/code/datums/diseases/retrovirus.dm b/code/datums/diseases/retrovirus.dm deleted file mode 100644 index 4c012eaaf805..000000000000 --- a/code/datums/diseases/retrovirus.dm +++ /dev/null @@ -1,87 +0,0 @@ -/datum/disease/dna_retrovirus - name = "Retrovirus" - max_stages = 4 - spread_text = "Contact" - spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_SKIN | DISEASE_SPREAD_CONTACT_FLUIDS - cure_text = "Rest or an injection of mutadone" - cure_chance = 3 - agent = "" - viable_mobtypes = list(/mob/living/carbon/human) - desc = "A DNA-altering retrovirus that scrambles the structural and unique enzymes of a host constantly." - severity = DISEASE_SEVERITY_HARMFUL - spreading_modifier = 0.4 - stage_prob = 1 - var/restcure = 0 - -/datum/disease/dna_retrovirus/New() - ..() - agent = "Virus class [pick("A","B","C","D","E","F")][pick("A","B","C","D","E","F")]-[rand(50,300)]" - if(prob(40)) - cures = list(/datum/reagent/medicine/mutadone) - else - restcure = 1 - -/datum/disease/dna_retrovirus/Copy() - var/datum/disease/dna_retrovirus/D = ..() - D.restcure = restcure - return D - -/datum/disease/dna_retrovirus/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - switch(stage) - if(1) - if(SPT_PROB(4, seconds_per_tick)) - to_chat(affected_mob, span_danger("Your head hurts.")) - if(SPT_PROB(4.5, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel a tingling sensation in your chest.")) - if(SPT_PROB(4.5, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel angry.")) - if(restcure && affected_mob.body_position == LYING_DOWN && SPT_PROB(16, seconds_per_tick)) - to_chat(affected_mob, span_notice("You feel better.")) - cure() - return FALSE - if(2) - if(SPT_PROB(4, seconds_per_tick)) - to_chat(affected_mob, span_danger("Your skin feels loose.")) - if(SPT_PROB(5, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel very strange.")) - if(SPT_PROB(2, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel a stabbing pain in your head!")) - affected_mob.Unconscious(40) - if(SPT_PROB(2, seconds_per_tick)) - to_chat(affected_mob, span_danger("Your stomach churns.")) - if(restcure && affected_mob.body_position == LYING_DOWN && SPT_PROB(10, seconds_per_tick)) - to_chat(affected_mob, span_notice("You feel better.")) - cure() - return FALSE - if(3) - if(SPT_PROB(5, seconds_per_tick)) - to_chat(affected_mob, span_danger("Your entire body vibrates.")) - if(SPT_PROB(19, seconds_per_tick)) - switch(rand(1,3)) - if(1) - scramble_dna(affected_mob, 1, 0, 0, rand(15,45)) - if(2) - scramble_dna(affected_mob, 0, 1, 0, rand(15,45)) - if(3) - scramble_dna(affected_mob, 0, 0, 1, rand(15,45)) - if(restcure && affected_mob.body_position == LYING_DOWN && SPT_PROB(10, seconds_per_tick)) - to_chat(affected_mob, span_notice("You feel better.")) - cure() - return FALSE - if(4) - if(SPT_PROB(37, seconds_per_tick)) - switch(rand(1,3)) - if(1) - scramble_dna(affected_mob, 1, 0, 0, rand(50,75)) - if(2) - scramble_dna(affected_mob, 0, 1, 0, rand(50,75)) - if(3) - scramble_dna(affected_mob, 0, 0, 1, rand(50,75)) - if(restcure && affected_mob.body_position == LYING_DOWN && SPT_PROB(2.5, seconds_per_tick)) - to_chat(affected_mob, span_notice("You feel better.")) - cure() - return FALSE diff --git a/code/datums/diseases/rhumba_beat.dm b/code/datums/diseases/rhumba_beat.dm deleted file mode 100644 index 01188137915f..000000000000 --- a/code/datums/diseases/rhumba_beat.dm +++ /dev/null @@ -1,42 +0,0 @@ -/datum/disease/rhumba_beat - name = "The Rhumba Beat" - max_stages = 5 - spread_text = "On contact" - spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_SKIN | DISEASE_SPREAD_CONTACT_FLUIDS - cure_text = "Chick Chicky Boom!" - cures = list("plasma") - agent = "Unknown" - viable_mobtypes = list(/mob/living/carbon/human) - spreading_modifier = 1 - severity = DISEASE_SEVERITY_BIOHAZARD - -/datum/disease/rhumba_beat/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - switch(stage) - if(2) - if(SPT_PROB(26, seconds_per_tick)) - affected_mob.adjustFireLoss(5, FALSE) - if(SPT_PROB(0.5, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel strange...")) - if(3) - if(SPT_PROB(2.5, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel the urge to dance...")) - else if(SPT_PROB(2.5, seconds_per_tick)) - affected_mob.emote("gasp") - else if(SPT_PROB(5, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel the need to chick chicky boom...")) - if(4) - if(SPT_PROB(10, seconds_per_tick)) - if(prob(50)) - affected_mob.adjust_fire_stacks(2) - affected_mob.ignite_mob() - else - affected_mob.emote("gasp") - to_chat(affected_mob, span_danger("You feel a burning beat inside...")) - if(5) - to_chat(affected_mob, span_danger("Your body is unable to contain the Rhumba Beat...")) - if(SPT_PROB(29, seconds_per_tick)) - explosion(affected_mob, devastation_range = -1, light_impact_range = 2, flame_range = 2, flash_range = 3, adminlog = FALSE, explosion_cause = src) // This is equivalent to a lvl 1 fireball diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm deleted file mode 100644 index 43269cb2ee73..000000000000 --- a/code/datums/diseases/transformation.dm +++ /dev/null @@ -1,368 +0,0 @@ -/datum/disease/transformation - name = "Transformation" - max_stages = 5 - spread_text = "Acute" - spread_flags = DISEASE_SPREAD_SPECIAL - cure_text = "A coder's love (theoretical)." - agent = "Shenanigans" - viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/alien) - severity = DISEASE_SEVERITY_BIOHAZARD - stage_prob = 5 - visibility_flags = HIDDEN_SCANNER|HIDDEN_PANDEMIC - disease_flags = CURABLE - var/list/stage1 = list("You feel unremarkable.") - var/list/stage2 = list("You feel boring.") - var/list/stage3 = list("You feel utterly plain.") - var/list/stage4 = list("You feel white bread.") - var/list/stage5 = list("Oh the humanity!") - var/new_form = /mob/living/carbon/human - var/bantype - var/transformed_antag_datum //Do we add a specific antag datum once the transformation is complete? - -/datum/disease/transformation/Copy() - var/datum/disease/transformation/D = ..() - D.stage1 = stage1.Copy() - D.stage2 = stage2.Copy() - D.stage3 = stage3.Copy() - D.stage4 = stage4.Copy() - D.stage5 = stage5.Copy() - D.new_form = D.new_form - return D - - -/datum/disease/transformation/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - switch(stage) - if(1) - if (length(stage1) && SPT_PROB(stage_prob, seconds_per_tick)) - to_chat(affected_mob, pick(stage1)) - if(2) - if (length(stage2) && SPT_PROB(stage_prob, seconds_per_tick)) - to_chat(affected_mob, pick(stage2)) - if(3) - if (length(stage3) && SPT_PROB(stage_prob * 2, seconds_per_tick)) - to_chat(affected_mob, pick(stage3)) - if(4) - if (length(stage4) && SPT_PROB(stage_prob * 2, seconds_per_tick)) - to_chat(affected_mob, pick(stage4)) - if(5) - do_disease_transformation(affected_mob) - - -/datum/disease/transformation/proc/do_disease_transformation(mob/living/affected_mob) - if(iscarbon(affected_mob) && affected_mob.stat != DEAD) - if(length(stage5)) - to_chat(affected_mob, pick(stage5)) - if(QDELETED(affected_mob)) - return - if(HAS_TRAIT_FROM(affected_mob, TRAIT_NO_TRANSFORM, REF(src))) - return - ADD_TRAIT(affected_mob, TRAIT_NO_TRANSFORM, REF(src)) - for(var/obj/item/W in affected_mob.get_equipped_items(include_pockets = TRUE)) - affected_mob.dropItemToGround(W) - for(var/obj/item/I in affected_mob.held_items) - affected_mob.dropItemToGround(I) - var/mob/living/new_mob = new new_form(affected_mob.loc) - if(istype(new_mob)) - if(bantype && is_banned_from(affected_mob.ckey, bantype)) - replace_banned_player(new_mob) - new_mob.set_combat_mode(TRUE) - if(affected_mob.mind) - affected_mob.mind.transfer_to(new_mob) - else - new_mob.key = affected_mob.key - if(transformed_antag_datum) - new_mob.mind.add_antag_datum(transformed_antag_datum) - new_mob.name = affected_mob.real_name - new_mob.real_name = new_mob.name - qdel(affected_mob) - -/datum/disease/transformation/proc/replace_banned_player(mob/living/new_mob) // This can run well after the mob has been transferred, so need a handle on the new mob to kill it if needed. - set waitfor = FALSE - - var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [affected_mob.real_name]?", bantype, bantype, 5 SECONDS, affected_mob) - if(LAZYLEN(candidates)) - var/mob/dead/observer/C = pick(candidates) - to_chat(affected_mob, span_userdanger("Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")) - message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(affected_mob)]) to replace a jobbanned player.") - affected_mob.ghostize(FALSE) - affected_mob.key = C.key - else - to_chat(new_mob, span_userdanger("Your mob has been claimed by death! Appeal your job ban if you want to avoid this in the future!")) - new_mob.investigate_log("has been killed because there was no one to replace them as a job-banned player.", INVESTIGATE_DEATHS) - new_mob.death() - if (!QDELETED(new_mob)) - new_mob.ghostize(can_reenter_corpse = FALSE) - new_mob.key = null - -/datum/disease/transformation/jungle_flu - name = "Jungle Flu" - cure_text = "Death." - cures = list(/datum/reagent/medicine/adminordrazine) - spread_text = "Unknown" - spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS - viable_mobtypes = list(/mob/living/carbon/human) - spreading_modifier = 1 - cure_chance = 0.5 - disease_flags = CAN_CARRY|CAN_RESIST - desc = "A neutered but still dangerous descendent of the ancient \"Jungle Fever\", victims will eventually genetically backtrack into a primate. \ - Luckily, once turned the new monkey will not gain the rabies-like rage of the fever." - severity = DISEASE_SEVERITY_BIOHAZARD - stage_prob = 2 - visibility_flags = NONE - agent = "Kongey Vibrion M-909" - new_form = /mob/living/carbon/human/species/monkey - - stage1 = list() - stage2 = list() - stage3 = list() - stage4 = list( - span_warning("You breathe through your mouth."), - span_warning("You have a craving for bananas."), - span_warning("Your back hurts."), - span_warning("Your mind feels clouded."), - ) - stage5 = list(span_warning("You feel like monkeying around.")) - -/datum/disease/transformation/jungle_flu/do_disease_transformation(mob/living/carbon/affected_mob) - affected_mob.monkeyize() - -/datum/disease/transformation/jungle_flu/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - switch(stage) - if(2) - if(SPT_PROB(1, seconds_per_tick)) - to_chat(affected_mob, span_notice("Your [pick("arm", "back", "elbow", "head", "leg")] itches.")) - if(3) - if(SPT_PROB(2, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel a stabbing pain in your head.")) - affected_mob.adjust_confusion(10 SECONDS) - if(4) - if(SPT_PROB(1.5, seconds_per_tick)) - affected_mob.say(pick("Eeee!", "Eeek, ook ook!", "Eee-eeek!", "Ungh, ungh."), forced = "jungle fever") - -/datum/disease/transformation/robot - - name = "Robotic Transformation" - cure_text = "An injection of copper." - cures = list(/datum/reagent/copper) - cure_chance = 2.5 - agent = "R2D2 Nanomachines" - desc = "This disease, actually acute nanomachine infection, converts the victim into a cyborg." - severity = DISEASE_SEVERITY_BIOHAZARD - visibility_flags = NONE - stage1 = list() - stage2 = list(span_danger("Beep...boop.."), "Your joints feel stiff.") - stage3 = list( - span_danger("You can feel something move...inside."), - span_danger("Your joints feel very stiff."), - span_warning("Your skin feels loose."), - ) - stage4 = list(span_danger("You can feel... something...inside you."), span_danger("Your skin feels very loose."),) - stage5 = list(span_danger("Your skin feels as if it's about to burst off!")) - new_form = /mob/living/silicon/robot - infectable_biotypes = MOB_ORGANIC|MOB_UNDEAD|MOB_ROBOTIC - bantype = JOB_CYBORG - - -/datum/disease/transformation/robot/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - switch(stage) - if(3) - if (SPT_PROB(4, seconds_per_tick)) - affected_mob.say(pick("beep, beep!", "Beep, boop", "Boop...bop"), forced = "robotic transformation") - if (SPT_PROB(2, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel a stabbing pain in your head.")) - affected_mob.Unconscious(40) - if(4) - if (SPT_PROB(10, seconds_per_tick)) - affected_mob.say(pick("beep, beep!", "Boop bop boop beep.", "I wwwaaannntt tttoo dddiiieeee...", "kkkiiiill mmme"), forced = "robotic transformation") - - -/datum/disease/transformation/xeno - - name = "Xenomorph Transformation" - cure_text = "Spaceacillin & Glycerol" - cures = list(/datum/reagent/medicine/spaceacillin, /datum/reagent/glycerol) - cure_chance = 2.5 - agent = "Rip-LEY Alien Microbes" - desc = "This disease changes the victim into a xenomorph." - severity = DISEASE_SEVERITY_BIOHAZARD - visibility_flags = NONE - stage1 = list() - stage2 = list("Your throat feels scratchy.", span_danger("Kill...")) - stage3 = list( - span_danger("You can feel something move...inside."), - span_danger("Your throat feels very scratchy."), - span_warning("Your skin feels tight."), - ) - stage4 = list( - span_danger("You can feel... something...inside you."), - span_danger("Your blood boils!"), - span_danger("Your skin feels very tight."), - ) - stage5 = list(span_danger("Your skin feels as if it's about to burst off!")) - new_form = /mob/living/carbon/alien/adult/hunter - bantype = ROLE_ALIEN - - -/datum/disease/transformation/xeno/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - switch(stage) - if(3) - if(SPT_PROB(2, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel a stabbing pain in your head.")) - affected_mob.Unconscious(40) - if(4) - if(SPT_PROB(10, seconds_per_tick)) - affected_mob.say(pick("Going to... devour you...", "Hsssshhhhh!", "You look delicious."), forced = "xenomorph transformation") - - -/datum/disease/transformation/slime - name = "Advanced Mutation Transformation" - cure_text = "frost oil" - cures = list(/datum/reagent/consumable/frostoil) - cure_chance = 55 - agent = "Advanced Mutation Toxin" - desc = "This highly concentrated extract converts anything into more of itself." - severity = DISEASE_SEVERITY_BIOHAZARD - visibility_flags = NONE - stage1 = list("You don't feel very well.") - stage2 = list("Your skin feels a little slimy.") - stage3 = list(span_danger("Your appendages are melting away."), span_danger("Your limbs begin to lose their shape.")) - stage4 = list(span_danger("You are turning into a slime.")) - stage5 = list(span_danger("You have become a slime.")) - new_form = /mob/living/simple_animal/slime - - -/datum/disease/transformation/slime/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - switch(stage) - if(1) - if(ishuman(affected_mob)) - var/mob/living/carbon/human/human = affected_mob - if(isjellyperson(human)) - stage = 5 - if(3) - if(ishuman(affected_mob)) - var/mob/living/carbon/human/human = affected_mob - if(!ismonkey(human) && !isjellyperson(human)) - human.set_species(/datum/species/jelly/slime) - -/datum/disease/transformation/slime/do_disease_transformation(mob/living/affected_mob) - if(affected_mob.client && ishuman(affected_mob)) // if they are a human who's not a monkey and are sentient, then let them have the old fun - var/mob/living/carbon/human/human = affected_mob - if(!ismonkey(human)) - new_form = /mob/living/simple_animal/slime/random - return ..() - -/datum/disease/transformation/corgi - name = "The Barkening" - cure_text = "Death" - cures = list(/datum/reagent/medicine/adminordrazine) - agent = "Fell Doge Majicks" - desc = "This disease transforms the victim into a corgi." - severity = DISEASE_SEVERITY_BIOHAZARD - visibility_flags = NONE - stage1 = list("BARK.") - stage2 = list("You feel the need to wear silly hats.") - stage3 = list(span_danger("Must... eat... chocolate...."), span_danger("YAP")) - stage4 = list(span_danger("Visions of washing machines assail your mind!")) - stage5 = list(span_danger("AUUUUUU!!!")) - new_form = /mob/living/basic/pet/dog/corgi - - -/datum/disease/transformation/corgi/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - switch(stage) - if(3) - if (SPT_PROB(4, seconds_per_tick)) - affected_mob.say(pick("Woof!", "YAP"), forced = "corgi transformation") - if(4) - if (SPT_PROB(10, seconds_per_tick)) - affected_mob.say(pick("AUUUUUU", "Bark!"), forced = "corgi transformation") - - -/datum/disease/transformation/morph - name = "Gluttony's Blessing" - cure_text = "Nothing" - cures = list(/datum/reagent/consumable/nothing) - agent = "Gluttony's Blessing" - desc = "A 'gift' from somewhere terrible." - stage_prob = 10 - severity = DISEASE_SEVERITY_BIOHAZARD - visibility_flags = NONE - stage1 = list("Your stomach rumbles.") - stage2 = list("Your skin feels saggy.") - stage3 = list(span_danger("Your appendages are melting away."), span_danger("Your limbs begin to lose their shape.")) - stage4 = list(span_danger("You're ravenous.")) - stage5 = list(span_danger("You have become a morph.")) - new_form = /mob/living/basic/morph - infectable_biotypes = MOB_ORGANIC|MOB_MINERAL|MOB_UNDEAD //magic! - transformed_antag_datum = /datum/antagonist/morph - -/datum/disease/transformation/gondola - name = "Gondola Transformation" - cure_text = "Condensed Capsaicin, ingested or injected." //getting pepper sprayed doesn't help - cures = list(/datum/reagent/consumable/condensedcapsaicin) //beats the hippie crap right out of your system - cure_chance = 55 - stage_prob = 2.5 - agent = "Tranquility" - desc = "Consuming the flesh of a Gondola comes at a terrible price." - severity = DISEASE_SEVERITY_BIOHAZARD - visibility_flags = NONE - stage1 = list("You seem a little lighter in your step.") - stage2 = list("You catch yourself smiling for no reason.") - stage3 = list( - span_danger("A cruel sense of calm overcomes you."), - span_danger("You can't feel your arms!"), - span_danger("You let go of the urge to hurt clowns."), - ) - stage4 = list(span_danger("You can't feel your arms. It does not bother you anymore."), span_danger("You forgive the clown for hurting you.")) - stage5 = list(span_danger("You have become a Gondola.")) - new_form = /mob/living/simple_animal/pet/gondola - - -/datum/disease/transformation/gondola/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - switch(stage) - if(2) - if(SPT_PROB(2.5, seconds_per_tick)) - affected_mob.emote("smile") - if(SPT_PROB(10, seconds_per_tick)) - affected_mob.reagents.add_reagent_list(list(/datum/reagent/pax = 5)) - if(3) - if(SPT_PROB(2.5, seconds_per_tick)) - affected_mob.emote("smile") - if(SPT_PROB(10, seconds_per_tick)) - affected_mob.reagents.add_reagent_list(list(/datum/reagent/pax = 5)) - if(4) - if(SPT_PROB(2.5, seconds_per_tick)) - affected_mob.emote("smile") - if(SPT_PROB(10, seconds_per_tick)) - affected_mob.reagents.add_reagent_list(list(/datum/reagent/pax = 5)) - if(SPT_PROB(1, seconds_per_tick)) - var/obj/item/held_item = affected_mob.get_active_held_item() - if(held_item) - to_chat(affected_mob, span_danger("You let go of what you were holding.")) - affected_mob.dropItemToGround(held_item) diff --git a/code/datums/diseases/tuberculosis.dm b/code/datums/diseases/tuberculosis.dm index 5e4f83104951..41dbf2d54c1b 100644 --- a/code/datums/diseases/tuberculosis.dm +++ b/code/datums/diseases/tuberculosis.dm @@ -4,7 +4,7 @@ max_stages = 5 spread_text = "Airborne" cure_text = "Spaceacillin & Convermol" - cures = list(/datum/reagent/medicine/spaceacillin, /datum/reagent/medicine/c2/convermol) + cures = list(/datum/reagent/medicine/antipathogenic/spaceacillin, /datum/reagent/medicine/c2/convermol) agent = "Fungal Tubercle bacillus Cosmosis" viable_mobtypes = list(/mob/living/carbon/human) cure_chance = 2.5 //like hell are you getting out of hell diff --git a/code/datums/diseases/wizarditis.dm b/code/datums/diseases/wizarditis.dm deleted file mode 100644 index f5a716befb36..000000000000 --- a/code/datums/diseases/wizarditis.dm +++ /dev/null @@ -1,114 +0,0 @@ -/datum/disease/wizarditis - name = "Wizarditis" - max_stages = 4 - spread_text = "Airborne" - cure_text = "The Manly Dorf" - cures = list(/datum/reagent/consumable/ethanol/manly_dorf) - cure_chance = 100 - agent = "Rincewindus Vulgaris" - viable_mobtypes = list(/mob/living/carbon/human) - disease_flags = CAN_CARRY|CAN_RESIST|CURABLE - spreading_modifier = 0.75 - desc = "Some speculate that this virus is the cause of the Space Wizard Federation's existence. Subjects affected show the signs of brain damage, yelling obscure sentences or total gibberish. On late stages subjects sometime express the feelings of inner power, and, cite, 'the ability to control the forces of cosmos themselves!' A gulp of strong, manly spirits usually reverts them to normal, humanlike, condition." - severity = DISEASE_SEVERITY_HARMFUL - required_organs = list(/obj/item/bodypart/head) - -/* -BIRUZ BENNAR -SCYAR NILA - teleport -NEC CANTIO - dis techno -EI NATH - shocking grasp -AULIE OXIN FIERA - knock -TARCOL MINTI ZHERI - forcewall -STI KALY - blind -*/ - -/datum/disease/wizarditis/stage_act(seconds_per_tick, times_fired) - . = ..() - if(!.) - return - - switch(stage) - if(2) - if(SPT_PROB(0.25, seconds_per_tick)) - affected_mob.say(pick("You shall not pass!", "Expeliarmus!", "By Merlins beard!", "Feel the power of the Dark Side!"), forced = "wizarditis") - if(SPT_PROB(0.25, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel [pick("that you don't have enough mana", "that the winds of magic are gone", "an urge to summon familiar")].")) - if(3) - if(SPT_PROB(0.25, seconds_per_tick)) - affected_mob.say(pick("NEC CANTIO!","AULIE OXIN FIERA!", "STI KALY!", "TARCOL MINTI ZHERI!"), forced = "wizarditis") - if(SPT_PROB(0.25, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel [pick("the magic bubbling in your veins","that this location gives you a +1 to INT","an urge to summon familiar")].")) - if(4) - if(SPT_PROB(0.5, seconds_per_tick)) - affected_mob.say(pick("NEC CANTIO!","AULIE OXIN FIERA!","STI KALY!","EI NATH!"), forced = "wizarditis") - return - if(SPT_PROB(0.25, seconds_per_tick)) - to_chat(affected_mob, span_danger("You feel [pick("the tidal wave of raw power building inside","that this location gives you a +2 to INT and +1 to WIS","an urge to teleport")].")) - spawn_wizard_clothes(50) - if(SPT_PROB(0.005, seconds_per_tick)) - teleport() - - -/datum/disease/wizarditis/proc/spawn_wizard_clothes(chance = 0) - if(ishuman(affected_mob)) - var/mob/living/carbon/human/H = affected_mob - if(prob(chance)) - if(!istype(H.head, /obj/item/clothing/head/wizard)) - if(!H.dropItemToGround(H.head)) - qdel(H.head) - H.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(H), ITEM_SLOT_HEAD) - return - if(prob(chance)) - if(!istype(H.wear_suit, /obj/item/clothing/suit/wizrobe)) - if(!H.dropItemToGround(H.wear_suit)) - qdel(H.wear_suit) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(H), ITEM_SLOT_OCLOTHING) - return - if(prob(chance)) - if(!istype(H.shoes, /obj/item/clothing/shoes/sandal/magic)) - if(!H.dropItemToGround(H.shoes)) - qdel(H.shoes) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal/magic(H), ITEM_SLOT_FEET) - return - else - var/mob/living/carbon/H = affected_mob - if(prob(chance)) - var/obj/item/staff/S = new(H) - if(!H.put_in_hands(S)) - qdel(S) - - -/datum/disease/wizarditis/proc/teleport() - var/list/theareas = get_areas_in_range(80, affected_mob) - for(var/area/space/S in theareas) - theareas -= S - - if(!theareas || !theareas.len) - return - - var/area/thearea = pick(theareas) - - var/list/L = list() - var/turf/mob_turf = get_turf(affected_mob) - for(var/turf/T in get_area_turfs(thearea.type)) - if(!is_valid_z_level(T, mob_turf)) - continue - if(T.name == "space") - continue - if(!T.density) - var/clear = 1 - for(var/obj/O in T) - if(O.density) - clear = 0 - break - if(clear) - L+=T - - if(!L) - return - - affected_mob.say("SCYAR NILA [uppertext(thearea.name)]!", forced = "wizarditis teleport") - affected_mob.forceMove(pick(L)) - - return diff --git a/code/datums/dna.dm b/code/datums/dna.dm index bd557d354979..704dfedd8e0c 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -848,7 +848,8 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) apply_status_effect(/datum/status_effect/go_away) if(7) to_chat(src, span_notice("Oh, I actually feel quite alright!")) - ForceContractDisease(new/datum/disease/decloning()) //slow acting, non-viral clone damage based GBS + infect_disease_predefined(DISEASE_DECLONING, TRUE, "[ROUND_TIME()] Infected by DNA Instability") + //ForceContractDisease(new/datum/disease/decloning()) //slow acting, non-viral clone damage based GBS if(8) var/list/elligible_organs = list() for(var/obj/item/organ/internal/internal_organ in organs) //make sure we dont get an implant or cavity item @@ -862,7 +863,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) if(prob(20)) O.animate_atom_living() if(9 to 10) - ForceContractDisease(new/datum/disease/gastrolosis()) + //ForceContractDisease(new/datum/disease/gastrolosis()) to_chat(src, span_notice("Oh, I actually feel quite alright!")) else switch(rand(0,6)) diff --git a/code/datums/elements/dextrous.dm b/code/datums/elements/dextrous.dm index ef4fa290eb2b..6e3aa30a51a5 100644 --- a/code/datums/elements/dextrous.dm +++ b/code/datums/elements/dextrous.dm @@ -55,7 +55,7 @@ if (istype(obj_item) && !obj_item.atom_storage && !(obj_item.item_flags & IN_STORAGE)) return NONE if (!isitem(target) && (hand_haver.istate & ISTATE_HARM)) - return NONE + return if (hand_haver.istate & ISTATE_SECONDARY) INVOKE_ASYNC(target, TYPE_PROC_REF(/atom, attack_hand_secondary), hand_haver, modifiers) else diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index 226253183825..89596951b2c2 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -95,6 +95,7 @@ if(!msg) return + add_event_to_buffer(user, data = msg, log_key = "EMOTE", voluntary = intentional) user.log_message(msg, LOG_EMOTE) var/dchatmsg = "[user] [msg]" diff --git a/code/datums/id_trim/jobs.dm b/code/datums/id_trim/jobs.dm index 910ecdda6f61..096ddc56f2be 100644 --- a/code/datums/id_trim/jobs.dm +++ b/code/datums/id_trim/jobs.dm @@ -1109,7 +1109,7 @@ job = /datum/job/station_engineer /datum/id_trim/job/virologist - assignment = "Virologist" + assignment = "Pathologist" trim_state = "trim_virologist" department_color = COLOR_MEDICAL_BLUE subdepartment_color = COLOR_MEDICAL_BLUE diff --git a/code/datums/status_effects/debuffs/terrified.dm b/code/datums/status_effects/debuffs/terrified.dm index 8645a0a977c9..8f190aab3570 100644 --- a/code/datums/status_effects/debuffs/terrified.dm +++ b/code/datums/status_effects/debuffs/terrified.dm @@ -74,9 +74,8 @@ span_alert("The shadows begin to creep up from the corners of your vision, and then there is nothing..."), span_hear("You hear something heavy collide with the ground."), ) - var/datum/disease/heart_failure/heart_attack = new(src) - heart_attack.stage_prob = 2 //Advances twice as fast - owner.ForceContractDisease(heart_attack) + owner.infect_disease_predefined(DISEASE_HEART, TRUE) + //owner.ForceContractDisease(heart_attack) owner.Unconscious(20 SECONDS) qdel(src) //Victim passes out from fear, calming them down and permenantly damaging their heart. diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm index 9d209df7b546..36fa5f4acd1a 100644 --- a/code/datums/wires/robot.dm +++ b/code/datums/wires/robot.dm @@ -43,6 +43,7 @@ if(new_ai && (new_ai != R.connected_ai)) R.set_connected_ai(new_ai) log_silicon("[key_name(usr)] synced [key_name(R)] [R.connected_ai ? "from [key_name(R.connected_ai)]": ""] to [key_name(new_ai)]") + add_event_to_buffer(usr, R, "synced [key_name(R)] [R.connected_ai ? "from [key_name(R.connected_ai)]": ""] to [key_name(new_ai)]", "SILICON") if(R.shell) R.undeploy() //If this borg is an AI shell, disconnect the controlling AI and assign ti to a new AI R.notify_ai(AI_NOTIFICATION_AI_SHELL) @@ -53,16 +54,19 @@ R.builtInCamera.toggle_cam(usr, FALSE) R.visible_message(span_notice("[R]'s camera lens focuses loudly."), span_notice("Your camera lens focuses loudly.")) log_silicon("[key_name(usr)] toggled [key_name(R)]'s camera to [R.builtInCamera.status ? "on" : "off"] via pulse") + add_event_to_buffer(usr, R, "toggled [key_name(R)]'s camera to [R.builtInCamera.status ? "on" : "off"] via pulse", "SILICON") if(WIRE_LAWSYNC) // Forces a law update if possible. if(R.lawupdate) R.visible_message(span_notice("[R] gently chimes."), span_notice("LawSync protocol engaged.")) log_silicon("[key_name(usr)] forcibly synced [key_name(R)]'s laws via pulse") + add_event_to_buffer(usr, R, "forcibly synced [key_name(R)]'s laws via pulse", "SILICON") // TODO, log the laws they gained here R.lawsync() R.show_laws() if(WIRE_LOCKDOWN) R.SetLockdown(!R.lockcharge) // Toggle log_silicon("[key_name(usr)] [!R.lockcharge ? "locked down" : "released"] [key_name(R)] via pulse") + add_event_to_buffer(usr, R, "[!R.lockcharge ? "locked down" : "released"] [key_name(R)] via pulse", "SILICON") if(WIRE_RESET_MODEL) if(R.has_model()) @@ -75,6 +79,7 @@ if(!mend) R.notify_ai(AI_NOTIFICATION_CYBORG_DISCONNECTED) log_silicon("[key_name(usr)] cut AI wire on [key_name(R)][R.connected_ai ? " and disconnected from [key_name(R.connected_ai)]": ""]") + add_event_to_buffer(usr, R, "cut AI wire on [key_name(R)][R.connected_ai ? " and disconnected from [key_name(R.connected_ai)]": ""]", "SILICON") if(R.shell) R.undeploy() R.set_connected_ai(null) @@ -84,9 +89,11 @@ if(!R.emagged) R.lawupdate = TRUE log_silicon("[key_name(usr)] enabled [key_name(R)]'s lawsync via wire") + add_event_to_buffer(usr, R, "enabled [key_name(R)]'s lawsync via wire", "SILICON") else if(!R.deployed) //AI shells must always have the same laws as the AI R.lawupdate = FALSE log_silicon("[key_name(usr)] disabled [key_name(R)]'s lawsync via wire") + add_event_to_buffer(usr, R, "disabled [key_name(R)]'s lawsync via wire", "SILICON") R.logevent("Lawsync Module fault [mend ? "cleared" : "detected"]") if (WIRE_CAMERA) // Disable the camera. if(!QDELETED(R.builtInCamera) && !R.scrambledcodes) @@ -95,14 +102,17 @@ R.visible_message(span_notice("[R]'s camera lens focuses loudly."), span_notice("Your camera lens focuses loudly.")) R.logevent("Camera Module fault [mend?"cleared":"detected"]") log_silicon("[key_name(usr)] [mend ? "enabled" : "disabled"] [key_name(R)]'s camera via wire") + add_event_to_buffer(usr, R, "[mend ? "enabled" : "disabled"] [key_name(R)]'s camera via wire", "SILICON") if(WIRE_LOCKDOWN) // Simple lockdown. R.SetLockdown(!mend) R.logevent("Motor Controller fault [mend?"cleared":"detected"]") log_silicon("[key_name(usr)] [!R.lockcharge ? "locked down" : "released"] [key_name(R)] via wire") + add_event_to_buffer(usr, R, "[!R.lockcharge ? "locked down" : "released"] [key_name(R)] via wire", "SILICON") if(WIRE_RESET_MODEL) if(R.has_model() && !mend) R.ResetModel() log_silicon("[key_name(usr)] reset [key_name(R)]'s module via wire") + add_event_to_buffer(usr, R, "reset [key_name(R)]'s module via wire", "SILICON") /datum/wires/robot/can_reveal_wires(mob/user) if(HAS_TRAIT(user, TRAIT_KNOW_CYBORG_WIRES)) diff --git a/code/datums/wounds/burns.dm b/code/datums/wounds/burns.dm index 37f4072ffbf9..96b0b108cfdd 100644 --- a/code/datums/wounds/burns.dm +++ b/code/datums/wounds/burns.dm @@ -48,7 +48,7 @@ return if(victim.reagents) - if(victim.reagents.has_reagent(/datum/reagent/medicine/spaceacillin)) + if(victim.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin)) sanitization += 0.9 if(victim.reagents.has_reagent(/datum/reagent/space_cleaner/sterilizine/)) sanitization += 0.9 diff --git a/code/datums/wounds/slash.dm b/code/datums/wounds/slash.dm index c2869c947375..8ad8beff17a3 100644 --- a/code/datums/wounds/slash.dm +++ b/code/datums/wounds/slash.dm @@ -207,10 +207,10 @@ /datum/wound/slash/flesh/proc/lick_wounds(mob/living/carbon/human/user) // transmission is one way patient -> felinid since google said cat saliva is antiseptic or whatever, and also because felinids are already risking getting beaten for this even without people suspecting they're spreading a deathvirus for(var/i in victim.diseases) - var/datum/disease/iter_disease = i + var/datum/disease/advanced/iter_disease = i if(iter_disease.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS)) continue - user.ForceContractDisease(iter_disease) + user.infect_disease(iter_disease, notes = "Spread via Licking (Blood)") user.visible_message(span_notice("[user] begins licking the wounds on [victim]'s [limb.plaintext_zone]."), span_notice("You begin licking the wounds on [victim]'s [limb.plaintext_zone]..."), ignored_mobs=victim) to_chat(victim, span_notice("[user] begins to lick the wounds on your [limb.plaintext_zone].")) diff --git a/code/game/area/areas/station.dm b/code/game/area/areas/station.dm index f5ec05394281..5f866ae8bc86 100644 --- a/code/game/area/areas/station.dm +++ b/code/game/area/areas/station.dm @@ -1041,7 +1041,7 @@ icon_state = "patients" /area/station/medical/virology - name = "Virology" + name = "Pathology" icon_state = "virology" ambience_index = AMBIENCE_VIROLOGY diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 7b67bd6efbc9..3ee32cfcd35b 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1015,6 +1015,9 @@ ///to add blood from a mob onto something, and transfer their dna info /atom/proc/add_mob_blood(mob/living/injected_mob) var/list/blood_dna = injected_mob.get_blood_dna_list() + if(iscarbon(injected_mob)) + var/mob/living/carbon/mob = injected_mob + try_infect_with_mobs_diseases(mob) if(!blood_dna) return FALSE return add_blood_DNA(blood_dna) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 527ed55b2f1b..8b3da89e225c 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -476,6 +476,8 @@ /atom/movable/proc/start_pulling(atom/movable/pulled_atom, state, force = move_force, supress_message = FALSE) if(QDELETED(pulled_atom)) return FALSE + if(!istype(pulled_atom)) + return FALSE if(!(pulled_atom.can_be_pulled(src, state, force))) return FALSE @@ -502,6 +504,7 @@ pulling = pulled_atom pulled_atom.set_pulledby(src) SEND_SIGNAL(src, COMSIG_ATOM_START_PULL, pulled_atom, state, force) + SEND_SIGNAL(pulled_atom, COMSIG_ATOM_PULLED, src, state, force) setGrabState(state) if(ismob(pulled_atom)) var/mob/pulled_mob = pulled_atom diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm index 423633d31ac3..109cd7c9bea6 100644 --- a/code/game/machinery/computer/law.dm +++ b/code/game/machinery/computer/law.dm @@ -10,6 +10,7 @@ AddComponent(/datum/component/gps, "Encrypted Upload") if(!mapload) log_silicon("\A [name] was created at [loc_name(src)].") + add_event_to_buffer(src, data = "was created at [loc_name(src)].", log_key = "SILICON") message_admins("\A [name] was created at [ADMIN_VERBOSEJMP(src)].") /obj/machinery/computer/upload/attackby(obj/item/O, mob/user, params) diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 88ca3b05df78..8a11557225e3 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -94,6 +94,7 @@ R.ai_lockdown = TRUE message_admins(span_notice("[ADMIN_LOOKUPFLW(usr)] [!R.lockcharge ? "locked down" : "released"] [ADMIN_LOOKUPFLW(R)]!")) log_silicon("[key_name(usr)] [!R.lockcharge ? "locked down" : "released"] [key_name(R)]!") + add_event_to_buffer(usr, R, "[!R.lockcharge ? "locked down" : "released"] [key_name(R)]!", "SILICON") log_combat(usr, R, "[!R.lockcharge ? "locked down" : "released"] cyborg") R.SetLockdown(!R.lockcharge) to_chat(R, !R.lockcharge ? span_notice("Your lockdown has been lifted!") : span_alert("You have been locked down!")) @@ -123,6 +124,7 @@ var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs if(istype(R) && !R.emagged && (R.connected_ai == usr || isAdminGhostAI(usr)) && !R.scrambledcodes && can_control(usr, R)) log_silicon("[key_name(usr)] emagged [key_name(R)] using robotic console!") + add_event_to_buffer(usr, R, "emagged [key_name(R)] using robotic console!", "SILICON") message_admins("[ADMIN_LOOKUPFLW(usr)] emagged cyborg [key_name_admin(R)] using robotic console!") R.SetEmagged(TRUE) R.logevent("WARN: root privleges granted to PID [num2hex(rand(1,65535), -1)][num2hex(rand(1,65535), -1)].") //random eight digit hex value. Two are used because rand(1,4294967295) throws an error @@ -136,6 +138,7 @@ var/turf/T = get_turf(drone) message_admins("[ADMIN_LOOKUPFLW(usr)] detonated [key_name_admin(drone)] at [ADMIN_VERBOSEJMP(T)]!") log_silicon("[key_name(usr)] detonated [key_name(drone)]!") + add_event_to_buffer(usr, drone, "detonated [key_name(drone)]", "SILICON") var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread s.set_up(3, TRUE, drone) s.start() diff --git a/code/game/machinery/transformer.dm b/code/game/machinery/transformer.dm index aa6301e0b8da..d2d4218d5ad8 100644 --- a/code/game/machinery/transformer.dm +++ b/code/game/machinery/transformer.dm @@ -110,6 +110,7 @@ new_borg.lawsync() new_borg.lawupdate = TRUE log_silicon("[key_name(new_borg)] resynced to [key_name(master_ai)]") + add_event_to_buffer(new_borg, master_ai, "resynced to [key_name(master_ai)]", "SILICON") addtimer(CALLBACK(src, PROC_REF(unlock_new_robot), new_borg), 5 SECONDS) /obj/machinery/transformer/proc/unlock_new_robot(mob/living/silicon/robot/new_borg) diff --git a/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm b/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm index 9aa2c5332db9..9c3bab579a5e 100644 --- a/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm +++ b/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm @@ -82,7 +82,7 @@ if(ishuman(impacted_thing)) var/mob/living/carbon/human/mob_to_infect = impacted_thing - mob_to_infect.ForceContractDisease(new /datum/disease/revblight(), FALSE, TRUE) + //mob_to_infect.ForceContractDisease(new /datum/disease/revblight(), FALSE, TRUE) // TODO Replace with advanced custom disease new /obj/effect/temp_visual/revenant(get_turf(mob_to_infect)) to_chat(mob_to_infect, span_revenminor("A cacophony of ghostly wailing floods your ears for a moment. The noise subsides, but a distant whispering continues echoing inside of your head...")) diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index 6fb9aa201883..1e88422368fb 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -16,6 +16,8 @@ ///The amount of reagent this decal holds, if decal_reagent is defined var/reagent_amount = 0 + var/list/diseases = list() + /obj/effect/decal/cleanable/Initialize(mapload, list/datum/disease/diseases) . = ..() if (random_icon_states && (icon_state == initial(icon_state)) && length(random_icon_states) > 0) @@ -31,13 +33,17 @@ return INITIALIZE_HINT_QDEL if(LAZYLEN(diseases)) + var/list/datum/disease/diseases_to_add = list() for(var/datum/disease/D in diseases) - if(D.spread_flags & DISEASE_SPREAD_CONTACT_FLUIDS) + if(D.spread_flags & (DISEASE_SPREAD_CONTACT_FLUIDS)) diseases_to_add += D if(LAZYLEN(diseases_to_add)) AddComponent(/datum/component/infective, diseases_to_add) - + for(var/datum/disease/D in diseases) + if(D.spread_flags & (DISEASE_SPREAD_BLOOD)) + src.diseases |= D + AddElement(/datum/element/beauty, beauty) var/turf/T = get_turf(src) diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index 7915ce1cfb70..ba0b31853f85 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -183,7 +183,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark) icon_state = "Chief Medical Officer" /obj/effect/landmark/start/virologist - name = "Virologist" + name = "Pathologist" icon_state = "Virologist" /obj/effect/landmark/start/psychologist diff --git a/code/game/objects/items/AI_modules/_AI_modules.dm b/code/game/objects/items/AI_modules/_AI_modules.dm index e46b70338929..493d90ca1793 100644 --- a/code/game/objects/items/AI_modules/_AI_modules.dm +++ b/code/game/objects/items/AI_modules/_AI_modules.dm @@ -80,6 +80,7 @@ to_chat(user, span_alert("Not enough memory allocated to [law_datum.owner ? law_datum.owner : "the AI core"]'s law processor to handle this amount of laws.")) message_admins("[ADMIN_LOOKUPFLW(user)] tried to upload laws to [law_datum.owner ? ADMIN_LOOKUPFLW(law_datum.owner) : "an AI core"] that would exceed the law cap.") log_silicon("[key_name(user)] tried to upload laws to [law_datum.owner ? key_name(law_datum.owner) : "an AI core"] that would exceed the law cap.") + add_event_to_buffer(user, law_datum.owner, "tried to upload laws to [law_datum.owner ? key_name(law_datum.owner) : "an AI core"] that would exceed the law cap.", "SILICON") overflow = TRUE var/law2log = transmitInstructions(law_datum, user, overflow) //Freeforms return something extra we need to log @@ -108,6 +109,7 @@ borg_txt = borg_txt.Join() GLOB.lawchanges.Add("[time] : [user.name]([user.key]) used [src.name] on [ainame]([aikey]).[law2log ? " The law specified [law2log]" : ""], [length(affected_cyborgs) ? ", impacting synced borgs [borg_txt]" : ""]") log_silicon("LAW: [key_name(user)] used [src.name] on [key_name(law_datum.owner)] from [AREACOORD(user)].[law2log ? " The law specified [law2log]" : ""], [length(affected_cyborgs) ? ", impacting synced borgs [borg_txt]" : ""]") + add_event_to_buffer(user, law_datum.owner, "used [src.name] on [key_name(law_datum.owner)] from [AREACOORD(user)].[law2log ? " The law specified [law2log]" : ""], [length(affected_cyborgs) ? ", impacting synced borgs [borg_txt]" : ""]", "SILICON") message_admins("[ADMIN_LOOKUPFLW(user)] used [src.name] on [ADMIN_LOOKUPFLW(law_datum.owner)] from [AREACOORD(user)].[law2log ? " The law specified [law2log]" : ""] , [length(affected_cyborgs) ? ", impacting synced borgs [borg_flw.Join()]" : ""]") if(law_datum.owner) deadchat_broadcast(" changed [span_name("[ainame]")]'s laws at [get_area_name(user, TRUE)].", span_name("[user]"), follow_target=user, message_type=DEADCHAT_LAWCHANGE) diff --git a/code/game/objects/items/airlock_painter.dm b/code/game/objects/items/airlock_painter.dm index 54478d61a423..80eeebd32bd5 100644 --- a/code/game/objects/items/airlock_painter.dm +++ b/code/game/objects/items/airlock_painter.dm @@ -27,7 +27,7 @@ "Security" = /obj/machinery/door/airlock/security, "Command" = /obj/machinery/door/airlock/command, "Medical" = /obj/machinery/door/airlock/medical, - "Virology" = /obj/machinery/door/airlock/virology, + "Pathology" = /obj/machinery/door/airlock/virology, "Research" = /obj/machinery/door/airlock/research, "Hydroponics" = /obj/machinery/door/airlock/hydroponics, "Freezer" = /obj/machinery/door/airlock/freezer, diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index 61ba48c081b4..a0a0cb129803 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -39,6 +39,7 @@ target.transfer_ai(AI_TRANS_TO_CARD, user, null, src) if(AI) log_silicon("[key_name(user)] carded [key_name(AI)]", src) + add_event_to_buffer(user, AI, "carded [key_name(AI)]", "SILICON") update_appearance() return TRUE return ..() diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm index a6cdcd5108c3..74fd325e87cf 100644 --- a/code/game/objects/items/devices/scanners/health_analyzer.dm +++ b/code/game/objects/items/devices/scanners/health_analyzer.dm @@ -334,13 +334,14 @@ render_list += "" //Diseases + /* for(var/thing in target.diseases) var/datum/disease/D = thing if(!(D.visibility_flags & HIDDEN_SCANNER)) render_list += "Warning: [D.form] detected\n\
Name: [D.name].\nType: [D.spread_text].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]
\
" // divs do not need extra linebreak - + */ // Blood Level if(target.has_dna()) var/mob/living/carbon/carbontarget = target diff --git a/code/game/objects/items/dice.dm b/code/game/objects/items/dice.dm index 789f62504412..34e98bad04e2 100644 --- a/code/game/objects/items/dice.dm +++ b/code/game/objects/items/dice.dm @@ -381,9 +381,26 @@ explosion(get_turf(user), devastation_range = -1, light_impact_range = 2, flame_range = 2, explosion_cause = src) if(9) //Cold - var/datum/disease/cold = new /datum/disease/cold() selected_turf.visible_message(span_userdanger("[user] looks a little under the weather!")) - user.ForceContractDisease(cold, FALSE, TRUE) + var/virus_choice = pick(subtypesof(/datum/disease/advanced)- typesof(/datum/disease/advanced/premade)) + var/list/anti = list( + ANTIGEN_BLOOD = 1, + ANTIGEN_COMMON = 1, + ANTIGEN_RARE = 2, + ANTIGEN_ALIEN = 0, + ) + var/list/bad = list( + EFFECT_DANGER_HELPFUL = 0, + EFFECT_DANGER_FLAVOR = 1, + EFFECT_DANGER_ANNOYING = 2, + EFFECT_DANGER_HINDRANCE = 3, + EFFECT_DANGER_HARMFUL = 1, + EFFECT_DANGER_DEADLY = 0, + ) + var/datum/disease/advanced/new_disease = new virus_choice + new_disease.makerandom(list(50,90),list(50,100),anti,bad,src) + user.infect_disease(new_disease, TRUE, "(Die of Fate 7)") + if(10) //Nothing selected_turf.visible_message(span_userdanger("Nothing seems to happen.")) diff --git a/code/game/objects/items/dna_injector.dm b/code/game/objects/items/dna_injector.dm index ca7af78ecba3..f71adada7595 100644 --- a/code/game/objects/items/dna_injector.dm +++ b/code/game/objects/items/dna_injector.dm @@ -180,10 +180,12 @@ target.dna.add_mutation(added_mutation, MUT_EXTRA) else if(research && target.client) filled = TRUE + /* for(var/datum/disease/advance/disease in target.diseases) for(var/datum/symptom/symp in disease.symptoms) if((symp.type == /datum/symptom/genetic_mutation) || (symp.type == /datum/symptom/viralevolution)) crispr_charge = TRUE + */ log_combat(user, target, "[!doitanyway ? "failed to inject" : "injected"]", "[src] ([mutation])[crispr_charge ? " with CRISPR charge" : ""]") return TRUE diff --git a/code/game/objects/items/food/monkeycube.dm b/code/game/objects/items/food/monkeycube.dm index e78d45ad2e83..fa826ec02ffb 100644 --- a/code/game/objects/items/food/monkeycube.dm +++ b/code/game/objects/items/food/monkeycube.dm @@ -12,7 +12,11 @@ /obj/item/food/monkeycube/proc/Expand() var/mob/spammer = get_mob_by_key(fingerprintslast) - var/mob/living/bananas = new spawned_mob(drop_location(), TRUE, spammer) + var/mob/living/bananas + if(spawned_mob == /mob/living/carbon/human/species/monkey) + bananas = new spawned_mob(drop_location(), TRUE, spammer) + else + bananas = new spawned_mob(drop_location()) if(faction) bananas.faction = faction if (!QDELETED(bananas)) diff --git a/code/game/objects/items/food/snacks.dm b/code/game/objects/items/food/snacks.dm index 6edcc0c75fc7..8e96ebd37014 100644 --- a/code/game/objects/items/food/snacks.dm +++ b/code/game/objects/items/food/snacks.dm @@ -42,9 +42,30 @@ /obj/item/food/candy/bronx/proc/after_eat(mob/living/eater) if(ishuman(eater)) - var/mob/living/carbon/human/carl = eater - var/datum/disease/disease = new /datum/disease/parasite() - carl.ForceContractDisease(disease, make_copy = FALSE, del_on_fail = TRUE) + var/datum/disease/advanced/parasite/disease = new + var/list/anti = list( + ANTIGEN_BLOOD = 1, + ANTIGEN_COMMON = 1, + ANTIGEN_RARE = 0, + ANTIGEN_ALIEN = 0, + ) + var/list/bad = list( + EFFECT_DANGER_HELPFUL = 1, + EFFECT_DANGER_FLAVOR = 4, + EFFECT_DANGER_ANNOYING = 4, + EFFECT_DANGER_HINDRANCE = 0, + EFFECT_DANGER_HARMFUL = 0, + EFFECT_DANGER_DEADLY = 0, + ) + + disease.makerandom(list(30,55),list(0,50),anti,bad,null) + + disease.log += "
[ROUND_TIME()] Infected [key_name(eater)]" + if(!length(eater.diseases)) + eater.diseases = list() + eater.diseases += disease + + disease.AddToGoggleView(eater) /obj/item/food/candy/bronx/examine(mob/user) . = ..() diff --git a/code/game/objects/items/rcd/RCD.dm b/code/game/objects/items/rcd/RCD.dm index 511c18f309f7..7d2873cb762a 100644 --- a/code/game/objects/items/rcd/RCD.dm +++ b/code/game/objects/items/rcd/RCD.dm @@ -93,7 +93,7 @@ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/command/glass, TITLE = "Command"), list(AIRLOCK_TYPE = /obj/machinery/door/airlock/medical/glass, TITLE = "Medical"), list(AIRLOCK_TYPE = /obj/machinery/door/airlock/research/glass, TITLE = "Research"), - list(AIRLOCK_TYPE = /obj/machinery/door/airlock/virology/glass, TITLE = "Virology"), + list(AIRLOCK_TYPE = /obj/machinery/door/airlock/virology/glass, TITLE = "Pathology"), list(AIRLOCK_TYPE = /obj/machinery/door/airlock/mining/glass, TITLE = "Mining"), list(AIRLOCK_TYPE = /obj/machinery/door/airlock/maintenance/glass, TITLE = "Maintenance"), list(AIRLOCK_TYPE = /obj/machinery/door/airlock/external/glass, TITLE = "External"), @@ -111,7 +111,7 @@ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/medical, TITLE = "Medical"), list(AIRLOCK_TYPE = /obj/machinery/door/airlock/research, TITLE = "Research"), list(AIRLOCK_TYPE = /obj/machinery/door/airlock/freezer, TITLE = "Freezer"), - list(AIRLOCK_TYPE = /obj/machinery/door/airlock/virology, TITLE = "Virology"), + list(AIRLOCK_TYPE = /obj/machinery/door/airlock/virology, TITLE = "Pathology"), list(AIRLOCK_TYPE = /obj/machinery/door/airlock/mining, TITLE = "Mining"), list(AIRLOCK_TYPE = /obj/machinery/door/airlock/maintenance, TITLE = "Maintenance"), list(AIRLOCK_TYPE = /obj/machinery/door/airlock/external, TITLE = "External"), diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index 6873b838ebde..12aeabe69f2a 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -24,6 +24,7 @@ AI.add_malf_picker() AI.hack_software = TRUE log_silicon("[key_name(user)] has upgraded [key_name(AI)] with a [src].") + add_event_to_buffer(user, AI, "has upgraded [key_name(AI)] with a [src].", "SILICON") message_admins("[ADMIN_LOOKUPFLW(user)] has upgraded [ADMIN_LOOKUPFLW(AI)] with a [src].") to_chat(user, span_notice("You install [src], upgrading [AI].")) qdel(src) diff --git a/code/game/objects/items/robot/items/hypo.dm b/code/game/objects/items/robot/items/hypo.dm index 5c501a59c15c..389d10175196 100644 --- a/code/game/objects/items/robot/items/hypo.dm +++ b/code/game/objects/items/robot/items/hypo.dm @@ -6,7 +6,7 @@ /datum/reagent/medicine/c2/libital,\ /datum/reagent/medicine/c2/multiver,\ /datum/reagent/medicine/salglu_solution,\ - /datum/reagent/medicine/spaceacillin\ + /datum/reagent/medicine/antipathogenic/spaceacillin\ ) #define EXPANDED_MEDICAL_REAGENTS list(\ /datum/reagent/medicine/haloperidol,\ diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index 06f33f98750f..f600999d86cd 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -422,18 +422,22 @@ return created_name = new_name log_silicon("[key_name(user)] has set \"[new_name]\" as a cyborg shell name at [loc_name(user)]") + add_event_to_buffer(user, src, "has set \"[new_name]\" as a cyborg shell name at [loc_name(user)].", "SILICON") return TRUE if("locomotion") locomotion = !locomotion log_silicon("[key_name(user)] has [locomotion ? "enabled" : "disabled"] movement on a cyborg shell at [loc_name(user)]") + add_event_to_buffer(user, src, "has [locomotion ? "enabled" : "disabled"] movement on a cyborg shell at [loc_name(user)].", "SILICON") return TRUE if("panel") panel_locked = !panel_locked log_silicon("[key_name(user)] has [panel_locked ? "locked" : "unlocked"] the panel on a cyborg shell at [loc_name(user)]") + add_event_to_buffer(user, src, "has [panel_locked ? "locked" : "unlocked"] the panel on a cyborg shell at [loc_name(user)].", "SILICON") return TRUE if("aisync") aisync = !aisync log_silicon("[key_name(user)] has [aisync ? "enabled" : "disabled"] the AI sync for a cyborg shell at [loc_name(user)]") + add_event_to_buffer(user, src, "has [aisync ? "enabled" : "disabled"] the AI sync for a cyborg shell at [loc_name(user)].", "SILICON") return TRUE if("set_ai") var/selected_ai = select_active_ai(user, z) @@ -444,8 +448,10 @@ return forced_ai = selected_ai log_silicon("[key_name(user)] set the default AI for a cyborg shell to [key_name(selected_ai)] at [loc_name(user)]") + add_event_to_buffer(user, src, "set the default AI for a cyborg shell to [key_name(selected_ai)] at [loc_name(user)].", "SILICON") return TRUE if("lawsync") lawsync = !lawsync log_silicon("[key_name(user)] has [lawsync ? "enabled" : "disabled"] the law sync for a cyborg shell at [loc_name(user)]") + add_event_to_buffer(user, src, "has [lawsync ? "enabled" : "disabled"] the law sync for a cyborg shell at [loc_name(user)].", "SILICON") return TRUE diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 791088890dac..39ff2fe2defa 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -251,7 +251,7 @@ repeating = TRUE heal_brute = 10 stop_bleeding = 0.6 - grind_results = list(/datum/reagent/medicine/spaceacillin = 2) + grind_results = list(/datum/reagent/medicine/antipathogenic/spaceacillin = 2) merge_type = /obj/item/stack/medical/suture /obj/item/stack/medical/suture/emergency @@ -310,7 +310,7 @@ flesh_regeneration = 3 var/is_open = TRUE ///This var determines if the sterile packaging of the mesh has been opened. - grind_results = list(/datum/reagent/medicine/spaceacillin = 2) + grind_results = list(/datum/reagent/medicine/antipathogenic/spaceacillin = 2) merge_type = /obj/item/stack/medical/mesh /obj/item/stack/medical/mesh/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1) diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm index 495485238d33..84534458943f 100644 --- a/code/game/objects/items/storage/backpack.dm +++ b/code/game/objects/items/storage/backpack.dm @@ -494,7 +494,7 @@ inhand_icon_state = "duffel-sci" /obj/item/storage/backpack/duffelbag/virology - name = "virologist's duffel bag" + name = "pathologist's duffel bag" desc = "A large duffel bag for holding extra viral bottles." icon_state = "duffel-virology" inhand_icon_state = "duffel-virology" diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 894d4c8f234c..10a9081e6fe1 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -238,7 +238,6 @@ new /obj/item/storage/belt/fannypack/yellow(src) // 0 tc new /obj/item/grenade/spawnergrenade/buzzkill(src) // these are the random super bees this is definitely all of the tc budget for this one new /obj/item/grenade/spawnergrenade/buzzkill(src) // 10 tc per grenade - new /obj/item/reagent_containers/cup/bottle/beesease(src) // 10 tc? new /obj/item/melee/beesword(src) //priceless if(KIT_MR_FREEZE) diff --git a/code/game/objects/structures/shower.dm b/code/game/objects/structures/shower.dm index 65e534be81bf..326a8ea109c5 100644 --- a/code/game/objects/structures/shower.dm +++ b/code/game/objects/structures/shower.dm @@ -245,6 +245,11 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16)) /obj/machinery/shower/proc/wash_atom(atom/target) target.wash(CLEAN_RAD | CLEAN_WASH) reagents.expose(target, (TOUCH), SHOWER_EXPOSURE_MULTIPLIER * SHOWER_SPRAY_VOLUME / max(reagents.total_volume, SHOWER_SPRAY_VOLUME)) + if(isitem(target)) + var/obj/item/item = target + if(length(item.viruses)) + for(var/datum/disease/advanced/D as anything in item.viruses) + item.remove_disease(D) if(isliving(target)) var/mob/living/living_target = target check_heat(living_target) diff --git a/code/game/objects/structures/syndicate_uplink_beacon.dm b/code/game/objects/structures/syndicate_uplink_beacon.dm index f4d4ea6fe642..d1b91167d5ac 100644 --- a/code/game/objects/structures/syndicate_uplink_beacon.dm +++ b/code/game/objects/structures/syndicate_uplink_beacon.dm @@ -105,6 +105,7 @@ flick("relay_traitor_activate", src) do_sparks(number = 5, cardinal_only = FALSE, source = src) log_traitor("[key_name(resolved_owner)] acquired a replacement uplink via the syndicate uplink beacon.") + add_event_to_buffer(resolved_owner, data = "acquired a replacement uplink via the syndicate uplink beacon.", log_key = "TRAITOR") // Adds screentips /obj/structure/syndicate_uplink_beacon/add_context(atom/source, list/context, obj/item/held_item, mob/user) diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm index 20fbf9fee509..1f751e904896 100644 --- a/code/game/turfs/open/_open.dm +++ b/code/game/turfs/open/_open.dm @@ -267,7 +267,7 @@ slipper.Immobilize(1 SECONDS) slipper.incapacitate(1 SECONDS) else - slipper.Knockdown(knockdown_amount) + slipper.bananeer(total_time = knockdown_amount * 0.1, stun_duration = knockdown_amount, height = (knockdown_amount * 0.5), flip_count = round(knockdown_amount * 0.1)) slipper.Paralyze(paralyze_amount) slipper.stop_pulling() diff --git a/code/game/world.dm b/code/game/world.dm index a1301045f64e..17fe02200003 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -51,7 +51,7 @@ GLOBAL_VAR(restart_counter) GLOB.revdata = new InitTgs() - + SSmetrics.world_init_time = REALTIMEOFDAY config.Load(params[OVERRIDE_CONFIG_DIRECTORY_PARAMETER]) load_admins() diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 18cefa1e6e1c..59c677335253 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -115,6 +115,8 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list( /client/proc/cmd_admin_gib_self, /client/proc/cmd_select_equipment, /client/proc/command_report_footnote, + /client/proc/diseases_panel, + /client/proc/disease_view, /client/proc/delay_command_report, /client/proc/drop_bomb, /client/proc/drop_dynex_bomb, @@ -777,13 +779,9 @@ GLOBAL_PROTECT(admin_verbs_poll) if(!istype(T)) to_chat(src, span_notice("You can only give a disease to a mob of type /mob/living."), confidential = TRUE) return - var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in sort_list(SSdisease.diseases, GLOBAL_PROC_REF(cmp_typepaths_asc)) - if(!D) - return - T.ForceContractDisease(new D, FALSE, TRUE) + //T.ForceContractDisease(new D, FALSE, TRUE) + make_custom_virus(src, T) SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Disease") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] gave [key_name(T)] the disease [D].") - message_admins(span_adminnotice("[key_name_admin(usr)] gave [key_name_admin(T)] the disease [D].")) /client/proc/object_say(obj/O in world) set category = "Admin.Events" diff --git a/code/modules/admin/verbs/borgpanel.dm b/code/modules/admin/verbs/borgpanel.dm index 959485252298..1e2eaaf634b9 100644 --- a/code/modules/admin/verbs/borgpanel.dm +++ b/code/modules/admin/verbs/borgpanel.dm @@ -90,10 +90,12 @@ borg.cell.charge = newcharge message_admins("[key_name_admin(user)] set the charge of [ADMIN_LOOKUPFLW(borg)] to [borg.cell.charge].") log_silicon("[key_name(user)] set the charge of [key_name(borg)] to [borg.cell.charge].") + add_event_to_buffer(user, borg, "set the charge of [key_name(borg)] to [borg.cell.charge].", "SILICON") if ("remove_cell") QDEL_NULL(borg.cell) message_admins("[key_name_admin(user)] deleted the cell of [ADMIN_LOOKUPFLW(borg)].") log_silicon("[key_name(user)] deleted the cell of [key_name(borg)].") + add_event_to_buffer(user, borg, "deleted the cell of [key_name(borg)].", "SILICON") if ("change_cell") var/chosen = pick_closest_path(null, make_types_fancy(typesof(/obj/item/stock_parts/cell))) if (!ispath(chosen)) @@ -107,44 +109,54 @@ borg.diag_hud_set_borgcell() message_admins("[key_name_admin(user)] changed the cell of [ADMIN_LOOKUPFLW(borg)] to [new_cell].") log_silicon("[key_name(user)] changed the cell of [key_name(borg)] to [new_cell].") + add_event_to_buffer(user, borg, "changed the cell of [key_name(borg)] to [new_cell].", "SILICON") if ("toggle_emagged") borg.SetEmagged(!borg.emagged) if (borg.emagged) message_admins("[key_name_admin(user)] emagged [ADMIN_LOOKUPFLW(borg)].") log_silicon("[key_name(user)] emagged [key_name(borg)].") + add_event_to_buffer(user, borg, "emagged [key_name(borg)].", "SILICON") else message_admins("[key_name_admin(user)] un-emagged [ADMIN_LOOKUPFLW(borg)].") log_silicon("[key_name(user)] un-emagged [key_name(borg)].") + add_event_to_buffer(user, borg, "un-emagged [key_name(borg)].", "SILICON") if ("toggle_lawupdate") borg.lawupdate = !borg.lawupdate if (borg.lawupdate) message_admins("[key_name_admin(user)] enabled lawsync on [ADMIN_LOOKUPFLW(borg)].") log_silicon("[key_name(user)] enabled lawsync on [key_name(borg)].") + add_event_to_buffer(user, borg, "enabled lawsync on [key_name(borg)].", "SILICON") else message_admins("[key_name_admin(user)] disabled lawsync on [ADMIN_LOOKUPFLW(borg)].") log_silicon("[key_name(user)] disabled lawsync on [key_name(borg)].") + add_event_to_buffer(user, borg, "disabled lawsync on [key_name(borg)].", "SILICON") if ("toggle_lockdown") borg.SetLockdown(!borg.lockcharge) if (borg.lockcharge) message_admins("[key_name_admin(user)] locked down [ADMIN_LOOKUPFLW(borg)].") log_silicon("[key_name(user)] locked down [key_name(borg)].") + add_event_to_buffer(user, borg, "locked down [key_name(borg)].", "SILICON") else message_admins("[key_name_admin(user)] released [ADMIN_LOOKUPFLW(borg)] from lockdown.") log_silicon("[key_name(user)] released [key_name(borg)] from lockdown.") + add_event_to_buffer(user, borg, "released [key_name(borg)] from lockdown.", "SILICON") if ("toggle_scrambledcodes") borg.scrambledcodes = !borg.scrambledcodes if (borg.scrambledcodes) message_admins("[key_name_admin(user)] enabled scrambled codes on [ADMIN_LOOKUPFLW(borg)].") log_silicon("[key_name(user)] enabled scrambled codes on [key_name(borg)].") + add_event_to_buffer(user, borg, "enabled scrambled codes on [key_name(borg)].", "SILICON") else message_admins("[key_name_admin(user)] disabled scrambled codes on [ADMIN_LOOKUPFLW(borg)].") log_silicon("[key_name(user)] disabled scrambled codes on [key_name(borg)].") + add_event_to_buffer(user, borg, "disabled scrambled codes on [key_name(borg)].", "SILICON") if ("rename") var/new_name = sanitize_name(tgui_input_text(user, "What would you like to name this cyborg?", "Cyborg Reclassification", borg.real_name, MAX_NAME_LEN), allow_numbers = TRUE) if(!new_name) return message_admins("[key_name_admin(user)] renamed [ADMIN_LOOKUPFLW(borg)] to [new_name].") log_silicon("[key_name(user)] renamed [key_name(borg)] to [new_name].") + add_event_to_buffer(user, borg, "renamed [key_name(borg)] to [new_name].", "SILICON") borg.fully_replace_character_name(borg.real_name,new_name) if ("toggle_upgrade") var/upgradepath = text2path(params["upgrade"]) @@ -152,6 +164,7 @@ if (installedupgrade) message_admins("[key_name_admin(user)] removed the [installedupgrade] upgrade from [ADMIN_LOOKUPFLW(borg)].") log_silicon("[key_name(user)] removed the [installedupgrade] upgrade from [key_name(borg)].") + add_event_to_buffer(user, borg, "removed the [installedupgrade] upgrade from [key_name(borg)].", "SILICON") qdel(installedupgrade) // see [mob/living/silicon/robot/on_upgrade_deleted()]. else var/obj/item/borg/upgrade/upgrade = new upgradepath(borg) @@ -159,6 +172,7 @@ borg.upgrades += upgrade message_admins("[key_name_admin(user)] added the [upgrade] borg upgrade to [ADMIN_LOOKUPFLW(borg)].") log_silicon("[key_name(user)] added the [upgrade] borg upgrade to [key_name(borg)].") + add_event_to_buffer(user, borg, "added the [upgrade] borg upgrade to [key_name(borg)].", "SILICON") if ("toggle_radio") var/channel = params["channel"] if (channel in borg.radio.channels) // We're removing a channel @@ -176,6 +190,7 @@ borg.radio.keyslot.independent = FALSE message_admins("[key_name_admin(user)] removed the [channel] radio channel from [ADMIN_LOOKUPFLW(borg)].") log_silicon("[key_name(user)] removed the [channel] radio channel from [key_name(borg)].") + add_event_to_buffer(user, borg, "removed the [channel] radio channel from [key_name(borg)].", "SILICON") else // We're adding a channel if (!borg.radio.keyslot) // Assert that an encryption key exists borg.radio.keyslot = new() @@ -186,6 +201,7 @@ borg.radio.keyslot.independent = TRUE message_admins("[key_name_admin(user)] added the [channel] radio channel to [ADMIN_LOOKUPFLW(borg)].") log_silicon("[key_name(user)] added the [channel] radio channel to [key_name(borg)].") + add_event_to_buffer(user, borg, "added the [channel] radio channel to [key_name(borg)].", "SILICON") borg.radio.recalculateChannels() if ("setmodule") var/new_model_path = text2path(params["module"]) @@ -193,6 +209,7 @@ borg.model.transform_to(new_model_path) message_admins("[key_name_admin(user)] changed the model of [ADMIN_LOOKUPFLW(borg)] to [new_model_path].") log_silicon("[key_name(user)] changed the model of [key_name(borg)] to [new_model_path].") + add_event_to_buffer(user, borg, "changed the model of [key_name(borg)] to [new_model_path].", "SILICON") if ("slavetoai") var/mob/living/silicon/ai/newai = locate(params["slavetoai"]) in GLOB.ai_list if (newai && newai != borg.connected_ai) @@ -203,6 +220,7 @@ borg.notify_ai(TRUE) message_admins("[key_name_admin(user)] slaved [ADMIN_LOOKUPFLW(borg)] to the AI [ADMIN_LOOKUPFLW(newai)].") log_silicon("[key_name(user)] slaved [key_name(borg)] to the AI [key_name(newai)].") + add_event_to_buffer(user, borg, "slaved [key_name(borg)] to the AI [key_name(newai)].", "SILICON") else if (params["slavetoai"] == "null") borg.notify_ai(AI_NOTIFICATION_CYBORG_DISCONNECTED) if(borg.shell) @@ -210,6 +228,7 @@ borg.set_connected_ai(null) message_admins("[key_name_admin(user)] freed [ADMIN_LOOKUPFLW(borg)] from being slaved to an AI.") log_silicon("[key_name(user)] freed [key_name(borg)] from being slaved to an AI.") + add_event_to_buffer(user, borg, "freed [key_name(borg)] from being slaved to an AI.", "SILICON") if (borg.lawupdate) borg.lawsync() diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index d16b10ce572b..c158a57412a3 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -9,6 +9,7 @@ msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN) if(!msg) return + add_event_to_buffer(src, data = msg, log_key = "PRAYER") log_prayer("[src.key]/([src.name]): [msg]") if(usr.client) if(usr.client.prefs.muted & MUTE_PRAY) diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm index 8407c364c192..6a01b34de36d 100644 --- a/code/modules/admin/verbs/secrets.dm +++ b/code/modules/admin/verbs/secrets.dm @@ -94,12 +94,16 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller) //Buttons for helpful stuff. This is where people land in the tgui if("clear_virus") - var/choice = tgui_alert(usr, "Are you sure you want to cure all disease?",, list("Yes", "Cancel")) + var/choice = tgui_alert(usr, "Are you sure you want to cure all disease (Only clears players diseases)?",, list("Yes", "Cancel")) if(choice == "Yes") message_admins("[key_name_admin(holder)] has cured all diseases.") - for(var/thing in SSdisease.active_diseases) - var/datum/disease/D = thing - D.cure(0) + for(var/mob/living/living as anything in GLOB.alive_mob_list) + if(!isliving(living)) + continue + if(!length(living.diseases)) + continue + for(var/datum/disease/advanced/disease as anything in living.diseases) + disease.cure(living) if("list_bombers") holder.list_bombers() diff --git a/code/modules/antagonists/abductor/equipment/glands/viral.dm b/code/modules/antagonists/abductor/equipment/glands/viral.dm index c3d82af96740..c958501e77bf 100644 --- a/code/modules/antagonists/abductor/equipment/glands/viral.dm +++ b/code/modules/antagonists/abductor/equipment/glands/viral.dm @@ -9,9 +9,33 @@ /obj/item/organ/internal/heart/gland/viral/activate() to_chat(owner, span_warning("You feel sick.")) - var/datum/disease/advance/A = random_virus(pick(2,6),6) - A.carrier = TRUE - owner.ForceContractDisease(A, FALSE, TRUE) + + var/list/anti = list( + ANTIGEN_BLOOD = 1, + ANTIGEN_COMMON = 1, + ANTIGEN_RARE = 0, + ANTIGEN_ALIEN = 0, + ) + var/list/bad = list( + EFFECT_DANGER_HELPFUL = 1, + EFFECT_DANGER_FLAVOR = 4, + EFFECT_DANGER_ANNOYING = 4, + EFFECT_DANGER_HINDRANCE = 0, + EFFECT_DANGER_HARMFUL = 0, + EFFECT_DANGER_DEADLY = 0, + ) + var/virus_choice = pick(subtypesof(/datum/disease/advanced)- typesof(/datum/disease/advanced/premade)) + var/datum/disease/advanced/D = new virus_choice + + D.makerandom(list(30,55),list(0,50),anti,bad,null) + + D.log += "
[ROUND_TIME()] Infected [key_name(owner)]" + if(!length(owner)) + owner.diseases = list() + owner.diseases += D + + D.AddToGoggleView(owner) + /obj/item/organ/internal/heart/gland/viral/proc/random_virus(max_symptoms, max_level) if(max_symptoms > VIRUS_SYMPTOM_LIMIT) diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm index 4b0f92630389..9b9cabf2b0aa 100644 --- a/code/modules/antagonists/changeling/changeling.dm +++ b/code/modules/antagonists/changeling/changeling.dm @@ -412,6 +412,7 @@ purchased_powers[power_path] = new_action new_action.on_purchase(owner.current) // Grant() is ran in this proc, see changeling_powers.dm. log_changeling_power("[key_name(owner)] adapted the [new_action] power") + add_event_to_buffer(owner, data = "adapted the [new_action] power.", log_key = "CHANGELING") return TRUE @@ -436,6 +437,7 @@ can_respec = FALSE SSblackbox.record_feedback("tally", "changeling_power_purchase", 1, "Readapt") log_changeling_power("[key_name(owner)] readapted their changeling powers") + add_event_to_buffer(owner, data = "readapted their changeling powers", log_key = "CHANGELING") return TRUE /* diff --git a/code/modules/antagonists/disease/disease_abilities.dm b/code/modules/antagonists/disease/disease_abilities.dm index 1925877fee25..c7d8a9997fd7 100644 --- a/code/modules/antagonists/disease/disease_abilities.dm +++ b/code/modules/antagonists/disease/disease_abilities.dm @@ -10,33 +10,20 @@ 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/chills, 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/disfiguration, new /datum/disease_ability/symptom/medium/polyvitiligo, 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, @@ -46,7 +33,6 @@ 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 @@ -328,19 +314,12 @@ new /datum/disease_ability/symptom/powerful/youth /******MEDIUM******/ -/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) - short_desc = "Cause victims to hallucinate." - long_desc = "Cause victims to hallucinate. Decreases stats, especially resistance." - /datum/disease_ability/symptom/medium/choking symptoms = list(/datum/symptom/choking) short_desc = "Cause victims to choke." @@ -351,41 +330,17 @@ new /datum/disease_ability/symptom/powerful/youth short_desc = "Cause victims to become confused." long_desc = "Cause victims to become confused intermittently." -/datum/disease_ability/symptom/medium/vomit - symptoms = list(/datum/symptom/vomit) - 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/medium/voice_change symptoms = list(/datum/symptom/voice_change) short_desc = "Change the voice of victims." long_desc = "Change the voice of victims, causing confusion in communications." -/datum/disease_ability/symptom/medium/visionloss - symptoms = list(/datum/symptom/visionloss) - 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/medium/fever symptoms = list(/datum/symptom/fever) -/datum/disease_ability/symptom/medium/chills - symptoms = list(/datum/symptom/chills) - /datum/disease_ability/symptom/medium/headache symptoms = list(/datum/symptom/headache) -/datum/disease_ability/symptom/medium/viraladaptation - symptoms = list(/datum/symptom/viraladaptation) - 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/medium/polyvitiligo symptoms = list(/datum/symptom/polyvitiligo) @@ -397,19 +352,6 @@ new /datum/disease_ability/symptom/powerful/youth short_desc = "Cause victims to itch." long_desc = "Cause victims to itch, increasing all stats except stealth." -/datum/disease_ability/symptom/medium/heal/weight_loss - symptoms = list(/datum/symptom/weight_loss) - 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/medium/heal/mind_restoration - symptoms = list(/datum/symptom/mind_restoration) - /******POWERFUL******/ /datum/disease_ability/symptom/powerful/fire @@ -418,21 +360,10 @@ new /datum/disease_ability/symptom/powerful/youth /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 diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm index 4ab42df425de..58307ebbdec7 100644 --- a/code/modules/antagonists/heretic/heretic_antag.dm +++ b/code/modules/antagonists/heretic/heretic_antag.dm @@ -140,6 +140,7 @@ return TRUE log_heretic_knowledge("[key_name(owner)] gained knowledge: [initial(researched_path.name)]") + add_event_to_buffer(owner, data = "gained knowledge: [initial(researched_path.name)]", log_key = "HERETIC") knowledge_points -= initial(researched_path.cost) return TRUE diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm index 55c3cead1a7c..38b14e65b313 100644 --- a/code/modules/antagonists/heretic/heretic_knowledge.dm +++ b/code/modules/antagonists/heretic/heretic_knowledge.dm @@ -634,6 +634,7 @@ to_chat(user, span_hypnophrase(span_big("[drain_message]"))) desc += " (Completed!)" log_heretic_knowledge("[key_name(user)] completed a [name] at [worldtime2text()].") + add_event_to_buffer(user, data = "completed a [name] at [worldtime2text()].", log_key = "HERETIC") user.add_mob_memory(/datum/memory/heretic_knowlege_ritual) return TRUE @@ -659,6 +660,10 @@ They have [length(our_heretic.researched_knowledge)] knowledge nodes researched, totalling [total_points] points \ and have sacrificed [our_heretic.total_sacrifices] people ([our_heretic.high_value_sacrifices] of which were high value)") + add_event_to_buffer(user, data = "gained knowledge of their final ritual at [worldtime2text()]. \ + They have [length(our_heretic.researched_knowledge)] knowledge nodes researched, totalling [total_points] points \ + and have sacrificed [our_heretic.total_sacrifices] people ([our_heretic.high_value_sacrifices] of which were high value)", log_key = "HERETIC") + /datum/heretic_knowledge/ultimate/can_be_invoked(datum/antagonist/heretic/invoker) if(invoker.ascended) return FALSE @@ -703,6 +708,7 @@ SSblackbox.record_feedback("tally", "heretic_ascended", 1, route) log_heretic_knowledge("[key_name(user)] completed their final ritual at [worldtime2text()].") + add_event_to_buffer(user, data = "completed their final ritual at [worldtime2text()].", log_key = "HERETIC") return TRUE /datum/heretic_knowledge/ultimate/cleanup_atoms(list/selected_atoms) diff --git a/code/modules/antagonists/malf_ai/malf_ai_module_picker.dm b/code/modules/antagonists/malf_ai/malf_ai_module_picker.dm index 51058f82ac02..d96348a54a52 100644 --- a/code/modules/antagonists/malf_ai/malf_ai_module_picker.dm +++ b/code/modules/antagonists/malf_ai/malf_ai_module_picker.dm @@ -123,4 +123,5 @@ action.build_all_button_icons() processing_time -= AM.cost log_malf_upgrades("[key_name(AI)] purchased [AM.name]") + add_event_to_buffer(AI, data = "purchased [AM.name].", log_key = "MALF") SSblackbox.record_feedback("nested tally", "malfunction_modules_bought", 1, list("[initial(AM.name)]", "[AM.cost]")) diff --git a/code/modules/antagonists/traitor/traitor_objective.dm b/code/modules/antagonists/traitor/traitor_objective.dm index 30a8ba679441..0a4558fc1376 100644 --- a/code/modules/antagonists/traitor/traitor_objective.dm +++ b/code/modules/antagonists/traitor/traitor_objective.dm @@ -190,6 +190,7 @@ SEND_SIGNAL(src, COMSIG_TRAITOR_OBJECTIVE_FAILED) handle_cleanup() log_traitor("[key_name(handler.owner)] [objective_state == OBJECTIVE_STATE_INACTIVE? "missed" : "failed"] [to_debug_string()]") + add_event_to_buffer(handler.owner, data = "[objective_state == OBJECTIVE_STATE_INACTIVE? "missed" : "failed"] [to_debug_string()]", log_key = "TRAITOR") if(penalty_cost) handler.telecrystals -= penalty_cost objective_state = OBJECTIVE_STATE_FAILED @@ -207,6 +208,7 @@ SEND_SIGNAL(src, COMSIG_TRAITOR_OBJECTIVE_COMPLETED) handle_cleanup() log_traitor("[key_name(handler.owner)] [objective_state == OBJECTIVE_STATE_INACTIVE? "missed" : "completed"] [to_debug_string()]") + add_event_to_buffer(handler.owner, data = "[objective_state == OBJECTIVE_STATE_INACTIVE? "missed" : "completed"] [to_debug_string()]", log_key = "TRAITOR") objective_state = OBJECTIVE_STATE_COMPLETED SEND_GLOBAL_SIGNAL(COMSIG_GLOB_TRAITOR_OBJECTIVE_COMPLETED, src) save_objective() @@ -246,6 +248,7 @@ /datum/traitor_objective/proc/on_objective_taken(mob/user) SStraitor.on_objective_taken(src) log_traitor("[key_name(handler.owner)] has taken an objective: [to_debug_string()]") + add_event_to_buffer(handler.owner, data = "has taken an objective: [to_debug_string()]", log_key = "TRAITOR") /// Used for generating the UI buttons for the UI. Use ui_perform_action to respond to clicks. /datum/traitor_objective/proc/generate_ui_buttons(mob/user) diff --git a/code/modules/antagonists/traitor/uplink_handler.dm b/code/modules/antagonists/traitor/uplink_handler.dm index 40b5d97c0cff..458bd7392aa5 100644 --- a/code/modules/antagonists/traitor/uplink_handler.dm +++ b/code/modules/antagonists/traitor/uplink_handler.dm @@ -161,6 +161,8 @@ return objective.forced = force log_traitor("[key_name(owner)] has received a potential objective: [objective.to_debug_string()] | Forced: [force]") + add_event_to_buffer(owner, data = "has received a potential objective: [objective.to_debug_string()] | Forced: [force]", log_key = "TRAITOR") + objective.original_progression = objective.progression_reward objective.update_progression_reward() potential_objectives += objective diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm index 52b0726282a3..82e3da8a7f1e 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm @@ -99,6 +99,7 @@ if(log_buy) log_spellbook("[key_name(user)] improved their knowledge of [initial(existing.name)] to level [existing.spell_level] for [cost] points") + add_event_to_buffer(user, data = "improved their knowledge of [initial(existing.name)] to level [existing.spell_level] for [cost] points", log_key = "WIZARD") SSblackbox.record_feedback("nested tally", "wizard_spell_improved", 1, list("[name]", "[existing.spell_level]")) log_purchase(user.key) return existing @@ -110,6 +111,7 @@ if(log_buy) log_spellbook("[key_name(user)] learned [new_spell] for [cost] points") + add_event_to_buffer(user, data = "learned [new_spell] for [cost] points", log_key = "WIZARD") SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name) log_purchase(user.key) return new_spell @@ -176,6 +178,7 @@ qdel(to_refund) name = initial(name) log_spellbook("[key_name(user)] refunded [src] for [amount_to_refund] points") + add_event_to_buffer(user, data = "refunded [src] for [amount_to_refund] points", log_key = "WIZARD") return amount_to_refund return -1 @@ -203,6 +206,7 @@ var/atom/spawned_path = new item_path(user.loc) if(log_buy) log_spellbook("[key_name(user)] bought [src] for [cost] points") + add_event_to_buffer(user, data = "bought [src] for [cost] points", log_key = "WIZARD") SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name) log_purchase(user.key) try_equip_item(user, spawned_path) @@ -223,6 +227,7 @@ /datum/spellbook_entry/summon/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE) if(log_buy) log_spellbook("[key_name(user)] cast [src] for [cost] points") + add_event_to_buffer(user, data = "cast [src] for [cost] points", log_key = "WIZARD") SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name) log_purchase(user.key) return TRUE diff --git a/code/modules/asset_cache/assets/rcd.dm b/code/modules/asset_cache/assets/rcd.dm index bbb469860907..2125c67f6878 100644 --- a/code/modules/asset_cache/assets/rcd.dm +++ b/code/modules/asset_cache/assets/rcd.dm @@ -37,7 +37,7 @@ "Medical" = 'icons/obj/doors/airlocks/station/medical.dmi', "Research" = 'icons/obj/doors/airlocks/station/research.dmi', "Freezer" = 'icons/obj/doors/airlocks/station/freezer.dmi', - "Virology" = 'icons/obj/doors/airlocks/station/virology.dmi', + "Pathology" = 'icons/obj/doors/airlocks/station/virology.dmi', "Mining" = 'icons/obj/doors/airlocks/station/mining.dmi', "Maintenance" = 'icons/obj/doors/airlocks/station/maintenance.dmi', "External" = 'icons/obj/doors/airlocks/external/external.dmi', diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm index 784e0b424c97..560bfc524999 100644 --- a/code/modules/atmospherics/machinery/portable/scrubber.dm +++ b/code/modules/atmospherics/machinery/portable/scrubber.dm @@ -63,6 +63,8 @@ scrub(target.return_air()) for(var/turf/open/open_turf in view(3, src)) + if(!isopenturf(open_turf)) + continue if(open_turf.pollution) open_turf.pollution.scrub_amount(POLLUTION_HEIGHT_DIVISOR) diff --git a/code/modules/bitrunning/virtual_domain/domains/gondola_asteroid.dm b/code/modules/bitrunning/virtual_domain/domains/gondola_asteroid.dm index 01d58e398038..bb758ef40dfb 100644 --- a/code/modules/bitrunning/virtual_domain/domains/gondola_asteroid.dm +++ b/code/modules/bitrunning/virtual_domain/domains/gondola_asteroid.dm @@ -28,8 +28,4 @@ /datum/reagent/gondola_mutation_toxin/virtual_domain name = "Advanced Tranquility" - gondola_disease = /datum/disease/transformation/gondola/virtual_domain - -/datum/disease/transformation/gondola/virtual_domain - stage_prob = 9 - new_form = /mob/living/simple_animal/pet/gondola/virtual_domain + disease_cat = DISEASE_GONDOLA_DIGITAL diff --git a/code/modules/cargo/bounties/reagent.dm b/code/modules/cargo/bounties/reagent.dm index 1ae252c096e9..6430451282c0 100644 --- a/code/modules/cargo/bounties/reagent.dm +++ b/code/modules/cargo/bounties/reagent.dm @@ -214,7 +214,7 @@ /datum/bounty/pill/simple_pill/New() //reagent that are possible to be chem factory'd var/static/list/possible_reagents = list(\ - /datum/reagent/medicine/spaceacillin,\ + /datum/reagent/medicine/antipathogenic/spaceacillin,\ /datum/reagent/medicine/c2/synthflesh,\ /datum/reagent/medicine/pen_acid,\ /datum/reagent/medicine/atropine,\ diff --git a/code/modules/cargo/packs/medical.dm b/code/modules/cargo/packs/medical.dm index c6d52730d6e1..099ff13b136b 100644 --- a/code/modules/cargo/packs/medical.dm +++ b/code/modules/cargo/packs/medical.dm @@ -135,11 +135,7 @@ /obj/item/reagent_containers/cup/bottle/cold, /obj/item/reagent_containers/cup/bottle/random_virus = 4, /obj/item/reagent_containers/cup/bottle/fake_gbs, - /obj/item/reagent_containers/cup/bottle/magnitis, - /obj/item/reagent_containers/cup/bottle/pierrot_throat, /obj/item/reagent_containers/cup/bottle/brainrot, - /obj/item/reagent_containers/cup/bottle/anxiety, - /obj/item/reagent_containers/cup/bottle/beesease, /obj/item/storage/box/syringes, /obj/item/storage/box/beakers, /obj/item/reagent_containers/cup/bottle/mutagen, diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 368e1e726d82..e1b61e99132c 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -518,6 +518,10 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( //This is down here because of the browse() calls in tooltip/New() if(!tooltips) tooltips = new /datum/tooltip(src) + + if(((player_age != -1) && player_age < CONFIG_GET(number/minimum_age)) && !(ckey in GLOB.interviews.approved_ckeys)) + interviewee = TRUE + register_for_interview() if (!interviewee) initialize_menus() diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index 43fa4f803d26..ba9dcde6d9f9 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -71,7 +71,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") if(!(prefs.chat_toggles & CHAT_OOC)) to_chat(src, span_danger("You have OOC muted.")) return - + add_event_to_buffer(mob, data = raw_msg, log_key = "OOC") mob.log_talk(raw_msg, LOG_OOC) var/keyname = key diff --git a/code/modules/clothing/gloves/special.dm b/code/modules/clothing/gloves/special.dm index 4ba9cb144861..22aa4e6c305a 100644 --- a/code/modules/clothing/gloves/special.dm +++ b/code/modules/clothing/gloves/special.dm @@ -113,9 +113,13 @@ desc = "Pricy sterile gloves that are thicker than latex. Excellent grip ensures very fast carrying of patients along with the faster use time of various chemical related items." icon_state = "nitrile" inhand_icon_state = "greyscale_gloves" + armor_type = /datum/armor/nitrile greyscale_colors = "#99eeff" clothing_traits = list(TRAIT_QUICKER_CARRY, TRAIT_FASTMED) +/datum/armor/nitrile + bio = 100 + /obj/item/clothing/gloves/tinkerer name = "tinker's gloves" desc = "Overdesigned engineering gloves that have automated construction subrutines dialed in, allowing for faster construction while worn." diff --git a/code/modules/clothing/spacesuits/_spacesuits.dm b/code/modules/clothing/spacesuits/_spacesuits.dm index 9e22760d68eb..d3b5682cc332 100644 --- a/code/modules/clothing/spacesuits/_spacesuits.dm +++ b/code/modules/clothing/spacesuits/_spacesuits.dm @@ -299,9 +299,9 @@ user.apply_status_effect(/datum/status_effect/freon) if(!ishuman(user)) return FIRELOSS - var/mob/living/carbon/human/humanafterall = user - var/datum/disease/advance/cold/pun = new //in the show, arnold survives his stunt, but catches a cold because of it - humanafterall.ForceContractDisease(pun, FALSE, TRUE) //this'll show up on health analyzers and the like + //var/mob/living/carbon/human/humanafterall = user + //var/datum/disease/advance/cold/pun = new //in the show, arnold survives his stunt, but catches a cold because of it + //humanafterall.ForceContractDisease(pun, FALSE, TRUE) //this'll show up on health analyzers and the like return FIRELOSS #undef THERMAL_REGULATOR_COST diff --git a/code/modules/clothing/under/jobs/medical.dm b/code/modules/clothing/under/jobs/medical.dm index 4014814ee231..4ff11f78f5c1 100644 --- a/code/modules/clothing/under/jobs/medical.dm +++ b/code/modules/clothing/under/jobs/medical.dm @@ -65,12 +65,12 @@ /obj/item/clothing/under/rank/medical/virologist desc = "It's made of a special fiber that gives special protection against biohazards. It has a virologist rank stripe on it." - name = "virologist's jumpsuit" + name = "pathologist's jumpsuit" icon_state = "virology" inhand_icon_state = "w_suit" /obj/item/clothing/under/rank/medical/virologist/skirt - name = "virologist's jumpskirt" + name = "pathologist's jumpskirt" desc = "It's made of a special fiber that gives special protection against biohazards. It has a virologist rank stripe on it." icon_state = "virologywhite_skirt" inhand_icon_state = "w_suit" diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm index 09994933c398..1d2772304b4e 100644 --- a/code/modules/events/disease_outbreak.dm +++ b/code/modules/events/disease_outbreak.dm @@ -106,30 +106,31 @@ var/datum/round_event_control/disease_outbreak/disease_event = control afflicted += disease_event.disease_candidates disease_event.disease_candidates.Cut() //Clean the list after use - if(!virus_type) - var/list/virus_candidates = list() - //Practically harmless diseases. Mostly just gives medical something to do. - virus_candidates += list(/datum/disease/flu, /datum/disease/cold9) - - //The more dangerous ones - virus_candidates += list(/datum/disease/beesease, /datum/disease/brainrot, /datum/disease/fluspanish) - - //The wacky ones - virus_candidates += list(/datum/disease/magnitis, /datum/disease/anxiety) - - //The rest of the diseases either aren't conventional "diseases" or are too unique/extreme to be considered for a normal event - virus_type = pick(virus_candidates) - - var/datum/disease/new_disease - new_disease = new virus_type() + var/virus_choice = pick(subtypesof(/datum/disease/advanced)- typesof(/datum/disease/advanced/premade)) + var/list/anti = list( + ANTIGEN_BLOOD = 1, + ANTIGEN_COMMON = 1, + ANTIGEN_RARE = 2, + ANTIGEN_ALIEN = 0, + ) + var/list/bad = list( + EFFECT_DANGER_HELPFUL = 0, + EFFECT_DANGER_FLAVOR = 1, + EFFECT_DANGER_ANNOYING = 2, + EFFECT_DANGER_HINDRANCE = 3, + EFFECT_DANGER_HARMFUL = 1, + EFFECT_DANGER_DEADLY = 0, + ) + var/datum/disease/advanced/new_disease = new virus_choice + new_disease.makerandom(list(50,90),list(50,100),anti,bad,src) new_disease.carrier = TRUE illness_type = new_disease.name var/mob/living/carbon/human/victim while(length(afflicted)) victim = pick_n_take(afflicted) - if(victim.ForceContractDisease(new_disease, FALSE)) + if(victim.infect_disease(new_disease, TRUE, notes = "Infected via Outbreak [key_name(victim)]")) message_admins("Event triggered: Disease Outbreak - [new_disease.name] starting with patient zero [ADMIN_LOOKUPFLW(victim)]!") log_game("Event triggered: Disease Outbreak - [new_disease.name] starting with patient zero [key_name(victim)].") announce_to_ghosts(victim) @@ -215,213 +216,38 @@ afflicted += disease_event.disease_candidates disease_event.disease_candidates.Cut() - if(!max_symptoms) - max_symptoms = rand(ADV_MIN_SYMPTOMS, ADV_MAX_SYMPTOMS) - - if(!requested_severity) - var/rng_severity = rand(1, 100) - if(rng_severity < ADV_RNG_LOW) - requested_severity = ADV_DISEASE_MEDIUM - - else if(rng_severity < ADV_RNG_MID) - requested_severity = ADV_DISEASE_HARMFUL - - else - requested_severity = ADV_DISEASE_DANGEROUS - - var/datum/disease/advance/advanced_disease = new /datum/disease/advance/random/event(max_symptoms, requested_severity) - - var/list/name_symptoms = list() - for(var/datum/symptom/new_symptom as anything in advanced_disease.symptoms) - name_symptoms += new_symptom.name - - illness_type = advanced_disease.name + var/virus_choice = pick(subtypesof(/datum/disease/advanced)- typesof(/datum/disease/advanced/premade)) + var/list/anti = list( + ANTIGEN_BLOOD = 1, + ANTIGEN_COMMON = 1, + ANTIGEN_RARE = 2, + ANTIGEN_ALIEN = 0, + ) + var/list/bad = list( + EFFECT_DANGER_HELPFUL = 0, + EFFECT_DANGER_FLAVOR = 0, + EFFECT_DANGER_ANNOYING = 2, + EFFECT_DANGER_HINDRANCE = 3, + EFFECT_DANGER_HARMFUL = 3, + EFFECT_DANGER_DEADLY = 1, + ) + var/datum/disease/advanced/new_disease = new virus_choice + new_disease.makerandom(list(50,90),list(50,100),anti,bad,src) + new_disease.carrier = TRUE + illness_type = new_disease.name var/mob/living/carbon/human/victim while(length(afflicted)) victim = pick_n_take(afflicted) - if(victim.ForceContractDisease(advanced_disease, FALSE)) - message_admins("Event triggered: Disease Outbreak: Advanced - starting with patient zero [ADMIN_LOOKUPFLW(victim)]! Details: [advanced_disease.admin_details()] sp:[advanced_disease.spread_flags] ([advanced_disease.spread_text])") - log_game("Event triggered: Disease Outbreak: Advanced - starting with patient zero [key_name(victim)]. Details: [advanced_disease.admin_details()] sp:[advanced_disease.spread_flags] ([advanced_disease.spread_text])") - log_virus("Disease Outbreak: Advanced has triggered a custom virus outbreak of [advanced_disease.admin_details()] in [victim]!") + if(victim.infect_disease(new_disease, TRUE, notes = "Infected via Outbreak [key_name(victim)]")) + message_admins("Event triggered: Disease Outbreak - [new_disease.name] starting with patient zero [ADMIN_LOOKUPFLW(victim)]!") + log_game("Event triggered: Disease Outbreak - [new_disease.name] starting with patient zero [key_name(victim)].") announce_to_ghosts(victim) return CHECK_TICK //don't lag the server to death if(isnull(victim)) log_game("Event Disease Outbreak: Advanced attempted to start, but failed.") -/datum/disease/advance/random/event - name = "Event Disease" - copy_type = /datum/disease/advance - -/datum/round_event/disease_outbreak/advance/setup() - announce_when = ADV_ANNOUNCE_DELAY - setup = TRUE //MONKESTATION ADDITION - -/** - * Generate advanced virus - * - * Uses the parameters to create a list of symptoms, picking from various severities - * Viral Evolution and Eternal Youth are special modifiers, so we roll separately. - */ -/datum/disease/advance/random/event/New(max_symptoms, requested_severity) - var/list/datum/symptom/possible_symptoms = list( - /datum/symptom/beard, - /datum/symptom/chills, - /datum/symptom/confusion, - /datum/symptom/cough, - /datum/symptom/disfiguration, - /datum/symptom/dizzy, - /datum/symptom/fever, - /datum/symptom/hallucigen, - /datum/symptom/headache, - /datum/symptom/itching, - /datum/symptom/polyvitiligo, - /datum/symptom/shedding, - /datum/symptom/sneeze, - /datum/symptom/voice_change, - ) - - switch(requested_severity) - if(ADV_DISEASE_HARMFUL) - possible_symptoms += list( - /datum/symptom/choking, - /datum/symptom/deafness, - /datum/symptom/genetic_mutation, - /datum/symptom/narcolepsy, - /datum/symptom/vomit, - /datum/symptom/weight_loss, - ) - - if(ADV_DISEASE_DANGEROUS) - possible_symptoms += list( - /datum/symptom/alkali, - /datum/symptom/asphyxiation, - /datum/symptom/fire, - /datum/symptom/flesh_death, - /datum/symptom/flesh_eating, - /datum/symptom/visionloss, - ) - - var/current_severity = 0 - - while(symptoms.len < max_symptoms) - var/datum/symptom/chosen_symptom = pick_n_take(possible_symptoms) - - if(!chosen_symptom) - stack_trace("Advanced disease could not pick a symptom!") - return - - //Checks if the chosen symptom is severe enough to meet requested severity. If not, pick a new symptom. - //If we've met requested severity already, we don't care and will keep the chosen symptom. - var/datum/symptom/new_symptom = new chosen_symptom - - if((current_severity < requested_severity) && (new_symptom.severity < requested_severity)) - continue - - symptoms += new_symptom - - //Applies the illness name based on the most severe symptom. - if(new_symptom.severity > current_severity) - name = "[new_symptom.illness]" - current_severity = new_symptom.severity - - //Modifiers to keep the disease base stats above 0 (unless RNG gets a really bad roll.) - //Eternal Youth for +4 to resistance and stage speed. - //Viral modifiers to slow down/resist or go fast and loud. - if(prob(66)) - var/list/datum/symptom/possible_modifiers = list( - /datum/symptom/viraladaptation, - /datum/symptom/viralevolution, - ) - var/datum/symptom/chosen_modifier = pick(possible_modifiers) - symptoms += new chosen_modifier - symptoms += new /datum/symptom/youth - - Refresh() - -/** - * Assign virus properties - * - * Now that we've picked our symptoms and severity, we determine the other stats - * (Stage Speed, Resistance, Transmissibility) - * The LOW/MID percentiles can be adjusted in the defines. - * If the virus is severity DANGEROUS we do not hide it from health scanners at event start. - * If the virus is airborne, also don't hide it. - */ -/datum/disease/advance/random/event/assign_properties() - - if(!length(properties)) - stack_trace("Advanced virus properties were empty or null!") - return - - addtimer(CALLBACK(src, PROC_REF(make_visible)), ((ADV_ANNOUNCE_DELAY * 2) - 10) SECONDS) - - properties["transmittable"] = rand(4,7) - spreading_modifier = max(CEILING(0.4 * properties["transmittable"], 1), 1) - cure_chance = clamp(7.5 - (0.5 * properties["resistance"]), 5, 10) // Can be between 5 and 10 - stage_prob = max(0.4 * properties["stage_rate"], 1) - set_severity(properties["severity"]) - visibility_flags |= HIDDEN_SCANNER - - //If we have an advanced (high stage) disease, add it to the name. - if(properties["stage_rate"] >= 7) - name = "Advanced [name]" - - //If severe enough, alert immediately on scanners - if(severity == "Dangerous" || severity == "BIOHAZARD") - visibility_flags &= ~HIDDEN_SCANNER - set_spread(DISEASE_SPREAD_CONTACT_SKIN) - - else - var/transmissibility = rand(1, 100) - - if(transmissibility < ADV_SPREAD_LOW) - set_spread(DISEASE_SPREAD_CONTACT_FLUIDS) - - else if(transmissibility < ADV_SPREAD_MID) - set_spread(DISEASE_SPREAD_CONTACT_SKIN) - - else - set_spread(DISEASE_SPREAD_AIRBORNE) - visibility_flags &= ~HIDDEN_SCANNER - - generate_cure(properties) - -/** - * Set the transmission methods on the generated virus - * - * Apply the transmission methods we rolled in the assign_properties proc - */ -/datum/disease/advance/random/event/set_spread(spread_id) - switch(spread_id) - if(DISEASE_SPREAD_CONTACT_FLUIDS) - spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_FLUIDS - spread_text = "Fluids" - if(DISEASE_SPREAD_CONTACT_SKIN) - spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_FLUIDS | DISEASE_SPREAD_CONTACT_SKIN - spread_text = "Skin contact" - if(DISEASE_SPREAD_AIRBORNE) - spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_FLUIDS | DISEASE_SPREAD_CONTACT_SKIN | DISEASE_SPREAD_AIRBORNE - spread_text = "Respiration" - -/** - * Determine the cure - * - * Rolls one of five possible cure groups, then selects a cure from it and applies it to the virus. - */ -/datum/disease/advance/random/event/generate_cure() - if(!length(properties)) - stack_trace("Advanced virus properties were empty or null!") - return - - var/res = rand(4, 7) - cures = list(pick(advance_cures[res])) - oldres = res - // Get the cure name from the cure_id - var/datum/reagent/cure = GLOB.chemical_reagents_list[cures[1]] - cure_text = cure.name - #undef ADV_MIN_SYMPTOMS #undef ADV_MAX_SYMPTOMS #undef ADV_ANNOUNCE_DELAY diff --git a/code/modules/events/heart_attack.dm b/code/modules/events/heart_attack.dm index 46f1ae3857de..0cdadfbc499f 100644 --- a/code/modules/events/heart_attack.dm +++ b/code/modules/events/heart_attack.dm @@ -30,7 +30,7 @@ /datum/round_event_control/heart_attack/proc/generate_candidates() heart_attack_candidates.Cut() for(var/mob/living/carbon/human/candidate in shuffle(GLOB.player_list)) - if(candidate.stat == DEAD || HAS_TRAIT(candidate, TRAIT_CRITICAL_CONDITION) || !candidate.can_heartattack() || (/datum/disease/heart_failure in candidate.diseases) || candidate.undergoing_cardiac_arrest()) + if(candidate.stat == DEAD || HAS_TRAIT(candidate, TRAIT_CRITICAL_CONDITION) || !candidate.can_heartattack() || candidate.undergoing_cardiac_arrest()) continue if(!(candidate.mind.assigned_role.job_flags & JOB_CREW_MEMBER))//only crewmembers can get one, a bit unfair for some ghost roles and it wastes the event continue @@ -74,8 +74,7 @@ message_admins("[winner] has just survived a random heart attack!") //time to spawn them a trophy :) victims -= winner else - var/datum/disease/heart_disease = new /datum/disease/heart_failure() - winner.ForceContractDisease(heart_disease, FALSE, TRUE) + winner.infect_disease_predefined(DISEASE_HEART, TRUE) announce_to_ghosts(winner) victims -= winner return TRUE diff --git a/code/modules/events/shuttle_loan/shuttle_loan_datum.dm b/code/modules/events/shuttle_loan/shuttle_loan_datum.dm index 4f5564563f90..060b15efaef4 100644 --- a/code/modules/events/shuttle_loan/shuttle_loan_datum.dm +++ b/code/modules/events/shuttle_loan/shuttle_loan_datum.dm @@ -45,8 +45,6 @@ var/turf/assistant_turf = pick_n_take(empty_shuttle_turfs) new infected_assistant(assistant_turf) spawn_list.Add(/obj/structure/closet/crate) - spawn_list.Add(/obj/item/reagent_containers/cup/bottle/pierrot_throat) - spawn_list.Add(/obj/item/reagent_containers/cup/bottle/magnitis) /datum/shuttle_loan_situation/department_resupply sender = "CentCom Supply Department" diff --git a/code/modules/jobs/job_types/virologist.dm b/code/modules/jobs/job_types/virologist.dm index d49af7b29b32..3d45ab588587 100644 --- a/code/modules/jobs/job_types/virologist.dm +++ b/code/modules/jobs/job_types/virologist.dm @@ -40,13 +40,14 @@ /datum/outfit/job/virologist - name = "Virologist" + name = "Pathologist" jobtype = /datum/job/virologist id_trim = /datum/id_trim/job/virologist uniform = /obj/item/clothing/under/rank/medical/virologist backpack_contents = list( /obj/item/extrapolator = 1, + /obj/item/storage/box/vials = 1, ) suit = /obj/item/clothing/suit/toggle/labcoat/virologist suit_store = /obj/item/flashlight/pen diff --git a/code/modules/mob/dead/new_player/login.dm b/code/modules/mob/dead/new_player/login.dm index 4f021d61e5e3..6b60c51f61bd 100644 --- a/code/modules/mob/dead/new_player/login.dm +++ b/code/modules/mob/dead/new_player/login.dm @@ -14,13 +14,13 @@ mind.active = TRUE mind.set_current(src) + if((client.player_age != -1) && client.player_age <= CONFIG_GET(number/minimum_age) && !(client.ckey in GLOB.interviews.approved_ckeys)) + client.interviewee = TRUE + // Check if user should be added to interview queue if (!client.holder && CONFIG_GET(flag/panic_bunker) && CONFIG_GET(flag/panic_bunker_interview) && !(client.ckey in GLOB.interviews.approved_ckeys)) var/required_living_minutes = CONFIG_GET(number/panic_bunker_living) var/living_minutes = client.get_exp_living(TRUE) - if(client.player_age != -1) - if(!max(0, 14 - client.player_age)) - client.interviewee = TRUE if(!CONFIG_GET(flag/minimum_account_age)) if (required_living_minutes >= living_minutes) client.interviewee = TRUE diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 70c90c87ac63..b85a556154a1 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -263,6 +263,8 @@ continue item.post_equip_item(humanc.client?.prefs, humanc) + DiseaseCarrierCheck(humanc) + /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) @@ -365,3 +367,23 @@ // Add verb for re-opening the interview panel, fixing chat and re-init the verbs for the stat panel add_verb(src, /mob/dead/new_player/proc/open_interview) add_verb(client, /client/verb/fix_tgui_panel) + +/client/proc/register_for_interview() + // First we detain them by removing all the verbs they have on client + for (var/v in verbs) + var/procpath/verb_path = v + remove_verb(src, verb_path) + + // Then remove those on their mob as well + for (var/v in mob.verbs) + var/procpath/verb_path = v + remove_verb(mob, verb_path) + + // Then we create the interview form and show it to the client + var/datum/interview/I = GLOB.interviews.interview_for_client(mob) + if (I) + I.ui_interact(mob) + + // Add verb for re-opening the interview panel, fixing chat and re-init the verbs for the stat panel + add_verb(mob, /mob/dead/new_player/proc/open_interview) + add_verb(src, /client/verb/fix_tgui_panel) diff --git a/code/modules/mob/living/basic/drone/_drone.dm b/code/modules/mob/living/basic/drone/_drone.dm index 143d4f618e8d..a430498e2b0c 100644 --- a/code/modules/mob/living/basic/drone/_drone.dm +++ b/code/modules/mob/living/basic/drone/_drone.dm @@ -43,6 +43,7 @@ unique_name = TRUE faction = list(FACTION_NEUTRAL,FACTION_SILICON,FACTION_TURRET) hud_type = /datum/hud/dextrous/drone + dexterous = TRUE // Going for a sort of pale green here lighting_cutoff_red = 30 lighting_cutoff_green = 35 diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm index 607c0e0eb26c..738a69b7b455 100644 --- a/code/modules/mob/living/basic/pets/parrot/poly.dm +++ b/code/modules/mob/living/basic/pets/parrot/poly.dm @@ -229,10 +229,6 @@ animate(filter, alpha = 200, time = 2 SECONDS, loop = -1) animate(alpha = 60, time = 2 SECONDS) - var/datum/disease/parrot_possession/on_possession = new /datum/disease/parrot_possession - on_possession.set_parrot(src) - possessed_human.ForceContractDisease(on_possession, make_copy = FALSE, del_on_fail = TRUE) - #undef POLY_DEFAULT #undef POLY_LONGEST_SURVIVAL #undef POLY_BEATING_DEATHSTREAK diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm index 93e61e6cb826..2bc080de59ef 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm @@ -262,7 +262,7 @@ if(blight.stage < 5) blight.stage++ if(!blightfound) - H.ForceContractDisease(new /datum/disease/revblight(), FALSE, TRUE) + //H.ForceContractDisease(new /datum/disease/revblight(), FALSE, TRUE) to_chat(H, span_revenminor("You feel [pick("suddenly sick", "a surge of nausea", "like your skin is wrong")].")) else if(mob.reagents) diff --git a/code/modules/mob/living/basic/vermin/mouse.dm b/code/modules/mob/living/basic/vermin/mouse.dm index bd9bb8fb3b2d..959318e010a7 100644 --- a/code/modules/mob/living/basic/vermin/mouse.dm +++ b/code/modules/mob/living/basic/vermin/mouse.dm @@ -56,8 +56,6 @@ var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = PROC_REF(on_entered), ) - var/datum/disease/advance/R = new /datum/disease/advance/random(rand(1, 6), 9, 1) - ratdisease += R AddElement(/datum/element/connect_loc, loc_connections) make_tameable() diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 21b337521ff5..6ca62821a3a8 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -221,10 +221,10 @@ if(blood_id == /datum/reagent/blood) //normal blood if(blood_data["viruses"]) for(var/thing in blood_data["viruses"]) - var/datum/disease/D = thing + var/datum/disease/advanced/D = thing if((D.spread_flags & DISEASE_SPREAD_SPECIAL) || (D.spread_flags & DISEASE_SPREAD_NON_CONTAGIOUS)) continue - C.ForceContractDisease(D) + C.infect_disease(D, TRUE, "Infected [key_name(C)] (Infected Blood 100% Infection)") if(!(blood_data["blood_type"] in get_safe_blood(C.dna.blood_type))) C.reagents.add_reagent(/datum/reagent/toxin, amount * 0.5) return TRUE @@ -249,6 +249,9 @@ var/datum/disease/D = thing blood_data["viruses"] += D.Copy() + if (immune_system) + blood_data["immunity"] = immune_system.GetImmunity() + blood_data["blood_DNA"] = dna.unique_enzymes if(LAZYLEN(disease_resistances)) blood_data["resistances"] = disease_resistances.Copy() 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 7d07f1c20224..50ae9ac45ae2 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -67,7 +67,7 @@ var/slowdown = 1 if(ishuman(owner)) var/mob/living/carbon/human/baby_momma = owner - slowdown = baby_momma.reagents.has_reagent(/datum/reagent/medicine/spaceacillin) ? 2 : 1 // spaceacillin doubles the time it takes to grow + slowdown = baby_momma.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin) ? 2 : 1 // spaceacillin doubles the time it takes to grow if(owner.has_status_effect(/datum/status_effect/nest_sustenance)) slowdown *= 0.80 //egg gestates 20% faster if you're trapped in a nest diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 9647d868bd41..adcedd0e9abf 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -6,6 +6,7 @@ GLOB.carbon_list += src AddComponent(/datum/component/carbon_sprint) + immune_system = new(src) var/static/list/loc_connections = list( COMSIG_CARBON_DISARM_PRESHOVE = PROC_REF(disarm_precollide), COMSIG_CARBON_DISARM_COLLIDE = PROC_REF(disarm_collision), @@ -21,6 +22,7 @@ QDEL_LIST(organs) QDEL_LIST(bodyparts) QDEL_LIST(implants) + QDEL_NULL(immune_system) for(var/wound in all_wounds) // these LAZYREMOVE themselves when deleted so no need to remove the list here qdel(wound) for(var/scar in all_scars) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 116e3efea6bc..e01c2ce4bb7d 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -178,15 +178,25 @@ /mob/living/carbon/attack_hand(mob/living/carbon/human/user, list/modifiers) if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_HAND, user, modifiers) & COMPONENT_CANCEL_ATTACK_CHAIN) . = TRUE - for(var/thing in diseases) - var/datum/disease/D = thing - if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN) - user.ContactContractDisease(D) - for(var/thing in user.diseases) - var/datum/disease/D = thing - if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN) - ContactContractDisease(D) + + if(length(diseases) && isliving(user)) + var/mob/living/living = user + var/block = living.check_contact_sterility(BODY_ZONE_EVERYTHING) + var/list/contact = filter_disease_by_spread(diseases, required = DISEASE_SPREAD_CONTACT_SKIN) + if(length(contact) && !block) + for(var/datum/disease/advanced/V as anything in contact) + living.infect_disease(V, notes="(Skin Contact - (Bump), coming from [src])") + + if(isliving(user)) + var/mob/living/living = user + var/block = check_contact_sterility(BODY_ZONE_EVERYTHING) + if(length(living.diseases)) + var/list/contact = filter_disease_by_spread(living.diseases, required = DISEASE_SPREAD_CONTACT_SKIN) + if(length(contact) && !block) + for(var/datum/disease/advanced/V as anything in contact) + infect_disease(V, notes="(Skin Contact - (Bump), coming from [living])") + for(var/datum/surgery/operations as anything in surgeries) if((user.istate & ISTATE_HARM)) @@ -205,6 +215,7 @@ /mob/living/carbon/attack_paw(mob/living/carbon/human/user, list/modifiers) + /* if(try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE)) for(var/thing in diseases) var/datum/disease/D = thing @@ -215,7 +226,7 @@ var/datum/disease/D = thing if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN) ContactContractDisease(D) - + */ if(!(user.istate & ISTATE_HARM)) help_shake_act(user) return FALSE @@ -225,7 +236,7 @@ var/datum/disease/D = thing if(D.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS)) continue - ForceContractDisease(D) + try_contact_infect(D, note="Monkey Bite Infected") return TRUE diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 2660d5e3196f..4d5068bf0bc8 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -171,7 +171,7 @@ var/target_area = parse_zone(check_zone(user.zone_selected)) //our intended target SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting) - + I.disease_contact(src, check_zone(user.zone_selected)) SSblackbox.record_feedback("nested tally", "item_used_for_combat", 1, list("[I.force]", "[I.type]")) SSblackbox.record_feedback("tally", "zone_targeted", 1, target_area) diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 7aa38d0e373f..f2af4b449529 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -21,6 +21,7 @@ //Reagent processing needs to come before breathing, to prevent edge cases. handle_dead_metabolization(seconds_per_tick, times_fired) //Dead metabolization first since it can modify life metabolization. handle_organs(seconds_per_tick, times_fired) + handle_virus_updates(seconds_per_tick) . = ..() if(QDELETED(src)) @@ -86,7 +87,6 @@ environment = loc.return_air() var/datum/gas_mixture/breath - if(!get_organ_slot(ORGAN_SLOT_BREATHING_TUBE)) if(health <= HEALTH_THRESHOLD_FULLCRIT || (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 @@ -151,6 +151,7 @@ loc_as_obj.handle_internal_lifeform(src,0) check_breath(breath) + breath_airborne_diseases() //monkestation edit - VIROLOGY if(breath) loc.assume_air(breath) @@ -351,9 +352,26 @@ else // Miasma sickness if(prob(1 * miasma_pp)) - var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(max_symptoms = 2, max_level = 3) - miasma_disease.name = "Unknown" - ForceContractDisease(miasma_disease, make_copy = TRUE, del_on_fail = TRUE) + var/virus_choice = pick(subtypesof(/datum/disease/advanced)- typesof(/datum/disease/advanced/premade)) + var/list/anti = list( + ANTIGEN_BLOOD = 1, + ANTIGEN_COMMON = 1, + ANTIGEN_RARE = 2, + ANTIGEN_ALIEN = 0, + ) + var/list/bad = list( + EFFECT_DANGER_HELPFUL = 0, + EFFECT_DANGER_FLAVOR = 1, + EFFECT_DANGER_ANNOYING = 2, + EFFECT_DANGER_HINDRANCE = 3, + EFFECT_DANGER_HARMFUL = 1, + EFFECT_DANGER_DEADLY = 0, + ) + var/datum/disease/advanced/new_disease = new virus_choice + new_disease.makerandom(list(50,90),list(50,100),anti,bad,src) + new_disease.carrier = TRUE + new_disease = new_disease.name + infect_disease(new_disease, TRUE, "Miasma Disease Infection [key_name(src)]") // Miasma side-effects. switch(miasma_pp) if(0.25 to 5) @@ -509,9 +527,6 @@ if(SPT_PROB(D.infectivity, seconds_per_tick)) D.spread() - if(stat != DEAD || D.process_dead) - D.stage_act(seconds_per_tick, times_fired) - /mob/living/carbon/handle_wounds(seconds_per_tick, times_fired) for(var/thing in all_wounds) var/datum/wound/W = thing diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 1d9d1f40a46d..aa311698d597 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -95,6 +95,23 @@ if(m_intent == MOVE_INTENT_WALK) return TRUE + if(length(diseases) && isliving(M)) + var/mob/living/living = M + var/block = living.check_contact_sterility(BODY_ZONE_EVERYTHING) + var/list/contact = filter_disease_by_spread(diseases, required = DISEASE_SPREAD_CONTACT_SKIN) + if(length(contact) && !block) + for(var/datum/disease/advanced/V as anything in contact) + living.infect_disease(V, notes="(Skin Contact - (Bump), coming from [src])") + + if(isliving(M)) + var/mob/living/living = M + var/block = check_contact_sterility(BODY_ZONE_EVERYTHING) + if(length(living.diseases)) + var/list/contact = filter_disease_by_spread(living.diseases, required = DISEASE_SPREAD_CONTACT_SKIN) + if(length(contact) && !block) + for(var/datum/disease/advanced/V as anything in contact) + infect_disease(V, notes="(Skin Contact - (Bump), coming from [living])") + SEND_SIGNAL(src, COMSIG_LIVING_MOB_BUMP, M) //Even if we don't push/swap places, we "touched" them, so spread fire spreadFire(M) @@ -119,16 +136,6 @@ var/mob/living/L = M theyre_blocking = L.istate & ISTATE_BLOCKING they_can_move = L.mobility_flags & MOBILITY_MOVE - //Also spread diseases - for(var/thing in diseases) - var/datum/disease/D = thing - if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN) - L.ContactContractDisease(D) - - for(var/thing in L.diseases) - var/datum/disease/D = thing - if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN) - ContactContractDisease(D) //Should stop you pushing a restrained person out of the way if(L.pulledby && L.pulledby != src && HAS_TRAIT(L, TRAIT_RESTRAINED)) @@ -309,9 +316,11 @@ AM.setDir(current_dir) now_pushing = FALSE -/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, supress_message = FALSE) +/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, supress_message = FALSE) if(!AM || !src) return FALSE + if(isturf(AM)) + return FALSE if(!(AM.can_be_pulled(src, state, force))) return FALSE if(throwing || !(mobility_flags & MOBILITY_PULL)) @@ -382,15 +391,22 @@ SEND_SIGNAL(M, COMSIG_LIVING_GET_PULLED, src) //Share diseases that are spread by touch - for(var/thing in diseases) - var/datum/disease/D = thing - if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN) - L.ContactContractDisease(D) - - for(var/thing in L.diseases) - var/datum/disease/D = thing - if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN) - ContactContractDisease(D) + if(length(diseases) && isliving(M)) + var/mob/living/living = M + var/block = living.check_contact_sterility(BODY_ZONE_EVERYTHING) + var/list/contact = filter_disease_by_spread(diseases, required = DISEASE_SPREAD_CONTACT_SKIN) + if(length(contact) && !block) + for(var/datum/disease/advanced/V as anything in contact) + living.infect_disease(V, notes="(Skin Contact - (Grab), coming from [src])") + + if(isliving(M)) + var/mob/living/living = M + var/block = check_contact_sterility(BODY_ZONE_EVERYTHING) + if(length(living.diseases)) + var/list/contact = filter_disease_by_spread(living.diseases, required = DISEASE_SPREAD_CONTACT_SKIN) + if(length(contact) && !block) + for(var/datum/disease/advanced/V as anything in contact) + infect_disease(V, notes="(Skin Contact - (Grab), coming from [living])") if(iscarbon(L)) var/mob/living/carbon/C = L @@ -468,6 +484,8 @@ /mob/living/_pointed(atom/pointing_at) if(!..()) return FALSE + add_event_to_buffer(src, pointing_at, "points at [pointing_at]", "EMOTE") + log_message("points at [pointing_at]", LOG_EMOTE) visible_message("[span_name("[src]")] points at [pointing_at].", span_notice("You point at [pointing_at].")) @@ -506,7 +524,7 @@ /mob/living/incapacitated(flags) if((flags & IGNORE_CRIT) && ((stat >= SOFT_CRIT && (stat != DEAD && stat != UNCONSCIOUS)) && !src.pulledby)) return FALSE - + if(HAS_TRAIT(src, TRAIT_INCAPACITATED)) return TRUE diff --git a/code/modules/mob/living/silicon/ai/vox_sounds.dm b/code/modules/mob/living/silicon/ai/vox_sounds.dm index fd0d04a51ded..ebf74f1a8fb2 100644 --- a/code/modules/mob/living/silicon/ai/vox_sounds.dm +++ b/code/modules/mob/living/silicon/ai/vox_sounds.dm @@ -911,8 +911,8 @@ GLOBAL_LIST_INIT(vox_sounds, list("abduction" = 'sound/vox_fem/abduction.ogg', "victor" = 'sound/vox_fem/victor.ogg', "violated" = 'sound/vox_fem/violated.ogg', "violation" = 'sound/vox_fem/violation.ogg', -"virologist" = 'sound/vox_fem/virologist.ogg', -"virology" = 'sound/vox_fem/virology.ogg', +"pathologist" = 'sound/vox_fem/virologist.ogg', +"pathology" = 'sound/vox_fem/virology.ogg', "virus" = 'sound/vox_fem/virus.ogg', "vitals" = 'sound/vox_fem/vitals.ogg', "v" = 'sound/vox_fem/v.ogg', diff --git a/code/modules/mob/living/silicon/laws.dm b/code/modules/mob/living/silicon/laws.dm index 4f250e05b7f0..02600d62336d 100644 --- a/code/modules/mob/living/silicon/laws.dm +++ b/code/modules/mob/living/silicon/laws.dm @@ -15,6 +15,7 @@ var/list/the_laws = laws.get_law_list(include_zeroth = TRUE) var/lawtext = the_laws.Join(" ") log_silicon("LAW: [key_name(src)] spawned with [lawtext]") + add_event_to_buffer(src, data = "spawned with [lawtext].", log_key = "SILICON") /mob/living/silicon/proc/deadchat_lawchange() var/list/the_laws = laws.get_law_list(include_zeroth = TRUE) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 540b4e2fdffb..7cc41688d3b4 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -81,6 +81,7 @@ logevent("System brought online.") log_silicon("New cyborg [key_name(src)] created with [connected_ai ? "master AI: [key_name(connected_ai)]" : "no master AI"]") + add_event_to_buffer(src, data ="created with [connected_ai ? "master AI: [key_name(connected_ai)]" : "no master AI"].", log_key = "SILICON") log_current_laws() alert_control = new(src, list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER, ALARM_CAMERA, ALARM_BURGLAR, ALARM_MOTION), list(z)) @@ -402,6 +403,7 @@ log_combat(usr, src, "detonated cyborg") log_silicon("CYBORG: [key_name(src)] has been detonated by [key_name(usr)].") + add_event_to_buffer(src, data ="has been detonated by [key_name(usr)].", log_key = "SILICON") if(connected_ai) to_chat(connected_ai, "

[span_alert("ALERT - Cyborg detonation detected: [name]")]
") @@ -419,6 +421,7 @@ set_lockcharge(FALSE) scrambledcodes = TRUE log_silicon("CYBORG: [key_name(src)] has been unlinked from an AI.") + add_event_to_buffer(src, data ="has been unlinked from an AI.", log_key = "SILICON") //Disconnect it's camera so it's not so easily tracked. if(!QDELETED(builtInCamera)) QDEL_NULL(builtInCamera) @@ -735,6 +738,7 @@ update_transform() logevent("Chassis model has been reset.") log_silicon("CYBORG: [key_name(src)] has reset their cyborg model.") + add_event_to_buffer(src, data ="has reset their cyborg model.", log_key = "SILICON") model.transform_to(/obj/item/robot_model) // Remove upgrades. diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index 88cb1f2f0164..5bce8974386d 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -353,17 +353,20 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real logevent("ALERT: Foreign software execution prevented.") to_chat(connected_ai, span_danger("ALERT: Cyborg unit \[[src]\] successfully defended against subversion.")) log_silicon("EMAG: [key_name(user)] attempted to emag cyborg [key_name(src)], but they were slaved to traitor AI [connected_ai].") + add_event_to_buffer(user, src, data ="attempted to emag cyborg [key_name(src)], but they were slaved to traitor AI [connected_ai].", log_key = "SILICON") return TRUE // emag succeeded, it was just counteracted //monkestation edit start if(IS_CLOCK(src)) //cant emag clock borgs to_chat(src, span_brass("The light of Rat'var protects you from subversion!")) log_silicon("EMAG: [key_name(user)] attempted to emag cyborg [key_name(src)], but they were a clockwork borg.") + add_event_to_buffer(user, src, data ="attempted to emag cyborg [key_name(src)], but they were a clockwork borg.", log_key = "SILICON") return TRUE // emag succeeded, it was just counteracted //monkestation edit end if(shell) //AI shells cannot be emagged, so we try to make it look like a standard reset. Smart players may see through this, however. to_chat(user, span_danger("[src] is remotely controlled! Your emag attempt has triggered a system reset instead!")) log_silicon("EMAG: [key_name(user)] attempted to emag an AI shell belonging to [key_name(src) ? key_name(src) : connected_ai]. The shell has been reset as a result.") + add_event_to_buffer(user, src, data ="attempted to emag an AI shell belonging to [key_name(src) ? key_name(src) : connected_ai]. The shell has been reset as a result.", log_key = "SILICON") ResetModel() return TRUE @@ -373,6 +376,7 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real set_connected_ai(null) message_admins("[ADMIN_LOOKUPFLW(user)] emagged cyborg [ADMIN_LOOKUPFLW(src)]. Laws overridden.") log_silicon("EMAG: [key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") + add_event_to_buffer(user, src, data ="emagged cyborg [key_name(src)]. Laws overridden.", log_key = "SILICON") var/time = time2text(world.realtime,"hh:mm:ss") if(user) GLOB.lawchanges.Add("[time] : [user.name]([user.key]) emagged [name]([key])") diff --git a/code/modules/mob/living/silicon/robot/robot_model.dm b/code/modules/mob/living/silicon/robot/robot_model.dm index 71a52382a115..9c93b9acf18c 100644 --- a/code/modules/mob/living/silicon/robot/robot_model.dm +++ b/code/modules/mob/living/silicon/robot/robot_model.dm @@ -241,6 +241,7 @@ cyborg.diag_hud_set_aishell() cyborg.update_icons() log_silicon("CYBORG: [key_name(cyborg)] has transformed into the [new_model] model.") + add_event_to_buffer(cyborg, data ="has transformed into the [new_model] model.", log_key = "SILICON") INVOKE_ASYNC(new_model, PROC_REF(do_transform_animation)) qdel(src) diff --git a/code/modules/mob_spawn/corpses/job_corpses.dm b/code/modules/mob_spawn/corpses/job_corpses.dm index 28c7e8c1b882..b4edda2289eb 100644 --- a/code/modules/mob_spawn/corpses/job_corpses.dm +++ b/code/modules/mob_spawn/corpses/job_corpses.dm @@ -61,15 +61,15 @@ /obj/effect/mob_spawn/corpse/human/assistant/beesease_infection/special(mob/living/spawned_mob) . = ..() - spawned_mob.ForceContractDisease(new /datum/disease/beesease) + //spawned_mob.ForceContractDisease(new /datum/disease/beesease) /obj/effect/mob_spawn/corpse/human/assistant/brainrot_infection/special(mob/living/spawned_mob) . = ..() - spawned_mob.ForceContractDisease(new /datum/disease/brainrot) + //spawned_mob.ForceContractDisease(new /datum/disease/brainrot) /obj/effect/mob_spawn/corpse/human/assistant/spanishflu_infection/special(mob/living/spawned_mob) . = ..() - spawned_mob.ForceContractDisease(new /datum/disease/fluspanish) + //spawned_mob.ForceContractDisease(new /datum/disease/fluspanish) /obj/effect/mob_spawn/corpse/human/bartender name = JOB_BARTENDER diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index c94e74755f9d..229ef7eb2d48 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -316,8 +316,7 @@ drinker.set_jitter_if_lower(700 SECONDS) if(SPT_PROB(0.5, seconds_per_tick) && iscarbon(drinker)) - var/datum/disease/heart_attack = new /datum/disease/heart_failure - drinker.ForceContractDisease(heart_attack) + drinker.infect_disease_predefined(DISEASE_HEART, TRUE) to_chat(drinker, span_userdanger("You're pretty sure you just felt your heart stop for a second there..")) drinker.playsound_local(drinker, 'sound/effects/singlebeat.ogg', 100, 0) diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index 55fa63367ae9..4174ef1ffb99 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -811,7 +811,7 @@ return for(var/possible_purger in kronkaine_fiend.reagents.reagent_list) if(istype(possible_purger, /datum/reagent/medicine/c2/multiver) || istype(possible_purger, /datum/reagent/medicine/haloperidol)) - kronkaine_fiend.ForceContractDisease(new /datum/disease/adrenal_crisis(), FALSE, TRUE) //We punish players for purging, since unchecked purging would allow players to reap the stamina healing benefits without any drawbacks. This also has the benefit of making haloperidol a counter, like it is supposed to be. + kronkaine_fiend.infect_disease_predefined(DISEASE_TRAUMA, TRUE, "[ROUND_TIME()] Infected From Krokaine") break /datum/reagent/drug/kronkaine/overdose_process(mob/living/kronkaine_fiend, seconds_per_tick, times_fired) diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 7452315dd32c..e414c1bbaa6c 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -237,9 +237,9 @@ patient.cure_husk(BURN) patient.visible_message(span_nicegreen("[patient]'s body rapidly absorbs moisture from the environment, taking on a more healthy appearance.")) -/datum/reagent/medicine/spaceacillin +/datum/reagent/medicine/antipathogenic/spaceacillin name = "Spaceacillin" - description = "Spaceacillin will provide limited resistance against disease and parasites. Also reduces infection in serious burns." + description = "A generic antipathogenic agent." color = "#E1F2E6" metabolization_rate = 0.1 * REAGENTS_METABOLISM ph = 8.1 @@ -579,8 +579,7 @@ /datum/reagent/medicine/ephedrine/overdose_process(mob/living/affected_mob, seconds_per_tick, times_fired) if(SPT_PROB(1 * (1 + (1-normalise_creation_purity())), seconds_per_tick) && iscarbon(affected_mob)) - var/datum/disease/D = new /datum/disease/heart_failure - affected_mob.ForceContractDisease(D) + affected_mob.infect_disease_predefined(DISEASE_HEART, TRUE) to_chat(affected_mob, span_userdanger("You're pretty sure you just felt your heart stop for a second there..")) affected_mob.playsound_local(affected_mob, 'sound/effects/singlebeat.ogg', 100, 0) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 2fddba00e8b2..c00b0702f521 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -1,5 +1,19 @@ /datum/reagent/blood - data = list("viruses"=null,"blood_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null,"quirks"=null) + data = list( + "viruses"=null, + "blood_DNA"=null, + "blood_type"=null, + "resistances"=null, + "trace_chem"=null, + "mind"=null, + "ckey"=null, + "gender"=null, + "real_name"=null, + "cloneable"=null, + "factions"=null, + "quirks"=null, + "immunity" = null, + ) name = "Blood" color = "#9e0101" // rgb: 200, 0, 0 metabolization_rate = 12.5 * REAGENTS_METABOLISM //fast rate so it disappears fast. @@ -34,13 +48,13 @@ for(var/thing in data["viruses"]) var/datum/disease/strain = thing - if((strain.spread_flags & DISEASE_SPREAD_SPECIAL) || (strain.spread_flags & DISEASE_SPREAD_NON_CONTAGIOUS)) - continue - - if(methods & (INJECT|INGEST|PATCH)) - exposed_mob.ForceContractDisease(strain) - else if((methods & (TOUCH|VAPOR)) && (strain.spread_flags & DISEASE_SPREAD_CONTACT_FLUIDS)) - exposed_mob.ContactContractDisease(strain) + if(istype(strain, /datum/disease/advanced)) + var/datum/disease/advanced/advanced = strain + if(methods & (INJECT|INGEST|PATCH)) + exposed_mob.infect_disease(advanced, TRUE, "(Contact, splashed with infected blood)") + if((methods & (TOUCH | VAPOR)) && (advanced.spread_flags & DISEASE_SPREAD_BLOOD)) + if(exposed_mob.check_bodypart_bleeding(BODY_ZONE_EVERYTHING)) + exposed_mob.infect_disease(advanced, notes="(Blood, splashed with infected blood)") if(iscarbon(exposed_mob)) var/mob/living/carbon/exposed_carbon = exposed_mob @@ -828,7 +842,7 @@ /datum/reagent/aslimetoxin/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message=TRUE, touch_protection=0) . = ..() if(methods & ~TOUCH) - exposed_mob.ForceContractDisease(new /datum/disease/transformation/slime(), FALSE, TRUE) + exposed_mob.infect_disease_predefined(DISEASE_SLIME, TRUE, "[ROUND_TIME()] Advanced Mutation Toxin Infections [key_name(exposed_mob)]") /datum/reagent/gluttonytoxin name = "Gluttony's Blessing" @@ -840,7 +854,7 @@ /datum/reagent/gluttonytoxin/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message=TRUE, touch_protection=0) . = ..() if(reac_volume >= 1)//This prevents microdosing from infecting masses of people - exposed_mob.ForceContractDisease(new /datum/disease/transformation/morph(), FALSE, TRUE) + exposed_mob.infect_disease_predefined(DISEASE_MORPH, TRUE, "[ROUND_TIME()] Gluttony Toxin Infections [key_name(exposed_mob)]") /datum/reagent/serotrotium name = "Serotrotium" @@ -1394,7 +1408,7 @@ /datum/reagent/cyborg_mutation_nanomachines/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message = TRUE, touch_protection = 0) . = ..() if((methods & (PATCH|INGEST|INJECT)) || ((methods & VAPOR) && prob(min(reac_volume,100)*(1 - touch_protection)))) - exposed_mob.ForceContractDisease(new /datum/disease/transformation/robot(), FALSE, TRUE) + exposed_mob.infect_disease_predefined(DISEASE_ROBOT, TRUE, "[ROUND_TIME()] Nanomachine Infections [key_name(exposed_mob)]") /datum/reagent/xenomicrobes name = "Xenomicrobes" @@ -1406,7 +1420,7 @@ /datum/reagent/xenomicrobes/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message = TRUE, touch_protection = 0) . = ..() if((methods & (PATCH|INGEST|INJECT)) || ((methods & VAPOR) && prob(min(reac_volume,100)*(1 - touch_protection)))) - exposed_mob.ForceContractDisease(new /datum/disease/transformation/xeno(), FALSE, TRUE) + exposed_mob.infect_disease_predefined(DISEASE_XENO, TRUE, "[ROUND_TIME()] Xenomicrobes Infections [key_name(exposed_mob)]") /datum/reagent/fungalspores name = "Tubercle Bacillus Cosmosis Microbes" @@ -1420,7 +1434,8 @@ /datum/reagent/fungalspores/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message = TRUE, touch_protection = 0) . = ..() if((methods & (PATCH|INGEST|INJECT)) || ((methods & VAPOR) && prob(min(reac_volume,100)*(1 - touch_protection)))) - exposed_mob.ForceContractDisease(new /datum/disease/tuberculosis(), FALSE, TRUE) + return + //exposed_mob.ForceContractDisease(new /datum/disease/tuberculosis(), FALSE, TRUE) //TODO VIROLOGY SLIME TRANS /datum/reagent/snail name = "Agent-S" @@ -1433,7 +1448,8 @@ /datum/reagent/snail/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message = TRUE, touch_protection = 0) . = ..() if((methods & (PATCH|INGEST|INJECT)) || ((methods & VAPOR) && prob(min(reac_volume,100)*(1 - touch_protection)))) - exposed_mob.ForceContractDisease(new /datum/disease/gastrolosis(), FALSE, TRUE) + return + //exposed_mob.ForceContractDisease(new /datum/disease/gastrolosis(), FALSE, TRUE) //TODO VIROLOGY SLIME TRANS /datum/reagent/fluorosurfactant//foam precursor name = "Fluorosurfactant" @@ -2610,12 +2626,13 @@ color = "#9A6750" //RGB: 154, 103, 80 taste_description = "inner peace" penetrates_skin = NONE - var/datum/disease/transformation/gondola_disease = /datum/disease/transformation/gondola + var/disease_cat = DISEASE_GONDOLA /datum/reagent/gondola_mutation_toxin/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message = TRUE, touch_protection = 0) . = ..() if((methods & (PATCH|INGEST|INJECT)) || ((methods & VAPOR) && prob(min(reac_volume,100)*(1 - touch_protection)))) - exposed_mob.ForceContractDisease(new gondola_disease, FALSE, TRUE) + exposed_mob.infect_disease_predefined(disease_cat, TRUE, "[ROUND_TIME()] Gondola Reagent Infections [key_name(exposed_mob)]") + //exposed_mob.ForceContractDisease(new gondola_disease, FALSE, TRUE) //TODO VIROLOGY SLIME TRANS /datum/reagent/spider_extract diff --git a/code/modules/reagents/chemistry/recipes/medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm index 9deb5f7b67e5..103890aaa797 100644 --- a/code/modules/reagents/chemistry/recipes/medicine.dm +++ b/code/modules/reagents/chemistry/recipes/medicine.dm @@ -24,7 +24,7 @@ reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_CLONE /datum/chemical_reaction/medicine/spaceacillin - results = list(/datum/reagent/medicine/spaceacillin = 2) + results = list(/datum/reagent/medicine/antipathogenic/spaceacillin = 2) required_reagents = list(/datum/reagent/cryptobiolin = 1, /datum/reagent/medicine/epinephrine = 1) reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_OTHER diff --git a/code/modules/reagents/reagent_containers/cups/bottle.dm b/code/modules/reagents/reagent_containers/cups/bottle.dm index 50907cc8b8fb..8fb3012a703a 100644 --- a/code/modules/reagents/reagent_containers/cups/bottle.dm +++ b/code/modules/reagents/reagent_containers/cups/bottle.dm @@ -251,11 +251,6 @@ desc = "A small bottle. Contains an untested viral culture in synthblood medium." spawned_disease = /datum/disease/advance/random -/obj/item/reagent_containers/cup/bottle/pierrot_throat - name = "Pierrot's Throat culture bottle" - desc = "A small bottle. Contains H0NI<42 virion culture in synthblood medium." - spawned_disease = /datum/disease/pierrot_throat - /obj/item/reagent_containers/cup/bottle/cold name = "Rhinovirus culture bottle" desc = "A small bottle. Contains XY-rhinovirus culture in synthblood medium." @@ -265,12 +260,6 @@ name = "Flu virion culture bottle" desc = "A small bottle. Contains H13N1 flu virion culture in synthblood medium." spawned_disease = /datum/disease/advance/flu - -/obj/item/reagent_containers/cup/bottle/retrovirus - name = "Retrovirus culture bottle" - desc = "A small bottle. Contains a retrovirus culture in a synthblood medium." - spawned_disease = /datum/disease/dna_retrovirus - /obj/item/reagent_containers/cup/bottle/gbs name = "GBS culture bottle" desc = "A small bottle. Contains Gravitokinetic Bipotential SADS+ culture in synthblood medium."//Or simply - General BullShit @@ -288,26 +277,6 @@ icon_state = "bottle3" spawned_disease = /datum/disease/brainrot -/obj/item/reagent_containers/cup/bottle/magnitis - name = "Magnitis culture bottle" - desc = "A small bottle. Contains a small dosage of Fukkos Miracos." - spawned_disease = /datum/disease/magnitis - -/obj/item/reagent_containers/cup/bottle/wizarditis - name = "Wizarditis culture bottle" - desc = "A small bottle. Contains a sample of Rincewindus Vulgaris." - spawned_disease = /datum/disease/wizarditis - -/obj/item/reagent_containers/cup/bottle/anxiety - name = "Severe Anxiety culture bottle" - desc = "A small bottle. Contains a sample of Lepidopticides." - spawned_disease = /datum/disease/anxiety - -/obj/item/reagent_containers/cup/bottle/beesease - name = "Beesease culture bottle" - desc = "A small bottle. Contains a sample of invasive Apidae." - spawned_disease = /datum/disease/beesease - /obj/item/reagent_containers/cup/bottle/fluspanish name = "Spanish flu culture bottle" desc = "A small bottle. Contains a sample of Inquisitius." diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index c0e0f5194f82..147b55067c75 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -317,7 +317,7 @@ base_icon_state = "firstaid" volume = 15 amount_per_transfer_from_this = 15 - list_reagents = list(/datum/reagent/medicine/epinephrine = 12, /datum/reagent/medicine/coagulant = 2.5, /datum/reagent/medicine/spaceacillin = 0.5) + list_reagents = list(/datum/reagent/medicine/epinephrine = 12, /datum/reagent/medicine/coagulant = 2.5, /datum/reagent/medicine/antipathogenic/spaceacillin = 0.5) /obj/item/reagent_containers/hypospray/medipen/blood_loss name = "hypovolemic-response autoinjector" diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 046c1320a0d3..16adcffb8f04 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -174,7 +174,7 @@ /obj/item/reagent_containers/syringe/antiviral name = "syringe (spaceacillin)" desc = "Contains antiviral agents." - list_reagents = list(/datum/reagent/medicine/spaceacillin = 15) + list_reagents = list(/datum/reagent/medicine/antipathogenic/spaceacillin = 15) /obj/item/reagent_containers/syringe/bioterror name = "bioterror syringe" diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index e01124c6d69a..9e082d8777db 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -296,6 +296,7 @@ "swab", "syringe", "xlarge_beaker", + "vial", ) /datum/techweb_node/basic_circuitry @@ -398,13 +399,18 @@ "defibrillator", "genescanner", "healthanalyzer", + "antibodyscanner", "med_spray_bottle", "medical_kiosk", "medigel", "medipen_refiller", - "pandemic", "soda_dispenser", "extrapolator", + "diseasesplicer", + "incubator", + "diseaseanalyzer", + "centrifuge", + "path_data", ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) discount_experiments = list(/datum/experiment/dissection/human = 1000) diff --git a/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm b/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm index edeeaad05c6a..140983d7b7de 100644 --- a/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm +++ b/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm @@ -64,8 +64,8 @@ //Handle debuffing growth based on viruses here. for(var/datum/micro_organism/virus/active_virus in biological_sample.micro_organisms) - if(reagents.has_reagent(/datum/reagent/medicine/spaceacillin, REAGENTS_METABOLISM)) - reagents.remove_reagent(/datum/reagent/medicine/spaceacillin, REAGENTS_METABOLISM) + if(reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin, REAGENTS_METABOLISM)) + reagents.remove_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin, REAGENTS_METABOLISM) continue //This virus is stopped, We have antiviral stuff . -= virus_suspectibility diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index eeddea6bb999..e7798bea962b 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -428,8 +428,7 @@ /obj/item/slime_extract/black/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) switch(activation_type) if(SLIME_ACTIVATE_MINOR) - to_chat(user, span_userdanger("You feel something wrong inside you...")) - user.ForceContractDisease(new /datum/disease/transformation/slime(), FALSE, TRUE) + user.infect_disease_predefined(DISEASE_SLIME, TRUE, "[ROUND_TIME()] Black slime extract Infection [key_name(user)]") return 100 if(SLIME_ACTIVATE_MAJOR) diff --git a/code/modules/surgery/advanced/viral_bonding.dm b/code/modules/surgery/advanced/viral_bonding.dm index 74f131b0b7ed..ec2fd77aabdc 100644 --- a/code/modules/surgery/advanced/viral_bonding.dm +++ b/code/modules/surgery/advanced/viral_bonding.dm @@ -26,7 +26,7 @@ TOOL_WELDER = 50, /obj/item = 30) // 30% success with any hot item. time = 100 - chems_needed = list(/datum/reagent/medicine/spaceacillin,/datum/reagent/consumable/virus_food,/datum/reagent/toxin/formaldehyde) + chems_needed = list(/datum/reagent/medicine/antipathogenic/spaceacillin,/datum/reagent/consumable/virus_food,/datum/reagent/toxin/formaldehyde) /datum/surgery_step/viral_bond/tool_check(mob/user, obj/item/tool) if(implement_type == TOOL_WELDER || implement_type == /obj/item) diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 79ac97f3dd4b..3a8c6b5485b6 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -99,7 +99,6 @@ #include "closets.dm" #include "combat.dm" #include "component_tests.dm" -#include "confusion.dm" #include "connect_loc.dm" #include "container_sanity.dm" #include "crayons.dm" diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 8d1607fd1196..e5621499dce5 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -111,6 +111,7 @@ /datum/uplink_item/proc/purchase(mob/user, datum/uplink_handler/uplink_handler, atom/movable/source) var/atom/A = spawn_item(item, user, uplink_handler, source) log_uplink("[key_name(user)] purchased [src] for [cost] telecrystals from [source]'s uplink") + add_event_to_buffer(user, data = "purchased [src] for [cost] telecrystals from [source]'s uplink", log_key = "UPLINK") if(purchase_log_vis && uplink_handler.purchase_log) uplink_handler.purchase_log.LogPurchase(A, src, cost) if(lock_other_purchases) diff --git a/code/modules/uplink/uplink_items/bundle.dm b/code/modules/uplink/uplink_items/bundle.dm index 708d255ea8a1..3e0a67c201ce 100644 --- a/code/modules/uplink/uplink_items/bundle.dm +++ b/code/modules/uplink/uplink_items/bundle.dm @@ -34,6 +34,7 @@ if(possible_items.len) var/datum/uplink_item/uplink_item = pick(possible_items) log_uplink("[key_name(user)] purchased a random uplink item from [handler.owner]'s uplink with [handler.telecrystals] telecrystals remaining") + add_event_to_buffer(user, data = "purchased a random uplink item from [handler.owner]'s uplink with [handler.telecrystals] telecrystals remaining", log_key = "UPLINK") SSblackbox.record_feedback("tally", "traitor_random_uplink_items_gotten", 1, initial(uplink_item.name)) handler.purchase_item(user, uplink_item) handler.debug_mode = debug_mode //monkestation edit diff --git a/code/modules/vending/magivend.dm b/code/modules/vending/magivend.dm index fe48dac2b848..2c9ef67fd8a5 100644 --- a/code/modules/vending/magivend.dm +++ b/code/modules/vending/magivend.dm @@ -16,7 +16,6 @@ /obj/item/clothing/shoes/sandal/magic = 1, /obj/item/staff = 2, ) - contraband = list(/obj/item/reagent_containers/cup/bottle/wizarditis = 1) //No one can get to the machine to hack it anyways; for the lulz - Microwave armor_type = /datum/armor/vending_magivend resistance_flags = FIRE_PROOF default_price = 0 //Just in case, since it's primary use is storage. diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm index 7f303cc64a89..ceb9bb11c4ce 100644 --- a/code/modules/zombie/items.dm +++ b/code/modules/zombie/items.dm @@ -31,7 +31,7 @@ return // spaceacillin has a 75% chance to block infection - if(istype(target) && target.reagents.has_reagent(/datum/reagent/medicine/spaceacillin) && prob(75)) + if(istype(target) && target.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin) && prob(75)) return var/obj/item/organ/internal/zombie_infection/infection diff --git a/icons/mob/simple/animal.dmi b/icons/mob/simple/animal.dmi index de00dab4c702..602648205a26 100644 Binary files a/icons/mob/simple/animal.dmi and b/icons/mob/simple/animal.dmi differ diff --git a/monkestation/code/datums/antag_tokens.dm b/monkestation/code/datums/antag_tokens.dm index 0654ffb30693..6fd81d17b32d 100644 --- a/monkestation/code/datums/antag_tokens.dm +++ b/monkestation/code/datums/antag_tokens.dm @@ -61,6 +61,7 @@ GLOBAL_LIST_INIT(used_monthly_token, list()) if(use_donor) if(donator_token) donator_token = FALSE + add_event_to_buffer(owner, data = "used donator token [owner.prefs.token_month].", log_key = "META") owner.prefs.token_month = text2num(time2text(world.time, "MM")) owner.prefs.save_preferences() return @@ -92,7 +93,7 @@ GLOBAL_LIST_INIT(used_monthly_token, list()) if(!in_queue) return to_chat(owner, "Your request to play as [in_queue] has been approved.") - + add_event_to_buffer(owner, data = "antag token for [in_queue] approved.", log_key = "META") spend_token(in_queued_tier, queued_donor) if(!owner.mob.mind) owner.mob.mind_initialize() @@ -105,6 +106,7 @@ GLOBAL_LIST_INIT(used_monthly_token, list()) /datum/antag_token_holder/proc/reject_token() to_chat(owner, "Your request to play as [in_queue] has been denied.") + add_event_to_buffer(owner, data = "antag token for [in_queue] denied.", log_key = "META") in_queue = null in_queued_tier = null queued_donor = FALSE diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component.dm b/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component.dm index 46606e039d65..fbca47eb435f 100644 --- a/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component.dm +++ b/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component.dm @@ -148,6 +148,8 @@ RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, PROC_REF(emp_act)) RegisterSignal(parent, COMSIG_ATOM_EX_ACT, PROC_REF(ex_act)) RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays)) + RegisterSignal(parent, COMSIG_ATOM_PULLED, PROC_REF(log_pull)) + RegisterSignal(parent, COMSIG_ATOM_NO_LONGER_PULLED, PROC_REF(log_stop_pull)) /datum/component/artifact/UnregisterFromParent() GLOB.running_artifact_list -= parent @@ -161,6 +163,8 @@ COMSIG_ATOM_ATTACK_ROBOT, COMSIG_ATOM_EX_ACT, COMSIG_ATOM_EMP_ACT, + COMSIG_ATOM_NO_LONGER_PULLED, + COMSIG_ATOM_PULLED, )) /datum/component/artifact/proc/setup() @@ -176,6 +180,7 @@ holder.visible_message(span_notice("[holder] [activation_message]")) active = TRUE holder.add_overlay(act_effect) + add_event_to_buffer(parent, data = "has been activated!", log_key = "ARTIFACT") effect_activate(silent) return TRUE @@ -188,6 +193,7 @@ holder.visible_message(span_notice("[holder] [deactivation_message]")) active = FALSE holder.cut_overlay(act_effect) + add_event_to_buffer(parent, data = "has been deactivated!", log_key = "ARTIFACT") effect_deactivate(silent) /datum/component/artifact/proc/process_stimuli(stimuli, stimuli_value, triggers_faults = TRUE) diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component_signal_procs.dm b/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component_signal_procs.dm index 02155a0dea6a..de710cdb4d90 100644 --- a/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component_signal_procs.dm +++ b/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component_signal_procs.dm @@ -47,24 +47,30 @@ else if(!(user.istate & ISTATE_HARM)) holder.visible_message(span_notice("[user] gently pushes [user.pulling] against the [holder].")) process_stimuli(STIMULUS_CARBON_TOUCH) + add_event_to_buffer(user, user.pulling, "pushes [user.pulling] into [src.parent]", "ARTIFACT") return if(artifact_size == ARTIFACT_SIZE_LARGE) //only large artifacts since the average spessman wouldnt notice) user.visible_message(span_notice("[user] touches [holder].")) - + if(ishuman(user)) - var/mob/living/carbon/human/human = user + var/mob/living/carbon/human/human = user var/obj/item/bodypart/arm = human.get_active_hand() if(arm.bodytype & BODYTYPE_ROBOTIC) process_stimuli(STIMULUS_SILICON_TOUCH) + add_event_to_buffer(user, src.parent, "touched the [src.parent] with the [arm]", "ARTIFACT") else process_stimuli(STIMULUS_CARBON_TOUCH) + add_event_to_buffer(user, src.parent, "touched the [src.parent] with the [arm]", "ARTIFACT") else if(iscarbon(user)) process_stimuli(STIMULUS_CARBON_TOUCH) + add_event_to_buffer(user, src.parent, "touched the [src.parent]", "ARTIFACT") else if(issilicon(user)) process_stimuli(STIMULUS_SILICON_TOUCH) + add_event_to_buffer(user, src.parent, "touched the [src.parent]", "ARTIFACT") process_stimuli(STIMULUS_FORCE, 1) + add_event_to_buffer(user, src.parent, "touched the [src.parent]", "ARTIFACT") if(active) effect_touched(user) @@ -75,7 +81,7 @@ //just redirect to on_unarmed /datum/component/artifact/proc/on_robot_attack(datum/source, mob/living/user) SIGNAL_HANDLER - on_unarmed(source, user) + on_unarmed(source, user) /datum/component/artifact/proc/ex_act(atom/source, severity) SIGNAL_HANDLER @@ -90,3 +96,11 @@ /datum/component/artifact/proc/on_attackby(atom/source, obj/item/I, mob/user) SIGNAL_HANDLER I.on_artifact_interact(src, user) + +/datum/component/artifact/proc/log_pull(datum/source, atom/puller) + SIGNAL_HANDLER + add_event_to_buffer(puller, source, "has started pulling [parent]", "ARTIFACT") + +/datum/component/artifact/proc/log_stop_pull(datum/source, atom/puller) + SIGNAL_HANDLER + add_event_to_buffer(puller, source, "has stopped pulling [parent]", "ARTIFACT") diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm index dcf646932d27..0951967d8f00 100644 --- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm +++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm @@ -15,6 +15,7 @@ powers += power power.Grant(owner.current) log_uplink("[key_name(owner.current)] purchased [power].") + add_event_to_buffer(owner.current, data = "purchased [power].", log_key = "UPLINK") return TRUE ///Called when a Bloodsucker loses a power: (power) diff --git a/monkestation/code/modules/bloodsuckers/vassals/vassal_misc_procs.dm b/monkestation/code/modules/bloodsuckers/vassals/vassal_misc_procs.dm index 042a0ee3f644..bf12807d769c 100644 --- a/monkestation/code/modules/bloodsuckers/vassals/vassal_misc_procs.dm +++ b/monkestation/code/modules/bloodsuckers/vassals/vassal_misc_procs.dm @@ -44,6 +44,7 @@ powers += power power.Grant(owner.current) log_uplink("[key_name(owner.current)] purchased [power].") + add_event_to_buffer(owner.current, data = "purchased [power].", log_key = "UPLINK") /datum/antagonist/vassal/proc/LevelUpPowers() for(var/datum/action/cooldown/bloodsucker/power in powers) diff --git a/monkestation/code/modules/cargoborg/code/cargoborg_items.dm b/monkestation/code/modules/cargoborg/code/cargoborg_items.dm index 5c5fbf56dbf5..9b2cf071e518 100644 --- a/monkestation/code/modules/cargoborg/code/cargoborg_items.dm +++ b/monkestation/code/modules/cargoborg/code/cargoborg_items.dm @@ -220,6 +220,7 @@ extracted_item.forceMove(extraction_turf) visible_message(span_notice("[src.loc] unloads [extracted_item] from [src].")) log_silicon("[user] unloaded [extracted_item] onto [extraction_turf] ([AREACOORD(extraction_turf)]).") + add_event_to_buffer(user, extracted_item, "unloaded [extracted_item] onto [extraction_turf] ([AREACOORD(extraction_turf)]).", "SILICON") in_use = FALSE return @@ -276,6 +277,7 @@ lifting_up.forceMove(src) var/turf/lifting_up_from = get_turf(lifting_up.loc) log_silicon("[user] loaded [lifting_up] (Contains mobs: [contains_mobs]) into [src] at ([AREACOORD(lifting_up_from)]).") + add_event_to_buffer(user, lifting_up, "loaded [lifting_up] (Contains mobs: [contains_mobs]) into [src] at ([AREACOORD(lifting_up_from)]).", "SILICON") visible_message(span_notice("[src.loc] loads [lifting_up] into [src]'s cargo hold.")) in_use = FALSE diff --git a/monkestation/code/modules/cassettes/cassette_approval.dm b/monkestation/code/modules/cassettes/cassette_approval.dm index 6bae8ae8a81b..6ac7429548df 100644 --- a/monkestation/code/modules/cassettes/cassette_approval.dm +++ b/monkestation/code/modules/cassettes/cassette_approval.dm @@ -126,6 +126,7 @@ GLOBAL_LIST_INIT(cassette_reviews, list()) approve_review(usr) if("deny") to_chat(submitter, span_warning("You feel a wave of disapointment wash over you, you can tell that your cassette was denied by the Space Board of Music")) + add_event_to_buffer(submitter, data = "tape has been rejected.", log_key = "MUSIC") action_taken = TRUE /datum/cassette_review/proc/approve_review(mob/user) @@ -135,6 +136,7 @@ GLOBAL_LIST_INIT(cassette_reviews, list()) to_chat(submitter, span_notice("You can feel the Space Board of Music has approved your cassette:[submitted_tape.name].")) submitted_tape.forceMove(get_turf(submitter)) message_admins("[submitter]'s tape has been approved by [user]") + add_event_to_buffer(submitter, user, data = "tape has been approved by [user].", log_key = "MUSIC") action_taken = TRUE /proc/fetch_review(id) diff --git a/monkestation/code/modules/cassettes/machines/dj_station.dm b/monkestation/code/modules/cassettes/machines/dj_station.dm index 61099e853d71..fa68ef10d422 100644 --- a/monkestation/code/modules/cassettes/machines/dj_station.dm +++ b/monkestation/code/modules/cassettes/machines/dj_station.dm @@ -75,6 +75,7 @@ GLOBAL_VAR(dj_booth) to_chat(user, span_info("You estimate it will take about [time_left ? DisplayTimeText(((time_left * 10) + 6000)) : DisplayTimeText(COOLDOWN_TIMELEFT(src, next_song_timer))] to cool down.")) return start_broadcast() + add_event_to_buffer(user, src, data = "started broadcasting [inserted_tape].", log_key = "MUSIC") /obj/machinery/cassette/dj_station/AltClick(mob/user) . = ..() @@ -165,6 +166,7 @@ GLOBAL_VAR(dj_booth) UnregisterSignal(anything, COMSIG_CARBON_EQUIP_EARS) UnregisterSignal(anything, COMSIG_MOVABLE_Z_CHANGED) people_with_signals = list() + add_event_to_buffer(src, data = "has stopped broadcasting [inserted_tape].", log_key = "MUSIC") /obj/machinery/cassette/dj_station/proc/start_broadcast() var/choice = tgui_input_list(usr, "Choose which song to play.", "[src]", current_namelist) diff --git a/monkestation/code/modules/client/preferences/alt_jobs/titles.dm b/monkestation/code/modules/client/preferences/alt_jobs/titles.dm index 81ad7e2d9f7d..c32ef37878e9 100644 --- a/monkestation/code/modules/client/preferences/alt_jobs/titles.dm +++ b/monkestation/code/modules/client/preferences/alt_jobs/titles.dm @@ -333,8 +333,8 @@ /datum/job/virologist alt_titles = list( - "Virologist", "Pathologist", + "Fish Doctor", "Junior Pathologist", ) diff --git a/monkestation/code/modules/client/preferences/inventory.dm b/monkestation/code/modules/client/preferences/inventory.dm index e88372730e55..0128fe2093fd 100644 --- a/monkestation/code/modules/client/preferences/inventory.dm +++ b/monkestation/code/modules/client/preferences/inventory.dm @@ -64,6 +64,8 @@ amount = round(amount, 1) //make sure whole number metacoins += amount //store the updated metacoins in a variable, but not the actual game-to-game storage mechanism (load_metacoins() pulls from database) + add_event_to_buffer(parent, data = "monkecoins were changed by [amount] Reason: [reason].", log_key = "META") + //SQL query - updates the metacoins in the database (this is where the storage actually happens) var/datum/db_query/query_inc_metacoins = SSdbcore.NewQuery("UPDATE [format_table_name("player")] SET metacoins = metacoins + '[amount]' WHERE ckey = '[ckey]'") query_inc_metacoins.warn_execute() diff --git a/monkestation/code/modules/client/verbs.dm b/monkestation/code/modules/client/verbs.dm index 40fbcc906dc9..93f815f662a8 100644 --- a/monkestation/code/modules/client/verbs.dm +++ b/monkestation/code/modules/client/verbs.dm @@ -77,7 +77,7 @@ GLOBAL_LIST_INIT(low_threat_antags, list( /proc/wait_for_approval(client/requestor, datum/antagonist/requested_antag) var/msg = "[span_admin("[span_prefix("ANTAG TOKEN:")] [key_name(requestor)] [ADMIN_APPROVE_TOKEN(requestor)] [ADMIN_REJECT_TOKEN(requestor)] | [requestor] has requested to use their antag token to be a [requested_antag].")]" - + add_event_to_buffer(requestor, data = "has requested to use their antag token to be a [requested_antag].", log_key = "META") for(var/client/X in GLOB.admins) X << 'sound/items/bikehorn.ogg' diff --git a/monkestation/code/modules/ghost_players/arena/fight_button.dm b/monkestation/code/modules/ghost_players/arena/fight_button.dm index fc4e063bac70..1dbe4e8a2066 100644 --- a/monkestation/code/modules/ghost_players/arena/fight_button.dm +++ b/monkestation/code/modules/ghost_players/arena/fight_button.dm @@ -117,7 +117,7 @@ /obj/structure/fight_button/proc/set_rules(mob/living/carbon/human/ghost/user) var/max_amount = user.client.prefs.metacoins - var/choice = tgui_input_number(user, "How much would you like to wager?", "[src.name]", 100, max_amount, 100) + var/choice = tgui_input_number(user, "How much would you like to wager?", "[src.name]", 100, max_amount, 0) if(!choice) return FALSE payout = choice diff --git a/monkestation/code/modules/hydroponics/seeds/coconut.dm b/monkestation/code/modules/hydroponics/seeds/coconut.dm index 300dbd4f9b8d..33b17f932f48 100644 --- a/monkestation/code/modules/hydroponics/seeds/coconut.dm +++ b/monkestation/code/modules/hydroponics/seeds/coconut.dm @@ -21,7 +21,7 @@ genes = list(/datum/plant_gene/trait/repeated_harvest) reagents_add = list(/datum/reagent/consumable/milk = 0.05, /datum/reagent/consumable/nutriment/vitamin = 0.04, /datum/reagent/consumable/nutriment = 0.02) - possible_mutations = list(/datum/hydroponics/plant_mutation/infusion/coconut_gun) + infusion_mutations = list(/datum/hydroponics/plant_mutation/infusion/coconut_gun) /obj/item/seeds/coconut/gun name = "coconut gun tree" diff --git a/monkestation/code/modules/mech_comp/objects/flush.dm b/monkestation/code/modules/mech_comp/objects/flush.dm index a549adb6004b..500c3c37e629 100644 --- a/monkestation/code/modules/mech_comp/objects/flush.dm +++ b/monkestation/code/modules/mech_comp/objects/flush.dm @@ -49,9 +49,10 @@ COOLDOWN_START(src, flush_cd, 5 SECONDS) -/obj/item/mcobject/flusher/proc/expel(obj/structure/disposalholder/holder) - var/turf/target - for(var/atom/movable/AM in holder) - target = get_offset_target_turf(holder, rand(5)-rand(5), rand(5)-rand(5)) - AM?.throw_at(target, 5, 1) - qdel(holder) +/obj/item/mcobject/flusher/proc/expel(obj/structure/disposalholder/H) + playsound(src, 'sound/machines/hiss.ogg', 50, FALSE, FALSE) + flick("comp_flush1", src) + pipe_eject(H) + + H.vent_gas(loc) + qdel(H) diff --git a/monkestation/code/modules/metrics/metric_subsystem.dm b/monkestation/code/modules/metrics/metric_subsystem.dm new file mode 100644 index 000000000000..ea51e2da2de3 --- /dev/null +++ b/monkestation/code/modules/metrics/metric_subsystem.dm @@ -0,0 +1,100 @@ + +/datum/config_entry/flag/metrics_enabled + default = TRUE + +SUBSYSTEM_DEF(metrics) + name = "Metrics" + init_order = INIT_ORDER_METRICS + wait = 30 SECONDS + runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME // ALL THE LEVELS + ss_id = "metrics" + flags = SS_KEEP_TIMING // This needs to ingest every 30 IRL seconds, not ingame seconds. I mean it doesn't but it fucks with my pretty graph. + /// The real time of day the server started. Used to calculate time drift + var/world_init_time = 0 // Not set in here. Set in world/New() + /// If this is on live the world will end, just kidding but admin chat will be literally unusuable + var/debug = FALSE + +/datum/controller/subsystem/metrics/Initialize(start_timeofday) + if(!CONFIG_GET(flag/metrics_enabled) && !debug) + flags |= SS_NO_FIRE // Disable firing to save CPU + return SS_INIT_SUCCESS + +/datum/controller/subsystem/metrics/fire(resumed) + if (!SSdbcore.Connect() && !debug) + return + var/sql_at_fire = SQLtime() + var/list/generic_insert = get_metric_data_main() + var/list/subsystem_extra_insert = list() + var/list/subsystem_insert = list() + for(var/datum/controller/subsystem/SS in Master.subsystems) + var/list/data = SS.get_metrics() + subsystem_insert += list(list( + "datetime" = sql_at_fire, + "round_id" = text2num(GLOB.round_id), //NUM + "ss_id" = SS.ss_id, //VARSET + "relation_id_SS" = data["relation_id_SS"], //VARSET + "cost" = data["cost"], //DECIMAL + "tick_usage" = data["tick_usage"], //DECIMAL + "relational_id" = generic_insert["relational_id"] //VARSET + )) + if(length(data["custom"])) + var/list/custom_data = data["custom"] + for(var/item in custom_data) + subsystem_extra_insert += list(list( + "datetime" = sql_at_fire, + "round_id" = text2num(GLOB.round_id), //NUM + "ss_id" = SS.ss_id, //VARSET + "relation_id_SS" = data["relation_id_SS"], //VARSET + "ss_value" = json_encode(list("name" = item, "value" = custom_data[item])), //LONG STRING + )) + if(debug) //sqls are handled after this + return + + var/datum/db_query/query_add_metrics = SSdbcore.NewQuery({" + INSERT INTO [format_table_name("metric_data")] (`datetime`, `cpu`, `maptick`, `elapsed_processed`, `elapsed_real`, `client_count`, `round_id`, `relational_id`) VALUES (:datetime, :cpu, :maptick, :elapsed_processed, :elapsed_real, :client_count, :round_id, :relational_id)"}, + list( + "datetime" = sql_at_fire, + "cpu" = generic_insert["cpu"], + "maptick" = generic_insert["maptick"], + "elapsed_processed" = generic_insert["elapsed_processed"], + "elapsed_real" = generic_insert["elapsed_real"], + "client_count" = generic_insert["client_count"], + "round_id" = generic_insert["round_id"], + "relational_id" = generic_insert["relational_id"], + )) + if(!query_add_metrics.Execute()) + addtimer(CALLBACK(src, PROC_REF(retry_failed), generic_insert, sql_at_fire), 10 SECONDS) + qdel(query_add_metrics) + + SSdbcore.MassInsert(format_table_name("subsystem_metrics"), subsystem_insert , ignore_errors = TRUE, duplicate_key = TRUE) + if(length(subsystem_extra_insert)) + SSdbcore.MassInsert(format_table_name("subsystem_extra_metrics"), subsystem_extra_insert , ignore_errors = TRUE, duplicate_key = TRUE) + +/datum/controller/subsystem/metrics/proc/get_metric_data_main() + var/list/out = list() + out["cpu"] = world.cpu //DECIMAL + out["maptick"] = world.map_cpu //DECIMAL + out["elapsed_processed"] = world.time //NUM + out["elapsed_real"] = (REALTIMEOFDAY - world_init_time) //NUM + out["client_count"] = length(GLOB.clients) //NUM + out["round_id"] = text2num(GLOB.round_id) // This is so we can filter the metrics by a single round ID //NUM + out["relational_id"] = "[text2num(GLOB.round_id)]-[time_stamp()]-[rand(100, 100000)]" //VARSET + + return out + +/datum/controller/subsystem/metrics/proc/retry_failed(list/generic_insert, sql_at_fire) + var/datum/db_query/query_add_metrics = SSdbcore.NewQuery({" + INSERT INTO [format_table_name("metric_data")] (`datetime`, `cpu`, `maptick`, `elapsed_processed`, `elapsed_real`, `client_count`, `round_id`, `relational_id`) VALUES (:datetime, :cpu, :maptick, :elapsed_processed, :elapsed_real, :client_count, :round_id, :relational_id)"}, + list( + "datetime" = sql_at_fire, + "cpu" = generic_insert["cpu"], + "maptick" = generic_insert["maptick"], + "elapsed_processed" = generic_insert["elapsed_processed"], + "elapsed_real" = generic_insert["elapsed_real"], + "client_count" = generic_insert["client_count"], + "round_id" = generic_insert["round_id"], + "relational_id" = generic_insert["relational_id"], + )) + if(!query_add_metrics.Execute()) + addtimer(CALLBACK(src, PROC_REF(retry_failed), generic_insert, sql_at_fire), 10 SECONDS) + qdel(query_add_metrics) diff --git a/monkestation/code/modules/metrics/sql_logging_subsystem.dm b/monkestation/code/modules/metrics/sql_logging_subsystem.dm new file mode 100644 index 000000000000..0629012089a4 --- /dev/null +++ b/monkestation/code/modules/metrics/sql_logging_subsystem.dm @@ -0,0 +1,135 @@ +/* IMPORTANT INFORMATION +* If you are making a new proc make sure the lists are in proper format so we dont have to re-aggregate it inside the db +* Its done as followed +* list( +* "timestamp" = SQLtime() - THIS IS SUPER SUPER IMPORTANT IF NO TIMESTAMP PROVIDED YOUR LOGS WILL NOT BE IN THE CORRECT ORDER AFTER PARSE +* "round_id" = CURRENT_ROUND +* "logging_key" = KEY - Example: GAME +* "source" = LOG_SOURCE - Example: large beaker +* "source_ckey" = if has a ckey, mobs ckey +* "target" = Target of whatever log - optional populated with No Target Log if not set +* "target_ckey" = Ckey of the target if applicable set to No Target Log without target, or No Target Ckey for ones with targets +* "log_data" = What we Log - Example: Reacted with Potassium Creating a Small Explosion +* "x" = SOURCE_X +* "y" = SOURCE_Y +* "z" = SOURCE_Z +* "area" = SOURCE_AREA +* "map" = Current rounds map name +* "roundstate" = INT of the roundstate refer to code/__DEFINES/subsystems.dm for what each value means +* "voluntary" = TRUE/FALSE - Boolean value for if this log was generated involunarily, for instance virus emotes are involuntary. +*) +*/ + +/* THIS IS JUST FOR CLARITY SAKE. +* log_{type}'s finished +* log_traitor +* log_attack +* log_bomb +* log_wound +* log_say +* log_uplink +* log_malf_upgrades +* log_changeling_power +* log_heretic_knowledge +* log_spellbook +* log_economy +* log_emote +* log_ooc +* log_prayer +* log_silicon +*/ + +/datum/config_entry/flag/sqlgamelogs + default = TRUE + + +SUBSYSTEM_DEF(sql_logging) + name = "SQL Logging" + + init_order = INIT_ORDER_METRICS + wait = 2 MINUTES + runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME // ALL THE LEVELS + ss_id = "sql-logs" + flags = SS_KEEP_TIMING // We want consistancy so we run every 5 irl minutes plus at round end. + ///this list is our to be added queue thats stored as an array list emptied every 5 minutes + var/list/buffered_data = list() + +/datum/controller/subsystem/sql_logging/Initialize(start_timeofday) + if(!CONFIG_GET(flag/sqlgamelogs)) + flags |= SS_NO_FIRE // Disable firing to save CPU + return SS_INIT_SUCCESS + + +/datum/controller/subsystem/sql_logging/fire(resumed) + if (!SSdbcore.Connect() || !length(buffered_data)) + return + + if(SSdbcore.MassInsert(format_table_name("logs"), buffered_data , ignore_errors = TRUE, duplicate_key = TRUE)) + buffered_data = list() + +/proc/add_event_to_buffer(atom/source, atom/target, data, log_key = "GAME", voluntary = TRUE) + if(!data) + return + if(istype(source, /datum/mind)) + var/datum/mind/mind = source + source = mind.current + + var/list/sorted_data = list() + + var/source_name = "Unsourced Data" + var/source_x = 0 + var/source_y = 0 + var/source_z = 0 + var/source_area = "Unsourced Data" + var/source_ckey = "Non Client Source" + + var/target_name = "No Target" + var/target_ckey = "No Target" + if(target && !isnull(target)) + if(ismob(target)) + var/mob/mob = target + if(mob.client) + target_ckey = mob.client.ckey + target_name = mob.real_name + else + target_name = target.name + target_ckey = "Non Client Target" + + if(source && !isnull(source)) + if(ismob(source)) + var/mob/mob = source + if(mob.client) + source_ckey = mob.client.ckey + source_name = mob.real_name + + else + source_name = source.name + source_x = source.x + source_y = source.y + source_z = source.z + var/area/area = get_area(source) + if(!isnull(area)) + source_area = area.name + + if(isnull(source_name)) + source_name = target_ckey + + sorted_data = list( + "timestamp" = SQLtime(), + "round_id" = text2num(GLOB.round_id), + "logging_key" = log_key, + "source" = source_name, + "source_ckey" = source_ckey, + "target" = target_name, + "target_ckey" = target_ckey, + "log_data" = data, + "x" = source_x, + "y" = source_y, + "z" = source_z, + "area" = source_area, + "map" = SSmapping.config.map_name, + "roundstate" = SSticker.current_state, + "voluntary" = voluntary, + ) + + SSsql_logging.buffered_data += list(sorted_data) diff --git a/monkestation/code/modules/metrics/subsystem_analytics/generics.dm b/monkestation/code/modules/metrics/subsystem_analytics/generics.dm new file mode 100644 index 000000000000..0a806c365c6e --- /dev/null +++ b/monkestation/code/modules/metrics/subsystem_analytics/generics.dm @@ -0,0 +1,69 @@ +/datum/controller/subsystem/air/get_metrics() + . = ..() + var/list/cust = list() + cust["active_turfs"] = length(active_turfs) + cust["hotspots"] = length(hotspots) + .["custom"] = cust + +/datum/controller/subsystem/garbage/get_metrics() + . = ..() + var/list/cust = list() + if((delslasttick + gcedlasttick) == 0) // Account for DIV0 + cust["gcr"] = 0 + else + cust["gcr"] = (gcedlasttick / (delslasttick + gcedlasttick)) + cust["total_harddels"] = totaldels + cust["total_softdels"] = totalgcs + var/i = 0 + for(var/list/L in queues) + i++ + cust["queue_[i]"] = length(L) + + .["custom"] = cust + +/datum/controller/subsystem/lighting/get_metrics() + . = ..() + var/list/cust = list() + cust["sources_queue"] = length(sources_queue) + cust["corners_queue"] = length(corners_queue) + cust["objects_queue"] = length(objects_queue) + .["custom"] = cust + +/datum/controller/subsystem/machines/get_metrics() + . = ..() + var/list/cust = list() + cust["processing_machines"] = length(processing) + .["custom"] = cust + +/datum/controller/subsystem/mobs/get_metrics() + . = ..() + var/list/cust = list() + cust["processing_mobs"] = length(GLOB.mob_living_list) + .["custom"] = cust + + +/datum/controller/subsystem/processing/get_metrics() + . = ..() + var/list/cust = list() + cust["processing"] = length(processing) + .["custom"] = cust + +/datum/controller/subsystem/tgui/get_metrics() + . = ..() + var/list/cust = list() + cust["open_uis"] = length(open_uis) + cust["current_run"] = length(current_run) + .["custom"] = cust + + +/datum/controller/subsystem/timer/get_metrics() + . = ..() + var/list/cust = list() + cust["bucket_count"] = bucket_count + .["custom"] = cust + +/datum/controller/subsystem/weather/get_metrics() + . = ..() + var/list/cust = list() + cust["processing_weather"] = length(processing) + .["custom"] = cust diff --git a/monkestation/code/modules/possession/basic_additions.dm b/monkestation/code/modules/possession/basic_additions.dm index 9fa5040a6d43..0289ef949d96 100644 --- a/monkestation/code/modules/possession/basic_additions.dm +++ b/monkestation/code/modules/possession/basic_additions.dm @@ -44,50 +44,51 @@ /mob/living/basic/update_held_items() . = ..() - remove_overlay(1) - var/list/hands_overlays = list() - - for(var/obj/item/I in held_items) - if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD) - I.screen_loc = ui_hand_position(get_held_index_of_item(I)) - client.screen += I - if(length(observers)) - for(var/mob/dead/observe as anything in observers) - if(observe.client && observe.client.eye == src) - observe.client.screen += I - else - observers -= observe - if(!observers.len) - observers = null - break - - var/used_list_index = dir - if(dir == WEST) - used_list_index = 4 - if(dir == EAST) - used_list_index = 3 - if(!uses_directional_offsets) - used_list_index = 1 - - var/icon_file = I.lefthand_file - var/x_offset = l_x_shift[used_list_index] - var/y_offset = l_y_shift[used_list_index] - var/vertical_offset = 0 - vertical_offset = CEILING(get_held_index_of_item(I) / 2, 1) - 1 - if(get_held_index_of_item(I) % 2 == 0) - icon_file = I.righthand_file - y_offset = r_y_shift[used_list_index] - x_offset = r_x_shift[used_list_index] - - var/mutable_appearance/hand_overlay = I.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE) - hand_overlay.pixel_y += y_offset + (vertical_offset * base_vertical_shift) - hand_overlay.pixel_x += x_offset - - hands_overlays += hand_overlay - - if(hands_overlays.len) - possession_overlays[1] = hands_overlays - apply_overlay(1) + if(!isdrone(src)) + remove_overlay(1) + var/list/hands_overlays = list() + + for(var/obj/item/I in held_items) + if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD) + I.screen_loc = ui_hand_position(get_held_index_of_item(I)) + client.screen += I + if(length(observers)) + for(var/mob/dead/observe as anything in observers) + if(observe.client && observe.client.eye == src) + observe.client.screen += I + else + observers -= observe + if(!observers.len) + observers = null + break + + var/used_list_index = dir + if(dir == WEST) + used_list_index = 4 + if(dir == EAST) + used_list_index = 3 + if(!uses_directional_offsets) + used_list_index = 1 + + var/icon_file = I.lefthand_file + var/x_offset = l_x_shift[used_list_index] + var/y_offset = l_y_shift[used_list_index] + var/vertical_offset = 0 + vertical_offset = CEILING(get_held_index_of_item(I) / 2, 1) - 1 + if(get_held_index_of_item(I) % 2 == 0) + icon_file = I.righthand_file + y_offset = r_y_shift[used_list_index] + x_offset = r_x_shift[used_list_index] + + var/mutable_appearance/hand_overlay = I.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE) + hand_overlay.pixel_y += y_offset + (vertical_offset * base_vertical_shift) + hand_overlay.pixel_x += x_offset + + hands_overlays += hand_overlay + + if(hands_overlays.len) + possession_overlays[1] = hands_overlays + apply_overlay(1) /mob/living/basic/proc/remove_overlay(cache_index) var/I = possession_overlays[cache_index] @@ -104,11 +105,11 @@ if(number > 0) dexterous = TRUE advanced_simple = TRUE - add_traits(list(TRAIT_ADVANCEDTOOLUSER, TRAIT_CAN_STRIP, TRAIT_LITERATE), ROUNDSTART_TRAIT) + add_traits(list(TRAIT_CAN_HOLD_ITEMS, TRAIT_ADVANCEDTOOLUSER, TRAIT_CAN_STRIP, TRAIT_LITERATE), ROUNDSTART_TRAIT) else dexterous = FALSE advanced_simple = FALSE - remove_traits(list(TRAIT_ADVANCEDTOOLUSER, TRAIT_CAN_STRIP, TRAIT_LITERATE), ROUNDSTART_TRAIT) + remove_traits(list(TRAIT_CAN_HOLD_ITEMS, TRAIT_ADVANCEDTOOLUSER, TRAIT_CAN_STRIP, TRAIT_LITERATE), ROUNDSTART_TRAIT) //general disarm proc /mob/living/proc/disarm(mob/living/carbon/target) @@ -221,22 +222,21 @@ /mob/living/basic/perform_hand_swap(hand_index) . = ..() - if(!.) - return - if(!dexterous) - return - if(!hand_index) - hand_index = (active_hand_index % held_items.len)+1 - var/oindex = active_hand_index - active_hand_index = hand_index - if(hud_used) - var/atom/movable/screen/inventory/hand/H - H = hud_used.hand_slots["[hand_index]"] - if(H) - H.update_appearance() - H = hud_used.hand_slots["[oindex]"] - if(H) - H.update_appearance() + if(!isdrone(src)) + if(!dexterous) + return + if(!hand_index) + hand_index = (active_hand_index % held_items.len)+1 + var/oindex = active_hand_index + active_hand_index = hand_index + if(hud_used) + var/atom/movable/screen/inventory/hand/H + H = hud_used.hand_slots["[hand_index]"] + if(H) + H.update_appearance() + H = hud_used.hand_slots["[oindex]"] + if(H) + H.update_appearance() /mob/living/basic/put_in_hands(obj/item/I, del_on_fail = FALSE, merge_stacks = TRUE, ignore_animation = TRUE) . = ..() diff --git a/monkestation/code/modules/store/pre_round/_pre_round_store.dm b/monkestation/code/modules/store/pre_round/_pre_round_store.dm index ba09c21cab82..0572a8c72b06 100644 --- a/monkestation/code/modules/store/pre_round/_pre_round_store.dm +++ b/monkestation/code/modules/store/pre_round/_pre_round_store.dm @@ -106,5 +106,6 @@ GLOBAL_LIST_EMPTY(cached_preround_items) backpack.atom_storage.attempt_insert(created_item, new_player_mob, force = TRUE) owners_prefs.adjust_metacoins(new_player_mob.client.ckey, (-initial(bought_item.item_cost)), donator_multipler = FALSE) + add_event_to_buffer(new_player_mob, data = "spawned in with a [created_item] (Pre-round Store).", log_key = "META") ui_close() qdel(new_player_mob.client.readied_store) diff --git a/monkestation/code/modules/store/store_items/_store_items.dm b/monkestation/code/modules/store/store_items/_store_items.dm index 56812a9e344f..1fc50c62e4e2 100644 --- a/monkestation/code/modules/store/store_items/_store_items.dm +++ b/monkestation/code/modules/store/store_items/_store_items.dm @@ -26,6 +26,7 @@ return FALSE buyers_preferences.adjust_metacoins(buyer.ckey, -item_cost, donator_multipler = FALSE) + add_event_to_buffer(buyer, data = "bought a [name] - [item_cost].", log_key = "META") if(!one_time_buy) finalize_purchase(buyer) return diff --git a/monkestation/code/modules/trading/box_rolling.dm b/monkestation/code/modules/trading/box_rolling.dm index 53144837c7fb..0a11c23127e8 100644 --- a/monkestation/code/modules/trading/box_rolling.dm +++ b/monkestation/code/modules/trading/box_rolling.dm @@ -84,6 +84,7 @@ maptext_y += 60 if(user.client) message_admins("[user.client.ckey] opened a lootbox and recieved [rolled_item.name]!") + add_event_to_buffer(user, data = "opened a lootbox and recieved [rolled_item.name]!", log_key = "META") log_game("[user.client.ckey] opened a lootbox and recieved [rolled_item.name]!") preview.filters += filter(type = "drop_shadow", x = 0, y = 0, size= 5, offset = 0, color = "#F0CA85") if(type_string == "Unusual") diff --git a/monkestation/code/modules/trading/lootbox_buying.dm b/monkestation/code/modules/trading/lootbox_buying.dm index 64ee420640bc..172ae9f70cd9 100644 --- a/monkestation/code/modules/trading/lootbox_buying.dm +++ b/monkestation/code/modules/trading/lootbox_buying.dm @@ -25,6 +25,7 @@ /client/proc/open_lootbox() message_admins("[ckey] opened a lootbox!") + add_event_to_buffer(src, data = "has opened a lootbox!", log_key = "META") log_game("[ckey] opened a lootbox!") if(!mob) return diff --git a/monkestation/code/modules/virology/__base_procs.dm b/monkestation/code/modules/virology/__base_procs.dm new file mode 100644 index 000000000000..4d6bae392b6c --- /dev/null +++ b/monkestation/code/modules/virology/__base_procs.dm @@ -0,0 +1,8 @@ +/atom/proc/infection_attempt(mob/living/perp, datum/disease/D, bodypart = null) + return FALSE + +/atom/proc/infect_disease(datum/disease/disease, forced = FALSE, notes = "", decay = TRUE) + return FALSE + +/atom/proc/try_infect_with_mobs_diseases(mob/living/carbon/infectee) + return FALSE diff --git a/monkestation/code/modules/virology/__effects.dm b/monkestation/code/modules/virology/__effects.dm new file mode 100644 index 000000000000..9afb8b71a0a0 --- /dev/null +++ b/monkestation/code/modules/virology/__effects.dm @@ -0,0 +1,31 @@ +/obj/effect/decal/cleanable/virusdish + name = "broken virus containment dish" + icon = 'monkestation/code/modules/virology/icons/virology.dmi' + icon_state = "brokendish-outline" + density = 0 + anchored = 1 + mouse_opacity = 1 + layer = OBJ_LAYER + var/last_openner + var/datum/disease/advanced/contained_virus + +/obj/effect/decal/cleanable/virusdish/Entered(mob/living/perp) + ..() + infection_attempt(perp) + +/obj/effect/decal/cleanable/virusdish/infection_attempt(mob/living/perp) + //Now if your feet aren't well protected, or are bleeding, you might get infected. + var/block = 0 + var/bleeding = 0 + if (perp.body_position & LYING_DOWN) + block = perp.check_contact_sterility(BODY_ZONE_EVERYTHING) + bleeding = perp.check_bodypart_bleeding(BODY_ZONE_EVERYTHING) + else + block = perp.check_contact_sterility(BODY_ZONE_LEGS) + bleeding = perp.check_bodypart_bleeding(BODY_ZONE_LEGS) + + + if (!block && contained_virus.spread_flags & DISEASE_SPREAD_CONTACT_SKIN) + perp.infect_disease(contained_virus, notes="(Contact, from [(perp.body_position & LYING_DOWN)?"lying":"standing"] over a broken virus dish[last_openner ? " broken by [last_openner]" : ""])") + else if (bleeding && (contained_virus.spread_flags & DISEASE_SPREAD_BLOOD)) + perp.infect_disease(contained_virus, notes="(Blood, from [(perp.body_position & LYING_DOWN)?"lying":"standing"] over a broken virus dish[last_openner ? " broken by [last_openner]" : ""])") diff --git a/monkestation/code/modules/virology/disease/_disease.dm b/monkestation/code/modules/virology/disease/_disease.dm new file mode 100644 index 000000000000..f42110bfeac2 --- /dev/null +++ b/monkestation/code/modules/virology/disease/_disease.dm @@ -0,0 +1,1002 @@ +GLOBAL_LIST_INIT(infected_contact_mobs, list()) +GLOBAL_LIST_INIT(virusDB, list()) + +/datum/disease + //the disease's antigens, that the body's immune_system will read to produce corresponding antibodies. Without antigens, a disease cannot be cured. + var/list/antigen = list() + //alters a pathogen's propensity to mutate. Set to FALSE to forbid a pathogen from ever mutating. + var/mutation_modifier = TRUE + //the antibody concentration at which the disease will fully exit the body + var/strength = 100 + //the percentage of the strength at which effects will start getting disabled by antibodies. + var/robustness = 100 + //chance to cure the disease at every proc when the body is getting cooked alive. + var/max_bodytemperature = 1000 + //very low temperatures will stop the disease from activating/progressing + var/min_bodytemperature = 120 + ///split category used for predefined diseases atm + var/category = DISEASE_NORMAL + + //logging + var/log = "" + var/origin = "Unknown" + var/logged_virusfood = FALSE + var/fever_warning = FALSE + + //cosmetic + var/color + var/pattern = 1 + var/pattern_color + + ///pathogenic warfare - If you have a second disease of a form name in the list they will start fighting. + var/list/can_kill = list("Bacteria") + + //When an opportunity for the disease to spread_flags to a mob arrives, runs this percentage through prob() + //Ignored if infected materials are ingested (injected with infected blood, eating infected meat) + var/infectionchance = 70 + var/infectionchance_base = 70 + + //ticks increases by [speed] every time the disease activates. Drinking Virus Food also accelerates the process by 10. + var/ticks = 0 + var/speed = 1 + + var/stageprob = 25 + + //when spreading to another mob, that new carrier has the disease's stage reduced by stage_variance + var/stage_variance = -1 + + var/uniqueID = 0// 0000 to 9999, set when the pathogen gets initially created + var/subID = 0// 000 to 9999, set if the pathogen underwent effect or antigen mutation + var/childID = 0// 01 to 99, incremented as the pathogen gets analyzed after a mutation + //bitflag showing which transmission types are allowed for this disease + var/allowed_transmission = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_SKIN | DISEASE_SPREAD_CONTACT_FLUIDS | DISEASE_SPREAD_AIRBORNE + +/proc/filter_disease_by_spread(list/diseases, required = NONE) + if(!length(diseases)) + return list() + + var/list/viable = list() + for(var/datum/disease/advanced/disease as anything in diseases) + if(!(disease.spread_flags & required)) + continue + viable += disease + return viable + +/datum/disease/advanced/proc/update_global_log() + if ("[uniqueID]-[subID]" in GLOB.inspectable_diseases) + return + GLOB.inspectable_diseases["[uniqueID]-[subID]"] = Copy() + +/datum/disease/advanced/proc/clean_global_log() + var/ID = "[uniqueID]-[subID]" + if (ID in GLOB.virusDB) + return + + for (var/mob/living/L in GLOB.mob_list) + if(!length(L.diseases)) + continue + for(var/datum/disease/advanced/D as anything in L.diseases) + if (ID == "[D.uniqueID]-[D.subID]") + return + + for (var/obj/item/I in GLOB.infected_items) + for(var/datum/disease/advanced/D as anything in I.viruses) + if (ID == "[D.uniqueID]-[D.subID]") + return + + var/dishes = 0 + for (var/obj/item/weapon/virusdish/dish in GLOB.virusdishes) + if (dish.contained_virus) + if (ID == "[dish.contained_virus.uniqueID]-[dish.contained_virus.subID]") + dishes++ + if (dishes > 1)//counting the dish we're in currently + return + //If a pathogen that isn't in the database mutates, we check whether it infected anything, and remove it from the disease list if it didn't + //so we don't clog up the Diseases Panel with irrelevant mutations + GLOB.inspectable_diseases -= ID + +/datum/disease/advanced/proc/AddToGoggleView(mob/living/infectedMob) + if (spread_flags & DISEASE_SPREAD_CONTACT_SKIN) + GLOB.infected_contact_mobs |= infectedMob + if (!infectedMob.pathogen) + infectedMob.pathogen = image('monkestation/code/modules/virology/icons/effects.dmi',infectedMob,"pathogen_contact") + infectedMob.pathogen.plane = HUD_PLANE + infectedMob.pathogen.appearance_flags = RESET_COLOR|RESET_ALPHA + for (var/mob/living/L in GLOB.science_goggles_wearers) + if (L.client) + L.client.images |= infectedMob.pathogen + return + + if (spread_flags & DISEASE_SPREAD_BLOOD) + GLOB.infected_contact_mobs |= infectedMob + if (!infectedMob.pathogen) + infectedMob.pathogen = image('monkestation/code/modules/virology/icons/effects.dmi',infectedMob,"pathogen_blood") + infectedMob.pathogen.plane = HUD_PLANE + infectedMob.pathogen.appearance_flags = RESET_COLOR|RESET_ALPHA + for (var/mob/living/L in GLOB.science_goggles_wearers) + if (L.client) + L.client.images |= infectedMob.pathogen + return + +/datum/disease/advanced/proc/incubate(atom/incubator, mutatechance=1, specified_stage=0) + mutatechance *= mutation_modifier + + var/mob/living/body = null + var/obj/item/weapon/virusdish/dish = null + var/obj/machinery/disease2/incubator/machine = null + + if (isliving(incubator)) + body = incubator + else if (istype(incubator,/obj/item/weapon/virusdish)) + dish = incubator + if (istype(dish.loc,/obj/machinery/disease2/incubator)) + machine = dish.loc + + if(specified_stage) + for(var/datum/symptom/e in symptoms) + if(e.stage == specified_stage) + e.multiplier_tweak(0.1 * rand(1, 3)) + minormutate(specified_stage) + if(e.chance == e.max_chance && prob(strength) && e.max_chance <= initial(e.max_chance) * 3) + e.max_chance++ + + if (mutatechance > 0 && (body || dish) && incubator.reagents) + if (incubator.reagents.has_reagent(/datum/reagent/toxin/mutagen, 0.5) && incubator.reagents.has_reagent(/datum/reagent/consumable/nutriment/protein,0.5)) + if(incubator.reagents.remove_reagent(/datum/reagent/toxin/mutagen, 0.5) && incubator.reagents.remove_reagent(/datum/reagent/consumable/nutriment/protein,0.5)) + log += "
[ROUND_TIME()] Robustness Strengthening (Mutagen and Protein in [incubator])" + var/change = rand(1,5) + robustness = min(100,robustness + change) + for(var/datum/symptom/e in symptoms) + e.multiplier_tweak(0.1) + if (dish) + if (machine) + machine.update_minor(dish,0,change,0.1) + else if (incubator.reagents.has_reagent(/datum/reagent/toxin/mutagen, 0.5) && incubator.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin,0.5)) + if(incubator.reagents.remove_reagent(/datum/reagent/toxin/mutagen, 0.5) && incubator.reagents.remove_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin,0.5)) + log += "
[ROUND_TIME()] Robustness Weakening (Mutagen and spaceacillin in [incubator])" + var/change = rand(1,5) + robustness = max(0,robustness - change) + for(var/datum/symptom/e in symptoms) + e.multiplier_tweak(-0.1) + if (dish) + if (machine) + machine.update_minor(dish,0,-change,-0.1) + else + if(incubator.reagents.remove_reagent(/datum/reagent/toxin/mutagen, 0.05) && prob(mutatechance)) + log += "
[ROUND_TIME()] Effect Mutation (Mutagen in [incubator])" + effectmutate(body != null) + if (dish) + if(dish.info && dish.analysed) + dish.info = "OUTDATED : [dish.info]" + dish.analysed = 0 + dish.update_icon() + if (machine) + machine.update_major(dish) + if(incubator.reagents.remove_reagent(/datum/reagent/consumable/nutriment/protein,0.05) && prob(mutatechance)) + log += "
[ROUND_TIME()] Strengthening (/datum/reagent/consumable/nutriment/protein in [incubator])" + var/change = rand(1,5) + strength = min(100,strength + change) + if (dish) + if (machine) + machine.update_minor(dish,change) + if(incubator.reagents.remove_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin,0.05) && prob(mutatechance)) + log += "
[ROUND_TIME()] Weakening (/datum/reagent/medicine/antipathogenic/spaceacillin in [incubator])" + var/change = rand(1,5) + strength = max(0,strength - change) + if (dish) + if (machine) + machine.update_minor(dish,-change) + if(incubator.reagents.remove_reagent(/datum/reagent/uranium/radium,0.02) && prob(mutatechance/8)) + log += "
[ROUND_TIME()] Antigen Mutation (Radium in [incubator])" + antigenmutate() + if (dish) + if(dish.info && dish.analysed) + dish.info = "OUTDATED : [dish.info]" + dish.analysed = 0 + if (machine) + machine.update_major(dish) + +/datum/disease/advanced/proc/makerandom(var/list/str = list(), var/list/rob = list(), var/list/anti = list(), var/list/bad = list(), var/atom/source = null) + //ID + uniqueID = rand(0,9999) + subID = rand(0,9999) + + //base stats + strength = rand(str[1],str[2]) + robustness = rand(rob[1],rob[2]) + roll_antigen(anti) + + //effects + for(var/i = 1; i <= max_stages; i++) + var/selected_badness = pick( + bad[EFFECT_DANGER_HELPFUL];EFFECT_DANGER_HELPFUL, + bad[EFFECT_DANGER_FLAVOR];EFFECT_DANGER_FLAVOR, + bad[EFFECT_DANGER_ANNOYING];EFFECT_DANGER_ANNOYING, + bad[EFFECT_DANGER_HINDRANCE];EFFECT_DANGER_HINDRANCE, + bad[EFFECT_DANGER_HARMFUL];EFFECT_DANGER_HARMFUL, + bad[EFFECT_DANGER_DEADLY];EFFECT_DANGER_DEADLY, + ) + var/datum/symptom/e = new_effect(text2num(selected_badness), i) + symptoms += e + log += "
[ROUND_TIME()] Added effect [e.name] ([e.chance]% Occurence)." + + //slightly randomized infection chance + var/variance = initial(infectionchance)/10 + infectionchance = rand(initial(infectionchance)-variance,initial(infectionchance)+variance) + infectionchance_base = infectionchance + + //cosmetic petri dish stuff - if set beforehand, will not be randomized + if (!color) + var/list/randomhexes = list("8","9","a","b","c","d","e") + color = "#[pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)]" + pattern = rand(1,6) + pattern_color = "#[pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)]" + + //spreading vectors - if set beforehand, will not be randomized + if (!spread_flags) + randomize_spread() + + //logging + log += "
[ROUND_TIME()] Created and Randomized
" + + //admin panel + if (origin == "Unknown") + if (istype(source,/obj/item/weapon/virusdish)) + if (isturf(source.loc)) + var/turf/T = source.loc + if (istype(T.loc,/area/centcom)) + origin = "Centcom" + else if (istype(T.loc,/area/station/medical/virology)) + origin = "Pathology" + update_global_log() + +/datum/disease/advanced/proc/new_effect(badness = 2, stage = 0) + var/list/datum/symptom/list = list() + var/list/to_choose = subtypesof(/datum/symptom) + for(var/e in to_choose) + var/datum/symptom/f = new e + if(!f.restricted && f.stage == stage && text2num(f.badness) == badness) + list += f + if (list.len <= 0) + return new_random_effect(badness+1,badness-1,stage) + else + var/datum/symptom/e = pick(list) + e.chance = rand(1, e.max_chance) + return e + +/datum/disease/advanced/proc/new_random_effect(var/max_badness = 5, var/min_badness = 0, var/stage = 0, var/old_effect) + var/list/datum/symptom/list = list() + var/list/to_choose = subtypesof(/datum/symptom) + if(old_effect) //So it doesn't just evolve right back into the previous virus type + to_choose.Remove(old_effect) + for(var/e in to_choose) + var/datum/symptom/f = new e + if(!f.restricted && f.stage == stage && text2num(f.badness) <= max_badness && text2num(f.badness) >= min_badness) + list += f + if (list.len <= 0) + return new_random_effect(min(max_badness+1,5),max(0,min_badness-1),stage) + else + var/datum/symptom/e = pick(list) + e.chance = rand(1, e.max_chance) + return e + +/datum/disease/advanced/proc/randomize_spread() + spread_flags = DISEASE_SPREAD_BLOOD //without blood spread_flags, the disease cannot be extracted or cured, we don't want that for regular diseases + if (prob(5)) //5% chance of spreading through both contact and the air. + spread_flags |= DISEASE_SPREAD_CONTACT_SKIN + spread_flags |= DISEASE_SPREAD_AIRBORNE + else if (prob(40)) //38% chance of spreading through the air only. + spread_flags |= DISEASE_SPREAD_AIRBORNE + else if (prob(60)) //34,2% chance of spreading through contact only. + spread_flags |= DISEASE_SPREAD_CONTACT_SKIN + //22,8% chance of staying in blood + +/datum/disease/advanced/proc/minormutate(index) + var/datum/symptom/e = get_effect(index) + e.minormutate() + infectionchance = min(50,infectionchance + rand(0,10)) + log += "
[ROUND_TIME()] Infection chance now [infectionchance]%" + +/datum/disease/advanced/proc/minorstrength(index) + var/datum/symptom/e = get_effect(index) + e.multiplier_tweak(0.1) + +/datum/disease/advanced/proc/minorweak(index) + var/datum/symptom/e = get_effect(index) + e.multiplier_tweak(-0.1) + +/datum/disease/advanced/proc/get_effect(index) + if(!index) + return pick(symptoms) + return symptoms[clamp(index,0,symptoms.len)] + +//Major Mutations +/datum/disease/advanced/proc/effectmutate(var/inBody=FALSE) + clean_global_log() + subID = rand(0,9999) + var/list/randomhexes = list("7","8","9","a","b","c","d","e") + var/colormix = "#[pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)]" + color = BlendRGB(color,colormix,0.25) + var/i = rand(1, symptoms.len) + var/datum/symptom/e = symptoms[i] + var/datum/symptom/f + if (inBody)//mutations that occur directly in a body don't cause helpful symptoms to become deadly instantly. + f = new_random_effect(min(5,text2num(e.badness)+1), max(0,text2num(e.badness)-1), e.stage, e.type) + else + f = new_random_effect(min(5,text2num(e.badness)+2), max(0,text2num(e.badness)-3), e.stage, e.type)//badness is slightly more likely to go down than up. + symptoms[i] = f + log += "
[ROUND_TIME()] Mutated effect [e.name] [e.chance]% into [f.name] [f.chance]%." + update_global_log() + +/datum/disease/advanced/proc/antigenmutate() + clean_global_log() + subID = rand(0,9999) + var/old_dat = get_antigen_string() + roll_antigen() + log += "
[ROUND_TIME()] Mutated antigen [old_dat] into [get_antigen_string()]." + update_global_log() + +/datum/disease/advanced/proc/get_antigen_string() + var/dat = "" + for (var/A in antigen) + dat += "[A]" + return dat + +/datum/disease/advanced/proc/roll_antigen(list/factors = list()) + if (factors.len <= 0) + antigen = list(pick(GLOB.all_antigens)) + antigen |= pick(GLOB.all_antigens) + else + var/selected_first_antigen = pick( + factors[ANTIGEN_BLOOD];ANTIGEN_BLOOD, + factors[ANTIGEN_COMMON];ANTIGEN_COMMON, + factors[ANTIGEN_RARE];ANTIGEN_RARE, + factors[ANTIGEN_ALIEN];ANTIGEN_ALIEN, + ) + + antigen = list(pick(antigen_family(selected_first_antigen))) + + var/selected_second_antigen = pick( + factors[ANTIGEN_BLOOD];ANTIGEN_BLOOD, + factors[ANTIGEN_COMMON];ANTIGEN_COMMON, + factors[ANTIGEN_RARE];ANTIGEN_RARE, + factors[ANTIGEN_ALIEN];ANTIGEN_ALIEN, + ) + + antigen |= pick(antigen_family(selected_second_antigen)) + + +/datum/disease/advanced/proc/activate(mob/living/mob, starved = FALSE, seconds_per_tick) + if((mob.stat == DEAD) && !process_dead) + return + + //Searing body temperatures cure diseases, on top of killing you. + if(mob.bodytemperature > max_bodytemperature) + cure(mob,1) + return + + if(!(infectable_biotypes & mob.mob_biotypes)) + return + + if(!mob.immune_system.CanInfect(src)) + cure(mob) + return + + //Freezing body temperatures halt diseases completely + if(mob.bodytemperature < min_bodytemperature) + return + + //Virus food speeds up disease progress + if(!ismouse(mob)) + if(mob.reagents?.has_reagent(/datum/reagent/consumable/virus_food)) + mob.reagents.remove_reagent(/datum/reagent/consumable/virus_food, 0.1) + if(!logged_virusfood) + log += "
[ROUND_TIME()] Virus Fed ([mob.reagents.get_reagent_amount(/datum/reagent/consumable/virus_food)]U)" + logged_virusfood=1 + ticks += 10 + else + logged_virusfood=0 + if(prob(strength * 0.1)) + incubate(mob, 1) + //Moving to the next stage + if(ticks > stage*100 && prob(stageprob)) + incubate(mob, 1) + if(stage < max_stages) + log += "
[ROUND_TIME()] NEXT STAGE ([stage])" + stage++ + ticks = 0 + + //Pathogen killing each others + for (var/datum/disease/advanced/enemy_pathogen as anything in mob.diseases) + if(enemy_pathogen == src) + continue + + if ((enemy_pathogen.form in can_kill) && strength > enemy_pathogen.strength) + log += "
[ROUND_TIME()] destroyed enemy [enemy_pathogen.form] #[enemy_pathogen.uniqueID]-[enemy_pathogen.subID] ([strength] > [enemy_pathogen.strength])" + enemy_pathogen.cure(mob) + + // This makes it so that only ever gets affected by the equivalent of one virus so antags don't just stack a bunch + if(starved) + return + + var/list/immune_data = GetImmuneData(mob) + + for(var/datum/symptom/e in symptoms) + if (e.can_run_effect(immune_data[1], seconds_per_tick)) + e.run_effect(mob, src) + + //fever is a reaction of the body's immune system to the infection. The higher the antibody concentration (and the disease still not cured), the higher the fever + if (mob.bodytemperature < BODYTEMP_HEAT_DAMAGE_LIMIT)//but we won't go all the way to burning up just because of a fever, probably + var/fever = round((robustness / 100) * (immune_data[2] / 10) * (stage / max_stages)) + switch (mob.mob_size) + if (MOB_SIZE_TINY) + mob.bodytemperature += fever*0.2 + if (MOB_SIZE_SMALL) + mob.bodytemperature += fever*0.5 + if (MOB_SIZE_HUMAN) + mob.bodytemperature += fever + if (MOB_SIZE_LARGE) + mob.bodytemperature += fever*1.5 + if (MOB_SIZE_HUGE) + mob.bodytemperature += fever*2 + + if (fever > 0 && prob(3)) + switch (fever_warning) + if (0) + to_chat(mob, span_warning("You feel a fever coming on, your body warms up and your head hurts a bit.")) + fever_warning++ + if (1) + if (mob.bodytemperature > 320) + to_chat(mob, span_warning("Your palms are sweaty.")) + fever_warning++ + if (2) + if (mob.bodytemperature > 335) + to_chat(mob, span_warning("Your knees are weak.")) + fever_warning++ + if (3) + if (mob.bodytemperature > 350) + to_chat(mob, span_warning("Your arms are heavy.")) + fever_warning++ + + + ticks += speed + +/proc/virus_copylist(list/list) + if(!length(list)) + return list() + var/list/L = list() + for(var/datum/disease/advanced/D as anything in list) + L += D.Copy() + return L + +/datum/disease/advanced/cure(mob/living/carbon/mob, condition=0) + /* TODO + switch (condition) + if (0) + log_debug("[form] [uniqueID]-[subID] in [key_name(mob)] has been cured, and is being removed from their body.") + if (1) + log_debug("[form] [uniqueID]-[subID] in [key_name(mob)] has died from extreme temperature inside their host, and is being removed from their body.") + if (2) + log_debug("[form] [uniqueID]-[subID] in [key_name(mob)] has been wiped out by an immunity overload.") + */ + for(var/datum/symptom/e in symptoms) + e.disable_effect(mob, src) + mob.diseases -= src + add_event_to_buffer(mob, data = "was cured of virus: [admin_details()] at [loc_name(mob.loc)].", log_key = "VIRUS") + //--Plague Stuff-- + /* + var/datum/faction/plague_mice/plague = find_active_faction_by_type(/datum/faction/plague_mice) + if (plague && ("[uniqueID]-[subID]" == plague.diseaseID)) + plague.update_hud_icons() + */ + //---------------- + var/list/V = filter_disease_by_spread(mob.diseases, required = DISEASE_SPREAD_CONTACT_SKIN) + if (V && V.len <= 0) + GLOB.infected_contact_mobs -= mob + if (mob.pathogen) + for (var/mob/living/L in GLOB.science_goggles_wearers) + if (L.client) + L.client.images -= mob.pathogen + + +/datum/disease/advanced/proc/GetImmuneData(mob/living/mob) + var/lowest_stage = stage + var/highest_concentration = 0 + + if (mob.immune_system) + var/immune_system = mob.immune_system.GetImmunity() + var/immune_str = immune_system[1] + var/list/antibodies = immune_system[2] + var/subdivision = (strength - ((robustness * strength) / 100)) / max_stages + //for each antigen, we measure the corresponding antibody concentration in the carrier's immune system + //the less robust the pathogen, the more likely that further stages' effects won't activate at a given concentration + for (var/A in antigen) + var/concentration = immune_str * antibodies[A] + highest_concentration = max(highest_concentration,concentration) + var/i = lowest_stage + while (i > 0) + if (concentration > (strength - i * subdivision)) + lowest_stage = i-1 + i-- + + return list(lowest_stage,highest_concentration) + +/datum/disease/advanced/proc/name(override=FALSE) + .= "[form] #["[uniqueID]"][childID ? "-["[childID]"]" : ""]" + + if (!override && ("[uniqueID]-[subID]" in GLOB.virusDB)) + var/datum/data/record/V = GLOB.virusDB["[uniqueID]-[subID]"] + .= V.fields["name"] + +/datum/disease/advanced/proc/real_name() + .= "[form] #["[uniqueID]"]-["[subID]"]" + if ("[uniqueID]-[subID]" in GLOB.virusDB) + var/datum/data/record/v = GLOB.virusDB["[uniqueID]-[subID]"] + var/nickname = v.fields["nickname"] ? " \"[v.fields["nickname"]]\"" : "" + . += nickname + +/datum/disease/advanced/proc/get_subdivisions_string() + var/subdivision = (strength - ((robustness * strength) / 100)) / max_stages + var/dat = "(" + for (var/i = 1 to max_stages) + dat += "[round(strength - i * subdivision)]" + if (i < max_stages) + dat += ", " + dat += ")" + return dat + +/datum/disease/advanced/proc/get_info() + var/r = "GNAv3 [name()]" + r += "
Strength / Robustness : [strength]% / [robustness]% - [get_subdivisions_string()]" + r += "
Infectability : [infectionchance]%" + r += "
Spread forms : [get_spread_string()]" + r += "
Progress Speed : [stageprob]%" + r += "
" + for(var/datum/symptom/e in symptoms) + r += "
Stage [e.stage] - [e.name] (Danger: [e.badness]). Strength: [e.multiplier]. Occurrence: [e.chance]%.
" + r += "
[e.desc]
" + r += "
" + r += "
Antigen pattern: [get_antigen_string()]" + r += "
last analyzed at: [worldtime2text()]" + return r + +/datum/disease/advanced/proc/get_spread_string() + var/dat = "" + var/check = 0 + if (spread_flags & DISEASE_SPREAD_BLOOD) + dat += "Blood" + check += DISEASE_SPREAD_BLOOD + if (spread_flags > check) + dat += ", " + if (spread_flags & DISEASE_SPREAD_CONTACT_SKIN) + dat += "Skin Contact" + check += DISEASE_SPREAD_CONTACT_SKIN + if (spread_flags > check) + dat += ", " + if (spread_flags & DISEASE_SPREAD_AIRBORNE) + dat += "Airborne" + check += DISEASE_SPREAD_AIRBORNE + if (spread_flags > check) + dat += ", " + if (spread_flags & DISEASE_SPREAD_CONTACT_FLUIDS) + dat += "Fluid Contact" + check += DISEASE_SPREAD_CONTACT_FLUIDS + if(spread_flags > check) + dat += ", " + if (spread_flags & DISEASE_SPREAD_NON_CONTAGIOUS) + dat += "Non Contagious" + check += DISEASE_SPREAD_NON_CONTAGIOUS + if(spread_flags > check) + dat += ", " + if (spread_flags & DISEASE_SPREAD_SPECIAL) + dat += "UNKNOWN SPREAD" + check += DISEASE_SPREAD_SPECIAL + if(spread_flags > check) + dat += ", " + /* + if (spread_flags & SPREAD_COLONY) + dat += "Colonizing" + check += SPREAD_COLONY + if (spread_flags > check) + dat += ", " + if (spread_flags & SPREAD_MEMETIC) + dat += "Memetic" + check += SPREAD_MEMETIC + if (spread_flags > check) + dat += ", " + */ + return dat + +/datum/disease/advanced/proc/addToDB() + if ("[uniqueID]-[subID]" in GLOB.virusDB) + return 0 + childID = 0 + for (var/virus_file in GLOB.virusDB) + var/datum/data/record/v = GLOB.virusDB[virus_file] + if (v.fields["id"] == uniqueID) + childID++ + var/datum/data/record/v = new() + v.fields["id"] = uniqueID + v.fields["sub"] = subID + v.fields["child"] = childID + v.fields["form"] = form + v.fields["name"] = name() + v.fields["nickname"] = "" + v.fields["description"] = get_info() + v.fields["description_hidden"] = get_info(TRUE) + v.fields["custom_desc"] = "No comments yet." + v.fields["antigen"] = get_antigen_string() + v.fields["spread_flags_type"] = get_spread_string() + v.fields["danger"] = "Undetermined" + GLOB.virusDB["[uniqueID]-[subID]"] = v + return 1 + +/datum/disease/advanced/virus + form = "Virus" + max_stages = 4 + infectionchance = 70 + infectionchance_base = 70 + stageprob = 10 + stage_variance = -1 + can_kill = list("Bacteria") + +/datum/disease/advanced/bacteria//faster spread_flags and progression, but only 3 stages max, and reset to stage 1 on every spread_flags + form = "Bacteria" + max_stages = 3 + infectionchance = 90 + infectionchance_base = 90 + stageprob = 30 + stage_variance = -4 + can_kill = list("Parasite") + +/datum/disease/advanced/parasite//slower spread_flags. stage preserved on spread_flags + form = "Parasite" + infectionchance = 50 + infectionchance_base = 50 + stageprob = 10 + stage_variance = 0 + can_kill = list("Virus") + +/datum/disease/advanced/prion//very fast progression, but very slow spread_flags and resets to stage 1. + form = "Prion" + infectionchance = 10 + infectionchance_base = 10 + stageprob = 80 + stage_variance = -10 + can_kill = list() + + +/datum/disease/advanced/vv_get_dropdown() + . = ..() + VV_DROPDOWN_OPTION("","------") + VV_DROPDOWN_OPTION(VV_HK_VIEW_DISEASE_DATA, "View Disease Data") + +/datum/disease/advanced/vv_do_topic(list/href_list) + . = ..() + if(href_list[VV_HK_VIEW_DISEASE_DATA]) + create_disease_info_pane(usr) + +/datum/disease/advanced/proc/create_disease_info_pane(mob/user) + var/datum/browser/popup = new(user, "\ref[src]", "GNAv3 [form] #[uniqueID]-[subID]", 600, 500, src) + var/content = get_info() + content += "
LOGS
" + content += log + popup.set_content(content) + popup.open() + +/* +/client/proc/view_disease_data() + set category = "Admin.Logging" + set name = "View Disease List" + set desc = "views disease list and on selection opens the data" + + if(!holder) + return + var/list/diseases = list() + for(var/datum/disease/advanced/disease as anything in GLOB.inspectable_diseases) + if(!disease || !istype(disease)) + continue + if(disease.affected_mob) + diseases["GNAv3 [disease.form] #[disease.uniqueID]-[disease.subID]-[disease.childID] [disease.affected_mob]"] = disease + else + diseases["GNAv3 [disease.form] #[disease.uniqueID]-[disease.subID]-[disease.childID]"] = disease + var/disease = input("Choose a disease", "Diseases") as null|anything in sort_list(diseases, /proc/cmp_typepaths_asc) + if(!disease) + return + var/datum/disease/advanced/actual_disease = diseases[disease] + if(!actual_disease) + return + actual_disease.create_disease_info_pane(usr) +*/ + +/proc/make_custom_virus(client/C, mob/living/infectedMob) + if(!istype(C) || !C.holder) + return 0 + + var/datum/disease/advanced/D = new /datum/disease/advanced() + D.origin = "Badmin" + + var/list/known_forms = list() + for (var/disease_type in subtypesof(/datum/disease/advanced)) + var/datum/disease/advanced/d_type = disease_type + known_forms[initial(d_type.form)] = d_type + + known_forms += "custom" + + /* + if (islist(GLOB.inspectable_diseases) && GLOB.inspectable_diseases.len > 0) + known_forms += "infect with an already existing pathogen" + */ + + var/chosen_form = input(C, "Choose a form for your pathogen", "Choose a form") as null | anything in known_forms + if (!chosen_form) + qdel(D) + return + + if (chosen_form == "infect with an already existing pathogen") + var/list/existing_pathogen = list() + for(var/datum/disease/advanced/dis as anything in GLOB.inspectable_diseases) + existing_pathogen += dis + var/chosen_pathogen = input(C, "Choose a pathogen", "Choose a pathogen") as null | anything in existing_pathogen + if (!chosen_pathogen) + qdel(D) + return + var/datum/disease/advanced/dis = chosen_pathogen + D = dis.Copy() + D.origin = "[D.origin] (Badmin)" + else + if (chosen_form == "custom") + var/form_name = copytext(sanitize(input(C, "Give your custom form a name", "Name your form", "Pathogen") as null | text),1,MAX_NAME_LEN) + if (!form_name) + qdel(D) + return + D.form = form_name + D.max_stages = input(C, "How many stages will your pathogen have?", "Custom Pathogen", D.max_stages) as num + D.max_stages = clamp(D.max_stages,1,99) + D.infectionchance = input(C, "What will be your pathogen's infection chance?", "Custom Pathogen", D.infectionchance) as num + D.infectionchance = clamp(D.infectionchance,0,100) + D.infectionchance_base = D.infectionchance + D.stageprob = input(C, "What will be your pathogen's progression speed?", "Custom Pathogen", D.stageprob) as num + D.stageprob = clamp(D.stageprob,0,100) + D.stage_variance = input(C, "What will be your pathogen's stage variance?", "Custom Pathogen", D.stage_variance) as num + D.stageprob = clamp(D.stageprob,-1*D.max_stages,0) + //D.can_kill = something something a while loop but probably not worth the effort. If you need it for your bus code it yourself. + else + var/d_type = known_forms[chosen_form] + var/datum/disease/advanced/d_inst = new d_type + D.form = chosen_form + D.max_stages = d_inst.max_stages + D.infectionchance = d_inst.infectionchance + D.stageprob = d_inst.stageprob + D.stage_variance = d_inst.stage_variance + D.can_kill = d_inst.can_kill.Copy() + qdel(d_inst) + + D.strength = input(C, "What will be your pathogen's strength? (1-50 is trivial to cure. 50-100 requires a bit more effort)", "Pathogen Strength", D.infectionchance) as num + D.strength = clamp(D.strength,0,100) + + D.robustness = input(C, "What will be your pathogen's robustness? (1-100) Lower values mean that infected can carry the pathogen without getting affected by its symptoms.", "Pathogen Robustness", D.infectionchance) as num + D.robustness = clamp(D.strength,0,100) + + D.uniqueID = clamp(input(C, "You can specify the 4 number ID for your Pathogen, or just use this randomly generated one.", "Pick a unique ID", rand(0,9999)) as num, 0, 9999) + + D.subID = rand(0,9999) + D.childID = 0 + + for(var/i = 1; i <= D.max_stages; i++) // run through this loop until everything is set + var/datum/symptom/symptom = input(C, "Choose a symptom for your disease's stage [i] (out of [D.max_stages])", "Choose a Symptom") as null | anything in (subtypesof(/datum/symptom)) + if (!symptom) + return 0 + + var/datum/symptom/e = new symptom(D) + e.stage = i + e.chance = input(C, "Choose the default chance for this effect to activate", "Effect", e.chance) as null | num + e.chance = clamp(e.chance,0,100) + e.max_chance = input(C, "Choose the maximum chance for this effect to activate", "Effect", e.max_chance) as null | num + e.max_chance = clamp(e.max_chance,0,100) + e.multiplier = input(C, "Choose the default strength for this effect", "Effect", e.multiplier) as null | num + e.multiplier = clamp(e.multiplier,0,100) + e.max_multiplier = input(C, "Choose the maximum strength for this effect", "Effect", e.max_multiplier) as null | num + e.max_multiplier = clamp(e.max_multiplier,0,100) + + D.log += "Added [e.name] at [e.chance]% chance and [e.multiplier] strength
" + D.symptoms += e + + if (alert("Do you want to specify which antigen are selected?","Choose your Antigen","Yes","No") == "Yes") + D.antigen = list(input(C, "Choose your first antigen", "Choose your Antigen") as null | anything in GLOB.all_antigens) + if (!D.antigen) + D.antigen = list(input(C, "Choose your second antigen", "Choose your Antigen") as null | anything in GLOB.all_antigens) + else + D.antigen |= input(C, "Choose your second antigen", "Choose your Antigen") as null | anything in GLOB.all_antigens + if (!D.antigen) + if (alert("Beware, your disease having no antigen means that it's incurable. We can still roll some random antigen for you. Are you sure you want your pathogen to have no antigen anyway?","Choose your Antigen","Yes","No") == "No") + D.roll_antigen() + else + D.antigen = list() + else + D.roll_antigen() + + var/list/randomhexes = list("8","9","a","b","c","d","e") + D.color = "#[pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)]" + D.pattern = rand(1,6) + D.pattern_color = "#[pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)]" + if (alert("Do you want to specify the appearance of your pathogen in a petri dish?","Choose your appearance","Yes","No") == "Yes") + D.color = input(C, "Choose the color of the dish", "Cosmetic") as color + D.pattern = input(C, "Choose the shape of the pattern inside the dish (1 to 6)", "Cosmetic",rand(1,6)) as num + D.pattern = clamp(D.pattern,1,6) + D.pattern_color = input(C, "Choose the color of the pattern", "Cosmetic") as color + + D.spread_flags = 0 + if (alert("Can this virus spread_flags into blood? (warning! if choosing No, this virus will be impossible to sample and analyse!)","Spreading Vectors","Yes","No") == "Yes") + D.spread_flags |= DISEASE_SPREAD_BLOOD + if(D.allowed_transmission & DISEASE_SPREAD_CONTACT_SKIN) + if (alert("Can this virus spread_flags by contact, and on items?","Spreading Vectors","Yes","No") == "Yes") + D.spread_flags |= DISEASE_SPREAD_CONTACT_SKIN + if(D.allowed_transmission & DISEASE_SPREAD_AIRBORNE) + if (alert("Can this virus spread_flags through the air?","Spreading Vectors","Yes","No") == "Yes") + D.spread_flags |= DISEASE_SPREAD_AIRBORNE + /* + if(D.allowed_transmission & SPREAD_COLONY) + if (alert("Does this fungus prefer suits? Exclusive with contact/air.","Spreading Vectors","Yes","No") == "Yes") + D.spread_flags |= SPREAD_COLONY + D.spread_flags &= ~(SPREAD_BLOOD|SPREAD_AIRBORNE) + if(D.allowed_transmission & SPREAD_MEMETIC) + if (alert("Can this virus spread_flags through words?","Spreading Vectors","Yes","No") == "Yes") + D.spread_flags |= SPREAD_MEMETIC + */ + GLOB.inspectable_diseases -= "[D.uniqueID]-[D.subID]"//little odds of this happening thanks to subID but who knows + D.update_global_log() + + if (alert("Lastly, do you want this pathogen to be added to the station's Database? (allows medical HUDs to locate infected mobs, among other things)","Pathogen Database","Yes","No") == "Yes") + D.addToDB() + + if (istype(infectedMob)) + D.log += "
[ROUND_TIME()] Infected [key_name(infectedMob)]" + if(!length(infectedMob.diseases)) + infectedMob.diseases = list() + infectedMob.diseases += D + var/nickname = "" + if ("[D.uniqueID]-[D.subID]" in GLOB.virusDB) + var/datum/data/record/v = GLOB.virusDB["[D.uniqueID]-[D.subID]"] + nickname = v.fields["nickname"] ? " \"[v.fields["nickname"]]\"" : "" + log_admin("[infectedMob] was infected with [D.form] #[D.uniqueID]-[D.subID][nickname] by [C.ckey]") + message_admins("[infectedMob] was infected with [D.form] #["[D.uniqueID]"]-["[D.subID]"][nickname] by [C.ckey]") + D.AddToGoggleView(infectedMob) + else + var/obj/item/weapon/virusdish/dish = new(C.mob.loc) + dish.contained_virus = D + dish.growth = rand(5, 50) + dish.name = "growth dish (Unknown [D.form])" + if ("[D.uniqueID]-[D.subID]" in GLOB.virusDB) + dish.name = "growth dish ([D.name(TRUE)])" + dish.update_icon() + + return 1 + +/mob/var/disease_view = FALSE +/client/proc/disease_view() + set category = "Admin.Debug" + set name = "Disease View" + set desc = "See viro Overlay" + + if(!holder) + return + if(!mob) + return + if(mob.disease_view) + mob.stopvirusView() + else + mob.virusView() + mob.disease_view = !mob.disease_view + +/client/proc/diseases_panel() + set category = "Admin.Logging" + set name = "Disease Panel" + set desc = "See diseases and disease information" + + if(!holder) + return + holder.diseases_panel() + +/datum/admins/var/viewingID + +/datum/admins/proc/diseases_panel() + if (!GLOB.inspectable_diseases || !length(GLOB.inspectable_diseases)) + alert("There are no pathogen in the round currently!") + return + var/list/logs = list() + var/dat = {" + + + + +

Disease Panel

+ + + + + + + + + + "} + + for (var/ID in GLOB.inspectable_diseases) + var/infctd_mobs = 0 + var/infctd_mobs_dead = 0 + var/infctd_items = 0 + var/dishes = 0 + for (var/mob/living/L in GLOB.mob_list) + for(var/datum/disease/advanced/D as anything in L.diseases) + if (ID == "[D.uniqueID]-[D.subID]") + infctd_mobs++ + if (L.stat == DEAD) + infctd_mobs_dead++ + if(!length(logs["[ID]"])) + logs["[ID]"]= list() + logs["[ID]"] += "[L]" + logs["[ID]"]["[L]"] = D.log + + for (var/obj/item/I in GLOB.infected_items) + for(var/datum/disease/advanced/D as anything in I.viruses) + if (ID == "[D.uniqueID]-[D.subID]") + infctd_items++ + if(!length(logs["[ID]"])) + logs["[ID]"] = list() + logs["[ID]"] += "[I]" + logs["[ID]"]["[I]"] = D.log + for (var/obj/item/weapon/virusdish/dish in GLOB.virusdishes) + if (dish.contained_virus) + if (ID == "[dish.contained_virus.uniqueID]-[dish.contained_virus.subID]") + dishes++ + if(!length(logs["[ID]"])) + logs["[ID]"] = list() + logs["[ID]"] += "[dish]" + logs["[ID]"]["[dish]"] = dish.contained_virus.log + + var/datum/disease/advanced/D = GLOB.inspectable_diseases[ID] + dat += {" + + + + + + + + "} + + dat += {"
Disease IDOriginin Database?Infected PeopleInfected Itemsin Growth Dishes
[D.form] #["[D.uniqueID]"]-["[D.subID]"][D.origin][(ID in GLOB.virusDB) ? "Yes" : "No"][infctd_mobs][infctd_mobs_dead ? " (including [infctd_mobs_dead] dead)" : "" ][infctd_items][dishes]
+ "} + dat += {" + + + "} + for(var/item in logs[viewingID]) + dat += {" + + "} + dat += {"
Disease Logs
[item] - [viewingID]
[logs[viewingID][item]] +
+ + + "} + usr << browse(dat, "window=diseasespanel;size=705x450") + +/datum/admins/Topic(href, href_list) + . = ..() + if(href_list["diseasepanel_examine"]) + viewingID = href_list["diseasepanel_examine"] + diseases_panel() diff --git a/monkestation/code/modules/virology/disease/premades/_base_premades.dm b/monkestation/code/modules/virology/disease/premades/_base_premades.dm new file mode 100644 index 000000000000..790a0a50eb88 --- /dev/null +++ b/monkestation/code/modules/virology/disease/premades/_base_premades.dm @@ -0,0 +1,24 @@ +//Pre-Defined Global Diseases for use by nanites/wendigo/xenomicrobes/ect. +GLOBAL_LIST_INIT(global_diseases, list()) + +/proc/create_global_diseases() + for(var/disease_type in subtypesof(/datum/disease/advanced/premade)) + var/datum/disease/advanced/premade/D = new disease_type + GLOB.global_diseases[D.category] = D + D.update_global_log() + +/datum/disease/advanced/premade/New() + . = ..() + antigen = list(pick(antigen_family(ANTIGEN_RARE))) + antigen |= pick(antigen_family(ANTIGEN_RARE)) + uniqueID = rand(0,9999) + subID = rand(0,9999) + strength = rand(70,100) + +/datum/disease/advanced/premade/makerandom(list/str = list(), list/rob = list(), list/anti = list(), list/bad = list(), atom/source = null) + return + +/mob/living/proc/infect_disease_predefined(category, forced, notes) + if(!GLOB.global_diseases[category]) + return + infect_disease(GLOB.global_diseases[category], forced, notes) diff --git a/monkestation/code/modules/virology/disease/premades/adrenal_crisis.dm b/monkestation/code/modules/virology/disease/premades/adrenal_crisis.dm new file mode 100644 index 000000000000..f3919b51c9df --- /dev/null +++ b/monkestation/code/modules/virology/disease/premades/adrenal_crisis.dm @@ -0,0 +1,15 @@ +/datum/disease/advanced/premade/adrenal_crisis + name = "Adrenal Crisis" + origin = "Trauma" + category = DISEASE_TRAUMA + + symptoms = list( + new /datum/symptom/bad_adrenaline + ) + spread_flags = DISEASE_SPREAD_BLOOD + robustness = 90 + + infectionchance = 0 + infectionchance_base = 0 + stage_variance = 0 + diff --git a/monkestation/code/modules/virology/disease/premades/anxiety.dm b/monkestation/code/modules/virology/disease/premades/anxiety.dm new file mode 100644 index 000000000000..6faf90aac98b --- /dev/null +++ b/monkestation/code/modules/virology/disease/premades/anxiety.dm @@ -0,0 +1,15 @@ +/datum/disease/advanced/premade/decloning + name = "Severe Anxiety" + form = "Infection" + origin = "Social Settings" + category = DISEASE_ANXIETY + + symptoms = list( + new /datum/symptom/anxiety + ) + spread_flags = DISEASE_SPREAD_BLOOD + robustness = 90 + strength = 100 + + infectionchance = 0 + infectionchance_base = 0 diff --git a/monkestation/code/modules/virology/disease/premades/decloning.dm b/monkestation/code/modules/virology/disease/premades/decloning.dm new file mode 100644 index 000000000000..2bddfa7cae66 --- /dev/null +++ b/monkestation/code/modules/virology/disease/premades/decloning.dm @@ -0,0 +1,16 @@ +/datum/disease/advanced/premade/decloning + name = "Cellular Degeneration" + form = "Virus" + origin = "Instability" + category = DISEASE_DECLONING + + symptoms = list( + new /datum/symptom/mutation + ) + spread_flags = DISEASE_SPREAD_BLOOD + robustness = 90 + strength = 100 + + infectionchance = 0 + infectionchance_base = 0 + can_kill = list("Bacteria") diff --git a/monkestation/code/modules/virology/disease/premades/flu.dm b/monkestation/code/modules/virology/disease/premades/flu.dm new file mode 100644 index 000000000000..15b891b7929d --- /dev/null +++ b/monkestation/code/modules/virology/disease/premades/flu.dm @@ -0,0 +1,16 @@ +/datum/disease/advanced/premade/cold + name = "Common Cold" + form = "Virus" + category = DISEASE_COLD + + symptoms = list( + new /datum/symptom/cough, + new /datum/symptom/sneeze, + new /datum/symptom/fridge, + ) + spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_SKIN | DISEASE_SPREAD_CONTACT_FLUIDS + robustness = 45 + + infectionchance = 70 + infectionchance_base = 86 + can_kill = list("Bacteria") diff --git a/monkestation/code/modules/virology/disease/premades/heart_attack.dm b/monkestation/code/modules/virology/disease/premades/heart_attack.dm new file mode 100644 index 000000000000..9d9b3133d13b --- /dev/null +++ b/monkestation/code/modules/virology/disease/premades/heart_attack.dm @@ -0,0 +1,15 @@ +/datum/disease/advanced/premade/heart_failure + name = "Heart Eating Worms" + form = "Worms" + origin = "Heart Worms" + category = DISEASE_HEART + + symptoms = list( + new /datum/symptom/heart_failure + ) + spread_flags = DISEASE_SPREAD_BLOOD + robustness = 75 + + infectionchance = 0 + infectionchance_base = 0 + stage_variance = 0 diff --git a/monkestation/code/modules/virology/disease/premades/transformations.dm b/monkestation/code/modules/virology/disease/premades/transformations.dm new file mode 100644 index 000000000000..aa22e69619d3 --- /dev/null +++ b/monkestation/code/modules/virology/disease/premades/transformations.dm @@ -0,0 +1,102 @@ +/datum/disease/advanced/premade/gondola + name = "Gondola Transformation" + form = "Gondola Cells" + origin = "Gondola Toxins" + category = DISEASE_GONDOLA + + symptoms = list( + new /datum/symptom/transformation/gondola + ) + spread_flags = DISEASE_SPREAD_BLOOD + robustness = 75 + + infectionchance = 0 + infectionchance_base = 0 + stage_variance = 0 + +/datum/disease/advanced/premade/gondola/digital + category = DISEASE_GONDOLA_DIGITAL + + symptoms = list( + new /datum/symptom/transformation/gondola/digital + ) + +/datum/disease/advanced/premade/xeno + name = "Xenomorph Transformation" + form = "Foreign Cells" + origin = "UNKNOWN" + category = DISEASE_XENO + + symptoms = list( + new /datum/symptom/transformation/xeno + ) + spread_flags = DISEASE_SPREAD_BLOOD + robustness = 75 + + infectionchance = 0 + infectionchance_base = 0 + stage_variance = 0 + +/datum/disease/advanced/premade/corgi + name = "Puppification" + form = "Puppy Cells" + origin = "Ian" + category = DISEASE_CORGI + + symptoms = list( + new /datum/symptom/transformation/corgi + ) + spread_flags = DISEASE_SPREAD_BLOOD + robustness = 75 + + infectionchance = 0 + infectionchance_base = 0 + stage_variance = 0 + +/datum/disease/advanced/premade/slime + name = "Slime Syndrome" + form = "Simplified Cells" + origin = "Slime Colonies" + category = DISEASE_SLIME + + symptoms = list( + new /datum/symptom/transformation/slime + ) + spread_flags = DISEASE_SPREAD_BLOOD + robustness = 75 + + infectionchance = 0 + infectionchance_base = 0 + stage_variance = 0 + +/datum/disease/advanced/premade/morph + name = "Gluttony" + form = "Hungering Cells" + origin = "The Hivemind" + category = DISEASE_MORPH + + symptoms = list( + new /datum/symptom/transformation/morph + ) + spread_flags = DISEASE_SPREAD_BLOOD + robustness = 75 + + infectionchance = 0 + infectionchance_base = 0 + stage_variance = 0 + +/datum/disease/advanced/premade/robot + name = "Nanite Conversion" + form = "Nanites" + origin = "Swarmers" + category = DISEASE_ROBOT + + symptoms = list( + new /datum/symptom/transformation/robot + ) + spread_flags = DISEASE_SPREAD_BLOOD + robustness = 75 + + infectionchance = 0 + infectionchance_base = 0 + stage_variance = 0 diff --git a/monkestation/code/modules/virology/disease/symtoms/_symptom.dm b/monkestation/code/modules/virology/disease/symtoms/_symptom.dm new file mode 100644 index 000000000000..5c23fa7cada7 --- /dev/null +++ b/monkestation/code/modules/virology/disease/symtoms/_symptom.dm @@ -0,0 +1,74 @@ +/datum/symptom + // How dangerous the symptom is. + // 0 = generally helpful (ex: full glass syndrome) + // 1 = neutral, just flavor text (ex: headache) + // 2 = minor inconvenience (ex: tourettes) + // 3 = severe inconvenience (ex: random tripping) + // 4 = likely to indirectly lead to death (ex: Harlequin Ichthyosis) + // 5 = will definitely kill you (ex: gibbingtons/necrosis) + var/badness = EFFECT_DANGER_ANNOYING + ///are we a restricted type + var/restricted = FALSE + var/encyclopedia = "" + var/stage = -1 + // Diseases start at stage 1. They slowly and cumulatively proceed their way up. + // Try to keep more severe effects in the later stages. + + var/chance = 3 + // Under normal conditions, the percentage chance per tick to activate. + var/max_chance = 6 + // Maximum percentage chance per tick. + + var/multiplier = 1 + // How strong the effects are. Use this in activate(). + var/max_multiplier = 1 + // Maximum multiplier. + + var/count = 0 + // How many times the effect has activated so far. + var/max_count = -1 + // How many times the effect should be allowed to activate. If -1, always activate. + + +/datum/symptom/proc/minormutate() + if (prob(20)) + chance = rand(initial(chance), max_chance) + +/datum/symptom/proc/multiplier_tweak(tweak) + multiplier = clamp(multiplier+tweak,1,max_multiplier) + + +/datum/symptom/proc/can_run_effect(active_stage = -1, seconds_per_tick) + if((count < max_count || max_count == -1) && (stage <= active_stage || active_stage == -1) && prob(min(chance * seconds_per_tick, max_chance))) + return 1 + return 0 + +/datum/symptom/proc/run_effect(mob/living/carbon/mob, datum/disease/advanced/disease) + if(count < 1) + first_activate(mob, disease) + activate(mob, disease) + count += 1 + +///this runs the first time its activated +/datum/symptom/proc/first_activate(mob/living/carbon/mob, datum/disease/advanced/disease) + +// The actual guts of the effect. Has a prob(chance)% to get called per tick. +/datum/symptom/proc/activate(mob/living/carbon/mob, datum/disease/advanced/disease) + +// If activation makes any permanent changes to the effect, this is where you undo them. +// Will not get called if the virus has never been activated. +/datum/symptom/proc/deactivate(mob/living/carbon/mob, datum/disease/advanced/disease) + +/datum/symptom/proc/on_touch(mob/living/carbon/mob, toucher, touched, touch_type) + // Called when the sufferer of the symptom bumps, is bumped, or is touched by hand. +/datum/symptom/proc/on_death(mob/living/carbon/mob) + // Called when the sufferer of the symptom dies +/datum/symptom/proc/side_effect(mob/living/mob) + // Called on every Life() while the body is alive +///called before speech goes out, returns FALSE if we stop, otherwise returns Edited Message +/datum/symptom/proc/on_speech(mob/living/mob) + + +/datum/symptom/proc/disable_effect(mob/living/mob, datum/disease/advanced/disease) + if (count > 0) + deactivate(mob, disease) diff --git a/monkestation/code/modules/virology/disease/symtoms/animation_procs.dm b/monkestation/code/modules/virology/disease/symtoms/animation_procs.dm new file mode 100644 index 000000000000..a6dda0939c95 --- /dev/null +++ b/monkestation/code/modules/virology/disease/symtoms/animation_procs.dm @@ -0,0 +1,84 @@ +#define COLOR_MARTIX_BASE list( 1.00, 0.00, 0.00, 0.00,\ + 0.00, 1.00, 0.00, 0.00,\ + 0.00, 0.00, 1.00, 0.00,\ + 0.00, 0.00, 0.00, 1.00,\ + 0.00, 0.00, 0.00, 0.00) + +#define COLOR_MATRIX_GRAYSCALE list(0.33,0.33,0.33,0.00,\ + 0.33,0.33,0.33,0.00,\ + 0.33,0.33,0.33,0.00,\ + 0.00,0.00,0.00,1.00,\ + 0.00,0.00,0.00,0.00) + +/atom/proc/fade_matrix(time = 1 SECONDS, matrix = COLOR_MATRIX_GRAYSCALE) + color = COLOR_MARTIX_BASE + animate(src, color=matrix, time=time, easing=SINE_EASING) + +/atom/proc/fade_from_matrix(time = 1 SECONDS, matrix = COLOR_MATRIX_GRAYSCALE) + color = matrix + animate(src, color=COLOR_MARTIX_BASE, time=time, easing=SINE_EASING) + +//generalized version of lavadrakes Lavaswoop +/atom/proc/fading_leap_up() + var/matrix/M = matrix() + var/loop_count = 15 + while(loop_count > 0) + loop_count-- + animate(src, transform = M, pixel_z = src.pixel_z + 12, alpha = src.alpha - 17, time = 1, loop = 1, easing = LINEAR_EASING) + M.Scale(1.2,1.2) + sleep(0.1 SECONDS) + alpha = 0 + +//inverse of above +/atom/proc/fading_leap_down() + var/matrix/M = matrix() + var/loop_count = 12 + M.Scale(15,15) + while(loop_count > 0) + loop_count-- + animate(src, transform = M, pixel_z = src.pixel_z - 12, alpha = src.alpha + 17, time = 1, loop = 1, easing = LINEAR_EASING) + M.Scale(0.8,0.8) + sleep(0.1 SECONDS) + animate(src, transform = M, pixel_z = 0, alpha = 255, time = 1, loop = 1, easing = LINEAR_EASING) + M.Scale(1,1) + + +///😰😰😰😰😰😰 +/atom/proc/bananeer(dir=null, total_time = 0.5 SECONDS, height = 16, stun_duration = 1 SECONDS, flip_count = 1) + animate(src) // cleanse animations as funny as a ton of stacked flips would be it would be an eye sore + var/matrix/M = transform + var/turn = 90 + if(isnull(dir)) + if(dir == EAST) + turn = 90 + else if(dir == WEST) + turn = -90 + else + if(prob(50)) + turn = -90 + + + var/flip_anim_step_time = total_time / (1 + 4 * flip_count) + animate(src, transform = matrix(M, turn, MATRIX_ROTATE | MATRIX_MODIFY), time = flip_anim_step_time, flags = ANIMATION_PARALLEL) + for(var/i in 1 to flip_count) + animate(transform = matrix(M, turn, MATRIX_ROTATE | MATRIX_MODIFY), time = flip_anim_step_time) + animate(transform = matrix(M, turn, MATRIX_ROTATE | MATRIX_MODIFY), time = flip_anim_step_time) + animate(transform = matrix(M, turn, MATRIX_ROTATE | MATRIX_MODIFY), time = flip_anim_step_time) + animate(transform = matrix(M, turn, MATRIX_ROTATE | MATRIX_MODIFY), time = flip_anim_step_time) + var/matrix/M2 = transform + animate(transform = matrix(M, 1.2, 0.7, MATRIX_SCALE | MATRIX_MODIFY), time = total_time * 0.125) + animate(transform = M2, time = total_time * 0.125) + + animate(src, pixel_y=height, time= total_time * 0.5, flags=ANIMATION_PARALLEL) + animate(pixel_y=-4, time= total_time * 0.5) + + if(isliving(src)) + var/mob/living/living = src + living.Knockdown(stun_duration) + animate(src, pixel_x = 0, pixel_y = 0, transform = src.transform.Turn(-turn), time = 3, easing = LINEAR_EASING, flags=ANIMATION_PARALLEL) + else + spawn(stun_duration + total_time) + animate(src, pixel_x = 0, pixel_y = 0, transform = src.transform.Turn(-turn), time = 3, easing = LINEAR_EASING, flags=ANIMATION_PARALLEL) + +#undef COLOR_MARTIX_BASE +#undef COLOR_MATRIX_GRAYSCALE diff --git a/monkestation/code/modules/virology/disease/symtoms/restricted/stage1.dm b/monkestation/code/modules/virology/disease/symtoms/restricted/stage1.dm new file mode 100644 index 000000000000..04aa2f3fc4e8 --- /dev/null +++ b/monkestation/code/modules/virology/disease/symtoms/restricted/stage1.dm @@ -0,0 +1,136 @@ +/datum/symptom/transformation + name = "Human Transformation" + desc = "Turns the target into a human." + restricted = 1 + max_count = 1 + stage = 1 + badness = EFFECT_DANGER_HARMFUL + max_chance = 10 + var/new_form = /mob/living/carbon/human + var/bantype + var/transformed_antag_datum //Do we add a specific antag datum once the transformation is complete? + var/old_form + +/datum/symptom/transformation/activate(mob/living/carbon/mob) + old_form = mob.type + do_disease_transformation(mob, new_form) + +/* +/datum/symptom/transformation/deactivate(mob/living/carbon/mob) + do_disease_transformation(mob, old_form) + to_chat(mob, span_notice("You feel like yourself again!")) +*/ + +/datum/symptom/transformation/proc/do_disease_transformation(mob/living/affected_mob, form) + if(!form) + return + if(affected_mob.stat != DEAD) + if(QDELETED(affected_mob)) + return + if(HAS_TRAIT_FROM(affected_mob, TRAIT_NO_TRANSFORM, REF(src))) + return + ADD_TRAIT(affected_mob, TRAIT_NO_TRANSFORM, REF(src)) + if(iscarbon(affected_mob)) + for(var/obj/item/W in affected_mob.get_equipped_items(include_pockets = TRUE)) + affected_mob.dropItemToGround(W) + for(var/obj/item/I in affected_mob.held_items) + affected_mob.dropItemToGround(I) + var/mob/living/new_mob = new form(affected_mob.loc) + if(istype(new_mob)) + if(bantype && is_banned_from(affected_mob.ckey, bantype)) + replace_banned_player(new_mob, affected_mob) + new_mob.set_combat_mode(TRUE) + if(affected_mob.mind) + affected_mob.mind.transfer_to(new_mob) + else + new_mob.key = affected_mob.key + if(transformed_antag_datum) + new_mob.mind.add_antag_datum(transformed_antag_datum) + new_mob.name = affected_mob.real_name + new_mob.real_name = new_mob.name + qdel(affected_mob) + +/datum/symptom/transformation/proc/replace_banned_player(mob/living/new_mob, mob/living/affected_mob) // This can run well after the mob has been transferred, so need a handle on the new mob to kill it if needed. + set waitfor = FALSE + + var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [affected_mob.real_name]?", bantype, bantype, 5 SECONDS, affected_mob) + if(LAZYLEN(candidates)) + var/mob/dead/observer/C = pick(candidates) + to_chat(affected_mob, span_userdanger("Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")) + message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(affected_mob)]) to replace a jobbanned player.") + affected_mob.ghostize(FALSE) + affected_mob.key = C.key + else + to_chat(new_mob, span_userdanger("Your mob has been claimed by death! Appeal your job ban if you want to avoid this in the future!")) + new_mob.investigate_log("has been killed because there was no one to replace them as a job-banned player.", INVESTIGATE_DEATHS) + new_mob.death() + if (!QDELETED(new_mob)) + new_mob.ghostize(can_reenter_corpse = FALSE) + new_mob.key = null + +/datum/symptom/transformation/robot + name = "Robotic Transformation" + new_form = /mob/living/silicon/robot + bantype = JOB_CYBORG + +/datum/symptom/transformation/xeno + name = "Xenomorph Transformation" + new_form = /mob/living/carbon/alien/adult/hunter + bantype = ROLE_ALIEN + +/datum/symptom/transformation/slime + name = "Advanced Mutation Transformation" + new_form = /mob/living/simple_animal/slime + +/datum/symptom/transformation/corgi + name = "The Barkening" + new_form = /mob/living/basic/pet/dog/corgi + +/datum/symptom/transformation/morph + name = "Gluttony's Blessing" + new_form = /mob/living/basic/morph + transformed_antag_datum = /datum/antagonist/morph + +/datum/symptom/transformation/gondola + name = "Gondola Transformation" + max_chance = 50 + new_form = /mob/living/simple_animal/pet/gondola + +/datum/symptom/transformation/gondola/digital + new_form = /mob/living/simple_animal/pet/gondola/virtual_domain + +/datum/symptom/anxiety + name = "Severe Anxiety" + desc = "Causes the host to suffer from severe anxiety" + stage = 1 + badness = EFFECT_DANGER_ANNOYING + restricted = TRUE + max_multiplier = 4 + +/datum/symptom/anxiety/activate(mob/living/carbon/mob, datum/disease/advanced/disease) + + switch(round(multiplier, 1)) + if(2) //also changes say, see say.dm + if(prob(2.5)) + to_chat(mob, span_notice("You feel anxious.")) + if(3) + if(prob(5)) + to_chat(mob, span_notice("Your stomach flutters.")) + if(prob(2.5)) + to_chat(mob, span_notice("You feel panicky.")) + if(prob(1)) + to_chat(mob, span_danger("You're overtaken with panic!")) + mob.adjust_confusion(rand(2 SECONDS, 3 SECONDS)) + if(4) + if(prob(5)) + to_chat(mob, span_danger("You feel butterflies in your stomach.")) + if(prob(2.5)) + mob.visible_message(span_danger("[mob] stumbles around in a panic."), \ + span_userdanger("You have a panic attack!")) + mob.adjust_confusion(rand(6 SECONDS, 8 SECONDS)) + mob.adjust_jitter(rand(12 SECONDS, 16 SECONDS)) + if(prob(1)) + mob.visible_message(span_danger("[mob] coughs up butterflies!"), \ + span_userdanger("You cough up butterflies!")) + new /mob/living/basic/butterfly(mob.loc) + new /mob/living/basic/butterfly(mob.loc) diff --git a/monkestation/code/modules/virology/disease/symtoms/restricted/stage2.dm b/monkestation/code/modules/virology/disease/symtoms/restricted/stage2.dm new file mode 100644 index 000000000000..0babeaecf5ec --- /dev/null +++ b/monkestation/code/modules/virology/disease/symtoms/restricted/stage2.dm @@ -0,0 +1,197 @@ +/datum/symptom/narcolepsy + name = "Aurora Snorealis" + desc = "The virus causes a hormone imbalance, making the host sleepy and narcoleptic." + stage = 2 + restricted = TRUE + badness = EFFECT_DANGER_ANNOYING + max_multiplier = 5 + var/yawning = FALSE + + +/datum/symptom/narcolepsy/activate(mob/living/carbon/M) + switch(round(multiplier, 1)) + if(1) + if(prob(50)) + to_chat(M, span_warning("You feel tired.")) + if(2) + if(prob(50)) + to_chat(M, span_warning("You feel very tired.")) + if(3) + if(prob(50)) + to_chat(M, span_warning("You try to focus on staying awake.")) + + M.adjust_drowsiness_up_to(2.5 SECONDS, 20 SECONDS) + + if(4) + if(prob(50)) + if(yawning) + to_chat(M, span_warning("You try and fail to suppress a yawn.")) + else + to_chat(M, span_warning("You nod off for a moment.")) //you can't really yawn while nodding off, can you? + + M.adjust_drowsiness_up_to(5 SECONDS, 20 SECONDS) + + if(yawning) + M.emote("yawn") + if(M.check_airborne_sterility()) + return + var/strength = 0 + for (var/datum/disease/advanced/V as anything in M.diseases) + strength += V.infectionchance + strength = round(strength/M.diseases.len) + + var/i = 1 + while (strength > 0 && i < 10) //stronger viruses create more clouds at once, max limit of 10 clouds + new /obj/effect/pathogen_cloud/core(get_turf(src), M, virus_copylist(M.diseases)) + strength -= 30 + i++ + + if(5) + if(prob(50)) + to_chat(M, span_warning("[pick("So tired...","You feel very sleepy.","You have a hard time keeping your eyes open.","You try to stay awake.")]")) + + M.adjust_drowsiness_up_to(10 SECONDS, 20 SECONDS) + + if(yawning) + M.emote("yawn") + if(M.check_airborne_sterility()) + return + var/strength = 0 + for (var/datum/disease/advanced/V as anything in M.diseases) + strength += V.infectionchance + strength = round(strength/M.diseases.len) + + var/i = 1 + while (strength > 0 && i < 10) //stronger viruses create more clouds at once, max limit of 10 clouds + new /obj/effect/pathogen_cloud/core(get_turf(src), M, virus_copylist(M.diseases)) + strength -= 30 + i++ + + +#define STARLIGHT_CAN_HEAL 2 +#define STARLIGHT_CAN_HEAL_WITH_PENALTY 1 +#define STARLIGHT_CANNOT_HEAL 0 +#define STARLIGHT_MAX_RANGE 2 + +/datum/symptom/starlight + name = "Starlight Condensation" + desc = "The virus reacts to direct starlight, producing regenerative chemicals. Works best against toxin-based damage." + max_multiplier = 5 + stage = 2 + restricted = TRUE + badness = EFFECT_DANGER_HELPFUL + + var/list/passive_message = span_notice("You miss the feeling of starlight on your skin.") + var/nearspace_penalty = 0.3 + +/datum/symptom/starlight/activate(mob/living/carbon/mob) + + var/mob/living/M = mob + switch(round(multiplier)) + if(4, 5) + var/effectiveness = CanHeal(mob) + if(!effectiveness) + if(passive_message && prob(2) && passive_message_condition(M)) + to_chat(M, passive_message) + return + else + Heal(M, effectiveness) + return + +/datum/symptom/starlight/proc/CanTileHealDirectional(turf/turf_to_check, direction) + if(direction == UP) + turf_to_check = GET_TURF_ABOVE(turf_to_check) + if(!turf_to_check) + return STARLIGHT_CANNOT_HEAL + var/area/area_to_check = get_area(turf_to_check) + var/levels_of_glass = 0 // Since starlight condensation only works 2 tiles to the side anyways, it shouldn't work with like 100 z-levels of glass + while(levels_of_glass <= STARLIGHT_MAX_RANGE) + // Outdoors covers lavaland and unroofed areas but with tiles under, + // while space covers normal space and those caused by explosions, + // if there is a floor tile when checking above, that means + // a roof exists so the outdoors should only work downwards + if(isspaceturf(turf_to_check) || (area_to_check.outdoors && direction == DOWN)) + if (levels_of_glass) + return STARLIGHT_CAN_HEAL_WITH_PENALTY // Glass gives a penalty. + return STARLIGHT_CAN_HEAL // No glass = can heal fully. + + // Our turf is transparent, but it's NOT openspace - it's something like glass which reduces power + if(istransparentturf(turf_to_check) && !(istype(turf_to_check, /turf/open/openspace))) + levels_of_glass += 1 + + // Our turf is transparent OR openspace - we can check higher or lower z-levels + if(istransparentturf(turf_to_check) || istype(turf_to_check, /turf/open/openspace)) + // Check above or below us + if(direction == UP) + turf_to_check = GET_TURF_ABOVE(turf_to_check) + else + turf_to_check = GET_TURF_BELOW(turf_to_check) + + // If we found a turf above or below us, + // then we can rerun the loop on the newly found turf / area + // (Probably, with +1 to levels_of_glass) + if(turf_to_check) + area_to_check = get_area(turf_to_check) + continue + + // If we didn't find a turf above or below us - + // Checking below, we assume that space is below us (as we're standing on station) + // Checking above, we check that the area is "outdoors" before assuming if it is space or not. + else + if(direction == DOWN || (direction == UP && area_to_check.outdoors)) + if (levels_of_glass) + return STARLIGHT_CAN_HEAL_WITH_PENALTY + return STARLIGHT_CAN_HEAL + + return STARLIGHT_CANNOT_HEAL // Hit a non-space, Non-transparent turf - no healsies + +/datum/symptom/starlight/proc/CanTileHeal(turf/original_turf, satisfied_with_penalty) + var/current_heal_level = CanTileHealDirectional(original_turf, DOWN) + if(current_heal_level == STARLIGHT_CAN_HEAL) + return current_heal_level + if(current_heal_level && satisfied_with_penalty) // do not care if there is a healing penalty or no + return current_heal_level + var/heal_level_from_above = CanTileHealDirectional(original_turf, UP) + if(heal_level_from_above > current_heal_level) + return heal_level_from_above + else + return current_heal_level + +/datum/symptom/starlight/proc/CanHeal(mob/living/carbon/mob) + var/mob/living/affected_mob = mob + var/turf/turf_of_mob = get_turf(affected_mob) + switch(CanTileHeal(turf_of_mob, FALSE)) + if(STARLIGHT_CAN_HEAL_WITH_PENALTY) + return power * nearspace_penalty + if(STARLIGHT_CAN_HEAL) + return power + for(var/turf/turf_to_check in view(affected_mob, STARLIGHT_MAX_RANGE)) + if(CanTileHeal(turf_to_check, TRUE)) + return power * nearspace_penalty + +#undef STARLIGHT_CAN_HEAL +#undef STARLIGHT_CAN_HEAL_WITH_PENALTY +#undef STARLIGHT_CANNOT_HEAL +#undef STARLIGHT_MAX_RANGE + +/datum/symptom/starlight/proc/Heal(mob/living/carbon/M, actual_power) + var/heal_amt = actual_power + if(M.getToxLoss() && prob(5)) + to_chat(M, span_notice("Your skin tingles as the starlight seems to heal you.")) + + M.adjustToxLoss(-(4 * heal_amt)) //most effective on toxins + + var/list/parts = M.get_damaged_bodyparts(1,1, BODYTYPE_ORGANIC) + + if(!parts.len) + return + + for(var/obj/item/bodypart/L in parts) + if(L.heal_damage(heal_amt/parts.len, heal_amt/parts.len, BODYTYPE_ORGANIC)) + M.update_damage_overlays() + return 1 + +/datum/symptom/starlight/proc/passive_message_condition(mob/living/M) + if(M.getBruteLoss() || M.getFireLoss() || M.getToxLoss()) + return TRUE + return FALSE diff --git a/monkestation/code/modules/virology/disease/symtoms/restricted/stage3.dm b/monkestation/code/modules/virology/disease/symtoms/restricted/stage3.dm new file mode 100644 index 000000000000..1fd8e9e54005 --- /dev/null +++ b/monkestation/code/modules/virology/disease/symtoms/restricted/stage3.dm @@ -0,0 +1,71 @@ +/datum/symptom/bad_adrenaline + name = "Bad Adrenaline" + desc = "If left untreated the subject will suffer from lethargy, dizziness and periodic loss of conciousness." + stage = 3 + restricted = TRUE + max_multiplier = 2 + + +/datum/symptom/bad_adrenaline/activate(mob/living/carbon/affected_mob) + switch(round(multiplier)) + if(1) + if(prob(2.5)) + to_chat(affected_mob, span_warning(pick("You feel lightheaded.", "You feel lethargic."))) + if(2) + if(prob(5)) + affected_mob.Unconscious(40) + + if(prob(10)) + affected_mob.adjust_slurring(14 SECONDS) + + if(prob(7)) + affected_mob.set_dizzy_if_lower(20 SECONDS) + + if(prob(2.5)) + to_chat(affected_mob, span_warning(pick("You feel pain shoot down your legs!", "You feel like you are going to pass out at any moment.", "You feel really dizzy."))) + +/datum/symptom/mutation + name = "DNA Degradation" + desc = "Attacks the infected's DNA, causing it to break down." + stage = 3 + badness = EFFECT_DANGER_DEADLY + max_multiplier = 5 + restricted = TRUE + +/datum/symptom/mutation/activate(mob/living/carbon/mob) + switch(round(multiplier, 1)) + if(2) + if(prob(1)) + mob.emote("itch") + if(prob(1)) + mob.emote("yawn") + if(3) + if(prob(1)) + mob.emote("itch") + if(prob(1)) + mob.emote("drool") + if(prob(1.5)) + mob.adjustCloneLoss(1, FALSE) + if(prob(1)) + to_chat(mob, span_danger("Your skin feels strange.")) + + if(4) + if(prob(1)) + mob.emote("itch") + if(prob(1)) + mob.emote("drool") + if(prob(2.5)) + mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1, 170) + mob.adjustCloneLoss(2, FALSE) + if(prob(7.5)) + mob.adjust_stutter(6 SECONDS) + if(5) + if(prob(1)) + mob.emote("itch") + if(prob(1)) + mob.emote("drool") + if(prob(2.5)) + to_chat(mob, span_danger("Your skin starts degrading!")) + if(prob(5)) + mob.adjustCloneLoss(5, FALSE) + mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2, 170) diff --git a/monkestation/code/modules/virology/disease/symtoms/restricted/stage4.dm b/monkestation/code/modules/virology/disease/symtoms/restricted/stage4.dm new file mode 100644 index 000000000000..e4510d59f977 --- /dev/null +++ b/monkestation/code/modules/virology/disease/symtoms/restricted/stage4.dm @@ -0,0 +1,85 @@ +/datum/symptom/heart_failure + name = "Myocardial Infarction" + desc = "If left untreated the subject will die!" + restricted = TRUE + max_multiplier = 5 + var/sound = FALSE + +/datum/symptom/heart_failure/activate(mob/living/carbon/affected_mob) + . = ..() + if(ismouse(affected_mob)) + affected_mob.death() + return FALSE + + if(!affected_mob.can_heartattack()) + affected_mob.death() + return FALSE + + switch(round(multiplier)) + if(1 to 2) + if(prob(1)) + to_chat(affected_mob, span_warning("You feel [pick("discomfort", "pressure", "a burning sensation", "pain")] in your chest.")) + if(prob(1)) + to_chat(affected_mob, span_warning("You feel dizzy.")) + affected_mob.adjust_confusion(6 SECONDS) + if(prob(1.5)) + to_chat(affected_mob, span_warning("You feel [pick("full", "nauseated", "sweaty", "weak", "tired", "short of breath", "uneasy")].")) + if(3 to 4) + if(!sound) + affected_mob.playsound_local(affected_mob, 'sound/health/slowbeat.ogg', 40, FALSE, channel = CHANNEL_HEARTBEAT, use_reverb = FALSE) + sound = TRUE + if(prob(1.5)) + to_chat(affected_mob, span_danger("You feel a sharp pain in your chest!")) + if(prob(25)) + affected_mob.vomit(95) + affected_mob.emote("cough") + affected_mob.Paralyze(40) + affected_mob.losebreath += 4 + if(prob(1.5)) + to_chat(affected_mob, span_danger("You feel very weak and dizzy...")) + affected_mob.adjust_confusion(8 SECONDS) + affected_mob.stamina.adjust(-40, FALSE) + affected_mob.emote("cough") + if(5) + affected_mob.stop_sound_channel(CHANNEL_HEARTBEAT) + affected_mob.playsound_local(affected_mob, 'sound/effects/singlebeat.ogg', 100, FALSE, use_reverb = FALSE) + if(affected_mob.stat == CONSCIOUS) + affected_mob.visible_message(span_danger("[affected_mob] clutches at [affected_mob.p_their()] chest as if [affected_mob.p_their()] heart is stopping!"), \ + span_userdanger("You feel a terrible pain in your chest, as if your heart has stopped!")) + affected_mob.stamina.adjust(-60, FALSE) + affected_mob.set_heartattack(TRUE) + affected_mob.reagents.add_reagent(/datum/reagent/medicine/c2/penthrite, 3) // To give the victim a final chance to shock their heart before losing consciousness + return FALSE + +/datum/symptom/catapult_sneeze + name = "Sneezing?" + desc = "The virus causes irritation of the nasal cavity, making the host sneeze occasionally. Sneezes from this symptom will spread the virus in a 4 meter cone in front of the host." + restricted = TRUE + stage = 4 + max_multiplier = 10 + badness = EFFECT_DANGER_HARMFUL + COOLDOWN_DECLARE(launch_cooldown) + +/datum/symptom/catapult_sneeze/activate(mob/living/mob) + mob.emote("sneeze") + + if(prob(5 * multiplier) && COOLDOWN_FINISHED(src, launch_cooldown)) + to_chat(mob, span_userdanger("You are launched violently backwards by an all-mighty sneeze!")) + var/launch_distance = round(multiplier) + var/turf/target = get_ranged_target_turf(mob, turn(mob.dir, 180), launch_distance) + mob.throw_at(target, launch_distance, rand(3,9)) //with the wounds update, sneezing at 7 speed was causing peoples bones to spontaneously explode, turning cartoonish sneezing into a nightmarishly lethal GBS 2.0 outbreak + COOLDOWN_START(src, launch_cooldown, 10 SECONDS) + + if(ishuman(mob)) + var/mob/living/carbon/human/host = mob + if (prob(50) && isturf(mob.loc)) + if(istype(host.wear_mask, /obj/item/clothing/mask/cigarette)) + var/obj/item/clothing/mask/cigarette/I = host.get_item_by_slot(ITEM_SLOT_MASK) + if(prob(20)) + var/turf/Q = get_turf(mob) + var/turf/endLocation + var/spitForce = pick(0,1,2,3) + endLocation = get_ranged_target_turf(Q, mob.dir, spitForce) + to_chat(mob, "You sneezed \the [host.wear_mask] out of your mouth!") + host.dropItemToGround(I) + I.throw_at(endLocation,spitForce,1) diff --git a/monkestation/code/modules/virology/disease/symtoms/stage1.dm b/monkestation/code/modules/virology/disease/symtoms/stage1.dm new file mode 100644 index 000000000000..0be48bb975da --- /dev/null +++ b/monkestation/code/modules/virology/disease/symtoms/stage1.dm @@ -0,0 +1,332 @@ +/datum/symptom/invisible + name = "Waiting Syndrome" + desc = "A self-defeating symptom that doesn't seem to do anything in particular." + stage = 1 + badness = EFFECT_DANGER_HELPFUL + +/datum/symptom/invisible/activate(mob/living/mob) + return + +/datum/symptom/sneeze + name = "Coldingtons Effect" + desc = "Makes the infected sneeze every so often, leaving some infected mucus on the floor." + stage = 1 + badness = EFFECT_DANGER_ANNOYING + +/datum/symptom/sneeze/activate(mob/living/mob) + mob.emote("sneeze") + if(!ishuman(mob)) + return + var/mob/living/carbon/human/host = mob + if (prob(50) && isturf(mob.loc)) + if(istype(host.wear_mask, /obj/item/clothing/mask/cigarette)) + var/obj/item/clothing/mask/cigarette/I = host.get_item_by_slot(ITEM_SLOT_MASK) + if(prob(20)) + var/turf/Q = get_turf(mob) + var/turf/endLocation + var/spitForce = pick(0,1,2,3) + endLocation = get_ranged_target_turf(Q, mob.dir, spitForce) + to_chat(mob, "You sneezed \the [host.wear_mask] out of your mouth!") + host.dropItemToGround(I) + I.throw_at(endLocation,spitForce,1) + +/datum/symptom/gunck + name = "Flemmingtons" + desc = "Causes a sensation of mucous running down the infected's throat." + stage = 1 + badness = EFFECT_DANGER_FLAVOR + +/datum/symptom/gunck/activate(mob/living/mob) + to_chat(mob, " Mucus runs down the back of your throat.") + +/datum/symptom/drool + name = "Saliva Effect" + desc = "Causes the infected to drool." + stage = 1 + badness = EFFECT_DANGER_FLAVOR + +/datum/symptom/drool/activate(mob/living/mob) + mob.emote("drool") + + +/datum/symptom/twitch + name = "Twitcher" + desc = "Causes the infected to twitch." + stage = 1 + badness = EFFECT_DANGER_FLAVOR + +/datum/symptom/twitch/activate(mob/living/mob) + mob.emote("twitch") + +/datum/symptom/headache + name = "Headache" + desc = "Gives the infected a light headache." + stage = 1 + badness = EFFECT_DANGER_FLAVOR + +/datum/symptom/headache/activate(mob/living/mob) + to_chat(mob, "Your head hurts a bit.") + +/datum/symptom/drained + name = "Drained Feeling" + desc = "Gives the infected a drained sensation." + stage = 1 + badness = EFFECT_DANGER_FLAVOR + +/datum/symptom/drained/activate(mob/living/mob) + to_chat(mob, span_warning("You feel drained.")) + + +/datum/symptom/eyewater + name = "Watery Eyes" + desc = "Causes the infected's tear ducts to overact." + stage = 1 + badness = EFFECT_DANGER_FLAVOR + +/datum/symptom/eyewater/activate(mob/living/mob) + to_chat(mob, span_warning("Your eyes sting and water!")) + + +/datum/symptom/wheeze + name = "Wheezing" + desc = "Inhibits the infected's ability to breathe slightly, causing them to wheeze." + stage = 1 + badness = EFFECT_DANGER_FLAVOR + +/datum/symptom/wheeze/activate(mob/living/mob) + mob.emote("me",1,"wheezes.") + +/datum/symptom/bee_vomit + name = "Melisso-Emeto Syndrome" + desc = "Converts the lungs of the infected into a bee-hive." + encyclopedia = "Giving the infected a steady drip of honey in exchange of coughing up a bee every so often. The higher the symptom strength, the more honey is generated, and the more bees will be coughed up and more often as well. While Honey is a great healing reagent, it is also high on nutrients. Expect to become fat quickly.." + stage = 1 + badness = EFFECT_DANGER_ANNOYING + max_multiplier = 10 + +/datum/symptom/bee_vomit/activate(mob/living/mob) + if(!ismouse(mob)) + if ((mob.reagents.get_reagent_amount(/datum/reagent/consumable/honey) < 5 + multiplier * 0.5) && prob(multiplier * 3)) + mob.reagents.add_reagent(/datum/reagent/consumable/honey, multiplier) + + if(prob(4*multiplier)) + to_chat(mob, span_warning("You feel a buzzing in your throat")) + + spawn(5 SECONDS) + var/turf/open/T = get_turf(mob) + if(prob(50)) + mob.visible_message(span_warning("[mob] coughs out a bee!"),span_danger("You cough up a bee!")) + for(var/i = 0 to multiplier) + var/bee_type = pick( + 100;/mob/living/basic/bee/friendly, + 10;/mob/living/basic/bee, + 5;/mob/living/basic/bee/toxin, + ) + var/mob/living/basic/bee/bee = new bee_type(T) + if(multiplier < 8) + addtimer(CALLBACK(src, PROC_REF(kill_bee), bee), 15 SECONDS * multiplier) + +/datum/symptom/bee_vomit/proc/kill_bee(mob/living/basic/bee/bee) + bee.visible_message(span_warning("The bee falls apart!"), span_warning("You fall apart")) + bee.death() + +/datum/symptom/soreness + name = "Myalgia Syndrome" + desc = "Makes the infected more perceptive of their aches and pains." + stage = 1 + chance = 5 + max_chance = 30 + badness = EFFECT_DANGER_FLAVOR + +/datum/symptom/soreness/activate(mob/living/mob) + to_chat(mob, span_notice("You feel a little sore.")) + if(iscarbon(mob)) + var/mob/living/carbon/host = mob + host.stamina.adjust(-10) + +/datum/symptom/wendigo_warning + name = "Fullness Syndrome" + desc = "An unsual symptom that causes the infected to feel hungry, even after eating." + stage = 1 + badness = EFFECT_DANGER_ANNOYING + var/list/host_messages = list( + "Your stomach grumbles.", + "You feel peckish.", + "So hungry...", + "Your stomach feels empty.", + "Hunger...", + "Who are we...?", + "Our mind hurts...", + "You feel... different...", + "There's something wrong." + ) + +/datum/symptom/wendigo_warning/activate(mob/living/mob) + to_chat(mob, span_warning("[pick(host_messages)]")) + mob.adjust_nutrition(-25) + + +/datum/symptom/cult_hallucination + name = "Visions of the End-Times" + desc = "UNKNOWN" + stage = 1 + badness = EFFECT_DANGER_ANNOYING + max_multiplier = 2.5 + var/list/rune_words_rune = list("ire","ego","nahlizet","certum","veri","jatkaa","mgar","balaq", "karazet", "geeri") + +/datum/symptom/cult_hallucination/activate(mob/living/mob) + if(IS_CULTIST(mob)) + return + if(istype(get_area(mob), /area/station/service/chapel)) + return + var/client/C = mob.client + if(!C) + return + mob.whisper("...[pick(rune_words_rune)]...") + + var/list/turf_list = list() + for(var/turf/T in spiral_block(get_turf(mob), 40)) + if(locate(/obj/structure/grille) in T.contents) + continue + if(istype(get_area(T), /area/station/service/chapel)) + continue + if(prob(2*multiplier)) + turf_list += T + if(turf_list.len) + for(var/turf/open/T in turf_list) + var/delay = rand(0, 50) // so the runes don't all appear at once + spawn(delay) + + var/runenum = rand(1,2) + var/image/rune_holder = image('monkestation/code/modules/virology/icons/deityrunes.dmi',T,"") + var/image/rune_render = image('monkestation/code/modules/virology/icons/deityrunes.dmi',T,"fullrune-[runenum]") + rune_render.color = LIGHT_COLOR_BLOOD_MAGIC + + C.images += rune_holder + + // anim(target = T, a_icon = 'monkestation/code/modules/virology/icons/deityrunes.dmi', flick_anim = "fullrune-[runenum]-write", col = DEFAULT_BLOOD, sleeptime = 36) + + spawn(30) + + rune_render.icon_state = "fullrune-[runenum]" + rune_holder.overlays += rune_render + AnimateFakeRune(rune_holder) + + var/duration = rand(20 SECONDS, 40 SECONDS) + spawn(duration) + if(C) + rune_holder.overlays -= rune_render + // anim(target = T, a_icon = 'icons/effects/deityrunes.dmi', flick_anim = "fullrune-[runenum]-erase", col = DEFAULT_BLOOD) + spawn(12) + C.images -= rune_holder + + +/datum/symptom/cult_hallucination/proc/AnimateFakeRune(var/image/rune) + animate(rune, color = list(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0), time = 10, loop = -1)//1 + animate(color = list(1.125,0.06,0,0,0,1.125,0.06,0,0.06,0,1.125,0,0,0,0,1,0,0,0,0), time = 2)//2 + animate(color = list(1.25,0.12,0,0,0,1.25,0.12,0,0.12,0,1.25,0,0,0,0,1,0,0,0,0), time = 2)//3 + animate(color = list(1.375,0.19,0,0,0,1.375,0.19,0,0.19,0,1.375,0,0,0,0,1,0,0,0,0), time = 1.5)//4 + animate(color = list(1.5,0.27,0,0,0,1.5,0.27,0,0.27,0,1.5,0,0,0,0,1,0,0,0,0), time = 1.5)//5 + animate(color = list(1.625,0.35,0.06,0,0.06,1.625,0.35,0,0.35,0.06,1.625,0,0,0,0,1,0,0,0,0), time = 1)//6 + animate(color = list(1.75,0.45,0.12,0,0.12,1.75,0.45,0,0.45,0.12,1.75,0,0,0,0,1,0,0,0,0), time = 1)//7 + animate(color = list(1.875,0.56,0.19,0,0.19,1.875,0.56,0,0.56,0.19,1.875,0,0,0,0,1,0,0,0,0), time = 1)//8 + animate(color = list(2,0.67,0.27,0,0.27,2,0.67,0,0.67,0.27,2,0,0,0,0,1,0,0,0,0), time = 5)//9 + animate(color = list(1.875,0.56,0.19,0,0.19,1.875,0.56,0,0.56,0.19,1.875,0,0,0,0,1,0,0,0,0), time = 1)//8 + animate(color = list(1.75,0.45,0.12,0,0.12,1.75,0.45,0,0.45,0.12,1.75,0,0,0,0,1,0,0,0,0), time = 1)//7 + animate(color = list(1.625,0.35,0.06,0,0.06,1.625,0.35,0,0.35,0.06,1.625,0,0,0,0,1,0,0,0,0), time = 1)//6 + animate(color = list(1.5,0.27,0,0,0,1.5,0.27,0,0.27,0,1.5,0,0,0,0,1,0,0,0,0), time = 1)//5 + animate(color = list(1.375,0.19,0,0,0,1.375,0.19,0,0.19,0,1.375,0,0,0,0,1,0,0,0,0), time = 1)//4 + animate(color = list(1.25,0.12,0,0,0,1.25,0.12,0,0.12,0,1.25,0,0,0,0,1,0,0,0,0), time = 1)//3 + animate(color = list(1.125,0.06,0,0,0,1.125,0.06,0,0.06,0,1.125,0,0,0,0,1,0,0,0,0), time = 1)//2 + +/proc/spiral_block(turf/epicenter, range, draw_red=FALSE) + if(!epicenter) + return list() + + if(!range) + return list(epicenter) + + . = list() + + var/turf/T + var/y + var/x + var/c_dist = 1 + . += epicenter + + while( c_dist <= range ) + y = epicenter.y + c_dist + x = epicenter.x - c_dist + 1 + //bottom + for(x in x to epicenter.x+c_dist) + T = locate(x,y,epicenter.z) + if(T) + . += T + if(draw_red) + T.color = "red" + sleep(5) + + y = epicenter.y + c_dist - 1 + x = epicenter.x + c_dist + for(y in y to epicenter.y-c_dist step -1) + T = locate(x,y,epicenter.z) + if(T) + . += T + if(draw_red) + T.color = "red" + sleep(5) + + y = epicenter.y - c_dist + x = epicenter.x + c_dist - 1 + for(x in x to epicenter.x-c_dist step -1) + T = locate(x,y,epicenter.z) + if(T) + . += T + if(draw_red) + T.color = "red" + sleep(5) + + y = epicenter.y - c_dist + 1 + x = epicenter.x - c_dist + for(y in y to epicenter.y+c_dist) + T = locate(x,y,epicenter.z) + if(T) + . += T + if(draw_red) + T.color = "red" + sleep(5) + c_dist++ + + if(draw_red) + sleep(30) + for(var/turf/Q in .) + Q.color = null + +/datum/symptom/itching + name = "Itching" + desc = "Makes you Itch!" + stage = 1 + badness = EFFECT_DANGER_ANNOYING + var/scratch = FALSE + ///emote cooldowns + COOLDOWN_DECLARE(itching_cooldown) + ///if FALSE, there is a percentage chance that the mob will emote scratching while itching_cooldown is on cooldown. If TRUE, won't emote again until after the off cooldown scratch occurs. + var/off_cooldown_scratched = FALSE + +/datum/symptom/itching/activate(mob/living/mob) + if(!iscarbon(mob)) + return + var/mob/living/carbon/affected_mob = mob + var/obj/item/bodypart/bodypart = affected_mob.get_bodypart(affected_mob.get_random_valid_zone(even_weights = TRUE)) + if(bodypart && IS_ORGANIC_LIMB(bodypart) && !(bodypart.bodypart_flags & BODYPART_PSEUDOPART)) //robotic limbs will mean less scratching overall (why are golems able to damage themselves with self-scratching, but not androids? the world may never know) + var/can_scratch = scratch && !affected_mob.incapacitated() + if(can_scratch) + bodypart.receive_damage(0.5) + //below handles emotes, limiting the emote of emotes passed to chat + if(COOLDOWN_FINISHED(src, itching_cooldown) || !COOLDOWN_FINISHED(src, itching_cooldown) && prob(60) && !off_cooldown_scratched) + affected_mob.visible_message("[can_scratch ? span_warning("[affected_mob] scratches [affected_mob.p_their()] [bodypart.plaintext_zone].") : ""]", span_warning("Your [bodypart.plaintext_zone] itches. [can_scratch ? " You scratch it." : ""]")) + COOLDOWN_START(src, itching_cooldown, 5 SECONDS) + if(!off_cooldown_scratched && !COOLDOWN_FINISHED(src, itching_cooldown)) + off_cooldown_scratched = TRUE + else + off_cooldown_scratched = FALSE diff --git a/monkestation/code/modules/virology/disease/symtoms/stage2.dm b/monkestation/code/modules/virology/disease/symtoms/stage2.dm new file mode 100644 index 000000000000..7661c3d68871 --- /dev/null +++ b/monkestation/code/modules/virology/disease/symtoms/stage2.dm @@ -0,0 +1,608 @@ + + +/datum/symptom/cough + max_chance = 10 + stage = 2 + badness = EFFECT_DANGER_ANNOYING + +/datum/symptom/cough/activate(mob/living/carbon/mob) + mob.emote("cough") + + var/datum/gas_mixture/breath + if (ishuman(mob)) + var/mob/living/carbon/human/H = mob + breath = H.get_breath_from_internal(BREATH_VOLUME) + if(!breath)//not wearing internals + var/head_block = 0 + if (ishuman(mob)) + var/mob/living/carbon/human/H = mob + if (H.head && (H.head.flags_cover & HEADCOVERSMOUTH)) + head_block = 1 + if(!head_block) + if(!mob.wear_mask || !(mob.wear_mask.flags_cover & MASKCOVERSMOUTH)) + if(isturf(mob.loc)) + if(mob.check_airborne_sterility()) + return + var/strength = 0 + for (var/datum/disease/advanced/V in mob.diseases) + strength += V.infectionchance + strength = round(strength / mob.diseases.len) + var/i = 1 + while (strength > 0 && i < 10) //stronger viruses create more clouds at once, max limit of 10 clouds + new /obj/effect/pathogen_cloud/core(get_turf(src), mob, virus_copylist(mob.diseases)) + strength -= 30 + i++ + +/datum/symptom/beard + name = "Facial Hypertrichosis" + desc = "Causes the infected to spontaneously grow a beard, regardless of gender. Only affects humans." + stage = 2 + max_multiplier = 5 + badness = EFFECT_DANGER_FLAVOR + + +/datum/symptom/beard/activate(mob/living/mob) + if(istype(mob, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = mob + if(ishuman(mob)) + var/beard_name = "" + spawn(5 SECONDS) + if(multiplier >= 1 && multiplier < 2) + beard_name = "Beard (Jensen)" + if(multiplier >= 2 && multiplier < 3) + beard_name = "Beard (Full)" + if(multiplier >= 3 && multiplier < 4) + beard_name = "Beard (Very Long)" + if(multiplier >= 4) + beard_name = "Beard (Dwarf)" + if(beard_name != "" && H.facial_hairstyle != beard_name) + H.facial_hairstyle = beard_name + to_chat(H, span_warning("Your chin itches.")) + H.update_body_parts() + +/datum/symptom/drowsness + name = "Automated Sleeping Syndrome" + desc = "Makes the infected feel more drowsy." + stage = 2 + badness = EFFECT_DANGER_HINDRANCE + multiplier = 5 + max_multiplier = 10 + +/datum/symptom/drowsness/activate(mob/living/mob) + mob.adjust_drowsiness_up_to(multiplier, 40 SECONDS) + +/datum/symptom/cough//creates pathogenic clouds that may contain even non-airborne viruses. + name = "Anima Syndrome" + desc = "Causes the infected to cough rapidly, releasing pathogenic clouds." + stage = 2 + badness = EFFECT_DANGER_ANNOYING + max_chance = 10 + +/datum/symptom/cough/activate(var/mob/living/mob) + mob.emote("cough") + if(!ishuman(mob)) + return + var/mob/living/carbon/human/H = mob + var/datum/gas_mixture/breath + breath = H.get_breath_from_internal(BREATH_VOLUME) + if(!breath)//not wearing internals + if(!H.wear_mask) + if(isturf(mob.loc)) + var/list/blockers = list() + blockers = list(H.wear_mask,H.glasses,H.head) + for (var/item in blockers) + var/obj/item/clothing/I = item + if (!istype(I)) + continue + if (I.clothing_flags & BLOCK_GAS_SMOKE_EFFECT) + return + if(mob.check_airborne_sterility()) + return + var/strength = 0 + for (var/datum/disease/advanced/V as anything in mob.diseases) + strength += V.infectionchance + strength = round(strength/mob.diseases.len) + + var/i = 1 + while (strength > 0 && i < 10) //stronger viruses create more clouds at once, max limit of 10 clouds + new /obj/effect/pathogen_cloud/core(get_turf(src), mob, virus_copylist(mob.diseases)) + strength -= 30 + i++ + +/datum/symptom/hungry + name = "Appetiser Effect" + desc = "Starves the infected." + stage = 2 + badness = EFFECT_DANGER_ANNOYING + multiplier = 10 + max_multiplier = 20 + +/datum/symptom/hungry/activate(mob/living/mob) + mob.nutrition = max(0, mob.nutrition - 20*multiplier) + +/datum/symptom/fridge + name = "Refridgerator Syndrome" + desc = "Causes the infected to shiver at random." + encyclopedia = "No matter whether the room is cold or hot. This has no effect on their body temperature." + stage = 2 + max_multiplier = 4 + multiplier = 1 + badness = EFFECT_DANGER_FLAVOR + +/datum/symptom/fridge/activate(mob/living/mob) + to_chat(mob, span_warning("[pick("You feel cold.", "You shiver.")]")) + mob.emote("shiver") + set_body_temp(mob) + +/datum/symptom/fridge/proc/set_body_temp(mob/living/M) + if(multiplier >= 3) // when unsafe the shivers can cause cold damage + M.add_body_temperature_change("chills", -6 * power * multiplier) + else + // Get the max amount of change allowed before going under cold damage limit, then cap the maximum allowed temperature change from safe chills to 5 over the cold damage limit + var/change_limit = min(M.get_body_temp_cold_damage_limit() + 5 - M.get_body_temp_normal(apply_change=FALSE), 0) + M.add_body_temperature_change("chills", max(-6 * power * multiplier, change_limit)) + +/datum/symptom/fridge/deactivate(mob/living/carbon/mob) + if(mob) + mob.remove_body_temperature_change("chills") + +/datum/symptom/hair + name = "Hair Loss" + desc = "Causes rapid hairloss in the infected." + stage = 2 + badness = EFFECT_DANGER_FLAVOR + multiplier = 1 + max_multiplier = 5 + +/datum/symptom/hair/activate(mob/living/mob) + if(ishuman(mob)) + var/mob/living/carbon/human/H = mob + if(H.hairstyle != "Bald") + if (H.hairstyle != "Balding Hair") + to_chat(H, span_danger("Your hair starts to fall out in clumps...")) + if (prob(multiplier*20)) + H.hairstyle = "Balding Hair" + H.update_body_parts() + else + to_chat(H, span_danger("You have almost no hair left...")) + if (prob(multiplier*20)) + H.hairstyle = "Bald" + H.update_body_parts() + +/datum/symptom/stimulant + name = "Adrenaline Extra" + desc = "Causes the infected to synthesize artificial adrenaline." + stage = 2 + badness = EFFECT_DANGER_HELPFUL + max_multiplier = 20 + +/datum/symptom/stimulant/activate(mob/living/mob) + to_chat(mob, span_notice("You feel a rush of energy inside you!")) + if(ismouse(mob)) + mob.Shake(3,3, 10 SECONDS) + return + if (mob.reagents.get_reagent_amount(/datum/reagent/adrenaline) < 10) + if(prob(5 * multiplier) && multiplier >= 8) + mob.reagents.add_reagent(/datum/reagent/adrenaline, 11) //you are gonna probably die + else + mob.reagents.add_reagent(/datum/reagent/adrenaline, 4) + if (prob(30)) + mob.adjust_jitter_up_to(1 SECONDS, 30 SECONDS) + +/datum/symptom/drunk + name = "Vermouth Syndrome" + desc = "Causes the infected to synthesize pure ethanol." + stage = 2 + badness = EFFECT_DANGER_HARMFUL + multiplier = 3 + max_multiplier = 7 + +/datum/symptom/drunk/activate(mob/living/mob) + if(ismouse(mob)) + return + to_chat(mob, span_notice("You feel like you had one hell of a party!")) + if (mob.reagents.get_reagent_amount(/datum/reagent/consumable/ethanol/vermouth) < multiplier*5) + mob.reagents.add_reagent(/datum/reagent/consumable/ethanol/vermouth, multiplier*5) + + +/datum/symptom/bloodynose + name = "Intranasal Hemorrhage" + desc = "Causes the infected's nasal pathways to hemorrhage, causing a nosebleed, potentially carrying the pathogen." + stage = 2 + badness = EFFECT_DANGER_ANNOYING + +/datum/symptom/bloodynose/activate(mob/living/mob) + if (prob(30)) + if (ishuman(mob)) + var/mob/living/carbon/human/H = mob + if (!(TRAIT_NOBLOOD in H.dna.species.inherent_traits)) + H.add_splatter_floor(get_turf(mob), 1) + else + var/obj/effect/decal/cleanable/blood/D= locate(/obj/effect/decal/cleanable/blood) in get_turf(mob) + if(D==null) + D = new /obj/effect/decal/cleanable/blood(get_turf(mob)) + D.diseases |= virus_copylist(mob.diseases) + + +/datum/symptom/lantern + name = "Lantern Syndrome" + desc = "Causes the infected to glow." + stage = 2 + badness = EFFECT_DANGER_HELPFUL + multiplier = 4 + max_multiplier = 10 + var/uncolored = 0 + var/flavortext = 0 + var/color = rgb(255, 255, 255) + +/datum/symptom/lantern/activate(mob/living/mob) + if(ismouse(mob)) + mob.set_light(multiplier, multiplier/3, l_color = color) + return + if(mob.reagents.has_reagent(/datum/reagent/space_cleaner)) + uncolored = 1 //Having spacecleaner in your system when the effect activates will permanently make the color white. + if(mob.reagents.reagent_list.len == 0 || uncolored == TRUE) + color = rgb(255, 255, 255) + else + color = mix_color_from_reagents(mob.reagents.reagent_list) + if(!flavortext) + to_chat(mob, span_notice("You are glowing!")) + flavortext = 1 + mob.set_light(multiplier, multiplier, multiplier/3, l_color = color) + +/datum/symptom/lantern/deactivate(mob/living/mob) + mob.set_light(0, 0, 0, l_color = rgb(0,0,0)) + to_chat(mob, span_notice("You don't feel as bright.")) + flavortext = 0 + +/datum/symptom/vitreous + name = "Vitreous resonance" + desc = "Causes the infected to shake uncontrollably, at the same frequency that is required to break glass." + stage = 2 + chance = 25 + max_chance = 75 + max_multiplier = 2 + badness = EFFECT_DANGER_ANNOYING + +/datum/symptom/vitreous/activate(mob/living/carbon/human/H) + H.Shake(3, 3, 3 SECONDS) + if(ishuman(H)) + addtimer(CALLBACK(src, PROC_REF(shatter), H), 0.5 SECONDS) + +/datum/symptom/vitreous/proc/shatter(mob/living/carbon/human/H) + var/obj/item/reagent_containers/glass_to_shatter = H.get_active_held_item() + var/obj/item/bodypart/check_arm = H.get_active_hand() + if(!glass_to_shatter) + return + if (is_type_in_list(glass_to_shatter, list(/obj/item/reagent_containers/cup/glass))) + to_chat(H, span_warning("Your [check_arm] resonates with the glass in \the [glass_to_shatter], shattering it to bits!")) + glass_to_shatter.reagents.expose(H, TOUCH) + new/obj/effect/decal/cleanable/generic(get_turf(H)) + playsound(H, 'sound/effects/glassbr1.ogg', 25, 1) + spawn(1 SECONDS) + if (H && check_arm) + if (prob(50 * multiplier)) + to_chat(H, span_notice("Your [check_arm] deresonates, healing completely!")) + check_arm.heal_damage(1000) // full heal + else + to_chat(H, span_warning("Your [check_arm] deresonates, sustaining burns!")) + check_arm.take_damage(15 * multiplier, BRUTE) + qdel(glass_to_shatter) + else if (prob(1)) + to_chat(H, span_notice("Your [check_arm] aches for the cold, smooth feel of container-grade glass...")) + +/datum/symptom/spiky_skin + name = "Porokeratosis Acanthus" + desc = "Causes the infected to generate keratin spines along their skin." + stage = 2 + max_count = 1 + badness = EFFECT_DANGER_HINDRANCE + var/skip = FALSE + multiplier = 4 + max_multiplier = 8 + +/datum/symptom/spiky_skin/activate(mob/living/mob, multiplier) + to_chat(mob, span_warning("Your skin feels a little prickly.")) + +/datum/symptom/spiky_skin/deactivate(mob/living/mob) + if(!skip) + to_chat(mob, span_notice("Your skin feels nice and smooth again!")) + ..() + +/datum/symptom/spiky_skin/on_touch(mob/living/mob, mob/living/toucher, mob/living/touched, touch_type) + if(!count || skip) + return + if(!istype(toucher) || !istype(touched)) + return + var/obj/item/bodypart/E + var/mob/living/carbon/human/H + if(toucher == mob) //we bumped into someone else + if(ishuman(touched)) + H = touched + E = H.get_bodypart(H.get_random_valid_zone()) + else //someone else bumped into us + if(ishuman(toucher)) + H = toucher + E = H.get_bodypart(H.get_random_valid_zone()) + + if(toucher == mob) + if(E) + to_chat(mob, span_warning("As you bump into \the [touched], your spines dig into \his [E]!")) + E.take_damage(multiplier, BRUTE) + else + to_chat(mob, span_warning("As you bump into \the [touched], your spines dig into \him!")) + var/mob/living/L = touched + if(istype(L) && !istype(L, /mob/living/silicon)) + L.apply_damage(multiplier, BRUTE, E) + var/mob/M = touched + log_attack("[M] damaged [H] with keratin spikes") + else + if(E) + to_chat(mob, span_warning("As \the [toucher] [touch_type == DISEASE_BUMP ? "bumps into" : "touches"] you, your spines dig into \his [E]!")) + to_chat(toucher, span_danger("As you [touch_type == DISEASE_BUMP ? "bump into" : "touch"] \the [mob], \his spines dig into your [E]!")) + E.take_damage(multiplier) + else + to_chat(mob, span_warning("As \the [toucher] [touch_type == DISEASE_BUMP ? "bumps into" : "touches"] you, your spines dig into \him!")) + to_chat(toucher, span_danger("As you [touch_type == DISEASE_BUMP ? "bump into" : "touch"] \the [mob], \his spines dig into you!")) + var/mob/living/L = toucher + if(istype(L) && !istype(L, /mob/living/silicon)) + L.apply_damage(multiplier) + var/mob/M = touched + log_attack("[M] damaged [H] with keratin spikes") + +/* TODO LATER + +/datum/symptom/calorieburn + name = "Caloric expenditure overefficiency" + desc = "Causes the infected to burn calories at a higher rate." + encyclopedia = "Higher Strength means accelerated metabolism." + stage = 2 + multiplier = 1.5 + max_multiplier = 4 + max_count = 1 + badness = EFFECT_DANGER_HINDRANCE + +/datum/symptom/calorieburn/activate(var/mob/living/mob) + if(ishuman(mob)) + var/mob/living/carbon/human/H = mob + H.calorie_burn_rate *= multiplier + +/datum/symptom/calorieburn/deactivate(var/mob/living/mob) + if (count) + if(ishuman(mob)) + var/mob/living/carbon/human/H = mob + H.calorie_burn_rate /= multiplier + +/datum/symptom/calorieconserve + name = "Caloric expenditure defficiency" + desc = "Causes the infected to burn calories at a lower rate." + encyclopedia = "Higher Strength means decelerated metabolism." + stage = 2 + multiplier = 1.5 + max_multiplier = 4 + max_count = 1 + badness = EFFECT_DANGER_HINDRANCE + +/datum/symptom/calorieconserve/activate(var/mob/living/mob) + if(ishuman(mob)) + var/mob/living/carbon/human/H = mob + H.calorie_burn_rate /= multiplier + +/datum/symptom/calorieconserve/deactivate(var/mob/living/mob) + if(count) + if(ishuman(mob)) + var/mob/living/carbon/human/H = mob + H.calorie_burn_rate *= multiplier +*/ + +/datum/symptom/famine + name = "Faminous Potation" + desc = "The infected emanates a field that kills off plantlife. Lethal to species descended from plants." + stage = 2 + max_multiplier = 3 + badness = EFFECT_DANGER_HINDRANCE + +/datum/symptom/famine/activate(var/mob/living/mob) + if(ishuman(mob)) + var/mob/living/carbon/human/H = mob + if(H.dna) + if(ispodperson(H)) //Plantmen take a LOT of damage + H.adjustCloneLoss(5 * multiplier) + + for(var/obj/machinery/hydroponics/H in range(3*multiplier,mob)) + switch(rand(1,3)) + if(1) + H.adjust_waterlevel(-rand(1,10)) + H.adjust_plant_nutriments(-rand(1,5)) + if(2) + H.adjust_toxic(rand(1,50)) + if(3) + H.adjust_weedlevel(10) + H.adjust_pestlevel(10) + if(prob(5)) + H.plantdies() + + + for(var/obj/item/food/grown/G in range(2*multiplier,mob)) + G.visible_message("\The [G] rots at an alarming rate!") + new /obj/item/food/badrecipe(get_turf(G)) + qdel(G) + if(prob(30/multiplier)) + break +/datum/symptom/cyborg_vomit + name = "Oleum Syndrome" + desc = "Causes the infected to internally synthesize oil and other inorganic material." + stage = 2 + badness = EFFECT_DANGER_ANNOYING + +/datum/symptom/cyborg_vomit/activate(mob/living/mob) + if(prob(90)) //90% chance for just oil + mob.visible_message(span_danger("[mob.name] vomits up some oil!")) + mob.adjustToxLoss(-3) + var/obj/effect/decal/cleanable/oil/O = new /obj/effect/decal/cleanable/oil(get_turf(mob)) + playsound(O, 'sound/effects/splat.ogg', 50, 1) + mob.Stun(0.5 SECONDS) + else //10% chance for a random bot! + to_chat(mob, span_danger("You feel like something's about to burst out of you!")) + sleep(100) + var/list/possible_bots = list( + /mob/living/simple_animal/bot/cleanbot, + /mob/living/simple_animal/bot/medbot, + /mob/living/simple_animal/bot/secbot, + /mob/living/simple_animal/bot/floorbot, + /mob/living/simple_animal/bot/buttbot + ) + var/chosen_bot = pick(possible_bots) + var/mob/living/simple_animal/bot/B = new chosen_bot(get_turf(mob)) + new /obj/effect/decal/cleanable/blood(get_turf(mob)) + mob.visible_message("A [B.name] bursts out of [mob.name]'s mouth!") + playsound(B, 'sound/effects/splat.ogg', 50, 1) + mob.emote("scream") + mob.adjustBruteLoss(15) + mob.Stun(1 SECONDS) + + +/datum/symptom/mommi_shrink + name = "Dysplasia Syndrome" + desc = "Rapidly restructures the body of the infected, causing them to shrink in size." + badness = EFFECT_DANGER_FLAVOR + stage = 2 + var/activated = 0 + +/datum/symptom/mommi_shrink/activate(var/mob/living/mob) + if(activated) + return + to_chat(mob, "You feel small...") + mob.transform.Scale(0.5, 0.5) + mob.update_transform() + mob.pass_flags |= PASSTABLE + + activated = 1 + +/datum/symptom/mommi_shrink/deactivate(mob/living/mob) + to_chat(mob, "You feel like an adult again.") + mob.transform.Scale(2, 2) + mob.update_transform() + mob.pass_flags &= ~PASSTABLE + activated = 0 + +/datum/symptom/wendigo_vomit + name = "Gastrointestinal Inflammation" + desc = "Inflames the GI tract of the infected, causing relentless vomitting." + stage = 2 + badness = EFFECT_DANGER_HINDRANCE + chance = 6 + max_chance = 12 + +/datum/symptom/wendigo_vomit/activate(mob/living/mob) + if(!ishuman(mob)) + return + + var/mob/living/carbon/human/H = mob + H.vomit(stun = FALSE) + +/datum/symptom/antitox + name = "Antioxidantisation Syndrome" + desc = "A very real syndrome beloved by Super-Food Fans and Essential Oil Enthusiasts; encourages the production of anti-toxin within the body." + stage = 2 + badness = EFFECT_DANGER_HELPFUL + +/datum/symptom/antitox/activate(mob/living/mob) + to_chat(mob, "You feel your toxins being purged!") + mob.adjustToxLoss(-4) + +/datum/symptom/cult_vomit + name = "Hemoptysis" + desc = "Causes the infected to cough up blood." + stage = 2 + badness = EFFECT_DANGER_HINDRANCE + var/active = 0 + +/datum/symptom/cult_vomit/activate(mob/living/carbon/M) + if(!ishuman(M) || active) + return + if(istype(get_area(M), /area/station/service/chapel)) + return + if(IS_CULTIST(M)) + return + + var/mob/living/carbon/human/mob = M + active = 1 + to_chat(mob, span_warning("You feel a burning sensation in your throat.")) + sleep(10 SECONDS) + to_chat(mob, span_danger("You feel an agonizing pain in your throat!")) + sleep(10 SECONDS) + mob.visible_message(span_danger("[mob] vomits up blood!"), span_danger("You vomit up blood!")) + var/obj/effect/decal/cleanable/blood/S = new(loc = get_turf(mob)) + S.count = 1 + playsound(mob, 'sound/effects/splat.ogg', 50, 1) + mob.Stun(5) + mob.blood_volume -= 8 + active = 0 + +/datum/symptom/choking + name = "Choking" + desc = "The virus causes inflammation of the host's air conduits, leading to intermittent choking." + max_multiplier = 10 + multiplier = 1 + badness = EFFECT_DANGER_HINDRANCE + max_chance = 20 + stage = 2 + +/datum/symptom/choking/activate(mob/living/carbon/mob) + mob.emote("gasp") + if(prob(25)) + to_chat(mob, span_warning("[pick("You're having difficulty breathing.", "Your breathing becomes heavy.")]")) + mob.adjustOxyLoss(rand(2, 3) * multiplier) + +/datum/symptom/disfiguration + name = "Disfiguration" + desc = "The virus liquefies facial muscles, disfiguring the host." + max_count = 1 + +/datum/symptom/disfiguration/activate(mob/living/carbon/mob) + ADD_TRAIT(mob, TRAIT_DISFIGURED, DISEASE_TRAIT) + mob.visible_message(span_warning("[mob]'s face appears to cave in!"), span_notice("You feel your face crumple and cave in!")) + +/datum/symptom/disfiguration/deactivate(mob/living/carbon/mob) + REMOVE_TRAIT(mob, TRAIT_DISFIGURED, DISEASE_TRAIT) + +/datum/symptom/blindness + name = "Hyphema" + desc = "Sufferers exhibit dangerously low levels of frames per second in the eyes, leading to damage and eventually blindness." + max_multiplier = 4 + stage = 2 + badness = EFFECT_DANGER_HARMFUL + +/datum/symptom/blindness/activate(mob/living/carbon/mob) + if(!iscarbon(mob)) + return + + var/obj/item/organ/internal/eyes/eyes = mob.get_organ_slot(ORGAN_SLOT_EYES) + if(!eyes) + return // can't do much + + switch(round(multiplier)) + if(1, 2) + if(prob(base_message_chance) && !suppress_warning) + to_chat(mob, span_warning("Your eyes itch.")) + + if(3, 4) + to_chat(mob, span_boldwarning("Your eyes burn!")) + mob.set_eye_blur_if_lower(10 SECONDS) + eyes.apply_organ_damage(1) + + else + mob.set_eye_blur_if_lower(20 SECONDS) + eyes.apply_organ_damage(5) + + // Applies nearsighted at minimum + if(!mob.is_nearsighted_from(EYE_DAMAGE) && eyes.damage <= eyes.low_threshold) + eyes.set_organ_damage(eyes.low_threshold) + + if(prob(eyes.damage - eyes.low_threshold + 1)) + if(!mob.is_blind_from(EYE_DAMAGE)) + to_chat(mob, span_userdanger("You go blind!")) + eyes.apply_organ_damage(eyes.maxHealth) + else + to_chat(mob, span_userdanger("Your eyes burn horrifically!")) diff --git a/monkestation/code/modules/virology/disease/symtoms/stage3.dm b/monkestation/code/modules/virology/disease/symtoms/stage3.dm new file mode 100644 index 000000000000..dfbb0a81687e --- /dev/null +++ b/monkestation/code/modules/virology/disease/symtoms/stage3.dm @@ -0,0 +1,727 @@ +GLOBAL_LIST_INIT(disease_hivemind_users, list()) + + +/datum/symptom/toxins + name = "Hyperacidity" + desc = "Inhibits the infected's ability to process natural toxins, producing a buildup of said toxins." + stage = 3 + max_multiplier = 3 + badness = EFFECT_DANGER_HARMFUL + +/datum/symptom/toxins/activate(mob/living/carbon/mob) + mob.adjustToxLoss((2*multiplier)) + + +/datum/symptom/shakey + name = "World Shaking Syndrome" + desc = "Attacks the infected's motor output, giving them a sense of vertigo." + stage = 3 + max_multiplier = 3 + badness = EFFECT_DANGER_HINDRANCE + +/datum/symptom/shakey/activate(mob/living/carbon/mob) + shake_camera(mob,5*multiplier) + + +/datum/symptom/telepathic + name = "Abductor Syndrome" + desc = "Repurposes a portion of the users brain, making them incapable of normal speech but allows you to talk into a hivemind." + stage = 3 + max_count = 1 + badness = EFFECT_DANGER_HELPFUL + +/datum/symptom/telepathic/first_activate(mob/living/carbon/mob) + GLOB.disease_hivemind_users |= mob + RegisterSignal(mob, COMSIG_MOB_SAY, PROC_REF(handle_speech)) + +/datum/symptom/telepathic/deactivate(mob/living/carbon/mob) + GLOB.disease_hivemind_users -= mob + UnregisterSignal(mob, COMSIG_MOB_SAY) + +/datum/symptom/telepathic/proc/handle_speech(datum/source, list/speech_args) + SIGNAL_HANDLER + var/message = speech_args[SPEECH_MESSAGE] + var/mob/living/carbon/human/mob = source + mob.log_talk(message, LOG_SAY, tag="HIVEMIND DISEASE") + for(var/mob/living/living as anything in GLOB.disease_hivemind_users) + if(!isliving(living)) + continue + to_chat(living, span_abductor("[mob.real_name]:[message]")) + + for(var/mob/dead_mob in GLOB.dead_mob_list) + var/link = FOLLOW_LINK(dead_mob, mob) + to_chat(dead_mob, "[mob.real_name][link]:[message]") + + speech_args[SPEECH_MESSAGE] = "" //yep we dont speak anymore + +/datum/symptom/mind + name = "Lazy Mind Syndrome" + desc = "Rots the infected's brain." + stage = 3 + badness = EFFECT_DANGER_HARMFUL + +/datum/symptom/mind/activate(mob/living/carbon/mob) + if(istype(mob, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = mob + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5, 50) + else + mob.setCloneLoss(50) + +/datum/symptom/hallucinations + name = "Hallucinational Syndrome" + desc = "Induces hallucination in the infected." + stage = 3 + badness = EFFECT_DANGER_HINDRANCE + +/datum/symptom/hallucinations/activate(mob/living/carbon/mob) + mob.adjust_hallucinations(5 SECONDS) + +/datum/symptom/giggle + name = "Uncontrolled Laughter Effect" + desc = "Gives the infected a sense of humor." + stage = 3 + badness = EFFECT_DANGER_FLAVOR + +/datum/symptom/giggle/activate(mob/living/carbon/mob) + mob.emote("giggle") + +/datum/symptom/chickenpox + name = "Chicken Pox" + desc = "Causes the infected to begin coughing up eggs of the poultry variety." + stage = 3 + badness = EFFECT_DANGER_ANNOYING + var/eggspawn = /obj/item/food/egg + +/datum/symptom/chickenpox/activate(mob/living/carbon/mob) + if (prob(30)) + mob.say(pick("BAWWWK!", "BAAAWWK!", "CLUCK!", "CLUUUCK!", "BAAAAWWWK!")) + if (prob(15)) + mob.emote("me",1,"vomits up a chicken egg!") + playsound(mob.loc, 'sound/effects/splat.ogg', 50, 1) + new eggspawn(get_turf(mob)) + +/datum/symptom/confusion + name = "Topographical Cretinism" + desc = "Attacks the infected's ability to differentiate left and right." + stage = 3 + badness = EFFECT_DANGER_HINDRANCE + max_multiplier = 5 + max_chance = 15 + +/datum/symptom/confusion/activate(mob/living/carbon/mob) + to_chat(mob, span_notice("You have trouble telling right and left apart all of a sudden.")) + mob.adjust_confusion(1 SECONDS * multiplier) + +/datum/symptom/groan + name = "Groaning Syndrome" + desc = "Causes the infected to groan randomly." + stage = 3 + badness = EFFECT_DANGER_FLAVOR + +/datum/symptom/groan/activate(mob/living/carbon/mob) + mob.emote("groan") + + +/datum/symptom/sweat + name = "Hyper-perspiration Effect" + desc = "Causes the infected's sweat glands to go into overdrive." + stage = 3 + badness = EFFECT_DANGER_HINDRANCE + +/datum/symptom/sweat/activate(mob/living/carbon/mob) + if(prob(30)) + mob.emote("me",1,"is sweating profusely!") + + if(istype(mob.loc,/turf/open)) + var/turf/open/T = mob.loc + T.add_liquid_list(list(/datum/reagent/water = 20), TRUE) + +/datum/symptom/elvis + name = "Elvisism" + desc = "Makes the infected the king of rock and roll." + stage = 3 + badness = EFFECT_DANGER_HINDRANCE + +/datum/symptom/elvis/first_activate(mob/living/carbon/mob) + if(ismouse(mob)) + var/mob/living/basic/mouse/mouse = mob + mouse.icon_state = "mouse_elvis" + mouse.base_icon_state = "mouse_elvis" + mouse.icon_living = "mouse_elvis" + mouse.icon_dead = "mouse_brown_dead" + return + mob.dna.add_mutation(/datum/mutation/human/elvis, MUT_EXTRA) + +/datum/symptom/elvis/activate(mob/living/carbon/mob) + if(!ishuman(mob)) + return + + var/mob/living/carbon/human/H = mob + + /* + var/obj/item/clothing/glasses/H_glasses = H.get_item_by_slot(slot_glasses) + if(!istype(H_glasses, /obj/item/clothing/glasses/sunglasses/virus)) + var/obj/item/clothing/glasses/sunglasses/virus/virussunglasses = new + mob.u_equip(H_glasses,1) + mob.equip_to_slot(virussunglasses, slot_glasses) + */ + + mob.adjust_confusion(1 SECONDS) + + if(prob(50)) + mob.say(pick("Uh HUH!", "Thank you, Thank you very much...", "I ain't nothin' but a hound dog!", "Swing low, sweet chariot!")) + else + mob.emote("me",1,pick("curls his lip!", "gyrates his hips!", "thrusts his hips!")) + + if(istype(H)) + + if(!(H.hairstyle == "Pompadour (Big)")) + spawn(50) + H.hairstyle = "Pompadour (Big)" + H.hair_color = "#242424" + H.update_body() + + if(!(H.facial_hairstyle == "Sideburns (Elvis)")) + spawn(50) + H.facial_hairstyle = "Sideburns (Elvis)" + H.facial_hair_color = "#242424" + H.update_body() + +/datum/symptom/elvis/deactivate(mob/living/carbon/mob) + if(ismouse(mob)) + return + /* + if(ishuman(mob)) + var/mob/living/carbon/human/dude = mob + if(istype(dude.glasses, /obj/item/clothing/glasses/sunglasses/virus)) + dude.glasses.canremove = 1 + dude.u_equip(dude.glasses,1) + */ + mob.dna.remove_mutation(/datum/mutation/human/elvis) + +/datum/symptom/pthroat + name = "Pierrot's Throat" + desc = "Overinduces a sense of humor in the infected, causing them to be overcome by the spirit of a clown." + stage = 3 + max_multiplier = 4 + badness = EFFECT_DANGER_HINDRANCE + +/datum/symptom/pthroat/activate(mob/living/carbon/mob) + // + if(ismouse(mob)) + var/mob/living/basic/mouse/mouse = mob + mouse.icon_state = "mouse_clown" + mouse.icon_living = "mouse_clown" + mouse.icon_dead = "mouse_clown_dead" + mouse.held_state = "mouse_clown" + + if(!ishuman(mob)) + return + + var/obj/item/clothing/mask/gas/clown_hat/virus/virusclown_hat = new /obj/item/clothing/mask/gas/clown_hat/virus + if(mob.wear_mask && !istype(mob.wear_mask, /obj/item/clothing/mask/gas/clown_hat/virus)) + mob.dropItemToGround(mob.wear_mask, TRUE) + mob.equip_to_slot(virusclown_hat, ITEM_SLOT_MASK) + if(!mob.wear_mask) + mob.equip_to_slot(virusclown_hat, ITEM_SLOT_MASK) + mob.reagents.add_reagent(/datum/reagent/drug/mushroomhallucinogen, 20) + mob.say(pick("HONK!", "Honk!", "Honk.", "Honk?", "Honk!!", "Honk?!", "Honk...")) + if(ishuman(mob)) + var/mob/living/carbon/human/affected = mob + if(multiplier >=2) //clown shoes added + if(affected.shoes && !istype(affected.shoes, /obj/item/clothing/shoes/clown_shoes)) + var/obj/item/clothing/shoes/clown_shoes/virusshoes = new /obj/item/clothing/shoes/clown_shoes + affected.dropItemToGround(affected.shoes, TRUE) + affected.equip_to_slot(virusshoes, ITEM_SLOT_FEET) + if(!affected.shoes) + var/obj/item/clothing/shoes/clown_shoes/virusshoes = new /obj/item/clothing/shoes/clown_shoes + affected.equip_to_slot(virusshoes, ITEM_SLOT_FEET) + if(multiplier >=3) //clown suit added + var/obj/item/clothing/under/rank/civilian/clown/virussuit = new /obj/item/clothing/under/rank/civilian/clown + if(affected.w_uniform && !istype(affected.w_uniform, /obj/item/clothing/under/rank/civilian/clown)) + affected.dropItemToGround(affected.w_uniform, TRUE) + affected.equip_to_slot(virussuit, ITEM_SLOT_ICLOTHING) + if(!affected.w_uniform) + affected.equip_to_slot(virussuit, ITEM_SLOT_ICLOTHING) + +/datum/symptom/pthroat/first_activate(mob/living/carbon/mob) + RegisterSignal(mob, COMSIG_MOB_SAY, PROC_REF(handle_speech)) + +/datum/symptom/pthroat/deactivate(mob/living/carbon/mob) + UnregisterSignal(mob, COMSIG_MOB_SAY) + +/datum/symptom/pthroat/proc/handle_speech(datum/source, list/speech_args) + SIGNAL_HANDLER + + var/message = speech_args[SPEECH_MESSAGE] + var/list/split_message = splittext(message, " ") //List each word in the message + var/applied = 0 + for (var/i in 1 to length(split_message)) + if(prob(3 * multiplier)) //Stage 1: 3% Stage 2: 6% Stage 3: 9% Stage 4: 12% + if(findtext(split_message[i], "*") || findtext(split_message[i], ";") || findtext(split_message[i], ":")) + continue + split_message[i] = "HONK" + if (applied++ > stage) + break + if (applied) + speech_args[SPEECH_SPANS] |= SPAN_CLOWN // a little bonus + message = jointext(split_message, " ") + speech_args[SPEECH_MESSAGE] = message + +/datum/symptom/horsethroat + name = "Horse Throat" + desc = "Inhibits communication from the infected through spontaneous generation of a horse mask." + stage = 3 + badness = EFFECT_DANGER_HINDRANCE + +/datum/symptom/horsethroat/activate(mob/living/carbon/mob) + if(ismouse(mob)) + var/mob/living/basic/mouse/mouse = mob + mouse.icon_state = "mouse_horse" + mouse.icon_living = "mouse_horse" + mouse.icon_dead = "mouse_horse_dead" + mouse.held_state = "mouse_horse" + + mob.say(pick("NEIGH!", "Neigh!", "Neigh.", "Neigh?", "Neigh!!", "Neigh?!", "Neigh...")) + if(!ishuman(mob)) + return + + var/mob/living/carbon/human/human = mob + var/obj/item/clothing/mask/animal/horsehead/magichead = new /obj/item/clothing/mask/animal/horsehead + if(human.wear_mask && !istype(human.wear_mask,/obj/item/clothing/mask/animal/horsehead)) + human.dropItemToGround(human.wear_mask, TRUE) + human.equip_to_slot(magichead, ITEM_SLOT_MASK) + if(!human.wear_mask) + human.equip_to_slot(magichead, ITEM_SLOT_MASK) + to_chat(human, span_warning("You feel a little horse!")) + +/datum/symptom/anime_hair + name = "Pro-tagonista Syndrome" + desc = "Causes the infected to believe they are the center of the universe. Outcome may vary depending on symptom strength." + stage = 3 + max_count = 1 + max_chance = 20 + var/given_katana = FALSE + max_multiplier = 4 + badness = EFFECT_DANGER_ANNOYING + var/old_haircolor = "" + +/datum/symptom/anime_hair/first_activate(mob/living/carbon/mob) + RegisterSignal(mob, COMSIG_MOB_SAY, PROC_REF(handle_speech)) + +/datum/symptom/anime_hair/activate(mob/living/carbon/mob) + if(ishuman(mob)) + var/mob/living/carbon/human/affected = mob + var/list/hair_colors = list("pink","red","green","blue","purple") + var/hair_color = pick(hair_colors) + + old_haircolor = affected.hair_color + + switch(hair_color) + if("pink") + affected.hair_color = "#e983d8" + if("red") + affected.hair_color = "#E01631" + if("green") + affected.hair_color = "#008000" + if("blue") + affected.hair_color = "#0000FF" + if("purple") + affected.hair_color = "#800080" + affected.update_body() + + if(multiplier) + if(multiplier >= 1.5) + //Give them schoolgirl outfits /obj/item/clothing/under/costume/schoolgirl + var/list/outfits = list( + /obj/item/clothing/under/costume/schoolgirl, + /obj/item/clothing/under/costume/schoolgirl/red, + /obj/item/clothing/under/costume/schoolgirl/green, + /obj/item/clothing/under/costume/schoolgirl/orange + ) + var/outfit_path = pick(outfits) + var/obj/item/clothing/under/costume/schoolgirl/schoolgirl = new outfit_path + ADD_TRAIT(schoolgirl, TRAIT_NODROP, "disease") + if(affected.w_uniform && !istype(affected.w_uniform, /obj/item/clothing/under/costume/schoolgirl)) + affected.dropItemToGround(affected.w_uniform,1) + affected.equip_to_slot(schoolgirl, ITEM_SLOT_ICLOTHING) + if(!affected.w_uniform) + affected.equip_to_slot(schoolgirl, ITEM_SLOT_ICLOTHING) + if(multiplier >= 1.8) + //Kneesocks /obj/item/clothing/shoes/kneesocks + var/obj/item/clothing/shoes/kneesocks/kneesock = new /obj/item/clothing/shoes/kneesocks + ADD_TRAIT(kneesock, TRAIT_NODROP, "disease") + if(affected.shoes && !istype(affected.shoes, /obj/item/clothing/shoes/kneesocks)) + affected.dropItemToGround(affected.shoes,1) + affected.equip_to_slot(kneesock, ITEM_SLOT_FEET) + if(!affected.w_uniform) + affected.equip_to_slot(kneesock, ITEM_SLOT_FEET) + + if(multiplier >= 2) + //Regular cat ears /obj/item/clothing/head/kitty + var /obj/item/clothing/head/costume/kitty/kitty = new /obj/item/clothing/head/costume/kitty + if(affected.head && !istype(affected.head, /obj/item/clothing/head/costume/kitty)) + affected.dropItemToGround(affected.head, TRUE) + affected.equip_to_slot(kitty, ITEM_SLOT_HEAD) + if(!affected.head) + affected.equip_to_slot(kitty, ITEM_SLOT_HEAD) + + if(multiplier >= 2.5 && !given_katana) + if(multiplier >= 3) + //REAL katana /obj/item/katana + var/obj/item/katana/real_katana = new /obj/item/katana + affected.put_in_hands(real_katana) + else + //Toy katana /obj/item/toy/katana + var/obj/item/toy/katana/fake_katana = new /obj/item/toy/katana + affected.put_in_hands(fake_katana) + given_katana = TRUE + +/datum/symptom/anime_hair/deactivate(mob/living/carbon/mob) + UnregisterSignal(mob, COMSIG_MOB_SAY) + to_chat(mob, "You no longer feel quite like the main character. ") + if (ishuman(mob)) + var/mob/living/carbon/human/affected = mob + if(affected.shoes && istype(affected.shoes, /obj/item/clothing/shoes/kneesocks)) + REMOVE_TRAIT(affected.shoes, TRAIT_NODROP, "disease") + if(affected.w_uniform && istype(affected.w_uniform, /obj/item/clothing/under/costume/schoolgirl)) + REMOVE_TRAIT(affected.w_uniform, TRAIT_NODROP, "disease") + + affected.hair_color = old_haircolor + +/datum/symptom/anime_hair/proc/handle_speech(datum/source, list/speech_args) + SIGNAL_HANDLER + + var/message = speech_args[SPEECH_MESSAGE] + if(prob(20)) + message += pick(" Nyaa", " nya", " Nyaa~", "~") + + speech_args[SPEECH_MESSAGE] = message + +/datum/symptom/butterfly_skin + name = "Epidermolysis Bullosa" + desc = "Inhibits the strength of the infected's skin, causing it to tear on contact." + stage = 3 + max_count = 1 + badness = EFFECT_DANGER_HARMFUL + var/skip = FALSE + +/datum/symptom/butterfly_skin/activate(mob/living/carbon/mob) + to_chat(mob, span_warning("Your skin feels a little fragile.")) + +/datum/symptom/butterfly_skin/deactivate(mob/living/carbon/mob) + if(!skip) + to_chat(mob, span_notice("Your skin feels nice and durable again!")) + ..() + +/datum/symptom/butterfly_skin/on_touch(mob/living/carbon/mob, toucher, touched, touch_type) + if(count && !skip) + var/obj/item/bodypart/part + if(ishuman(mob)) + var/mob/living/carbon/human/H = mob + part = H.get_bodypart(H.get_random_valid_zone()) + if(toucher == mob) + if(part) + to_chat(mob, span_warning("As you bump into \the [touched], some of the skin on your [part] shears off!")) + part.take_damage(10) + else + to_chat(mob, span_warning("As you bump into \the [touched], some of your skin shears off!")) + mob.adjustBruteLoss(10) + else + if(part) + to_chat(mob, span_warning("As \the [toucher] [touch_type == DISEASE_BUMP ? "bumps into" : "touches"] you, some of the skin on your [part] shears off!")) + to_chat(toucher, span_danger("As you [touch_type == DISEASE_BUMP ? "bump into" : "touch"] \the [mob], some of the skin on \his [part] shears off!")) + part.take_damage(10) + else + to_chat(mob, span_warning("As \the [toucher] [touch_type == DISEASE_BUMP ? "bumps into" : "touches"] you, some of your skin shears off!")) + to_chat(toucher, span_danger("As you [touch_type == DISEASE_BUMP ? "bump into" : "touch"] \the [mob], some of \his skin shears off!")) + mob.adjustBruteLoss(10) + +/datum/symptom/thick_blood + name = "Hyper-Fibrinogenesis" + desc = "Causes the infected to oversynthesize coagulant, as well as rapidly restore lost blood." + stage = 3 + badness = EFFECT_DANGER_HELPFUL + +/datum/symptom/thick_blood/activate(mob/living/carbon/mob) + var/mob/living/carbon/human/H = mob + if (ishuman(H)) + if(H.is_bleeding()) + H.restore_blood() + to_chat(H, span_notice("You feel your blood regenerate, and your bleeding to stop!")) + +/datum/symptom/teratoma + name = "Teratoma Syndrome" + desc = "Causes the infected to oversynthesize stem cells engineered towards organ generation. Said generated organs are expelled from the body upon completion." + stage = 3 + badness = EFFECT_DANGER_FLAVOR + +/datum/symptom/teratoma/activate(mob/living/carbon/mob) + var/fail_counter = 0 + var/not_passed = TRUE + var/obj/item/organ/spawned_organ + while(not_passed && fail_counter <= 10) + var/organ_type = pick(typesof(/obj/item/organ/internal)) + spawned_organ = new organ_type(get_turf(mob)) + if(spawned_organ.status != ORGAN_ORGANIC) + qdel(spawned_organ) + fail_counter++ + continue + not_passed = FALSE + + if(!not_passed) + mob.visible_message(span_warning("\A [spawned_organ.name] is extruded from \the [mob]'s body and falls to the ground!"),span_warning("\A [spawned_organ.name] is extruded from your body and falls to the ground!")) + +/datum/symptom/damage_converter + name = "Toxic Compensation" + desc = "Stimulates cellular growth within the body, causing it to regenerate tissue damage. Repair done by these cells causes toxins to build up in the body." + badness = EFFECT_DANGER_FLAVOR + stage = 3 + chance = 10 + max_chance = 50 + multiplier = 5 + max_multiplier = 10 + +/datum/symptom/damage_converter/activate(mob/living/carbon/mob) + if(mob.getFireLoss() > 0 || mob.getBruteLoss() > 0) + var/get_damage = rand(1, 3) + mob.adjustFireLoss(-get_damage) + mob.adjustBruteLoss(-get_damage) + mob.adjustToxLoss(max(1,get_damage * multiplier / 5)) + +/datum/symptom/mommi_hallucination + name = "Supermatter Syndrome" + desc = "Causes the infected to experience engineering-related hallucinations." + stage = 3 + badness = EFFECT_DANGER_ANNOYING + +/datum/symptom/mommi_hallucination/activate(mob/living/carbon/mob) + if(prob(50)) + mob << sound('sound/effects/supermatter.ogg', volume = 25) + + var/mob/living/silicon/robot/mommi = /mob/living/silicon/robot + for(var/mob/living/M in viewers(mob)) + if(M == mob) + continue + + var/image/crab = image(icon = null) + crab.appearance = initial(mommi.appearance) + + crab.loc = M + crab.override = 1 + + var/client/C = mob.client + if(C) + C.images += crab + var/duration = rand(60 SECONDS, 120 SECONDS) + + spawn(duration) + if(C) + C.images.Remove(crab) + + var/list/turf_list = list() + for(var/turf/T in spiral_block(get_turf(mob), 40)) + if(prob(4)) + turf_list += T + if(turf_list.len) + for(var/turf/open/floor/T in turf_list) + var/image/supermatter = image('icons/obj/engine/supermatter.dmi', T ,"sm", ABOVE_MOB_LAYER) + + var/client/C = mob.client + if(C) + C.images += supermatter + var/duration = rand(60 SECONDS, 120 SECONDS) + + spawn(duration) + if(C) + C.images.Remove(supermatter) + + +/datum/symptom/wendigo_hallucination + name = "Eldritch Mind Syndrome" + desc = "UNKNOWN" + badness = EFFECT_DANGER_HARMFUL + stage = 3 + + +/datum/symptom/wendigo_hallucination/first_activate(mob/living/carbon/mob) + RegisterSignal(mob, COMSIG_MOB_SAY, PROC_REF(handle_speech)) + +/datum/symptom/wendigo_hallucination/deactivate(mob/living/carbon/mob) + UnregisterSignal(mob, COMSIG_MOB_SAY) + +/datum/symptom/wendigo_hallucination/activate(mob/living/carbon/mob) + if(!ishuman(mob)) + return + var/mob/living/carbon/human/H = mob + H.adjust_jitter(10 SECONDS) + + //creepy sounds copypasted from hallucination code + var/list/possible_sounds = list( + 'monkestation/code/modules/virology/sounds/ghost.ogg', 'monkestation/code/modules/virology/sounds/ghost2.ogg', 'monkestation/code/modules/virology/sounds/heart_beat_single.ogg', 'monkestation/code/modules/virology/sounds/ear_ring_single.ogg', 'monkestation/code/modules/virology/sounds/screech.ogg',\ + 'monkestation/code/modules/virology/sounds/behind_you1.ogg', 'monkestation/code/modules/virology/sounds/behind_you2.ogg', 'monkestation/code/modules/virology/sounds/far_noise.ogg', 'monkestation/code/modules/virology/sounds/growl1.ogg', 'monkestation/code/modules/virology/sounds/growl2.ogg',\ + 'monkestation/code/modules/virology/sounds/growl3.ogg', 'monkestation/code/modules/virology/sounds/im_here1.ogg', 'monkestation/code/modules/virology/sounds/im_here2.ogg', 'monkestation/code/modules/virology/sounds/i_see_you1.ogg', 'monkestation/code/modules/virology/sounds/i_see_you2.ogg',\ + 'monkestation/code/modules/virology/sounds/look_up1.ogg', 'monkestation/code/modules/virology/sounds/look_up2.ogg', 'monkestation/code/modules/virology/sounds/over_here1.ogg', 'monkestation/code/modules/virology/sounds/over_here2.ogg', 'monkestation/code/modules/virology/sounds/over_here3.ogg',\ + 'monkestation/code/modules/virology/sounds/turn_around1.ogg', 'monkestation/code/modules/virology/sounds/turn_around2.ogg', 'monkestation/code/modules/virology/sounds/veryfar_noise.ogg', 'monkestation/code/modules/virology/sounds/wail.ogg') + mob.playsound_local(mob.loc, pick(possible_sounds)) + + + +/datum/symptom/wendigo_hallucination/proc/handle_speech(datum/source, list/speech_args) + SIGNAL_HANDLER + + var/message = speech_args[SPEECH_MESSAGE] + message = replacetext(message,"I","we") + message = replacetext(message,"me","us") + speech_args[SPEECH_MESSAGE] = message + + +/datum/symptom/asphyxiation + name = "Acute respiratory distress syndrome" + desc = "The virus causes shrinking of the host's lungs, causing severe asphyxiation. May also lead to brain damage in critical patients." + badness = EFFECT_DANGER_DEADLY + max_chance = 10 + multiplier = 5 + stage = 3 + +/datum/symptom/asphyxiation/activate(mob/living/carbon/mob) + mob.emote("gasp") + if(prob(20) && multiplier >= 4 && iscarbon(mob)) + mob.reagents.add_reagent_list(list(/datum/reagent/toxin/pancuronium = 3, /datum/reagent/toxin/sodium_thiopental = 3)) + mob.adjustOxyLoss(rand(5,15) * multiplier) + if(mob.getOxyLoss() >= 120 && multiplier == 5) + mob.adjustOxyLoss(rand(5,7) * multiplier) + mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, multiplier) + +/datum/symptom/wizarditis + name = "Wizarditis" + max_multiplier = 4 + stage = 3 + desc = "Some speculate that this virus is the cause of the Space Wizard Federation's existence. Subjects affected show the signs of brain damage, yelling obscure sentences or total gibberish. On late stages subjects sometime express the feelings of inner power, and, cite, 'the ability to control the forces of cosmos themselves!' A gulp of strong, manly spirits usually reverts them to normal, humanlike, condition." + badness = EFFECT_DANGER_HARMFUL + +/datum/symptom/wizarditis/activate(mob/living/carbon/affected_mob) + switch(round(multiplier)) + if(2) + if(prob(10)) + affected_mob.say(pick("You shall not pass!", "Expeliarmus!", "By Merlins beard!", "Feel the power of the Dark Side!"), forced = "wizarditis") + if(prob(10)) + to_chat(affected_mob, span_danger("You feel [pick("that you don't have enough mana", "that the winds of magic are gone", "an urge to summon familiar")].")) + if(3) + if(prob(10)) + affected_mob.say(pick("NEC CANTIO!","AULIE OXIN FIERA!", "STI KALY!", "TARCOL MINTI ZHERI!"), forced = "wizarditis") + if(prob(10)) + to_chat(affected_mob, span_danger("You feel [pick("the magic bubbling in your veins","that this location gives you a +1 to INT","an urge to summon familiar")].")) + if(4) + if(prob(10)) + affected_mob.say(pick("NEC CANTIO!","AULIE OXIN FIERA!","STI KALY!","EI NATH!"), forced = "wizarditis") + return + if(prob(5)) + to_chat(affected_mob, span_danger("You feel [pick("the tidal wave of raw power building inside","that this location gives you a +2 to INT and +1 to WIS","an urge to teleport")].")) + spawn_wizard_clothes(50, affected_mob) + if(prob(1)) + teleport(affected_mob) + + +/datum/symptom/wizarditis/proc/spawn_wizard_clothes(chance = 0, mob/living/carbon/affected_mob) + if(ishuman(affected_mob)) + var/mob/living/carbon/human/H = affected_mob + if(prob(chance)) + if(!istype(H.head, /obj/item/clothing/head/wizard)) + if(!H.dropItemToGround(H.head)) + qdel(H.head) + H.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(H), ITEM_SLOT_HEAD) + return + if(prob(chance)) + if(!istype(H.wear_suit, /obj/item/clothing/suit/wizrobe)) + if(!H.dropItemToGround(H.wear_suit)) + qdel(H.wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(H), ITEM_SLOT_OCLOTHING) + return + if(prob(chance)) + if(!istype(H.shoes, /obj/item/clothing/shoes/sandal/magic)) + if(!H.dropItemToGround(H.shoes)) + qdel(H.shoes) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal/magic(H), ITEM_SLOT_FEET) + return + else + var/mob/living/carbon/H = affected_mob + if(prob(chance)) + var/obj/item/staff/S = new(H) + if(!H.put_in_hands(S)) + qdel(S) + + +/datum/symptom/wizarditis/proc/teleport(mob/living/carbon/affected_mob) + var/list/theareas = get_areas_in_range(80, affected_mob) + for(var/area/space/S in theareas) + theareas -= S + + if(!theareas || !theareas.len) + return + + var/area/thearea = pick(theareas) + + var/list/L = list() + var/turf/mob_turf = get_turf(affected_mob) + for(var/turf/T in get_area_turfs(thearea.type)) + if(!is_valid_z_level(T, mob_turf)) + continue + if(T.name == "space") + continue + if(!T.density) + var/clear = 1 + for(var/obj/O in T) + if(O.density) + clear = 0 + break + if(clear) + L+=T + + if(!L) + return + + affected_mob.say("SCYAR NILA [uppertext(thearea.name)]!", forced = "wizarditis teleport") + affected_mob.forceMove(pick(L)) + + return + +/datum/symptom/polyvitiligo + name = "Chroma Imbalance" + desc = "The virus replaces the melanin in the skin with reactive pigment." + stage = 3 + max_multiplier = 6 + badness = EFFECT_DANGER_FLAVOR + +/datum/symptom/polyvitiligo/activate(mob/living/carbon/mob) + if(!iscarbon(mob)) + return + switch(round(multiplier, 1)) + if(5) + var/static/list/banned_reagents = list(/datum/reagent/colorful_reagent/powder/invisible, /datum/reagent/colorful_reagent/powder/white) + var/color = pick(subtypesof(/datum/reagent/colorful_reagent/powder) - banned_reagents) + if(mob.reagents.total_volume <= (mob.reagents.maximum_volume/10)) // no flooding humans with 1000 units of colorful reagent + mob.reagents.add_reagent(color, 5 * multiplier) + else + if (prob(50)) // spam + mob.visible_message(span_warning("[mob] looks rather vibrant..."), span_notice("The colors, man, the colors...")) + +/datum/symptom/metabolism + name = "Metabolic Boost" + desc = "The virus causes the host's metabolism to accelerate rapidly, making them process chemicals twice as fast,\ + but also causing increased hunger." + max_multiplier = 5 + stage = 3 + badness = EFFECT_DANGER_HELPFUL + + +/datum/symptom/metabolism/activate(mob/living/carbon/mob) + if(!iscarbon(mob)) + return + + mob.reagents.metabolize(mob, (multiplier * 0.5) * SSMOBS_DT, 0, can_overdose=TRUE) //this works even without a liver; it's intentional since the virus is metabolizing by itself + mob.overeatduration = max(mob.overeatduration - 4 SECONDS, 0) + mob.adjust_nutrition(-(4 + multiplier) * HUNGER_FACTOR) //Hunger depletes at 10x the normal speed + if(prob(2 * multiplier)) + to_chat(mob, span_notice("You feel an odd gurgle in your stomach, as if it was working much faster than normal.")) + diff --git a/monkestation/code/modules/virology/disease/symtoms/stage4.dm b/monkestation/code/modules/virology/disease/symtoms/stage4.dm new file mode 100644 index 000000000000..e872b22d4e25 --- /dev/null +++ b/monkestation/code/modules/virology/disease/symtoms/stage4.dm @@ -0,0 +1,413 @@ +/datum/symptom/spaceadapt + name = "Space Adaptation Effect" + desc = "Heals the infected from the effects of space exposure, should they remain in a vacuum." + stage = 4 + max_count = 1 + badness = EFFECT_DANGER_HELPFUL + chance = 10 + max_chance = 25 + +/datum/symptom/spaceadapt/activate(mob/living/carbon/mob) + mob.dna.add_mutation(/datum/mutation/human/pressure_adaptation) + mob.dna.add_mutation(/datum/mutation/human/temperature_adaptation) + +/datum/symptom/spaceadapt/deactivate(mob/living/carbon/mob) + mob.dna.remove_mutation(/datum/mutation/human/pressure_adaptation) + mob.dna.remove_mutation(/datum/mutation/human/temperature_adaptation) + +/datum/symptom/minttoxin + name = "Creosote Syndrome" + desc = "Causes the infected to synthesize a wafer thin mint." + stage = 4 + badness = EFFECT_DANGER_HARMFUL + +/datum/symptom/minttoxin/activate(mob/living/carbon/mob) + if(istype(mob) && mob.reagents.get_reagent_amount(/datum/reagent/consumable/mintextract) < 5) + to_chat(mob, span_notice("You feel a minty freshness")) + mob.reagents.add_reagent(/datum/reagent/consumable/mintextract, 5) + +/datum/symptom/deaf + name = "Dead Ear Syndrome" + desc = "Kills the infected's aural senses." + stage = 4 + max_multiplier = 5 + badness = EFFECT_DANGER_HINDRANCE + +/datum/symptom/deaf/activate(mob/living/carbon/mob) + var/obj/item/organ/internal/ears/ears = mob.get_organ_slot(ORGAN_SLOT_EARS) + if(!ears) + return //cutting off your ears to cure the deafness: the ultimate own + to_chat(mob, span_userdanger("Your ears pop and begin ringing loudly!")) + ears.deaf = min(20, ears.deaf + 15) + + if(prob(multiplier * 5)) + if(ears.damage < ears.maxHealth) + to_chat(mob, span_userdanger("Your ears pop painfully and start bleeding!")) + // Just absolutely murder me man + ears.apply_organ_damage(ears.maxHealth) + mob.emote("scream") + ADD_TRAIT(mob, TRAIT_DEAF, DISEASE_TRAIT) + +/datum/symptom/deaf/deactivate(mob/living/carbon/mob) + REMOVE_TRAIT(mob, TRAIT_DEAF, DISEASE_TRAIT) + + +/datum/symptom/killertoxins + name = "Toxification Syndrome" + desc = "A more advanced version of Hyperacidity, causing the infected to rapidly generate toxins." + stage = 4 + badness = EFFECT_DANGER_DEADLY + multiplier = 3 + max_multiplier = 5 + +/datum/symptom/killertoxins/activate(mob/living/carbon/mob) + mob.adjustToxLoss(5*multiplier) + + +/datum/symptom/dna + name = "Reverse Pattern Syndrome" + desc = "Attacks the infected's DNA, causing rapid spontaneous mutation, and inhibits the ability for the infected to be affected by cryogenics." + stage = 4 + badness = EFFECT_DANGER_DEADLY + +/datum/symptom/dna/activate(mob/living/carbon/mob) + mob.bodytemperature = max(mob.bodytemperature, 350) + scramble_dna(mob, TRUE, TRUE, TRUE, rand(15,45)) + mob.adjustCloneLoss(10) + + +/datum/symptom/immortal + name = "Longevity Syndrome" + desc = "Grants functional immortality to the infected so long as the symptom is active. Heals broken bones and healing external damage. Creates a backlash if cured." + stage = 4 + badness = EFFECT_DANGER_HELPFUL + var/total_healed = 0 + +/datum/symptom/immortal/activate(mob/living/carbon/mob) + if(istype(mob, /mob/living/carbon/human)) + for(var/datum/wound/wound as anything in mob.all_wounds) + to_chat(mob, span_notice("You feel the [wound] heal itself.")) + wound.remove_wound() + break + + var/heal_amt = 5*multiplier + var/current_health = mob.getBruteLoss() + if(current_health >= heal_amt) + total_healed += heal_amt * 0.2 + else + total_healed += (heal_amt - current_health) * 0.2 + mob.adjustBruteLoss(-heal_amt) + mob.adjustFireLoss(-heal_amt) + mob.adjustCloneLoss(-heal_amt) + +/datum/symptom/immortal/deactivate(mob/living/carbon/mob) + if(istype(mob, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = mob + to_chat(H, span_warning("You suddenly feel hurt and old...")) + H.age += 4 * multiplier * total_healed + mob.adjustBruteLoss(total_healed) + mob.adjustFireLoss(total_healed) + +/datum/symptom/bones + name = "Fragile Person Syndrome" + desc = "Attacks the infected's body structure, making it more fragile." + stage = 4 + badness = EFFECT_DANGER_HINDRANCE + +/datum/symptom/bones/activate(mob/living/carbon/mob) + if(istype(mob, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = mob + for (var/obj/item/bodypart/bp in H.bodyparts) + bp.wound_resistance -= 10 + +/datum/symptom/bones/deactivate(mob/living/carbon/mob) + if(istype(mob, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = mob + for (var/obj/item/bodypart/bp in H.bodyparts) + bp.wound_resistance += 10 + +/datum/symptom/fizzle + name = "Fizzle Effect" + desc = "Causes an ill, though harmless, sensation in the infected's throat." + stage = 4 + badness = EFFECT_DANGER_FLAVOR + +/datum/symptom/fizzle/activate(mob/living/carbon/mob) + mob.emote("me",1,pick("sniffles...", "clears their throat...")) + +/datum/symptom/delightful + name = "Delightful Effect" + desc = "A more powerful version of Full Glass. Makes the infected feel delightful." + stage = 4 + badness = EFFECT_DANGER_HELPFUL + +/datum/symptom/delightful/activate(mob/living/carbon/mob) + to_chat(mob, "You feel delightful!") + if (mob.reagents.get_reagent_amount(/datum/reagent/drug/happiness) < 10) + mob.reagents.add_reagent(/datum/reagent/drug/happiness, 10) + +/datum/symptom/spawn + name = "Arachnogenesis Effect" + desc = "Converts the infected's stomach to begin producing creatures of the arachnid variety." + stage = 4 + max_multiplier = 7 + badness = EFFECT_DANGER_HARMFUL + var/spawn_type= /mob/living/basic/spider/growing/spiderling/guard + var/spawn_name="spiderling" + +/datum/symptom/spawn/activate(mob/living/carbon/mob) + playsound(mob.loc, 'sound/effects/splat.ogg', 50, 1) + var/mob/living/spawned_mob = new spawn_type(get_turf(mob)) + mob.emote("me",1,"vomits up a live [spawn_name]!") + if(multiplier < 4) + addtimer(CALLBACK(src, PROC_REF(kill_mob), spawned_mob), 1 MINUTES) + +/datum/symptom/spawn/proc/kill_mob(mob/living/basic/mob) + mob.visible_message(span_warning("The [mob] falls apart!"), span_warning("You fall apart")) + mob.death() + +/datum/symptom/spawn/roach + name = "Blattogenesis Effect" + desc = "Converts the infected's stomach to begin producing creatures of the blattid variety." + stage = 4 + badness = EFFECT_DANGER_HINDRANCE + spawn_type=/mob/living/basic/cockroach + spawn_name="cockroach" + +/datum/symptom/gregarious + name = "Gregarious Impetus" + desc = "Infests the social structures of the infected's brain, causing them to feel better in crowds of other potential victims, and punishing them for being alone." + stage = 4 + badness = EFFECT_DANGER_HINDRANCE + max_chance = 25 + max_multiplier = 4 + +/datum/symptom/gregarious/activate(mob/living/carbon/mob) + var/others_count = 0 + for(var/mob/living/carbon/m in oview(5, mob)) + others_count += 1 + + if (others_count >= multiplier) + to_chat(mob, span_notice("A friendly sensation is satisfied with how many are near you - for now.")) + mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, -multiplier) + mob.reagents.add_reagent(/datum/reagent/drug/happiness, multiplier) // ADDICTED TO HAVING FRIENDS + if (multiplier < max_multiplier) + multiplier += 0.15 // The virus gets greedier + else + to_chat(mob, span_warning("A hostile sensation in your brain stings you... it wants more of the living near you.")) + mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, multiplier / 2) + mob.AdjustParalyzed(multiplier) // This practically permaparalyzes you at higher multipliers but + mob.AdjustKnockdown(multiplier) // that's your fucking fault for not being near enough people + mob.AdjustStun(multiplier) // You'll have to wait until the multiplier gets low enough + if (multiplier > 1) + multiplier -= 0.3 // The virus tempers expectations + +/datum/symptom/magnitis + name = "Magnitis" + desc = "This disease disrupts the magnetic field of the body, making it act as if a powerful magnet." + stage = 4 + badness = EFFECT_DANGER_HARMFUL + chance = 5 + max_chance = 20 + +/datum/symptom/magnitis/activate(mob/living/carbon/mob) + if(mob.reagents.has_reagent(/datum/reagent/iron)) + return + + var/intensity = 1 + (count > 10) + (count > 20) + if (prob(20)) + to_chat(mob, span_warning("You feel a [intensity < 3 ? "slight" : "powerful"] shock course through your body.")) + for(var/obj/M in orange(3 * intensity,mob)) + if(!M.anchored) + var/iter = rand(1,intensity) + for(var/i=0,i= disease.strength) + return FALSE + return TRUE + +/datum/immune_system/proc/ApplyAntipathogenics(threshold) + if (overloaded) + return + + for (var/datum/disease/advanced/disease as anything in host.diseases) + for (var/A in disease.antigen) + var/tally = 0.5 + if (isturf(host.loc) && (host.body_position == LYING_DOWN)) + tally += 0.5 + var/obj/structure/bed/B = locate() in host.loc + if (host.buckled == B)//fucking chairs n stuff + tally += 1 + if (host.IsUnconscious()) + if (tally < 2) + tally += 1 + else + tally += 2//if we're sleeping in a bed, we get up to 4 + else if(istype(host.loc, /obj/machinery/atmospherics/components/unary/cryo_cell)) + tally += 1.5 + + if (antibodies[A] < threshold) + antibodies[A] = min(antibodies[A] + tally, threshold)//no overshooting here + else + if (prob(threshold) && prob(tally * 10) && prob((100 - antibodies[A])*100/(100-threshold)))//smaller and smaller chance for further increase + antibodies[A] = min(antibodies[A] + 1, 100) + + +/datum/immune_system/proc/ApplyVaccine(list/antigen, amount = 1, decay = 0) + if (overloaded) + return + + for (var/A in antigen) + antibodies[A] = min(antibodies[A] + 10 * amount, 100) + if(decay) + addtimer(CALLBACK(src, PROC_REF(decay_vaccine), antigen, amount), decay) + +/datum/immune_system/proc/decay_vaccine(list/antigens, amount = 1) + for (var/A in antigens) + antibodies[A] = max(antibodies[A] - 8 * amount, 10) diff --git a/monkestation/code/modules/virology/items/_base_item_additions.dm b/monkestation/code/modules/virology/items/_base_item_additions.dm new file mode 100644 index 000000000000..ff9ea99d0310 --- /dev/null +++ b/monkestation/code/modules/virology/items/_base_item_additions.dm @@ -0,0 +1,94 @@ + +GLOBAL_LIST_INIT(infected_items, list()) + +/obj/item + //how sterile an item is, not used for much atm + var/sterility = 0 +/obj/item + var/list/viruses = list() + +/obj/item/attack_hand(mob/user, list/modifiers) + . = ..() + disease_contact(user) + +//Called by attack_hand(), transfers diseases between the mob and the item +/obj/item/proc/disease_contact(mob/living/carbon/M, bodypart = null) + //first let's try to infect them with our viruses + for(var/datum/disease/advanced/V as anything in viruses) + infection_attempt(M, V, bodypart) + + if (!bodypart)//no bodypart specified? that should mean we're being held. + bodypart = BODY_ZONE_ARMS + + //secondly, do they happen to carry contact-spreading viruses themselves? + var/list/contact_diseases = filter_disease_by_spread(M.diseases, required = DISEASE_SPREAD_CONTACT_SKIN) + if (contact_diseases?.len) + //if so are their hands protected? + var/block = M.check_contact_sterility(bodypart) + for (var/datum/disease/advanced/D in contact_diseases) + if(!block) + infect_disease(D, notes="(Contact, from being touched by [M])") + +//Called by disease_contact(), trying to infect people who pick us up +/obj/item/infection_attempt(mob/living/perp, datum/disease/advanced/D, bodypart = null) + if (!istype(D)) + return + + if (src in perp.held_items) + bodypart = BODY_ZONE_ARMS + + if (bodypart) + var/block = perp.check_contact_sterility(bodypart) + var/bleeding = perp.check_bodypart_bleeding(bodypart) + if (!block && (D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)) + perp.infect_disease(D, notes="(Contact, from picking up \a [src])") + else if (bleeding && (D.spread_flags & DISEASE_SPREAD_BLOOD))//if we're covered with a blood-spreading disease, we may infect people with bleeding hands. + perp.infect_disease(D, notes="(Blood, from picking up \a [src])") + +/obj/item/infect_disease(datum/disease/advanced/disease, forced = FALSE, notes = "", decay = TRUE) + if(!istype(disease)) + return FALSE + if(!disease.spread_flags) + return FALSE + if(prob(disease.infectionchance) || forced) + var/datum/disease/advanced/D = disease.Copy() + D.log += "
[ROUND_TIME()] Infected \a [src] [notes]" + + GLOB.infected_items |= src + + LAZYADD(viruses, D) + //SSdisease.active_diseases += D + D.after_add() + + add_event_to_buffer(src, data = "was infected by virus: [D.admin_details()] at [loc_name(loc)].", log_key = "VIRUS") + + if (!pathogen) + pathogen = image('monkestation/code/modules/virology/icons/effects.dmi', src, "pathogen_contact") + pathogen.plane = HUD_PLANE + pathogen.appearance_flags = RESET_COLOR|RESET_ALPHA + for (var/mob/L in GLOB.science_goggles_wearers) + if (L.client) + L.client.images |= pathogen + if (decay) + addtimer(CALLBACK(src, PROC_REF(remove_disease), D), (disease.infectionchance/10) MINUTES) + +/obj/item/proc/remove_disease(datum/disease/disease) + viruses -= disease + SSdisease.active_diseases -= disease + if(!length(viruses)) + GLOB.infected_items -= src + if (pathogen) + for (var/mob/L in GLOB.science_goggles_wearers) + if(L.client) + L.client.images -= pathogen + + +/obj/item/try_infect_with_mobs_diseases(mob/living/carbon/infectee) + if(!infectee) + return + if(!length(infectee.diseases)) + return + var/list/blood_diseases = filter_disease_by_spread(infectee.diseases, required = DISEASE_SPREAD_BLOOD) + if(length(blood_diseases)) + for(var/datum/disease/advanced/V as anything in blood_diseases) + infect_disease(V, TRUE, "(Blood, coming from [infectee])") diff --git a/monkestation/code/modules/virology/items/antibodyscanner.dm b/monkestation/code/modules/virology/items/antibodyscanner.dm new file mode 100644 index 000000000000..4f359fd9088b --- /dev/null +++ b/monkestation/code/modules/virology/items/antibodyscanner.dm @@ -0,0 +1,142 @@ + +/obj/item/device/antibody_scanner + name = "immunity scanner" + desc = "A hand-held body scanner able to evaluate the immune system of the subject." + icon = 'monkestation/code/modules/virology/icons/items.dmi' + icon_state = "antibody" + inhand_icon_state = "healthanalyzer" + worn_icon_state = "healthanalyzer" + lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' + siemens_coefficient = 1 + slot_flags = ITEM_SLOT_BELT + throwforce = 3 + w_class = WEIGHT_CLASS_TINY + throw_speed = 5 + custom_materials = list(/datum/material/iron=200) + + +/obj/item/device/antibody_scanner/attack(mob/living/carbon/L, mob/living/carbon/human/user) + if(!istype(L)) + //to_chat(user, span_notice("Incompatible object, scan aborted.")) + return + + if (issilicon(L)) + to_chat(user, span_warning("Incompatible with silicon lifeforms, scan aborted.")) + return + + playsound(user, 'sound/items/weeoo1.ogg', 50, 1) + var/info = "" + var/icon/scan = icon('monkestation/code/modules/virology/icons/virology_bg.dmi',"immunitybg") + var/display_width = scan.Width() + + var/list/antigens_that_matter = list() + if (L.immune_system) + finding_antigens: + for (var/antibody in L.immune_system.antibodies) + if (L.immune_system.antibodies[antibody] > 0) + antigens_that_matter += antibody + continue + if (length(L.diseases)) + for (var/datum/disease/advanced/D as anything in L.diseases) + var/ID = "[D.uniqueID]-[D.subID]" + if(ID in GLOB.virusDB) + if (antibody in D.antigen) + antigens_that_matter += antibody + continue finding_antigens + + var/bar_spacing = round(display_width/antigens_that_matter.len) + var/bar_width = round(bar_spacing/2) + var/bar_offset = round(bar_width/4) + var/x_adjustment = 5//Sometimes you have to adjust things manually so they look good. This var moves all the gauges and graduations on the x axis. + + if (L.immune_system) + var/immune_system = L.immune_system.GetImmunity() + var/immune_str = immune_system[1] + var/list/antibodies = immune_system[2] + + info += "Immune System Status: [round(immune_str*100)]%" + info += "
Antibody Concentrations:" + + var/i = 0 + for (var/antibody in antigens_that_matter) + var/rgb = "#FFFFFF" + switch (antibody) + if ("O","A","B","Rh") + rgb = "#80DEFF" + if ("Q","U","V") + rgb = "#81FF9F" + if ("M","N","P") + rgb = "#E6FF81" + if ("X","Y","Z") + rgb = "#FF9681" + if ("C") + rgb = "#F54B4B" + //add colors for new special antigens here + scan.DrawBox(rgb,i*bar_spacing+bar_offset+x_adjustment,6,i*bar_spacing+bar_width+bar_offset+x_adjustment,6+antibodies[antibody]*3*immune_str) + i++ + + if (length(L.diseases)) + for (var/datum/disease/advanced/D as anything in L.diseases) + var/ID = "[D.uniqueID]-[D.subID]" + scan.DrawBox("#FF0000",6,6+D.strength*3,display_width-5,6+D.strength*3) + if(ID in GLOB.virusDB) + var/subdivision = (D.strength - ((D.robustness * D.strength) / 100)) / D.max_stages + var/i = 0 + for (var/antigen in antigens_that_matter) + if (antigen in D.antigen) + var/box_size = 3 + scan.DrawBox("#FF0000",bar_width-box_size+bar_spacing*i,6+D.strength*3-3,bar_width+box_size+bar_spacing*i,6+D.strength*3+3) + scan.DrawBox("#FF0000",bar_width+bar_spacing*i,6+D.strength*3,bar_width+bar_spacing*i,6+round(D.strength - D.max_stages * subdivision)*3) + var/stick_out = 6//how far the graduations go left and right of the gauge + for (var/j = 1 to D.max_stages) + var/alt = round(D.strength - j * subdivision) + scan.DrawBox("#FF0000",i*bar_spacing+bar_offset-stick_out+x_adjustment,6+alt*3,i*bar_spacing+bar_offset+bar_width+stick_out+x_adjustment,6+alt*3) + i++ + + info += "
" + info += "
" + info += "" + info += "" + if (L.immune_system) + for (var/antibody in antigens_that_matter) + info += "" + info += "" + info += "" + if (L.immune_system) + for (var/antibody in antigens_that_matter) + info += "" + info += "
[antibody]
[round(L.immune_system.antibodies[antibody]*L.immune_system.strength)]%" + info += "
" + + if (length(L.diseases)) + for (var/datum/disease/advanced/D as anything in L.diseases) + var/ID = "[D.uniqueID]-[D.subID]" + if(ID in GLOB.virusDB) + var/datum/data/record/V = GLOB.virusDB[ID] + info += "
[V.fields["name"]][V.fields["nickname"] ? " \"[V.fields["nickname"]]\"" : ""] detected. Strength: [D.strength]. Robustness: [D.robustness]. Antigen: [D.get_antigen_string()]" + else + info += "
Unknown [D.form] detected. Strength: [D.strength]" + + var/datum/browser/popup = new(user, "\ref[src]", name, 600, 600, src) + popup.set_content(info) + popup.open() + +/obj/item/device/antibody_scanner/pre_attack(atom/A, mob/living/user, params) + if(!Adjacent(A)) + return + if (isitem(A)) + var/obj/item/I = A + playsound(user, 'sound/items/weeoo1.ogg', 50, 1) + var/span = "warning" + if(I.sterility <= 0) + span = "danger" + else if (I.sterility >= 100) + span = "notice" + to_chat(user,"Scanning \the [I]...sterility level = [I.sterility]%") + if (istype(I,/obj/item/weapon/virusdish)) + var/obj/item/weapon/virusdish/dish = I + if (dish.open && dish.contained_virus) + to_chat(user,span_danger("However, since its lid has been opened, unprotected contact with the dish can result in infection.")) + + . = ..() diff --git a/monkestation/code/modules/virology/items/clothing/mask.dm b/monkestation/code/modules/virology/items/clothing/mask.dm new file mode 100644 index 000000000000..faf9e5a4a362 --- /dev/null +++ b/monkestation/code/modules/virology/items/clothing/mask.dm @@ -0,0 +1,2 @@ +/obj/item/clothing/mask/gas/clown_hat/virus + desc = "A true prankster's facial attire. A clown is incomplete without his wig and mask. On second look, it looks like it's coming out of the wearers skin!" diff --git a/monkestation/code/modules/virology/items/clothing/shoes.dm b/monkestation/code/modules/virology/items/clothing/shoes.dm new file mode 100644 index 000000000000..7e071fff087c --- /dev/null +++ b/monkestation/code/modules/virology/items/clothing/shoes.dm @@ -0,0 +1,7 @@ +/obj/item/clothing/shoes/kneesocks + name = "kneesocks" + desc = "A pair of girly knee-high socks." + icon_state = "kneesock" + worn_icon_state = "kneesock" + icon = 'monkestation/code/modules/virology/icons/shoes.dmi' + worn_icon = 'monkestation/code/modules/virology/icons/shoes_worn.dmi' diff --git a/monkestation/code/modules/virology/items/disease_disk.dm b/monkestation/code/modules/virology/items/disease_disk.dm new file mode 100644 index 000000000000..1826de1e650f --- /dev/null +++ b/monkestation/code/modules/virology/items/disease_disk.dm @@ -0,0 +1,15 @@ +/obj/item/disk/disease + name = "blank GNA disk" + desc = "A disk for storing the structure of a pathogen's Glycol Nucleic Acid pertaining to a specific symptom." + var/datum/symptom/effect = null + var/stage = 1 + +/obj/item/disk/disease/premade/New() + name = "blank GNA disk (stage: [stage])" + effect = new /datum/symptom + +/obj/item/disk/disease/update_desc(updates) + . = ..() + desc = "[initial(desc)]\n" + desc += "Strength: [effect.multiplier]\n" + desc += "Occurrence: [effect.chance]" diff --git a/monkestation/code/modules/virology/items/mousecubes.dm b/monkestation/code/modules/virology/items/mousecubes.dm new file mode 100644 index 000000000000..3e47c9d5f1ff --- /dev/null +++ b/monkestation/code/modules/virology/items/mousecubes.dm @@ -0,0 +1,10 @@ +/obj/item/food/monkeycube/mouse + name = "mouse cube" + tastes = list("the sewers" = 1, "cheese" = 1) + food_reagents = list(/datum/reagent/blood = 30) + spawned_mob = /mob/living/basic/mouse + +/obj/item/storage/box/monkeycubes/mousecubes + name = "mouse cube box" + desc = "Drymate brand mouse cubes. Just add water!" + cube_type = /obj/item/food/monkeycube/mouse diff --git a/monkestation/code/modules/virology/items/science_goggles.dm b/monkestation/code/modules/virology/items/science_goggles.dm new file mode 100644 index 000000000000..e8afac4b6692 --- /dev/null +++ b/monkestation/code/modules/virology/items/science_goggles.dm @@ -0,0 +1,74 @@ + +/obj/item/clothing/glasses/science + ///are we toggled + var/toggled = FALSE + +/obj/item/clothing/glasses/science/attack_self(mob/user, modifiers) + . = ..() + playsound(user, 'sound/items/weeoo1.ogg', 50, 1) + to_chat(user, "You turn [src] [toggled ? "Off" : "On"]") + toggled = !toggled + if(!ishuman(user)) + return + var/mob/living/carbon/human/human = user + if(toggled && human.glasses == src) + enable(user) + +/obj/item/clothing/glasses/science/proc/enable(mob/M) + if (toggled) + M.virusView() + + +/obj/item/clothing/glasses/science/proc/disable(mob/M) + M.stopvirusView() + + +/obj/item/clothing/glasses/science/equipped(mob/M, slot) + ..() + if(slot != ITEM_SLOT_EYES) + return + if(toggled) + enable(M) + RegisterSignal(M, COMSIG_MOB_UNEQUIPPED_ITEM, PROC_REF(clear_effects)) + + +/obj/item/clothing/glasses/science/proc/clear_effects(mob/living/source, obj/item/dropped_item) + SIGNAL_HANDLER + if(dropped_item != src) + return + + if (!source.client) + return + disable(source) + UnregisterSignal(source, list(COMSIG_MOB_UNEQUIPPED_ITEM)) + + +/mob/proc/virusView() + if(!client) + return + GLOB.science_goggles_wearers.Add(src) + for (var/obj/item/I in GLOB.infected_items) + if (I.pathogen) + client.images |= I.pathogen + for (var/mob/living/L in GLOB.infected_contact_mobs) + if (L.pathogen) + client.images |= L.pathogen + for (var/obj/effect/pathogen_cloud/C as anything in GLOB.pathogen_clouds) + if (C.pathogen) + client.images |= C.pathogen + for (var/obj/effect/decal/cleanable/C in GLOB.infected_cleanables) + if (C.pathogen) + client.images |= C.pathogen + +/mob/proc/stopvirusView() + if(!client) + return + GLOB.science_goggles_wearers.Remove(src) + for (var/obj/item/I in GLOB.infected_items) + client.images -= I.pathogen + for (var/mob/living/L in GLOB.infected_contact_mobs) + client.images -= L.pathogen + for(var/obj/effect/pathogen_cloud/C as anything in GLOB.pathogen_clouds) + client.images -= C.pathogen + for (var/obj/effect/decal/cleanable/C in GLOB.infected_cleanables) + client.images -= C.pathogen diff --git a/monkestation/code/modules/virology/items/vials.dm b/monkestation/code/modules/virology/items/vials.dm new file mode 100644 index 000000000000..e76b11b6ad94 --- /dev/null +++ b/monkestation/code/modules/virology/items/vials.dm @@ -0,0 +1,18 @@ +/obj/item/reagent_containers/cup/beaker/vial + name = "vial" + icon = 'monkestation/code/modules/virology/icons/items.dmi' + desc = "A small glass vial. Can hold up to 25 units." + icon_state = "vial" + inhand_icon_state = "beaker" + custom_materials = list(/datum/material/glass = 250) + volume = 25 + possible_transfer_amounts = list(5,10,15,25) + fill_icon_thresholds = list(0, 1, 20, 40, 60, 80, 100) + fill_icon = 'monkestation/code/modules/virology/icons/items.dmi' + +/obj/item/storage/box/vials + name = "box of vials" + +/obj/item/storage/box/vials/PopulateContents() + for(var/i in 1 to 7) + new /obj/item/reagent_containers/cup/beaker/vial( src ) diff --git a/monkestation/code/modules/virology/items/virusdish.dm b/monkestation/code/modules/virology/items/virusdish.dm new file mode 100644 index 000000000000..b16989728bc3 --- /dev/null +++ b/monkestation/code/modules/virology/items/virusdish.dm @@ -0,0 +1,301 @@ +///////////////VIRUS DISH/////////////// +GLOBAL_LIST_INIT(virusdishes, list()) +/obj/item/weapon/virusdish + name = "growth dish" + desc = "A petri dish fit to contain viral, bacteriologic, parasitic, or any other kind of pathogenic culture." + icon = 'monkestation/code/modules/virology/icons/virology.dmi' + icon_state = "virusdish" + w_class = WEIGHT_CLASS_SMALL + //sterility = 100//the outside of the dish is sterile. + var/growth = 0 + var/info = "" + var/analysed = FALSE + var/datum/disease/advanced/contained_virus + var/open = FALSE + var/cloud_delay = 8 SECONDS//similar to a mob's breathing + var/last_cloud_time = 0 + var/mob/last_openner + var/takes_left = 2 + +/obj/item/weapon/virusdish/New(loc) + ..() + reagents = new(10) + reagents.my_atom = src + GLOB.virusdishes.Add(src) + + var/list/reagent_change_signals = list( + COMSIG_REAGENTS_ADD_REAGENT, + COMSIG_REAGENTS_NEW_REAGENT, + COMSIG_REAGENTS_REM_REAGENT, + COMSIG_REAGENTS_DEL_REAGENT, + COMSIG_REAGENTS_CLEAR_REAGENTS, + COMSIG_REAGENTS_REACTED, + ) + RegisterSignals(src.reagents, reagent_change_signals, PROC_REF(on_reagent_change)) + +/obj/item/weapon/virusdish/Destroy() + contained_virus = null + STOP_PROCESSING(SSobj, src) + GLOB.virusdishes.Remove(src) + . = ..() + +/* +/obj/item/weapon/virusdish/clean_blood() + ..() + if (open) + contained_virus = null + growth = 0 + update_icon() +*/ + +/obj/item/weapon/virusdish/update_icon() + . = ..() + overlays.len = 0 + if (!contained_virus) + if (open) + icon_state = "virusdish1" + else + icon_state = "virusdish" + return + icon_state = "virusdish-outline" + var/image/I1 = image(icon,src,"virusdish-bottom") + I1.color = contained_virus.color + var/image/I2 = image(icon,src,"pattern-[contained_virus.pattern]") + I2.color = contained_virus.pattern_color + var/image/I3 = image(icon,src,"virusdish-[open?"open":"closed"]") + I3.color = contained_virus.color + overlays += I1 + if (open) + overlays += I3 + I2.alpha = growth*255/200+127 + overlays += I2 + else + overlays += I2 + overlays += I3 + I2.alpha = (growth*255/200+127)*60/100 + overlays += I2 + var/image/I4 = image(icon,src,"virusdish-reflection") + overlays += I4 + if (analysed) + overlays += "virusdish-label" + else if (info != "" && copytext(info, 1, 9) == "OUTDATED") + overlays += "virusdish-outdated" + +/obj/item/weapon/virusdish/attack_hand(mob/living/user, list/modifiers) + ..() + infection_attempt(user) + +/obj/item/weapon/virusdish/attack_self(mob/living/user, list/modifiers) + open = !open + update_appearance() + to_chat(user,span_notice("You [open?"open":"close"] dish's lid.")) + update_desc() + if (open) + last_openner = user + if (contained_virus) + contained_virus.log += "
[ROUND_TIME()] Containment Dish opened by [key_name(user)]." + START_PROCESSING(SSobj, src) + else + if (contained_virus) + contained_virus.log += "
[ROUND_TIME()] Containment Dish closed by [key_name(user)]." + STOP_PROCESSING(SSobj, src) + infection_attempt(user) + +/obj/item/weapon/virusdish/attackby(obj/item/I, mob/living/user, params) + ..() + if(istype(I,/obj/item/hand_labeler)) + return + if(istype(I, /obj/item/reagent_containers/syringe) && takes_left) + takes_left-- + var/obj/item/reagent_containers/syringe/B = I + var/list/data = list("viruses"=null,"blood_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"viruses"=list(),"immunity"=list()) + data["viruses"] |= list(contained_virus) + B.reagents.add_reagent(/datum/reagent/blood, B.volume, data) + to_chat(user, span_notice("You take some blood from the [src]")) + if (open) + if (istype(I,/obj/item/reagent_containers)) + var/success = 0 + var/obj/container = I + if (!container.is_open_container() && istype(container, /obj/item/reagent_containers)) + return + if(I.is_open_container()) + success = I.reagents.trans_to(src, 10, transfered_by = user) + if (success > 0) + to_chat(user, span_notice("You transfer [success] units of the solution to \the [src].")) + return + if((user.istate & ISTATE_HARM) && I.force) + visible_message(span_danger("The virus dish is smashed to bits!")) + shatter(user) + +/obj/item/weapon/virusdish/is_open_container() + return open + +/obj/item/weapon/virusdish/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(.) + return + if(open) + if (istype(target,/obj/item/reagent_containers)) + var/success = 0 + var/obj/container = target + if (!container.is_open_container() && istype(container, /obj/item/reagent_containers)) + return + if(is_open_container()) + success = reagents.trans_to(target, 10, transfered_by = user) + if (success > 0) + to_chat(user, span_notice("You transfer [success] units of the solution to \the [target].")) + return + if(istype(target, /obj/structure/reagent_dispensers)) + var/obj/structure/reagent_dispensers/obj = target + var/success = obj.reagents.trans_to(src, 10, transfered_by = user) + if (success > 0) + to_chat(user, span_notice("You transfer [success] units of the solution to \the [src].")) + + if (istype(target,/obj/structure/toilet)) + var/obj/structure/toilet/T = target + if (T.open) + empty(user,target) + if (istype(target,/obj/structure/urinal)||istype(target,/obj/structure/sink)) + empty(user,target) + +/obj/item/weapon/virusdish/proc/empty(mob/user, atom/target) + if (user && target) + to_chat(user,span_notice("You empty \the [src]'s reagents into \the [target].")) + reagents.clear_reagents() + +/obj/item/weapon/virusdish/process() + if (!contained_virus || !(contained_virus.spread_flags & DISEASE_SPREAD_AIRBORNE)) + STOP_PROCESSING(SSobj, src) + return + if(world.time - last_cloud_time >= cloud_delay) + last_cloud_time = world.time + var/list/L = list() + L += contained_virus + new /obj/effect/pathogen_cloud/core(get_turf(src), last_openner, virus_copylist(L), FALSE) + +/obj/item/weapon/virusdish/random + name = "growth dish" +/obj/item/weapon/virusdish/random/New(loc) + ..(loc) + if (loc)//because fuck you /datum/subsystem/supply_shuttle/Initialize() + var/virus_choice = pick(subtypesof(/datum/disease/advanced)- typesof(/datum/disease/advanced/premade)) + contained_virus = new virus_choice + var/list/anti = list( + ANTIGEN_BLOOD = 2, + ANTIGEN_COMMON = 2, + ANTIGEN_RARE = 1, + ANTIGEN_ALIEN = 0, + ) + var/list/bad = list( + EFFECT_DANGER_HELPFUL = 1, + EFFECT_DANGER_FLAVOR = 2, + EFFECT_DANGER_ANNOYING = 2, + EFFECT_DANGER_HINDRANCE = 2, + EFFECT_DANGER_HARMFUL = 2, + EFFECT_DANGER_DEADLY = 0, + ) + contained_virus.makerandom(list(50,90),list(10,100),anti,bad,src) + growth = rand(5, 50) + name = "growth dish (Unknown [contained_virus.form])" + update_appearance() + contained_virus.origin = "Random Dish" + else + GLOB.virusdishes.Remove(src) + +/obj/item/weapon/virusdish/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) + ..() + if(isturf(hit_atom)) + visible_message(span_danger("The virus dish shatters on impact!")) + shatter(throwingdatum.thrower) + +/obj/item/weapon/virusdish/proc/incubate(mutatechance=5, growthrate=3, effect_focus = 0) + if (contained_virus) + if(reagents.remove_reagent(/datum/reagent/consumable/virus_food, 0.2)) + growth = min(growth + growthrate, 100) + if(reagents.remove_reagent(/datum/reagent/water, 0.2)) + growth = max(growth - growthrate, 0) + contained_virus.incubate(src,mutatechance,effect_focus) + +/obj/item/weapon/virusdish/proc/on_reagent_change(datum/reagents/reagents) + SIGNAL_HANDLER + + if (contained_virus) + var/datum/reagent/blood/blood = locate() in reagents.reagent_list + if (blood) + var/list/L = list() + L |= contained_virus + blood.data["diseases"] |= filter_disease_by_spread(L, required = DISEASE_SPREAD_BLOOD) + +/obj/item/weapon/virusdish/proc/shatter(mob/user) + var/obj/effect/decal/cleanable/virusdish/dish = new(get_turf(src)) + dish.pixel_x = pixel_x + dish.pixel_y = pixel_y + if (contained_virus) + dish.contained_virus = contained_virus.Copy() + dish.last_openner = key_name(user) + src.transfer_fingerprints_to(dish) + playsound(get_turf(src), "shatter", 70, 1) + var/image/I1 + var/image/I2 + if (contained_virus) + I1 = image(icon,src,"brokendish-color") + I1.color = contained_virus.color + I2 = image(icon,src,"pattern-[contained_virus.pattern]b") + I2.color = contained_virus.pattern_color + else + I1 = image(icon,src,"brokendish") + dish.overlays += I1 + if (contained_virus) + dish.overlays += I2 + contained_virus.log += "
[ROUND_TIME()] Containment Dish shattered by [key_name(user)]." + if (contained_virus.spread_flags & DISEASE_SPREAD_AIRBORNE) + var/strength = contained_virus.infectionchance + var/list/L = list() + L += contained_virus + while (strength > 0) + new /obj/effect/pathogen_cloud/core(get_turf(src), last_openner, virus_copylist(L), FALSE) + strength -= 40 + qdel(src) + +/obj/item/weapon/virusdish/update_desc(updates) + . = ..() + desc = initial(desc) + if(open) + desc += "\nIts lid is open!" + else + desc += "\nIts lid is closed!" + if(info) + desc += "\nThere is a sticker with some printed information on it. (Read it)" + + +/obj/item/weapon/virusdish/Topic(href, href_list) + if(..()) + return TRUE + if(href_list["examine"]) + var/datum/browser/popup = new(usr, "\ref[src]", name, 600, 300, src) + popup.set_content(info) + popup.open() + +/obj/item/weapon/virusdish/infection_attempt(mob/living/perp, datum/disease/D) + if (open)//If the dish is open, we may get infected by the disease inside on top of those that might be stuck on it. + var/block = 0 + var/bleeding = 0 + if (src in perp.held_items) + block = perp.check_contact_sterility(BODY_ZONE_ARMS) + bleeding = perp.check_bodypart_bleeding(BODY_ZONE_ARMS) + if (!block && (contained_virus.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)) + perp.infect_disease(contained_virus, notes="(Contact, from picking up \a [src])") + else if (bleeding && (contained_virus.spread_flags & DISEASE_SPREAD_BLOOD)) + perp.infect_disease(contained_virus, notes="(Blood, from picking up \a [src])") + else if (isturf(loc) && loc == perp.loc)//is our perp standing over the open dish? + if (perp.body_position & LYING_DOWN) + block = perp.check_contact_sterility(BODY_ZONE_EVERYTHING) + bleeding = perp.check_bodypart_bleeding(BODY_ZONE_EVERYTHING) + else + block = perp.check_contact_sterility(BODY_ZONE_LEGS) + bleeding = perp.check_bodypart_bleeding(BODY_ZONE_LEGS) + if (!block && (contained_virus.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)) + perp.infect_disease(contained_virus, notes="(Contact, from [(perp.body_position & LYING_DOWN)?"lying":"standing"] over a virus dish[last_openner ? " opened by [key_name(last_openner)]" : ""])") + else if (bleeding && (contained_virus.spread_flags & DISEASE_SPREAD_BLOOD)) + perp.infect_disease(contained_virus, notes="(Blood, from [(perp.body_position & LYING_DOWN)?"lying":"standing"] over a virus dish[last_openner ? " opened by [key_name(last_openner)]" : ""])") + ..(perp,D) diff --git a/monkestation/code/modules/virology/living/infection_procs.dm b/monkestation/code/modules/virology/living/infection_procs.dm new file mode 100644 index 000000000000..1d92e627239e --- /dev/null +++ b/monkestation/code/modules/virology/living/infection_procs.dm @@ -0,0 +1,140 @@ +/mob/living/proc/find_nearby_disease()//only tries to find Contact and Blood spread diseases. Airborne ones are handled by breath_airborne_diseases() + if(buckled)//Riding a vehicle? + return + if(HAS_TRAIT(src, TRAIT_MOVE_FLYING))//Flying? + return + + var/turf/T = get_turf(src) + + //Virus Dishes aren't toys, handle with care, especially when they're open. + for(var/obj/effect/decal/cleanable/virusdish/dish in T) + dish.infection_attempt(src) + + for(var/obj/item/weapon/virusdish/dish in T) + if (dish.open && dish.contained_virus) + dish.infection_attempt(src, dish.contained_virus) + + var/obj/item/weapon/virusdish/dish = locate() in held_items + if (dish && dish.open && dish.contained_virus) + dish.infection_attempt(src, dish.contained_virus) + + //Now to check for stuff that's on the floor + var/block = 0 + var/bleeding = 0 + if (body_position == LYING_DOWN) + block = check_contact_sterility(BODY_ZONE_EVERYTHING) + bleeding = check_bodypart_bleeding(BODY_ZONE_EVERYTHING) + else + block = check_contact_sterility(BODY_ZONE_LEGS) + bleeding = check_bodypart_bleeding(BODY_ZONE_LEGS) + + + var/static/list/viral_cleanable_types = list( + /obj/effect/decal/cleanable/blood, + /obj/effect/decal/cleanable/vomit, + ) + + for(var/obj/effect/decal/cleanable/C in T) + if (is_type_in_list(C,viral_cleanable_types)) + assume_contact_diseases(C.diseases, C, block, bleeding) + + return FALSE + +//This one is used for one-way infections, such as getting splashed with someone's blood due to clobbering them to death +/mob/living/proc/oneway_contact_diseases(mob/living/carbon/L, block=0, bleeding=0) + assume_contact_diseases(L.diseases,L,block,bleeding) + +//This one is used for two-ways infections, such as hand-shakes, hugs, punches, people bumping into each others, etc +/mob/living/proc/share_contact_diseases(mob/living/carbon/L, block=0, bleeding=0) + L.assume_contact_diseases(diseases ,src,block,bleeding) + assume_contact_diseases(L.diseases, L, block, bleeding) + +///////////////////////DISEASE STUFF/////////////////////////////////////////////////////////////////// + +//Blocked is whether clothing prevented the spread of contact/blood +/mob/living/proc/assume_contact_diseases(list/disease_list, atom/source, blocked=0, bleeding=0) + if (istype(disease_list) && disease_list.len > 0) + for(var/datum/disease/advanced/V as anything in disease_list) + if (!V) + message_admins("[key_name(src)] is trying to assume contact diseases from touching \a [source], but the disease_list contains an ID ([V.uniqueID]-[V.subID]) that isn't associated to an actual disease datum! Ping Dwasint about it please.") + return + if(!blocked && V.spread_flags & DISEASE_SPREAD_CONTACT_SKIN) + infect_disease(V, notes="(Contact, from [source])") + /* + else if(suitable_colony() && V.spread & SPREAD_COLONY) + infect_disease(V, notes="(Colonized, from [source])") + */ + else if(!blocked && bleeding && (V.spread_flags & DISEASE_SPREAD_BLOOD)) + infect_disease(V, notes="(Blood, from [source])") + +//Called in Life() by humans (in handle_breath.dm), monkeys and mice +/mob/living/proc/breath_airborne_diseases()//only tries to find Airborne spread diseases. Blood and Contact ones are handled by find_nearby_disease() + if (!check_airborne_sterility() && isturf(loc))//checking for sterile mouth protections + breath_airborne_diseases_from_clouds() + + var/turf/T = get_turf(src) + var/list/breathable_cleanable_types = list( + /obj/effect/decal/cleanable/blood, + /obj/effect/decal/cleanable/vomit, + ) + + for(var/obj/effect/decal/cleanable/C in T) + if (is_type_in_list(C,breathable_cleanable_types)) + if(istype(C.diseases,/list) && C.diseases.len > 0) + for(var/datum/disease/advanced/V as anything in C.diseases) + if(V.spread_flags & DISEASE_SPREAD_AIRBORNE) + infect_disease(V, notes="(Airborne, from [C])") + /* + for(var/obj/effect/rune/R in T) + if(istype(R.virus2,/list) && R.virus2.len > 0) + for(var/datum/disease/advanced/V as anything in R.diseases) + if(V.spread_flags & DISEASE_SPREAD_AIRBORNE) + infect_disease(V, notes="(Airborne, from [R])") + */ + + spawn (1) + //we don't want the rest of the mobs to start breathing clouds before they've settled down + //otherwise it can produce exponential amounts of lag if many mobs are in an enclosed space + spread_airborne_diseases() + +/mob/living/proc/breath_airborne_diseases_from_clouds() + for(var/turf/T in range(1, src)) + var/sanity = 0 + for(var/obj/effect/pathogen_cloud/cloud in T.contents) + if(sanity > 10) + break + sanity++ //anything more than 10 and you aint getting air really + if (!cloud.sourceIsCarrier || cloud.source != src || cloud.modified) + if (Adjacent(cloud)) + for (var/datum/disease/advanced/V in cloud.viruses) + //if (V.spread & SPREAD_AIRBORNE) //Anima Syndrome allows for clouds of non-airborne viruses + infect_disease(V, notes="(Airborne, from a pathogenic cloud[cloud.source ? " created by [key_name(cloud.source)]" : ""])") + +/mob/living/proc/handle_virus_updates(seconds_per_tick) + if(status_flags & GODMODE) + return 0 + + find_nearby_disease()//getting diseases from blood/mucus/vomit splatters and open dishes + + activate_diseases(seconds_per_tick) + +/mob/living/proc/activate_diseases(seconds_per_tick) + if (length(diseases)) + var/active_disease = pick(diseases)//only one disease will activate its effects at a time. + for (var/datum/disease/advanced/V as anything in diseases) + if(istype(V)) + V.activate(src, active_disease != V, seconds_per_tick) + + if(HAS_TRAIT(src, TRAIT_IRRADIATED)) + if (prob(50))//radiation turns your body into an inefficient pathogenic incubator. + V.incubate(src, 1) + //effect mutations won't occur unless the mob also has ingested mutagen + //and even if they occur, the new effect will have a badness similar to the old one, so helpful pathogen won't instantly become deadly ones. + +/mob/living/proc/try_contact_infect(datum/disease/advanced/D, zone = BODY_ZONE_EVERYTHING, note = "Try Contact Infect") + if(!(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)) + return + var/block = check_contact_sterility(zone) + if(block) + infect_disease(D, notes = note) + diff --git a/monkestation/code/modules/virology/living/mouse.dm b/monkestation/code/modules/virology/living/mouse.dm new file mode 100644 index 000000000000..c374939b1217 --- /dev/null +++ b/monkestation/code/modules/virology/living/mouse.dm @@ -0,0 +1,56 @@ +/mob/living/basic/mouse + var/disease_chance = 50 + +/mob/living/basic/mouse/Initialize(mapload, tame, new_body_color) + . = ..() + immune_system = new(src) + + if(prob(disease_chance)) + var/virus_choice = pick(subtypesof(/datum/disease/advanced)- typesof(/datum/disease/advanced/premade)) + var/list/anti = list( + ANTIGEN_BLOOD = 2, + ANTIGEN_COMMON = 2, + ANTIGEN_RARE = 1, + ANTIGEN_ALIEN = 0, + ) + var/list/bad = list( + EFFECT_DANGER_HELPFUL = 1, + EFFECT_DANGER_FLAVOR = 2, + EFFECT_DANGER_ANNOYING = 2, + EFFECT_DANGER_HINDRANCE = 2, + EFFECT_DANGER_HARMFUL = 2, + EFFECT_DANGER_DEADLY = 0, + ) + var/datum/disease/advanced/disease = new virus_choice + disease.makerandom(list(50,90),list(10,100),anti,bad,src) + diseases = list() + diseases += disease + disease.after_add() + src.med_hud_set_status() + disease.log += "
[ROUND_TIME()] Infected [src]" + + log_virus("[key_name(src)] was infected by virus: [disease.admin_details()] at [loc_name(loc)]") + disease.origin = "Rat" + disease.AddToGoggleView(src) + +/mob/living/basic/mouse/Destroy() + . = ..() + if(src in GLOB.infected_contact_mobs) + GLOB.infected_contact_mobs -= src + QDEL_NULL(immune_system) + +/mob/living/basic/mouse/attackby(obj/item/attacking_item, mob/living/user, params) + . = ..() + if(!istype(attacking_item, /obj/item/reagent_containers/syringe)) + return + if(!do_after(user, 1.5 SECONDS, src)) + return + var/obj/item/reagent_containers/syringe/I = attacking_item + var/list/data = list("viruses"=null,"blood_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"viruses"=list(),"immunity"=list()) + data["viruses"] |= diseases + data["immunity"] = immune_system.GetImmunity() + I.reagents.add_reagent(/datum/reagent/blood, I.volume, data) + +/mob/living/basic/mouse/Life(seconds_per_tick, times_fired) + . = ..() + handle_virus_updates(seconds_per_tick) diff --git a/monkestation/code/modules/virology/living/spread_disease.dm b/monkestation/code/modules/virology/living/spread_disease.dm new file mode 100644 index 000000000000..fce3a82fef0e --- /dev/null +++ b/monkestation/code/modules/virology/living/spread_disease.dm @@ -0,0 +1,89 @@ +/mob/living + ///our immune system + var/datum/immune_system/immune_system + +/atom + ///image + var/image/pathogen + +/mob/living/proc/spread_airborne_diseases() + //spreading our own airborne viruses + if (diseases && diseases.len > 0) + var/list/airborne_viruses = filter_disease_by_spread(diseases, required = DISEASE_SPREAD_AIRBORNE) + if (airborne_viruses && airborne_viruses.len > 0) + var/strength = 0 + for (var/datum/disease/advanced/V as anything in airborne_viruses) + strength += V.infectionchance + strength = round(strength/airborne_viruses.len) + while (strength > 0)//stronger viruses create more clouds at once + new /obj/effect/pathogen_cloud/core(get_turf(src), src, airborne_viruses) + strength -= 40 + +/mob/living/carbon/infect_disease(datum/disease/advanced/disease, forced = FALSE, notes = "", decay = TRUE) + if(!istype(disease)) + return FALSE + + if(!(disease.infectable_biotypes & mob_biotypes)) + return + + if(!disease.spread_flags) + return FALSE + + for(var/datum/disease/advanced/D as anything in diseases) + if("[disease.uniqueID]-[disease.subID]" == "[D.uniqueID]-[D.subID]") // child ids are for pathogenic mutations and aren't accounted for as thats fucked. + return FALSE + + if(immune_system && !immune_system.CanInfect(disease)) + return FALSE + + if(prob(disease.infectionchance) || forced) + var/datum/disease/advanced/D = disease.Copy() + if (D.infectionchance > 10) + D.infectionchance = max(10, D.infectionchance - 10)//The virus gets weaker as it jumps from people to people + + D.stage = clamp(D.stage+D.stage_variance, 1, D.max_stages) + D.log += "
[ROUND_TIME()] Infected [key_name(src)] [notes]. Infection chance now [D.infectionchance]%" + + LAZYADD(diseases, D) + D.affected_mob = src + //SSdisease.active_diseases += D + D.after_add() + src.med_hud_set_status() + + log_virus("[key_name(src)] was infected by virus: [D.admin_details()] at [loc_name(loc)]") + add_event_to_buffer(src, data = "was infected by virus: [D.admin_details()] at [loc_name(loc)] Full Log: [D.log]", log_key = "VIRUS") + + D.AddToGoggleView(src) + return TRUE + +/mob/dead/new_player/proc/DiseaseCarrierCheck(mob/living/carbon/human/H) + if(world.time < SSautotransfer.starttime + 30 MINUTES) + return + // 10% of players are joining the station with some minor disease if latejoined + if(prob(10)) + var/virus_choice = pick(subtypesof(/datum/disease/advanced)- typesof(/datum/disease/advanced/premade)) + var/datum/disease/advanced/D = new virus_choice + + var/list/anti = list( + ANTIGEN_BLOOD = 1, + ANTIGEN_COMMON = 1, + ANTIGEN_RARE = 0, + ANTIGEN_ALIEN = 0, + ) + var/list/bad = list( + EFFECT_DANGER_HELPFUL = 1, + EFFECT_DANGER_FLAVOR = 4, + EFFECT_DANGER_ANNOYING = 4, + EFFECT_DANGER_HINDRANCE = 0, + EFFECT_DANGER_HARMFUL = 0, + EFFECT_DANGER_DEADLY = 0, + ) + + D.makerandom(list(30,55),list(0,50),anti,bad,null) + + D.log += "
[ROUND_TIME()] Infected [key_name(H)]" + if(!length(H.diseases)) + H.diseases = list() + H.diseases += D + + D.AddToGoggleView(H) diff --git a/monkestation/code/modules/virology/living/sterile_procs.dm b/monkestation/code/modules/virology/living/sterile_procs.dm new file mode 100644 index 000000000000..b69e41e7d8f1 --- /dev/null +++ b/monkestation/code/modules/virology/living/sterile_procs.dm @@ -0,0 +1,91 @@ +/mob/living/proc/check_contact_sterility(body_part) + return 0 + +/mob/living/carbon/human/check_contact_sterility(body_part) + var/list/clothing_to_check = list( + wear_mask, + w_uniform, + head, + wear_suit, + back, + gloves, + handcuffed, + legcuffed, + belt, + shoes, + wear_mask, + glasses, + ears, + wear_id) + + var/list/checks = list(body_part) + if(body_part == BODY_ZONE_EVERYTHING) + checks = list(BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_R_LEG, BODY_ZONE_HEAD) + if(body_part == BODY_ZONE_LEGS) + checks = list(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) + if(body_part == BODY_ZONE_ARMS) + checks = list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM) + + for(var/item in checks) + for (var/thing in clothing_to_check) + var/obj/item/cloth = thing + if(isnull(cloth)) + continue + var/list/coverage = cover_flags2body_zones(cloth.body_parts_covered) + if((item in coverage) && prob(cloth.get_armor_rating(BIO))) + return TRUE + return FALSE + + +/mob/living/proc/check_bodypart_bleeding(zone) + return FALSE + +/mob/living/carbon/human/check_bodypart_bleeding(zone) + var/bleeding = FALSE + var/list/clothing_to_check = list( + wear_mask, + w_uniform, + head, + wear_suit, + back, + gloves, + handcuffed, + legcuffed, + belt, + shoes, + wear_mask, + glasses, + ears, + wear_id) + var/list/checks = list(zone) + if(zone == BODY_ZONE_EVERYTHING) + checks = list(BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_R_ARM, BODY_ZONE_HEAD) + if(zone == BODY_ZONE_LEGS) + checks = list(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) + if(zone == BODY_ZONE_ARMS) + checks = list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM) + + for(var/item in checks) + var/obj/item/bodypart/bodypart = get_bodypart(item) + if(!bodypart) + return FALSE + for (var/thing in clothing_to_check) + var/obj/item/cloth = thing + if(!cloth) + if(bodypart.get_modified_bleed_rate()) + return TRUE + else if(istype(cloth) && (cloth.body_parts_covered & body_zone2cover_flags(item)) && !prob(cloth.get_armor_rating(BIO))) + if(bodypart.get_modified_bleed_rate()) + return TRUE + return bleeding + +/mob/living/proc/check_airborne_sterility() + return 0 + +/mob/living/carbon/human/check_airborne_sterility() + var/block = FALSE + if (wear_mask && (wear_mask.flags_cover & MASKCOVERSMOUTH) && prob(wear_mask.get_armor_rating(BIO))) + block = TRUE + if (head && (head.flags_cover & HEADCOVERSMOUTH) && prob(head.get_armor_rating(BIO))) + block = TRUE + return block diff --git a/monkestation/code/modules/virology/machines/analyzer.dm b/monkestation/code/modules/virology/machines/analyzer.dm new file mode 100644 index 000000000000..9e894d64d119 --- /dev/null +++ b/monkestation/code/modules/virology/machines/analyzer.dm @@ -0,0 +1,215 @@ +/obj/machinery/disease2/diseaseanalyser + name = "disease analyzer" + desc = "For analysing pathogenic dishes of sufficient growth." + icon = 'monkestation/code/modules/virology/icons/virology.dmi' + icon_state = "analyser" + anchored = TRUE + density = TRUE + light_color = "#6496FA" + light_outer_range = 2 + light_power = 1 + + circuit = /obj/item/circuitboard/machine/diseaseanalyser + + idle_power_usage = 100 + active_power_usage = 100//1000 extra power once per analysis + + var/process_time = 5 + var/minimum_growth = 100 + var/obj/item/weapon/virusdish/dish = null + var/last_scan_name = "" + var/last_scan_info = "" + + var/mob/scanner = null + +/obj/machinery/disease2/diseaseanalyser/RefreshParts() + . = ..() + var/scancount = 0 + for(var/datum/stock_part/scanning_module/M in component_parts) + scancount += M.tier + minimum_growth = round((initial(minimum_growth) - (scancount * 6))) + +/obj/machinery/disease2/diseaseanalyser/attackby(obj/item/I, mob/living/user, params) + . = ..() + + if(machine_stat & (BROKEN)) + to_chat(user, span_warning("\The [src] is broken. Some components will have to be replaced before it can work again.")) + return + + if (scanner) + to_chat(user, span_warning("\The [scanner] is currently busy using this analyzer.")) + return + + if(.) + return + + if (dish) + if (istype(I,/obj/item/weapon/virusdish)) + to_chat(user, span_warning("There is already a dish in there. Alt+Click or perform the analysis to retrieve it first.")) + else if (istype(I,/obj/item/reagent_containers)) + dish.attackby(I,user) + else + if (istype(I,/obj/item/weapon/virusdish)) + var/obj/item/weapon/virusdish/D = I + if (D.open) + visible_message(span_notice("\The [user] inserts \the [I] in \the [src]."),span_notice("You insert \the [I] in \the [src].")) + playsound(loc, 'sound/machines/click.ogg', 50, 1) + user.dropItemToGround(I, TRUE) + I.forceMove(src) + dish = I + update_appearance() + else + to_chat(user, span_warning("You must open the dish's lid before it can be analysed. Be sure to wear proper protection first (at least a sterile mask and latex gloves).")) + +/obj/machinery/disease2/diseaseanalyser/attack_hand(mob/user) + . = ..() + if(machine_stat & (BROKEN)) + to_chat(user, span_notice("\The [src] is broken. Some components will have to be replaced before it can work again.")) + return + if(machine_stat & (NOPOWER)) + to_chat(user, span_notice("Deprived of power, \the [src] is unresponsive.")) + if (dish) + playsound(loc, 'sound/machines/click.ogg', 50, 1) + dish.forceMove(loc) + dish = null + update_appearance() + return + + if(.) + return + + if (scanner) + to_chat(user, span_warning("\The [scanner] is currently busy using this analyzer.")) + return + + if (!dish) + to_chat(user, span_notice("Place an open growth dish first to analyse its pathogen.")) + return + + if (dish.growth < minimum_growth) + say("Pathogen growth insufficient. Minimal required growth: [minimum_growth]%.") + to_chat(user,span_notice("Add some virus food to the dish and incubate.")) + if (minimum_growth == 100) + to_chat(user,span_notice("Replacing the machine's scanning modules with better parts will lower the growth requirement.")) + dish.forceMove(loc) + dish = null + update_appearance() + return + + scanner = user + icon_state = "analyser_processing" + flick("analyser_turnon",src) + + spawn (1) + var/mutable_appearance/I = mutable_appearance(icon,"analyser_light",src) + I.plane = ABOVE_LIGHTING_PLANE + add_overlay(I) + use_power(1000) + set_light(2,2) + playsound(src, 'sound/machines/chime.ogg', 50) + + if(do_after(user, 5 SECONDS, src)) + if(machine_stat & (BROKEN|NOPOWER)) + return + if(!istype(dish.contained_virus, /datum/disease/advanced)) + QDEL_NULL(dish) + say("ERROR:Bad Pathogen detected PURGING") + if (dish.contained_virus.addToDB()) + say("Added new pathogen to database.") + var/datum/data/record/v = GLOB.virusDB["[dish.contained_virus.uniqueID]-[dish.contained_virus.subID]"] + dish.info = dish.contained_virus.get_info() + dish.update_desc() + last_scan_name = dish.contained_virus.name(TRUE) + if (v) + last_scan_name += v.fields["nickname"] ? " \"[v.fields["nickname"]]\"" : "" + + dish.name = "growth dish ([last_scan_name])" + last_scan_info = dish.info + var/datum/browser/popup = new(user, "\ref[dish]", dish.name, 600, 500, src) + popup.set_content(dish.info) + popup.open() + dish.analysed = TRUE + dish.update_appearance() + dish.forceMove(loc) + dish = null + else + playsound(src, 'sound/machines/buzz-sigh.ogg', 25) + + update_appearance() + flick("analyser_turnoff",src) + scanner = null + +/obj/machinery/disease2/diseaseanalyser/update_icon() + . = ..() + icon_state = "analyser" + + if (machine_stat & (NOPOWER)) + icon_state = "analyser0" + + if (machine_stat & (BROKEN)) + icon_state = "analyserb" + + if(machine_stat & (BROKEN|NOPOWER)) + set_light(0) + else + set_light(2,1) + + +/obj/machinery/disease2/diseaseanalyser/update_overlays() + . = ..() + if (dish) + .+= mutable_appearance(icon, "smalldish-outline",src) + if (dish.contained_virus) + var/mutable_appearance/I = mutable_appearance(icon,"smalldish-color",src) + I.color = dish.contained_virus.color + .+= I + else + .+= mutable_appearance(icon, "smalldish-empty",src) + +/obj/machinery/disease2/diseaseanalyser/verb/PrintPaper() + set name = "Print last analysis" + set category = "Object" + set src in oview(1) + + if(!usr || !isturf(usr.loc)) + return + + if(machine_stat & (BROKEN)) + to_chat(usr, span_notice("\The [src] is broken. Some components will have to be replaced before it can work again.")) + return + if(machine_stat & (NOPOWER)) + to_chat(usr, span_notice("Deprived of power, \the [src] is unresponsive.")) + return + + var/turf/T = get_turf(src) + playsound(T, "sound/effects/fax.ogg", 50, 1) + flick_overlay("analyser-paper", src) + visible_message("\The [src] prints a sheet of paper.") + spawn(1 SECONDS) + var/obj/item/paper/P = new(T) + P.name = last_scan_name + P.add_raw_text(last_scan_info) + P.pixel_x = 8 + P.pixel_y = -8 + P.update_appearance() + +/obj/machinery/disease2/diseaseanalyser/process() + if(machine_stat & (NOPOWER|BROKEN)) + scanner = null + return + + if (scanner && !(scanner in range(src,1))) + update_appearance() + flick("analyser_turnoff",src) + scanner = null + + +/obj/machinery/disease2/diseaseanalyser/AltClick() + if((!usr.Adjacent(src) || usr.incapacitated())) + return ..() + + if (dish && !scanner) + playsound(loc, 'sound/machines/click.ogg', 50, 1) + dish.forceMove(loc) + dish = null + update_appearance() diff --git a/monkestation/code/modules/virology/machines/centrifuge.dm b/monkestation/code/modules/virology/machines/centrifuge.dm new file mode 100644 index 000000000000..a64776a50cf0 --- /dev/null +++ b/monkestation/code/modules/virology/machines/centrifuge.dm @@ -0,0 +1,480 @@ +#define CENTRIFUGE_LIGHTSPECIAL_OFF 0 +#define CENTRIFUGE_LIGHTSPECIAL_BLINKING 1 +#define CENTRIFUGE_LIGHTSPECIAL_ON 2 + + +/obj/machinery/disease2/centrifuge + name = "isolation centrifuge" + desc = "Used to isolate pathogen and antibodies in blood. Make sure to keep the vials balanced when spinning for optimal efficiency." + icon = 'monkestation/code/modules/virology/icons/virology.dmi' + icon_state = "centrifuge" + density = TRUE + anchored = TRUE + + var/datum/browser/popup = null + + var/on = 0 + + var/list/vials = list(null,null,null,null) + var/list/vial_valid = list(0,0,0,0) + var/list/vial_task = list( + list(0,0,0,0,0,), + list(0,0,0,0,0,), + list(0,0,0,0,0,), + list(0,0,0,0,0,), + ) + + light_color = "#8DC6E9" + light_outer_range = 2 + light_power = 1 + + circuit = /obj/item/circuitboard/machine/centrifuge + + idle_power_usage = 100 + active_power_usage = 300 + + var/base_efficiency = 1 + var/upgrade_efficiency = 0.3 // the higher, the better will upgrade affect efficiency + + var/efficiency = 1 + + var/special = CENTRIFUGE_LIGHTSPECIAL_OFF + +/obj/machinery/disease2/centrifuge/New() + . = ..() + + RefreshParts() + +/obj/machinery/disease2/centrifuge/RefreshParts() + . = ..() + var/manipcount = 0 + for(var/datum/stock_part/manipulator/M in component_parts) + manipcount += M.tier + base_efficiency = 1 + upgrade_efficiency * (manipcount-2) + + +/obj/machinery/disease2/centrifuge/attackby(obj/item/I, mob/living/user, params) + . = ..() + + if(machine_stat & (BROKEN)) + to_chat(user, span_warning("\The [src] is broken. Some components will have to be replaced before it can work again.") ) + return FALSE + + if(.) + return + + if (istype(I, /obj/item/reagent_containers/cup/beaker/vial)) + special = CENTRIFUGE_LIGHTSPECIAL_OFF + if (on) + to_chat(user,span_warning("You cannot add or remove vials while the centrifuge is active. Turn it Off first.") ) + return + var/obj/item/reagent_containers/cup/beaker/vial/vial = I + for (var/i = 1 to vials.len) + if(!vials[i]) + vials[i] = vial + vial_valid[i] = vial_has_antibodies(vial) + visible_message(span_notice("\The [user] adds \the [vial] to \the [src]."),span_notice("You add \the [vial] to \the [src].")) + playsound(loc, 'sound/machines/click.ogg', 50, 1) + user.transferItemToLoc(vial, loc) + vial.forceMove(src) + update_appearance() + updateUsrDialog() + return TRUE + + to_chat(user,span_warning("There is no room for more vials.") ) + return FALSE + + +/obj/machinery/disease2/centrifuge/proc/vial_has_antibodies(obj/item/reagent_containers/cup/beaker/vial/vial) + if (!vial) + return FALSE + + var/datum/reagent/blood/blood = locate() in vial.reagents.reagent_list + if (blood && blood.data && blood.data["immunity"]) + var/list/immune_system = blood.data["immunity"] + if (istype(immune_system) && immune_system.len > 0) + var/list/antibodies = immune_system[2] + for (var/antibody in antibodies) + if (antibodies[antibody] >= 30) + return TRUE + +//Also handles luminosity +/obj/machinery/disease2/centrifuge/update_icon() + . = ..() + icon_state = "centrifuge" + + if (machine_stat & (NOPOWER)) + icon_state = "centrifuge0" + + if (machine_stat & (BROKEN)) + icon_state = "centrifugeb" + + if(machine_stat & (BROKEN|NOPOWER)) + set_light(0) + else + if (on) + icon_state = "centrifuge_moving" + set_light(2,2) + else + set_light(2,1) + +/obj/machinery/disease2/centrifuge/update_overlays() + . = ..() + if(!(machine_stat & (BROKEN|NOPOWER))) + if(on) + var/mutable_appearance/centrifuge_light = emissive_appearance(icon,"centrifuge_light",src) + .+= centrifuge_light + var/mutable_appearance/centrifuge_glow = emissive_appearance(icon,"centrifuge_glow",src) + centrifuge_glow.blend_mode = BLEND_ADD + .+= centrifuge_glow + var/mutable_appearance/centrifuge_light_n = mutable_appearance(icon,"centrifuge_light",src) + .+= centrifuge_light_n + var/mutable_appearance/centrifuge_glow_n = mutable_appearance(icon,"centrifuge_glow",src) + centrifuge_glow.blend_mode = BLEND_ADD + .+= centrifuge_glow_n + + switch (special) + if (CENTRIFUGE_LIGHTSPECIAL_BLINKING) + .+= emissive_appearance(icon,"centrifuge_special_update",src) + .+= mutable_appearance(icon,"centrifuge_special_update",src) + special = CENTRIFUGE_LIGHTSPECIAL_ON + if (CENTRIFUGE_LIGHTSPECIAL_ON) + .+= emissive_appearance(icon,"centrifuge_special",src) + .+= mutable_appearance(icon,"centrifuge_special_update",src) + + for (var/i = 1 to vials.len) + if(vials[i]) + var/obj/item/reagent_containers/cup/beaker/vial/vial = vials[i] + .+= mutable_appearance(icon, "centrifuge_vial[i][on ? "_moving" : ""]",src) + if(vial.reagents.total_volume) + var/mutable_appearance/filling = mutable_appearance(icon, "centrifuge_vial[i]_filling[on ? "_moving" : ""]",src) + filling.icon += mix_color_from_reagents(vial.reagents.reagent_list) + .+= filling + +/obj/machinery/disease2/centrifuge/proc/add_vial_dat(obj/item/reagent_containers/cup/beaker/vial/vial, list/vial_task = list(0,0,0,0,0), slot = 1) + var/dat = "" + var/valid = vial_valid[slot] + + var/datum/reagent/blood/blood = locate() in vial.reagents.reagent_list + if (!blood) + var/datum/reagent/vaccine/vaccine = locate() in vial.reagents.reagent_list + if (!vaccine) + dat += "[vial.name] (no blood detected)" + else + var/vaccines = "" + for (var/A in vaccine.data["antigen"]) + vaccines += "[A]" + if (vaccines == "") + vaccines = "blank" + dat += "[vial.name] (Vaccine ([vaccines]))" + else + if (vial_task[1]) + switch (vial_task[1]) + if ("dish") + var/target = vial_task[2] + var/progress = vial_task[3] + dat += "[vial.name] (isolating [target]: [round(progress)]%) X" + if ("vaccine") + var/target = vial_task[2] + var/progress = vial_task[3] + dat += "[vial.name] (synthesizing vaccine ([target]): [round(progress)]%) X" + + else + if(blood.data && blood.data["viruses"]) + var/list/blood_diseases = blood.data["viruses"] + if (blood_diseases && blood_diseases.len > 0) + dat += "[vial.name] (pathogen detected) ISOLATE TO DISH [valid ? "SYNTHESIZE VACCINE" : "(not enough antibodies for a vaccine)"]" + else + dat += "[vial.name] (no pathogen detected) [valid ? "SYNTHESIZE VACCINE" : "(not enough antibodies for a vaccine)"]" + return dat + +/obj/machinery/disease2/centrifuge/attack_hand(mob/user, list/modifiers) + . = ..() + if(machine_stat & (BROKEN)) + to_chat(user, span_notice("\The [src] is broken. Some components will have to be replaced before it can work again.") ) + return + + if(machine_stat & (NOPOWER)) + to_chat(user, span_notice("Deprived of power, \the [src] is unresponsive.") ) + for (var/i = 1 to vials.len) + if(vials[i]) + var/obj/item/reagent_containers/cup/beaker/vial/vial = vials[i] + playsound(loc, 'sound/machines/click.ogg', 50, 1) + vial.forceMove(loc) + vials[i] = null + vial_valid[i] = 0 + vial_task[i] = list(0,0,0,0,0) + update_appearance() + sleep(1) + return + + if(.) + return + + user.set_machine(src) + + special = CENTRIFUGE_LIGHTSPECIAL_OFF + + var/dat = "" + dat += "Power status: [on?"On":"Off"]" + dat += "
" + for (var/i = 1 to vials.len) + if(vials[i]) + dat += add_vial_dat(vials[i],vial_task[i],i) + else + dat += "Insert a vial" + if(i < vials.len) + dat += "
" + dat += "
" + + popup = new(user, "centrifuge", "Isolation Centrifuge", 666, 189) + popup.set_window_options("can_close=1;can_minimize=1;can_maximize=0;can_resize=1;titlebar=1;") + popup.set_content(dat) + popup.open() + +/obj/machinery/disease2/centrifuge/process() + if(machine_stat & (NOPOWER|BROKEN)) + return + + if(on) + use_power = 2 + + //first of all, let's see how (un)balanced are those vials. + //we're not taking reagent density into account because even my autism has its limits + var/obj/item/reagent_containers/cup/beaker/vial/vial1 = vials[1]//left + var/obj/item/reagent_containers/cup/beaker/vial/vial2 = vials[2]//up + var/obj/item/reagent_containers/cup/beaker/vial/vial3 = vials[3]//right + var/obj/item/reagent_containers/cup/beaker/vial/vial4 = vials[4]//down + var/vial_unbalance_X = 0 + if (vial1) + vial_unbalance_X += 5 + vial1.reagents.total_volume + if (vial3) + vial_unbalance_X -= 5 + vial3.reagents.total_volume + var/vial_unbalance_Y = 0 + if (vial2) + vial_unbalance_Y += 5 + vial2.reagents.total_volume + if (vial4) + vial_unbalance_Y -= 5 + vial4.reagents.total_volume + + var/vial_unbalance = abs(vial_unbalance_X) + abs(vial_unbalance_Y) // vials can contain up to 25 units, so maximal unbalance is 60. + + efficiency = base_efficiency / (1 + vial_unbalance / 60) // which will at most double the time taken. + + for (var/i = 1 to vials.len) + if(vials[i]) + var/list/v_task = vial_task[i] + if(v_task[1]) + vial_task[i] = centrifuge_act(vials[i],vial_task[i]) + else + use_power = 1 + + update_appearance() + updateUsrDialog() + +/obj/machinery/disease2/centrifuge/proc/centrifuge_act(obj/item/reagent_containers/cup/beaker/vial/vial, list/vial_task = list(0,0,0,0,0)) + var/list/result = list(0,0,0,0,0) + if (!vial) + return result + result = vial_task + switch (result[1]) + if ("dish") + result[3] += (efficiency * 2) / (1 + 0.3 * result[5])//additional pathogen in the sample will lengthen the process + if (result[3] >= 100) + print_dish(result[4]) + result = list(0,0,0,0,0) + if ("vaccine") + if (result[4] > 50) + result[3] += (efficiency * 2) * max(1,result[4]-50) + else if (result[4] < 50) + result[3] += (efficiency * 2) / max(1,50-result[4]) + else + result[3] += (efficiency * 2) + if (result[3] >= 100) + special = CENTRIFUGE_LIGHTSPECIAL_BLINKING + var/amt= vial.reagents.get_reagent_amount(/datum/reagent/blood) + vial.reagents.remove_reagent(/datum/reagent/blood, amt) + var/data = list("antigen" = list(result[2])) + vial.reagents.add_reagent(/datum/reagent/vaccine, amt,data) + result = list(0,0,0,0,0) + return result + +/obj/machinery/disease2/centrifuge/Topic(href, href_list) + + if(..()) + return 1 + + special = CENTRIFUGE_LIGHTSPECIAL_OFF + + if (href_list["power"]) + on = !on + update_appearance() + + else if (href_list["insertvial"]) + var/mob/living/user + if (isliving(usr)) + user = usr + if (!user) + return + var/obj/item/reagent_containers/cup/beaker/vial/vial = user.get_active_hand() + if (istype(vial)) + if (on) + to_chat(user,span_warning("You cannot add or remove vials while the centrifuge is active. Turn it Off first.")) + return + else + var/i = text2num(href_list["insertvial"]) + if (!vials[i]) + vials[i] = vial + vial_valid[i] = vial_has_antibodies(vial) + visible_message(span_notice("\The [user] adds \the [vial] to \the [src]."),span_notice("You add \the [vial] to \the [src].")) + playsound(loc, 'sound/machines/click.ogg', 50, 1) + user.transferItemToLoc(vial, loc) + vial.forceMove(src) + else + to_chat(user,span_warning("There is already a vial in that slot.")) + return + + else if (href_list["ejectvial"]) + if (on) + to_chat(usr,span_warning("You cannot add or remove vials while the centrifuge is active. Turn it Off first.")) + return + else + var/i = text2num(href_list["ejectvial"]) + if (vials[i]) + var/obj/item/reagent_containers/cup/beaker/vial/vial = vials[i] + vial.forceMove(src.loc) + if (Adjacent(usr)) + vial.forceMove(usr.loc) + usr.put_in_hands(vial) + vials[i] = null + vial_valid[i] = 0 + vial_task[i] = list(0,0,0,0,0) + + else if (href_list["interrupt"]) + var/i = text2num(href_list["interrupt"]) + vial_task[i] = list(0,0,0,0,0) + + else if (href_list["isolate"]) + var/i = text2num(href_list["isolate"]) + vial_task[i] = isolate(vials[i],usr) + + else if (href_list["synthvaccine"]) + var/i = text2num(href_list["synthvaccine"]) + vial_task[i] = cure(vials[i],usr) + + update_appearance() + add_fingerprint(usr) + updateUsrDialog() + attack_hand(usr) + +/obj/machinery/disease2/centrifuge/proc/isolate(obj/item/reagent_containers/cup/beaker/vial/vial, mob/user) + var/list/result = list(0,0,0,0,0) + if (!vial) + return result + + var/datum/reagent/blood/blood = locate() in vial.reagents.reagent_list + if (blood && blood.data && blood.data["viruses"]) + var/list/blood_viruses = blood.data["viruses"] + if (istype(blood_viruses) && blood_viruses.len > 0) + var/list/pathogen_list = list() + for (var/datum/disease/advanced/D as anything in blood_viruses) + if(!istype(D)) + continue + var/pathogen_name = "Unknown [D.form]" + pathogen_list[pathogen_name] = D + + popup.close() + user.unset_machine() + var/choice = input(user, "Choose a pathogen to isolate on a growth dish.", "Isolate to dish") as null|anything in pathogen_list + user.set_machine() + if (!choice) + return result + var/datum/disease/advanced/target = pathogen_list[choice] + + result[1] = "dish" + result[2] = "Unknown [target.form]" + result[3] = 0 + result[4] = target + result[5] = length(pathogen_list) + + return result + +/obj/machinery/disease2/centrifuge/proc/cure(obj/item/reagent_containers/cup/beaker/vial/vial, mob/user) + var/list/result = list(0,0,0,0,0) + if (!vial) + return result + + var/datum/reagent/blood/blood = locate() in vial.reagents.reagent_list + if (blood && blood.data && blood.data["immunity"]) + var/list/immune_system = blood.data["immunity"] + if (istype(immune_system) && immune_system.len > 0) + if (immune_system[1] < 1) + to_chat(user,span_warning("Impossible to acquire antibodies from this blood sample. It seems that it came from a donor with a poor immune system, either due to recent cloning or a radium overload.") ) + return result + + var/list/antibodies = immune_system[2] + var/list/antibody_choices = list() + for (var/antibody in antibodies) + if (antibodies[antibody] >= 30) + if (antibodies[antibody] > 50) + var/delay = max(1,60 / max(1,(antibodies[antibody] - 50))) + antibody_choices["[antibody] (Expected Duration: [round(delay)] seconds)"] = antibody + else if (antibodies[antibody] < 50) + var/delay = max(1,50 - min(49,antibodies[antibody])) + antibody_choices["[antibody] (Expected Duration: [round(delay)] minutes)"] = antibody + else + antibody_choices["[antibody] (Expected Duration: one minute)"] = antibody + + if (antibody_choices.len <= 0) + to_chat(user,span_warning("Impossible to create a vaccine from this blood sample. Antibody levels too low. Minimal level = 30%. The higher the concentration, the faster the vaccine is synthesized.") ) + return result + + popup.close() + user.unset_machine() + var/choice = input(user, "Choose an antibody to develop into a vaccine. This will destroy the blood sample. The higher the concentration, the faster the vaccine is synthesized.", "Synthesize Vaccine") as null|anything in antibody_choices + user.set_machine() + if (!choice) + return result + + var/antibody = antibody_choices[choice] + + result[1] = "vaccine" + result[2] = antibody + result[3] = 0 + result[4] = antibodies[antibody] + + return result + +/obj/machinery/disease2/centrifuge/proc/print_dish(var/datum/disease/advanced/D) + special = CENTRIFUGE_LIGHTSPECIAL_BLINKING + /* + anim(target = src, a_icon = icon, flick_anim = "centrifuge_print", sleeptime = 10) + anim(target = src, a_icon = icon, flick_anim = "centrifuge_print_color", sleeptime = 10, col = D.color) + */ + visible_message("\The [src] prints a growth dish.") + spawn(10) + var/obj/item/weapon/virusdish/dish = new/obj/item/weapon/virusdish(src.loc) + dish.contained_virus = D.Copy() + dish.contained_virus.infectionchance = dish.contained_virus.infectionchance_base + dish.update_appearance() + dish.name = "growth dish (Unknown [dish.contained_virus.form])" + + +/obj/machinery/disease2/centrifuge/Destroy() + for (var/i = 1 to vials.len) + if(vials[i]) + var/obj/item/reagent_containers/cup/beaker/vial/vial = vials[i] + vial.forceMove(loc) + vials = list(null,null,null,null) + vial_valid = list(0,0,0,0) + vial_task = list( + list(0,0,0,0,0,), + list(0,0,0,0,0,), + list(0,0,0,0,0,), + list(0,0,0,0,0,), + ) + special = CENTRIFUGE_LIGHTSPECIAL_OFF + . = ..() + +#undef CENTRIFUGE_LIGHTSPECIAL_OFF +#undef CENTRIFUGE_LIGHTSPECIAL_BLINKING +#undef CENTRIFUGE_LIGHTSPECIAL_ON diff --git a/monkestation/code/modules/virology/machines/curer.dm b/monkestation/code/modules/virology/machines/curer.dm new file mode 100644 index 000000000000..7bed0efdb892 --- /dev/null +++ b/monkestation/code/modules/virology/machines/curer.dm @@ -0,0 +1,99 @@ +/obj/machinery/computer/curer + name = "Cure Research Machine" + icon_state = "dna" + var/curing + var/virusing + + var/obj/item/reagent_containers/cup/beaker/vial/container = null + +/obj/machinery/computer/curer/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/reagent_containers/cup/beaker/vial)) + var/mob/living/carbon/C = user + if(!container) + if(C.forceMove(I, src)) + container = I + if(istype(I,/obj/item/weapon/virusdish)) + if(virusing) + to_chat(user, "The pathogen materializer is still recharging..") + return + var/obj/item/reagent_containers/cup/beaker/vial/product = new(src.loc) + + var/list/data = list("viruses"=null,"blood_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"viruses"=list(),"immunity"=0) + data["viruses"] |= I:viruses + product.reagents.add_reagent(/datum/reagent/blood, 30,data) + + virusing = 1 + spawn(1200) virusing = 0 + + return + src.attack_hand(user) + return + +/obj/machinery/computer/curer/attack_hand(mob/user) + if(..()) + return + var/dat + if(curing) + dat = "Antibody production in progress" + else if(virusing) + dat = "Virus production in progress" + else if(container) + // see if there's any blood in the container + var/datum/reagent/blood/B = locate(/datum/reagent/blood) in container.reagents.reagent_list + + if(B) + dat = "Blood sample inserted." + var/code = "" + for(var/V in GLOB.all_antigens) if(text2num(V) & B.data["antibodies"]) code += GLOB.all_antigens[V] + dat += "
Antibodies: [code]" + dat += "
Begin antibody production" + else + dat += "
Please check container contents." + dat += "
Eject container" + else + dat = "Please insert a container." + + user << browse(dat, "window=computer;size=400x500") + onclose(user, "computer") + return + +/obj/machinery/computer/curer/process() + ..() + + if(machine_stat & (NOPOWER|BROKEN)) + return + use_power(500) + + if(curing) + curing -= 1 + if(curing == 0) + if(container) + createcure(container) + return + +/obj/machinery/computer/curer/Topic(href, href_list) + if(..()) + return + usr.machine = src + + if (href_list["antibody"]) + curing = 10 + else if(href_list["eject"]) + container.forceMove(src.loc) + container = null + + src.add_fingerprint(usr) + src.updateUsrDialog() + return + + +/obj/machinery/computer/curer/proc/createcure(obj/item/reagent_containers/cup/beaker/container) + var/obj/item/reagent_containers/cup/beaker/vial/product = new(src.loc) + + var/datum/reagent/blood/B = locate() in container.reagents.reagent_list + + var/list/data = list() + data["antigen"] = B.data["immunity"] + + product.reagents.add_reagent(/datum/reagent/vaccine , 30, data) + diff --git a/monkestation/code/modules/virology/machines/disease_records.dm b/monkestation/code/modules/virology/machines/disease_records.dm new file mode 100644 index 000000000000..3f0ac5150729 --- /dev/null +++ b/monkestation/code/modules/virology/machines/disease_records.dm @@ -0,0 +1,66 @@ +/obj/machinery/computer/records/pathology + name = "disease records console" + desc = "This can be used to check disease records." + icon_screen = "medcomp" + icon_keyboard = "med_key" + req_one_access = list(ACCESS_MEDICAL, ACCESS_DETECTIVE, ACCESS_GENETICS) + circuit = /obj/item/circuitboard/computer/pathology_data + light_color = LIGHT_COLOR_BLUE + +/obj/machinery/computer/records/pathology/ui_interact(mob/user, datum/tgui/ui) + . = ..() + if(.) + return + ui = SStgui.try_update_ui(user, src, ui) + if (!ui) + create_character_preview_view(user) + ui = new(user, src, "PathologyRecords") + ui.set_autoupdate(FALSE) + ui.open() + +/obj/machinery/computer/records/pathology/ui_data(mob/user) + var/list/data = ..() + + var/list/records = list() + for(var/ID in GLOB.virusDB) + var/datum/data/record/target = GLOB.virusDB[ID] + records += list(list( + crew_ref = "[target.fields["id"]]-[target.fields["sub"]]", + id = target.fields["id"], + sub = target.fields["sub"], + child = target.fields["child"], + form = target.fields["form"], + name = target.fields["name"], + nickname = target.fields["nickname"], + description = target.fields["description"], + custom_desc = target.fields["custom_desc"], + antigen = target.fields["antigen"], + spread_flags = target.fields["spread_flags_type"], + danger = target.fields["danger"], + )) + + data["records"] = records + + return data + +/obj/machinery/computer/records/pathology/ui_act(action, list/params, datum/tgui/ui) + . = ..() + var/datum/data/record/target + if(params["crew_ref"] && (params["crew_ref"] in GLOB.virusDB)) + target = GLOB.virusDB[params["crew_ref"]] + if(!target && params["crew_ref"]) + return FALSE + + switch(action) + if("edit_field") + target.fields[params["field"]] = params["value"] + return TRUE + if("expunge_record") + GLOB.virusDB[params["crew_ref"]] = null + qdel(target) + GLOB.virusDB -= params["crew_ref"] + return TRUE + if(.) + return + + return FALSE diff --git a/monkestation/code/modules/virology/machines/incubator.dm b/monkestation/code/modules/virology/machines/incubator.dm new file mode 100644 index 000000000000..21c7f72067cb --- /dev/null +++ b/monkestation/code/modules/virology/machines/incubator.dm @@ -0,0 +1,475 @@ +#define INCUBATOR_DISH_GROWTH (1 << 0) +#define INCUBATOR_DISH_REAGENT (1 << 1) +#define INCUBATOR_DISH_MAJOR (1 << 2) +#define INCUBATOR_DISH_MINOR (1 << 3) + +/obj/machinery/disease2/incubator + name = "pathogenic incubator" + desc = "Uses radiation to accelerate the incubation of pathogen. The dishes must be filled with reagents for the incubation to have any effects." + density = TRUE + anchored = TRUE + icon = 'monkestation/code/modules/virology/icons/virology.dmi' + icon_state = "incubator" + + circuit = /obj/item/circuitboard/machine/incubator + + light_color = "#6496FA" + light_outer_range = 2 + light_power = 1 + + idle_power_usage = 100 + active_power_usage = 200 + + // Contains instances of /dish_incubator_dish. + var/list/dish_data = list(null, null, null) + + var/on = FALSE + + var/mutatechance = 5 + var/growthrate = 4 + var/can_focus = 0 //Whether the machine can focus on an effect to mutate it or not + var/effect_focus = 0 //What effect of the disease are we focusing on? + +/obj/machinery/disease2/incubator/New() + . = ..() + RefreshParts() + +/obj/machinery/disease2/incubator/Initialize(mapload) + . = ..() + START_PROCESSING(SSobj, src) + +/obj/machinery/disease2/incubator/Destroy() + . = ..() + STOP_PROCESSING(SSobj, src) + +/obj/machinery/disease2/incubator/RefreshParts() + . = ..() + var/scancount = 0 + var/lasercount = 0 + for(var/datum/stock_part/scanning_module/SP in component_parts) + scancount += SP.tier-1 + for(var/datum/stock_part/micro_laser/SP in component_parts) + lasercount += SP.tier-1 + if(lasercount >= 4) + can_focus = 1 + else + can_focus = 0 + mutatechance = initial(mutatechance) * max(1, scancount) + growthrate = initial(growthrate) + lasercount + + +/obj/machinery/disease2/incubator/attackby(obj/item/I, mob/living/user, params) + . = ..() + + if (machine_stat & (BROKEN)) + to_chat(user, span_warning("\The [src] is broken. Some components will have to be replaced before it can work again.")) + return FALSE + + if (.) + return + + if (istype(I, /obj/item/weapon/virusdish)) + for (var/i in 1 to dish_data.len) + if (dish_data[i] == null) // Empty slot + addDish(I, user, i) + return TRUE + + to_chat(user, span_warning("There is no more room inside \the [src]. Remove a dish first.")) + return FALSE + + +/obj/machinery/disease2/incubator/proc/addDish(obj/item/weapon/virusdish/VD, mob/user, slot) + if (!VD.open) + to_chat(user, span_warning("You must open the dish's lid before it can be put inside the incubator. Be sure to wear proper protection first (at least a sterile mask and latex gloves).")) + return + + if (dish_data[slot] != null) + to_chat(user,span_warning("This slot is already occupied. Remove the dish first.")) + return + + if (!user.transferItemToLoc(VD, src)) + return + + var/dish_incubator_dish/dish_datum = new + dish_datum.dish = VD + dish_data[slot] = dish_datum + + visible_message(span_notice("\The [user] adds \the [VD] to \the [src]."),span_notice("You add \the [VD] to \the [src].")) + playsound(loc, 'sound/machines/click.ogg', 50, 1) + update_appearance() + + +/obj/machinery/disease2/incubator/ui_act(action, params) + . = ..() + if (.) + return + + switch(action) + if ("power") + on = !on + if (on) + for (var/dish_incubator_dish/dish_datum in dish_data) + if (dish_datum.dish.contained_virus) + dish_datum.dish.contained_virus.log += "
[ROUND_TIME()] Incubation started by [key_name(usr)]" + + update_appearance() + return TRUE + + if ("ejectdish") + var/slot = text2num(params["slot"]) + if (slot == null || slot < 1 || slot > dish_data.len) + return TRUE + + var/dish_incubator_dish/dish_datum = dish_data[slot] + if (dish_datum == null) + return TRUE + + dish_datum.dish.forceMove(loc) + if (Adjacent(usr)) + usr.put_in_hands(dish_datum.dish) + + dish_datum.dish.update_appearance() + dish_data[slot] = null + update_appearance() + return TRUE + + if ("insertdish") + var/slot = text2num(params["slot"]) + if (slot == null || slot < 1 || slot > dish_data.len) + return TRUE + + var/mob/living/user = usr + if (!isliving(user)) + return TRUE + + var/obj/item/weapon/virusdish/VD = user.get_active_hand() + if (istype(VD)) + addDish(VD, user, slot) + + update_appearance() + return TRUE + + if ("examinedish") + var/slot = text2num(params["slot"]) + if (slot == null || slot < 1 || slot > dish_data.len) + return TRUE + + var/dish_incubator_dish/dish_datum = dish_data[slot] + if (dish_datum == null) + return TRUE + + dish_datum.dish.examine(usr) + return TRUE + + if ("flushdish") + var/slot = text2num(params["slot"]) + if (slot == null || slot < 1 || slot > dish_data.len) + return TRUE + + var/dish_incubator_dish/dish_datum = dish_data[slot] + if (dish_datum == null) + return TRUE + + dish_datum.dish.reagents.clear_reagents() + return TRUE + if ("changefocus") + var/slot = text2num(params["slot"]) + if(slot == null || slot < 1 || slot > dish_data.len) + return TRUE + var/dish_incubator_dish/dish_datum = dish_data[slot] + if (dish_datum == null) + return TRUE + var/stage_to_focus = input(usr, "Choose a stage to focus on. This will block symptoms from other stages from being mutated. Input 0 to disable effect focusing.", "Choose a stage.") as num + if(!stage_to_focus) + to_chat(usr, span_notice("The effect focusing is now turned off.")) + else + to_chat(usr, span_notice("\The [src] will now focus on stage [stage_to_focus].")) + effect_focus = stage_to_focus + return TRUE + +/obj/machinery/disease2/incubator/attack_hand(mob/user) + . = ..() + if (machine_stat & (BROKEN)) + to_chat(user, span_notice("\The [src] is broken. Some components will have to be replaced before it can work again.")) + return + if (machine_stat & (NOPOWER)) + to_chat(user, span_notice("Deprived of power, \the [src] is unresponsive.")) + for (var/i in 1 to dish_data.len) + var/dish_incubator_dish/dish_datum = dish_data[i] + if (dish_datum == null) + continue + + playsound(loc, 'sound/machines/click.ogg', 50, 1) + dish_datum.dish.forceMove(loc) + update_appearance() + dish_data[i] = null + sleep(1) + + return + + if (.) + return + + ui_interact(user) + + + +/obj/machinery/disease2/incubator/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if (!ui) + ui = new(user, src, "DiseaseIncubator", "Incubator") + ui.open() + +/obj/machinery/disease2/incubator/ui_data(mob/user) + // this is the data which will be sent to the ui + var/list/data = list() + + data["on"] = on + data["can_focus"] = can_focus + var/list/dish_ui_data = list() + data["dishes"] = dish_ui_data + + for (var/i = 1 to dish_data.len) + var/dish_incubator_dish/dish_datum = dish_data[i] + var/list/dish_ui_datum = list() + // tfw no linq + dish_ui_data[++dish_ui_data.len] = dish_ui_datum + + var/inserted = dish_datum != null + dish_ui_datum["inserted"] = inserted + if (!inserted) + dish_ui_datum["name"] = "Empty Slot" + continue + + dish_ui_datum["name"] = dish_datum.dish.name + dish_ui_datum["growth"] = dish_datum.dish.growth + dish_ui_datum["reagents_volume"] = dish_datum.dish.reagents.total_volume + dish_ui_datum["major_mutations"] = dish_datum.major_mutations_count + dish_ui_datum["minor_mutations_strength"] = mutatechance //add support for other reagents + dish_ui_datum["minor_mutations_robustness"] = mutatechance //add support for other reagents + dish_ui_datum["minor_mutations_effects"] = mutatechance //add support for other reagents + dish_ui_datum["dish_slot"] = i + + return data + +/obj/machinery/disease2/incubator/process() + if (machine_stat & (NOPOWER|BROKEN)) + return + + if (on) + use_power = ACTIVE_POWER_USE + for (var/dish_incubator_dish/dish_datum in dish_data) + dish_datum.dish.incubate(mutatechance, growthrate, effect_focus) + else + use_power = IDLE_POWER_USE + + update_appearance() + + +/obj/machinery/disease2/incubator/proc/find_dish_datum(obj/item/weapon/virusdish/dish) + for (var/dish_incubator_dish/dish_datum in dish_data) + if (dish_datum.dish == dish) + return dish_datum + + return null + + +/obj/machinery/disease2/incubator/proc/update_major(obj/item/weapon/virusdish/dish) + var/dish_incubator_dish/dish_datum = find_dish_datum(dish) + if (dish_datum == null) + return + + dish_datum.updates_new |= INCUBATOR_DISH_MAJOR + dish_datum.updates &= ~INCUBATOR_DISH_MAJOR + dish_datum.major_mutations_count++ + + +/obj/machinery/disease2/incubator/proc/update_minor(obj/item/weapon/virusdish/dish, str=0, rob=0, eff=0) + var/dish_incubator_dish/dish_datum = find_dish_datum(dish) + if (dish_datum == null) + return + + dish_datum.updates_new |= INCUBATOR_DISH_MINOR + dish_datum.updates &= ~INCUBATOR_DISH_MINOR + dish_datum.minor_mutation_strength += str; + dish_datum.minor_mutation_robustness += rob; + dish_datum.minor_mutation_effects += eff; + + +/obj/machinery/disease2/incubator/update_icon() + . = ..() + icon_state = "incubator" + + if (machine_stat & (NOPOWER)) + icon_state = "incubator0" + + if (machine_stat & (BROKEN)) + icon_state = "incubatorb" + + if (on) + light_color = "#E1C400" + else + light_color = "#6496FA" + + if(machine_stat & (BROKEN|NOPOWER)) + set_light(0) + else + if (on) + set_light(2,2) + else + set_light(2,1) + +/obj/machinery/disease2/incubator/update_overlays() + . = ..() + if(!(machine_stat & (BROKEN|NOPOWER))) + if (on) + . += mutable_appearance(icon,"incubator_light",src) + . += mutable_appearance(icon,"incubator_glass",src) + . += emissive_appearance(icon,"incubator_light",src) + . += emissive_appearance(icon,"incubator_glass",src) + + for (var/i = 1 to dish_data.len) + if (dish_data[i] != null) + . += add_dish_sprite(dish_data[i], i) + +/obj/machinery/disease2/incubator/proc/add_dish_sprite(dish_incubator_dish/dish_datum, slot) + var/obj/item/weapon/virusdish/dish = dish_datum.dish + var/list/overlays = list() + + slot-- + var/mutable_appearance/dish_outline = mutable_appearance(icon,"smalldish2-outline",src) + dish_outline.alpha = 128 + dish_outline.pixel_y = -5 * slot + overlays += dish_outline + var/mutable_appearance/dish_content = mutable_appearance(icon,"smalldish2-empty",src) + dish_content.alpha = 128 + dish_content.pixel_y = -5 * slot + if (dish.contained_virus) + dish_content.icon_state = "smalldish2-color" + dish_content.color = dish.contained_virus.color + overlays += dish_content + + //updating the light indicators + if (dish.contained_virus && !(machine_stat & (BROKEN|NOPOWER))) + var/mutable_appearance/grown_gauge = mutable_appearance(icon,"incubator_growth7",src) + grown_gauge.plane = ABOVE_LIGHTING_PLANE + grown_gauge.pixel_y = -5 * slot + if (dish.growth < 100) + grown_gauge.icon_state = "incubator_growth[min(6,max(1,round(dish.growth*70/1000)))]" + else + var/update = FALSE + if (!(dish_datum.updates & INCUBATOR_DISH_GROWTH)) + dish_datum.updates += INCUBATOR_DISH_GROWTH + update = TRUE + + if (update) + var/mutable_appearance/grown_light = emissive_appearance(icon,"incubator_grown_update",src) + grown_light.pixel_y = -5 * slot + var/mutable_appearance/grown_light_n = mutable_appearance(icon,"incubator_grown_update",src) + grown_light_n.pixel_y = -5 * slot + + overlays += grown_light + overlays += grown_light_n + else + var/mutable_appearance/grown_light = emissive_appearance(icon,"incubator_grown",src) + grown_light.pixel_y = -5 * slot + var/mutable_appearance/grown_light_n = mutable_appearance(icon,"incubator_grown",src) + grown_light_n.pixel_y = -5 * slot + + overlays += grown_light_n + overlays += grown_light + + overlays += grown_gauge + if (dish.reagents.total_volume < 0.02) + var/update = FALSE + if (!(dish_datum.updates & INCUBATOR_DISH_REAGENT)) + dish_datum.updates += INCUBATOR_DISH_REAGENT + update = TRUE + + if (update) + var/mutable_appearance/reagents_light = emissive_appearance(icon,"incubator_reagents_update",src) + reagents_light.pixel_y = -5 * slot + var/mutable_appearance/reagents_light_n = mutable_appearance(icon,"incubator_reagents_update",src) + reagents_light_n.pixel_y = -5 * slot + + overlays += reagents_light_n + overlays += reagents_light + else + var/mutable_appearance/reagents_light = emissive_appearance(icon,"incubator_reagents",src) + reagents_light.pixel_y = -5 * slot + var/mutable_appearance/reagents_light_n = mutable_appearance(icon,"incubator_reagents",src) + reagents_light_n.pixel_y = -5 * slot + + overlays += reagents_light_n + overlays += reagents_light + + if (dish_datum.updates_new & INCUBATOR_DISH_MAJOR) + if (!(dish_datum.updates & INCUBATOR_DISH_MAJOR)) + dish_datum.updates += INCUBATOR_DISH_MAJOR + var/mutable_appearance/effect_light = emissive_appearance(icon,"incubator_major_update",src) + effect_light.pixel_y = -5 * slot + var/mutable_appearance/effect_light_n = mutable_appearance(icon,"incubator_major_update",src) + effect_light_n.pixel_y = -5 * slot + + overlays += effect_light_n + overlays += effect_light + else + var/mutable_appearance/effect_light = emissive_appearance(icon,"incubator_major",src) + effect_light.pixel_y = -5 * slot + var/mutable_appearance/effect_light_n = mutable_appearance(icon,"incubator_major",src) + effect_light_n.pixel_y = -5 * slot + + overlays += effect_light_n + overlays += effect_light + + if (dish_datum.updates_new & INCUBATOR_DISH_MINOR) + if (!(dish_datum.updates & INCUBATOR_DISH_MINOR)) + dish_datum.updates += INCUBATOR_DISH_MINOR + var/mutable_appearance/effect_light = emissive_appearance(icon,"incubator_minor_update",src) + effect_light.pixel_y = -5 * slot + var/mutable_appearance/effect_light_n = mutable_appearance(icon,"incubator_minor_update",src) + effect_light_n.pixel_y = -5 * slot + + overlays += effect_light_n + + overlays += effect_light + else + var/mutable_appearance/effect_light = mutable_appearance(icon,"incubator_minor",src) + effect_light.pixel_y = -5 * slot + var/mutable_appearance/effect_light_n = mutable_appearance(icon,"incubator_minor",src) + effect_light_n.pixel_y = -5 * slot + + overlays += effect_light_n + overlays += effect_light + + return overlays + +/obj/machinery/disease2/incubator/Destroy() + . = ..() + for (var/i in 1 to dish_data.len) + var/dish_incubator_dish/dish_datum = dish_data[i] + if (dish_datum == null) + continue + + dish_datum.dish.forceMove(loc) + dish_data[i] = null + + ..() + +/dish_incubator_dish + // The inserted virus dish. + var/obj/item/weapon/virusdish/dish + + var/major_mutations_count = 0 + + var/minor_mutation_strength = 0 + var/minor_mutation_robustness = 0 + var/minor_mutation_effects = 0 + + var/updates_new = 0 + var/updates = 0 + +#undef INCUBATOR_DISH_GROWTH +#undef INCUBATOR_DISH_REAGENT +#undef INCUBATOR_DISH_MAJOR +#undef INCUBATOR_DISH_MINOR diff --git a/monkestation/code/modules/virology/machines/isolator.dm b/monkestation/code/modules/virology/machines/isolator.dm new file mode 100644 index 000000000000..516b4514b754 --- /dev/null +++ b/monkestation/code/modules/virology/machines/isolator.dm @@ -0,0 +1,108 @@ +/obj/machinery/disease2/isolator + name = "Pathogenic Isolator" + desc = "Takes a syringe of blood, and isolates the pathogens inside into a dish." + density = 1 + anchored = 1 + icon = 'monkestation/code/modules/virology/icons/virology.dmi' + icon_state = "isolator" + var/datum/disease/advanced/isolated_disease = null + var/isolating = 0 + var/beaker = null + +/obj/machinery/disease2/isolator/attackby(obj/item/I, mob/living/user, params) + if(!istype(I,/obj/item/reagent_containers/syringe)) + return + + var/obj/item/reagent_containers/syringe/B = I + + if(src.beaker) + to_chat(user, "A syringe is already loaded into the machine.") + return + + if(user.dropItemToGround(B)) + B.forceMove(src) + src.beaker = B + if(istype(B,/obj/item/reagent_containers/syringe)) + to_chat(user, "You add the syringe to the machine!") + src.updateUsrDialog() + icon_state = "isolator_in" + +/obj/machinery/disease2/isolator/Topic(href, href_list) + if(..()) + return + + usr.machine = src + if(!beaker) + return + var/datum/reagents/R = beaker:reagents + + if (href_list["isolate"]) + var/datum/reagent/blood/Blood + for(var/datum/reagent/blood/B in R.reagent_list) + if(B && B.data["viruses"]) + Blood = B + break + // /vg/: Try to fix isolators + if(!Blood) + to_chat(usr, span_warning("ERROR: Unable to locate blood within the beaker. Bug?")) +// testing("Unable to locate blood in [beaker]!") + return + var/list/virus = virus_copylist(Blood.data["viruses"]) + var/choice = text2num(href_list["isolate"]) + for (var/datum/disease/advanced/V as anything in virus) + if (V.uniqueID == choice) + isolated_disease = V + isolating = 40 + icon_state = "isolator_processing" + src.updateUsrDialog() + return + + else if (href_list["main"]) + attack_hand(usr) + return + else if (href_list["eject"]) + beaker:forceMove(src.loc) + beaker = null + icon_state = "isolator" + src.updateUsrDialog() + return + +/obj/machinery/disease2/isolator/attack_hand(mob/user, list/modifiers) + if(machine_stat & BROKEN) + return + user.machine = src + var/dat = "" + if(!beaker) + + dat = {"Please insert sample into the isolator.
+Close"} + else if(isolating) + dat = "Isolating" + else + var/datum/reagents/R = beaker:reagents + dat += "Eject

" + if(!R.total_volume) + dat += "[beaker] is empty." + else + dat += "Contained reagents:
    " + for(var/datum/reagent/blood/G in R.reagent_list) + if(G.data["viruses"]) + var/list/virus = G.data["viruses"] + for (var/datum/disease/advanced/V as anything in virus) + dat += "
  • [G.name]: Isolate pathogen #[V.uniqueID]
  • " + else + dat += "
  • No pathogen
  • " + user << browse("Pathogenic IsolatorIsolator menu:

    [dat]
", "window=isolator;size=575x400") + onclose(user, "isolator") + return + +/obj/machinery/disease2/isolator/process() + if(isolating > 0) + isolating -= 1 + if(isolating == 0) + var/obj/item/weapon/virusdish/d = new /obj/item/weapon/virusdish(src.loc) + d.contained_virus = isolated_disease.Copy() + d.contained_virus.log += "[ROUND_TIME()]
Transferred to Virus dish" + d.update_icon() + isolated_disease = null + icon_state = "isolator_in" diff --git a/monkestation/code/modules/virology/machines/splicer.dm b/monkestation/code/modules/virology/machines/splicer.dm new file mode 100644 index 000000000000..f91fa3bbd3ba --- /dev/null +++ b/monkestation/code/modules/virology/machines/splicer.dm @@ -0,0 +1,268 @@ +#define DISEASE_SPLICER_BURNING_TICKS 5 +#define DISEASE_SPLICER_SPLICING_TICKS 5 +#define DISEASE_SPLICER_SCANNING_TICKS 5 + +/obj/machinery/computer/diseasesplicer + name = "disease splicer" + icon = 'monkestation/code/modules/virology/icons/virology.dmi' + icon_state = "splicer" + + icon_keyboard = null + icon_screen = null + circuit = /obj/item/circuitboard/computer/diseasesplicer + var/datum/symptom/memorybank = null + var/analysed = FALSE // If the buffered effect came from a dish that had been analyzed this is TRUE + var/obj/item/weapon/virusdish/dish = null + var/burning = 0 // Time in process ticks until disk burning is over + + var/splicing = 0 // Time in process ticks until splicing is over + var/scanning = 0 // Time in process ticks until scan is over + var/spliced = FALSE // If at least one effect has been spliced into the current dish this is TRUE + + ///the stage we are set to grab from + var/target_stage = 1 + idle_power_usage = 100 + active_power_usage = 600 + + light_color = "#00FF00" + +/obj/machinery/computer/diseasesplicer/attackby(obj/I, mob/user) + if(!(istype(I,/obj/item/weapon/virusdish) || istype(I,/obj/item/disk/disease))) + return ..() + + if(istype(I, /obj/item/weapon/virusdish)) + if(dish) + to_chat(user, span_warning("A virus containment dish is already inside \the [src].")) + return + if(!user.transferItemToLoc(I, src)) + to_chat(user, span_warning("You can't let go of \the [I]!")) + return + dish = I + playsound(loc, 'sound/machines/click.ogg', 50, 1) + update_icon() + + if(istype(I, /obj/item/disk/disease)) + var/obj/item/disk/disease/disk = I + visible_message(span_notice("[user] swipes \the [disk] against \the [src]."), span_notice("You swipe \the [disk] against \the [src], copying the data into the machine's buffer.")) + memorybank = disk.effect + flick_overlay("splicer_disk", src) + spawn(2) + update_icon() + + attack_hand(user) + +/obj/machinery/computer/diseasesplicer/ui_interact(mob/user, datum/tgui/ui) + . = ..() + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "DiseaseSplicer") + ui.open() + + +/obj/machinery/computer/diseasesplicer/ui_data(mob/user) + . = ..() + var/list/data = list( + "splicing" = splicing, + "scanning" = scanning, + "burning" = burning, + "target_stage" = target_stage, + ) + + if(dish) + if(dish.contained_virus) + if (dish.analysed) + data["dish_name"] = dish.contained_virus.name() + else + data["dish_name"] = "Unknown [dish.contained_virus.form]" + else + data["dish_name"] = "Empty Dish" + + if(memorybank) + data["memorybank"] = "[analysed ? memorybank.name : "Unknown DNA strand"] (Stage [memorybank.stage])" + + if(!dish) + data["dish_error"] = "no dish inserted" + else if(!dish.contained_virus) + data["dish_error"] = "no pathogen in dish" + else if(!dish.analysed) + data["dish_error"] = "dish not analysed" + else if(dish.growth < 50) + data["dish_error"] = "not enough cells" + return data + +/obj/machinery/computer/diseasesplicer/attack_hand(mob/user, list/modifiers) + . = ..() + + if(machine_stat & (NOPOWER|BROKEN)) + eject_dish() + return + + if(.) + return + + ui_interact(user) + +/obj/machinery/computer/diseasesplicer/process() + if(machine_stat & (NOPOWER|BROKEN)) + return + if(scanning || splicing || burning) + use_power = 2 + else + use_power = 1 + + if(scanning) + scanning-- + if(!scanning) + update_icon() + if(splicing) + splicing-- + if(!splicing) + update_icon() + if(burning) + burning-- + if(!burning) + update_icon() + flick_overlay("splicer_print", src) + var/obj/item/disk/disease/d = new /obj/item/disk/disease(src) + if(analysed) + d.name = "\improper [memorybank.name] GNA disk (Stage: [memorybank.stage])" + else + d.name = "unknown GNA disk (Stage: [memorybank.stage])" + d.effect = memorybank + d.update_desc() + spawn(10) + d.forceMove(loc) + d.pixel_x = -6 + d.pixel_y = 3 + + +/obj/machinery/computer/diseasesplicer/update_overlays() + ..() + if (dish) + var/mutable_appearance/dish_outline = mutable_appearance(icon,"smalldish2-outline") + dish_outline.alpha = 128 + dish_outline.pixel_x = -1 + dish_outline.pixel_y = -13 + .+= dish_outline + var/mutable_appearance/dish_content = mutable_appearance(icon,"smalldish2-empty") + dish_content.alpha = 128 + dish_content.pixel_x = -1 + dish_content.pixel_y = -13 + if (dish.contained_virus) + dish_content.icon_state = "smalldish2-color" + dish_content.color = dish.contained_virus.color + .+=dish_outline + + if(machine_stat & (BROKEN|NOPOWER)) + return + + if (dish && dish.contained_virus) + if (dish.analysed) + var/mutable_appearance/scan_pattern = mutable_appearance(icon,"pattern-[dish.contained_virus.pattern]b") + scan_pattern.color = "#00FF00" + scan_pattern.pixel_x = -2 + scan_pattern.pixel_y = 4 + .+= scan_pattern + else + .+= mutable_appearance(icon,"splicer_unknown") + + if(scanning || splicing) + var/mutable_appearance/splicer_glass = emissive_appearance(icon,"splicer_glass") + splicer_glass.blend_mode = BLEND_ADD + .+= splicer_glass + + if (memorybank) + .+= emissive_appearance(icon,"splicer_buffer") + +/obj/machinery/computer/diseasesplicer/proc/buffer2dish() + if(!memorybank || !dish || !dish.contained_virus) + return + + var/list/effects = dish.contained_virus.symptoms + for(var/x = 1 to effects.len) + var/datum/symptom/e = effects[x] + if(e.stage == target_stage) + effects[x] = memorybank.Copy(dish.contained_virus) + dish.contained_virus.log += "
[ROUND_TIME()] [memorybank.name] spliced in by [key_name(usr)] (replaces [e.name])" + break + + splicing = DISEASE_SPLICER_SPLICING_TICKS + spliced = TRUE + update_icon() + +/obj/machinery/computer/diseasesplicer/proc/dish2buffer(target_stage) + if(!dish || !dish.contained_virus) + return + if(dish.growth < 50) + return + var/list/effects = dish.contained_virus.symptoms + for(var/x = 1 to effects.len) + var/datum/symptom/e = effects[x] + if(e.stage == target_stage) + memorybank = e + break + scanning = DISEASE_SPLICER_SCANNING_TICKS + analysed = dish.analysed + qdel(dish) + dish = null + update_icon() + flick("splicer_scan", src) + +/obj/machinery/computer/diseasesplicer/proc/eject_dish() + if(!dish) + return + if(spliced) + //Here we generate a new ID so the spliced pathogen gets it's own entry in the database instead of being shown as the old one. + dish.contained_virus.subID = rand(0, 9999) + var/list/randomhexes = list("7","8","9","a","b","c","d","e") + var/colormix = "#[pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)][pick(randomhexes)]" + dish.contained_virus.color = BlendRGB(dish.contained_virus.color,colormix,0.25) + dish.contained_virus.addToDB() + say("Updated pathogen database with new spliced entry.") + dish.info = dish.contained_virus.get_info() + dish.name = "growth dish ([dish.contained_virus.name()])" + spliced = FALSE + dish.contained_virus.update_global_log() + + dish.forceMove(loc) + if (Adjacent(usr)) + dish.forceMove(usr.loc) + usr.put_in_hands(dish) + dish = null + update_icon() + + +/obj/machinery/computer/diseasesplicer/ui_act(action, list/params) + . = ..() + if(.) + return TRUE + + if(scanning || splicing || burning) + return FALSE + + add_fingerprint(usr) + + switch(action) + if("eject_dish") + eject_dish() + return TRUE + if("erase_buffer") + memorybank = null + update_appearance() + return TRUE + if("dish_effect_to_buffer") + dish2buffer(target_stage) + return TRUE + if("splice_buffer_to_dish") + buffer2dish() + return TRUE + if("burn_buffer_to_disk") + burning = DISEASE_SPLICER_BURNING_TICKS + return TRUE + if("target_stage") + target_stage = params["stage"] + return FALSE + +#undef DISEASE_SPLICER_BURNING_TICKS +#undef DISEASE_SPLICER_SPLICING_TICKS +#undef DISEASE_SPLICER_SCANNING_TICKS diff --git a/monkestation/code/modules/virology/readme.md b/monkestation/code/modules/virology/readme.md new file mode 100644 index 000000000000..7eaa3bce0137 --- /dev/null +++ b/monkestation/code/modules/virology/readme.md @@ -0,0 +1,40 @@ +## Title: + + +MODULE ID: VIROLOGY + +### Description: + +This PR aims to make virology not ass to play with + + + +### TG Proc/File Changes: + + + - code\modules\mob\living\carbon\life.dm + - code\datums\diseases\_disease.dm + - code\game\objects\effects\decals\cleanable.dm + - code\game\atoms.dm + - code\modules\mob\living\carbon\human\human_defense.dm + +### Defines: + + + + +### Master file additions + + + +### Included files that are not contained in this module: + +- N/A + + +### Credits: + + + + +Coded by Dwasint diff --git a/monkestation/code/modules/virology/reagents/antipathenogenics.dm b/monkestation/code/modules/virology/reagents/antipathenogenics.dm new file mode 100644 index 000000000000..8e9f5834c675 --- /dev/null +++ b/monkestation/code/modules/virology/reagents/antipathenogenics.dm @@ -0,0 +1,28 @@ +/datum/reagent/medicine/antipathogenic + name = "Placebo" + description = "Highly ineffective, don't bet on those to keep you healthy." + color = "#006600" //rgb: 000, 102, 000 + data = list( + "threshold" = 0, + ) + + +/datum/reagent/medicine/antipathogenic/on_mob_life(mob/living/carbon/M, seconds_per_tick, times_fired) + if(..()) + return TRUE + M.immune_system.ApplyAntipathogenics(data["threshold"]) + +/datum/reagent/medicine/antipathogenic/spaceacillin + data = list( + "threshold" = 50, + ) + +/datum/reagent/consumable/nutriment/soup/chicken_noodle_soup + data = list( + "threshhold" = 20 + ) + +/datum/reagent/consumable/nutriment/soup/chicken_noodle_soup/on_mob_life(mob/living/carbon/M, seconds_per_tick, times_fired) + if(..()) + return TRUE + M.immune_system.ApplyAntipathogenics(data["threshold"]) diff --git a/monkestation/code/modules/virology/reagents/symptom_reagents.dm b/monkestation/code/modules/virology/reagents/symptom_reagents.dm new file mode 100644 index 000000000000..8426ad8603b0 --- /dev/null +++ b/monkestation/code/modules/virology/reagents/symptom_reagents.dm @@ -0,0 +1,40 @@ +/datum/reagent/adrenaline + name = "Liquid Adrenaline" + description = "Dangerous in large quantities" + color = "#ffffff" + chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + overdose_threshold = 11 + metabolization_rate = 1 + +/datum/reagent/adrenaline/on_mob_metabolize(mob/living/L) + . = ..() + L.add_movespeed_modifier(/datum/movespeed_modifier/reagent/stimulants) + ADD_TRAIT(L, TRAIT_BATON_RESISTANCE, type) + +/datum/reagent/adrenaline/on_mob_end_metabolize(mob/living/L) + L.remove_movespeed_modifier(/datum/movespeed_modifier/reagent/stimulants) + REMOVE_TRAIT(L, TRAIT_BATON_RESISTANCE, type) + ..() + +/datum/reagent/adrenaline/on_mob_life(mob/living/carbon/M, seconds_per_tick, times_fired) + . = ..() + M.AdjustStun(-0.5 SECONDS) + M.AdjustKnockdown(-0.5 SECONDS) + M.AdjustUnconscious(-0.5 SECONDS) + M.AdjustImmobilized(-0.5 SECONDS) + M.AdjustParalyzed(-0.5 SECONDS) + M.stamina?.adjust(20) + +/datum/reagent/adrenaline/overdose_start(mob/living/M) + . = ..() + to_chat(M, span_danger("You can feel your everything start to hurt.")) + +/datum/reagent/adrenaline/overdose_process(mob/living/M, seconds_per_tick, times_fired) + M.adjustOrganLoss(ORGAN_SLOT_HEART, 0.2 * seconds_per_tick) + if(SPT_PROB(18, seconds_per_tick)) + M.stamina?.adjust(-2.5, FALSE) + M.adjustToxLoss(1, FALSE, required_biotype = affected_biotype) + M.losebreath++ + . = TRUE + ..() + \ No newline at end of file diff --git a/monkestation/code/modules/virology/reagents/vaccine.dm b/monkestation/code/modules/virology/reagents/vaccine.dm new file mode 100644 index 000000000000..f489a4e4bd87 --- /dev/null +++ b/monkestation/code/modules/virology/reagents/vaccine.dm @@ -0,0 +1,30 @@ +/datum/reagent/vaccine + name = "Vaccine" + description = "A subunit vaccine. Introduces antigens without pathogenic particles to the body, allowing the immune system to produce enough antibodies to prevent any current or future infection." + reagent_state = LIQUID + color = "#A6A6A6" //rgb: 166, 166, 166 + data = list( + "antigen" = list(), + ) + metabolization_rate = 1 + +/datum/reagent/vaccine/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired) + . = ..() + drinker.immune_system.ApplyVaccine(data["antigen"], 1, 30 MINUTES) + +/datum/reagent/vaccine/on_merge(list/mix_data) + if(data && mix_data) + if(data["antigen"] || mix_data["antigen"]) + var/list/mix1 = data["antigen"] + var/list/mix2 = data["antigen"] + + var/list/to_mix = list() + + for(var/antigen in mix1) + to_mix |= antigen + + for(var/antigen in mix2) + to_mix |= antigen + + data["antigen"] = to_mix + return TRUE diff --git a/monkestation/code/modules/virology/research/circuitboards.dm b/monkestation/code/modules/virology/research/circuitboards.dm new file mode 100644 index 000000000000..896acf89affb --- /dev/null +++ b/monkestation/code/modules/virology/research/circuitboards.dm @@ -0,0 +1,37 @@ +/obj/item/circuitboard/machine/centrifuge + name = "Centrifuge" + greyscale_colors = CIRCUIT_COLOR_MEDICAL + build_path = /obj/machinery/disease2/centrifuge + req_components = list( + /datum/stock_part/manipulator = 3 + ) + +/obj/item/circuitboard/machine/diseaseanalyser + name = "Disease Analyzer" + greyscale_colors = CIRCUIT_COLOR_MEDICAL + build_path = /obj/machinery/disease2/diseaseanalyser + req_components = list( + /datum/stock_part/scanning_module = 3, + /datum/stock_part/manipulator = 1, + /datum/stock_part/micro_laser = 1, + ) + +/obj/item/circuitboard/machine/incubator + name = "Pathogenic Incubator" + greyscale_colors = CIRCUIT_COLOR_MEDICAL + build_path = /obj/machinery/disease2/incubator + req_components = list( + /datum/stock_part/scanning_module = 2, + /datum/stock_part/matter_bin = 1, + /datum/stock_part/micro_laser = 2, + ) + +/obj/item/circuitboard/computer/diseasesplicer + name = "Disease Splicer" + greyscale_colors = CIRCUIT_COLOR_MEDICAL + build_path = /obj/machinery/computer/diseasesplicer + +/obj/item/circuitboard/computer/pathology_data + name = "Pathology Data" + greyscale_colors = CIRCUIT_COLOR_MEDICAL + build_path = /obj/machinery/computer/records/pathology diff --git a/monkestation/code/modules/virology/research/research.dm b/monkestation/code/modules/virology/research/research.dm new file mode 100644 index 000000000000..78a13c0fc6d9 --- /dev/null +++ b/monkestation/code/modules/virology/research/research.dm @@ -0,0 +1,71 @@ +/datum/design/board/incubator + name = "Dish Incubator Board" + desc = "The circuit board for a Dish Incubator." + id = "incubator" + build_path = /obj/item/circuitboard/machine/incubator + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_MEDICAL + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL + +/datum/design/board/analyzer + name = "Disease Analyzer Board" + desc = "The circuit board for a Disease Analyzer." + id = "diseaseanalyzer" + build_path = /obj/item/circuitboard/machine/diseaseanalyser + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_MEDICAL + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL + +/datum/design/board/centrifuge + name = "Centrifuge Board" + desc = "The circuit board for a Centrifuge." + id = "centrifuge" + build_path = /obj/item/circuitboard/machine/centrifuge + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_MEDICAL + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL + +/datum/design/board/diseasesplicer + name = "Disease Splicer Board" + desc = "The circuit board for a Disease Splicer." + id = "diseasesplicer" + build_path = /obj/item/circuitboard/computer/diseasesplicer + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_MEDICAL + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL + +/datum/design/antibodyscanner + name = "Immunity Scanner" + id = "antibodyscanner" + build_type = PROTOLATHE | AWAY_LATHE + materials = list(/datum/material/iron = 500, /datum/material/glass = 50) + build_path = /obj/item/device/antibody_scanner + category = list( + RND_CATEGORY_TOOLS + RND_SUBCATEGORY_TOOLS_MEDICAL + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE + +/datum/design/vial + name = "Vial" + id = "vial" + build_type = PROTOLATHE | AWAY_LATHE + materials = list(/datum/material/glass = 200) + build_path = /obj/item/reagent_containers/cup/beaker/vial + category = list( + RND_CATEGORY_TOOLS + RND_SUBCATEGORY_TOOLS_MEDICAL + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE + +/datum/design/board/path_data + name = "Pathology Records Computer Board" + desc = "The circuit board for a Pathology Records Computer." + id = "path_data" + build_path = /obj/item/circuitboard/computer/pathology_data + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_MEDICAL + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL diff --git a/monkestation/code/modules/virology/sounds/behind_you1.ogg b/monkestation/code/modules/virology/sounds/behind_you1.ogg new file mode 100644 index 000000000000..d3ddef748059 Binary files /dev/null and b/monkestation/code/modules/virology/sounds/behind_you1.ogg differ diff --git a/monkestation/code/modules/virology/sounds/behind_you2.ogg b/monkestation/code/modules/virology/sounds/behind_you2.ogg new file mode 100644 index 000000000000..530a71698cb2 Binary files /dev/null and b/monkestation/code/modules/virology/sounds/behind_you2.ogg differ diff --git a/monkestation/code/modules/virology/sounds/ear_ring_single.ogg b/monkestation/code/modules/virology/sounds/ear_ring_single.ogg new file mode 100644 index 000000000000..1b8d9685ca26 Binary files /dev/null and b/monkestation/code/modules/virology/sounds/ear_ring_single.ogg differ diff --git a/monkestation/code/modules/virology/sounds/far_noise.ogg b/monkestation/code/modules/virology/sounds/far_noise.ogg new file mode 100644 index 000000000000..11d849a89f22 Binary files /dev/null and b/monkestation/code/modules/virology/sounds/far_noise.ogg differ diff --git a/monkestation/code/modules/virology/sounds/ghost.ogg b/monkestation/code/modules/virology/sounds/ghost.ogg new file mode 100644 index 000000000000..e6b6da36e53e Binary files /dev/null and b/monkestation/code/modules/virology/sounds/ghost.ogg differ diff --git a/monkestation/code/modules/virology/sounds/ghost2.ogg b/monkestation/code/modules/virology/sounds/ghost2.ogg new file mode 100644 index 000000000000..ed384e40d22b Binary files /dev/null and b/monkestation/code/modules/virology/sounds/ghost2.ogg differ diff --git a/monkestation/code/modules/virology/sounds/growl1.ogg b/monkestation/code/modules/virology/sounds/growl1.ogg new file mode 100644 index 000000000000..80aca01ce754 Binary files /dev/null and b/monkestation/code/modules/virology/sounds/growl1.ogg differ diff --git a/monkestation/code/modules/virology/sounds/growl2.ogg b/monkestation/code/modules/virology/sounds/growl2.ogg new file mode 100644 index 000000000000..f10abf0fb26f Binary files /dev/null and b/monkestation/code/modules/virology/sounds/growl2.ogg differ diff --git a/monkestation/code/modules/virology/sounds/growl3.ogg b/monkestation/code/modules/virology/sounds/growl3.ogg new file mode 100644 index 000000000000..3f3799bc0f27 Binary files /dev/null and b/monkestation/code/modules/virology/sounds/growl3.ogg differ diff --git a/monkestation/code/modules/virology/sounds/heart_beat_single.ogg b/monkestation/code/modules/virology/sounds/heart_beat_single.ogg new file mode 100644 index 000000000000..52dd5ac41399 Binary files /dev/null and b/monkestation/code/modules/virology/sounds/heart_beat_single.ogg differ diff --git a/monkestation/code/modules/virology/sounds/i_see_you1.ogg b/monkestation/code/modules/virology/sounds/i_see_you1.ogg new file mode 100644 index 000000000000..66d5b65d5983 Binary files /dev/null and b/monkestation/code/modules/virology/sounds/i_see_you1.ogg differ diff --git a/monkestation/code/modules/virology/sounds/i_see_you2.ogg b/monkestation/code/modules/virology/sounds/i_see_you2.ogg new file mode 100644 index 000000000000..da908f2c4b4a Binary files /dev/null and b/monkestation/code/modules/virology/sounds/i_see_you2.ogg differ diff --git a/monkestation/code/modules/virology/sounds/im_here1.ogg b/monkestation/code/modules/virology/sounds/im_here1.ogg new file mode 100644 index 000000000000..b3a7142365e0 Binary files /dev/null and b/monkestation/code/modules/virology/sounds/im_here1.ogg differ diff --git a/monkestation/code/modules/virology/sounds/im_here2.ogg b/monkestation/code/modules/virology/sounds/im_here2.ogg new file mode 100644 index 000000000000..bd841bcdb14f Binary files /dev/null and b/monkestation/code/modules/virology/sounds/im_here2.ogg differ diff --git a/monkestation/code/modules/virology/sounds/look_up1.ogg b/monkestation/code/modules/virology/sounds/look_up1.ogg new file mode 100644 index 000000000000..045d5d4a768f Binary files /dev/null and b/monkestation/code/modules/virology/sounds/look_up1.ogg differ diff --git a/monkestation/code/modules/virology/sounds/look_up2.ogg b/monkestation/code/modules/virology/sounds/look_up2.ogg new file mode 100644 index 000000000000..1e6f14b21be4 Binary files /dev/null and b/monkestation/code/modules/virology/sounds/look_up2.ogg differ diff --git a/monkestation/code/modules/virology/sounds/over_here1.ogg b/monkestation/code/modules/virology/sounds/over_here1.ogg new file mode 100644 index 000000000000..90f557b005b7 Binary files /dev/null and b/monkestation/code/modules/virology/sounds/over_here1.ogg differ diff --git a/monkestation/code/modules/virology/sounds/over_here2.ogg b/monkestation/code/modules/virology/sounds/over_here2.ogg new file mode 100644 index 000000000000..2719e204a271 Binary files /dev/null and b/monkestation/code/modules/virology/sounds/over_here2.ogg differ diff --git a/monkestation/code/modules/virology/sounds/over_here3.ogg b/monkestation/code/modules/virology/sounds/over_here3.ogg new file mode 100644 index 000000000000..1ef1a1d8340f Binary files /dev/null and b/monkestation/code/modules/virology/sounds/over_here3.ogg differ diff --git a/monkestation/code/modules/virology/sounds/scary.ogg b/monkestation/code/modules/virology/sounds/scary.ogg new file mode 100644 index 000000000000..fbd16f32574f Binary files /dev/null and b/monkestation/code/modules/virology/sounds/scary.ogg differ diff --git a/monkestation/code/modules/virology/sounds/screech.ogg b/monkestation/code/modules/virology/sounds/screech.ogg new file mode 100644 index 000000000000..b90f612621e7 Binary files /dev/null and b/monkestation/code/modules/virology/sounds/screech.ogg differ diff --git a/monkestation/code/modules/virology/sounds/turn_around1.ogg b/monkestation/code/modules/virology/sounds/turn_around1.ogg new file mode 100644 index 000000000000..7a83755a93fb Binary files /dev/null and b/monkestation/code/modules/virology/sounds/turn_around1.ogg differ diff --git a/monkestation/code/modules/virology/sounds/turn_around2.ogg b/monkestation/code/modules/virology/sounds/turn_around2.ogg new file mode 100644 index 000000000000..f4328458dd9e Binary files /dev/null and b/monkestation/code/modules/virology/sounds/turn_around2.ogg differ diff --git a/monkestation/code/modules/virology/sounds/veryfar_noise.ogg b/monkestation/code/modules/virology/sounds/veryfar_noise.ogg new file mode 100644 index 000000000000..b27ce5dce999 Binary files /dev/null and b/monkestation/code/modules/virology/sounds/veryfar_noise.ogg differ diff --git a/monkestation/code/modules/virology/sounds/wail.ogg b/monkestation/code/modules/virology/sounds/wail.ogg new file mode 100644 index 000000000000..66318be3e94e Binary files /dev/null and b/monkestation/code/modules/virology/sounds/wail.ogg differ diff --git a/monkestation/code/modules/virology/subsystems/pathogen_clouds.dm b/monkestation/code/modules/virology/subsystems/pathogen_clouds.dm new file mode 100644 index 000000000000..6d3c50b01d7c --- /dev/null +++ b/monkestation/code/modules/virology/subsystems/pathogen_clouds.dm @@ -0,0 +1,79 @@ +SUBSYSTEM_DEF(pathogen_clouds) + name = "Pathogen Clouds" + init_order = INIT_ORDER_PATHOGEN + priority = FIRE_PRIORITY_PATHOGEN + wait = 1 SECONDS + flags = SS_BACKGROUND + runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + + var/list/current_run_cores = list() + var/list/current_run_clouds = list() + var/list/cores = list() + var/list/clouds = list() + var/current_run_level = "clouds" + + +/datum/controller/subsystem/pathogen_clouds/stat_entry(msg) + msg += "Run Cores:[length(current_run_cores)]" + msg += "Cores:[length(cores)]" + msg += "Run Clouds:[length(current_run_clouds)]" + msg += "Clouds:[length(clouds)]" + return ..() + + +/datum/controller/subsystem/pathogen_clouds/Initialize() + return SS_INIT_SUCCESS + +/datum/controller/subsystem/pathogen_clouds/fire(resumed = FALSE) + + if(!length(cores) && !length(clouds)) + current_run_clouds = list() + current_run_cores = list() + return + + if(current_run_level == "clouds") + for(var/obj/effect/pathogen_cloud/cloud as anything in current_run_clouds) + if(QDELETED(cloud) || isnull(cloud)) + current_run_clouds -= cloud + continue + //If we exist ontop of a core transfer viruses and die unless parent this means something moved back. + //This should prevent mobs breathing in hundreds of clouds at once + for(var/obj/effect/pathogen_cloud/core/core in cloud.loc) + for(var/datum/disease/advanced/V as anything in cloud.viruses) + if("[V.uniqueID]-[V.subID]" in core.id_list) + continue + core.viruses |= V.Copy() + core.modified = TRUE + qdel(cloud) + CHECK_TICK + current_run_clouds -= cloud + current_run_level = "cores" + if(!length(current_run_clouds)) + for(var/obj/effect/pathogen_cloud/cloud as anything in clouds) + if(QDELETED(cloud)) + clouds -= cloud + current_run_clouds = clouds.Copy() + + if(current_run_level == "cores") + for(var/obj/effect/pathogen_cloud/core as anything in current_run_cores) + if(QDELETED(core) || isnull(core)) + current_run_cores -= core + continue + + if(!core.moving || core.target == get_turf(core)) + for (var/obj/effect/pathogen_cloud/core/other_C in core.loc) + if(other_C == core) + return + if (!other_C.moving) + for(var/datum/disease/advanced/V as anything in other_C.viruses) + if("[V.uniqueID]-[V.subID]" in core.id_list) + continue + core.viruses |= V.Copy() + core.modified = TRUE + qdel(other_C) + CHECK_TICK + core.moving = FALSE + current_run_cores -= core + current_run_level = "clouds" + if(!length(current_run_cores)) + current_run_cores = cores.Copy() diff --git a/strings/phobia.json b/strings/phobia.json index 4f41453dad12..1ba55311c6d4 100644 --- a/strings/phobia.json +++ b/strings/phobia.json @@ -148,8 +148,8 @@ "nurse", "sleeper", "viro", - "virologist", - "virology" + "pathologist", + "pathology" ], "authority": [ diff --git a/tgstation.dme b/tgstation.dme index c97b041ec399..49b94b1685dc 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -404,6 +404,7 @@ #include "code\__DEFINES\~monkestation\status_effects.dm" #include "code\__DEFINES\~monkestation\storytellers.dm" #include "code\__DEFINES\~monkestation\traits.dm" +#include "code\__DEFINES\~monkestation\virology.dm" #include "code\__DEFINES\~monkestation\dcs\signals\signals_atom.dm" #include "code\__DEFINES\~monkestation\dcs\signals\signals_carbon.dm" #include "code\__DEFINES\~monkestation\dcs\signals\signals_traitor.dm" @@ -519,6 +520,7 @@ #include "code\__HELPERS\sorts\MergeSort.dm" #include "code\__HELPERS\sorts\TimSort.dm" #include "code\__HELPERS\~monkestation-helpers\icon_smoothing.dm" +#include "code\__HELPERS\~monkestation-helpers\virology.dm" #include "code\_globalvars\_regexes.dm" #include "code\_globalvars\admin.dm" #include "code\_globalvars\bitfields.dm" @@ -1229,61 +1231,28 @@ #include "code\datums\components\trader\trader.dm" #include "code\datums\diseases\_disease.dm" #include "code\datums\diseases\_MobProcs.dm" -#include "code\datums\diseases\adrenal_crisis.dm" -#include "code\datums\diseases\anxiety.dm" -#include "code\datums\diseases\beesease.dm" #include "code\datums\diseases\brainrot.dm" #include "code\datums\diseases\cold.dm" #include "code\datums\diseases\cold9.dm" -#include "code\datums\diseases\decloning.dm" -#include "code\datums\diseases\dna_spread.dm" #include "code\datums\diseases\fake_gbs.dm" #include "code\datums\diseases\flu.dm" #include "code\datums\diseases\fluspanish.dm" #include "code\datums\diseases\gastrolisis.dm" #include "code\datums\diseases\gbs.dm" -#include "code\datums\diseases\heart_failure.dm" -#include "code\datums\diseases\magnitis.dm" #include "code\datums\diseases\parasitic_infection.dm" #include "code\datums\diseases\parrotpossession.dm" -#include "code\datums\diseases\pierrot_throat.dm" -#include "code\datums\diseases\retrovirus.dm" -#include "code\datums\diseases\rhumba_beat.dm" -#include "code\datums\diseases\transformation.dm" #include "code\datums\diseases\tuberculosis.dm" -#include "code\datums\diseases\wizarditis.dm" #include "code\datums\diseases\advance\advance.dm" #include "code\datums\diseases\advance\presets.dm" -#include "code\datums\diseases\advance\symptoms\beard.dm" -#include "code\datums\diseases\advance\symptoms\chills.dm" -#include "code\datums\diseases\advance\symptoms\choking.dm" -#include "code\datums\diseases\advance\symptoms\confusion.dm" -#include "code\datums\diseases\advance\symptoms\cough.dm" -#include "code\datums\diseases\advance\symptoms\deafness.dm" -#include "code\datums\diseases\advance\symptoms\disfiguration.dm" #include "code\datums\diseases\advance\symptoms\dizzy.dm" #include "code\datums\diseases\advance\symptoms\fever.dm" #include "code\datums\diseases\advance\symptoms\fire.dm" #include "code\datums\diseases\advance\symptoms\flesh_eating.dm" -#include "code\datums\diseases\advance\symptoms\genetics.dm" -#include "code\datums\diseases\advance\symptoms\hallucigen.dm" #include "code\datums\diseases\advance\symptoms\headache.dm" #include "code\datums\diseases\advance\symptoms\heal.dm" -#include "code\datums\diseases\advance\symptoms\itching.dm" -#include "code\datums\diseases\advance\symptoms\narcolepsy.dm" #include "code\datums\diseases\advance\symptoms\oxygen.dm" -#include "code\datums\diseases\advance\symptoms\sensory.dm" -#include "code\datums\diseases\advance\symptoms\shedding.dm" -#include "code\datums\diseases\advance\symptoms\skin.dm" -#include "code\datums\diseases\advance\symptoms\sneeze.dm" -#include "code\datums\diseases\advance\symptoms\species.dm" #include "code\datums\diseases\advance\symptoms\symptoms.dm" -#include "code\datums\diseases\advance\symptoms\viral.dm" -#include "code\datums\diseases\advance\symptoms\vision.dm" #include "code\datums\diseases\advance\symptoms\voice_change.dm" -#include "code\datums\diseases\advance\symptoms\vomit.dm" -#include "code\datums\diseases\advance\symptoms\weight.dm" -#include "code\datums\diseases\advance\symptoms\youth.dm" #include "code\datums\elements\_element.dm" #include "code\datums\elements\ai_control_examine.dm" #include "code\datums\elements\ai_flee_while_injured.dm" @@ -6219,6 +6188,9 @@ #include "monkestation\code\modules\mentor\mentor_pm.dm" #include "monkestation\code\modules\mentor\mentor_say.dm" #include "monkestation\code\modules\mentor\mentor_who.dm" +#include "monkestation\code\modules\metrics\metric_subsystem.dm" +#include "monkestation\code\modules\metrics\sql_logging_subsystem.dm" +#include "monkestation\code\modules\metrics\subsystem_analytics\generics.dm" #include "monkestation\code\modules\mob\mob.dm" #include "monkestation\code\modules\mob\mob_defines.dm" #include "monkestation\code\modules\mob\dead\new_player\sprite_accessories\_base.dm" @@ -6585,6 +6557,56 @@ #include "monkestation\code\modules\vehicles\monkey_ball.dm" #include "monkestation\code\modules\vehicles\mecha\mecha_actions.dm" #include "monkestation\code\modules\vehicles\mecha\equipment\tools\other_tools.dm" +#include "monkestation\code\modules\virology\__base_procs.dm" +#include "monkestation\code\modules\virology\__effects.dm" +#include "monkestation\code\modules\virology\fullscreen.dm" +#include "monkestation\code\modules\virology\disease\_disease.dm" +#include "monkestation\code\modules\virology\disease\premades\_base_premades.dm" +#include "monkestation\code\modules\virology\disease\premades\adrenal_crisis.dm" +#include "monkestation\code\modules\virology\disease\premades\anxiety.dm" +#include "monkestation\code\modules\virology\disease\premades\decloning.dm" +#include "monkestation\code\modules\virology\disease\premades\flu.dm" +#include "monkestation\code\modules\virology\disease\premades\heart_attack.dm" +#include "monkestation\code\modules\virology\disease\premades\transformations.dm" +#include "monkestation\code\modules\virology\disease\symtoms\_symptom.dm" +#include "monkestation\code\modules\virology\disease\symtoms\animation_procs.dm" +#include "monkestation\code\modules\virology\disease\symtoms\stage1.dm" +#include "monkestation\code\modules\virology\disease\symtoms\stage2.dm" +#include "monkestation\code\modules\virology\disease\symtoms\stage3.dm" +#include "monkestation\code\modules\virology\disease\symtoms\stage4.dm" +#include "monkestation\code\modules\virology\disease\symtoms\restricted\stage1.dm" +#include "monkestation\code\modules\virology\disease\symtoms\restricted\stage2.dm" +#include "monkestation\code\modules\virology\disease\symtoms\restricted\stage3.dm" +#include "monkestation\code\modules\virology\disease\symtoms\restricted\stage4.dm" +#include "monkestation\code\modules\virology\effects\cleanables.dm" +#include "monkestation\code\modules\virology\effects\pathogen_cloud.dm" +#include "monkestation\code\modules\virology\immune_systems\_immune_system.dm" +#include "monkestation\code\modules\virology\items\_base_item_additions.dm" +#include "monkestation\code\modules\virology\items\antibodyscanner.dm" +#include "monkestation\code\modules\virology\items\disease_disk.dm" +#include "monkestation\code\modules\virology\items\mousecubes.dm" +#include "monkestation\code\modules\virology\items\science_goggles.dm" +#include "monkestation\code\modules\virology\items\vials.dm" +#include "monkestation\code\modules\virology\items\virusdish.dm" +#include "monkestation\code\modules\virology\items\clothing\mask.dm" +#include "monkestation\code\modules\virology\items\clothing\shoes.dm" +#include "monkestation\code\modules\virology\living\infection_procs.dm" +#include "monkestation\code\modules\virology\living\mouse.dm" +#include "monkestation\code\modules\virology\living\spread_disease.dm" +#include "monkestation\code\modules\virology\living\sterile_procs.dm" +#include "monkestation\code\modules\virology\machines\analyzer.dm" +#include "monkestation\code\modules\virology\machines\centrifuge.dm" +#include "monkestation\code\modules\virology\machines\curer.dm" +#include "monkestation\code\modules\virology\machines\disease_records.dm" +#include "monkestation\code\modules\virology\machines\incubator.dm" +#include "monkestation\code\modules\virology\machines\isolator.dm" +#include "monkestation\code\modules\virology\machines\splicer.dm" +#include "monkestation\code\modules\virology\reagents\antipathenogenics.dm" +#include "monkestation\code\modules\virology\reagents\symptom_reagents.dm" +#include "monkestation\code\modules\virology\reagents\vaccine.dm" +#include "monkestation\code\modules\virology\research\circuitboards.dm" +#include "monkestation\code\modules\virology\research\research.dm" +#include "monkestation\code\modules\virology\subsystems\pathogen_clouds.dm" #include "monkestation\code\modules\wiki_templater\templates.dm" #include "monkestation\code\modules\wiki_templater\test_generate.dm" #include "monkestation\code\random_rooms\room_base.dm" diff --git a/tgui/packages/tgui/interfaces/DiseaseIncubator.js b/tgui/packages/tgui/interfaces/DiseaseIncubator.js new file mode 100644 index 000000000000..31779ee701ca --- /dev/null +++ b/tgui/packages/tgui/interfaces/DiseaseIncubator.js @@ -0,0 +1,80 @@ +import { useBackend } from '../backend'; +import { Button, LabeledList, Section } from '../components'; +import { Window } from '../layouts'; + +export const DiseaseIncubator = (props, context) => { + const { act, data } = useBackend(context); + const { dishes, on, can_focus } = data; + return ( + +
act('power')} + /> + }> +
+ {dishes.map((dish) => ( +
+ {' '} +
+ ))} +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/DiseaseSplicer.js b/tgui/packages/tgui/interfaces/DiseaseSplicer.js new file mode 100644 index 000000000000..10d190a82371 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DiseaseSplicer.js @@ -0,0 +1,98 @@ +import { useBackend } from '../backend'; +import { Button, Dimmer, LabeledList, Icon, Slider, NoticeBox, Section } from '../components'; +import { Window } from '../layouts'; + +export const DiseaseSplicer = (props, context) => { + const { act, data } = useBackend(context); + const { + splicing, + scanning, + burning, + dish_name, + memorybank, + dish_error, + target_stage, + } = data; + return ( + + + {!!splicing && ( + + + {' Splicing...'} + + )} + {!!burning && ( + + + {' Burning...'} + + )} + {!!scanning && ( + + + {' Scanning...'} + + )} +
+
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PathologyRecords/RecordTabs.tsx b/tgui/packages/tgui/interfaces/PathologyRecords/RecordTabs.tsx new file mode 100644 index 000000000000..20c365f776cd --- /dev/null +++ b/tgui/packages/tgui/interfaces/PathologyRecords/RecordTabs.tsx @@ -0,0 +1,73 @@ +import { sortBy } from 'common/collections'; +import { flow } from 'common/fp'; +import { useBackend, useLocalState } from 'tgui/backend'; +import { Stack, Section, Tabs, NoticeBox, Box, Icon } from 'tgui/components'; +import { MedicalRecord, MedicalRecordData } from './types'; + +/** Displays all found records. */ +export const MedicalRecordTabs = (props, context) => { + const { act, data } = useBackend(context); + const { records = [], station_z } = data; + + const errorMessage = !records.length + ? 'No records found.' + : 'No match. Refine your search.'; + + const [search, setSearch] = useLocalState(context, 'search', ''); + + const sorted: MedicalRecord[] = flow([ + sortBy((record: MedicalRecord) => record.name?.toLowerCase()), + ])(records); + + return ( + + +
+ + {!sorted.length ? ( + {errorMessage} + ) : ( + sorted.map((record, index) => ( + + )) + )} + +
+
+
+ ); +}; + +/** Individual crew tab */ +const CrewTab = (props: { record: MedicalRecord }, context) => { + const [selectedRecord, setSelectedRecord] = useLocalState< + MedicalRecord | undefined + >(context, 'medicalRecord', undefined); + + const { act, data } = useBackend(context); + const { assigned_view } = data; + const { record } = props; + const { crew_ref, name, nickname } = record; + + /** Sets the record to preview */ + const selectRecord = (record: MedicalRecord) => { + if (selectedRecord?.crew_ref === crew_ref) { + setSelectedRecord(undefined); + } else { + setSelectedRecord(record); + act('view_record', { assigned_view: assigned_view, crew_ref: crew_ref }); + } + }; + + return ( + selectRecord(record)} + selected={selectedRecord?.crew_ref === crew_ref}> + + {nickname ? nickname : name} + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PathologyRecords/RecordView.tsx b/tgui/packages/tgui/interfaces/PathologyRecords/RecordView.tsx new file mode 100644 index 000000000000..2bf1b467b26b --- /dev/null +++ b/tgui/packages/tgui/interfaces/PathologyRecords/RecordView.tsx @@ -0,0 +1,141 @@ +import { Input, Box, Stack, Section, NoticeBox, LabeledList, Button } from 'tgui/components'; +import { getMedicalRecord } from './helpers'; +import { useBackend, useLocalState } from '../../backend'; +import { MedicalRecordData } from './types'; + +/** Views a selected record. */ +export const MedicalRecordView = (props, context) => { + const foundRecord = getMedicalRecord(context); + if (!foundRecord) return No record selected.; + + const { act, data } = useBackend(context); + const { assigned_view, station_z } = data; + + const { + crew_ref, + id, + sub, + child, + form, + name, + nickname, + description, + antigen, + spread_flags, + danger, + } = foundRecord; + const textHtml = { + __html: description, + }; + return ( + + +
act('expunge_record', { crew_ref: crew_ref })} + tooltip="Expunge record data." + /> + } + fill + scrollable + title={name} + wrap> + + + + + + {id}-{sub}-{child} + + {form} + + {spread_flags} + + {antigen} + + + + + + + +
+
+
+ ); +}; + +type Props = { + color?: string; + field: string; + target_ref: string; + text: string; +}; + +const EditableText = (props: Props, context) => { + const { color, field, target_ref, text } = props; + if (!field) return <> ; + + const { act } = useBackend(context); + const [editing, setEditing] = useLocalState( + context, + `editing_${field}`, + false + ); + + return editing ? ( + setEditing(false)} + onEnter={(event, value) => { + setEditing(false); + act('edit_field', { field: field, crew_ref: target_ref, value: value }); + }} + value={text} + /> + ) : ( + + + setEditing(true)}> + {!text ? '(none)' : text} + + + + + + + + + ); +}; + +const AuthView = (props, context) => { + const { act } = useBackend(context); + + return ( + <> + + + + + + + + + + + Secure Your Workspace. + + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/PathologyRecords/types.ts b/tgui/packages/tgui/interfaces/PathologyRecords/types.ts new file mode 100644 index 000000000000..be7665efc31e --- /dev/null +++ b/tgui/packages/tgui/interfaces/PathologyRecords/types.ts @@ -0,0 +1,33 @@ +import { BooleanLike } from 'common/react'; + +export type MedicalRecordData = { + assigned_view: string; + authenticated: BooleanLike; + station_z: BooleanLike; + physical_statuses: string[]; + mental_statuses: string[]; + records: MedicalRecord[]; + min_age: number; + max_age: number; +}; + +export type MedicalRecord = { + crew_ref: string; + name: string; + nickname: string; + sub: string; + id: string; + child: string; + description: string; + spread_flags: string; + danger: string; + antigen: string; + form: string; +}; + +export type MedicalNote = { + author: string; + content: string; + note_ref: string; + time: string; +};