diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_sloth.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_sloth.dmm index 4c3449a8e7f..272cd2e47a3 100644 --- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_sloth.dmm +++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_sloth.dmm @@ -25,6 +25,7 @@ /area/ruin/powered/sloth) "f" = ( /obj/structure/table/wood, +/obj/item/organ/internal/kidneys/xenobiology/lethargic, /obj/item/food/grown/citrus/orange, /turf/simulated/floor/sepia{ slowdown = 10 diff --git a/_maps/map_files/generic/centcomm.dmm b/_maps/map_files/generic/centcomm.dmm index c4d86937962..2b5afc58d7d 100644 --- a/_maps/map_files/generic/centcomm.dmm +++ b/_maps/map_files/generic/centcomm.dmm @@ -6052,6 +6052,7 @@ pixel_x = -2; pixel_y = 1 }, +/obj/item/dissector, /turf/simulated/floor/plasteel/freezer, /area/admin) "vl" = ( @@ -6240,6 +6241,8 @@ "vM" = ( /obj/structure/table, /obj/item/storage/toolbox/syndicate, +/obj/item/organ/internal/heart/xenobiology/cursed_bananium, +/obj/item/organ/internal/heart/xenobiology/bananium, /turf/simulated/floor/plasteel/dark, /area/admin) "vN" = ( @@ -6457,6 +6460,7 @@ pixel_x = 5; pixel_y = 5 }, +/obj/item/dissector/alien, /turf/simulated/floor/plasteel/freezer, /area/admin) "wA" = ( diff --git a/_maps/map_files/stations/boxstation.dmm b/_maps/map_files/stations/boxstation.dmm index 42015ddd2cc..aea320462ac 100644 --- a/_maps/map_files/stations/boxstation.dmm +++ b/_maps/map_files/stations/boxstation.dmm @@ -24059,7 +24059,7 @@ /turf/simulated/floor/plasteel, /area/station/maintenance/apmaint) "bER" = ( -/obj/effect/landmark/start/scientist, +/obj/effect/landmark/start/xenobiologist, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -38115,6 +38115,9 @@ /obj/effect/mapping_helpers/airlock/windoor/access/any/science/tox{ dir = 1 }, +/obj/effect/mapping_helpers/airlock/windoor/access/any/science/xenobio{ + dir = 1 + }, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "whitepurple" @@ -40542,7 +40545,7 @@ /turf/simulated/floor/plasteel, /area/station/engineering/atmos) "cSf" = ( -/obj/machinery/requests_console/directional/north, +/obj/machinery/organ_analyzer, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurple" @@ -40555,6 +40558,12 @@ /obj/item/clothing/glasses/science, /obj/structure/sign/poster/official/safety_eye_protection/directional/north, /obj/structure/shelf/science, +/obj/machinery/camera{ + c_tag = "Xenobiology Module North"; + network = list("Research","SS13") + }, +/obj/item/reagent_containers/glass/bottle/reagent/formaldehyde, +/obj/item/reagent_containers/glass/bottle/reagent/formaldehyde, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurple" @@ -40676,22 +40685,6 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/plasteel, /area/station/command/office/ce) -"cSH" = ( -/obj/machinery/processor{ - name = "Slime Processor" - }, -/obj/machinery/camera{ - c_tag = "Xenobiology North"; - network = list("Research","SS13") - }, -/obj/machinery/light{ - dir = 1 - }, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "whitepurple" - }, -/area/station/science/xenobiology) "cSI" = ( /turf/simulated/floor/engine, /area/station/engineering/engine/supermatter) @@ -42422,18 +42415,26 @@ /turf/simulated/floor/plating, /area/station/maintenance/apmaint2) "cZy" = ( -/obj/structure/table/glass, -/obj/item/clothing/gloves/color/latex, +/obj/machinery/optable, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "whitepurple" }, /area/station/science/xenobiology) "cZz" = ( -/obj/structure/reagent_dispensers/watertank, /obj/machinery/power/apc/directional/east, /obj/structure/cable, /obj/structure/disposalpipe/segment, +/obj/structure/table/glass, +/obj/item/stack/sheet/mineral/plasma{ + pixel_x = -2; + pixel_y = -2 + }, +/obj/item/stack/sheet/mineral/plasma, +/obj/item/stack/sheet/mineral/plasma{ + pixel_x = 2; + pixel_y = 2 + }, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "whitepurple" @@ -47697,7 +47698,7 @@ /obj/structure/chair{ dir = 4 }, -/obj/effect/landmark/start/scientist, +/obj/effect/landmark/start/xenobiologist, /turf/simulated/floor/plasteel{ icon_state = "whitepurplefull" }, @@ -54999,6 +55000,16 @@ icon_state = "white" }, /area/station/science/toxins/mixing) +"hhl" = ( +/obj/machinery/camera{ + c_tag = "Xenobiology Module East"; + dir = 1; + network = list("Research","SS13") + }, +/obj/machinery/alarm/directional/south, +/obj/structure/reagent_dispensers/watertank, +/turf/simulated/floor/plasteel, +/area/station/science/xenobiology) "hhE" = ( /obj/structure/mirror{ pixel_x = -28 @@ -55286,20 +55297,12 @@ }, /area/station/hallway/secondary/exit) "hqo" = ( -/obj/item/stack/sheet/mineral/plasma{ - pixel_x = -2; - pixel_y = -2 +/obj/machinery/computer/operating{ + dir = 4; + name = "dissection computer" }, -/obj/item/stack/sheet/mineral/plasma, -/obj/item/stack/sheet/mineral/plasma{ - pixel_x = 2; - pixel_y = 2 - }, -/obj/structure/table/glass, -/obj/machinery/newscaster/directional/west, /turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "whitepurple" + icon_state = "white" }, /area/station/science/xenobiology) "hqx" = ( @@ -56379,15 +56382,6 @@ /obj/structure/flora/junglebush/large, /turf/simulated/floor/grass/jungle/no_creep, /area/station/command/bridge) -"hTF" = ( -/obj/machinery/camera{ - c_tag = "Xenobiology East"; - dir = 1; - network = list("Research","SS13") - }, -/obj/machinery/alarm/directional/south, -/turf/simulated/floor/plasteel, -/area/station/science/xenobiology) "hTK" = ( /obj/structure/sink/directional/east, /obj/structure/mirror{ @@ -64945,7 +64939,6 @@ /obj/machinery/door/airlock/research{ name = "Test Lab" }, -/obj/effect/mapping_helpers/airlock/access/all/science/tox, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -64955,6 +64948,8 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/effect/mapping_helpers/airlock/access/any/science/xenobio, +/obj/effect/mapping_helpers/airlock/access/any/science/tox, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -66928,11 +66923,12 @@ /area/station/hallway/primary/starboard/west) "nqa" = ( /obj/structure/window/reinforced, -/obj/structure/table, /obj/item/storage/box/beakers{ pixel_x = 2; pixel_y = 2 }, +/obj/item/clothing/gloves/color/latex, +/obj/structure/table/reinforced, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "whitepurple" @@ -68151,7 +68147,16 @@ /area/station/public/dorms) "nXu" = ( /obj/structure/window/reinforced, -/obj/machinery/smithing/scientific_assembler, +/obj/structure/table/reinforced, +/obj/item/slime_scanner, +/obj/item/slime_scanner, +/obj/item/reagent_containers/spray/cleaner{ + pixel_x = 3; + pixel_y = -4 + }, +/obj/item/reagent_containers/glass/bottle/reagent/morphine{ + pixel_x = -13 + }, /turf/simulated/floor/plasteel{ icon_state = "whitepurple" }, @@ -69555,6 +69560,12 @@ /obj/structure/cable{ icon_state = "1-2" }, +/obj/structure/table/reinforced, +/obj/item/extinguisher, +/obj/item/extinguisher{ + pixel_x = 4; + pixel_y = 4 + }, /turf/simulated/floor/plasteel, /area/station/science/xenobiology) "oDG" = ( @@ -69998,10 +70009,9 @@ /area/station/supply/miningdock) "oLS" = ( /obj/structure/window/reinforced, -/obj/structure/table, -/obj/item/reagent_containers/spray/cleaner{ - pixel_x = -3 - }, +/obj/structure/table/reinforced, +/obj/item/storage/surgical_tray, +/obj/item/dissector, /turf/simulated/floor/plasteel{ icon_state = "whitepurple" }, @@ -73907,12 +73917,12 @@ /obj/effect/mapping_helpers/airlock/windoor/autoname/desk{ dir = 4 }, -/obj/effect/mapping_helpers/airlock/windoor/access/any/science/research{ - dir = 4 - }, /obj/item/desk_bell{ anchored = 1 }, +/obj/effect/mapping_helpers/airlock/windoor/access/any/science/tox{ + dir = 4 + }, /turf/simulated/floor/plasteel{ icon_state = "whitepurplefull" }, @@ -74162,6 +74172,8 @@ int_button_link_id = "xeno_btn_int" }, /obj/structure/disposalpipe/segment, +/obj/structure/table/glass, +/obj/item/storage/belt/medical/surgery, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "whitepurple" @@ -76300,6 +76312,22 @@ }, /turf/simulated/floor/bluegrid, /area/station/telecomms/chamber) +"rTH" = ( +/obj/machinery/processor{ + name = "Slime Processor" + }, +/obj/machinery/light{ + dir = 1 + }, +/obj/machinery/camera{ + c_tag = "Xenobiology North"; + network = list("Research","SS13") + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "whitepurple" + }, +/area/station/science/xenobiology) "rUf" = ( /obj/machinery/door/airlock/maintenance{ electrified_until = -1; @@ -79142,13 +79170,9 @@ /turf/simulated/floor/plating, /area/station/maintenance/apmaint2) "tAp" = ( -/obj/item/extinguisher, -/obj/item/extinguisher{ - pixel_x = 4; - pixel_y = 4 - }, -/obj/structure/table/glass, /obj/machinery/firealarm/directional/west, +/obj/machinery/requests_console/directional/north, +/obj/machinery/smithing/scientific_assembler, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "whitepurple" @@ -80635,9 +80659,10 @@ /obj/machinery/door/airlock/research/glass{ name = "Test Chamber" }, -/obj/effect/mapping_helpers/airlock/access/all/science/research, /obj/structure/disposalpipe/segment, /obj/machinery/door/firedoor, +/obj/effect/mapping_helpers/airlock/access/any/science/tox, +/obj/effect/mapping_helpers/airlock/access/any/science/xenobio, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -80945,10 +80970,10 @@ }, /area/station/medical/medbay) "urf" = ( -/obj/effect/landmark/start/scientist, /obj/structure/chair{ dir = 8 }, +/obj/effect/landmark/start/xenobiologist, /turf/simulated/floor/plasteel{ icon_state = "whitepurplefull" }, @@ -82205,10 +82230,11 @@ /obj/machinery/door/airlock/research{ name = "Test Lab" }, -/obj/effect/mapping_helpers/airlock/access/all/science/tox, /obj/structure/cable{ icon_state = "1-2" }, +/obj/effect/mapping_helpers/airlock/access/any/science/xenobio, +/obj/effect/mapping_helpers/airlock/access/any/science/tox, /turf/simulated/floor/plasteel, /area/station/science/misc_lab) "vet" = ( @@ -87689,15 +87715,7 @@ }, /area/station/science/xenobiology) "xUK" = ( -/obj/structure/table/glass, -/obj/item/radio/intercom{ - name = "west bump"; - pixel_x = -28 - }, -/obj/item/slime_scanner, -/obj/item/slime_scanner, -/obj/item/hemostat, -/obj/item/scalpel, +/obj/machinery/newscaster/directional/west, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "whitepurple" @@ -134271,7 +134289,7 @@ lDG lJN cEH cNh -hTF +hhl cBR cBR cBR @@ -134780,7 +134798,7 @@ qZN cSf cEH cEH -bER +cEH oLS cKZ cVf @@ -135034,9 +135052,9 @@ cpR mLY bGG qZN -cSH +rTH cXA -cEH +bER cZv nXu sqC diff --git a/_maps/map_files/stations/cerestation.dmm b/_maps/map_files/stations/cerestation.dmm index 832ed3d5479..95495c27d4d 100644 --- a/_maps/map_files/stations/cerestation.dmm +++ b/_maps/map_files/stations/cerestation.dmm @@ -2406,10 +2406,14 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/structure/chair/office{ - dir = 4 +/obj/structure/table, +/obj/item/clothing/gloves/color/latex, +/obj/item/reagent_containers/spray/cleaner{ + pixel_x = -3 + }, +/obj/item/reagent_containers/spray/cleaner{ + pixel_x = 5 }, -/obj/effect/landmark/start/scientist, /turf/simulated/floor/plasteel/white, /area/station/science/xenobiology) "arH" = ( @@ -12476,6 +12480,7 @@ /obj/structure/cable/orange{ icon_state = "1-8" }, +/obj/effect/landmark/start/xenobiologist, /turf/simulated/floor/plasteel/white, /area/station/science/xenobiology) "bRE" = ( @@ -15975,7 +15980,8 @@ dir = 10 }, /obj/structure/table/glass, -/obj/item/extinguisher, +/obj/item/storage/surgical_tray, +/obj/item/dissector, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "whitepurple" @@ -27180,8 +27186,9 @@ pixel_y = 28; name = "custom placement" }, -/obj/structure/table/glass, -/obj/item/stack/sheet/mineral/plasma, +/obj/machinery/computer/operating{ + name = "dissection computer" + }, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurple" @@ -28465,13 +28472,14 @@ /obj/machinery/door/airlock/maintenance{ name = "Aft Asteroid Maintenance" }, -/obj/effect/mapping_helpers/airlock/access/all/science/tox, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/effect/spawner/random/dirt/frequent, /obj/structure/cable/orange{ icon_state = "1-2" }, +/obj/effect/mapping_helpers/airlock/access/any/science/xenobio, +/obj/effect/mapping_helpers/airlock/access/any/science/tox, /turf/simulated/floor/plasteel/white, /area/station/science/test_chamber) "eGC" = ( @@ -31226,28 +31234,6 @@ icon_state = "neutralfull" }, /area/station/hallway/primary/fore/east) -"fmT" = ( -/obj/structure/cable/orange{ - icon_state = "4-8" - }, -/obj/structure/cable/orange{ - icon_state = "1-8" - }, -/obj/structure/cable/orange{ - icon_state = "2-8" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/structure/chair/office{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/obj/effect/landmark/start/scientist, -/turf/simulated/floor/plasteel/white, -/area/station/science/xenobiology) "fnr" = ( /obj/structure/sink/puddle, /turf/simulated/floor/plating/asteroid/ancient, @@ -46330,6 +46316,7 @@ "iKf" = ( /obj/structure/disposalpipe/segment, /obj/machinery/requests_console/directional/west, +/obj/machinery/smithing/scientific_assembler, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "whitepurple" @@ -47779,7 +47766,8 @@ /obj/machinery/door/airlock/research, /obj/effect/mapping_helpers/airlock/autoname, /obj/machinery/door/firedoor, -/obj/effect/mapping_helpers/airlock/access/any/science/research, +/obj/effect/mapping_helpers/airlock/access/any/science/xenobio, +/obj/effect/mapping_helpers/airlock/access/any/science/tox, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -49309,6 +49297,9 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, +/obj/machinery/computer/camera_advanced/xenobio{ + dir = 8 + }, /turf/simulated/floor/plasteel/white, /area/station/science/xenobiology) "jqB" = ( @@ -54055,6 +54046,18 @@ icon_state = "vault" }, /area/station/security/armory/secure) +"kpX" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/obj/structure/cable/orange{ + icon_state = "4-8" + }, +/turf/simulated/floor/plasteel/white, +/area/station/science/xenobiology) "kqa" = ( /turf/simulated/floor/wood/nitrogen, /area/station/maintenance/abandonedbar) @@ -54598,21 +54601,6 @@ }, /turf/simulated/floor/plasteel/white, /area/station/science/hallway) -"kvq" = ( -/obj/structure/chair/office{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/obj/structure/cable/orange{ - icon_state = "4-8" - }, -/turf/simulated/floor/plasteel/white, -/area/station/science/xenobiology) "kvs" = ( /obj/effect/turf_decal/stripes/line, /turf/simulated/floor/plasteel{ @@ -60033,7 +60021,12 @@ /turf/simulated/floor/plasteel, /area/station/engineering/atmos) "lCN" = ( -/obj/machinery/smithing/scientific_assembler, +/obj/structure/rack, +/obj/item/extinguisher{ + pixel_x = 4; + pixel_y = 4 + }, +/obj/item/extinguisher, /turf/simulated/floor/plasteel{ icon_state = "whitepurple" }, @@ -63902,6 +63895,7 @@ icon_state = "4-8" }, /obj/structure/disposalpipe/segment/corner, +/obj/item/stack/sheet/mineral/plasma, /turf/simulated/floor/plasteel/white, /area/station/science/xenobiology) "muW" = ( @@ -69274,6 +69268,9 @@ pixel_x = 4; pixel_y = 4 }, +/obj/item/reagent_containers/glass/bottle/reagent/morphine{ + pixel_y = -14 + }, /turf/simulated/floor/plasteel{ dir = 5; icon_state = "whitepurple" @@ -75585,6 +75582,22 @@ icon_state = "cafeteria" }, /area/station/medical/break_room) +"pfY" = ( +/obj/structure/cable/orange{ + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/effect/landmark/start/xenobiologist, +/obj/structure/chair/office{ + dir = 4 + }, +/turf/simulated/floor/plasteel/white, +/area/station/science/xenobiology) "pga" = ( /obj/structure/disposalpipe/segment, /obj/effect/spawner/random/dirt/often, @@ -81487,9 +81500,12 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, -/obj/machinery/computer/camera_advanced/xenobio{ - dir = 8 +/obj/structure/table, +/obj/item/clothing/glasses/science{ + pixel_x = 2; + pixel_y = 4 }, +/obj/item/clothing/glasses/science, /turf/simulated/floor/plasteel/white, /area/station/science/xenobiology) "qxe" = ( @@ -83971,6 +83987,13 @@ }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/structure/table/glass, +/obj/item/storage/belt/medical/surgery{ + pixel_y = 10 + }, +/obj/item/stack/sheet/mineral/plasma, +/obj/item/stack/sheet/mineral/plasma, +/obj/item/stack/sheet/mineral/plasma, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "whitepurplecorner" @@ -92368,7 +92391,6 @@ }, /obj/structure/disposalpipe/segment, /obj/structure/table/reinforced, -/obj/effect/mapping_helpers/airlock/windoor/access/any/science/research, /obj/machinery/door/window/classic/normal{ name = "R&D Desk" }, @@ -92387,6 +92409,7 @@ pixel_y = 7; anchored = 1 }, +/obj/effect/mapping_helpers/airlock/windoor/access/any/science/tox, /turf/simulated/floor/plasteel{ icon_state = "purplefull" }, @@ -93947,6 +93970,28 @@ icon_state = "neutralcorner" }, /area/station/hallway/primary/port/north) +"tiC" = ( +/obj/structure/cable/orange{ + icon_state = "4-8" + }, +/obj/structure/cable/orange{ + icon_state = "1-8" + }, +/obj/structure/cable/orange{ + icon_state = "2-8" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/obj/effect/landmark/start/xenobiologist, +/obj/structure/chair/office{ + dir = 8 + }, +/turf/simulated/floor/plasteel/white, +/area/station/science/xenobiology) "tiE" = ( /obj/structure/chair{ dir = 8 @@ -95087,16 +95132,11 @@ /turf/simulated/floor/wood, /area/station/maintenance/fore) "tvP" = ( -/obj/structure/table/glass, -/obj/item/clothing/glasses/science{ - pixel_x = 2; - pixel_y = 4 - }, -/obj/item/clothing/glasses/science, /obj/structure/cable/orange{ icon_state = "0-2" }, /obj/machinery/power/apc/directional/north, +/obj/machinery/optable, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurple" @@ -95168,8 +95208,9 @@ }, /area/station/hallway/primary/fore/east) "twG" = ( -/obj/structure/table/glass, +/obj/structure/shelf/science, /obj/item/book/manual/wiki/sop_science, +/obj/item/reagent_containers/glass/bottle/reagent/formaldehyde, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurple" @@ -100075,10 +100116,7 @@ /area/station/maintenance/fore) "uBC" = ( /obj/structure/disposalpipe/segment, -/obj/structure/extinguisher_cabinet{ - pixel_x = -24; - name = "custom placement" - }, +/obj/machinery/organ_analyzer, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "whitepurple" @@ -100603,7 +100641,8 @@ /obj/structure/cable/orange{ icon_state = "2-4" }, -/obj/effect/mapping_helpers/airlock/access/all/science/tox, +/obj/effect/mapping_helpers/airlock/access/any/science/tox, +/obj/effect/mapping_helpers/airlock/access/any/science/xenobio, /turf/simulated/floor/engine, /area/station/science/misc_lab) "uHD" = ( @@ -101796,7 +101835,8 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/effect/spawner/random/dirt/frequent, /obj/structure/disposalpipe/segment, -/obj/effect/mapping_helpers/airlock/access/any/science/research, +/obj/effect/mapping_helpers/airlock/access/any/science/tox, +/obj/effect/mapping_helpers/airlock/access/any/science/xenobio, /turf/simulated/floor/plasteel/white, /area/station/science/misc_lab) "uWB" = ( @@ -110335,6 +110375,10 @@ /obj/item/storage/box/beakers{ pixel_x = 10 }, +/obj/structure/extinguisher_cabinet{ + name = "north bump"; + pixel_y = 30 + }, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurple" @@ -138783,7 +138827,7 @@ cmZ wBr cGd erm -kvq +kpX wAG wAG meZ @@ -140329,7 +140373,7 @@ lSZ oDZ vMf wAG -jqw +pfY wAG rPK dko @@ -140843,7 +140887,7 @@ odF jtk btq wAG -arw +wdn wAG meZ jNL @@ -141357,7 +141401,7 @@ iZx oDZ vMf wAG -wdn +arw wAG rPK dko @@ -142128,7 +142172,7 @@ kII njH vld vld -fmT +tiC vld vld bYh diff --git a/_maps/map_files/stations/deltastation.dmm b/_maps/map_files/stations/deltastation.dmm index 5ff679a8877..41fbd316f0d 100644 --- a/_maps/map_files/stations/deltastation.dmm +++ b/_maps/map_files/stations/deltastation.dmm @@ -38864,6 +38864,8 @@ "cOO" = ( /obj/structure/table/reinforced, /obj/effect/turf_decal/delivery/hollow, +/obj/item/folder/white, +/obj/item/pen, /turf/simulated/floor/plasteel/white, /area/station/science/xenobiology) "cOP" = ( @@ -38891,12 +38893,15 @@ /turf/simulated/floor/plasteel/white, /area/station/science/xenobiology) "cOS" = ( -/obj/machinery/monkey_recycler, /obj/effect/turf_decal/delivery/hollow, /obj/structure/extinguisher_cabinet{ name = "north bump"; pixel_y = 30 }, +/obj/machinery/processor{ + desc = "A machine used to process slimes and retrieve their extract."; + name = "Slime Processor" + }, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "whitepurple" @@ -39578,7 +39583,6 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 6 }, -/obj/effect/landmark/start/scientist, /obj/structure/cable{ icon_state = "2-4" }, @@ -39588,6 +39592,7 @@ /obj/structure/cable{ icon_state = "4-8" }, +/obj/effect/landmark/start/xenobiologist, /turf/simulated/floor/plasteel/white, /area/station/science/xenobiology) "cRX" = ( @@ -39669,7 +39674,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 10 }, -/obj/effect/landmark/start/scientist, +/obj/effect/landmark/start/xenobiologist, /turf/simulated/floor/plasteel{ icon_state = "whitepurplefull" }, @@ -40054,7 +40059,6 @@ /turf/simulated/floor/plasteel/white, /area/station/science/xenobiology) "cTy" = ( -/obj/item/kirbyplants/large, /obj/machinery/light, /turf/simulated/floor/plasteel{ icon_state = "whitepurple" @@ -40134,11 +40138,8 @@ /turf/simulated/floor/plasteel/white, /area/station/science/xenobiology) "cTM" = ( -/obj/machinery/processor{ - desc = "A machine used to process slimes and retrieve their extract."; - name = "Slime Processor" - }, /obj/effect/turf_decal/delivery/hollow, +/obj/machinery/smithing/scientific_assembler, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "whitepurple" @@ -40531,7 +40532,7 @@ }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/effect/landmark/start/scientist, +/obj/effect/landmark/start/xenobiologist, /turf/simulated/floor/plasteel{ icon_state = "whitepurplefull" }, @@ -40574,12 +40575,12 @@ /obj/machinery/light{ dir = 4 }, -/obj/item/folder/white, -/obj/item/pen, /obj/structure/cable{ icon_state = "0-8" }, /obj/effect/turf_decal/stripes/line, +/obj/item/storage/surgical_tray, +/obj/item/dissector, /turf/simulated/floor/plasteel, /area/station/science/xenobiology) "cVs" = ( @@ -40893,13 +40894,14 @@ }, /area/station/science/rnd) "cWT" = ( -/obj/structure/table/reinforced, -/obj/item/paper_bin, -/obj/item/pen, -/obj/item/storage/bag/bio, -/obj/item/storage/bag/bio, /obj/machinery/firealarm/directional/west, /obj/effect/turf_decal/delivery/hollow, +/obj/structure/shelf/science, +/obj/item/storage/bag/bio, +/obj/item/storage/bag/bio, +/obj/item/slime_scanner, +/obj/item/slime_scanner, +/obj/item/clothing/gloves/color/latex, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "whitepurple" @@ -40912,31 +40914,34 @@ }, /area/station/science/xenobiology) "cWV" = ( -/obj/item/kirbyplants/large, /obj/effect/turf_decal/delivery/hollow, +/obj/machinery/computer/operating{ + dir = 1; + name = "dissection computer" + }, /turf/simulated/floor/plasteel{ icon_state = "whitepurple" }, /area/station/science/xenobiology) "cWW" = ( -/obj/structure/filingcabinet/chestdrawer, /obj/machinery/ai_status_display{ pixel_y = -32 }, /obj/effect/turf_decal/delivery/hollow, +/obj/machinery/optable, /turf/simulated/floor/plasteel{ icon_state = "whitepurple" }, /area/station/science/xenobiology) "cXa" = ( /obj/structure/table/reinforced, -/obj/item/clothing/gloves/color/latex, -/obj/item/slime_scanner, /obj/item/radio/intercom{ name = "south bump"; pixel_y = -28 }, /obj/effect/turf_decal/delivery, +/obj/item/storage/belt/medical/surgery, +/obj/item/reagent_containers/glass/bottle/reagent/morphine, /turf/simulated/floor/plasteel{ icon_state = "whitepurple" }, @@ -41410,18 +41415,6 @@ }, /turf/simulated/floor/plasteel/white, /area/station/science/rnd) -"cZC" = ( -/obj/structure/table/reinforced, -/obj/machinery/camera{ - c_tag = "Xenobiology East"; - dir = 8; - network = list("Research","SS13") - }, -/obj/machinery/reagentgrinder, -/obj/machinery/requests_console/directional/east, -/obj/effect/turf_decal/delivery, -/turf/simulated/floor/plasteel, -/area/station/science/xenobiology) "cZF" = ( /turf/simulated/floor/plasteel{ dir = 1; @@ -51019,6 +51012,21 @@ icon_state = "solarpanel" }, /area/station/engineering/solar/aft_port) +"dWe" = ( +/obj/structure/table/reinforced, +/obj/machinery/camera{ + c_tag = "Xenobio East"; + dir = 8; + network = list("Research","SS13") + }, +/obj/machinery/reagentgrinder, +/obj/machinery/requests_console/directional/east, +/obj/effect/turf_decal/delivery, +/obj/item/reagent_containers/glass/bottle/reagent/formaldehyde{ + pixel_x = -15 + }, +/turf/simulated/floor/plasteel, +/area/station/science/xenobiology) "dWf" = ( /obj/docking_port/stationary{ dheight = 9; @@ -52271,7 +52279,8 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/effect/mapping_helpers/airlock/access/all/science/tox, +/obj/effect/mapping_helpers/airlock/access/any/science/xenobio, +/obj/effect/mapping_helpers/airlock/access/any/science/tox, /turf/simulated/floor/plasteel/white, /area/station/science/misc_lab) "eky" = ( @@ -72721,7 +72730,6 @@ pixel_x = -6; pixel_y = 3 }, -/obj/effect/mapping_helpers/airlock/windoor/access/any/science/research, /obj/effect/mapping_helpers/airlock/polarized{ id = "rnd" }, @@ -72733,6 +72741,7 @@ id_tag = "researchdesk1"; name = "Research and Development Front Desk Shutters" }, +/obj/effect/mapping_helpers/airlock/windoor/access/any/science/tox, /turf/simulated/floor/plasteel/white, /area/station/science/rnd) "nyr" = ( @@ -75499,6 +75508,13 @@ icon_state = "neutralcorner" }, /area/station/hallway/primary/starboard/east) +"oIS" = ( +/obj/structure/filingcabinet/chestdrawer, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "whitepurple" + }, +/area/station/science/xenobiology) "oIT" = ( /obj/machinery/door/firedoor, /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -86235,7 +86251,7 @@ /turf/simulated/floor/plating/airless, /area/station/engineering/control) "tgQ" = ( -/obj/machinery/smithing/scientific_assembler, +/obj/machinery/organ_analyzer, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurple" @@ -91520,6 +91536,11 @@ /obj/effect/spawner/random/maintenance, /turf/simulated/floor/plating, /area/station/maintenance/apmaint) +"vpx" = ( +/obj/effect/turf_decal/delivery/hollow, +/obj/machinery/monkey_recycler, +/turf/simulated/floor/plasteel/white, +/area/station/science/xenobiology) "vpS" = ( /obj/structure/bookcase/random, /obj/effect/decal/cleanable/cobweb2, @@ -126823,7 +126844,7 @@ dWm cWA cTu cWA -dWm +vpx dWv dmq dhG @@ -131445,7 +131466,7 @@ eGq rkt oRX cIL -cQP +oIS cPe cTO cSg @@ -131705,7 +131726,7 @@ cIL cPb cQJ cSh -cZC +dWe cSh cVo cXa diff --git a/_maps/map_files/stations/emeraldstation.dmm b/_maps/map_files/stations/emeraldstation.dmm index 5087277d38d..04c46c4a59f 100644 --- a/_maps/map_files/stations/emeraldstation.dmm +++ b/_maps/map_files/stations/emeraldstation.dmm @@ -3169,8 +3169,7 @@ /obj/machinery/light{ dir = 8 }, -/obj/structure/table, -/obj/item/clothing/gloves/color/latex, +/obj/machinery/smithing/scientific_assembler, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurplecorner" @@ -17941,13 +17940,8 @@ }, /area/station/public/shops) "dDu" = ( -/obj/structure/reagent_dispensers/watertank, -/obj/machinery/camera{ - c_tag = "Xenobiology Control"; - dir = 9; - network = list("Research","SS13") - }, /obj/machinery/newscaster/directional/east, +/obj/machinery/organ_analyzer, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "whitepurplecorner" @@ -21968,6 +21962,11 @@ /obj/structure/cable{ icon_state = "4-8" }, +/obj/item/extinguisher, +/obj/item/extinguisher{ + pixel_x = 4; + pixel_y = 4 + }, /turf/simulated/floor/plasteel{ icon_state = "whitepurplefull" }, @@ -23088,7 +23087,17 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/machinery/smithing/scientific_assembler, +/obj/machinery/camera{ + c_tag = "Xenobiology Control"; + dir = 1; + network = list("Research","SS13") + }, +/obj/structure/shelf/science, +/obj/item/reagent_containers/spray/cleaner, +/obj/item/reagent_containers/spray/cleaner, +/obj/item/reagent_containers/glass/bottle/reagent/formaldehyde, +/obj/item/clothing/glasses/science, +/obj/item/clothing/glasses/science, /turf/simulated/floor/plasteel{ icon_state = "whitepurplecorner" }, @@ -30838,6 +30847,7 @@ /turf/simulated/floor/wood, /area/station/service/library) "gfL" = ( +/obj/effect/landmark/start/xenobiologist, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurplecorner" @@ -33833,13 +33843,7 @@ /turf/simulated/floor/plasteel, /area/station/maintenance/apmaint2) "gLd" = ( -/obj/item/radio/intercom{ - name = "west bump"; - pixel_x = -28 - }, -/obj/machinery/processor{ - name = "Slime Processor" - }, +/obj/structure/sink/directional/west, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurplecorner" @@ -35512,11 +35516,8 @@ /turf/simulated/floor/plating, /area/station/maintenance/starboard) "hdD" = ( -/obj/structure/rack, -/obj/item/storage/box/syringes, -/obj/item/storage/box/beakers{ - pixel_x = 2; - pixel_y = 2 +/obj/machinery/computer/operating{ + name = "dissection computer" }, /turf/simulated/floor/plasteel{ dir = 1; @@ -37064,10 +37065,10 @@ /obj/structure/chair{ dir = 4 }, -/obj/effect/landmark/start/scientist, /obj/structure/cable{ icon_state = "1-2" }, +/obj/effect/landmark/start/xenobiologist, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "whitepurple" @@ -38253,12 +38254,6 @@ /turf/simulated/floor/plating, /area/station/maintenance/security/aft_port) "hCL" = ( -/obj/structure/table/glass, -/obj/item/extinguisher{ - pixel_x = 4; - pixel_y = 4 - }, -/obj/item/extinguisher, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -38268,6 +38263,7 @@ /obj/structure/cable{ icon_state = "4-8" }, +/obj/machinery/monkey_recycler, /turf/simulated/floor/plasteel{ icon_state = "whitepurplefull" }, @@ -48655,20 +48651,19 @@ /turf/simulated/floor/plasteel, /area/station/hallway/secondary/exit) "jHf" = ( -/obj/item/stack/sheet/mineral/plasma{ - pixel_x = -2; - pixel_y = -2 - }, -/obj/item/stack/sheet/mineral/plasma, -/obj/item/stack/sheet/mineral/plasma{ - pixel_x = 2; - pixel_y = 2 - }, /obj/structure/table, /obj/machinery/power/apc/directional/east, /obj/structure/cable{ icon_state = "0-8" }, +/obj/item/storage/box/beakers{ + pixel_x = 2; + pixel_y = 2 + }, +/obj/item/storage/box/syringes, +/obj/item/stack/sheet/mineral/plasma, +/obj/item/stack/sheet/mineral/plasma, +/obj/item/stack/sheet/mineral/plasma, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "whitepurplecorner" @@ -60623,7 +60618,9 @@ /turf/simulated/floor/plasteel, /area/station/hallway/primary/central/sw) "mhu" = ( -/obj/machinery/monkey_recycler, +/obj/machinery/processor{ + name = "Slime Processor" + }, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurplecorner" @@ -85567,6 +85564,7 @@ /obj/structure/cable{ icon_state = "1-2" }, +/obj/structure/reagent_dispensers/watertank, /turf/simulated/floor/plasteel{ icon_state = "whitepurple" }, @@ -91974,9 +91972,10 @@ }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/effect/mapping_helpers/airlock/access/all/science/tox, /obj/structure/disposalpipe/segment, /obj/machinery/door/firedoor, +/obj/effect/mapping_helpers/airlock/access/any/science/xenobio, +/obj/effect/mapping_helpers/airlock/access/any/science/tox, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -98094,8 +98093,7 @@ /turf/simulated/floor/plasteel, /area/station/public/locker) "tzh" = ( -/obj/structure/sink/directional/north, -/obj/machinery/firealarm/directional/west, +/obj/machinery/optable, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurplecorner" @@ -106023,6 +106021,12 @@ /obj/machinery/disposal/delivery_chute{ dir = 4 }, +/obj/effect/mapping_helpers/airlock/windoor/access/any/science/tox{ + dir = 4 + }, +/obj/effect/mapping_helpers/airlock/windoor/access/any/science/xenobio{ + dir = 4 + }, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "whitepurple" @@ -108546,19 +108550,15 @@ /turf/simulated/floor/grass, /area/station/public/park) "vFk" = ( -/obj/structure/table, -/obj/item/reagent_containers/spray/cleaner{ - pixel_x = -3 - }, -/obj/item/reagent_containers/spray/cleaner{ - pixel_x = 5 - }, /obj/machinery/requests_console/directional/east, /obj/machinery/light_switch{ name = "north bump"; pixel_y = 24; pixel_x = 10 }, +/obj/structure/table, +/obj/item/storage/belt/medical/surgery, +/obj/item/reagent_containers/glass/bottle/reagent/morphine, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "whitepurplecorner" @@ -111453,7 +111453,6 @@ pixel_y = 8; anchored = 1 }, -/obj/effect/mapping_helpers/airlock/windoor/access/all/science/research, /obj/machinery/door/firedoor, /obj/effect/mapping_helpers/airlock/windoor/autoname/desk, /obj/machinery/door/poddoor/preopen{ @@ -111465,6 +111464,7 @@ name = "Research and Development Lab Shutters"; id_tag = "rndlab2" }, +/obj/effect/mapping_helpers/airlock/windoor/access/any/science/tox, /turf/simulated/floor/plasteel, /area/station/science/rnd) "wih" = ( @@ -112063,7 +112063,7 @@ /obj/structure/chair{ dir = 8 }, -/obj/effect/landmark/start/scientist, +/obj/effect/landmark/start/xenobiologist, /turf/simulated/floor/plasteel{ dir = 6; icon_state = "whitepurple" @@ -112703,8 +112703,9 @@ icon_state = "1-2" }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/effect/mapping_helpers/airlock/access/all/science/tox, /obj/machinery/door/firedoor, +/obj/effect/mapping_helpers/airlock/access/any/science/tox, +/obj/effect/mapping_helpers/airlock/access/any/science/xenobio, /turf/simulated/floor/engine, /area/station/science/misc_lab) "wvT" = ( @@ -115664,9 +115665,6 @@ /obj/machinery/computer/camera_advanced/xenobio{ dir = 8 }, -/obj/machinery/ai_status_display{ - pixel_y = -32 - }, /turf/simulated/floor/plasteel{ dir = 6; icon_state = "whitepurple" @@ -117142,12 +117140,19 @@ }, /area/station/security/prison/cell_block) "xrN" = ( -/obj/structure/table, -/obj/item/clothing/glasses/science{ - pixel_y = 4 - }, -/obj/item/clothing/glasses/science, /obj/machinery/alarm/directional/west, +/obj/structure/table/glass, +/obj/item/storage/surgical_tray, +/obj/item/dissector, +/obj/machinery/camera{ + c_tag = "Xenobiology Control"; + dir = 6; + network = list("Research","SS13") + }, +/obj/item/radio/intercom{ + name = "north bump"; + pixel_y = 28 + }, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurplecorner" @@ -120854,6 +120859,7 @@ /area/station/service/kitchen) "yfA" = ( /obj/structure/table/glass, +/obj/item/clothing/gloves/color/latex, /obj/item/slime_scanner, /obj/item/slime_scanner{ pixel_y = 4; diff --git a/_maps/map_files/stations/metastation.dmm b/_maps/map_files/stations/metastation.dmm index 0293118d0ad..c7095bf5eab 100644 --- a/_maps/map_files/stations/metastation.dmm +++ b/_maps/map_files/stations/metastation.dmm @@ -13263,6 +13263,7 @@ /obj/structure/chair/comfy/black{ dir = 4 }, +/obj/effect/landmark/start/xenobiologist, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "purple" @@ -30966,7 +30967,8 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/effect/mapping_helpers/airlock/access/all/science/research, +/obj/effect/mapping_helpers/airlock/access/any/science/xenobio, +/obj/effect/mapping_helpers/airlock/access/any/science/tox, /turf/simulated/floor/plasteel{ icon_state = "whitepurplefull" }, @@ -32016,25 +32018,13 @@ /area/station/maintenance/asmaint) "ddz" = ( /obj/machinery/alarm/directional/east, -/obj/structure/rack, -/obj/item/storage/box/beakers{ - pixel_x = 6; - pixel_y = 10 - }, -/obj/item/storage/box/syringes{ - pixel_x = -4; - pixel_y = 5 - }, -/obj/item/scalpel{ - pixel_y = -2 - }, -/obj/item/hemostat{ - pixel_x = 10 - }, /obj/effect/turf_decal/stripes/line{ dir = 5 }, /obj/machinery/requests_console/directional/north, +/obj/machinery/computer/operating{ + name = "dissection computer" + }, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -34816,6 +34806,15 @@ icon_state = "red" }, /area/station/security/warden) +"dZQ" = ( +/obj/effect/landmark/start/xenobiologist, +/obj/structure/chair/office{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "white" + }, +/area/station/science/xenobiology) "dZZ" = ( /obj/structure/closet/emcloset, /turf/simulated/floor/plasteel, @@ -36981,10 +36980,10 @@ /turf/simulated/floor/plating, /area/station/maintenance/fsmaint) "eMG" = ( -/obj/structure/closet/l3closet/scientist, /obj/structure/cable{ icon_state = "1-2" }, +/obj/machinery/organ_analyzer, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "purple" @@ -37249,6 +37248,7 @@ /obj/structure/chair/comfy/black{ dir = 4 }, +/obj/effect/landmark/start/xenobiologist, /turf/simulated/floor/plasteel{ icon_state = "purple" }, @@ -40600,7 +40600,6 @@ /turf/simulated/floor/wood, /area/station/public/mrchangs) "gbz" = ( -/obj/item/kirbyplants/large, /obj/machinery/light_switch{ dir = 4; name = "custom placement"; @@ -41512,6 +41511,9 @@ /obj/machinery/disposal/delivery_chute{ dir = 1 }, +/obj/effect/mapping_helpers/airlock/windoor/access/any/science/xenobio{ + dir = 1 + }, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "purple" @@ -42608,8 +42610,6 @@ /area/station/maintenance/asmaint) "gMT" = ( /obj/structure/table/glass, -/obj/item/slime_scanner, -/obj/item/slime_scanner, /obj/item/radio/intercom{ name = "west bump"; pixel_x = -28 @@ -42617,6 +42617,10 @@ /obj/machinery/light{ dir = 8 }, +/obj/item/storage/belt/medical/surgery, +/obj/item/reagent_containers/glass/bottle/reagent/morphine{ + pixel_y = 16 + }, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "whitepurple" @@ -46573,10 +46577,6 @@ icon_state = "whitegreen" }, /area/station/medical/virology) -"inr" = ( -/obj/structure/chair/office, -/turf/simulated/floor/wood, -/area/station/public/vacant_office) "inv" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -48656,6 +48656,19 @@ /obj/machinery/atmospherics/pipe/simple/heat_exchanging, /turf/simulated/floor/bluegrid/telecomms, /area/station/science/xenobiology) +"jba" = ( +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ + dir = 4 + }, +/obj/structure/chair/office{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/station/public/vacant_office) "jbe" = ( /obj/effect/spawner/window/reinforced, /turf/simulated/floor/plating, @@ -51960,43 +51973,6 @@ icon_state = "redfull" }, /area/station/security/permabrig) -"kls" = ( -/obj/structure/table/glass, -/obj/item/stack/sheet/mineral/plasma{ - pixel_x = -2; - pixel_y = -2 - }, -/obj/item/stack/sheet/mineral/plasma, -/obj/item/stack/sheet/mineral/plasma{ - pixel_x = 2; - pixel_y = 2 - }, -/obj/item/reagent_containers/dropper, -/obj/item/reagent_containers/glass/beaker/large{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/reagent_containers/glass/beaker{ - pixel_x = 8; - pixel_y = 2 - }, -/obj/machinery/light_switch{ - dir = 4; - name = "custom placement"; - pixel_x = -24; - pixel_y = -6 - }, -/obj/machinery/camera{ - c_tag = "Xenobiology - Fore"; - dir = 4; - network = list("Research","SS13") - }, -/obj/machinery/firealarm/directional/west, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "whitepurple" - }, -/area/station/science/xenobiology) "klS" = ( /obj/structure/table, /obj/machinery/recharger{ @@ -52410,7 +52386,6 @@ /turf/simulated/floor/plasteel, /area/station/hallway/primary/central/north) "kvh" = ( -/obj/structure/closet/bombcloset, /obj/structure/extinguisher_cabinet{ name = "west bump"; pixel_x = -27 @@ -54284,12 +54259,29 @@ pixel_y = -24; req_access = list(55) }, -/obj/item/book/manual/wiki/sop_science{ - pixel_y = 4 - }, /obj/effect/turf_decal/stripes/line{ dir = 6 }, +/obj/item/reagent_containers/glass/beaker/large{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/reagent_containers/glass/beaker{ + pixel_x = 8; + pixel_y = 2 + }, +/obj/item/stack/sheet/mineral/plasma{ + pixel_x = 2; + pixel_y = 2 + }, +/obj/item/stack/sheet/mineral/plasma{ + pixel_x = 2; + pixel_y = 2 + }, +/obj/item/stack/sheet/mineral/plasma{ + pixel_x = 2; + pixel_y = 2 + }, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -56278,6 +56270,31 @@ /obj/effect/spawner/random/dirt/maybe, /turf/simulated/floor/wood, /area/station/hallway/primary/central/south) +"lOM" = ( +/obj/machinery/light_switch{ + dir = 4; + name = "custom placement"; + pixel_x = -24; + pixel_y = -6 + }, +/obj/machinery/camera{ + c_tag = "Xenobiology Lab - Fore"; + dir = 4; + network = list("Research","SS13") + }, +/obj/machinery/firealarm/directional/west, +/obj/structure/shelf/science, +/obj/item/slime_scanner, +/obj/item/slime_scanner, +/obj/item/book/manual/wiki/sop_science{ + pixel_y = 4 + }, +/obj/item/clothing/gloves/color/latex, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "whitepurple" + }, +/area/station/science/xenobiology) "lOU" = ( /obj/machinery/atmospherics/pipe/simple/visible/cyan, /obj/effect/turf_decal/stripes/line{ @@ -58072,6 +58089,22 @@ /obj/structure/flora/ausbushes/fullgrass, /turf/simulated/floor/grass/no_creep, /area/station/science/research) +"myY" = ( +/obj/effect/turf_decal/stripes/line, +/obj/structure/table/glass, +/obj/item/storage/box/beakers{ + pixel_x = 6; + pixel_y = 10 + }, +/obj/item/storage/box/syringes{ + pixel_x = -4; + pixel_y = 5 + }, +/obj/item/reagent_containers/dropper, +/turf/simulated/floor/plasteel{ + icon_state = "white" + }, +/area/station/science/xenobiology) "mzf" = ( /obj/structure/window/plasmareinforced{ dir = 1 @@ -58824,19 +58857,6 @@ /obj/machinery/atmospherics/meter, /turf/simulated/floor/plating, /area/station/maintenance/asmaint) -"mOa" = ( -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/chair/office{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 4 - }, -/turf/simulated/floor/wood, -/area/station/public/vacant_office) "mOc" = ( /obj/structure/cable{ icon_state = "1-2" @@ -60059,10 +60079,14 @@ /area/station/hallway/secondary/entry/west) "nfR" = ( /obj/structure/table/glass, -/obj/item/clothing/gloves/color/latex, /obj/effect/turf_decal/stripes/line{ dir = 1 }, +/obj/item/storage/surgical_tray, +/obj/item/dissector, +/obj/item/reagent_containers/glass/bottle/reagent/formaldehyde{ + pixel_x = -15 + }, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -61306,6 +61330,12 @@ }, /turf/simulated/floor/plasteel, /area/station/public/dorms) +"nEy" = ( +/obj/structure/chair/office{ + dir = 8 + }, +/turf/simulated/floor/wood, +/area/station/public/vacant_office) "nEz" = ( /obj/effect/spawner/airlock/s_to_n, /obj/effect/spawner/window/reinforced/grilled, @@ -70273,6 +70303,15 @@ icon_state = "dark" }, /area/station/security/armory/secure) +"qWf" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/optable, +/turf/simulated/floor/plasteel{ + icon_state = "white" + }, +/area/station/science/xenobiology) "qWm" = ( /obj/structure/cable{ icon_state = "0-4" @@ -70698,14 +70737,6 @@ /obj/structure/reagent_dispensers/fueltank, /turf/simulated/floor/plating, /area/station/maintenance/starboard2) -"rdg" = ( -/obj/structure/chair/office{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "white" - }, -/area/station/science/xenobiology) "rdi" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/chair/wheelchair, @@ -76550,12 +76581,6 @@ /obj/structure/lattice/catwalk, /turf/space, /area/station/engineering/solar/fore_port) -"tpn" = ( -/obj/structure/chair/office{ - dir = 8 - }, -/turf/simulated/floor/wood, -/area/station/public/vacant_office) "tpq" = ( /obj/structure/lattice, /obj/structure/sign/electricshock{ @@ -80195,7 +80220,10 @@ /obj/structure/cable{ icon_state = "1-2" }, -/obj/effect/mapping_helpers/airlock/windoor/access/all/science/tox{ +/obj/effect/mapping_helpers/airlock/windoor/access/any/science/xenobio{ + dir = 1 + }, +/obj/effect/mapping_helpers/airlock/windoor/access/any/science/tox{ dir = 1 }, /turf/simulated/floor/plasteel, @@ -81955,6 +81983,10 @@ /obj/machinery/atmospherics/pipe/simple/hidden/cyan, /turf/simulated/floor/plating, /area/station/maintenance/fore) +"vvH" = ( +/obj/structure/chair/office, +/turf/simulated/floor/wood, +/area/station/public/vacant_office) "vvU" = ( /obj/machinery/keycard_auth{ pixel_y = -4; @@ -82294,12 +82326,13 @@ /obj/machinery/door/airlock/research/glass{ name = "Test Chamber" }, -/obj/effect/mapping_helpers/airlock/access/all/science/research, /obj/structure/disposalpipe/segment, /obj/machinery/door/firedoor, /obj/structure/cable{ icon_state = "1-2" }, +/obj/effect/mapping_helpers/airlock/access/any/science/xenobio, +/obj/effect/mapping_helpers/airlock/access/any/science/tox_storage, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -84041,10 +84074,10 @@ }, /area/station/hallway/primary/fore/west) "wsf" = ( -/obj/item/kirbyplants/large/plant12, /obj/structure/window/reinforced{ dir = 8 }, +/obj/structure/closet/bombcloset, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "purple" @@ -85264,7 +85297,10 @@ name = "Science Chemistry"; dir = 1 }, -/obj/effect/mapping_helpers/airlock/windoor/access/all/science/tox{ +/obj/effect/mapping_helpers/airlock/windoor/access/any/science/xenobio{ + dir = 1 + }, +/obj/effect/mapping_helpers/airlock/windoor/access/any/science/tox{ dir = 1 }, /turf/simulated/floor/plasteel, @@ -86724,10 +86760,10 @@ pixel_y = 8; anchored = 1 }, -/obj/effect/mapping_helpers/airlock/windoor/access/any/science/research{ +/obj/structure/table/reinforced, +/obj/effect/mapping_helpers/airlock/windoor/access/any/science/tox{ dir = 4 }, -/obj/structure/table/reinforced, /turf/simulated/floor/plasteel{ icon_state = "whitepurplefull" }, @@ -100577,7 +100613,7 @@ oJl eoq bHf bHf -tpn +nEy eoq bHf bFp @@ -100836,7 +100872,7 @@ vsf dNc vsf xyi -inr +vvH eyr bFp mbZ @@ -101088,7 +101124,7 @@ kOX yem rRg bHg -mOa +jba niq pCP mpH @@ -131229,7 +131265,7 @@ aaa uQm fcb gMT -kls +lOM xPh gbz kvh @@ -131485,12 +131521,12 @@ aaa aaa fvN nfR -rdg +dZQ cNz sFS cNz cNz -kjo +fbq fvN aaa aaa @@ -131741,13 +131777,13 @@ abf aaa aaa fvN -iUe +qWf cEA gZe deu sFX pNz -fbq +myY fvN aaa aaa diff --git a/code/__DEFINES/job_defines.dm b/code/__DEFINES/job_defines.dm index b7920fe728f..bec4d54b956 100644 --- a/code/__DEFINES/job_defines.dm +++ b/code/__DEFINES/job_defines.dm @@ -37,7 +37,7 @@ #define JOB_ROBOTICIST (1<<8) #define JOB_PARAMEDIC (1<<9) #define JOB_CORONER (1<<10) - +#define JOB_XENOBIOLOGIST (1<<15) #define JOBCAT_SUPPORT (1<<2) diff --git a/code/__DEFINES/misc_defines.dm b/code/__DEFINES/misc_defines.dm index 9b96625c553..b7ce7d5ef64 100644 --- a/code/__DEFINES/misc_defines.dm +++ b/code/__DEFINES/misc_defines.dm @@ -744,6 +744,20 @@ do { \ #define BANK_PIN_MIN 10000 #define BANK_PIN_MAX 99999 +/// Defines for hidden organ techs +#define TECH_MATERIAL "material" +#define TECH_ENGINEERING "engineering" +#define TECH_PLASMA "plasma" +#define TECH_POWER "power" +#define TECH_BLUESPACE "bluespace" +#define TECH_BIO "biotech" +#define TECH_COMBAT "combat" +#define TECH_MAGNETS "magnets" +#define TECH_PROGRAM "programming" +#define TECH_TOXINS "toxins" +#define TECH_SYNDICATE "syndicate" +#define TECH_ABDUCTOR "abductor" + //! The number of seconds between the start of the UNIX and BYOND epochs. #define BYOND_EPOCH_UNIX 946702800 diff --git a/code/__DEFINES/mob_defines.dm b/code/__DEFINES/mob_defines.dm index af2c8f4e733..598e60fbc09 100644 --- a/code/__DEFINES/mob_defines.dm +++ b/code/__DEFINES/mob_defines.dm @@ -16,6 +16,11 @@ #define ORGAN_DATUM_LUNGS "lungs" #define ORGAN_DATUM_BATTERY "battery" +// Organ quality datums. +#define ORGAN_DAMAGED 1 +#define ORGAN_NORMAL 2 +#define ORGAN_PRISTINE 3 + // For limb resistance flags #define CANNOT_BREAK (1 << 0) #define CANNOT_DISMEMBER (1 << 1) diff --git a/code/__DEFINES/tools_defines.dm b/code/__DEFINES/tools_defines.dm index 429135c408b..09e776c95f2 100644 --- a/code/__DEFINES/tools_defines.dm +++ b/code/__DEFINES/tools_defines.dm @@ -26,6 +26,7 @@ GLOBAL_LIST_INIT(construction_tool_behaviors, list( #define TOOL_BONESET "bone setter" #define TOOL_BONEGEL "bone gel" #define TOOL_FIXOVEIN "fix-o-vein" +#define TOOL_DISSECTOR "dissector" GLOBAL_LIST_INIT(surgery_tool_behaviors, list( TOOL_RETRACTOR, @@ -36,7 +37,8 @@ GLOBAL_LIST_INIT(surgery_tool_behaviors, list( TOOL_SAW, TOOL_BONESET, TOOL_BONEGEL, - TOOL_FIXOVEIN + TOOL_FIXOVEIN, + TOOL_DISSECTOR, )) #define MIN_TOOL_SOUND_DELAY 20 diff --git a/code/__HELPERS/name_helpers.dm b/code/__HELPERS/name_helpers.dm index 792e306fb55..0cd998d93fa 100644 --- a/code/__HELPERS/name_helpers.dm +++ b/code/__HELPERS/name_helpers.dm @@ -176,3 +176,18 @@ GLOBAL_DATUM(syndicate_code_response_regex, /regex) newKey += pick("diamond", "beer", "mushroom", "assistant", "clown", "captain", "twinkie", "security", "nuke", "small", "big", "escape", "yellow", "gloves", "monkey", "engine", "nuclear", "ai") newKey += pick("1", "2", "3", "4", "5", "6", "7", "8", "9", "0") return newKey + +/proc/quality_to_string(quality, use_normal = FALSE) + if(!quality) + return + switch(quality) + if(ORGAN_DAMAGED) + return "Damaged" + if(ORGAN_NORMAL) + if(use_normal) + return "Normal" + else + return "" + if(ORGAN_PRISTINE) + return "Pristine" + diff --git a/code/__HELPERS/tool_helpers.dm b/code/__HELPERS/tool_helpers.dm index b4e3ca5164a..2cad64cd18d 100644 --- a/code/__HELPERS/tool_helpers.dm +++ b/code/__HELPERS/tool_helpers.dm @@ -52,5 +52,6 @@ TOOL_RETRACTOR, TOOL_SAW, TOOL_SCALPEL, - TOOL_SCREWDRIVER + TOOL_SCREWDRIVER, + TOOL_DISSECTOR, ) diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm index ea1a597f444..0f2fd485598 100644 --- a/code/__HELPERS/trait_helpers.dm +++ b/code/__HELPERS/trait_helpers.dm @@ -340,6 +340,11 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Trait applied to a mob when it gets a required "operational datum" (components/elements). Sends out the source as the type of the element. #define TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM "element-required" +//****** ORGAN TRAITS *****// + +/// Does this person produce loud runechat +#define TRAIT_LOUD "loud" + //****** OBJ TRAITS *****// ///An /obj that should not increase the "depth" of the search for adjacency, @@ -378,6 +383,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define EYES_OF_GOD "eyes_of_god" #define GHOSTED "isghost" #define GHOST_ROLE "ghost_role" +#define ORGAN_TRAIT "organ" // unique trait sources #define STATUE_MUTE "statue" diff --git a/code/_globalvars/lists/maint_loot_tables.dm b/code/_globalvars/lists/maint_loot_tables.dm index 3f288e0525c..f1e3f6781e9 100644 --- a/code/_globalvars/lists/maint_loot_tables.dm +++ b/code/_globalvars/lists/maint_loot_tables.dm @@ -130,6 +130,7 @@ GLOBAL_LIST_INIT(maintenance_loot_tier_2, list( /obj/item/clothing/head/welding, /obj/item/crowbar/red, /obj/item/storage/belt/utility, + /obj/item/dissector, /obj/effect/spawner/random/smithed_item/any, ) = 45, diff --git a/code/_globalvars/lists/mob_lists.dm b/code/_globalvars/lists/mob_lists.dm index ec8e6fa4f59..ac599f8d6b5 100644 --- a/code/_globalvars/lists/mob_lists.dm +++ b/code/_globalvars/lists/mob_lists.dm @@ -35,3 +35,33 @@ GLOBAL_LIST_EMPTY(surgeries_list) GLOBAL_LIST_EMPTY(hear_radio_list) //Mobs that hear the radio even if there's no client GLOBAL_LIST_EMPTY(emote_list) + +// Dear God +GLOBAL_LIST_INIT(migo_sounds, list( + 'sound/items/bubblewrap.ogg', 'sound/items/change_jaws.ogg', 'sound/items/crowbar.ogg', 'sound/items/drink.ogg', + 'sound/items/deconstruct.ogg', 'sound/items/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/screwdriver.ogg', + 'sound/items/weeoo1.ogg', 'sound/items/wirecutter.ogg', 'sound/items/welder.ogg', 'sound/items/zip.ogg', 'sound/items/rped.ogg', 'sound/items/ratchet.ogg', + 'sound/items/polaroid1.ogg', 'sound/items/pshoom.ogg', 'sound/items/airhorn.ogg', 'sound/voice/bcreep.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/ed209_20sec.ogg', + 'sound/voice/hiss3.ogg', 'sound/voice/hiss6.ogg', 'sound/voice/mpatchedup.ogg', 'sound/voice/mfeelbetter.ogg', 'sound/weapons/sear.ogg', 'sound/ambience/antag/tatoralert.ogg', + 'sound/mecha/nominal.ogg', 'sound/mecha/weapdestr.ogg', 'sound/mecha/critdestr.ogg', 'sound/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', + 'sound/effects/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles.ogg', + 'sound/effects/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clownstep2.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', + 'sound/effects/empulse.ogg', 'sound/effects/explosionfar.ogg', 'sound/effects/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', + 'sound/effects/heartbeat.ogg', 'sound/effects/hyperspace_begin.ogg', 'sound/effects/hyperspace_end.ogg', 'sound/goonstation/effects/screech.ogg', 'sound/effects/phasein.ogg', + 'sound/effects/picaxe1.ogg', 'sound/effects/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', + 'sound/effects/supermatter.ogg', 'sound/misc/desceration-01.ogg', 'sound/misc/desceration-02.ogg', 'sound/misc/desceration-03.ogg', 'sound/misc/bloblarm.ogg', + 'sound/goonstation/misc/airraid_loop.ogg', 'sound/misc/interference.ogg', 'sound/misc/notice1.ogg', 'sound/misc/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', + 'sound/weapons/armbomb.ogg', 'sound/weapons/chainsaw.ogg', 'sound/weapons/emitter.ogg', 'sound/weapons/emitter2.ogg', 'sound/weapons/blade1.ogg', 'sound/weapons/bladeslice.ogg', + 'sound/weapons/blastcannon.ogg', 'sound/weapons/blaster.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/weapons/circsawhit.ogg', 'sound/weapons/cqchit2.ogg', 'sound/weapons/drill.ogg', + 'sound/weapons/genhit1.ogg', 'sound/weapons/gunshots/gunshot_silenced.ogg', 'sound/weapons/gunshots/gunshot.ogg', 'sound/weapons/handcuffs.ogg', 'sound/weapons/homerun.ogg', + 'sound/weapons/kenetic_accel.ogg','sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock_alien_prying.ogg', 'sound/machines/airlock_close.ogg', + 'sound/machines/airlockforced.ogg', 'sound/machines/airlock_open.ogg', 'sound/machines/alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/boltsdown.ogg', + 'sound/machines/boltsup.ogg', 'sound/machines/buzz-sigh.ogg', 'sound/machines/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/defib_charge.ogg', + 'sound/machines/defib_failed.ogg', 'sound/machines/defib_ready.ogg', 'sound/machines/defib_zap.ogg', 'sound/machines/deniedbeep.ogg', 'sound/machines/ding.ogg', + 'sound/machines/disposalflush.ogg', 'sound/machines/door_close.ogg', 'sound/machines/door_open.ogg', 'sound/machines/engine_alert1.ogg', 'sound/machines/engine_alert2.ogg', + 'sound/machines/hiss.ogg', 'sound/machines/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/ambience/signal.ogg', 'sound/machines/synth_no.ogg', + 'sound/machines/synth_yes.ogg', 'sound/machines/terminal_alert.ogg', 'sound/machines/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', + 'sound/ai/outbreak5.ogg', 'sound/ai/outbreak7.ogg', 'sound/ai/alert.ogg', 'sound/ai/radiation.ogg', 'sound/ai/eshuttle_call.ogg', 'sound/ai/eshuttle_dock.ogg', + 'sound/ai/eshuttle_recall.ogg', 'sound/ai/aimalf.ogg', 'sound/ambience/ambigen1.ogg', 'sound/ambience/ambigen3.ogg', 'sound/ambience/ambigen4.ogg', 'sound/ambience/ambigen5.ogg', + 'sound/ambience/ambigen6.ogg', 'sound/ambience/ambigen10.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg', + )) diff --git a/code/_globalvars/misc_globals.dm b/code/_globalvars/misc_globals.dm index 93a7cc22753..43c20f59d4c 100644 --- a/code/_globalvars/misc_globals.dm +++ b/code/_globalvars/misc_globals.dm @@ -72,6 +72,9 @@ GLOBAL_VAR_INIT(sparks_active, 0) ///Counter for the current amount of smoke GLOBAL_VAR_INIT(smokes_active, 0) +/// what xeno organs have been scanned today? +GLOBAL_LIST_EMPTY(scanned_organs) + /// A list of types of objects we want to record in admin logs when /// a player starts pulling them. GLOBAL_LIST_INIT(log_pulltypes, list( diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index 9f41c3c43c3..18ac10d17cc 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -116,6 +116,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_EXAMINE_HALLUCINATING" = TRAIT_EXAMINE_HALLUCINATING, "TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM" = TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM, "TRAIT_RELAYING_ATTACKER" = TRAIT_RELAYING_ATTACKER, + "TRAIT_LOUD" = TRAIT_LOUD, ), /datum/mind = list( diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm index 5a40bab9138..cbb4da95f5a 100644 --- a/code/datums/chatmessage.dm +++ b/code/datums/chatmessage.dm @@ -141,8 +141,11 @@ symbol = null output_color = "#b826b3" else + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(HAS_TRAIT(H, TRAIT_LOUD)) + size = "big" symbol = null - // Approximate text height var/static/regex/html_metachars = new(@"&[A-Za-z]{1,7};", "g") var/complete_text = "[symbol][text]" diff --git a/code/datums/components/special_tastes.dm b/code/datums/components/special_tastes.dm new file mode 100644 index 00000000000..5b5a9165dea --- /dev/null +++ b/code/datums/components/special_tastes.dm @@ -0,0 +1,76 @@ +/// Allows humanoids to eat arbitrary items +/datum/component/special_tastes + /// associated list of item to reagents and tastes + /// Typepath = (list(reagents = list(reagents), tastes = list(tastes)); Ex: list(/obj/item/stack/sheet/mineral/silver = list("reagents" = list("nutriment" = 5, "vitamin" = 1), "tastes" = list("metal and blood" = 1))) + var/list/obj/item/edible_items = list() + +/datum/component/special_tastes/Initialize(list/edible_items) + if(!length(edible_items)) + return COMPONENT_INCOMPATIBLE + if(!ishuman(parent)) + return COMPONENT_INCOMPATIBLE + for(var/obj/item/current_item as anything in edible_items) + if(!islist(edible_items[current_item])) // if not a list of reagents + return COMPONENT_INCOMPATIBLE + src.edible_items = edible_items + RegisterSignal(parent, COMSIG_ATTACK_BY, PROC_REF(attempt_ingest)) + +/// check if item is in the list of potential food +/datum/component/special_tastes/proc/attempt_ingest(datum/source, obj/item/attacking_item, mob/living/carbon/human/attacker) + SIGNAL_HANDLER // COMSIG_ATTACK_BY + if(parent != attacker) // only if they attack themselves with it + return + var/is_edible = FALSE + var/obj/item/similar_to + for(var/potential_type in edible_items) + if(istype(attacking_item, potential_type)) + is_edible = TRUE + similar_to = potential_type + break + if(!is_edible) + return + attempt_to_eat(attacker, attacking_item, similar_to) + return COMPONENT_SKIP_AFTERATTACK + +/// Handle eating and nutrition gain from eating minerals +/datum/component/special_tastes/proc/attempt_to_eat(mob/living/carbon/human/user, obj/item/attacking_item, obj/item/similar_to) + if(istype(attacking_item, /obj/item/stack)) + var/obj/item/stack/stacked_item = attacking_item + if(stacked_item.amount > 1) + to_chat(user, "You can only eat one of [stacked_item] at a time.") + return + var/obj/item/food/food = turn_into_food(attacking_item, similar_to) + user.drop_item_to_ground(attacking_item, TRUE, TRUE) + user.put_in_active_hand(food) + var/fullness = user.nutrition + 10 + for(var/datum/reagent/consumable/chem in food.reagents.reagent_list) + fullness += chem.nutriment_factor * chem.volume / (chem.metabolization_rate * user.metabolism_efficiency) + if(!(fullness > (600 * (1 + user.overeatduration / 2000)))) // need to work around the sleep()'s in eat() + user.consume(food) + food.On_Consume(user, user) + qdel(attacking_item) + return // couldnt eat it, undo + to_chat(user, "You are too full to eat [attacking_item].") + user.drop_item_to_ground(food, TRUE, TRUE) + user.put_in_active_hand(attacking_item) + qdel(food) + +/// Turn the item into food, can only be eaten in 1 bite to prevent creating a half-eaten item that those without the trait can also eat. +/datum/component/special_tastes/proc/turn_into_food(obj/item/future_food, obj/item/similar_to) + var/obj/item/food/food = new + food.name = future_food.name + food.desc = future_food.desc + food.icon = future_food.icon + food.icon_state = future_food.icon_state + var/list/food_info = edible_items[similar_to] + var/list/chem_info = food_info["reagents"] + var/list/taste_info = food_info["tastes"] + for(var/reagent_id in chem_info) + var/reagent_amount = chem_info[reagent_id] + food.reagents.add_reagent(reagent_id, reagent_amount, taste_info.Copy()) + food.bitesize = food.reagents.total_volume + return food + +/datum/component/special_tastes/Destroy(force, silent) + UnregisterSignal(parent, COMSIG_ATTACK_BY) + return ..() diff --git a/code/datums/components/surgery_initiator.dm b/code/datums/components/surgery_initiator.dm index 7a7f5966c89..51f0ad1a9e1 100644 --- a/code/datums/components/surgery_initiator.dm +++ b/code/datums/components/surgery_initiator.dm @@ -82,7 +82,7 @@ return if(!IS_HORIZONTAL(L) && !can_start_on_stander) return - if(IS_HORIZONTAL(L) && !on_operable_surface(L)) + if(IS_HORIZONTAL(L) && !on_operable_surface(L) && !isanimal(L)) return if(iscarbon(target)) var/mob/living/carbon/C = target @@ -128,7 +128,7 @@ var/datum/surgery/procedure if(!length(available_surgeries)) - if(IS_HORIZONTAL(target)) + if(IS_HORIZONTAL(target) || isanimal(target)) to_chat(user, "There aren't any surgeries you can perform there right now.") else to_chat(user, "You can't perform any surgeries there while [target] is standing.") @@ -209,8 +209,7 @@ if(!affected) skip_surgery = TRUE - else - // uh there's no reason this should be hit but let's be safe LOL + else if(!isanimal(patient)) // surgery is base to living mobs now but we should still probably restrict this skip_surgery = TRUE if(!skip_surgery) @@ -288,9 +287,15 @@ return - if(!isnull(affecting_limb) && (surgery.requires_organic_bodypart && affecting_limb.is_robotic()) || (!surgery.requires_organic_bodypart && !affecting_limb.is_robotic())) - to_chat(user, "That's not the right type of limb for this operation!") - return + if(iscarbon(target)) + if(!isnull(affecting_limb)) + if((surgery.requires_organic_bodypart && affecting_limb.is_robotic()) || (!surgery.requires_organic_bodypart && !affecting_limb.is_robotic())) + to_chat(user, "That's not the right type of limb for this operation!") + return + + if(surgery_needs_exposure(surgery, target)) + to_chat(user, "You have to expose [target.p_their()] [parse_zone(selected_zone)] first!") + return if(surgery.lying_required && !on_operable_surface(target)) to_chat(user, "Patient must be lying down for this operation.") @@ -304,12 +309,16 @@ to_chat(user, "Can't start the surgery!") return - if(surgery_needs_exposure(surgery, target)) - to_chat(user, "You have to expose [target.p_their()] [parse_zone(selected_zone)] first!") - return - var/datum/surgery/procedure = new surgery.type(target, selected_zone, affecting_limb) + // Need to pass dissection its steps because it changes alot depending on the creature + if(istype(procedure, /datum/surgery/dissect)) + if(isnull(target.surgery_container)) + log_debug("A dissection was started on [target] with contains_xeno_organ as TRUE, however its surgery_container was null!") + CRASH("[target] does not have a dissection surgery information set to it. Please inform an admin or developer.") + if(ispath(target.surgery_container, /datum/xenobiology_surgery_container)) + target.surgery_container = new target.surgery_container + procedure.steps = target.surgery_container.dissection_tool_step RegisterSignal(procedure, COMSIG_SURGERY_BLOOD_SPLASH, PROC_REF(on_blood_splash)) @@ -382,7 +391,6 @@ ) return - if(!isnull(surgery_start_sound)) playsound(src, surgery_start_sound, 50, TRUE) diff --git a/code/datums/station_traits/positive_traits.dm b/code/datums/station_traits/positive_traits.dm index 9b3279d2c33..0ed44d43124 100644 --- a/code/datums/station_traits/positive_traits.dm +++ b/code/datums/station_traits/positive_traits.dm @@ -193,6 +193,7 @@ /datum/job/rd = /obj/item/organ/internal/cyberimp/eyes/hud/diagnostic, /datum/job/roboticist = /obj/item/organ/internal/cyberimp/eyes/hud/diagnostic, /datum/job/scientist = /obj/item/organ/internal/ears/cybernetic, + /datum/job/xenobiologist = /obj/item/organ/internal/cyberimp/arm/surgery, /datum/job/officer = /obj/item/organ/internal/cyberimp/eyes/hud/security, /datum/job/mining = /obj/item/organ/internal/eyes/cybernetic/meson, /datum/job/engineer = /obj/item/organ/internal/eyes/cybernetic/shield, diff --git a/code/game/dna/mutations/mutation_powers.dm b/code/game/dna/mutations/mutation_powers.dm index 9d069526cff..a79158b7003 100644 --- a/code/game/dna/mutations/mutation_powers.dm +++ b/code/game/dna/mutations/mutation_powers.dm @@ -1,3 +1,4 @@ + /////////////////////////////////// // POWERS /////////////////////////////////// @@ -364,6 +365,24 @@ ..() block = GLOB.eatblock +// checks with those with the hungry organ from adding/removing matter eater +/datum/mutation/grant_spell/mattereater/activate(mob/living/M) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/internal/liver/xenobiology/hungry/O = H.get_int_organ(/obj/item/organ/internal/liver/xenobiology/hungry) + if(O) + return + return ..() + +/datum/mutation/grant_spell/mattereater/deactivate(mob/living/M) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/internal/liver/xenobiology/hungry/O = H.get_int_organ(/obj/item/organ/internal/liver/xenobiology/hungry) + if(O) + return + return ..() + + /datum/spell/eat name = "Eat" desc = "Eat just about anything!" diff --git a/code/game/gamemodes/miniantags/abduction/gland.dm b/code/game/gamemodes/miniantags/abduction/gland.dm index 83406308b33..10477b2f1e3 100644 --- a/code/game/gamemodes/miniantags/abduction/gland.dm +++ b/code/game/gamemodes/miniantags/abduction/gland.dm @@ -9,12 +9,18 @@ organ_datums = list(/datum/organ/heart/always_beating, /datum/organ/battery) // alien glands are immune to stopping, and provide power to IPCs tough = TRUE //not easily broken by combat damage - var/cooldown_low = 300 - var/cooldown_high = 300 - var/next_activation = 0 - var/uses // -1 For inifinite - var/human_only = FALSE + /// Do these organs have an repeatable ongoing effects? + var/has_ongoing_effect = TRUE + /// Is this organ's effect currently active var/active = FALSE + /// the low-end of random cooldown times between activations + var/cooldown_low = 300 + /// the high-end of random cooldown times between activations + var/cooldown_high = 300 + /// what time will the organ activate next + var/next_activation = 0 + /// How many uses of this organ remain before it goes dormant; -1 for infinite uses + var/uses var/mind_control_uses = 1 var/mind_control_duration = 1800 @@ -23,13 +29,6 @@ /obj/item/organ/internal/heart/gland/update_icon_state() return -/obj/item/organ/internal/heart/gland/proc/ownerCheck() - if(ishuman(owner)) - return TRUE - if(!human_only && iscarbon(owner)) - return TRUE - return FALSE - /obj/item/organ/internal/heart/gland/proc/Start() active = TRUE next_activation = world.time + rand(cooldown_low,cooldown_high) @@ -46,7 +45,7 @@ holder.icon_state = "hudgland_spent" /obj/item/organ/internal/heart/gland/proc/mind_control(command, mob/living/user) - if(!ownerCheck() || !mind_control_uses || active_mind_control) + if(!owner_check() || !mind_control_uses || active_mind_control) return mind_control_uses-- to_chat(owner, "You suddenly feel an irresistible compulsion to follow an order...") @@ -61,14 +60,13 @@ addtimer(CALLBACK(src, PROC_REF(clear_mind_control)), mind_control_duration) /obj/item/organ/internal/heart/gland/proc/clear_mind_control() - if(!ownerCheck() || !active_mind_control) + if(!owner_check() || !active_mind_control) return to_chat(owner, "You feel the compulsion fade, and you completely forget about your previous orders.") active_mind_control = FALSE update_gland_hud() /obj/item/organ/internal/heart/gland/remove(mob/living/carbon/M, special = 0) - active = FALSE if(initial(uses) == 1) uses = initial(uses) var/datum/atom_hud/abductor/hud = GLOB.huds[DATA_HUD_ABDUCTOR] @@ -85,19 +83,18 @@ update_gland_hud() /obj/item/organ/internal/heart/gland/on_life() - if(!active) - return - if(!ownerCheck()) - active = FALSE - return - if(next_activation <= world.time) - activate() - uses-- - next_activation = world.time + rand(cooldown_low,cooldown_high) - if(!uses) - active = FALSE + if(has_ongoing_effect && active) + if(!owner_check()) + active = FALSE + return + if(next_activation <= world.time) + trigger() + uses-- + next_activation = world.time + rand(cooldown_low, cooldown_high) + if(!uses) + active = FALSE -/obj/item/organ/internal/heart/gland/proc/activate() +/obj/item/organ/internal/heart/gland/proc/trigger() return /obj/item/organ/internal/heart/gland/heals @@ -108,7 +105,7 @@ mind_control_uses = 3 mind_control_duration = 3000 -/obj/item/organ/internal/heart/gland/heals/activate() +/obj/item/organ/internal/heart/gland/heals/trigger() if(!(owner.mob_biotypes & MOB_ORGANIC)) return to_chat(owner, "You feel curiously revitalized.") @@ -130,7 +127,7 @@ owner.faction |= "slime" owner.add_language("Bubblish") -/obj/item/organ/internal/heart/gland/slime/activate() +/obj/item/organ/internal/heart/gland/slime/trigger() to_chat(owner, "You feel nauseous!") owner.vomit(20) @@ -145,7 +142,7 @@ mind_control_uses = 1 mind_control_duration = 6000 -/obj/item/organ/internal/heart/gland/mindshock/activate() +/obj/item/organ/internal/heart/gland/mindshock/trigger() to_chat(owner, "You get a headache.") var/turf/T = get_turf(owner) @@ -167,12 +164,11 @@ cooldown_low = 900 cooldown_high = 1800 uses = -1 - human_only = TRUE icon_state = "species" mind_control_uses = 5 mind_control_duration = 3000 -/obj/item/organ/internal/heart/gland/pop/activate() +/obj/item/organ/internal/heart/gland/pop/trigger() to_chat(owner, "You feel unlike yourself.") var/species = pick(/datum/species/unathi, /datum/species/skrell, /datum/species/diona, /datum/species/tajaran, /datum/species/vulpkanin, /datum/species/kidan, /datum/species/grey) owner.set_species(species, keep_missing_bodyparts = TRUE) @@ -186,7 +182,7 @@ mind_control_uses = 4 mind_control_duration = 1800 -/obj/item/organ/internal/heart/gland/ventcrawling/activate() +/obj/item/organ/internal/heart/gland/ventcrawling/trigger() to_chat(owner, "You feel very stretchy.") owner.ventcrawler = VENTCRAWLER_ALWAYS @@ -199,7 +195,7 @@ mind_control_uses = 1 mind_control_duration = 1800 -/obj/item/organ/internal/heart/gland/viral/activate() +/obj/item/organ/internal/heart/gland/viral/trigger() to_chat(owner, "You feel sick.") var/datum/disease/advance/A = random_virus(pick(2, 6), 6) A.carrier = TRUE @@ -236,7 +232,7 @@ mind_control_uses = 3 mind_control_duration = 1800 -/obj/item/organ/internal/heart/gland/emp/activate() +/obj/item/organ/internal/heart/gland/emp/trigger() to_chat(owner, "You feel a spike of pain in your head.") empulse(get_turf(owner), 2, 5, 1) @@ -248,7 +244,7 @@ mind_control_uses = 2 mind_control_duration = 2400 -/obj/item/organ/internal/heart/gland/spiderman/activate() +/obj/item/organ/internal/heart/gland/spiderman/trigger() to_chat(owner, "You feel something crawling in your skin.") owner.faction |= "spiders" var/obj/structure/spider/spiderling/S = new(owner.loc) @@ -262,7 +258,7 @@ mind_control_uses = 2 mind_control_duration = 1800 -/obj/item/organ/internal/heart/gland/egg/activate() +/obj/item/organ/internal/heart/gland/egg/trigger() owner.visible_message("[owner] [pick(EGG_LAYING_MESSAGES)]") new /obj/item/food/egg/gland(get_turf(owner)) @@ -287,7 +283,7 @@ singlemutcheck(owner, GLOB.shockimmunityblock, MUTCHK_FORCED) return ..() -/obj/item/organ/internal/heart/gland/electric/activate() +/obj/item/organ/internal/heart/gland/electric/trigger() owner.visible_message("[owner]'s skin starts emitting electric arcs!",\ "You feel electric energy building up inside you!") playsound(get_turf(owner), "sparks", 100, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) @@ -304,7 +300,7 @@ mind_control_uses = 3 mind_control_duration = 1200 -/obj/item/organ/internal/heart/gland/chem/activate() +/obj/item/organ/internal/heart/gland/chem/trigger() owner.reagents.add_reagent(get_random_reagent_id(), 2) owner.adjustToxLoss(-2) ..() @@ -314,7 +310,7 @@ cooldown_high = 400 uses = -1 -/obj/item/organ/internal/heart/gland/bloody/activate() +/obj/item/organ/internal/heart/gland/bloody/trigger() owner.blood_volume = max(owner.blood_volume - 20, 0) owner.visible_message("[owner]'s skin erupts with blood!",\ "Blood pours from your skin!") @@ -333,7 +329,7 @@ mind_control_uses = 1 mind_control_duration = 800 -/obj/item/organ/internal/heart/gland/plasma/activate() +/obj/item/organ/internal/heart/gland/plasma/trigger() spawn(0) to_chat(owner, "You feel bloated.") sleep(150) diff --git a/code/game/gamemodes/miniantags/morph/morph.dm b/code/game/gamemodes/miniantags/morph/morph.dm index c53a1fa1d9a..346f7af7023 100644 --- a/code/game/gamemodes/miniantags/morph/morph.dm +++ b/code/game/gamemodes/miniantags/morph/morph.dm @@ -35,6 +35,8 @@ attacktext = "glomps" attack_sound = 'sound/effects/blobattack.ogg' butcher_results = list(/obj/item/food/meat/slab = 2) + contains_xeno_organ = TRUE + surgery_container = /datum/xenobiology_surgery_container/morph /// If the morph is disguised or not var/morphed = FALSE @@ -207,14 +209,36 @@ . = ..() add_to_all_human_data_huds() if(stat == DEAD && gibbed) - for(var/atom/movable/AM in src) - AM.forceMove(loc) - if(prob(90)) - step(AM, pick(GLOB.alldirs)) + eject_contents() // Only execute the below if we successfully died if(!.) return FALSE +/mob/living/simple_animal/hostile/morph/proc/eject_contents() + for(var/atom/movable/AM in src) + AM.forceMove(loc) + if(prob(90)) + step(AM, pick(GLOB.alldirs)) + +/mob/living/simple_animal/hostile/morph/crowbar_act(mob/living/user, obj/item/I) + if(stat != DEAD) + return CONTINUE_ATTACK + if(user.a_intent != INTENT_HELP) + return CONTINUE_ATTACK + if(!contents.len) + to_chat(user, "[src] doesnt have anything left inside it!") + return ITEM_INTERACT_COMPLETE + playsound(loc, 'sound/weapons/slice.ogg', 50, TRUE, -1) + visible_message( + "[src] begins to pry open the morph's massive jaws!", + "You begin to pry open the morph's massive jaws!", + "You hear wet, meaty tearing nearby!" + ) + if(do_after_once(user, 8 SECONDS, target = src)) + eject_contents() + playsound(loc, 'sound/effects/splat.ogg', 50, TRUE, -1) + return ITEM_INTERACT_COMPLETE + /mob/living/simple_animal/hostile/morph/attack_hand(mob/living/carbon/human/M) if(ambush_prepared) to_chat(M, "[src] feels a bit different from normal... it feels more.. SLIMEY?!") @@ -222,6 +246,13 @@ else return ..() +/mob/living/simple_animal/hostile/morph/examine(mob/user) + . = ..() + if(stat == DEAD) + if(length(contents)) + . += "You can use ALT + Click to eject anything the morph may have eaten." + else if(in_range(user, src)) + . += "\The [src] seems to have nothing left inside of it!" #define MORPH_ATTACKED if((. = ..()) && morphed) mimic_spell.restore_form(src) /mob/living/simple_animal/hostile/morph/attack_by(obj/item/O, mob/living/user, params) diff --git a/code/game/gamemodes/miniantags/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm index 4f58c51b9f0..4d457535414 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant.dm @@ -12,8 +12,8 @@ desc = "A malevolent spirit." icon = 'icons/mob/mob.dmi' icon_state = "revenant_idle" - mob_biotypes = MOB_SPIRIT + sentience_type = SENTIENCE_BOSS // no reviving funny ghost incorporeal_move = INCORPOREAL_MOVE_HOLY_BLOCK see_invisible = INVISIBILITY_REVENANT invisibility = INVISIBILITY_REVENANT @@ -37,6 +37,9 @@ pass_flags = PASSTABLE | PASSGRILLE | PASSMOB atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) initial_traits = list(TRAIT_FLYING) + contains_xeno_organ = TRUE + ignore_generic_organs = TRUE + surgery_container = /datum/xenobiology_surgery_container/revenant /// The revenant's idle icon var/icon_idle = "revenant_idle" @@ -73,20 +76,23 @@ var/list/drained_mobs = list() ///How many perfect, regen-cap increasing souls the revenant has. var/perfectsouls = 0 + /// Are we currently dying? extra check against becomming incorporeal + var/dying = FALSE /mob/living/simple_animal/revenant/Life(seconds, times_fired) ..() if(revealed && essence <= 0) + dying = TRUE death() if(essence_regenerating && !inhibited && essence < essence_regen_cap) //While inhibited, essence will not regenerate essence = min(essence_regen_cap, essence+essence_regen_amount) - if(unreveal_time && world.time >= unreveal_time) + if(unreveal_time && world.time >= unreveal_time && !dying) unreveal_time = 0 revealed = FALSE incorporeal_move = INCORPOREAL_MOVE_HOLY_BLOCK invisibility = INVISIBILITY_REVENANT to_chat(src, "You are once more concealed.") - if(unstun_time && world.time >= unstun_time) + if(unstun_time && world.time >= unstun_time && !dying) unstun_time = 0 notransform = FALSE to_chat(src, "You can move again!") @@ -206,9 +212,13 @@ icon_state = "revenant_draining" animate(src, alpha = 0, time = 3 SECONDS) visible_message("[src]'s body breaks apart into a fine pile of blue dust.") - new /obj/item/ectoplasm(get_turf(src)) - ghostize() - qdel(src) + ghostize(FALSE) + name = "ectoplasm" + desc = "A pile of clumpy dust from a restless spirit" + alpha = 255 + icon_state = "revenant_ectoplasm" + move_resist = null + return ..() /mob/living/simple_animal/revenant/attack_by(obj/item/W, mob/living/user, params) if(..()) @@ -279,6 +289,9 @@ update_spooky_icon() /mob/living/simple_animal/revenant/proc/update_spooky_icon() + if(dying) + return + if(!revealed) icon_state = icon_idle return @@ -290,6 +303,7 @@ if(draining) icon_state = icon_drain return + // No other state is happening, therefore we are stunned icon_state = icon_stun @@ -338,6 +352,7 @@ /datum/objective/revenant_fluff/check_completion() return TRUE +//no longer used /obj/item/ectoplasm name = "glimmering residue" desc = "A pile of fine blue dust. Small tendrils of violet mist swirl around it." diff --git a/code/game/jobs/job/science_jobs.dm b/code/game/jobs/job/science_jobs.dm index 192ba5186bd..87e76f75072 100644 --- a/code/game/jobs/job/science_jobs.dm +++ b/code/game/jobs/job/science_jobs.dm @@ -81,9 +81,8 @@ ACCESS_RESEARCH, ACCESS_TOX_STORAGE, ACCESS_TOX, - ACCESS_XENOBIOLOGY ) - alt_titles = list("Xenoarcheologist", "Anomalist", "Plasma Researcher", "Xenobiologist", "Chemical Researcher") + alt_titles = list("Anomalist", "Plasma Researcher", "Chemical Researcher") minimal_player_age = 3 exp_map = list(EXP_TYPE_CREW = 300) // All science-y guys get bonuses for maxing out their tech. @@ -108,6 +107,60 @@ satchel = /obj/item/storage/backpack/satchel_tox dufflebag = /obj/item/storage/backpack/duffel/science +/datum/job/xenobiologist + title = "Xenobiologist" + flag = JOB_XENOBIOLOGIST + department_flag = JOBCAT_MEDSCI + total_positions = 2 + spawn_positions = 2 + job_department_flags = DEP_FLAG_SCIENCE + supervisors = "the research director" + department_head = list("Research Director") + selection_color = "#ffeeff" + access = list( + ACCESS_MAINT_TUNNELS, + ACCESS_MINERAL_STOREROOM, + ACCESS_RESEARCH, + ACCESS_XENOBIOLOGY, + ACCESS_EVA, + ACCESS_MINING, + ACCESS_MINING_STATION, + ACCESS_EXTERNAL_AIRLOCKS, + ACCESS_TELEPORTER, + ) + alt_titles = list("Xenoarcheologist", "Slime Cultivator", "Slime Rancher") + minimal_player_age = 3 + exp_map = list(EXP_TYPE_CREW = 300) + // All science-y guys get bonuses for maxing out their tech. + required_objectives = list( + /datum/job_objective/scan_organs, + ) + + outfit = /datum/outfit/job/xenobiologist + +/datum/outfit/job/xenobiologist + name = "Xenobiologist" + jobtype = /datum/job/xenobiologist + + uniform = /obj/item/clothing/under/rank/rnd/scientist + suit = /obj/item/clothing/suit/storage/labcoat/science + shoes = /obj/item/clothing/shoes/white + gloves = /obj/item/clothing/gloves/color/latex/nitrile + glasses = /obj/item/clothing/glasses/science + l_ear = /obj/item/radio/headset/headset_sci + id = /obj/item/card/id/xenobiology + pda = /obj/item/pda/toxins + + backpack = /obj/item/storage/backpack/science + satchel = /obj/item/storage/backpack/satchel_tox + dufflebag = /obj/item/storage/backpack/duffel/science + + backpack_contents = list( + /obj/item/healthanalyzer = 1, + /obj/item/storage/box/bodybags = 1, + /obj/item/clipboard = 1, + ) + /datum/job/roboticist title = "Roboticist" flag = JOB_ROBOTICIST diff --git a/code/game/jobs/job_globals.dm b/code/game/jobs/job_globals.dm index b61c81ce712..95577ed3512 100644 --- a/code/game/jobs/job_globals.dm +++ b/code/game/jobs/job_globals.dm @@ -53,6 +53,7 @@ GLOBAL_LIST_INIT(science_positions, list( "Research Director", "Scientist", "Geneticist", //Part of both medical and science + "Xenobiologist", "Roboticist", )) diff --git a/code/game/jobs/job_objectives/science.dm b/code/game/jobs/job_objectives/science.dm index d07a3e96e3a..5d76431395f 100644 --- a/code/game/jobs/job_objectives/science.dm +++ b/code/game/jobs/job_objectives/science.dm @@ -41,3 +41,19 @@ /datum/job_objective/make_ripley/check_for_completion() return completed + +/datum/job_objective/scan_organs + objective_name = "Scan Extraterrestrial Organs" + description = "Scan alien organs via the organ analyzer to learn more about their local ecology." + gives_payout = TRUE + completion_payment = 200 + /// How many organs should be scanned to complete this goal + var/amount_to_scan = 10 + +/datum/job_objective/scan_organs/check_for_completion() + var/total_organs + for(var/item in GLOB.scanned_organs) + total_organs += GLOB.scanned_organs[item] + if(total_organs >= amount_to_scan) + return TRUE + return FALSE diff --git a/code/game/machinery/tcomms/nttc.dm b/code/game/machinery/tcomms/nttc.dm index 75cd125eac6..88bffe38194 100644 --- a/code/game/machinery/tcomms/nttc.dm +++ b/code/game/machinery/tcomms/nttc.dm @@ -96,6 +96,8 @@ "Scientist" = "sciradio", "Xenoarcheologist" = "sciradio", "Xenobiologist" = "sciradio", + "Slime Cultivator" = "sciradio", + "Slime Rancher" = "sciradio", // Security "Detective" = "secradio", "Forensic Technician" = "secradio", diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index 8fd9210a8c1..a3deace4bdf 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -422,6 +422,10 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/newplayer_start) //Without this you sp name = "Scientist" icon_state = "Sci" +/obj/effect/landmark/start/xenobiologist + name = "Xenobiologist" + icon_state = "Sci" + /obj/effect/landmark/start/security_officer name = "Security Officer" icon_state = "Sec" diff --git a/code/game/objects/effects/spawners/random/traders/trader_department_spawners.dm b/code/game/objects/effects/spawners/random/traders/trader_department_spawners.dm index e8e0f4f5df6..56f9b8f54ce 100644 --- a/code/game/objects/effects/spawners/random/traders/trader_department_spawners.dm +++ b/code/game/objects/effects/spawners/random/traders/trader_department_spawners.dm @@ -94,6 +94,8 @@ // Xenobio /obj/item/slimepotion/sentience = 50, // Low-value, but we want to encourage getting more players back in the round. /obj/item/slimepotion/transference = 50, + /obj/item/dissector/alien = 50, + /obj/item/regen_mesh = 50, // Might as well let AI be interested /obj/item/ai_upgrade/surveillance_upgrade = 25, diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm index eb93b769d59..e3b10e069e6 100644 --- a/code/game/objects/items/weapons/cards_ids.dm +++ b/code/game/objects/items/weapons/cards_ids.dm @@ -546,6 +546,12 @@ icon_state = "research" access = list(ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_MINERAL_STOREROOM) +/obj/item/card/id/xenobiology + name = "Xenobiology ID" + registered_name = "Xenobiologist" + icon_state = "xenobiologist" + access = list(ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_EVA, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_TELEPORTER) + /obj/item/card/id/roboticist name = "Roboticist ID" registered_name = "Roboticist" diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index 5ea254d4256..6098f358144 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -238,6 +238,7 @@ /obj/item/fix_o_vein, /obj/item/surgicaldrill, /obj/item/cautery, + /obj/item/dissector ) /obj/item/storage/belt/medical/surgery/loaded/populate_contents() diff --git a/code/game/objects/items/weapons/storage/surgical_tray.dm b/code/game/objects/items/weapons/storage/surgical_tray.dm index 3ca3190c944..f51ed89127a 100644 --- a/code/game/objects/items/weapons/storage/surgical_tray.dm +++ b/code/game/objects/items/weapons/storage/surgical_tray.dm @@ -19,7 +19,8 @@ /obj/item/bonesetter, /obj/item/stack/medical/bruise_pack, /obj/item/stack/medical/ointment, - /obj/item/surgical_drapes + /obj/item/surgical_drapes, + /obj/item/dissector, ) /obj/item/storage/surgical_tray/Initialize(mapload) diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index a628356606b..6f11c416704 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -181,6 +181,8 @@ for(var/obj/structure/closet/closet in get_turf(src)) if(closet != src && closet.anchored != 1) return FALSE + for(var/mob/living/simple_animal/hostile/megafauna/M in get_turf(src)) + return FALSE return TRUE /obj/structure/closet/proc/dump_contents() diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index 92526130f41..c3628f130d6 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -11,6 +11,8 @@ var/list/ui_users = list() var/broken_icon_state = "mirror_broke" +/obj/structure/mirror/organ + /obj/structure/mirror/Initialize(mapload, newdir = SOUTH, building = FALSE) . = ..() if(building) @@ -53,6 +55,10 @@ broken = TRUE GLOB.mirrors -= src +/obj/structure/mirror/organ/obj_break(damage_flag, mapload) + playsound(src, "shatter", 70, TRUE) + qdel(src) + /obj/structure/mirror/screwdriver_act(mob/user, obj/item/I) . = TRUE if(!I.tool_use_check(user, 0)) diff --git a/code/modules/awaymissions/mission_code/ruins/deepstorage.dm b/code/modules/awaymissions/mission_code/ruins/deepstorage.dm index b9f8ee35ad6..0587c247428 100644 --- a/code/modules/awaymissions/mission_code/ruins/deepstorage.dm +++ b/code/modules/awaymissions/mission_code/ruins/deepstorage.dm @@ -9,7 +9,7 @@ icon_living = "fleshling" attack_sound = 'sound/misc/demon_attack1.ogg' death_sound = 'sound/misc/demon_dies.ogg' - icon_dead = "" + icon_dead = "fleshling_dead" speed = 5 move_to_delay = 4 ranged = TRUE @@ -23,8 +23,7 @@ sentience_type = SENTIENCE_BOSS a_intent = INTENT_HARM deathmessage = "collapses into a pile of gibs. From the looks of it this is the deadest it can get... " - del_on_death = TRUE - + butcher_results = list(/obj/item/regen_mesh) /// Is the boss charging right now? var/charging = FALSE /// Did our boss die? diff --git a/code/modules/client/preference/character.dm b/code/modules/client/preference/character.dm index 1554e6f3a2c..09a828c724f 100644 --- a/code/modules/client/preference/character.dm +++ b/code/modules/client/preference/character.dm @@ -1259,6 +1259,22 @@ clothes_s.Blend(new /icon('icons/mob/clothing/back.dmi', "satchel-tox"), ICON_OVERLAY) if(4) clothes_s.Blend(new /icon('icons/mob/clothing/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_XENOBIOLOGIST) + clothes_s = new /icon('icons/mob/clothing/under/rnd.dmi', "science_s") + clothes_s.Blend(new /icon('icons/mob/clothing/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/clothing/suit.dmi', "labcoat_tox_open"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/clothing/hands.dmi', "nitrilegloves"), ICON_OVERLAY) + if(prob(50)) + clothes_s.Blend(new /icon('icons/mob/clothing/mask.dmi', "sterile"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/clothing/head.dmi', "metroid"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/clothing/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/clothing/back.dmi', "satchel-tox"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/clothing/back.dmi', "satchel"), ICON_OVERLAY) if(JOB_CHEMIST) clothes_s = new /icon('icons/mob/clothing/under/medical.dmi', "chemistry_s") clothes_s.Blend(new /icon('icons/mob/clothing/feet.dmi', "white"), ICON_UNDERLAY) diff --git a/code/modules/events/blob/blob_mobs.dm b/code/modules/events/blob/blob_mobs.dm index dac32472ac7..1f60e974f0c 100644 --- a/code/modules/events/blob/blob_mobs.dm +++ b/code/modules/events/blob/blob_mobs.dm @@ -199,6 +199,8 @@ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE move_resist = MOVE_FORCE_OVERPOWERING a_intent = INTENT_HARM + contains_xeno_organ = TRUE + surgery_container = /datum/xenobiology_surgery_container/blobbernaut /mob/living/simple_animal/hostile/blob/blobbernaut/Initialize(mapload) . = ..() @@ -235,6 +237,7 @@ . = ..() if(!.) return FALSE + move_resist = null flick("blobbernaut_death", src) /mob/living/simple_animal/hostile/blob/blobbernaut/proc/blob_talk() diff --git a/code/modules/mining/fulton.dm b/code/modules/mining/fulton.dm index 93942dd8783..71dee585df6 100644 --- a/code/modules/mining/fulton.dm +++ b/code/modules/mining/fulton.dm @@ -73,6 +73,9 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons) return ITEM_INTERACT_COMPLETE if(A.anchored || (A.move_resist > max_force_fulton)) return ITEM_INTERACT_COMPLETE + if(ismegafauna(A)) + to_chat(user, "[src] is too heavy to retrieve!") + return ITEM_INTERACT_COMPLETE to_chat(user, "You start attaching the pack to [A]...") if(do_after(user, 50, target = A)) to_chat(user, "You attach the pack to [A] and activate it.") diff --git a/code/modules/mining/lavaland/loot/hierophant_loot.dm b/code/modules/mining/lavaland/loot/hierophant_loot.dm index e7b312823d7..57bc3a1b28b 100644 --- a/code/modules/mining/lavaland/loot/hierophant_loot.dm +++ b/code/modules/mining/lavaland/loot/hierophant_loot.dm @@ -224,6 +224,8 @@ var/obj/effect/temp_visual/hierophant/blast/B = new /obj/effect/temp_visual/hierophant/blast(t, user, TRUE) //but absolutely will hurt enemies B.damage = 30 for(var/mob/living/L in range(1, source)) + if(ismegafauna(L) && L.stat == DEAD) + continue INVOKE_ASYNC(src, PROC_REF(teleport_mob), source, L, T, user) //regardless, take all mobs near us along sleep(6) //at this point the blasts detonate if(beacon) diff --git a/code/modules/mob/living/carbon/alien/alien_base.dm b/code/modules/mob/living/carbon/alien/alien_base.dm index 557757b751e..8db7eb9c5c6 100644 --- a/code/modules/mob/living/carbon/alien/alien_base.dm +++ b/code/modules/mob/living/carbon/alien/alien_base.dm @@ -22,6 +22,9 @@ ventcrawler = VENTCRAWLER_ALWAYS var/death_message = "lets out a waning guttural screech, green blood bubbling from its maw..." var/death_sound = 'sound/voice/hiss6.ogg' + ignore_generic_organs = TRUE + contains_xeno_organ = TRUE + surgery_container = /datum/xenobiology_surgery_container/alien /mob/living/carbon/alien/Initialize(mapload) . = ..() @@ -31,6 +34,10 @@ var/obj/item/organ/internal/organ = new organ_path() organ.insert(src) +/mob/living/carbon/alien/death(gibbed) + move_resist = null + return ..() + /// returns the list of type paths of the organs that we need to insert into /// this particular xeno upon its creation /mob/living/carbon/alien/proc/get_caste_organs() diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/alien_drone.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/alien_drone.dm index a9b25613ae6..a0f4659be3c 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/alien_drone.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/alien_drone.dm @@ -4,6 +4,7 @@ maxHealth = 150 health = 150 icon_state = "aliend_s" + surgery_container = /datum/xenobiology_surgery_container/alien/drone /mob/living/carbon/alien/humanoid/drone/Initialize(mapload) . = ..() diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm index ac5eea221d0..7948978bcbe 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm @@ -8,6 +8,7 @@ var/leap_on_click = FALSE /// Are we on leap cooldown? var/on_leap_cooldown = FALSE + surgery_container = /datum/xenobiology_surgery_container/alien/hunter /mob/living/carbon/alien/humanoid/hunter/Initialize(mapload) . = ..() diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm index 9b9ae103770..9e20672738e 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm @@ -4,6 +4,7 @@ maxHealth = 175 health = 175 icon_state = "aliens_s" + surgery_container = /datum/xenobiology_surgery_container/alien/sentinel /mob/living/carbon/alien/humanoid/sentinel/large name = "alien praetorian" diff --git a/code/modules/mob/living/carbon/alien/humanoid/queen.dm b/code/modules/mob/living/carbon/alien/humanoid/queen.dm index 84a31d4d7a4..789444d4c43 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/queen.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/queen.dm @@ -12,6 +12,7 @@ alien_disarm_damage = 60 //Queens do higher disarm stamina damage than normal aliens alien_slash_damage = 30 //Queens do higher slashing damage to people alien_movement_delay = 1 //This represents a movement delay of 1, or roughly 80% the movement speed of a normal carbon mob + surgery_container = /datum/xenobiology_surgery_container/alien/queen /mob/living/carbon/alien/humanoid/queen/Initialize(mapload) . = ..() diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm index 1f882728bc9..b56e3392ceb 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva.dm @@ -9,6 +9,7 @@ health = 25 butcher_results = list(/obj/item/food/monstermeat/xenomeat = 1) density = FALSE + surgery_container = /datum/xenobiology_surgery_container/alien/larva var/temperature_resistance = T0C+75 var/amount_grown = 0 diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 67c8f1040ab..1bd9d643cb3 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -21,15 +21,6 @@ if(volume > 10) // Anything over 10 volume will make the mob wetter. wetlevel = min(wetlevel + 1,5) -/mob/living/carbon/item_interaction(mob/living/user, obj/item/I, list/modifiers) - if(length(surgeries)) - if(user.a_intent == INTENT_HELP) - for(var/datum/surgery/S in surgeries) - if(S.next_step(user, src)) - return ITEM_INTERACT_COMPLETE - - return ..() - /mob/living/carbon/attack_hand(mob/living/carbon/human/user) if(!iscarbon(user)) return diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 3a73640d4b6..7367e882886 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -221,6 +221,30 @@ AM.setDir(current_dir) now_pushing = FALSE +/mob/living/examine(mob/user) + . = ..() + if(stat != DEAD) + return + if(!user.reagent_vision()) + return + var/datum/surgery/dissection + for(var/datum/surgery/dissect/D in surgeries) + dissection = D + if(dissection) + . += "You detect the next dissection step will be: [dissection.get_surgery_step()]" + if(surgery_container && !contains_xeno_organ) + . += "[src] looks like [p_they()] [p_have()] had [p_their()] organs dissected!" + + +/mob/living/item_interaction(mob/living/user, obj/item/I, list/modifiers) + if(length(surgeries)) + if(user.a_intent == INTENT_HELP) + for(var/datum/surgery/S in surgeries) + if(S.next_step(user, src)) + return ITEM_INTERACT_COMPLETE + + return ..() + /mob/living/CanPathfindPass(to_dir, datum/can_pass_info/pass_info) return TRUE // Unless you're a mule, something's trying to run you over. diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index b80b9349aeb..cf18665b4b5 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -110,6 +110,9 @@ /// List of traits that should be applied on Initialize var/list/initial_traits = list() + /// Sets our icon to `null` when `gib()` is used + var/gib_nullifies_icon = TRUE + /* Taste Vars */ @@ -119,5 +122,12 @@ var/last_taste_text ///If a creature gets to be super special and have extra range on their chat messages var/extra_message_range = 0 - /// Sets our icon to `null` when `gib()` is used - var/gib_nullifies_icon = TRUE + + /// Points to what specialized dissection information a mob gets. + var/datum/xenobiology_surgery_container/surgery_container + + /// Does this creature contain a xeno organ we can harvest? + var/contains_xeno_organ = FALSE + + /// The toggle for if this creature should ignore the rolls for generic organs upon dissection + var/ignore_generic_organs = FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/angel_statue.dm b/code/modules/mob/living/simple_animal/hostile/angel_statue.dm index 5e3352fe16f..041c03db203 100644 --- a/code/modules/mob/living/simple_animal/hostile/angel_statue.dm +++ b/code/modules/mob/living/simple_animal/hostile/angel_statue.dm @@ -167,6 +167,8 @@ base_cooldown = 300 clothes_req = FALSE aoe_range = 14 + /// Is this ability granted from a xenobiology organ? Causes user to spark. + var/from_organ = FALSE /datum/spell/aoe/flicker_lights/create_new_targeting() var/datum/spell_targeting/aoe/turf/targeting = new() @@ -177,6 +179,8 @@ for(var/turf/T in targets) for(var/obj/machinery/light/L in T) L.forced_flicker() + if(from_organ) + do_sparks(3, FALSE, user) //Blind AOE /datum/spell/aoe/blindness diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm index 832ffc92774..83327ba9c72 100644 --- a/code/modules/mob/living/simple_animal/hostile/carp.dm +++ b/code/modules/mob/living/simple_animal/hostile/carp.dm @@ -143,6 +143,9 @@ var/regen_cooldown = 0 + contains_xeno_organ = TRUE + surgery_container = /datum/xenobiology_surgery_container/megacarp + /mob/living/simple_animal/hostile/carp/megacarp/Initialize(mapload) . = ..() name = "[pick(GLOB.megacarp_first_names)] [pick(GLOB.megacarp_last_names)]" diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index e492428dacc..50a6484e493 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -32,6 +32,8 @@ attack_sound = 'sound/weapons/bite.ogg' gold_core_spawnable = HOSTILE_SPAWN var/venom_per_bite = 0 // While the /poison/ type path remains as-is for consistency reasons, we're really talking about venom, not poison. + contains_xeno_organ = TRUE + surgery_container = /datum/xenobiology_surgery_container/spider /mob/living/simple_animal/hostile/poison/giant_spider/Initialize(mapload) . = ..() diff --git a/code/modules/mob/living/simple_animal/hostile/headslug.dm b/code/modules/mob/living/simple_animal/hostile/headslug.dm index 562f97d1c0e..4dcdc0e41d5 100644 --- a/code/modules/mob/living/simple_animal/hostile/headslug.dm +++ b/code/modules/mob/living/simple_animal/hostile/headslug.dm @@ -31,6 +31,9 @@ var/datum/mind/origin var/egg_layed = FALSE sentience_type = SENTIENCE_OTHER + contains_xeno_organ = TRUE + ignore_generic_organs = TRUE + surgery_container = /datum/xenobiology_surgery_container/headslug /mob/living/simple_animal/hostile/headslug/proc/Infect(mob/living/carbon/victim) var/obj/item/organ/internal/body_egg/changeling_egg/egg = new(victim) diff --git a/code/modules/mob/living/simple_animal/hostile/hellhound.dm b/code/modules/mob/living/simple_animal/hostile/hellhound.dm index 8cc3209b113..a9c237f7498 100644 --- a/code/modules/mob/living/simple_animal/hostile/hellhound.dm +++ b/code/modules/mob/living/simple_animal/hostile/hellhound.dm @@ -34,6 +34,9 @@ var/smoke_lastuse = 0 var/smoke_freq = 300 // 30 seconds footstep_type = FOOTSTEP_MOB_CLAW + contains_xeno_organ = TRUE + ignore_generic_organs = TRUE + surgery_container = /datum/xenobiology_surgery_container/hound /mob/living/simple_animal/hostile/hellhound/Initialize(mapload) . = ..() diff --git a/code/modules/mob/living/simple_animal/hostile/killertomato.dm b/code/modules/mob/living/simple_animal/hostile/killertomato.dm index 46d359a6a1f..2b10afea5e4 100644 --- a/code/modules/mob/living/simple_animal/hostile/killertomato.dm +++ b/code/modules/mob/living/simple_animal/hostile/killertomato.dm @@ -25,3 +25,5 @@ minbodytemp = 150 maxbodytemp = 500 gold_core_spawnable = HOSTILE_SPAWN + contains_xeno_organ = TRUE + surgery_container = /datum/xenobiology_surgery_container/tomato diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm index d72a180151a..61a1326b1b7 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm @@ -56,6 +56,7 @@ Difficulty: Hard icon = 'icons/mob/lavaland/64x64megafauna.dmi' icon_state = "ancient_robot" icon_living = "ancient_robot" + icon_dead = "ancient_robot_dead" friendly = "stares down" speak_emote = list("BUZZES") universal_speak = TRUE @@ -69,7 +70,6 @@ Difficulty: Hard ranged = TRUE pixel_x = -16 pixel_y = -16 - del_on_death = TRUE loot = list(/obj/item/pinpointer/tendril) crusher_loot = list(/obj/item/crusher_trophy/adaptive_intelligence_core) internal_gps = /obj/item/gps/internal/ancient @@ -78,6 +78,9 @@ Difficulty: Hard deathmessage = "explodes into a shower of alloys" footstep_type = FOOTSTEP_MOB_HEAVY //make stomp like bubble attack_action_types = list() + contains_xeno_organ = TRUE + ignore_generic_organs = TRUE + surgery_container = /datum/xenobiology_surgery_container/vetus difficulty_ore_modifier = 4 //Vetus' whole deal was that it dropped ore before all megas did, so it gets a ton var/charging = FALSE @@ -158,7 +161,17 @@ Difficulty: Hard invisibility = 100 /mob/living/simple_animal/hostile/megafauna/ancient_robot/death(gibbed, allowed = FALSE) + icon = 'icons/mob/lavaland/corpses.dmi' if(allowed) + overlays.Cut() + underlays.Cut() + QDEL_NULL(TR) + QDEL_NULL(TL) + QDEL_NULL(BR) + QDEL_NULL(BL) + QDEL_NULL(beam) + body_shield_enabled = FALSE + update_appearance(UPDATE_OVERLAYS) return ..() else if(exploding) //but it refused return @@ -169,9 +182,10 @@ Difficulty: Hard /mob/living/simple_animal/hostile/megafauna/ancient_robot/Life(seconds, times_fired) ..() - if(!exploding) + if(!exploding && stat != DEAD) return - playsound(src, 'sound/items/timer.ogg', 70, 0) + else if(stat != DEAD) + playsound(src, 'sound/items/timer.ogg', 70, 0) /mob/living/simple_animal/hostile/megafauna/ancient_robot/drop_loot() var/core_type = null @@ -601,6 +615,8 @@ Difficulty: Hard return /mob/living/simple_animal/hostile/megafauna/ancient_robot/Moved(atom/OldLoc, Dir, Forced = FALSE) + if(stat == DEAD || exploding == TRUE) // a check so it doesnt try to move after death + return if(Dir) leg_walking_controler(Dir) if(charging) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index 1b2fa18e4ef..af6dcd873b0 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -37,7 +37,7 @@ Difficulty: Hard attack_sound = 'sound/misc/demon_attack1.ogg' icon_state = "bubblegum" icon_living = "bubblegum" - icon_dead = "" + icon_dead = "bubblegum_dead" friendly = "stares down" icon = 'icons/mob/lavaland/96x96megafauna.dmi' speak_emote = list("gurgles") @@ -52,7 +52,6 @@ Difficulty: Hard melee_queue_distance = 20 // as far as possible really, need this because of blood warp ranged = TRUE pixel_x = -32 - del_on_death = TRUE difficulty_ore_modifier = 3 crusher_loot = list(/obj/item/crusher_trophy/demon_claws) loot = list(/obj/item/clothing/suit/space/hostile_environment, @@ -72,8 +71,12 @@ Difficulty: Hard internal_gps = /obj/item/gps/internal/bubblegum medal_type = BOSS_MEDAL_BUBBLEGUM score_type = BUBBLEGUM_SCORE - deathmessage = "sinks into a pool of blood, fleeing the battle. You've won, for now... " + deathmessage = "sinks into a pile of grotesque viscera, fleeing the battle. You've won, for now... " death_sound = 'sound/misc/enter_blood.ogg' + contains_xeno_organ = TRUE + ignore_generic_organs = TRUE + surgery_container = /datum/xenobiology_surgery_container/bubblegum + attack_action_types = list(/datum/action/innate/megafauna_attack/triple_charge, /datum/action/innate/megafauna_attack/hallucination_charge, /datum/action/innate/megafauna_attack/hallucination_surround, @@ -150,15 +153,20 @@ Difficulty: Hard /mob/living/simple_animal/hostile/megafauna/bubblegum/death(gibbed) qdel(second_life_portal) if(enraged && !second_life) + del_on_death = TRUE var/obj/structure/closet/crate/necropolis/bubblegum/bait/jebait = new /obj/structure/closet/crate/necropolis/bubblegum/bait(get_turf(src)) var/obj/effect/bubblegum_trigger/great_chest_ahead = new /obj/effect/bubblegum_trigger(jebait, ListTargets()) new /obj/effect/landmark/spawner/bubblegum_exit(get_turf(src)) great_chest_ahead.forceMove(jebait) if(second_life) + del_on_death = FALSE var/area/A = get_area(src) for(var/mob/M in A) to_chat(M, "YOU FUCK... I... I'll... get you later. Enjoy the last few days of your life...") new /obj/effect/bubblegum_exit(get_turf(src)) + name = "Fleshy Mass" + desc = "Whatever this is, doesn't actually look like it comes from bubblegum, but many other entities. just thinking about how many creatures met their end to make this pile makes you shudder." + icon = 'icons/mob/lavaland/corpses.dmi' return ..() /mob/living/simple_animal/hostile/megafauna/bubblegum/drop_loot() @@ -663,6 +671,8 @@ Difficulty: Hard break RegisterSignal(src, COMSIG_HOSTILE_FOUND_TARGET, PROC_REF(i_see_you)) for(var/mob/living/carbon/human/H in range(20)) + if(stat == DEAD) + continue to_chat(H, "MY HANDS WILL RELISH ENDING YOU... HERE AND NOW!") FindTarget(list(H), 1) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index ac2b7f0b39c..d21946f4620 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -34,7 +34,7 @@ Difficulty: Very Hard attack_sound = 'sound/magic/ratvar_attack.ogg' icon_state = "eva" icon_living = "eva" - icon_dead = "" + icon_dead = "eva_dead" friendly = "stares down" icon = 'icons/mob/lavaland/96x96megafauna.dmi' speak_emote = list("roars") @@ -45,22 +45,25 @@ Difficulty: Very Hard move_to_delay = 10 ranged = TRUE pixel_x = -32 - del_on_death = TRUE universal_speak = TRUE internal_gps = /obj/item/gps/internal/colossus medal_type = BOSS_MEDAL_COLOSSUS score_type = COLOSSUS_SCORE - difficulty_ore_modifier = 3 crusher_loot = list(/obj/item/crusher_trophy/blaster_tubes) - deathmessage = "disintegrates, leaving a glowing core in its wake." + deathmessage = "flakes away into innumerable pieces with the wind. It's metallic arm graft staying behind, falling to the ground with a loud thud." + difficulty_ore_modifier = 3 death_sound = 'sound/misc/demon_dies.ogg' enraged_loot = /obj/item/disk/fauna_research/colossus + contains_xeno_organ = TRUE + ignore_generic_organs = TRUE + surgery_container = /datum/xenobiology_surgery_container/colossus + /// Have we used our final attack yet? + var/final_available = TRUE + attack_action_types = list(/datum/action/innate/megafauna_attack/spiral_attack, /datum/action/innate/megafauna_attack/aoe_attack, /datum/action/innate/megafauna_attack/shotgun, /datum/action/innate/megafauna_attack/alternating_cardinals) - /// Have we used our final attack yet? - var/final_available = TRUE /mob/living/simple_animal/hostile/megafauna/colossus/Initialize(mapload) . = ..() @@ -71,6 +74,10 @@ Difficulty: Very Hard for(var/mob/living/simple_animal/hostile/megafauna/hierophant/H in GLOB.mob_list) H.RegisterSignal(src, COMSIG_MOB_APPLY_DAMAGE, TYPE_PROC_REF(/mob/living/simple_animal/hostile/megafauna/hierophant, easy_anti_cheese)) +/mob/living/simple_animal/hostile/megafauna/colossus/death(gibbed) + . = ..() + icon = 'icons/mob/lavaland/corpses.dmi' + /datum/action/innate/megafauna_attack/spiral_attack name = "Spiral Shots" button_overlay_icon = 'icons/mob/actions/actions.dmi' diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm index dfbfc72a346..bf18c0deb0a 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm @@ -62,6 +62,10 @@ Difficulty: Medium death_sound = 'sound/misc/demon_dies.ogg' footstep_type = FOOTSTEP_MOB_HEAVY enraged_loot = /obj/item/disk/fauna_research/ash_drake + contains_xeno_organ = TRUE + ignore_generic_organs = TRUE + surgery_container = /datum/xenobiology_surgery_container/drake + attack_action_types = list(/datum/action/innate/megafauna_attack/fire_cone, /datum/action/innate/megafauna_attack/fire_cone_meteors, /datum/action/innate/megafauna_attack/mass_fire, @@ -101,6 +105,13 @@ Difficulty: Medium desc = "Here there be dragons." invisibility = 100 +/mob/living/simple_animal/hostile/megafauna/dragon/death(gibbed) + move_force = MOVE_FORCE_OVERPOWERING + move_resist = MOVE_FORCE_OVERPOWERING + pull_force = MOVE_FORCE_OVERPOWERING + mob_size = MOB_SIZE_LARGE + return ..() + /mob/living/simple_animal/hostile/megafauna/dragon/OpenFire() if(swooping) return @@ -603,6 +614,7 @@ Difficulty: Medium crusher_loot = list() butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30) attack_action_types = list() + contains_xeno_organ = FALSE /mob/living/simple_animal/hostile/megafauna/dragon/lesser/generate_random_loot() return diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index 758a7a3c269..15538919304 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -43,6 +43,7 @@ Difficulty: Hard attack_sound = 'sound/weapons/sonic_jackhammer.ogg' icon_state = "hierophant" icon_living = "hierophant" + icon_dead = "hierophant_dead" friendly = "stares down" icon = 'icons/mob/lavaland/hierophant_new.dmi' faction = list("boss") //asteroid mobs? get that shit out of my beautiful square house @@ -62,13 +63,11 @@ Difficulty: Hard internal_gps = /obj/item/gps/internal/hierophant medal_type = BOSS_MEDAL_HIEROPHANT score_type = HIEROPHANT_SCORE - del_on_death = TRUE death_sound = 'sound/magic/repulse.ogg' enraged_loot = /obj/item/disk/fauna_research/hierophant - attack_action_types = list(/datum/action/innate/megafauna_attack/blink, - /datum/action/innate/megafauna_attack/chaser_swarm, - /datum/action/innate/megafauna_attack/cross_blasts, - /datum/action/innate/megafauna_attack/blink_spam) + contains_xeno_organ = TRUE + ignore_generic_organs = TRUE + surgery_container = /datum/xenobiology_surgery_container/hierophant var/burst_range = 3 //range on burst aoe var/beam_range = 5 //range on cross blast beams @@ -463,6 +462,8 @@ Difficulty: Hard set_stat(CONSCIOUS) // deathgasp wont run if dead, stupid for(var/turf/simulated/wall/indestructible/hierophant/T in GLOB.hierophant_walls) T.collapse() + icon = 'icons/mob/lavaland/corpses.dmi' + DeleteComponent(/datum/component/boss_music) ..(/* force_grant = stored_nearby */) /mob/living/simple_animal/hostile/megafauna/hierophant/Destroy() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm index a5a412abf89..53f5698357a 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm @@ -22,6 +22,7 @@ Difficulty: Medium maxHealth = 2500 icon_state = "mega_legion" icon_living = "mega_legion" + icon_dead = "mega_legion_dead" desc = "One of many." icon = 'icons/mob/lavaland/96x96megafauna.dmi' attacktext = "chomps" @@ -33,7 +34,6 @@ Difficulty: Medium wander = FALSE speed = 2 ranged = TRUE - del_on_death = TRUE retreat_distance = 5 minimum_distance = 5 pixel_x = -32 @@ -52,6 +52,9 @@ Difficulty: Medium mouse_opacity = MOUSE_OPACITY_ICON stat_attack = UNCONSCIOUS // Overriden from /tg/ - otherwise Legion starts chasing its minions appearance_flags = 512 + contains_xeno_organ = TRUE + ignore_generic_organs = TRUE + surgery_container = /datum/xenobiology_surgery_container/legion /mob/living/simple_animal/hostile/megafauna/legion/Initialize(mapload) . = ..() @@ -91,6 +94,7 @@ Difficulty: Medium ..() /mob/living/simple_animal/hostile/megafauna/legion/death(gibbed) + icon = 'icons/mob/lavaland/corpses.dmi' for(var/mob/living/simple_animal/hostile/megafauna/legion/other in GLOB.mob_list) if(other != src) other.loot = list(/obj/item/storm_staff) //Initial does not work with lists. diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm index 5aaac9a66d3..5165e67478b 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -81,6 +81,9 @@ /mob/living/simple_animal/hostile/megafauna/death(gibbed) GLOB.alive_megafauna_list -= UID() + // lets normalize the icons a bit + pixel_x = 0 + pixel_y = 0 // this happens before the parent call because `del_on_death` may be set if(can_die() && !admin_spawned) var/datum/status_effect/crusher_damage/C = has_status_effect(STATUS_EFFECT_CRUSHERDAMAGETRACKING) diff --git a/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm b/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm index f5514b40fbc..2b9651ec57d 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm @@ -31,6 +31,9 @@ aggro_vision_range = 9 turns_per_move = 5 gold_core_spawnable = HOSTILE_SPAWN + contains_xeno_organ = TRUE + surgery_container = /datum/xenobiology_surgery_container/basilisk + loot = list(/obj/item/stack/ore/diamond{layer = ABOVE_MOB_LAYER}, /obj/item/stack/ore/diamond{layer = ABOVE_MOB_LAYER}) @@ -86,6 +89,7 @@ loot = list() butcher_results = list(/obj/item/stack/ore/diamond = 2, /obj/item/stack/sheet/sinew = 2, /obj/item/stack/sheet/bone = 1) initial_traits = list(TRAIT_FLYING) + surgery_container = /datum/xenobiology_surgery_container/watcher /mob/living/simple_animal/hostile/asteroid/basilisk/watcher/magmawing name = "magmawing watcher" diff --git a/code/modules/mob/living/simple_animal/hostile/mining/elites/goliath_broodmother.dm b/code/modules/mob/living/simple_animal/hostile/mining/elites/goliath_broodmother.dm index 75a0295bb4b..3843f05b34a 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/elites/goliath_broodmother.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/elites/goliath_broodmother.dm @@ -39,6 +39,9 @@ mouse_opacity = MOUSE_OPACITY_ICON deathmessage = "explodes into gore!" loot_drop = /obj/item/crusher_trophy/broodmother_tongue + contains_xeno_organ = TRUE + ignore_generic_organs = TRUE + surgery_container = /datum/xenobiology_surgery_container/goliath attack_action_types = list(/datum/action/innate/elite_attack/tentacle_patch, /datum/action/innate/elite_attack/spawn_children, diff --git a/code/modules/mob/living/simple_animal/hostile/mining/elites/herald.dm b/code/modules/mob/living/simple_animal/hostile/mining/elites/herald.dm index 6404381fbd7..9328e2953a1 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/elites/herald.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/elites/herald.dm @@ -41,7 +41,9 @@ death_sound = 'sound/misc/demon_dies.ogg' deathmessage = "begins to shudder as it becomes transparent..." loot_drop = /obj/item/clothing/neck/cloak/herald_cloak - + contains_xeno_organ = TRUE + ignore_generic_organs = TRUE + surgery_container = /datum/xenobiology_surgery_container/herald attack_action_types = list(/datum/action/innate/elite_attack/herald_trishot, /datum/action/innate/elite_attack/herald_directionalshot, diff --git a/code/modules/mob/living/simple_animal/hostile/mining/elites/legionnaire.dm b/code/modules/mob/living/simple_animal/hostile/mining/elites/legionnaire.dm index f4c893448bf..29c6dcb1752 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/elites/legionnaire.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/elites/legionnaire.dm @@ -40,6 +40,9 @@ sight = SEE_MOBS // So it can see through smoke / charge through walls like the kool aid man. var/datum/effect_system/smoke_spread/bad/smoke loot_drop = /obj/item/crusher_trophy/legionnaire_spine + contains_xeno_organ = TRUE + ignore_generic_organs = TRUE + surgery_container = /datum/xenobiology_surgery_container/legionnaire attack_action_types = list(/datum/action/innate/elite_attack/legionnaire_charge, /datum/action/innate/elite_attack/head_detach, diff --git a/code/modules/mob/living/simple_animal/hostile/mining/elites/pandora.dm b/code/modules/mob/living/simple_animal/hostile/mining/elites/pandora.dm index bc4f66ae3a1..6cbb3c0cbc5 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/elites/pandora.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/elites/pandora.dm @@ -41,6 +41,9 @@ death_sound = 'sound/magic/repulse.ogg' deathmessage = "'s lights flicker, before its top part falls down." loot_drop = /obj/item/clothing/accessory/pandora_hope + contains_xeno_organ = TRUE + ignore_generic_organs = TRUE + surgery_container = /datum/xenobiology_surgery_container/pandora attack_action_types = list(/datum/action/innate/elite_attack/chaser_burst, /datum/action/innate/elite_attack/magic_box, diff --git a/code/modules/mob/living/simple_animal/hostile/mining/goldgrub.dm b/code/modules/mob/living/simple_animal/hostile/mining/goldgrub.dm index a56c7c8def7..1b6a0675acb 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/goldgrub.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/goldgrub.dm @@ -26,6 +26,8 @@ deathmessage = "spits up the contents of its stomach before dying!" status_flags = CANPUSH search_objects = 1 + contains_xeno_organ = TRUE + surgery_container = /datum/xenobiology_surgery_container/goldgrub wanted_objects = list(/obj/item/stack/ore/diamond, /obj/item/stack/ore/gold, /obj/item/stack/ore/silver, /obj/item/stack/ore/uranium) diff --git a/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm index 53b469dd4b3..3b168e022b2 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm @@ -34,6 +34,8 @@ var/pre_attack_icon = "Goliath_preattack" loot = list(/obj/item/stack/sheet/animalhide/goliath_hide) footstep_type = FOOTSTEP_MOB_HEAVY + contains_xeno_organ = TRUE + surgery_container = /datum/xenobiology_surgery_container/goliath /mob/living/simple_animal/hostile/asteroid/goliath/Life() . = ..() diff --git a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm index 91e135c65c5..551a4b07478 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm @@ -32,6 +32,8 @@ pass_flags = PASSTABLE butcher_results = list(/obj/item/organ/internal/regenerative_core = 1) var/brood_type = /mob/living/simple_animal/hostile/asteroid/hivelordbrood + contains_xeno_organ = TRUE + surgery_container = /datum/xenobiology_surgery_container/hivelord /mob/living/simple_animal/hostile/asteroid/hivelord/OpenFire(the_target) if(world.time >= ranged_cooldown) @@ -298,6 +300,7 @@ sentience_type = SENTIENCE_BOSS attack_sound = 'sound/misc/demon_attack1.ogg' speed = 0 + butcher_results = list(/obj/item/regen_mesh) /mob/living/simple_animal/hostile/asteroid/big_legion/AttackingTarget() if(!isliving(target)) diff --git a/code/modules/mob/living/simple_animal/hostile/netherworld.dm b/code/modules/mob/living/simple_animal/hostile/netherworld.dm index c4088eb86fd..dcc173d775a 100644 --- a/code/modules/mob/living/simple_animal/hostile/netherworld.dm +++ b/code/modules/mob/living/simple_animal/hostile/netherworld.dm @@ -16,6 +16,8 @@ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 faction = list("nether") + contains_xeno_organ = TRUE + surgery_container = /datum/xenobiology_surgery_container/sweating /mob/living/simple_animal/hostile/netherworld/migo name = "mi-go" @@ -26,19 +28,18 @@ icon_dead = "mi-go-dead" attacktext = "lacerates" speed = -0.5 - var/static/list/migo_sounds deathmessage = "wails as its form turns into a pulpy mush." death_sound = 'sound/voice/hiss6.ogg' + surgery_container = /datum/xenobiology_surgery_container/migo /mob/living/simple_animal/hostile/netherworld/migo/Initialize(mapload) . = ..() - migo_sounds = list('sound/items/bubblewrap.ogg', 'sound/items/change_jaws.ogg', 'sound/items/crowbar.ogg', 'sound/items/drink.ogg', 'sound/items/deconstruct.ogg', 'sound/items/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/screwdriver.ogg', 'sound/items/weeoo1.ogg', 'sound/items/wirecutter.ogg', 'sound/items/welder.ogg', 'sound/items/zip.ogg', 'sound/items/rped.ogg', 'sound/items/ratchet.ogg', 'sound/items/polaroid1.ogg', 'sound/items/pshoom.ogg', 'sound/items/airhorn.ogg', 'sound/voice/bcreep.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/ed209_20sec.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss6.ogg', 'sound/voice/mpatchedup.ogg', 'sound/voice/mfeelbetter.ogg', 'sound/weapons/sear.ogg', 'sound/ambience/antag/tatoralert.ogg', 'sound/mecha/nominal.ogg', 'sound/mecha/weapdestr.ogg', 'sound/mecha/critdestr.ogg', 'sound/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', 'sound/effects/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles.ogg', 'sound/effects/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clownstep2.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', 'sound/effects/empulse.ogg', 'sound/effects/explosionfar.ogg', 'sound/effects/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', 'sound/effects/heartbeat.ogg', 'sound/effects/hyperspace_begin.ogg', 'sound/effects/hyperspace_end.ogg', 'sound/goonstation/effects/screech.ogg', 'sound/effects/phasein.ogg', 'sound/effects/picaxe1.ogg', 'sound/effects/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', 'sound/effects/supermatter.ogg', 'sound/misc/desceration-01.ogg', 'sound/misc/desceration-02.ogg', 'sound/misc/desceration-03.ogg', 'sound/misc/bloblarm.ogg', 'sound/goonstation/misc/airraid_loop.ogg', 'sound/misc/interference.ogg', 'sound/misc/notice1.ogg', 'sound/misc/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', 'sound/weapons/armbomb.ogg', 'sound/weapons/chainsaw.ogg', 'sound/weapons/emitter.ogg', 'sound/weapons/emitter2.ogg', 'sound/weapons/blade1.ogg', 'sound/weapons/bladeslice.ogg', 'sound/weapons/blastcannon.ogg', 'sound/weapons/blaster.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/weapons/circsawhit.ogg', 'sound/weapons/cqchit2.ogg', 'sound/weapons/drill.ogg', 'sound/weapons/genhit1.ogg', 'sound/weapons/gunshots/gunshot_silenced.ogg', 'sound/weapons/gunshots/gunshot.ogg', 'sound/weapons/handcuffs.ogg', 'sound/weapons/homerun.ogg', 'sound/weapons/kenetic_accel.ogg', 'sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock_alien_prying.ogg', 'sound/machines/airlock_close.ogg', 'sound/machines/airlockforced.ogg', 'sound/machines/airlock_open.ogg', 'sound/machines/alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/boltsdown.ogg', 'sound/machines/boltsup.ogg', 'sound/machines/buzz-sigh.ogg', 'sound/machines/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/defib_charge.ogg', 'sound/machines/defib_failed.ogg', 'sound/machines/defib_ready.ogg', 'sound/machines/defib_zap.ogg', 'sound/machines/deniedbeep.ogg', 'sound/machines/ding.ogg', 'sound/machines/disposalflush.ogg', 'sound/machines/door_close.ogg', 'sound/machines/door_open.ogg', 'sound/machines/engine_alert1.ogg', 'sound/machines/engine_alert2.ogg', 'sound/machines/hiss.ogg', 'sound/machines/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/ambience/signal.ogg', 'sound/machines/synth_no.ogg', 'sound/machines/synth_yes.ogg', 'sound/machines/terminal_alert.ogg', 'sound/machines/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', 'sound/ai/outbreak5.ogg', 'sound/ai/outbreak7.ogg', 'sound/ai/alert.ogg', 'sound/ai/radiation.ogg', 'sound/ai/eshuttle_call.ogg', 'sound/ai/eshuttle_dock.ogg', 'sound/ai/eshuttle_recall.ogg', 'sound/ai/aimalf.ogg', 'sound/ambience/ambigen1.ogg', 'sound/ambience/ambigen3.ogg', 'sound/ambience/ambigen4.ogg', 'sound/ambience/ambigen5.ogg', 'sound/ambience/ambigen6.ogg', 'sound/ambience/ambigen10.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg') //hahahaha fuck you code divers /mob/living/simple_animal/hostile/netherworld/migo/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) ..() if(stat) return - var/chosen_sound = pick(migo_sounds) + var/chosen_sound = pick(GLOB.migo_sounds) playsound(src, chosen_sound, 50, TRUE) /mob/living/simple_animal/hostile/netherworld/migo/Life() @@ -46,7 +47,7 @@ if(stat) return if(prob(10)) - var/chosen_sound = pick(migo_sounds) + var/chosen_sound = pick(GLOB.migo_sounds) playsound(src, chosen_sound, 50, TRUE) /mob/living/simple_animal/hostile/netherworld/blankbody diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm index efd63381926..a7b271d5439 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm @@ -31,6 +31,8 @@ cold_damage_per_tick = 10 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp unsuitable_atmos_damage = 10 footstep_type = FOOTSTEP_MOB_SHOE + contains_xeno_organ = TRUE + surgery_container = /datum/xenobiology_surgery_container/clown /mob/living/simple_animal/hostile/retaliate/clown/goblin icon = 'icons/mob/animal.dmi' diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm index 0130f3b695a..79eba3ee754 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm @@ -8,7 +8,7 @@ GLOBAL_LIST_EMPTY(ts_spiderlist) GLOBAL_LIST_EMPTY(ts_egg_list) GLOBAL_LIST_EMPTY(ts_spiderling_list) GLOBAL_LIST_EMPTY(ts_infected_list) - +#define DECOMPOSE_TIMER 5 MINUTES // -------------------------------------------------------------------------------- // --------------------- TERROR SPIDERS: DEFAULTS --------------------------------- // -------------------------------------------------------------------------------- @@ -19,6 +19,8 @@ GLOBAL_LIST_EMPTY(ts_infected_list) name = "terror spider" desc = "The generic parent of all other terror spider types. If you see this in-game, it is a bug." gender = FEMALE + contains_xeno_organ = TRUE + surgery_container = /datum/xenobiology_surgery_container/terror_spider // Icons icon = 'icons/mob/terrorspider.dmi' @@ -314,26 +316,22 @@ GLOBAL_LIST_EMPTY(ts_infected_list) /mob/living/simple_animal/hostile/poison/terror_spider/Life(seconds, times_fired) . = ..() - if(stat == DEAD) // Can't use if(.) for this due to the fact it can sometimes return FALSE even when mob is alive. - if(prob(2)) - // 2% chance every cycle to decompose - visible_message("The dead body of [src] decomposes!") - gib() - else - if(degenerate) - adjustToxLoss(rand(1, 10)) - if(regen_points < regen_points_max) - regen_points += regen_points_per_tick - if(getBruteLoss() || getFireLoss()) - if(regen_points > regen_points_per_hp) - if(getBruteLoss()) - adjustBruteLoss(-1) - regen_points -= regen_points_per_hp - else if(getFireLoss()) - adjustFireLoss(-1) - regen_points -= regen_points_per_hp - if(prob(5)) // AA 2022-08-11 - This gives me prob(80) vibes. Should probably be refactored. - CheckFaction() + if(stat == DEAD) + return + if(degenerate) + adjustToxLoss(rand(1, 10)) + if(regen_points < regen_points_max) + regen_points += regen_points_per_tick + if(getBruteLoss() || getFireLoss()) + if(regen_points > regen_points_per_hp) + if(getBruteLoss()) + adjustBruteLoss(-1) + regen_points -= regen_points_per_hp + else if(getFireLoss()) + adjustFireLoss(-1) + regen_points -= regen_points_per_hp + if(prob(5)) // AA 2022-08-11 - This gives me prob(80) vibes. Should probably be refactored. + CheckFaction() /mob/living/simple_animal/hostile/poison/terror_spider/proc/handle_dying() if(!hasdied) @@ -353,9 +351,14 @@ GLOBAL_LIST_EMPTY(ts_infected_list) if(can_die()) if(!gibbed) msg_terrorspiders("[src] has died in [get_area(src)].") + addtimer(CALLBACK(src, PROC_REF(decompose_now)), DECOMPOSE_TIMER) handle_dying() return ..() +/mob/living/simple_animal/hostile/poison/terror_spider/proc/decompose_now() + visible_message("The dead body of [src] decomposes!") + gib() + /mob/living/simple_animal/hostile/poison/terror_spider/proc/spider_special_action() return @@ -476,3 +479,5 @@ GLOBAL_LIST_EMPTY(ts_infected_list) . = ..() if(pulling && !ismob(pulling) && pulling.density) . += 6 // Drastic move speed penalty for dragging anything that is not a mob or a non dense object + +#undef DECOMPOSE_TIMER diff --git a/code/modules/mob/living/simple_animal/slime/slime_life.dm b/code/modules/mob/living/simple_animal/slime/slime_life.dm index 6d447e27666..49ba9217207 100644 --- a/code/modules/mob/living/simple_animal/slime/slime_life.dm +++ b/code/modules/mob/living/simple_animal/slime/slime_life.dm @@ -7,6 +7,7 @@ handle_feeding() if(stat == CONSCIOUS) // Slimes in stasis don't lose nutrition, don't change mood and don't respond to speech handle_nutrition() + handle_organs() if(QDELETED(src)) // Stop if the slime split during handle_nutrition() return reagents.remove_all(0.5 * REAGENTS_METABOLISM * length(reagents.reagent_list)) //Slimes are such snowflakes @@ -269,7 +270,7 @@ if(prob(60)) rabid = FALSE - if(prob(10)) + if(prob(10) && !trained) Discipline-- if(!client && !stop_automated_movement) @@ -361,6 +362,8 @@ newmood = ":3" else if(Target) newmood = "mischievous" + else if(trained) + newmood = ":33" if(!newmood) if(Discipline && prob(25)) @@ -476,3 +479,24 @@ if(hunger == 2 || rabid || attacked) return TRUE return TRUE + +// Handles xeno organ processing, and turns the unidentified organs into the true organ type. +/mob/living/simple_animal/slime/proc/handle_organs() + if(!holding_organ) + return + if(istype(loc, /obj/machinery/computer/camera_advanced/xenobio)) + return // no processing while in the computer + if(!trained) // if we somehow untrain mid process + say("BLECK!!", pick(speak_emote)) + eject_organ() + if(organ_progress < 50) + organ_progress += 1 + return + organ_progress = 1 + say("All done!", pick(speak_emote)) + var/obj/item/organ/internal/finished_organ = new holding_organ.true_organ_type(src.loc) + finished_organ.organ_quality = holding_organ.unknown_quality + finished_organ.icon_state = holding_organ.icon_state + finished_organ.name = "[quality_to_string(finished_organ.organ_quality, FALSE)] [finished_organ.name]" + underlays.Cut() + QDEL_NULL(holding_organ) diff --git a/code/modules/mob/living/simple_animal/slime/slime_mob.dm b/code/modules/mob/living/simple_animal/slime/slime_mob.dm index 9b49db04205..67e27d03428 100644 --- a/code/modules/mob/living/simple_animal/slime/slime_mob.dm +++ b/code/modules/mob/living/simple_animal/slime/slime_mob.dm @@ -9,6 +9,7 @@ var/is_adult = FALSE var/docile = FALSE faction = list("slime", "neutral") + new_attack_chain = TRUE harm_intent_damage = 5 icon_living = "grey baby slime" @@ -49,7 +50,7 @@ var/target_patience = 0 // AI variable, cooloff-ish for how long it's going to follow its target var/mood = "" // To show its face - var/mutator_used = FALSE //So you can't shove a dozen mutators into a single slime + var/mutator_used = FALSE // So you can't shove a dozen mutators into a single slime var/force_stasis = FALSE var/static/regex/slime_name_regex = new("\\w+ (baby|adult) slime \\(\\d+\\)") @@ -64,6 +65,12 @@ var/Discipline = 0 /// Stun variable var/SStun = 0 + // does this slime have a xeno organ inserted into it already? + var/obj/item/xeno_organ/holding_organ + // An elevated form of discipline. Is this slime trained and ready to begin organ therapy? + var/trained = FALSE + // how far along in the organ processing are we + var/organ_progress = 0 ///////////TIME FOR SUBSPECIES @@ -111,6 +118,22 @@ Target = null return ..() +/mob/living/simple_animal/slime/death() + if(holding_organ) + eject_organ() + underlays.Cut() + return ..() + +/mob/living/simple_animal/slime/update_overlays() + . = ..() + if(stat != DEAD) + . += "aslime-[mood]" + if(holding_organ) + var/mutable_appearance/underlay_appearance = mutable_appearance(layer = 3.9, plane = GAME_PLANE) // 3.9 so it wont potentially layer weird if someones laying below + underlay_appearance.icon = icon = 'icons/mob/slimes.dmi' + underlay_appearance.icon_state = "xeno_organ" + underlays += underlay_appearance + /mob/living/simple_animal/slime/proc/set_colour(new_colour) colour = new_colour update_appearance(UPDATE_NAME) @@ -136,7 +159,7 @@ if(stat != DEAD) icon_state = icon_text if(mood && stat == CONSCIOUS) - add_overlay("aslime-[mood]") + update_appearance(UPDATE_OVERLAYS) else icon_state = icon_dead @@ -347,6 +370,11 @@ for(var/datum/surgery/S in surgeries) if(S.next_step(M, src)) return 1 + if(M.a_intent == INTENT_HELP) + new /obj/effect/temp_visual/heart(src.loc) + if(prob(4)) + discipline_slime(M, positive_reinforcement = TRUE) + if(..()) //successful attack attacked += 10 @@ -355,18 +383,43 @@ attacked += 10 discipline_slime(M) - /mob/living/simple_animal/slime/item_interaction(mob/living/user, obj/item/I, list/modifiers) if(stat == DEAD && length(surgeries)) if(user.a_intent == INTENT_HELP || user.a_intent == INTENT_DISARM) for(var/datum/surgery/S in surgeries) if(S.next_step(user, src)) return ITEM_INTERACT_COMPLETE + if(istype(I, /obj/item/xeno_organ) && stat == CONSCIOUS) + if(!is_adult) + to_chat(user, "The slime is not old enough yet to process organs!") + return ITEM_INTERACT_COMPLETE + if(holding_organ) + to_chat(user, "The slime is already processing an organ!") + return ITEM_INTERACT_COMPLETE + if(trained) + if(src.mind) + src.visible_message("Sentient slimes are unable to process organs!") + return ITEM_INTERACT_COMPLETE + if(user.transfer_item_to(I, src, force = TRUE)) + holding_organ = I + src.visible_message("The slime gently pulls the offered organ into itself.") + update_appearance(UPDATE_OVERLAYS) + return ITEM_INTERACT_COMPLETE + else + to_chat(user, "The slime seems puzzled at what you want it to do with the organ! It needs to be trained first.") + return ITEM_INTERACT_COMPLETE if(istype(I, /obj/item/stack/sheet/mineral/plasma) && stat == CONSCIOUS) //Let's you feed slimes plasma. - to_chat(user, "You feed the slime the plasma. It chirps happily.") var/obj/item/stack/sheet/mineral/plasma/S = I - S.use(1) - discipline_slime(user) + if(S.amount < 5) + to_chat(user, "You need at least five sheets of plasma to feed the slime!") + return ITEM_INTERACT_COMPLETE + new /obj/effect/temp_visual/heart(loc) + visible_message("[user] feeds the slime some plasma. It chirps happily!", "[user] feeds you a few sheets of plasma! Yummy!!!") + S.use(5) + if(Discipline) + trained = TRUE + else + discipline_slime(user, positive_reinforcement = TRUE) return ITEM_INTERACT_COMPLETE if(I.force > 0) attacked += 10 @@ -375,9 +428,12 @@ user.changeNext_move(CLICK_CD_MELEE) to_chat(user, "[I] passes right through [src]!") return ITEM_INTERACT_COMPLETE - if(Discipline && prob(50)) // wow, buddy, why am I getting attacked?? + if(Discipline && prob(50) && !trained) // wow, buddy, why am I getting attacked?? Discipline = 0 return ITEM_INTERACT_COMPLETE + if(trained && prob(20)) // trained slimes are more resistant to losing discipline + trained = 0 + visible_message("[src] gets spooked and cowers from [user]!") /mob/living/simple_animal/slime/attacked_by(obj/item/I, mob/living/user) if(..()) @@ -394,9 +450,18 @@ . = ..() var/water_damage = rand(10, 15) * volume adjustBruteLoss(water_damage) + // Extinguishers just piss them off more + if(!client && istype(source, /obj/effect/particle_effect/water)) + adjustBruteLoss(5) // extra potent + if(trained && prob(75)) + say("Ow! HEY!!!", pick(speak_emote)) + trained = FALSE + rabid = TRUE if(!client && Target && volume >= 3) // Like cats Target = null - ++Discipline + discipline_slime(FALSE) + if(prob(5)) + say("Ow ow ow!", pick(speak_emote)) /mob/living/simple_animal/slime/examine(mob/user) . = ..() @@ -413,7 +478,8 @@ else . += "It has severe punctures and tears in its flesh!" . += "\n" - + if(holding_organ) + . += "It seems to be currently processing something inside of itself." switch(powerlevel) if(2 to 3) . += "It is flickering gently with a little electrical activity." @@ -429,23 +495,30 @@ . += "" -/mob/living/simple_animal/slime/proc/discipline_slime(mob/user) +/mob/living/simple_animal/slime/proc/discipline_slime(mob/user, positive_reinforcement = FALSE) if(stat) return - if(prob(80) && !client) + if(!client) Discipline++ if(!is_adult) - if(Discipline == 1) + if(Discipline >= 1) attacked = 0 - if(Target) + if(trained && !positive_reinforcement) + say("Sorry...", pick(speak_emote)) Target = null + if(buckled) Feedstop(silent = TRUE) //we unbuckle the slime from the mob it latched onto. SStun = world.time + rand(20,60) + + if(positive_reinforcement) + mood = ":33" + regenerate_icons() + return spawn(0) ADD_TRAIT(src, TRAIT_IMMOBILIZED, SLIME_TRAIT) if(user) @@ -480,3 +553,13 @@ /mob/living/simple_animal/slime/unit_test_dummy wander = FALSE stop_automated_movement = TRUE + +/mob/living/simple_animal/slime/proc/eject_organ() + holding_organ.forceMove(loc) + visible_message("[src] drops \the [holding_organ.name] as it splits!") + holding_organ = null + update_appearance() + +/mob/living/simple_animal/slime/sentience_act() + . = ..() + eject_organ() diff --git a/code/modules/mob/living/simple_animal/slime/slime_powers.dm b/code/modules/mob/living/simple_animal/slime/slime_powers.dm index d61574cc4ee..9fa8b8c54c5 100644 --- a/code/modules/mob/living/simple_animal/slime/slime_powers.dm +++ b/code/modules/mob/living/simple_animal/slime/slime_powers.dm @@ -167,7 +167,8 @@ if(istype(loc, /obj/machinery/computer/camera_advanced/xenobio)) return //no you cannot split while you're in the matrix (this prevents GC issues and slimes disappearing) - + if(holding_organ) + eject_organ() var/list/babies = list() var/new_nutrition = round(nutrition * 0.9) var/new_powerlevel = round(powerlevel / 4) diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm index be556df636d..af00d87af4c 100644 --- a/code/modules/research/designs/machine_designs.dm +++ b/code/modules/research/designs/machine_designs.dm @@ -720,3 +720,13 @@ materials = list(MAT_GLASS = 1000) build_path = /obj/item/circuitboard/autochef category = list("Misc. Machinery") + +/datum/design/organ_analyzer + name = "Machine Design (Organ Analyzer)" + desc = "The circuit board for an organ analyzer." + id = "organ_analyzer" + req_tech = list("programming" = 3, "bluespace" = 3, "materials" = 3) + build_type = IMPRINTER + materials = list(MAT_GLASS = 1000) + build_path = /obj/item/circuitboard/organ_analyzer + category = list("Research Machinery") diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index ce89c24011b..73eabdfeead 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -295,6 +295,37 @@ materials = list(MAT_METAL = 2000, MAT_SILVER = 1500, MAT_PLASMA = 500, MAT_TITANIUM = 1500) category = list("Medical") +/datum/design/dissector + name = "Dissection Manager" + desc = "An advanced handheld device that assists with the preparation and removal of non-standard alien organs." + id = "dissection_manager" + req_tech = list("biotech" = 3, "materials" = 3, "programming" = 3) + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000, MAT_GLASS = 1500) + build_path = /obj/item/dissector + category = list("Medical") + +/datum/design/improved_dissector + name = "Improved Dissection Manager" + desc = "An advanced handheld device that assists with the preparation and removal of non-standard alien organs. This one has had several improvements applied to it." + id = "dissection_manager_upgraded" + req_tech = list("biotech" = 7, "materials" = 6, "engineering" = 6) + build_type = PROTOLATHE + materials = list(MAT_METAL = 4000, MAT_GLASS = 2000, MAT_SILVER = 1500, MAT_GOLD = 2000) + build_path = /obj/item/dissector/upgraded + category = list("Medical") + +// allows for perfect dissections, should be very hard to obtain. +/datum/design/alien_dissector + name = "Alien Dissection Manager" + desc = "A tool of alien origin, capable of near impossible levels of precision during dissections." + id = "dissection_manager_alien" + req_tech = list("biotech" = 7, "materials" = 7, "abductor" = 4) + build_type = PROTOLATHE + materials = list(MAT_METAL = 6000, MAT_GLASS = 4500, MAT_DIAMOND = 3000, MAT_TITANIUM = 4000, MAT_PLASMA = 4000) + build_path = /obj/item/dissector/alien + category = list("Medical") + ///////////////////////////////////////// //////////Cybernetic Implants//////////// ///////////////////////////////////////// diff --git a/code/modules/research/xenobiology/organ_analyzer.dm b/code/modules/research/xenobiology/organ_analyzer.dm new file mode 100644 index 00000000000..0e30e5dee95 --- /dev/null +++ b/code/modules/research/xenobiology/organ_analyzer.dm @@ -0,0 +1,194 @@ +/obj/item/circuitboard/organ_analyzer + board_name = "Organ Analyzer" + icon_state = "science" + build_path = /obj/machinery/organ_analyzer + board_type = "machine" + origin_tech = "biotech=2" + + req_components = list( + /obj/item/stock_parts/scanning_module = 2, + /obj/item/stock_parts/manipulator = 1, + ) + +/obj/machinery/organ_analyzer + name = "Organ Analysis Chamber" + desc = "A sophisticated machine made to break down and study samples of alien organs. Only revitalized organs are valid for analysis." + density = TRUE + anchored = TRUE + icon = 'icons/obj/xeno_organs.dmi' + icon_state = "organ_analyzer0" + idle_power_consumption = 40 + layer = ABOVE_OBJ_LAYER + resistance_flags = FIRE_PROOF|ACID_PROOF + // what organ are we currently holding? + var/obj/item/organ/internal/contains_organ + // Are we actively taking apart an organ? + var/processing_organ = FALSE + // How long until the machine finishes? + var/time_to_complete = 15 SECONDS + // How good is the machine at giving credits for our work? + var/reward_coeff + + COOLDOWN_DECLARE(process_organ) + +/obj/machinery/organ_analyzer/Initialize(mapload) + . = ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/organ_analyzer(null) + component_parts += new /obj/item/stock_parts/scanning_module(null) + component_parts += new /obj/item/stock_parts/scanning_module(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + RefreshParts() + update_appearance(UPDATE_OVERLAYS) + + +/obj/machinery/organ_analyzer/update_overlays() + underlays = null + if(contains_organ) + var/mutable_appearance/underlay_organ = mutable_appearance(layer = OBJ_LAYER, plane = GAME_PLANE) + underlay_organ.icon = icon = 'icons/obj/xeno_organs.dmi' + underlay_organ.icon_state = contains_organ.icon_state + underlay_organ.pixel_y = -3 + underlays += underlay_organ + var/mutable_appearance/underlay_base = mutable_appearance(layer = BELOW_OBJ_LAYER, plane = GAME_PLANE) + underlay_base.icon = 'icons/obj/xeno_organs.dmi' + if(processing_organ) + underlay_base.icon_state += "organ_analyzer_on" + else + underlay_base.icon_state += "organ_analyzer_off" + underlays += underlay_base + +/obj/machinery/organ_analyzer/RefreshParts() + reward_coeff = 0 + for(var/obj/item/stock_parts/scanning_module/S in component_parts) + reward_coeff += S.rating + reward_coeff = reward_coeff / 2 // average the parts + +/obj/machinery/organ_analyzer/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(processing_organ || contains_organ) + to_chat(user, "The machine cannot be opened while holding an organ! Remove it first.") + return TRUE + default_deconstruction_screwdriver(user, "organ_analyzer1", "organ_analyzer0", I) + +/obj/machinery/organ_analyzer/crowbar_act(mob/user, obj/item/I) + if(!panel_open) + return + . = TRUE + default_deconstruction_crowbar(user, I) + +/obj/machinery/organ_analyzer/attack_hand(mob/user) + if(panel_open) + to_chat(user, "You can't interact with the machine while the panel is open!") + return + if(stat & (NOPOWER|BROKEN)) + return + if(contains_organ) + if(processing_organ) + to_chat(user, "The machine is already analyzing the organ!") + return + else + COOLDOWN_START(src, process_organ, time_to_complete) + processing_organ = TRUE + playsound(src, 'sound/machines/organ_analyzer.ogg', 60, FALSE) + update_appearance(UPDATE_OVERLAYS) + +/obj/machinery/organ_analyzer/item_interaction(mob/living/user, obj/item/used, list/modifiers) + if(!istype(used, /obj/item/organ/internal)) + to_chat(user, "The machines rejects [used]; it finds no possible potential in it.") + return ITEM_INTERACT_COMPLETE + if(panel_open) + to_chat(user, "You can't interact with the machine while the panel is open!") + return ITEM_INTERACT_COMPLETE + var/obj/item/organ/internal/organ = used + if(organ.is_xeno_organ) + if(organ.type in GLOB.scanned_organs) + if(GLOB.scanned_organs[organ.type] >= 3) // No more than + to_chat(user, "The analyzer rejects the organ. It's gotten as much data as it can from this type of organ.") + return ITEM_INTERACT_COMPLETE + else + GLOB.scanned_organs[organ.type]++ + else + GLOB.scanned_organs += list(organ.type = 1) + contains_organ = organ + user.transfer_item_to(organ, src) + src.visible_message("[user] inserts [organ] into the recepticle.") + update_appearance(UPDATE_OVERLAYS) + return ITEM_INTERACT_COMPLETE + return ..() + +/obj/machinery/organ_analyzer/AltClick(mob/user) + if(panel_open) + to_chat(user, "You can't interact with the machine while the panel is open!") + return + if(processing_organ) + to_chat(user, "You cannot remove an organ currently being processed!") + return + if(contains_organ) + user.put_in_hands(contains_organ) + contains_organ = null + update_appearance(UPDATE_OVERLAYS) + +/obj/machinery/organ_analyzer/process() + ..() + if(stat & (NOPOWER|BROKEN)) + processing_organ = FALSE + update_appearance(UPDATE_OVERLAYS) + return + if(processing_organ && COOLDOWN_FINISHED(src, process_organ)) + complete_analysis() + +/obj/machinery/organ_analyzer/proc/complete_analysis() + playsound(src, 'sound/machines/ping.ogg', 50, FALSE) + processing_organ = FALSE + + if(contains_organ.hidden_origin_tech) + handle_disk() + + var/account = GLOB.station_money_database.get_account_by_department(DEPARTMENT_SCIENCE) + var/datum/money_account_database/main_station/station_db = GLOB.station_money_database + var/quality_modifier = 1 + if(contains_organ.organ_quality == ORGAN_PRISTINE) + quality_modifier = 2 + if(contains_organ.organ_quality == ORGAN_DAMAGED) + quality_modifier = 0.5 + var/final_value = round((contains_organ.analyzer_price * reward_coeff) * quality_modifier) + station_db.credit_account(account, final_value, "Organ Analyzation Subsidy", "Xenobiology Organ Analyzer", FALSE) + SSblackbox.record_feedback("amount", "Organ_Analyzer_Revenue", final_value) + atom_say("Analysis complete. Depositing [final_value] credits into the science account.") + qdel(contains_organ) + contains_organ = null + update_appearance(UPDATE_OVERLAYS) + +/obj/machinery/organ_analyzer/proc/handle_disk() + var/obj/item/disk/tech_disk/disk = new /obj/item/disk/tech_disk(src.loc) + var/datum/tech/tech + + switch(contains_organ.hidden_origin_tech) + if(TECH_MATERIAL) + tech = new /datum/tech/materials + if(TECH_ENGINEERING) + tech = new /datum/tech/engineering + if(TECH_PLASMA) + tech = new /datum/tech/plasmatech + if(TECH_POWER) + tech = new /datum/tech/powerstorage + if(TECH_BLUESPACE) + tech = new /datum/tech/bluespace + if(TECH_BIO) + tech = new /datum/tech/biotech + if(TECH_COMBAT) + tech = new /datum/tech/combat + if(TECH_MAGNETS) + tech = new /datum/tech/magnets + if(TECH_PROGRAM) + tech = new /datum/tech/programming + if(TECH_TOXINS) + tech = new /datum/tech/toxins + if(TECH_SYNDICATE) + tech = new /datum/tech/syndicate + if(TECH_ABDUCTOR) + tech = new /datum/tech/abductor + + tech.level = contains_organ.hidden_tech_level + disk.load_tech(tech) diff --git a/code/modules/research/xenobiology/xenobiology_dissections.dm b/code/modules/research/xenobiology/xenobiology_dissections.dm new file mode 100644 index 00000000000..d2faa54c369 --- /dev/null +++ b/code/modules/research/xenobiology/xenobiology_dissections.dm @@ -0,0 +1,838 @@ +/obj/item/dissector + name = "\improper Dissection Manager" + desc = "An advanced handheld device that assists with the preparation and removal of non-standard alien organs." + icon = 'icons/obj/surgery.dmi' + icon_state = "dissector" + lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' + flags = CONDUCT + w_class = WEIGHT_CLASS_SMALL + tool_behaviour = TOOL_DISSECTOR + +/obj/item/dissector/upgraded + name = "\improper Improved Dissection Manager" + desc = "An advanced handheld device that assists with the preparation and removal of non-standard alien organs. This one has had several improvements applied to it." + icon_state = "dissector_upgrade" + toolspeed = 0.6 + +// allows for perfect pristine organ extraction. Only available from non-lavaland abductor tech +/obj/item/dissector/alien + name = "\improper Alien Dissection Manager" + desc = "A tool of alien origin, capable of near impossible levels of precision during dissections." + icon_state = "dissector_alien" + origin_tech = "abductor=3" + toolspeed = 0.2 + +/obj/item/dissector/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT) + AddComponent(/datum/component/surgery_initiator) + RegisterSignal(src, COMSIG_BIT_ATTACH, PROC_REF(add_bit)) + RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(remove_bit)) + +/datum/surgery_step/generic/dissect + name = "dissect" + allowed_tools = list( + /obj/item/dissector/alien = 100, + /obj/item/dissector/upgraded = 70, + /obj/item/organ_extractor = 60, + TOOL_DISSECTOR = 40, + /obj/item/scalpel/laser/manager = 10, + /obj/item/scalpel = 5, + /obj/item/wirecutters = 2, + /obj/item/kitchen/utensil/fork = 1, + ) + preop_sound = 'sound/surgery/organ1.ogg' + success_sound = 'sound/surgery/organ2.ogg' + failure_sound = 'sound/effects/bone_break_1.ogg' + time = 1.5 SECONDS + +/datum/surgery_step/generic/dissect/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_text[surgery.step_number]]") + return ..() + +/datum/surgery_step/generic/dissect/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_success_text[surgery.step_number]]") + if(length(surgery.steps) <= surgery.step_number) // only procs if its the finishing step + if(isalien(target)) // xenos are special snowflakes + var/picked = pick_organ(target) + var/obj/item/organ/internal/alien/xeno = new picked(target.loc) + xeno.organ_quality = pick_quality(tool, surgery.get_surgery_step()) + SSblackbox.record_feedback("nested tally", "xeno_organ_type", 1, list("[picked]", xeno.organ_quality)) + target.surgery_container.xeno_specialized_organs -= picked + for(var/obj/item/organ/internal/alien/A in target.internal_organs) + if(A.type == xeno) + A.remove() + if(istype(tool, /obj/item/organ_extractor)) + var/obj/item/organ_extractor/I = tool + I.insert_internal_organ_in_extractor(xeno) + else + if(istype(tool, /obj/item/organ_extractor)) // lets directly insert it into an organ extractor + var/obj/item/organ_extractor/I = tool + var/organ_type = pick_organ(target) + var/obj/item/organ/internal/organ = new organ_type(target.loc) + organ.organ_quality = pick_quality(tool, surgery.get_surgery_step()) + SSblackbox.record_feedback("nested tally", "xeno_organ_type", 1, list("[organ.type]", organ.organ_quality)) + I.insert_internal_organ_in_extractor(organ) + return SURGERY_STEP_CONTINUE + var/obj/item/xeno_organ/new_organ = new /obj/item/xeno_organ(target.loc) + if(length(target.surgery_container.custom_organ_states)) + new_organ.icon_state = pick(target.surgery_container.custom_organ_states) + new_organ.true_organ_type = pick_organ(target) + new_organ.unknown_quality = pick_quality(tool, surgery.get_surgery_step()) + user.put_in_inactive_hand(new_organ) + SSblackbox.record_feedback("nested tally", "xeno_organ_type", 1, list("[new_organ.true_organ_type]", new_organ.unknown_quality)) + target.contains_xeno_organ = FALSE + return SURGERY_STEP_CONTINUE + +/datum/surgery_step/generic/dissect/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_failure_text[surgery.step_number]]") + return SURGERY_STEP_RETRY + +/datum/surgery_step/generic/dissect/proc/pick_organ(mob/living/target) + var/chosen_organ_type + if(!target.ignore_generic_organs && (prob(75) || !target.surgery_container.xeno_specialized_organs)) //if it has no specialized organs always roll generic + chosen_organ_type = pick(target.surgery_container.xeno_generic_organs) + else + chosen_organ_type = pick(target.surgery_container.xeno_specialized_organs) + return chosen_organ_type + +/// Decides the quality of the new organ based off tool efficiency and tool bit mods +/datum/surgery_step/generic/dissect/proc/pick_quality(obj/item/I, datum/surgery_step/current_step) + if(istype(I, /obj/item/dissector/alien)) + return ORGAN_PRISTINE + var/quality_chance + for(var/key in current_step.allowed_tools) + if(ispath(key) && istype(I, key)) + quality_chance = allowed_tools[key] + break + else if(I.tool_behaviour == key) + quality_chance = allowed_tools[key] + break + + var/inverted_chance = 100 - quality_chance + quality_chance += ((inverted_chance * 0.66) * -(I.bit_efficiency_mod - 1)) + if(prob(quality_chance / 2)) // at best, ~50% chance + return ORGAN_PRISTINE + if(prob(quality_chance)) + return ORGAN_NORMAL + return ORGAN_DAMAGED + +// Dissections should always end in he dissect step for organ quality sake +/datum/surgery/dissect + name = "experimental dissection" + requires_bodypart = FALSE // most simplemobs wont have limbs + possible_locs = list(BODY_ZONE_CHEST) + target_mobtypes = list(/mob/living) + requires_organic_bodypart = FALSE + lying_required = FALSE + steps = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/dissect, + ) + +/datum/surgery/dissect/can_start(mob/user, mob/living/target) + . = ..() + if(!.) + return FALSE + if(target.stat == DEAD && target.contains_xeno_organ) + return TRUE + return FALSE + +/datum/surgery_step/fake_robotics + name = "fake robotics operations - do not use this parent object" + allowed_tools = list() + time = 10 SECONDS + +/datum/surgery_step/fake_robotics/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_text[surgery.step_number]]") + if(tool && tool.tool_behaviour) + tool.play_tool_sound(user, 30) + return ..() + +/datum/surgery_step/fake_robotics/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_success_text[surgery.step_number]]") + if(tool && tool.tool_behaviour) + tool.play_tool_sound(user, 30) + return SURGERY_STEP_CONTINUE + +/datum/surgery_step/fake_robotics/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_failure_text[surgery.step_number]]") + if(tool && tool.tool_behaviour) + tool.play_tool_sound(user, 30) + return SURGERY_STEP_RETRY + +/datum/surgery_step/fake_robotics/unscrew_hatch + name = "unscrew hatch" + allowed_tools = list( + TOOL_SCREWDRIVER = 100, + /obj/item/coin = 50, + /obj/item/kitchen/knife = 50 + ) + + time = 1.6 SECONDS + +/datum/surgery_step/fake_robotics/open_hatch + name = "open hatch" + allowed_tools = list( + TOOL_RETRACTOR = 100, + TOOL_CROWBAR = 100, + /obj/item/kitchen/utensil = 50 + ) + + time = 2.4 SECONDS + +/datum/surgery_step/fake_robotics/amputate + name = "remove component" + + allowed_tools = list( + TOOL_MULTITOOL = 100 + ) + + time = 5 SECONDS + +/datum/xenobiology_surgery_container + /// Does this organ have custom icon states it should be choosing from + var/list/custom_organ_states = list() + + /// Contains the results for any specialized organs this creature should have + var/list/xeno_specialized_organs = list() + + /// Holds the list of all generic non-specific organs + var/static/list/xeno_generic_organs = list( + /obj/item/organ/internal/liver/xenobiology/toxic, + /obj/item/organ/internal/liver/xenobiology/detox, + /obj/item/organ/internal/heart/xenobiology/vestigial, + /obj/item/organ/internal/heart/xenobiology/incompatible, + /obj/item/organ/internal/appendix/xenobiology/feverish, + /obj/item/organ/internal/appendix/xenobiology/freezing, + /obj/item/organ/internal/kidneys/xenobiology/lethargic, + /obj/item/organ/internal/ears/xenobiology/colorful, + ) + + /// Contains the list for which paths are needed at each dissection step. + var/list/dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/dissect, + ) + + /// Contains specialty text for each dissection step. + var/list/dissection_text = list( + "You begin prepping the subject for dissection...", + "You begin to make a careful incision into the subject's corpse.", + "You begin clamping any cavities leaking fluids into the surgical site.", + "You begin forcing the dissection cavity open.", + "You begin removing an unidentifiable mass out of the subject!", + ) + /// Contains specialty text for dissection success steps + var/list/dissection_success_text = list( + "You successfully set up a dissection site.", + "You successfully make an incision into the dissection site.", + "You successfully clamp any leaking cavities.", + "You successfully force the dissection cavity open.", + "You remove some kind of unidentifiable mass from the subject!", + ) + /// Contains specialty text for dissection step failures + var/list/dissection_failure_text = list( + "The tool fails to get a grip on the corpse's surface!", + "Your hand slips, slicing open the corpse in a wrong spot with the tool!", + "Your hand slips, tearing some of the flesh from the subject and worsening the leakage!", + "Your hand slips, tearing the edges of incision!", + "The tool fails to remove the organ from the surrounding flesh!", + ) + +/datum/xenobiology_surgery_container/goliath + xeno_specialized_organs = list(/obj/item/organ/internal/appendix/xenobiology/tendril) + +/datum/xenobiology_surgery_container/megacarp + xeno_specialized_organs = list(/obj/item/organ/internal/heart/xenobiology/megacarp) + +/datum/xenobiology_surgery_container/morph + xeno_specialized_organs = list(/obj/item/organ/internal/liver/xenobiology/hungry) + +/datum/xenobiology_surgery_container/hound + xeno_specialized_organs = list(/obj/item/organ/internal/liver/xenobiology/sharp) + +/datum/xenobiology_surgery_container/sweating + xeno_specialized_organs = list(/obj/item/organ/internal/kidneys/xenobiology/sweating) + +/datum/xenobiology_surgery_container/watcher + xeno_specialized_organs = list(/obj/item/organ/internal/kidneys/xenobiology/sinew) + +/datum/xenobiology_surgery_container/migo + xeno_specialized_organs = list(/obj/item/organ/internal/appendix/xenobiology/noisemaker) + +/datum/xenobiology_surgery_container/spider + xeno_specialized_organs = list(/obj/item/organ/internal/appendix/xenobiology/toxin_stinger) + +/datum/xenobiology_surgery_container/hivelord + xeno_specialized_organs = list(/obj/item/organ/internal/heart/xenobiology/hyperactive) + +/datum/xenobiology_surgery_container/goldgrub + xeno_specialized_organs = list(/obj/item/organ/internal/kidneys/xenobiology/metallic) + +/datum/xenobiology_surgery_container/tomato + xeno_specialized_organs = list(/obj/item/organ/internal/liver/xenobiology/soupy) + +/datum/xenobiology_surgery_container/herald + xeno_specialized_organs = list(/obj/item/organ/internal/lungs/xenobiology/mirror) + +/datum/xenobiology_surgery_container/clown + xeno_specialized_organs = list( + /obj/item/organ/internal/heart/xenobiology/bananium, + /obj/item/organ/internal/heart/xenobiology/cursed_bananium, + ) + +/datum/xenobiology_surgery_container/revenant + xeno_specialized_organs = list(/obj/item/organ/internal/appendix/xenobiology/electro_strands) + + dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/dissect, + ) + + dissection_text = list( + "You begin prepping the subject for dissection...", + "You begin to easily open up a surgical site from the ashen mound.", + "You begin removing an unidentifiable mass out of the subject!", + ) + dissection_success_text = list( + "You successfully set up a dissection site.", + "You successfully force the dissection cavity open.", + "You remove some kind of unidentifiable mass from the subject!", + ) + dissection_failure_text = list( + "The tool fails to get a grip on the nearly ashen pile!", + "You struggle to get the surgical site open as ash crumples back in on itself!", + "The tool fails to remove the organ from the surrounding flesh!", + ) + +/datum/xenobiology_surgery_container/colossus + xeno_specialized_organs = list(/obj/item/organ/internal/cyberimp/mouth/xenobiology/vocal_remnants) + + custom_organ_states = list("colossus1", "colossus2", "colossus3", "colossus4") + + dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/dissect, + ) + + dissection_text = list( + "You begin to prep the subject for dissection...", + "You make a careful incision into the subject's corpse.", + "You clamp any cavities leaking fluids into the surgical site.", + "You force the dissection cavity open.", + "You carefully begin severing the metal sections from the surrounding flesh.", + "You begin removing an unidentifiable mass out of the subject!", + ) + dissection_success_text = list( + "You successfully set up a dissection site.", + "You successfully make an incision into the dissection site.", + "You successfully clamp any leaking cavities.", + "You successfully force the dissection cavity open.", + "You manage to cut away and seperate the metal from flesh.", + "You remove some kind of unidentifiable mass from the subject!", + ) + dissection_failure_text = list( + "The tool fails to get a grip on the corpse's surface!", + "Your hand slips, slicing open the corpse in a wrong spot with the tool!", + "Your hand slips, tearing some of the flesh from the subject and worsening the leakage!", + "Your hand slips, tearing the edges of incision!", + "The mixture of metal and flesh seems impossible to distinguish in some places!", + "The tool fails to remove the organ from the surrounding flesh!", + ) + +/datum/xenobiology_surgery_container/pandora + xeno_specialized_organs = list(/obj/item/organ/internal/heart/xenobiology/paradox) + + custom_organ_states = list("hiero1", "hiero2") + + dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/fake_robotics/unscrew_hatch, + /datum/surgery_step/fake_robotics/open_hatch, + /datum/surgery_step/fake_robotics/amputate, + /datum/surgery_step/generic/dissect, + ) + + dissection_text = list( + "You begin to prep the subject for dissection...", + "You begin to unscrew the coverings.", + "You begin prying open the loose panel from the machine.", + "You carefully begin to disconnect the core from the surrounding power network.", + "You begin removing the core from the metal housing surrounding it.", + ) + dissection_success_text = list( + "You successfully set up a dissection site.", + "You successfully remove any screws keeping the panel shut.", + "You pry open the loose panels to expose the core within.", + "You successfully disconnect the core from the power connectors.", + "You remove the core from the metal housing!", + ) + dissection_failure_text = list( + "You begin to prep the subject for dissection...", + "You cant get enough torque to unscrew the rusted fastenings off!", + "You fail to find enough leverage to get the panel off!", + "You cant find how to safely remove the core from its attached wiring!", + "The tool fails to remove the core from the metal housing!", + ) + +/datum/xenobiology_surgery_container/legionnaire + xeno_specialized_organs = list(/obj/item/organ/internal/ears/xenobiology/sinister) + + custom_organ_states = list("legion1", "legion2") + + dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/generic/amputate, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/dissect, + ) + + dissection_text = list( + "You begin to prep the subject for dissection...", + "You begin sawing through the dense bone obstructions.", + "With a sickening crunch, you begin forcing the dissection cavity open.", + "You begin removing an unidentifiable mass out of the subject!" + ) + dissection_success_text = list( + "You successfully set up a dissection site.", + "You manage to detach the bones away from the dissection cavity.", + "You successfully force the dissection cavity open.", + "You remove some kind of unidentifiable mass from the subject!" + ) + dissection_failure_text = list( + "The tool fails to get a grip on the corpse's surface!", + "Your saw fails to find purchase against the hardened bone!", + "The surrounding bone refuses to budge!", + "The tool fails to remove the organ from the surrounding skeletal structure!" + ) + +/datum/xenobiology_surgery_container/bubblegum + xeno_specialized_organs = list(/obj/item/organ/internal/heart/xenobiology/bloody_sack) + + custom_organ_states = list("bubblegum1", "bubblegum2", "bubblegum3", "bubblegum4") + + dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/dissect, + ) + + dissection_text = list( + "You begin to prep the corpse for dissection... If you can even call it that at this point", + "You begin removing shards and chunks of bone, clearing a spot to safely cut deeper.", + "You slowly cut your way into the pile, looking for anything other than formless flesh and bone...", + "You begin clamping the mass amount of leaking arteries in the surgical site.", + "You begin forcing the dissection cavity open.", + "You finally find something, and begin to remove a unidentifiable mass out of the mass!", + ) + dissection_success_text = list( + "You successfully set up a dissection site.", + "You manage to clear out the dissection site of obstructive bone.", + "You manage to cut deep enough until something of signifigance seems to reveal.", + "You successfully clamp any leaking cavities.", + "You successfully force the dissection cavity open.", + "You remove some kind of unidentifiable mass from the subject!", + ) + dissection_failure_text = list( + "The tool fails to get a grip on the corpse's surface!", + "You begin removing shards and chunks of bone, clearing a spot to safely cut deeper.", + "You slowly cut your way into the pile, looking for anything other than formless flesh and bone..", + "You clamp the mass amount of leaking arteries in the surgical site.", + "You force the dissection cavity open.", + "The tool fails to remove the organ from the surrounding flesh!", + ) + +/datum/xenobiology_surgery_container/alien + xeno_specialized_organs = list(/obj/item/organ/internal/alien/hivenode) + + dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/saw_carapace, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/dissect, + ) + + dissection_text = list( + "You begin to prep the subject for dissection...", + "You begin sawing through the chitinous outer layer.", + "You begin clamping any cavities leaking fluids into the surgical site.", + "You begin forcing the dissection cavity open.", + "You begin removing an unidentifiable mass out of the subject!", + ) + dissection_success_text = list( + "You successfully set up a dissection site.", + "You manage to saw through the armored exoskeleton.", + "You successfully clamp any leaking cavities.", + "You successfully force the dissection cavity open.", + "You remove some kind of unidentifiable mass from the subject!", + ) + dissection_failure_text = list( + "The tool fails to get a grip on the corpse's surface!", + "You fail to get through the armored outer layer!", + "Your hand slips, tearing some of the flesh from the subject and worsening the leakage!", + "Your hand slips, tearing the edges of incision!", + "The tool fails to remove the organ from the surrounding flesh!", + ) + +/datum/xenobiology_surgery_container/alien/sentinel + xeno_specialized_organs = list( + /obj/item/organ/internal/alien/plasmavessel, + /obj/item/organ/internal/alien/acidgland, + /obj/item/organ/internal/alien/neurotoxin, + /obj/item/organ/internal/alien/hivenode, + ) + +/datum/xenobiology_surgery_container/alien/larva + xeno_specialized_organs = list( + /obj/item/organ/internal/alien/plasmavessel/larva, + /obj/item/organ/internal/alien/acidgland, + /obj/item/organ/internal/alien/neurotoxin, + /obj/item/organ/internal/alien/hivenode, + ) + +/datum/xenobiology_surgery_container/alien/queen + xeno_specialized_organs = list( + /obj/item/organ/internal/alien/plasmavessel/queen, + /obj/item/organ/internal/alien/acidgland, + /obj/item/organ/internal/alien/eggsac, + /obj/item/organ/internal/alien/neurotoxin, + /obj/item/organ/internal/alien/resinspinner, + ) + +/datum/xenobiology_surgery_container/alien/drone + xeno_specialized_organs = list( + /obj/item/organ/internal/alien/plasmavessel/drone, + /obj/item/organ/internal/alien/acidgland, + /obj/item/organ/internal/alien/resinspinner, + /obj/item/organ/internal/alien/hivenode, + ) + +/datum/xenobiology_surgery_container/alien/hunter + xeno_specialized_organs = list( + /obj/item/organ/internal/alien/plasmavessel/hunter, + /obj/item/organ/internal/alien/hivenode, + /obj/item/organ/internal/alien/resinspinner, + ) + +/datum/xenobiology_surgery_container/hierophant + xeno_specialized_organs = list(/obj/item/organ/internal/eyes/cybernetic/xenobiology/glowing) + + custom_organ_states = list("hiero1", "hiero2") + + dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/fake_robotics/unscrew_hatch, + /datum/surgery_step/fake_robotics/open_hatch, + /datum/surgery_step/fake_robotics/amputate, + /datum/surgery_step/generic/dissect, + ) + + dissection_text = list( + "You begin to prep the subject for dissection...", + "You begin to unscrew the coverings.", + "You begin prying open the loose panel from the machine.", + "You carefully begin to disconnect the core from the surrounding power network.", + "You begin removing the core from the metal housing surrounding it.", + ) + dissection_success_text = list( + "You successfully set up a dissection site.", + "You successfully remove any screws keeping the panel shut.", + "You pry open the loose panels to expose the core within.", + "You successfully disconnect the core from the power connectors.", + "You remove the core from the metal housing!", + ) + dissection_failure_text = list( + "The tool fails to get a grip on the corpse's surface!", + "You cant get enough torque to unscrew the rusted fastenings off!", + "You fail to find enough leverage to get the panel off!", + "You cant find how to safely remove the core from its attached wiring!", + "The tool fails to remove the core from the metal housing!", + ) + +/datum/xenobiology_surgery_container/blobbernaut + xeno_specialized_organs = list(/obj/item/organ/internal/eyes/xenobiology/receptors) + + dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/dissect, + ) + + dissection_text = list( + "You begin to prep the subject for dissection...", + "You begin forcing the dissection cavity open.", + "You begin closing off one of the many leaking fluid sacks in the corpse.", + "Your work area closed filled back in. You begin to re-open it.", + "You begin closing off more leaking sacks and fluid veins.", + "This damned dissection site WON'T STOP CLOSING!", + "You begin once again closing off more fluid sacks and leaking cavities....", + "You at last begin to remove something from the cadaver...", + ) + dissection_success_text = list( + "You successfully set up a dissection site.", + "You easily pry apart the semi-liquid corpse.", + "You close off one numerous leaking cavities.", + "You manage to get the dissection site back open", + "You close off more leaking sacks and fluid veins.", + "You manage to get the dissection site back open... again.", + "You finally get the leaking under control.", + "You remove some kind of unidentifiable mass from the subject!", + ) + dissection_failure_text = list( + "The tool fails to get a grip on the corpse's surface!", + "The you fail to open the incision site, it simply closes back up!", + "The leakage is too intense, you cant get it under control!", + "The you fail to open the incision site, it simply closes back up!", + "The leakage is too intense, you cant get it under control!", + "The you fail to open the incision site, it simply closes back up!", + "The leakage is too intense, you cant get it under control!", + "The tool fails to remove the organ from the goopy flesh!", + ) + +/datum/xenobiology_surgery_container/terror_spider + xeno_specialized_organs = list(/obj/item/organ/internal/appendix/xenobiology/toxin_stinger/terror) + + dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/generic/amputate, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/dissect, + ) + + dissection_text = list( + "You begin to prep the subject for dissection...", + "You begin sawing through the chitinout outer layer.", + "You begin clamping any cavities leaking fluids into the surgical site.", + "You begin forcing the dissection cavity open.", + "You begin removing an unidentifiable mass out of the subject!", + ) + dissection_success_text = list( + "You begin to prep the subject for dissection...", + "You manage to saw through the hardened exoskeleton.", + "You clamp any cavities leaking fluids into the surgical site.", + "You force the dissection cavity open.", + "You remove some kind of unidentifiable mass from the subject!", + ) + dissection_failure_text = list( + "You begin to prep the subject for dissection...", + "Your saw fails to find purchase against the reinforced exoskeleton!", + "Your hand slips, tearing some of the flesh from the subject and worsening the leakage!", + "Your hand slips, tearing the edges of incision!", + "The tool fails to remove the organ from the surrounding flesh!", + ) + +/datum/xenobiology_surgery_container/vetus + xeno_specialized_organs = list(/obj/item/organ/internal/cell/xenobiology/supercharged) + + custom_organ_states = list("vetus1", "vetus2") + + dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/fake_robotics/unscrew_hatch, + /datum/surgery_step/fake_robotics/open_hatch, + /datum/surgery_step/fake_robotics/amputate, + /datum/surgery_step/generic/dissect, + ) + + dissection_text = list( + "You begin to prep the subject for dissection...", + "You begin to unscrew the coverings.", + "You begin prying open the loose panel from the machine.", + "You carefully begin to disconnect the core from the machinery without setting off any secondary explosions.", + "You begin removing the core from the metal housing surrounding it.", + ) + dissection_success_text = list( + "You successfully set up a dissection site.", + "You successfully remove any screws keeping the panel shut.", + "You pry open the loose panels to expose the core within.", + "You successfully disconnect the core from the power connectors.", + "You remove the core from the metal housing!", + ) + dissection_failure_text = list( + "You begin to prep the subject for dissection...", + "You cant get enough torque to unscrew the rusted fastenings off!", + "You fail to find enough leverage to get the panel off!", + "You cant find how to safely remove the core from its attached wiring!", + "The tool fails to remove the core from the metal housing!", + ) + +/datum/xenobiology_surgery_container/drake + xeno_specialized_organs = list(/obj/item/organ/internal/lungs/xenobiology/flame_sack) + + dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/generic/amputate, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/dissect, + ) + + dissection_text = list( + "You begin to prep the subject for dissection...", + "You begin sawing through the hardened scales of the drake.", + "You carefully begin removing the scales from the corpse", + "You begin to make a careful incision into the subject's corpse.", + "You begin clamping any cavities leaking fluids into the surgical site.", + "You begin forcing the dissection cavity open.", + "You begin removing an unidentifiable mass out of the subject!", + ) + dissection_success_text = list( + "You successfully set up a dissection site.", + "You feel the resistance of the hardened scales give away to the saw.", + "You pluck away the remaining scales from around the dissection site.", + "You successfully make an incision into the dissection site.", + "You successfully clamp any leaking cavities.", + "You force the dissection cavity open.", + "You remove some kind of unidentifiable mass from the subject!", + ) + dissection_failure_text = list( + "The tool fails to get a grip on the corpse's surface!", + "You fail to find enough leverage to saw through the thick scales!", + "You cant get a good enough grip on the loosened scales to pluck them off!", + "Your hand slips, slicing open the corpse in a wrong spot with the tool!", + "Your hand slips, tearing some of the flesh from the subject and worsening the leakage!", + "Your hand slips, tearing the edges of incision!", + "The tool fails to remove the organ from the surrounding flesh!", + ) + +/datum/xenobiology_surgery_container/headslug + xeno_specialized_organs = list(/obj/item/organ/internal/heart/xenobiology/contortion) + + dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/dissect, + ) + + dissection_text = list( + "You begin to prep the subject for dissection...", + "You carefully begin making a tiny incision in the diminutive creature.", + "You begin removing an unidentifiable mass out of the subject!", + ) + dissection_success_text = list( + "You successfully set up a dissection site.", + "You successfully make an incision into the tiny creature.", + "You remove some kind of unidentifiable mass from the subject!", + ) + dissection_failure_text = list( + "The tool fails to get a grip on the corpse's surface!", + "Your hand slips, slicing open the corpse in a wrong spot with the tool!", + "The tool fails to remove the organ from the surrounding flesh!", + ) + +/datum/xenobiology_surgery_container/basilisk + xeno_specialized_organs = list(/obj/item/organ/internal/kidneys/xenobiology/sweating) + + dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/generic/drill, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/dissect, + ) + + dissection_text = list( + "You begin to prep the subject for dissection...", + "You begin drilling into the crystals to weakent he structure.", + "You begin breaking apart the crystals preventing access to the subject.", + "You begin removing the remaining pieces of obstructive material.", + "You begin to make a careful incision into the subject's corpse.", + "You begin clamping any cavities leaking fluids into the surgical site.", + "You begin forcing the dissection cavity open.", + "You begin removing an unidentifiable mass out of the subject!", + ) + dissection_success_text = list( + "You successfully set up a dissection site.", + "You drill a series of holes into the crystalline carapace.", + "You crack open the crystalline lattice into many pieces.", + "You colelct and remove the myriad of loose crystals.", + "You successfully make an incision into the dissection site.", + "You successfully clamp any leaking cavities.", + "You successfully force the dissection cavity open.", + "You remove some kind of unidentifiable mass from the subject!", + ) + dissection_failure_text = list( + "The tool fails to get a grip on the corpse's surface!", + "You fail to drill through the hardened crystals!", + "You fail to get enough leverage to break apart the crystal lattice!", + "You cant seem to get a good grip on the irregular chunks of material!", + "Your hand slips, slicing open the corpse in a wrong spot with the tool!", + "Your hand slips, tearing some of the flesh from the subject and worsening the leakage!", + "Your hand slips, tearing the edges of incision!", + "The tool fails to remove the organ from the surrounding flesh!", + ) + +/datum/xenobiology_surgery_container/legion + xeno_specialized_organs = list(/obj/item/organ/internal/heart/xenobiology/squirming) + custom_organ_states = list("legion1", "legion2") + dissection_tool_step = list( + /datum/surgery_step/generic/dissect, + /datum/surgery_step/generic/amputate, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/dissect, + ) + + dissection_text = list( + "You begin to prep the subject for dissection...", + "You begin to saw through the thick skull plating.", + "You make a careful incision into the subject's corpse.", + "You begin cutting away at the small skulls continuing to bud up from the corpse.", + "You begin forcing the dissection cavity open.", + "You once again cut away at the budding skulls attempting to seal up the surgical site.", + "You begin removing an unidentifiable mass out of the subject!" + ) + dissection_success_text = list( + "You successfully set up a dissection site.", + "You manage to crack through the thick skull plating.", + "You successfully make an incision into the dissection site", + "You manage to cut away and control the growing buds.", + "You force the dissection cavity open.", + "You again manage to cut away and control the growing buds.", + "You remove an unidentifiable mass out of the subject!" + ) + dissection_failure_text = list( + "The tool fails to get a grip on the corpse's surface!", + "You cant get enough leverage to saw through the thick skull plating!", + "Your hand slips, slicing open the corpse in a wrong spot with the tool!", + "You fail to cut the buds away faster than they're regrowing!", + "Your hand slips, tearing the edges of incision!", + "You fail to cut the buds away faster than they're regrowing!", + "The tool fails to remove the organ from the surrounding flesh!" + ) diff --git a/code/modules/research/xenobiology/xenobiology_organs.dm b/code/modules/research/xenobiology/xenobiology_organs.dm new file mode 100644 index 00000000000..60a4963617e --- /dev/null +++ b/code/modules/research/xenobiology/xenobiology_organs.dm @@ -0,0 +1,1607 @@ +/obj/item/regen_mesh + name = "regenerative Organ Mesh" + desc = "A specialized mesh carved from a fleshling that can improve the quality of any organ it's used on." + icon = 'icons/obj/lavaland/ash_flora.dmi' + icon_state = "mushroom_leaf_p" // its unused and looks fine soooo whatever lol + new_attack_chain = TRUE + +/obj/item/regen_mesh/item_interaction(mob/living/user, obj/item/used, list/modifiers) + if(..()) + return + if(istype(used, /obj/item/organ/internal)) + var/obj/item/organ/internal/organ = used + if(!organ.is_xeno_organ) + to_chat(user, "The regenerative mesh cannot improve this!") + return ITEM_INTERACT_COMPLETE + if(organ.organ_quality < ORGAN_PRISTINE) + organ.organ_quality = ORGAN_PRISTINE + to_chat(user, "The regenerative mesh clings to [organ] and fuses to its surface. \The [organ] now looks pristine!") + organ.name = "pristine [name]" + qdel(src) + return ITEM_INTERACT_COMPLETE + else + to_chat(user, "This organ is already in pristine condition!") + return ITEM_INTERACT_COMPLETE + + +// We dont want to keep the unidentified organ as an implantable version to skip the research phase +/obj/item/xeno_organ + name = "unidentified Mass" + desc = "This unusual clump of flesh, though now still, holds great potential. It will require revitalization via slime therapy to get any use out of it." + icon = 'icons/obj/xeno_organs.dmi' + icon_state = "organ4" + origin_tech = null + /// What type of object will this object turn into when analyzed? + var/true_organ_type = /obj/item/organ/internal/liver/xenobiology/toxic + /// What quality will be hidden from us? + var/unknown_quality = ORGAN_NORMAL + +/obj/item/xeno_organ/Initialize(mapload) + . = ..() + icon_state = "organ[rand(1, 18)]" + +// A list of parent objects, to inherent the functions of where they are placed. +/obj/item/organ/internal/liver/xenobiology + name = "unidentified mass" + desc = "This is a parent object and should not appear. Contact a developer." + icon = 'icons/obj/xeno_organs.dmi' + icon_state = "organ4" + dead_icon = null + origin_tech = null + tough = TRUE + is_xeno_organ = TRUE + +/obj/item/organ/internal/heart/xenobiology + name = "unidentified mass" + desc = "This is a parent object and should not appear. Contact a developer." + icon = 'icons/obj/xeno_organs.dmi' + icon_state = "organ4" + dead_icon = null + origin_tech = null + tough = TRUE + is_xeno_organ = TRUE + /// Is this organ viable to be randomly inserted instead when the paradox organ is implanted + var/can_paradox = FALSE + +/obj/item/organ/internal/lungs/xenobiology + name = "unidentified mass" + desc = "This is a parent object and should not appear. Contact a developer." + icon = 'icons/obj/xeno_organs.dmi' + icon_state = "organ4" + dead_icon = null + origin_tech = null + tough = TRUE + is_xeno_organ = TRUE + +/obj/item/organ/internal/kidneys/xenobiology + name = "unidentified mass" + desc = "This is a parent object and should not appear. Contact a developer." + icon = 'icons/obj/xeno_organs.dmi' + icon_state = "organ4" + dead_icon = null + origin_tech = null + tough = TRUE + is_xeno_organ = TRUE + +/obj/item/organ/internal/appendix/xenobiology + name = "unidentified mass" + desc = "This is a parent object and should not appear. Contact a developer." + icon = 'icons/obj/xeno_organs.dmi' + icon_state = "organ4" + dead_icon = null + origin_tech = null + tough = TRUE + is_xeno_organ = TRUE + +/obj/item/organ/internal/eyes/xenobiology + name = "unidentified mass" + desc = "This is a parent object and should not appear. Contact a developer." + icon = 'icons/obj/xeno_organs.dmi' + icon_state = "organ4" + dead_icon = null + origin_tech = null + tough = TRUE + is_xeno_organ = TRUE + +/obj/item/organ/internal/ears/xenobiology + name = "unidentified mass" + desc = "This is a parent object and should not appear. Contact a developer." + icon = 'icons/obj/xeno_organs.dmi' + icon_state = "organ4" + dead_icon = null + origin_tech = null + tough = TRUE + is_xeno_organ = TRUE + +/obj/item/organ/internal/cell/xenobiology + name = "unidentified electronic" + desc = "This is a parent object and should not appear. Contact a developer." + icon = 'icons/obj/xeno_organs.dmi' + icon_state = "organ4" + dead_icon = null + origin_tech = null + tough = TRUE + is_xeno_organ = TRUE + +/obj/item/organ/internal/cyberimp/mouth/xenobiology + name = "unidentified electronic" + desc = "This is a parent object and should not appear. Contact a developer." + icon = 'icons/obj/xeno_organs.dmi' + icon_state = "organ4" + dead_icon = null + origin_tech = null + tough = TRUE + is_xeno_organ = TRUE + +/obj/item/organ/internal/eyes/cybernetic/xenobiology + name = "unidentified electronic" + desc = "This is a parent object and should not appear. Contact a developer." + icon = 'icons/obj/xeno_organs.dmi' + icon_state = "organ4" + dead_icon = null + origin_tech = null + tough = TRUE + is_xeno_organ = TRUE + +/obj/item/organ/internal/liver/xenobiology/toxic + name = "toxic glands" + desc = "These fleshy glands' alien chemistry are incompatible with most humanoid life." + + +/obj/item/organ/internal/liver/xenobiology/toxic/on_life() + . = ..() + if(!(owner.mob_biotypes & MOB_ORGANIC)) + return + to_chat(owner, "You feel nauseous as your insides feel like they're disintegrating!") + switch(organ_quality) + if(ORGAN_DAMAGED) + owner.adjustToxLoss(1) + if(ORGAN_NORMAL) + owner.adjustToxLoss(2) + if(ORGAN_PRISTINE) + owner.adjustToxLoss(5) + if(prob(5)) + owner.add_vomit_floor(toxvomit = TRUE) + owner.AdjustConfused(rand(4 SECONDS, 6 SECONDS)) + +/obj/item/organ/internal/liver/xenobiology/detox + name = "chemical neutralizers" + desc = "These glands seem to absorb any liquid they come in contact with, neutralizing any unnatural substances." + analyzer_price = 25 + +/obj/item/organ/internal/liver/xenobiology/detox/on_life() + . = ..() + if(!(owner.mob_biotypes & MOB_ORGANIC)) + return + switch(organ_quality) + if(ORGAN_DAMAGED) + owner.adjustToxLoss(-1) + if(ORGAN_NORMAL) + owner.adjustToxLoss(-2) + if(ORGAN_PRISTINE) // careful, it removes good shit too + owner.adjustToxLoss(-3) + for(var/datum/reagent/R in owner.reagents.reagent_list) + owner.reagents.remove_reagent(R.id,4) + +/obj/item/organ/internal/heart/xenobiology/vestigial + name = "vestigial organ" + desc = "Whether this has ever had any function is a mystery. It certainly doesn't work in its current state." + +/obj/item/organ/internal/heart/xenobiology/vestigial/on_life() + . = ..() + if(!owner.undergoing_cardiac_arrest()) + owner.set_heartattack(TRUE) // what did you expect? + +/obj/item/organ/internal/heart/xenobiology/incompatible + name = "incompatible organ" + desc = "This organ is largely incompatible with humanoid physiology. You might be able to get it to work, but will likely cause a host of other issues." + +/obj/item/organ/internal/heart/xenobiology/incompatible/on_life() + if(prob(0.6)) // it'll fail... eventually + if(!owner.undergoing_cardiac_arrest()) + owner.set_heartattack(TRUE) // yeah probably shouldnt use this + if(prob(10)) + owner.AdjustConfused(5 SECONDS, 5 SECONDS, 30 SECONDS) + if(prob(10)) + owner.AdjustJitter(5 SECONDS) + if(prob(10)) + owner.vomit(20) + +/obj/item/organ/internal/lungs/xenobiology/flame_sack + name = "flame sack" + desc = "An unusual set of aerosolizing glands capable of starting light fires." + analyzer_price = 50 + hidden_origin_tech = TECH_PLASMA + hidden_tech_level = 5 + +/obj/item/organ/internal/lungs/xenobiology/flame_sack/insert(mob/living/carbon/M, special = 0, dont_remove_slot = 0) + . = ..() + if(!isunathi(M)) + var/datum/action/innate/unathi_ignite/fire = new + fire.Grant(M) + if(organ_quality == ORGAN_PRISTINE) // grants a 3-range ash drake breath + M.AddSpell(new /datum/spell/drake_breath) + +/obj/item/organ/internal/lungs/xenobiology/flame_sack/remove(mob/living/carbon/M, special = 0) + . = ..() + if(!isunathi(M)) + for(var/datum/action/innate/unathi_ignite/fire in M.actions) + fire.Remove(M) + M.RemoveSpell(/datum/spell/drake_breath) + +/datum/spell/drake_breath + name = "Drake Breath" + desc = "Draw from special glands in your body to produce a small but dazzling flame!" + base_cooldown = 3 MINUTES + clothes_req = FALSE + stat_allowed = CONSCIOUS + invocation_type = "none" + action_icon_state = "fireball0" + sound = 'sound/magic/fireball.ogg' + active = FALSE + antimagic_flags = NONE + + selection_activated_message = "You take in a deep breath, readying to breathe fire!" + selection_deactivated_message = "You relax your breaths as you decide not to breathe fire." + +/datum/spell/drake_breath/create_new_targeting() + var/datum/spell_targeting/clicked_atom/external/C = new() + C.range = 20 + return C + +/datum/spell/drake_breath/update_spell_icon() + if(!action) + return + action.button_overlay_icon_state = "fireball[active]" + action.UpdateButtons() + +/datum/spell/drake_breath/cast(list/targets, mob/living/user) + . = ..() + var/target = targets[1] //There is only ever one target + var/turfs = line_target(0, 3, target, user) + dragon_fire_line(user, turfs) + +/datum/spell/drake_breath/proc/line_target(offset, range, atom/at, mob/living/user) + if(!at) + return + var/angle = ATAN2(at.x - user.x, at.y - user.y) + offset + var/turf/T = get_turf(user) + for(var/i in 1 to range) + var/turf/check = locate(user.x + cos(angle) * i, user.y + sin(angle) * i, user.z) + if(!check) + break + T = check + return (get_line(user, T) - get_turf(user)) + +/obj/item/organ/internal/kidneys/xenobiology/sinew + name = "sinewous bands" + desc = "Long, strands of durable fibers that seem to grow at astonishing speeds." + analyzer_price = 25 + +/obj/item/organ/internal/kidneys/xenobiology/sinew/insert(mob/living/carbon/M, special = 0, dont_remove_slot = 0) + . = ..() + var/datum/spell/create_sinew/sinew_spell = new + sinew_spell.quality = organ_quality + M.AddSpell(sinew_spell) + +/obj/item/organ/internal/kidneys/xenobiology/sinew/remove(mob/living/carbon/M, special = 0) + . = ..() + M.RemoveSpell(/datum/spell/create_sinew) + +/datum/spell/create_sinew + name = "Create Sinew Bands" + desc = "Detach some of your sinew-ous bands to create a durable restraint" + action_icon = 'icons/obj/mining.dmi' + action_icon_state = "sinewcuff" + invocation_type = "none" + clothes_req = FALSE + stat_allowed = CONSCIOUS + base_cooldown = 5 MINUTES + var/quality = ORGAN_NORMAL + var/cooldown = 0 + antimagic_flags = NONE + +/datum/spell/create_sinew/create_new_targeting() + return new /datum/spell_targeting/self + +/datum/spell/create_sinew/cast(list/targets, mob/living/user) + . = ..() + var/obj/item/restraints/handcuffs/sinew_cuffs = new /obj/item/restraints/handcuffs/sinew + switch(quality) + if(ORGAN_DAMAGED) + user.adjustBruteLoss(10) + to_chat(user, "The bands don't detach cleanly, ripping some flesh away with it!") + user.put_in_hands(sinew_cuffs) + if(ORGAN_NORMAL) + user.put_in_hands(sinew_cuffs) + if(ORGAN_PRISTINE) + sinew_cuffs.name = "strong [sinew_cuffs.name]" + sinew_cuffs.breakouttime = 45 SECONDS + user.put_in_hands(sinew_cuffs) + +/obj/item/organ/internal/heart/xenobiology/hyperactive + name = "hyperactive organ" + desc = "This organ replaces its own cells so quickly, that it appears to spread this effect to other cells around it in a rather exhaustive process." + analyzer_price = 25 + can_paradox = TRUE + hidden_origin_tech = TECH_BIO + hidden_tech_level = 5 + +/obj/item/organ/internal/heart/xenobiology/hyperactive/on_life() + . = ..() + switch(organ_quality) + if(ORGAN_DAMAGED) + if(prob(20)) // about saline level + owner.adjustBruteLoss(-2) + owner.adjustFireLoss(-2) + if(owner.getBruteLoss() > 10 || owner.getFireLoss() > 10) // this shits exhausting! + if(prob(30)) + owner.setStaminaLoss(15) + if(ORGAN_NORMAL) + if(owner.getBruteLoss() > 10 || owner.getFireLoss() > 10) // this shits exhausting! + if(prob(20)) + owner.setStaminaLoss(15) + owner.adjustBruteLoss(-1) + owner.adjustFireLoss(-1) + if(ORGAN_PRISTINE) + owner.adjustBruteLoss(-1) + owner.adjustFireLoss(-1) + +/obj/item/organ/internal/kidneys/xenobiology/metallic + name = "metallic processor" + desc = "A dense, metallic organ that enables the consumption of precious metals as food. No guarentee for taste, though" + analyzer_price = 20 + /// Component that handles the ability to eat precious metals + var/datum/component/special_tastes/special_tastes + +/obj/item/organ/internal/kidneys/xenobiology/metallic/insert(mob/living/carbon/organ_owner, special = FALSE, dont_remove_slot = FALSE) + . = ..() + var/static/list/edible_minerals = list( + /obj/item/stack/sheet/mineral/silver = list( + "reagents" = list( + "nutriment" = 1, + "vitamin" = 1, + ), + "tastes" = list( + "metal and blood. ow" = 1, + ), + ), + /obj/item/stack/sheet/mineral/uranium = list( + "reagents" = list( + "nutriment" = 1, + "uranium" = 1, + ), + "tastes" = list( + "metal and blood. ow" = 1, + "bad ideas" = 1, + ), + ) + ) + if(organ_quality >= ORGAN_NORMAL) + edible_minerals += list( + /obj/item/stack/sheet/mineral/gold = list( + "reagents" = list( + "salglu_solution" = 5, + "nutriment" = 1, + "vitamin" = 1, + ), + "tastes" = list( + "metal and blood. ow" = 1, + ), + ) + ) + if(organ_quality >= ORGAN_PRISTINE) + edible_minerals += list( + /obj/item/stack/sheet/mineral/diamond = list( + "reagents" = list( + "nutriment" = 1, + "bicaridine" = 3, + "kelotane" = 3, + "vitamin" = 1, + ), + "tastes" = list( + "like your teeth. owch" = 1, + ), + ) + ) + special_tastes = organ_owner.AddComponent(/datum/component/special_tastes, edible_minerals) + +/obj/item/organ/internal/kidneys/xenobiology/metallic/remove(mob/living/carbon/organ_owner, special = FALSE) + special_tastes.RemoveComponent() + return ..() + +/obj/item/organ/internal/cyberimp/mouth/xenobiology/vocal_remnants + name = "vocal cord remnants" + desc = "The remnants of a great beast's vocal coords. While only a fraction of the true organ's power, these could probably still get decently loud." + analyzer_price = 35 + hidden_origin_tech = TECH_COMBAT + hidden_tech_level = 7 + +/obj/item/organ/internal/cyberimp/mouth/xenobiology/vocal_remnants/Initialize(mapload) + . = ..() + icon_state = pick("colossus1", "colossus2", "colossus3", "colossus4") + +/obj/item/organ/internal/cyberimp/mouth/xenobiology/vocal_remnants/insert(mob/living/carbon/M, special = 0, dont_remove_slot = 0) + . = ..() + ADD_TRAIT(M, TRAIT_LOUD, ORGAN_TRAIT) + +/obj/item/organ/internal/cyberimp/mouth/xenobiology/vocal_remnants/remove(mob/living/carbon/M, special = 0) + . = ..() + REMOVE_TRAIT(M, TRAIT_LOUD, ORGAN_TRAIT) + +/obj/item/organ/internal/appendix/xenobiology/toxin_stinger + name = "hidden stinger" + desc = "This organ holds a deceptive stinger tucked inside of itself, dripping with venom." + analyzer_price = 35 + var/terror = FALSE + hidden_origin_tech = TECH_COMBAT + hidden_tech_level = 5 + +/obj/item/organ/internal/appendix/xenobiology/toxin_stinger/insert(mob/living/carbon/M, special = 0, dont_remove_slot = 0) + . = ..() + var/datum/spell/organ_sting/spell = new() + if(organ_quality == ORGAN_DAMAGED) + spell.base_cooldown = 8 MINUTES + if(terror) + spell.terror = TRUE + M.AddSpell(spell) + +/obj/item/organ/internal/appendix/xenobiology/toxin_stinger/remove(mob/living/carbon/M, special = 0) + . = ..() + M.RemoveSpell(/datum/spell/organ_sting) + +/datum/spell/organ_sting + name = "Toxic Sting" + desc = "Silently sting an unsuspecting victim with a mild cocktail of poisons" + action_icon_state = "sting_blind" + action_background_icon_state = "bg_changeling" + invocation_type = "none" + clothes_req = FALSE + stat_allowed = CONSCIOUS + base_cooldown = 5 MINUTES + antimagic_flags = NONE + var/quality = ORGAN_NORMAL + var/cooldown = 0 + var/terror = FALSE + + selection_activated_message = "We unfold our stinger from our body, ready to sting someone." + selection_deactivated_message = "We retract our stinger for now." + +/datum/spell/organ_sting/create_new_targeting(list/targets, mob/living/user) + . = ..() + var/datum/spell_targeting/clicked_atom/external/C = new() + C.range = 20 + return C + +/datum/spell/organ_sting/cast(list/targets, mob/user) + . = ..() + var/atom/target = targets[1] //There is only ever one target + var/dist = get_dist(user.loc, target.loc) + if(dist > 2) // Too far, don't bother pathfinding + to_chat(user, "Our target is too far for our sting!") + revert_cast() + return FALSE + if(!length(get_path_to(user, target, max_distance = 2, simulated_only = FALSE, skip_first = FALSE))) + to_chat(user, "Our sting is blocked from reaching our target!") + revert_cast() + return + if(ismachineperson(target)) + to_chat(user, "This won't work on synthetics.") + revert_cast() + return FALSE + if(ishuman(target)) + var/mob/living/carbon/human/H = target + to_chat(target, "You feel a sharp prick in your side!") + if(quality == ORGAN_PRISTINE) + if(terror) + H.reagents.add_reagent("terror_black_toxin", 10) + else + H.reagents.add_reagent("spider_venom", 10) + else + if(terror) + H.reagents.add_reagent("spider_venom", 10) + else + H.reagents.add_reagent("toxin", 10) + else + to_chat(user, "We wouldn't get much use out of stinging that.") + revert_cast() + +/obj/item/organ/internal/heart/xenobiology/contortion + name = "contortion fibers" + desc = "A set of bands that wrap around joints and ligaments and muscles alike, pulling the body into unnatural shapes." + analyzer_price = 75 + can_paradox = TRUE + hidden_origin_tech = TECH_BIO + hidden_tech_level = 7 + +/obj/item/organ/internal/heart/xenobiology/contortion/insert(mob/living/carbon/human/M, special = 0, dont_remove_slot = 0) + . = ..() + var/datum/spell/contort/spell = new + if(organ_quality == ORGAN_PRISTINE) + spell.duration_time = 1 MINUTES + spell.quality = organ_quality + M.AddSpell(spell) + +/obj/item/organ/internal/heart/xenobiology/contortion/remove(mob/living/carbon/human/M, special = 0) + . = ..() + M.RemoveSpell(/datum/spell/contort) + +/datum/spell/contort + name = "Lesser Contortion" + desc = "Bend your bones an ligaments in unnatural ways to slip under things you would normally never be able to." + base_cooldown = 5 MINUTES + clothes_req = FALSE + stat_allowed = CONSCIOUS + invocation_type = null + action_icon_state = "contort_body" + action_background_icon_state = "bg_changeling" + sound = 'sound/effects/blood1.ogg' + sound = null + antimagic_flags = NONE + var/duration_time = 30 SECONDS + active = FALSE + var/quality + +/datum/spell/contort/create_new_targeting() + return new /datum/spell_targeting/self + +/datum/spell/contort/cast(list/targets, mob/living/carbon/human/user) + . = ..() + if(HAS_TRAIT_FROM(user, TRAIT_CONTORTED_BODY, ORGAN_TRAIT)) + deactivate(user) + return + ADD_TRAIT(user, TRAIT_CONTORTED_BODY, ORGAN_TRAIT) + RegisterSignal(user, COMSIG_MOB_DEATH, PROC_REF(deactivate)) + to_chat(user, "We contort our form to allow us to fit in and under things we normally wouldn't be able to.") + if(IS_HORIZONTAL(user)) + user.layer = TURF_LAYER + 0.2 + active = TRUE + if(quality == ORGAN_DAMAGED) + if(prob(10)) + var/obj/item/organ/external/limb_to_break = pick(user.bodyparts) + limb_to_break.fracture() + to_chat(user, "We tense ourself incorrectly, something inside our [limb_to_break] snaps!") + if(quality == ORGAN_PRISTINE) + duration_time = 1 MINUTES + addtimer(CALLBACK(src, PROC_REF(deactivate), user), duration_time) + revert_cast() + +/datum/spell/contort/proc/deactivate(mob/living/user) + if(!active) // in case we end this early + return + active = FALSE + REMOVE_TRAIT(user, TRAIT_CONTORTED_BODY, ORGAN_TRAIT) + UnregisterSignal(user, COMSIG_MOB_DEATH) + if(IS_HORIZONTAL(user)) + user.layer = initial(user.layer) + to_chat(user, "Our body stiffens and returns to form. That was exhausting!") + +/obj/item/organ/internal/heart/xenobiology/bloody_sack + name = "bloody sack" + desc = "A large sack spilling with blood. Despite the fact it oozes with blood, you can feel its hunger for more." + analyzer_price = 75 + can_paradox = TRUE + hidden_origin_tech = TECH_BLUESPACE + hidden_tech_level = 7 + +/obj/item/organ/internal/heart/xenobiology/bloody_sack/insert(mob/living/carbon/M, special = 0, dont_remove_slot = 0) + . = ..() + var/datum/spell/absorb_blood/spell = new + spell.quality = organ_quality + M.AddSpell(spell) + +/obj/item/organ/internal/heart/xenobiology/bloody_sack/remove(mob/living/carbon/M, special = 0) + . = ..() + M.RemoveSpell(/datum/spell/absorb_blood) + +/datum/spell/absorb_blood + name = "Call to Blood" + desc = "Draw upon dark energies to absorb nearby blood, healing you in the process." + clothes_req = FALSE + stat_allowed = CONSCIOUS + invocation_type = "none" + action_icon = 'icons/mob/actions/actions_cult.dmi' + action_icon_state = "blood_charge" + sound = null + active = FALSE + base_cooldown = 30 SECONDS + antimagic_flags = NONE + var/quality + +/datum/spell/absorb_blood/create_new_targeting() + return new /datum/spell_targeting/self + +/datum/spell/absorb_blood/cast(list/targets, mob/living/user) + . = ..() + var/temp = 0 + var/turf/T = get_turf(targets[1]) + if(!T) + return + if(quality == ORGAN_DAMAGED) + user.blood_volume = max(user.blood_volume - 50, 0) // Slurp slurp slurp + to_chat(user, "You can feel your stolen organ draining you of your blood!") + for(var/obj/effect/decal/cleanable/blood/B in range(T, 3)) + if(B.blood_state == BLOOD_STATE_HUMAN && (B.can_bloodcrawl_in())) + temp += 10 + else + temp += max((B.bloodiness ** 2) / 800, 1) + new /obj/effect/temp_visual/cult/turf/open/floor(get_turf(B)) + qdel(B) + for(var/obj/effect/decal/cleanable/trail_holder/TH in range(T, 2)) + new /obj/effect/temp_visual/cult/turf/open/floor(get_turf(TH)) + qdel(TH) + if(temp) + user.Beam(T, icon_state = "drainbeam", time = 15) + new /obj/effect/temp_visual/cult/sparks(get_turf(user)) + playsound(T, 'sound/misc/enter_blood.ogg', 50, extrarange = SOUND_RANGE_SET(7)) + if(quality == ORGAN_PRISTINE) + temp = clamp(temp, 1, 40) + else + temp = clamp(temp, 1, 25) + temp = round(temp) + user.adjustBruteLoss(-temp) + else + user.playsound_local(T, 'sound/effects/heartbeat.ogg', 50, FALSE) + + +/obj/item/organ/internal/liver/xenobiology/hungry + name = "hungry organ" + desc = "This organ seems to actively attempt to dissolve and absorb anything it touches." + analyzer_price = 40 + hidden_origin_tech = TECH_BIO + hidden_tech_level = 7 + +// checks with those with the eat gene from adding/removing matter eater +/obj/item/organ/internal/liver/xenobiology/hungry/insert(mob/living/carbon/human/M, special = 0, dont_remove_slot = 0) + . = ..() + if(is_type_in_list(/datum/mutation/grant_spell/mattereater, M.active_mutations)) + return + M.AddSpell(new /datum/spell/eat) + +/obj/item/organ/internal/liver/xenobiology/hungry/remove(mob/living/carbon/M, special = 0) + . = ..() + if(is_type_in_list(/datum/mutation/grant_spell/mattereater, M.active_mutations)) + return + M.RemoveSpell(/datum/spell/eat) + +/obj/item/organ/internal/liver/xenobiology/hungry/on_life() + . = ..() + switch(organ_quality) + if(ORGAN_DAMAGED) + owner.adjust_nutrition(-2) + if(ORGAN_NORMAL) + owner.adjust_nutrition(-1) + if(ORGAN_PRISTINE) + owner.adjust_nutrition(-0.3) + +/obj/item/organ/internal/appendix/xenobiology/tendril + name = "writhing tendrils" + desc = "This organ is constantly squirming and writhing around. Yuck." + analyzer_price = 20 + hidden_origin_tech = TECH_BIO + hidden_tech_level = 5 + +/obj/item/organ/internal/appendix/xenobiology/tendril/insert(mob/living/carbon/human/M, special = 0, dont_remove_slot = 0) + . = ..() + var/datum/spell/tendril_grab/spell = new + spell.quality = organ_quality + if(organ_quality == ORGAN_DAMAGED) + spell.base_cooldown = 45 SECONDS + M.AddSpell(spell) + +/obj/item/organ/internal/appendix/xenobiology/tendril/remove(mob/living/carbon/M, special = 0) + . = ..() + M.RemoveSpell(/datum/spell/tendril_grab) + +/datum/spell/tendril_grab + name = "Prehensile Tendril" + desc = "Dig a tendril though the floor to grab at an item, throwing it towards you." + base_cooldown = 30 SECONDS + clothes_req = FALSE + stat_allowed = CONSCIOUS + invocation_type = "none" + action_icon = 'icons/mob/lavaland/lavaland_monsters.dmi' + action_icon_state = "Goliath_tentacle_stationary" + action_background_icon_state = "bg_default" + sound = null + active = FALSE + antimagic_flags = NONE + var/quality + +/datum/spell/tendril_grab/create_new_targeting(list/targets, mob/living/user) + . = ..() + var/datum/spell_targeting/clicked_atom/external/C = new() + C.range = 20 + return C + +/datum/spell/tendril_grab/cast(list/targets, mob/user) + . = ..() + var/atom/target = targets[1] //There is only ever one target + var/turf/target_loc + if(!isturf(target)) + target_loc = target.loc + else + target_loc = target + new /obj/effect/temp_visual/goliath_flick(target_loc, target, quality, user) + +/obj/effect/temp_visual/goliath_flick + name = "Writhing Tendril" + icon = 'icons/mob/lavaland/lavaland_monsters.dmi' + icon_state = "Goliath_tentacle_spawn" + layer = BELOW_MOB_LAYER + /// how long will it take for the tendril to enter "retract" phase + var/time_to_return = 0.7 SECONDS + +/obj/effect/temp_visual/goliath_flick/Initialize(mapload, atom/target, organ_quality, mob/user) + . = ..() + timerid = addtimer(CALLBACK(src, PROC_REF(retract), target, organ_quality, user), time_to_return) + +/obj/effect/temp_visual/goliath_flick/proc/retract(atom/target, organ_quality, mob/user) + icon_state = "Goliath_tentacle_retract" + user.visible_message("[user] digs a massive tendril into the ground!") + timerid = QDEL_IN(src, 7) + if(target.loc == src.loc) + if(isliving(target)) + var/mob/living/M = target + playsound(M, 'sound/weapons/slap.ogg', 50, TRUE, -1, falloff_exponent = 4) // let it travel a little further + if(organ_quality == ORGAN_PRISTINE) + M.adjustBruteLoss(10) + else if(ismovable(target)) + var/atom/movable/T = target + if(!T.anchored) + var/dist = get_dist(user.loc, target.loc) + T.throw_at(user, dist - 1, 1, user, spin = TRUE) + else + T.visible_message("[T] refuses to budge!") + +/obj/item/organ/internal/eyes/cybernetic/xenobiology/glowing + name = "glowing core" + desc = "This organ glows with a strange energy from its depths. Is it even appropiate to call this an organ?" + analyzer_price = 40 + hidden_origin_tech = TECH_BLUESPACE + hidden_tech_level = 6 + status = ORGAN_ROBOT + +/obj/item/organ/internal/eyes/cybernetic/xenobiology/glowing/Initialize(mapload) + . = ..() + icon_state = pick("hiero1", "hiero2") + +/obj/item/organ/internal/eyes/cybernetic/xenobiology/glowing/insert(mob/living/carbon/M, special = 0, dont_remove_slot = 0) + . = ..() + var/datum/spell/turf_teleport/organ_teleport/spell = new + spell.quality = organ_quality + spell.inner_tele_radius = 3 + spell.outer_tele_radius = 8 + if(organ_quality == ORGAN_PRISTINE) + spell.base_cooldown = 60 SECONDS + spell.inner_tele_radius = 5 + spell.outer_tele_radius = 15 + M.AddSpell(spell) + +/obj/item/organ/internal/eyes/cybernetic/xenobiology/glowing/remove(mob/living/carbon/M, special = 0) + . = ..() + M.RemoveSpell(/datum/spell/turf_teleport/organ_teleport) + +/datum/spell/turf_teleport/organ_teleport + name = "Unstable Blink" + desc = "Touch someone to destabilize their location in bluespace for a moment." + base_cooldown = 2 MINUTES + clothes_req = FALSE + stat_allowed = CONSCIOUS + invocation_type = "none" + action_icon_state = "spell_teleport" + sound = null + active = FALSE + sound1 = 'sound/magic/blink.ogg' + sound2 = 'sound/magic/blink.ogg' + antimagic_flags = NONE + var/quality + +/datum/spell/turf_teleport/organ_teleport/create_new_targeting() + var/datum/spell_targeting/clicked_atom/external/C = new() + C.range = 20 + return C + +/datum/spell/turf_teleport/organ_teleport/cast(list/targets, mob/living/user) + var/atom/target = targets[1] + if(!isliving(target)) + to_chat(user, "We can only teleport living things!") + revert_cast() + return + if(target == user) + to_chat(user, "We are unable to teleport ourself!") + revert_cast() + return + if(get_dist(target.loc, user.loc) > 2) + to_chat(user, "They're too far away!") + revert_cast() + if(!length(get_path_to(user, target, max_distance = 2, simulated_only = FALSE, skip_first = FALSE))) + to_chat(user, "Something is blocking the way!") + revert_cast() + return + if(quality == ORGAN_BROKEN) + if(prob(25)) + targets += user + user.adjustBruteLoss(10) + user.adjustFireLoss(10) + to_chat(user, "You get dragged along into bluespace, your flesh searing from the unstable energies!") + else + to_chat(user, "Drawing upon unstable energy singes your flesh!") + user.adjustFireLoss(8) + user.mob_light(LIGHT_COLOR_PURPLE, 3, _duration = 3) + new /obj/effect/temp_visual/hierophant/telegraph/teleport(get_turf(target)) + return ..() + +/obj/item/organ/internal/kidneys/xenobiology/shivering + name = "shivering organ" + desc = "It constantly shivers, seeking to warm itself from its environment." + analyzer_price = 30 + +/obj/item/organ/internal/kidneys/xenobiology/shivering/on_life() + . = ..() + if(owner.get_temperature() < owner.dna.species.cold_level_1 + 40) + switch(organ_quality) + if(ORGAN_DAMAGED) + owner.bodytemperature += 15 + if(ORGAN_NORMAL) + owner.bodytemperature += 20 + if(ORGAN_PRISTINE) + owner.bodytemperature += 30 + +/obj/item/organ/internal/kidneys/xenobiology/sweating + name = "sweaty organ" + desc = "It constantly sweats, seeking to cool itself off from its environment." + analyzer_price = 30 + hidden_origin_tech = TECH_TOXINS + hidden_tech_level = 6 + +/obj/item/organ/internal/kidneys/xenobiology/shivering/on_life() + . = ..() + if(owner.get_temperature() > owner.dna?.species.heat_level_1 - 40) + switch(organ_quality) + if(ORGAN_DAMAGED) + owner.bodytemperature -= 15 + if(ORGAN_NORMAL) + owner.bodytemperature -= 20 + if(ORGAN_PRISTINE) + owner.bodytemperature -= 30 + +/obj/item/organ/internal/liver/xenobiology/soupy + name = "soupy organ" + desc = "This organ seems to barely keep its own form together. It also reeks of tomato sauce." + analyzer_price = 15 + hidden_origin_tech = TECH_BIO + hidden_tech_level = 6 + var/original_name + var/original_own_species_blood + var/original_exotic_blood + var/original_blood_color + var/original_blood_type + +/obj/item/organ/internal/liver/xenobiology/soupy/insert(mob/living/carbon/human/M, special = 0, dont_remove_slot = 0) + . = ..() + original_name = M.dna.species.name + original_exotic_blood = M.dna.species.exotic_blood + original_blood_color = M.dna.species.blood_color + original_blood_type = M.dna.blood_type + + M.dna.species.name = "Tomato Hybrid" + M.dna.species.exotic_blood = "tomatojuice" + M.dna.species.blood_color = "#e25821" + M.dna.blood_type = "Tomato" + to_chat(owner, "You feel unnaturally soupy...") + +/obj/item/organ/internal/liver/xenobiology/soupy/remove(mob/living/carbon/human/M, special = 0) + . = ..() + M.dna.species.name = original_name + M.dna.species.exotic_blood = original_exotic_blood + M.dna.species.blood_color = original_blood_color + M.dna.blood_type = original_blood_type + to_chat(owner, "You no longer constantly taste ketchup.") + +/obj/item/organ/internal/appendix/xenobiology/toxin_stinger/terror + name = "hidden terror stinger" + desc = "This organ holds a deceptive stinger tucked inside of itself, dripping with potent venom." + analyzer_price = 60 + terror = TRUE + hidden_origin_tech = TECH_COMBAT + hidden_tech_level = 7 + +/obj/item/organ/internal/lungs/xenobiology/mirror + name = "silvered organ" + desc = "This organ is dazzlingly reflective." + analyzer_price = 30 + hidden_origin_tech = TECH_MATERIAL + hidden_tech_level = 7 + +/obj/item/organ/internal/lungs/xenobiology/mirror/insert(mob/living/carbon/human/M, special = 0, dont_remove_slot = 0) + . = ..() + var/datum/spell/create_mirror/spell = new + spell.quality = organ_quality + M.AddSpell(spell) + +/obj/item/organ/internal/lungs/xenobiology/mirror/remove(mob/living/carbon/M, special = 0) + . = ..() + M.RemoveSpell(/datum/spell/create_mirror) + +/datum/spell/create_mirror + name = "create mirror" + desc = "Regurgitate a slick, mirrored surface where you are standing. Disgusting." + clothes_req = FALSE + stat_allowed = CONSCIOUS + invocation_type = "none" + action_icon = 'icons/mob/actions/actions_elites.dmi' + action_icon_state = "herald_mirror" + action_background_icon_state = "bg_default" + sound = 'sound/effects/splat.ogg' + active = FALSE + base_cooldown = 3 MINUTES + antimagic_flags = NONE + var/quality + +/datum/spell/create_mirror/create_new_targeting() + . = ..() + return new /datum/spell_targeting/self + +/datum/spell/create_mirror/cast(list/targets, mob/user) + . = ..() + var/turf/T = get_turf(user) + var/obj/M = new /obj/structure/mirror(T) + M.anchored = FALSE + if(quality == ORGAN_PRISTINE) + M.icon = 'icons/mob/lavaland/lavaland_elites.dmi' + M.icon_state = "herald_mirror" + +/obj/item/organ/internal/heart/xenobiology/squirming + name = "squirming organ" + desc = "This organ refuses to sit still, constantly moving about however it can." + analyzer_price = 75 + can_paradox = TRUE + hidden_origin_tech = TECH_POWER + hidden_tech_level = 7 + +/obj/item/organ/internal/heart/xenobiology/squirming/Initialize(mapload) + . = ..() + icon_state = pick("legion1", "legion2") + +/obj/item/organ/internal/heart/xenobiology/squirming/on_life() + . = ..() + if(owner.getStaminaLoss() >= 5) + switch(organ_quality) + if(ORGAN_DAMAGED) + owner.adjustStaminaLoss(-5) + if(ORGAN_NORMAL) + owner.adjustStaminaLoss(-10) + if(ORGAN_PRISTINE) // slightly better/different than emagged cybernetic + owner.adjustStaminaLoss(-25) + if(prob(20)) + owner.AdjustStunned(-2 SECONDS) + owner.AdjustKnockDown(-2 SECONDS) + +/obj/item/organ/internal/appendix/xenobiology/electro_strands + name = "electromagnetic strands" + desc = "A large number of electrically sensitive strands all bundled up together. It has lost most of its potential." + analyzer_price = 30 + hidden_origin_tech = TECH_MAGNETS + hidden_tech_level = 7 + +/obj/item/organ/internal/appendix/xenobiology/electro_strands/insert(mob/living/carbon/human/M, special = 0, dont_remove_slot = 0) + . = ..() + var/datum/spell/aoe/flicker_lights/spell = new + spell.from_organ = TRUE + M.AddSpell(spell) + +/obj/item/organ/internal/appendix/xenobiology/electro_strands/remove(mob/living/carbon/M, special = 0) + . = ..() + M.RemoveSpell(/datum/spell/aoe/flicker_lights) + + +/obj/item/organ/internal/liver/xenobiology/sharp + name = "sharp organ" + desc = "This organ sprouts several sharp points out of itself, which you can't imagine would feel good to get implanted." + analyzer_price = 30 + var/original_unarmed + var/original_sharpened + hidden_origin_tech = TECH_COMBAT + hidden_tech_level = 7 + +/obj/item/organ/internal/liver/xenobiology/sharp/insert(mob/living/carbon/human/M, special = 0, dont_remove_slot = 0) + . = ..() + original_unarmed = owner.dna.species.unarmed_type + if(original_unarmed != /datum/unarmed_attack/claws) + var/datum/unarmed_attack/claws/claws = new + owner.adjustBruteLoss(20) // OUCH!!! Son of a- + owner.emote("scream") + if(organ_quality == ORGAN_PRISTINE) + claws.has_been_sharpened = TRUE + owner.dna.species.unarmed = claws + else + var/datum/unarmed_attack/unarmed = owner.get_unarmed_attack() + original_sharpened = unarmed.sharp + unarmed.sharp = TRUE + +/obj/item/organ/internal/liver/xenobiology/sharp/remove(mob/living/carbon/human/M, special = 0) + . = ..() + if(original_unarmed != /datum/unarmed_attack/claws) + M.dna.species.unarmed = original_unarmed + else + var/datum/unarmed_attack/unarmed = owner.get_unarmed_attack() + unarmed.sharp = original_sharpened + +/obj/item/organ/internal/appendix/xenobiology/noisemaker + name = "mimicry organ" + desc = "This organ continues to make odd sounds, copying things that it has heard." + analyzer_price = 15 + hidden_origin_tech = TECH_ENGINEERING + hidden_tech_level = 5 + +/obj/item/organ/internal/appendix/xenobiology/noisemaker/insert(mob/living/carbon/human/M, special = 0, dont_remove_slot = 0) + . = ..() + var/datum/action/innate/migo_noise/noisy = new() + noisy.Grant(M) + +/obj/item/organ/internal/appendix/xenobiology/noisemaker/remove(mob/living/carbon/human/M, special = 0) + . = ..() + for(var/datum/action/innate/migo_noise/noisy in M.actions) + noisy.Remove(M) + +/obj/item/organ/internal/appendix/xenobiology/noisemaker/on_life() + . = ..() + if(organ_quality == ORGAN_BROKEN && prob(1)) + owner.custom_emote("honks") // you just cant help it + playsound(owner.loc, 'sound/items/bikehorn.ogg', 50, FALSE) + +/datum/action/innate/migo_noise + name = "Make some noise!" + desc = "Your organ YEARNS to make noises of all kinds. Let it loose for a moment!" + button_overlay_icon = 'icons/mob/animal.dmi' + button_overlay_icon_state = "mi-go" + var/cooldown = 0 + + COOLDOWN_DECLARE(migo_cooldown) + +/datum/action/innate/migo_noise/Activate() + var/mob/living/carbon/human/user = owner + if(!COOLDOWN_FINISHED(src, migo_cooldown)) + to_chat(user, "You can't get your noisy organ to speak again so soon!") + return + var/chosen_sound = pick(GLOB.migo_sounds) + playsound(owner.loc, chosen_sound, 50, TRUE) + COOLDOWN_START(src, migo_cooldown, 10 SECONDS) + +/obj/item/organ/internal/eyes/xenobiology/receptors + name = "photosensitive receptors" + desc = "A set of organs receptive to light in the spectrum of -- hey, wait a second. Aren't these just eyes?" + analyzer_price = 40 + hidden_origin_tech = TECH_BIO + hidden_tech_level = 7 + +/obj/item/organ/internal/eyes/xenobiology/receptors/insert(mob/living/carbon/M, special, dont_remove_slot) + switch(organ_quality) + if(ORGAN_BROKEN) + see_in_dark = 4 + if(ORGAN_NORMAL) + see_in_dark = 8 + if(ORGAN_PRISTINE) + see_in_dark = 8 + vision_flags = SEE_OBJS | SEE_TURFS // slightly better mesons + return ..() + +/obj/item/organ/internal/heart/xenobiology/paradox + name = "paradoxical organ" + desc = "The organ is constantly shifting and morphing; each time you look away it's something new." + analyzer_price = 40 + /// holds the list what all the valid hearts that the paradox heart can swap out with + var/list/acceptable_hearts = list() + +/obj/item/organ/internal/heart/xenobiology/paradox/Initialize(mapload) + . = ..() + icon_state = pick("hiero1", "hiero2") + + +/obj/item/organ/internal/heart/xenobiology/paradox/insert(mob/living/carbon/human/M, special, dont_remove_slot) + for(var/obj/item/organ/internal/heart/xenobiology/temp_organ as anything in subtypesof(/obj/item/organ/internal/heart/xenobiology)) + if(temp_organ::can_paradox) + acceptable_hearts += temp_organ + var/obj/item/organ/internal/heart/new_organ = pick(acceptable_hearts) + new_organ = new new_organ + new_organ.organ_quality = organ_quality + new_organ.insert(M) + qdel(src) + +/obj/item/organ/internal/heart/xenobiology/bananium + name = "bananium laced heart" + desc = "Squeak squeak squeak sqonk honk honk snorf" + analyzer_price = 5 + can_paradox = TRUE + hidden_origin_tech = TECH_SYNDICATE + hidden_tech_level = 3 + +/obj/item/organ/internal/heart/xenobiology/bananium/insert(mob/living/carbon/human/M, special, dont_remove_slot) + . = ..() + if(M.mind) + if(M.mind.assigned_role == "Clown") + M.AddComponent(/datum/component/squeak, null, null, null, null, null, TRUE, falloff_exponent = 20) + to_chat(M, "You feel great!") + return + else if(M.mind.assigned_role == "Mime") + M.AddComponent(/datum/component/squeak) + M.dna.SetSEState(GLOB.clumsyblock, TRUE, TRUE) + M.dna.SetSEState(GLOB.comicblock, TRUE, TRUE) + to_chat(M, "UH OH") + else + M.emote("scream") + M.AdjustJitter(20 SECONDS) + M.dna.SetSEState(GLOB.clumsyblock, TRUE, TRUE) + M.dna.SetSEState(GLOB.comicblock, TRUE, TRUE) + addtimer(CALLBACK(M, TYPE_PROC_REF(/mob/living/carbon/human/, bananatouched_harmless)), 3 SECONDS) + else + M.emote("scream") + M.AdjustJitter(20 SECONDS) + M.dna.SetSEState(GLOB.clumsyblock, TRUE, TRUE) + M.dna.SetSEState(GLOB.comicblock, TRUE, TRUE) + addtimer(CALLBACK(M, TYPE_PROC_REF(/mob/living/carbon/human/, bananatouched_harmless)), 3 SECONDS) + +/obj/item/organ/internal/heart/xenobiology/bananium/on_life() + . = ..() + if(!owner.mind) + return + if(owner.mind.assigned_role == "Clown" && organ_quality == ORGAN_PRISTINE) + owner.adjustBruteLoss(-3) + owner.adjustFireLoss(-3) + else if(owner.mind.assigned_role == "Mime") + if(prob(3)) + owner.emote("scream") + owner.vomit(20) + owner.EyeBlurry(10 SECONDS) + owner.AdjustJitter(20 SECONDS, 0, 100 SECONDS) + owner.AdjustDizzy(20 SECONDS, 0, 100 SECONDS) + owner.Druggy(30 SECONDS) + if(owner.dna.species.tox_mod <= 0) // If they can't take tox damage, make them take burn damage + owner.adjustFireLoss(1, robotic = TRUE) + else + owner.adjustToxLoss(1) + if(prob(2)) + var/list/clown_message = list( + "You feel light-headed.", + "You can't see straight.", + "Your heart tickles!", + "Your blood feels like singing.", + "Your funny bone aches.", + "What was that?!", + "You can hear bike horns in the distance.", + "You feel like SHOUTING!", + "Sinister laughter echoes in your ears.", + "Your legs feel like jelly.", + "You feel like telling a pun.", + ) + to_chat(owner, "[pick(clown_message)]") + + +/obj/item/organ/internal/heart/xenobiology/bananium/remove(mob/living/carbon/human/M, special) + . = ..() + if(M.mind) + if(M.mind.assigned_role == "Clown") + M.DeleteComponent(/datum/component/squeak) + to_chat(M, "Awh, the funs over...") + else + M.DeleteComponent(/datum/component/squeak) + to_chat(M, "Blissful silence...") + M.dna.SetSEState(GLOB.clumsyblock, FALSE, FALSE) + M.dna.SetSEState(GLOB.comicblock, FALSE, FALSE) + +/obj/item/organ/internal/heart/xenobiology/cursed_bananium + name = "cursed bananium heart" + desc = "This organ is wreathed in foul energy from cursed bananium. There may be great power here, but only the truest of souls could bring it forward." + analyzer_price = 50 + unremovable = TRUE + layer = ABOVE_MOB_LAYER // front and center baybee + hidden_origin_tech = TECH_SYNDICATE + hidden_tech_level = 3 + warning = TRUE + var/list/clown_noises = list( + "HONK!", + "SQUEAK!", + "TEEHEE!", + "HONK HONK HONK HONK HONK!!!", + "THEY MUST ALL BE HONKED!", + ) + +/obj/item/organ/internal/heart/xenobiology/cursed_bananium/insert(mob/living/carbon/human/M, special, dont_remove_slot) + if(M.mind && M.mind.assigned_role == "Clown") + addtimer(CALLBACK(src, PROC_REF(glorious_death), M), 5 MINUTES) + to_chat(owner, "YOU FEEL THE PURE, UNFILTERED JOY OF THE HONKMOTHER!!!") + playsound(M, 'sound/magic/magic_block_holy.ogg', 60) + ADD_TRAIT(M, TRAIT_GOTTAGOFAST, ORGAN_TRAIT) + M.color = rgb(255, 251, 0) + M.set_light(2, 5, rgb(255, 251, 0)) + M.AddComponent(/datum/component/squeak) + else + M.emote("scream") + M.SetJitter(20 SECONDS) + addtimer(CALLBACK(M, TYPE_PROC_REF(/mob/living/carbon/human/, makeCluwne)), 5 SECONDS) + return ..() + +/obj/item/organ/internal/heart/xenobiology/cursed_bananium/proc/glorious_death(mob/living/carbon/human/M) + playsound(M.loc, 'sound/effects/pray.ogg', 60, extrarange = 10, falloff_exponent = 2, ignore_walls = TRUE, pressure_affected = FALSE) + M.color = rgb(255, 252, 82) + M.set_light(10, 25, rgb(255, 252, 82)) + M.add_filter("sacrifice_glow", 2, list("type" = "outline", "color" = "#fbff00d2", "size" = 2)) + if(M.buckled) + unbuckle_mob(M, force = TRUE) + M.move_resist = INFINITY + M.Stun(10 SECONDS) + M.dir = 2 + animate(M, pixel_y = 64, time = 2 SECONDS) + addtimer(CALLBACK(src, PROC_REF(finalize_death), M), 2 SECONDS) + to_chat(owner, "THE JOY! THE POWER! YOU CAN FEEL THE HONKMOTHERS SMILE! YOU CAN FEEL- oh no") + +/obj/item/organ/internal/heart/xenobiology/cursed_bananium/proc/finalize_death(mob/living/carbon/human/M) + explosion(get_turf(M),0,2,4,4, smoke = TRUE, cause = owner) + M.gib() + +/obj/item/organ/internal/heart/xenobiology/cursed_bananium/on_life() + . = ..() + if(owner.mind) // I AM A GOD!!!! + if(owner.mind.assigned_role == "Clown") + owner.adjustBruteLoss(-15) + owner.adjustFireLoss(-15) + owner.adjustOxyLoss(-15) + owner.adjustToxLoss(-15) + owner.adjustStaminaLoss(-30) + owner.SetJitter(500 SECONDS) //High numbers for violent convulsions + if(prob(10)) + to_chat(owner, "[pick(clown_noises)]") + +/obj/item/organ/internal/cell/xenobiology/supercharged + name = "supercharged core" + desc = "This specialized core thrumms with potental and energy. It desperately seeks release." + analyzer_price = 80 + hidden_origin_tech = TECH_TOXINS + hidden_tech_level = 7 + + +/obj/item/organ/internal/cell/xenobiology/supercharged/Initialize(mapload) + . = ..() + icon_state = pick("vetus1", "vetus2") + +/obj/item/organ/internal/cell/xenobiology/supercharged/insert(mob/living/carbon/human/M, special = 0, dont_remove_slot = 0) + . = ..() + var/datum/spell/charge_up/explode/spell = new + spell.quality = organ_quality + M.AddSpell(spell) + +/obj/item/organ/internal/cell/xenobiology/supercharged/remove(mob/living/carbon/human/M, special = 0) + . = ..() + M.RemoveSpell(/datum/spell/charge_up/explode) + +/datum/spell/charge_up/explode + name = "Detonate" + desc = "Build up energy in your core, allowing for a fiery detonation!" + clothes_req = FALSE + stat_allowed = CONSCIOUS + invocation_type = "none" + action_icon_state = "genetic_incendiary" + action_background_icon_state = "bg_flayer" + max_charge_time = 10 SECONDS + base_cooldown = 10 MINUTES + charge_sound = new /sound('sound/magic/lightning_chargeup.ogg', channel = 7) + stop_charging_text = "You cool your core before it goes critical." + start_charging_text = "You begin to overload your core!" + stop_charging_fail_text = "You try to stop your cores charge but it's too late!" + antimagic_flags = NONE + var/quality + +/datum/spell/charge_up/explode/New() + ..() + charge_up_overlay = image(icon = 'icons/effects/effects.dmi', icon_state = "purplesparkles", layer = EFFECTS_LAYER) + +/datum/spell/charge_up/explode/create_new_targeting() + return new /datum/spell_targeting/self + +/datum/spell/charge_up/explode/Click() + if(cast_check(TRUE, FALSE, usr)) + if(!start_time) + INVOKE_ASYNC(src, PROC_REF(StartChargeup), usr) + else + if(!try_stop_buildup(usr)) + return // Don't remove the click intercept + +/datum/spell/charge_up/explode/Discharge(mob/user) + . = ..() + if(quality == ORGAN_DAMAGED) + explosion(user.loc, 1, 2, 3, 3, cause = user) // gaurenteed gib, just about + else + explosion(user.loc, 0, 2, 3, 3, cause = user) + if(quality == ORGAN_PRISTINE) + user.status_flags |= GODMODE + addtimer(CALLBACK(src, PROC_REF(cleanup), user), 0.3 SECONDS) // juuust long enough to survive the blast itself. + +/datum/spell/charge_up/explode/proc/cleanup(mob/living/carbon/human/user) + user.status_flags &= ~GODMODE + if(user.nutrition > 140) // overloading your powersource will do that + user.nutrition = 140 + else + user.nutrition = max(user.nutrition - 75, 5) + for(var/obj/item/organ/internal/cell/C in user.internal_organs) + C.damage += 20 // ouch. Maybe dont blow up + +/obj/item/organ/internal/heart/xenobiology/megacarp + name = "rancid clump" + desc = "It reeks of fish and... soy sauce?" + analyzer_price = 40 + can_paradox = TRUE + +/obj/item/organ/internal/heart/xenobiology/megacarp/insert(mob/living/carbon/human/M, special = 0, dont_remove_slot = 0) + if(!M.mind) + M.visible_message("[src] doesn't look like it's going to fit right now! It...refuses?") + return + var/datum/spell/shapeshift/megacarp/spell = new(organ_quality) + M.mind.AddSpell(spell) + return ..() + +/obj/item/organ/internal/heart/xenobiology/megacarp/remove(mob/living/carbon/human/M, special) + if(!M.mind) + return + M.mind.RemoveSpell(/datum/spell/shapeshift/megacarp) + return ..() + +/datum/spell/shapeshift/megacarp + name = "Fish Form" + desc = "Take on the shape of a massive fish after a short delay." + invocation = "*scream" + +/datum/spell/shapeshift/megacarp/New(quality) + . = ..() + if(quality == ORGAN_PRISTINE) + shapeshift_type = /mob/living/simple_animal/hostile/carp/megacarp/xeno_organ + current_shapes = list(/mob/living/simple_animal/hostile/carp/megacarp/xeno_organ) + current_casters = list() + possible_shapes = list(/mob/living/simple_animal/hostile/carp/megacarp/xeno_organ) + else + shapeshift_type = /mob/living/simple_animal/hostile/carp/xeno_organ + current_shapes = list(/mob/living/simple_animal/hostile/carp/xeno_organ) + current_casters = list() + possible_shapes = list(/mob/living/simple_animal/hostile/carp/xeno_organ) + +/datum/spell/shapeshift/megacarp/Shapeshift(mob/living/carbon/human/M) + M.visible_message("[M] screams in agony as scales and fins erupt out of [M.p_their()] flesh!", + "You begin channeling the painful transformation.") + if(!do_after(M, 5 SECONDS, FALSE, M)) + to_chat(M, "You lose concentration of the spell!") + return + return ..() + +/mob/living/simple_animal/hostile/carp/xeno_organ + maxHealth = 100 + health = 100 + gold_core_spawnable = NO_SPAWN + universal_speak = TRUE + universal_understand = TRUE + pass_flags = PASSTABLE + +/mob/living/simple_animal/hostile/carp/megacarp/xeno_organ + maxHealth = 175 + health = 175 + gold_core_spawnable = NO_SPAWN + universal_speak = TRUE + universal_understand = TRUE + pass_flags = PASSTABLE + +/obj/item/organ/internal/appendix/xenobiology/feverish + name = "feverish organ" + desc = "This organ is warm, and looks sickly. Yet by all means, there doesnt appear to be any infections." + analyzer_price = 15 + + +/obj/item/organ/internal/appendix/xenobiology/feverish/on_life() + . = ..() + if(owner.bodytemperature > 600) + return + switch(organ_quality) + if(ORGAN_DAMAGED) + owner.bodytemperature += 15 + if(ORGAN_NORMAL) + owner.bodytemperature += 20 + if(ORGAN_PRISTINE) + owner.bodytemperature += 30 + +/obj/item/organ/internal/appendix/xenobiology/freezing + name = "Freezing Organ" + desc = "This organ is cold to the touch, despite seeming to be very active." + analyzer_price = 15 + +/obj/item/organ/internal/appendix/xenobiology/freezing/on_life() + . = ..() + if(owner.bodytemperature < 30) + return + switch(organ_quality) + if(ORGAN_DAMAGED) + owner.bodytemperature -= 15 + if(ORGAN_NORMAL) + owner.bodytemperature -= 20 + if(ORGAN_PRISTINE) + owner.bodytemperature -= 30 + +/obj/item/organ/internal/kidneys/xenobiology/lethargic + name = "lethargic organ" + desc = "This organ barely seems to do anything, only being just active enough to keep itself alive. However, it seems exceptionally hardy." + analyzer_price = 20 + var/datum/armor/organ_resistance_boost + +/obj/item/organ/internal/kidneys/xenobiology/lethargic/Destroy() + qdel(organ_resistance_boost) + return ..() + +/obj/item/organ/internal/kidneys/xenobiology/lethargic/insert(mob/living/carbon/human/M, special = 0, dont_remove_slot = 0) + . = ..() + ADD_TRAIT(M, TRAIT_GOTTAGOSLOW, ORGAN_TRAIT) + if(organ_quality == ORGAN_DAMAGED) + organ_resistance_boost = new /datum/armor(5, 5, 5, 5, 5, 5, 5, 5, 0) + owner.physiology.stamina_mod *= 0.9 + if(organ_quality == ORGAN_NORMAL) + organ_resistance_boost = new /datum/armor(15, 15, 15, 15, 15, 15, 15, 15, 0) + owner.physiology.stamina_mod *= 0.75 + if(organ_quality == ORGAN_PRISTINE) + organ_resistance_boost = new /datum/armor(50, 50, 50, 50, 50, 50, 50, 50, 0) + owner.physiology.stamina_mod *= 0.5 + owner.physiology.armor = owner.physiology.armor.attachArmor(organ_resistance_boost) + +/obj/item/organ/internal/kidneys/xenobiology/lethargic/remove(mob/living/carbon/M, special) + . = ..() + REMOVE_TRAIT(M, TRAIT_GOTTAGOSLOW, ORGAN_TRAIT) + if(organ_quality == ORGAN_DAMAGED) + owner.physiology.stamina_mod /= 0.9 + if(organ_quality == ORGAN_NORMAL) + owner.physiology.stamina_mod /= 0.75 + if(organ_quality == ORGAN_PRISTINE) + owner.physiology.stamina_mod /= 0.5 + owner.physiology.armor = owner.physiology.armor.detachArmor(organ_resistance_boost) + QDEL_NULL(organ_resistance_boost) + +/obj/item/organ/internal/ears/xenobiology/colorful + name = "colorful organ" + desc = "This organ seems to constantly color and mold the other flesh around it. Thankfully, the changes are only aesthetic." + analyzer_price = 20 + + COOLDOWN_DECLARE(hair_change) + + +/obj/item/organ/internal/ears/xenobiology/colorful/on_life() + . = ..() + if(!COOLDOWN_FINISHED(src, hair_change)) + return + COOLDOWN_START(src, hair_change, 1 MINUTES) + scramble(1, owner, 100) + +/obj/item/organ/internal/ears/xenobiology/sinister + name = "sinister organ" + desc = "This organ is brimming with foul aura. Small buds seem to be growing out of it" + analyzer_price = 60 + hidden_origin_tech = TECH_COMBAT + hidden_tech_level = 6 + +/obj/item/organ/internal/ears/xenobiology/sinister/Initialize(mapload) + . = ..() + icon_state = pick("legion1", "legion2") + +/obj/item/organ/internal/ears/xenobiology/sinister/insert(mob/living/carbon/M, special = 0, dont_remove_slot = 0) + . = ..() + var/datum/spell/head_attack/spell = new + spell.quality = organ_quality + if(organ_quality == ORGAN_PRISTINE) + spell.base_cooldown = 3 MINUTES + M.AddSpell(spell) + +/obj/item/organ/internal/ears/xenobiology/sinister/remove(mob/living/carbon/M, special = 0) + . = ..() + if(!M) + return + M.RemoveSpell(/datum/spell/head_attack) + +/datum/spell/head_attack + name = "Aggressive Budding" + desc = "Grow and detach a new head from yourself to send towards your enemies. Cast again to destroy any existing heads." + base_cooldown = 5 MINUTES + clothes_req = FALSE + stat_allowed = CONSCIOUS + invocation_type = "*scream" + action_icon = 'icons/mob/actions/actions_elites.dmi' + action_icon_state = "head_detach" + action_background_icon_state = "bg_default" + sound = 'sound/effects/blood1.ogg' + sound = null + active = FALSE + antimagic_flags = NONE + var/quality + var/mob/living/simple_animal/hostile/asteroid/elite/legionnairehead/xenobiology/newhead + +/datum/spell/head_attack/create_new_targeting(list/targets, mob/living/user) + . = ..() + var/datum/spell_targeting/clicked_atom/external/C = new() + C.range = 20 + return C + +/datum/spell/head_attack/Click(mob/user) + . = ..() + if(newhead) + to_chat(user, "We disperse the summoned head.") + on_head_death() + return + +/datum/spell/head_attack/cast(list/targets, mob/living/carbon/human/user) + . = ..() + if(!isliving(targets[1])) + to_chat(user, "We can only select living targets!") + revert_cast() + return + var/mob/living/target = targets[1] // only ever one target + if(!user.get_organ("head")) + to_chat(user, "We cant use this without a head!") + revert_cast() + return + if(target == user) + to_chat(user, "It would be a bad idea to target ourselves.") + revert_cast() + return + if(target.stat == DEAD) + to_chat(user, "Our new head wont attack corpses.") + revert_cast() + return + if(!newhead) + if(quality == ORGAN_DAMAGED && prob(30)) + user.adjustBruteLoss(15) // ouch! + user.visible_message("[src] produces a new head!", "You produce a new head and send it to your target!") + newhead = new /mob/living/simple_animal/hostile/asteroid/elite/legionnairehead/xenobiology(user.loc, user, target) + RegisterSignal(newhead, COMSIG_MOB_DEATH, PROC_REF(on_head_death)) + revert_cast() + +/datum/spell/head_attack/proc/on_head_death() + SIGNAL_HANDLER // COMSIG_MOB_DEATH + cooldown_handler.start_recharge() + UnregisterSignal(newhead, COMSIG_MOB_DEATH) + QDEL_NULL(newhead) + +/mob/living/simple_animal/hostile/asteroid/elite/legionnairehead/xenobiology + desc = "The floating head of a crew member, sent to destroy their enemies." + maxHealth = 50 + health = 50 + density = FALSE + melee_damage_lower = 5 + melee_damage_upper = 8 + var/mob/living/permanent_target + var/obj/item/organ/external/head/dupe_head + + COOLDOWN_DECLARE(time_to_live) + +/mob/living/simple_animal/hostile/asteroid/elite/legionnairehead/xenobiology/Initialize(mapload, mob/living/carbon/human/user, mob/living/target) + . = ..() + GiveTarget(target) + faction = user.faction.Copy() + icon = null + name = "[user]'s head" + faction = list(user) + permanent_target = target + var/obj/item/user_head = user.get_organ("head") + dupe_head = DuplicateObject(user_head, perfectcopy=1, sameloc=0, newloc=src.contents) + var/matrix/M = matrix() + dupe_head.transform = M + var/image/IM = image(dupe_head.icon, dupe_head.icon_state) + IM.pixel_y -= 6 + IM.overlays = dupe_head.overlays.Copy() + overlays += IM + QDEL_NULL(dupe_head) + COOLDOWN_START(src, time_to_live, 30 SECONDS) + +/mob/living/simple_animal/hostile/asteroid/elite/legionnairehead/xenobiology/Destroy() + permanent_target = null + return ..() + +/mob/living/simple_animal/hostile/asteroid/elite/legionnairehead/xenobiology/Life(seconds, times_fired) + if(target != permanent_target) + src.visible_message("With no valid targets, the head crumbles into a pile of flesh.") + death() + if(COOLDOWN_FINISHED(src, time_to_live)) + src.visible_message("\The [src] loses energy, and it crumbles into a pile of flesh.") + death() + return ..() diff --git a/code/modules/surgery/generic.dm b/code/modules/surgery/generic.dm index 6e56f010317..4719f0b4808 100644 --- a/code/modules/surgery/generic.dm +++ b/code/modules/surgery/generic.dm @@ -12,6 +12,7 @@ allowed_tools = list( TOOL_SCALPEL = 100, /obj/item/kitchen/knife = 90, + TOOL_DISSECTOR = 75, /obj/item/kitchen/knife/shiv = 70, /obj/item/shard = 60, TOOL_WIRECUTTER = 35, @@ -29,34 +30,43 @@ time = 1.6 SECONDS /datum/surgery_step/generic/cut_open/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] starts the incision on [target]'s [affected.name] with [tool].", - "You start the incision on [target]'s [affected.name] with [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) - affected.custom_pain("You feel a horrible pain as if from a sharp knife in your [affected.name]!") + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] starts the incision on [target]'s [affected.name] with [tool].", + "You start the incision on [target]'s [affected.name] with [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + affected.custom_pain("You feel a horrible pain as if from a sharp knife in your [affected.name]!") return ..() /datum/surgery_step/generic/cut_open/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_success_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( "[user] has made an incision on [target]'s [affected.name] with [tool].", "You have made an incision on [target]'s [affected.name] with [tool].", chat_message_type = MESSAGE_TYPE_COMBAT - ) - affected.open = ORGAN_ORGANIC_OPEN - target.resume_bleeding() + ) + affected.open = ORGAN_ORGANIC_OPEN + target.resume_bleeding() return SURGERY_STEP_CONTINUE /datum/surgery_step/generic/cut_open/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user]'s hand slips, slicing open [target]'s [affected.name] in a wrong spot with [tool]!", - "Your hand slips, slicing open [target]'s [affected.name] in a wrong spot with [tool]!", - chat_message_type = MESSAGE_TYPE_COMBAT - ) - affected.receive_damage(10) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_failure_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user]'s hand slips, slicing open [target]'s [affected.name] in a wrong spot with [tool]!", + "Your hand slips, slicing open [target]'s [affected.name] in a wrong spot with [tool]!", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + affected.receive_damage(10) return SURGERY_STEP_RETRY /datum/surgery_step/generic/clamp_bleeders @@ -75,33 +85,42 @@ /datum/surgery_step/generic/clamp_bleeders/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] starts clamping bleeders in [target]'s [affected.name] with \the [tool].", - "You start clamping bleeders in [target]'s [affected.name] with \the [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) - affected.custom_pain("The pain in your [affected.name] is maddening!") + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] starts clamping bleeders in [target]'s [affected.name] with \the [tool].", + "You start clamping bleeders in [target]'s [affected.name] with \the [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + affected.custom_pain("The pain in your [affected.name] is maddening!") return ..() /datum/surgery_step/generic/clamp_bleeders/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] clamps bleeders in [target]'s [affected.name] with \the [tool].", - "You clamp bleeders in [target]'s [affected.name] with \the [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) - spread_germs_to_organ(affected, user, tool) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_success_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] clamps bleeders in [target]'s [affected.name] with \the [tool].", + "You clamp bleeders in [target]'s [affected.name] with \the [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + spread_germs_to_organ(affected, user, tool) return SURGERY_STEP_CONTINUE /datum/surgery_step/generic/clamp_bleeders/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user]'s hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!", - "Your hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!", - chat_message_type = MESSAGE_TYPE_COMBAT - ) - affected.receive_damage(10) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_failure_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user]'s hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!", + "Your hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + affected.receive_damage(10) return SURGERY_STEP_RETRY /datum/surgery_step/generic/retract_skin @@ -121,45 +140,54 @@ time = 2.4 SECONDS /datum/surgery_step/generic/retract_skin/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - var/msg = "[user] starts to pry open the incision on [target]'s [affected.name] with \the [tool]." - var/self_msg = "You start to pry open the incision on [target]'s [affected.name] with \the [tool]." - if(target_zone == BODY_ZONE_CHEST) - msg = "[user] starts to separate the ribcage and rearrange the organs in [target]'s torso with \the [tool]." - self_msg = "You start to separate the ribcage and rearrange the organs in [target]'s torso with \the [tool]." - if(target_zone == BODY_ZONE_PRECISE_GROIN) - msg = "[user] starts to pry open the incision and rearrange the organs in [target]'s lower abdomen with \the [tool]." - self_msg = "You start to pry open the incision and rearrange the organs in [target]'s lower abdomen with \the [tool]." - user.visible_message(msg, self_msg, chat_message_type = MESSAGE_TYPE_COMBAT) - affected.custom_pain("It feels like the skin on your [affected.name] is on fire!") + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + var/msg = "[user] starts to pry open the incision on [target]'s [affected.name] with \the [tool]." + var/self_msg = "You start to pry open the incision on [target]'s [affected.name] with \the [tool]." + if(target_zone == BODY_ZONE_CHEST) + msg = "[user] starts to separate the ribcage and rearrange the organs in [target]'s torso with \the [tool]." + self_msg = "You start to separate the ribcage and rearrange the organs in [target]'s torso with \the [tool]." + if(target_zone == BODY_ZONE_PRECISE_GROIN) + msg = "[user] starts to pry open the incision and rearrange the organs in [target]'s lower abdomen with \the [tool]." + self_msg = "You start to pry open the incision and rearrange the organs in [target]'s lower abdomen with \the [tool]." + user.visible_message(msg, self_msg, chat_message_type = MESSAGE_TYPE_COMBAT) + affected.custom_pain("It feels like the skin on your [affected.name] is on fire!") return ..() /datum/surgery_step/generic/retract_skin/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - var/msg = "[user] keeps the incision open on [target]'s [affected.name] with \the [tool]." - var/self_msg = "You keep the incision open on [target]'s [affected.name] with \the [tool]." - if(target_zone == BODY_ZONE_CHEST) - msg = "[user] keeps the ribcage open on [target]'s torso with \the [tool]." - self_msg = "You keep the ribcage open on [target]'s torso with \the [tool]." - if(target_zone == BODY_ZONE_PRECISE_GROIN) - msg = "[user] keeps the incision open on [target]'s lower abdomen with \the [tool]." - self_msg = "You keep the incision open on [target]'s lower abdomen with \the [tool]." - user.visible_message(msg, self_msg, chat_message_type = MESSAGE_TYPE_COMBAT) - affected.open = ORGAN_ORGANIC_ENCASED_OPEN + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_success_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + var/msg = "[user] keeps the incision open on [target]'s [affected.name] with \the [tool]." + var/self_msg = "You keep the incision open on [target]'s [affected.name] with \the [tool]." + if(target_zone == BODY_ZONE_CHEST) + msg = "[user] keeps the ribcage open on [target]'s torso with \the [tool]." + self_msg = "You keep the ribcage open on [target]'s torso with \the [tool]." + if(target_zone == BODY_ZONE_PRECISE_GROIN) + msg = "[user] keeps the incision open on [target]'s lower abdomen with \the [tool]." + self_msg = "You keep the incision open on [target]'s lower abdomen with \the [tool]." + user.visible_message(msg, self_msg, chat_message_type = MESSAGE_TYPE_COMBAT) + affected.open = ORGAN_ORGANIC_ENCASED_OPEN return SURGERY_STEP_CONTINUE /datum/surgery_step/generic/retract_skin/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - var/msg = "[user]'s hand slips, tearing the edges of incision on [target]'s [affected.name] with \the [tool]!" - var/self_msg = "Your hand slips, tearing the edges of incision on [target]'s [affected.name] with \the [tool]!" - if(target_zone == BODY_ZONE_CHEST) - msg = "[user]'s hand slips, damaging several organs [target]'s torso with \the [tool]!" - self_msg = "Your hand slips, damaging several organs [target]'s torso with \the [tool]!" - if(target_zone == BODY_ZONE_PRECISE_GROIN) - msg = "[user]'s hand slips, damaging several organs [target]'s lower abdomen with \the [tool]" - self_msg = "Your hand slips, damaging several organs [target]'s lower abdomen with \the [tool]!" - user.visible_message(msg, self_msg, chat_message_type = MESSAGE_TYPE_COMBAT) - target.apply_damage(12, BRUTE, affected, sharp = TRUE) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_failure_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + var/msg = "[user]'s hand slips, tearing the edges of incision on [target]'s [affected.name] with \the [tool]!" + var/self_msg = "Your hand slips, tearing the edges of incision on [target]'s [affected.name] with \the [tool]!" + if(target_zone == BODY_ZONE_CHEST) + msg = "[user]'s hand slips, damaging several organs [target]'s torso with \the [tool]!" + self_msg = "Your hand slips, damaging several organs [target]'s torso with \the [tool]!" + if(target_zone == BODY_ZONE_PRECISE_GROIN) + msg = "[user]'s hand slips, damaging several organs [target]'s lower abdomen with \the [tool]" + self_msg = "Your hand slips, damaging several organs [target]'s lower abdomen with \the [tool]!" + user.visible_message(msg, self_msg, chat_message_type = MESSAGE_TYPE_COMBAT) + target.apply_damage(12, BRUTE, affected, sharp = TRUE) return SURGERY_STEP_RETRY /datum/surgery_step/generic/cauterize @@ -187,34 +215,43 @@ return TRUE /datum/surgery_step/generic/cauterize/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] is beginning to cauterize the incision on [target]'s [affected.name] with \the [tool].", - "You are beginning to cauterize the incision on [target]'s [affected.name] with \the [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) - affected.custom_pain("Your [affected.name] is being burned!") + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] is beginning to cauterize the incision on [target]'s [affected.name] with \the [tool].", + "You are beginning to cauterize the incision on [target]'s [affected.name] with \the [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + affected.custom_pain("Your [affected.name] is being burned!") return ..() /datum/surgery_step/generic/cauterize/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] cauterizes the incision on [target]'s [affected.name] with \the [tool].", - "You cauterize the incision on [target]'s [affected.name] with \the [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) - affected.open = ORGAN_CLOSED - affected.germ_level = max(affected.germ_level - 50, 0) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_success_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] cauterizes the incision on [target]'s [affected.name] with \the [tool].", + "You cauterize the incision on [target]'s [affected.name] with \the [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + affected.open = ORGAN_CLOSED + affected.germ_level = max(affected.germ_level - 50, 0) return SURGERY_STEP_CONTINUE /datum/surgery_step/generic/cauterize/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user]'s hand slips, leaving a small burn on [target]'s [affected.name] with \the [tool]!", - "Your hand slips, leaving a small burn on [target]'s [affected.name] with \the [tool]!", - chat_message_type = MESSAGE_TYPE_COMBAT - ) - target.apply_damage(3, BURN, affected) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_failure_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user]'s hand slips, leaving a small burn on [target]'s [affected.name] with \the [tool]!", + "Your hand slips, leaving a small burn on [target]'s [affected.name] with \the [tool]!", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + target.apply_damage(3, BURN, affected) return SURGERY_STEP_RETRY /datum/surgery_step/generic/cauterize/premature @@ -254,26 +291,34 @@ time = 3 SECONDS /datum/surgery_step/generic/drill/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) - user.visible_message( - "[user] begins to drill into the bone in [target]'s [parse_zone(target_zone)].", - "You begin to drill into the bone in [target]'s [parse_zone(target_zone)]...", - chat_message_type = MESSAGE_TYPE_COMBAT - ) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_text[surgery.step_number]]") + else + user.visible_message( + "[user] begins to drill into the bone in [target]'s [parse_zone(target_zone)].", + "You begin to drill into the bone in [target]'s [parse_zone(target_zone)]...", + chat_message_type = MESSAGE_TYPE_COMBAT + ) return ..() /datum/surgery_step/generic/drill/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) - user.visible_message("[user] drills into [target]'s [parse_zone(target_zone)]!", "You drill into [target]'s [parse_zone(target_zone)].") + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_success_text[surgery.step_number]]") + else + user.visible_message("[user] drills into [target]'s [parse_zone(target_zone)]!", "You drill into [target]'s [parse_zone(target_zone)].") return SURGERY_STEP_CONTINUE /datum/surgery_step/generic/drill/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user]'s [tool] doesn't get a firm grip and tears at the bone in [target]'s [parse_zone(target_zone)]!", - "Your [tool] doesn't get a firm grip and tears at the bone in [target]'s [parse_zone(target_zone)]!", - chat_message_type = MESSAGE_TYPE_COMBAT - ) - - affected.receive_damage(15) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_failure_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user]'s [tool] doesn't get a firm grip and tears at the bone in [target]'s [parse_zone(target_zone)]!", + "Your [tool] doesn't get a firm grip and tears at the bone in [target]'s [parse_zone(target_zone)]!", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + affected.receive_damage(15) return SURGERY_STEP_RETRY @@ -299,42 +344,51 @@ time = 10 SECONDS /datum/surgery_step/generic/amputate/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] is beginning to amputate [target]'s [affected.name] with \the [tool].", - "You are beginning to cut through [target]'s [affected.amputation_point] with \the [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) - affected.custom_pain("Your [affected.amputation_point] is being ripped apart!") + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] is beginning to amputate [target]'s [affected.name] with \the [tool].", + "You are beginning to cut through [target]'s [affected.amputation_point] with \the [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + affected.custom_pain("Your [affected.amputation_point] is being ripped apart!") return ..() /datum/surgery_step/generic/amputate/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] amputates [target]'s [affected.name] at the [affected.amputation_point] with \the [tool].", - "You amputate [target]'s [affected.name] with \the [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_success_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] amputates [target]'s [affected.name] at the [affected.amputation_point] with \the [tool].", + "You amputate [target]'s [affected.name] with \the [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) - add_attack_logs(user, target, "Surgically removed [affected.name]. INTENT: [uppertext(user.a_intent)]")//log it + add_attack_logs(user, target, "Surgically removed [affected.name]. INTENT: [uppertext(user.a_intent)]")//log it - var/atom/movable/thing = affected.droplimb(1, DROPLIMB_SHARP) + var/atom/movable/thing = affected.droplimb(1, DROPLIMB_SHARP) - if(istype(target) && target.can_feel_pain()) - // okay if you can feel your arm getting chopped off you aren't gonna be singing - to_chat(target, "Your [affected] goes completely numb at the [affected.amputation_point]!") - target.emote("scream") - if(isitem(thing)) - user.put_in_hands(thing) + if(istype(target) && target.can_feel_pain()) + // okay if you can feel your arm getting chopped off you aren't gonna be singing + to_chat(target, "Your [affected] goes completely numb at the [affected.amputation_point]!") + target.emote("scream") + if(isitem(thing)) + user.put_in_hands(thing) return SURGERY_STEP_CONTINUE /datum/surgery_step/generic/amputate/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user]'s hand slips, sawing through the bone in [target]'s [affected.name] with \the [tool]!", - "Your hand slips, sawing through the bone in [target]'s [affected.name] with \the [tool]!", - chat_message_type = MESSAGE_TYPE_COMBAT - ) - affected.receive_damage(30) - affected.fracture() + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_failure_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user]'s hand slips, sawing through the bone in [target]'s [affected.name] with \the [tool]!", + "Your hand slips, sawing through the bone in [target]'s [affected.name] with \the [tool]!", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + affected.receive_damage(30) + affected.fracture() return SURGERY_STEP_RETRY diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm index 26c3f1fb32a..f8aa52a60b2 100644 --- a/code/modules/surgery/organs/blood.dm +++ b/code/modules/surgery/organs/blood.dm @@ -257,7 +257,7 @@ //to add a splatter of blood or other mob liquid. /mob/living/proc/add_splatter_floor(turf/T, small_drip, shift_x, shift_y) - if((get_blood_id() != "blood") && (get_blood_id() != "slimejelly"))//is it blood or welding fuel? + if((get_blood_id() != "blood") && (get_blood_id() != "slimejelly") && (get_blood_id() != "tomatojuice"))//is it blood or welding fuel? return if(!T) T = get_turf(src) diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index 6d49e4fd181..a5494419165 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -65,6 +65,8 @@ M.dna.SetSEState(GLOB.colourblindblock, FALSE) singlemutcheck(M, GLOB.colourblindblock, MUTCHK_FORCED) . = ..() + if(!M) + return M.update_tint() M.update_sight() diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 3c4491fb224..b173beed71e 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -9,6 +9,18 @@ var/unremovable = FALSE //Whether it shows up as an option to remove during surgery. /// An associated list of organ datums that this organ has. var/list/datum/organ/organ_datums + /// This contains the hidden RnD levels of an organ to prevent rnd from using it. + var/hidden_origin_tech + /// What is the level of tech for the hidden tech type? + var/hidden_tech_level = 1 + /// How much is this organ worth in the xenobiology organ analyzer? + var/analyzer_price = 10 + /// what quality is this organ? Only useful for xeno organs + var/organ_quality = ORGAN_NORMAL + /// Does this organ originate from the xenobiology dissection loop? + var/is_xeno_organ = FALSE + /// Does this organ give a warning upon being inserted? + var/warning = FALSE /obj/item/organ/internal/New(mob/living/carbon/holder) ..() @@ -34,6 +46,11 @@ QDEL_LIST_ASSOC_VAL(organ_datums) // The removal from internal_organ_datums should be handled when the organ is removed . = ..() +/obj/item/organ/internal/examine(mob/user) + . = ..() + if(is_xeno_organ) + . += "It looks like it would replace \the [slot]." + /obj/item/organ/internal/proc/insert(mob/living/carbon/M, special = 0, dont_remove_slot = 0) if(!iscarbon(M) || owner == M) return @@ -207,6 +224,9 @@ /obj/item/organ/internal/attack__legacy__attackchain(mob/living/carbon/M, mob/user) if(M == user && ishuman(user)) var/mob/living/carbon/human/H = user + if(is_xeno_organ) + to_chat(user, "It wouldnt be a very good idea to eat this.") + return ..() var/obj/item/food/S = prepare_eat() if(S) H.drop_item() @@ -394,3 +414,9 @@ SIGNAL_HANDLER REMOVE_TRAIT(src, TRAIT_ORGAN_INSERTED_WHILE_DEAD, "[UID()]") UnregisterSignal(owner, COMSIG_LIVING_DEFIBBED) + +/// Checks that the organ is inside of a host and that they are a valid recipient. Used for abductor glands +/obj/item/organ/internal/proc/owner_check() + if(ishuman(owner) || iscarbon(owner)) + return TRUE + return FALSE diff --git a/code/modules/surgery/organs/subtypes/xenos.dm b/code/modules/surgery/organs/subtypes/xenos.dm index 7532d223c9f..e061cd328b7 100644 --- a/code/modules/surgery/organs/subtypes/xenos.dm +++ b/code/modules/surgery/organs/subtypes/xenos.dm @@ -1,14 +1,16 @@ /obj/item/organ/internal/alien - origin_tech = "biotech=5" + origin_tech = null icon_state = null var/list/alien_powers = list() var/list/human_powers = list() + icon = 'icons/obj/xeno_organs.dmi' tough = TRUE sterile = TRUE /// Amount of credits that will be received by selling this in the cargo shuttle var/cargo_profit = 250 /// Has this organ been hijacked? Can hijack via a hemostat var/hijacked = FALSE + is_xeno_organ = TRUE /// This adds and removes alien spells upon addition, if a noncarbon tries to do this well... I blame adminbus /obj/item/organ/internal/alien/insert(mob/living/carbon/M, special = 0) @@ -27,6 +29,10 @@ else for(var/powers_to_remove in human_powers) M.RemoveSpell(new powers_to_remove) + if(isalien(M)) + destroy_on_removal = TRUE + else + destroy_on_removal = FALSE . = ..() /obj/item/organ/internal/alien/examine(mob/user) @@ -62,6 +68,8 @@ slot = "plasmavessel" alien_powers = list(/datum/spell/alien_spell/plant_weeds, /datum/spell/alien_spell/transfer_plasma) human_powers = list(/datum/spell/alien_spell/syphon_plasma) + hidden_origin_tech = TECH_PLASMA + hidden_tech_level = 7 var/stored_plasma = 100 var/max_plasma = 300 @@ -132,6 +140,8 @@ origin_tech = "biotech=5;materials=2;combat=2" alien_powers = list(/datum/spell/touch/alien_spell/corrosive_acid) human_powers = list(/datum/spell/touch/alien_spell/burning_touch) + hidden_origin_tech = TECH_ENGINEERING + hidden_tech_level = 6 /obj/item/organ/internal/alien/hivenode name = "xeno hive node" @@ -140,20 +150,24 @@ origin_tech = "biotech=5;magnets=4;bluespace=3" w_class = WEIGHT_CLASS_TINY alien_powers = list(/datum/spell/alien_spell/whisper) + hidden_origin_tech = TECH_BLUESPACE + hidden_tech_level = 6 /obj/item/organ/internal/alien/hivenode/insert(mob/living/carbon/M, special = 0) ..() M.faction |= "alien" - M.add_language("Hivemind") - M.add_language("Xenomorph") ADD_TRAIT(M, TRAIT_XENO_IMMUNE, "xeno immune") + if(organ_quality == ORGAN_PRISTINE) + M.add_language("Hivemind") + M.add_language("Xenomorph") /obj/item/organ/internal/alien/hivenode/remove(mob/living/carbon/M, special = 0) M.faction -= "alien" - M.remove_language("Hivemind") - M.remove_language("Xenomorph") REMOVE_TRAIT(M, TRAIT_XENO_IMMUNE, "xeno immune") . = ..() + if(organ_quality == ORGAN_PRISTINE) + M.remove_language("Hivemind") + M.remove_language("Xenomorph") /obj/item/organ/internal/alien/neurotoxin name = "xeno neurotoxin gland" @@ -162,6 +176,8 @@ origin_tech = "biotech=5;combat=5" alien_powers = list(/datum/spell/alien_spell/neurotoxin) human_powers = list(/datum/spell/alien_spell/neurotoxin/death_to_xenos) + hidden_origin_tech = TECH_BIO + hidden_tech_level = 6 /obj/item/organ/internal/alien/resinspinner name = "xeno resin organ" @@ -170,6 +186,8 @@ origin_tech = "biotech=5;materials=4" alien_powers = list(/datum/spell/alien_spell/build_resin) human_powers = list(/datum/spell/touch/alien_spell/consume_resin) + hidden_origin_tech = TECH_MATERIAL + hidden_tech_level = 7 /obj/item/organ/internal/alien/eggsac name = "xeno egg sac" @@ -180,3 +198,5 @@ alien_powers = list(/datum/spell/alien_spell/plant_weeds/eggs) human_powers = list(/datum/spell/alien_spell/combust_facehuggers) cargo_profit = 1000 + hidden_origin_tech = TECH_BIO + hidden_tech_level = 7 diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index 49e9551ffd6..aaaa9df0165 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -48,22 +48,6 @@ ) requires_organic_bodypart = TRUE -/datum/surgery/organ_manipulation/alien - name = "Alien Organ Manipulation" - requires_bodypart = FALSE // xenos just don't have "bodyparts" - possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM) - target_mobtypes = list(/mob/living/carbon/alien/humanoid) - steps = list( - /datum/surgery_step/saw_carapace, - /datum/surgery_step/cut_carapace, - /datum/surgery_step/retract_carapace, - /datum/surgery_step/proxy/manipulate_organs/alien, - /datum/surgery_step/generic/seal_carapace - ) - - - - /datum/surgery/organ_manipulation/can_start(mob/user, mob/living/carbon/target) . = ..() if(!.) @@ -132,12 +116,6 @@ ) // have to redefine all of these because xenos don't technically have bodyparts. -/datum/surgery/intermediate/manipulate/extract/xeno - requires_bodypart = FALSE - -/datum/surgery/intermediate/manipulate/implant/xeno - requires_bodypart = FALSE - /datum/surgery/intermediate/manipulate/mend/xeno requires_bodypart = FALSE @@ -147,10 +125,8 @@ /datum/surgery_step/proxy/manipulate_organs/alien name = "Manipulate Organs Xeno (proxy)" branches = list( - /datum/surgery/intermediate/manipulate/extract/xeno, - /datum/surgery/intermediate/manipulate/implant/xeno, /datum/surgery/intermediate/manipulate/mend/xeno, - /datum/surgery/intermediate/manipulate/clean/xeno + /datum/surgery/intermediate/manipulate/clean/xeno, ) // Internal surgeries. @@ -438,6 +414,11 @@ "You start transplanting [tool] into [target]'s [parse_zone(target_zone)].", chat_message_type = MESSAGE_TYPE_COMBAT ) + if(I.warning) + if(tgui_alert(user, "This is a permanent action, guaranteeing this person will be removed from the round. Are you sure?", "Insert [I]", list("Yes", "No")) != "Yes") + return + log_admin("[key_name(user)] has inserted a [I] into [key_name(target)]!") + log_attack(user, target, "Inserted [I]") return ..() /datum/surgery_step/internal/manipulate_organs/implant/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index e815107f622..edfbbea6afe 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -119,31 +119,40 @@ time = 1.6 SECONDS /datum/surgery_step/robotics/external/unscrew_hatch/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] starts to unscrew the maintenance hatch on [target]'s [affected.name] with \the [tool].", - "You start to unscrew the maintenance hatch on [target]'s [affected.name] with \the [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] starts to unscrew the maintenance hatch on [target]'s [affected.name] with \the [tool].", + "You start to unscrew the maintenance hatch on [target]'s [affected.name] with \the [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) return ..() /datum/surgery_step/robotics/external/unscrew_hatch/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] has opened the maintenance hatch on [target]'s [affected.name] with \the [tool].", - "You have opened the maintenance hatch on [target]'s [affected.name] with \the [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) - affected.open = ORGAN_SYNTHETIC_LOOSENED + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_success_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] has opened the maintenance hatch on [target]'s [affected.name] with \the [tool].", + "You have opened the maintenance hatch on [target]'s [affected.name] with \the [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + affected.open = ORGAN_SYNTHETIC_LOOSENED return SURGERY_STEP_CONTINUE /datum/surgery_step/robotics/external/unscrew_hatch/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user]'s [tool.name] slips, failing to unscrew [target]'s [affected.name].", - "Your [tool] slips, failing to unscrew [target]'s [affected.name].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_failure_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user]'s [tool.name] slips, failing to unscrew [target]'s [affected.name].", + "Your [tool] slips, failing to unscrew [target]'s [affected.name].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) return SURGERY_STEP_RETRY /datum/surgery_step/robotics/external/open_hatch @@ -157,31 +166,40 @@ time = 2.4 SECONDS /datum/surgery_step/robotics/external/open_hatch/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] starts to pry open the maintenance hatch on [target]'s [affected.name] with \the [tool].", - "You start to pry open the maintenance hatch on [target]'s [affected.name] with \the [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] starts to pry open the maintenance hatch on [target]'s [affected.name] with \the [tool].", + "You start to pry open the maintenance hatch on [target]'s [affected.name] with \the [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) return ..() /datum/surgery_step/robotics/external/open_hatch/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] opens the maintenance hatch on [target]'s [affected.name] with \the [tool].", - "You open the maintenance hatch on [target]'s [affected.name] with \the [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) - affected.open = ORGAN_SYNTHETIC_OPEN + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_success_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] opens the maintenance hatch on [target]'s [affected.name] with \the [tool].", + "You open the maintenance hatch on [target]'s [affected.name] with \the [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + affected.open = ORGAN_SYNTHETIC_OPEN return SURGERY_STEP_CONTINUE /datum/surgery_step/robotics/external/open_hatch/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user]'s [tool.name] slips, failing to open the hatch on [target]'s [affected.name].", - "Your [tool] slips, failing to open the hatch on [target]'s [affected.name].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_failure_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user]'s [tool.name] slips, failing to open the hatch on [target]'s [affected.name].", + "Your [tool] slips, failing to open the hatch on [target]'s [affected.name].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) return SURGERY_STEP_RETRY /datum/surgery_step/robotics/external/close_hatch @@ -195,31 +213,40 @@ time = 2.4 SECONDS /datum/surgery_step/robotics/external/close_hatch/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] begins to close and secure the hatch on [target]'s [affected.name] with \the [tool].", - "You begin to close and secure the hatch on [target]'s [affected.name] with \the [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] begins to close and secure the hatch on [target]'s [affected.name] with \the [tool].", + "You begin to close and secure the hatch on [target]'s [affected.name] with \the [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) return ..() /datum/surgery_step/robotics/external/close_hatch/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] closes and secures the hatch on [target]'s [affected.name] with \the [tool].", - "You close and secure the hatch on [target]'s [affected.name] with \the [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) - tool.play_tool_sound(target) - affected.open = ORGAN_CLOSED + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_success_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] closes and secures the hatch on [target]'s [affected.name] with \the [tool].", + "You close and secure the hatch on [target]'s [affected.name] with \the [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + tool.play_tool_sound(target) + affected.open = ORGAN_CLOSED return SURGERY_STEP_CONTINUE /datum/surgery_step/robotics/external/close_hatch/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user]'s [tool.name] slips, failing to close the hatch on [target]'s [affected.name].", - "Your [tool.name] slips, failing to close the hatch on [target]'s [affected.name].", - chat_message_type = MESSAGE_TYPE_COMBAT) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_failure_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user]'s [tool.name] slips, failing to close the hatch on [target]'s [affected.name].", + "Your [tool.name] slips, failing to close the hatch on [target]'s [affected.name].", + chat_message_type = MESSAGE_TYPE_COMBAT) return SURGERY_STEP_RETRY /datum/surgery_step/robotics/external/close_hatch/premature @@ -631,40 +658,47 @@ time = 10 SECONDS /datum/surgery_step/robotics/external/amputate/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] starts to decouple [target]'s [affected.name] with [tool].", - "You start to decouple [target]'s [affected.name] with [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] starts to decouple [target]'s [affected.name] with [tool].", + "You start to decouple [target]'s [affected.name] with [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) - affected.custom_pain("Your [affected.amputation_point] is being ripped apart!") + affected.custom_pain("Your [affected.amputation_point] is being ripped apart!") return ..() /datum/surgery_step/robotics/external/amputate/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message( - "[user] has decoupled [target]'s [affected.name] with [tool].", - "You have decoupled [target]'s [affected.name] with [tool].", - chat_message_type = MESSAGE_TYPE_COMBAT - ) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_success_text[surgery.step_number]]") + else + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + "[user] has decoupled [target]'s [affected.name] with [tool].", + "You have decoupled [target]'s [affected.name] with [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + add_attack_logs(user, target, "Surgically removed [affected.name] from. INTENT: [uppertext(user.a_intent)]")//log it - add_attack_logs(user, target, "Surgically removed [affected.name] from. INTENT: [uppertext(user.a_intent)]")//log it - - var/atom/movable/thing = affected.droplimb(1, DROPLIMB_SHARP) - if(isitem(thing)) - user.put_in_hands(thing) + var/atom/movable/thing = affected.droplimb(1, DROPLIMB_SHARP) + if(isitem(thing)) + user.put_in_hands(thing) return SURGERY_STEP_CONTINUE /datum/surgery_step/robotics/external/amputate/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - - user.visible_message( - "[user]'s hand slips!", - "Your hand slips!", - chat_message_type = MESSAGE_TYPE_COMBAT - ) + if(istype(surgery, /datum/surgery/dissect)) + to_chat(user, "[target.surgery_container.dissection_failure_text[surgery.step_number]]") + else + user.visible_message( + "[user]'s hand slips!", + "Your hand slips!", + chat_message_type = MESSAGE_TYPE_COMBAT + ) return SURGERY_STEP_RETRY /datum/surgery_step/robotics/external/customize_appearance diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index 54c7fe04687..fda729e751b 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -359,7 +359,8 @@ implement_speed_mod = allowed_tools[implement_type] / 100.0 // They also have some interesting ways that surgery success/fail prob get evaluated, maybe worth looking at - speed_mod /= (get_location_modifier(target) * 1 + surgery.speed_modifier) * implement_speed_mod + if(!istype(surgery, /datum/surgery/dissect)) + speed_mod /= (get_location_modifier(target) * 1 + surgery.speed_modifier) * implement_speed_mod var/modded_time = time * speed_mod if(slowdown_immune(user)) @@ -367,7 +368,8 @@ if(implement_type) // If this is set, we aren't in an allow_hand or allow_any_item step. prob_success = allowed_tools[implement_type] - prob_success *= get_location_modifier(target) + if(!istype(surgery, /datum/surgery/dissect)) + prob_success *= get_location_modifier(target) if(!do_after(user, modded_time, target = target)) surgery.step_in_progress = FALSE diff --git a/config/example/config.toml b/config/example/config.toml index c3332fa93f8..18408e9fddb 100644 --- a/config/example/config.toml +++ b/config/example/config.toml @@ -458,7 +458,8 @@ job_slot_amounts = [ {name = "Medical Doctor", lowpop = 5, highpop = 6}, {name = "Virologist", lowpop = 1, highpop = 1}, # Science {name = "Roboticist", lowpop = 2, highpop = 2}, - {name = "Scientist", lowpop = 6, highpop = 7}, # Security + {name = "Scientist", lowpop = 4, highpop = 5}, + {name = "Xenobiologist", lowpop = 2, highpop = 2}, # Security {name = "Detective", lowpop = 1, highpop = 1}, {name = "Security Officer", lowpop = 8, highpop = 9}, {name = "Warden", lowpop = 1, highpop = 1}, diff --git a/icons/mob/fleshling.dmi b/icons/mob/fleshling.dmi index 35c720812c1..d7e584898a7 100644 Binary files a/icons/mob/fleshling.dmi and b/icons/mob/fleshling.dmi differ diff --git a/icons/mob/hud/job_assets.dmi b/icons/mob/hud/job_assets.dmi index 9ed90edbb9c..c867a35af48 100644 Binary files a/icons/mob/hud/job_assets.dmi and b/icons/mob/hud/job_assets.dmi differ diff --git a/icons/mob/hud/sechud.dmi b/icons/mob/hud/sechud.dmi index 4ee6ca97b40..029faa601db 100644 Binary files a/icons/mob/hud/sechud.dmi and b/icons/mob/hud/sechud.dmi differ diff --git a/icons/mob/inhands/equipment/medical_lefthand.dmi b/icons/mob/inhands/equipment/medical_lefthand.dmi index 7e3f9c201a3..34968b30930 100644 Binary files a/icons/mob/inhands/equipment/medical_lefthand.dmi and b/icons/mob/inhands/equipment/medical_lefthand.dmi differ diff --git a/icons/mob/inhands/equipment/medical_righthand.dmi b/icons/mob/inhands/equipment/medical_righthand.dmi index f2479720204..d26ee52944e 100644 Binary files a/icons/mob/inhands/equipment/medical_righthand.dmi and b/icons/mob/inhands/equipment/medical_righthand.dmi differ diff --git a/icons/mob/lavaland/corpses.dmi b/icons/mob/lavaland/corpses.dmi new file mode 100644 index 00000000000..a0389b9d739 Binary files /dev/null and b/icons/mob/lavaland/corpses.dmi differ diff --git a/icons/mob/lavaland/lavaland_monsters.dmi b/icons/mob/lavaland/lavaland_monsters.dmi index 6890a6b99ee..84822ad6552 100644 Binary files a/icons/mob/lavaland/lavaland_monsters.dmi and b/icons/mob/lavaland/lavaland_monsters.dmi differ diff --git a/icons/mob/mob.dmi b/icons/mob/mob.dmi index 21a8e999d01..fafe5c71e32 100644 Binary files a/icons/mob/mob.dmi and b/icons/mob/mob.dmi differ diff --git a/icons/mob/slimes.dmi b/icons/mob/slimes.dmi index c50936e2290..3a1815819ed 100644 Binary files a/icons/mob/slimes.dmi and b/icons/mob/slimes.dmi differ diff --git a/icons/obj/card.dmi b/icons/obj/card.dmi index ee97fe3cb6e..4a4eecc406e 100644 Binary files a/icons/obj/card.dmi and b/icons/obj/card.dmi differ diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi index a4ceb595daf..cae9f6c454a 100644 Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ diff --git a/icons/obj/xeno_organs.dmi b/icons/obj/xeno_organs.dmi new file mode 100644 index 00000000000..00490ffe78e Binary files /dev/null and b/icons/obj/xeno_organs.dmi differ diff --git a/paradise.dme b/paradise.dme index 7f4a74f4c99..77b0ce556e5 100644 --- a/paradise.dme +++ b/paradise.dme @@ -522,6 +522,7 @@ #include "code\datums\components\shielded.dm" #include "code\datums\components\slippery.dm" #include "code\datums\components\spawner.dm" +#include "code\datums\components\special_tastes.dm" #include "code\datums\components\spooky.dm" #include "code\datums\components\squeak.dm" #include "code\datums\components\squish.dm" @@ -2954,8 +2955,11 @@ #include "code\modules\research\designs\stock_parts_designs.dm" #include "code\modules\research\designs\telecomms_designs.dm" #include "code\modules\research\designs\weapon_designs.dm" +#include "code\modules\research\xenobiology\organ_analyzer.dm" #include "code\modules\research\xenobiology\xenobio_camera.dm" #include "code\modules\research\xenobiology\xenobiology.dm" +#include "code\modules\research\xenobiology\xenobiology_dissections.dm" +#include "code\modules\research\xenobiology\xenobiology_organs.dm" #include "code\modules\response_team\ert.dm" #include "code\modules\response_team\ert_outfits.dm" #include "code\modules\ruins\ghost_bar.dm" diff --git a/sound/machines/organ_analyzer.ogg b/sound/machines/organ_analyzer.ogg new file mode 100644 index 00000000000..e45d2adebd0 Binary files /dev/null and b/sound/machines/organ_analyzer.ogg differ diff --git a/strings/tips.txt b/strings/tips.txt index 36a8fce215e..79ff69cfd01 100644 --- a/strings/tips.txt +++ b/strings/tips.txt @@ -51,6 +51,10 @@ As the Research Director, you can spy on and even forge PDA communications with As a Scientist, you can maximize the number of uses you get out of a slime by feeding it slime steroid, created from purple slimes, while alive. You can then apply extract enhancer, created from cerulean slimes, on each extract. As a Scientist, you can disable anomalies by scanning them with an analyzer, then send a signal on the frequency it gives you with a remote signaling device. This will leave behind an anomaly core, which can be used to construct nifty gadgets! As a Scientist, researchable stock parts can seriously improve the efficiency and speed of machines around the station. In some cases, it can even unlock new functions. +As a Xenobiologist, the organ extractor can be used in place of a dissector tool when extracting organs to retrieve a functional organ immediately. Who needs slimes? +Xenobiologists are granted teleporter access, allowing them immediate transportation to corpses otherwise unable to be brought to the science department. Make sure you can find your way back! +Not all alien organs are created equal. The quality of extracted organs can amplify or suppress its effects, and sometimes this even grants special effects! +As a Xenobiologist, get out of your basement and actually do some field work to get those organs you lazy bums! As a Roboticist, keep an ear out for anomaly announcements. If you get your hands on a bluespace anomaly core, you can build a Phazon mech! As a Roboticist, you can repair your cyborgs with a welding tool. If they have taken burn damage, you can remove their battery, expose the wiring with a screwdriver and replace their wires with a cable coil. As a Roboticist, you can use a multitool on an empty cyborg shell to alter various settings, including whether it will connect to the AI.