diff --git a/_maps/map_files/Delta/delta.dmm b/_maps/map_files/Delta/delta.dmm index 74b17010086..7f0c38b005a 100644 --- a/_maps/map_files/Delta/delta.dmm +++ b/_maps/map_files/Delta/delta.dmm @@ -60,6 +60,13 @@ pixel_x = 30 }, /mob/living/simple_animal/pet/sloth/paperwork, +/obj/machinery/requests_console{ + announcementConsole = 1; + department = "Quartermaster's Desk"; + departmentType = 5; + name = "Quartermaster Requests Console"; + pixel_y = -30 + }, /turf/simulated/floor/plasteel{ dir = 6; icon_state = "brown" @@ -6800,6 +6807,8 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, +/obj/effect/turf_decal/box, +/obj/machinery/hologram/holopad, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" }, @@ -7572,8 +7581,6 @@ /turf/simulated/wall/r_wall/coated, /area/engine/supermatter) "bbh" = ( -/obj/machinery/hologram/holopad, -/obj/effect/turf_decal/box, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 9 }, @@ -7583,6 +7590,9 @@ /obj/structure/cable{ icon_state = "4-8" }, +/obj/structure/chair/office/dark{ + dir = 4 + }, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" }, @@ -8405,10 +8415,16 @@ /area/hallway/secondary/entry) "bgT" = ( /obj/structure/table, -/obj/item/clipboard, -/obj/item/toy/figure/qm, +/obj/item/folder/yellow{ + pixel_y = 1 + }, /obj/item/lighter/zippo/qm{ - pixel_x = 10 + pixel_x = 10; + pixel_y = 4 + }, +/obj/item/toy/figure/qm{ + pixel_x = -8; + pixel_y = 2 }, /turf/simulated/floor/plasteel{ dir = 1; @@ -13829,9 +13845,6 @@ }, /area/hallway/primary/port/west) "bGv" = ( -/obj/structure/chair/office/dark{ - dir = 4 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" @@ -14684,21 +14697,8 @@ /area/bridge) "bLH" = ( /obj/structure/table/reinforced, -/obj/item/cartridge/quartermaster{ - pixel_x = -3 - }, -/obj/item/cartridge/quartermaster{ - pixel_x = 5; - pixel_y = 3 - }, -/obj/item/cartridge/quartermaster{ - pixel_x = -1; - pixel_y = 7 - }, -/obj/item/gps{ - desc = "A positioning system designed to keep an eye on your fellow workers."; - gpstag = "CARG0"; - icon_state = "gps-m" +/obj/item/qm_quest_tablet{ + pixel_y = -3 }, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" @@ -32602,10 +32602,16 @@ /area/medical/reception) "dmI" = ( /obj/structure/table/reinforced, -/obj/item/flashlight/lamp, +/obj/item/flashlight/lamp{ + pixel_x = -3; + pixel_y = 13 + }, /obj/structure/cable{ icon_state = "1-2" }, +/obj/item/reagent_containers/food/drinks/mug/serv{ + pixel_x = 10 + }, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" }, @@ -32688,9 +32694,7 @@ }, /area/quartermaster/office) "dmQ" = ( -/obj/machinery/computer/supplycomp{ - dir = 8 - }, +/obj/machinery/computer/card/minor/qm, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "brown" @@ -34373,9 +34377,6 @@ }, /area/quartermaster/qm) "duB" = ( -/obj/structure/chair/office/dark{ - dir = 4 - }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" @@ -47274,8 +47275,10 @@ dir = 4 }, /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, -/obj/structure/disposalpipe/segment{ - dir = 4 +/obj/structure/disposalpipe/junction{ + dir = 4; + icon_state = "pipe-j2"; + tag = "icon-pipe-j2 (EAST)" }, /turf/simulated/floor/plasteel{ dir = 8; @@ -51312,10 +51315,9 @@ /turf/simulated/floor/plating, /area/maintenance/garden) "gfk" = ( -/obj/item/paper_bin, -/obj/structure/table, -/obj/item/pen, -/obj/item/reagent_containers/food/drinks/mug/serv, +/obj/machinery/computer/supplycomp{ + dir = 8 + }, /turf/simulated/floor/plasteel{ icon_state = "brown" }, @@ -68097,6 +68099,27 @@ pixel_y = -28 }, /obj/structure/closet/secure_closet/quartermaster, +/obj/item/fulton_core, +/obj/item/extraction_pack, +/obj/item/flash, +/obj/item/megaphone, +/obj/item/gps{ + desc = "A positioning system designed to keep an eye on your fellow workers."; + gpstag = "CARG0"; + icon_state = "gps-m" + }, +/obj/item/cartridge/quartermaster{ + pixel_x = -1; + pixel_y = 7 + }, +/obj/item/cartridge/quartermaster{ + pixel_x = -3 + }, +/obj/item/cartridge/quartermaster{ + pixel_x = 5; + pixel_y = 3 + }, +/obj/item/clipboard, /turf/simulated/floor/plasteel{ icon_state = "brown" }, @@ -76426,8 +76449,8 @@ /area/medical/medbay2) "mkj" = ( /obj/item/reagent_containers/spray/cleaner/medical{ - pixel_y = 12; - pixel_x = 2 + pixel_x = 2; + pixel_y = 12 }, /obj/item/folder/white, /obj/item/pen/red, @@ -76985,8 +77008,8 @@ pixel_y = -9 }, /obj/item/reagent_containers/spray/cleaner/brig{ - pixel_y = 7; - pixel_x = 4 + pixel_x = 4; + pixel_y = 7 }, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, @@ -103732,6 +103755,11 @@ /obj/machinery/button/windowtint{ dir = 1; id = "qm"; + pixel_x = 5; + pixel_y = 24 + }, +/obj/machinery/light_switch{ + pixel_x = -5; pixel_y = 24 }, /turf/simulated/floor/plasteel{ @@ -112962,14 +112990,22 @@ /area/maintenance/engineering) "uvF" = ( /obj/structure/table/reinforced, -/obj/item/folder/yellow, -/obj/item/stamp/qm, +/obj/item/stamp/qm{ + pixel_x = 8; + pixel_y = 10 + }, /obj/structure/cable{ icon_state = "1-8" }, -/obj/item/qm_quest_tablet{ - pixel_y = -11 +/obj/item/paper_bin{ + pixel_x = 4; + pixel_y = -10 }, +/obj/item/pen{ + pixel_x = 4; + pixel_y = -10 + }, +/obj/machinery/keycard_auth, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" }, @@ -121672,7 +121708,6 @@ /obj/machinery/firealarm{ pixel_y = 26 }, -/obj/item/reagent_containers/spray/cleaner/chemical, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitepurple" diff --git a/_maps/map_files/cerestation/cerestation.dmm b/_maps/map_files/cerestation/cerestation.dmm index 14f030888fa..2909522cf25 100644 --- a/_maps/map_files/cerestation/cerestation.dmm +++ b/_maps/map_files/cerestation/cerestation.dmm @@ -5008,10 +5008,6 @@ /area/bridge) "aRC" = ( /obj/structure/filingcabinet/chestdrawer, -/obj/machinery/light_switch{ - dir = 8; - pixel_x = 23 - }, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "brown" @@ -5284,6 +5280,12 @@ /obj/machinery/button/windowtint{ dir = 1; id = "qm"; + pixel_y = -24; + pixel_x = -5 + }, +/obj/machinery/light_switch{ + dir = 8; + pixel_x = 5; pixel_y = -24 }, /turf/simulated/floor/plasteel{ @@ -5291,8 +5293,25 @@ }, /area/quartermaster/qm) "aTm" = ( -/obj/item/twohanded/required/kirbyplants{ - icon_state = "plant-21" +/obj/item/flash, +/obj/item/extraction_pack, +/obj/item/cartridge/quartermaster{ + pixel_x = -3 + }, +/obj/item/cartridge/quartermaster{ + pixel_x = -1; + pixel_y = 7 + }, +/obj/item/cartridge/quartermaster{ + pixel_x = 5; + pixel_y = 3 + }, +/obj/item/megaphone, +/obj/item/fulton_core, +/obj/structure/closet/secure_closet/quartermaster, +/obj/item/clipboard, +/obj/item/stamp/qm{ + pixel_y = 7 }, /turf/simulated/floor/plasteel{ dir = 6; @@ -18472,10 +18491,6 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/machinery/alarm{ - dir = 1; - pixel_y = -24 - }, /obj/machinery/camera{ c_tag = "Cargo Hall West"; dir = 1; @@ -19714,6 +19729,9 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 10 }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "brown" @@ -23346,9 +23364,15 @@ /area/hallway/primary/fore) "cWB" = ( /obj/structure/table, +/obj/item/paper_bin{ + pixel_y = 13; + pixel_x = -8 + }, +/obj/item/reagent_containers/food/drinks/mug/serv{ + pixel_x = 12; + pixel_y = 10 + }, /obj/machinery/cell_charger, -/obj/item/clipboard, -/obj/item/toy/figure/qm, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "brown" @@ -35787,13 +35811,6 @@ icon_state = "yellow" }, /area/hallway/primary/central) -"gxk" = ( -/obj/structure/disposalpipe/segment{ - dir = 2; - icon_state = "pipe-c" - }, -/turf/simulated/floor/plasteel, -/area/quartermaster/qm) "gxv" = ( /obj/structure/mirror{ icon_state = "mirror_broke"; @@ -38285,19 +38302,7 @@ }, /area/hallway/primary/starboard/south) "hkd" = ( -/obj/structure/closet/secure_closet/quartermaster, -/obj/item/megaphone, -/obj/item/cartridge/quartermaster{ - pixel_x = 5; - pixel_y = 3 - }, -/obj/item/cartridge/quartermaster{ - pixel_x = -1; - pixel_y = 7 - }, -/obj/item/cartridge/quartermaster{ - pixel_x = -3 - }, +/obj/machinery/computer/card/minor/qm, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "brown" @@ -45952,14 +45957,9 @@ /area/hallway/spacebridge/dockmed) "jtW" = ( /obj/structure/table, -/obj/item/folder, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 4 }, -/obj/item/lighter/zippo/qm{ - pixel_x = -12; - pixel_y = 0 - }, /turf/simulated/floor/plasteel, /area/quartermaster/qm) "juf" = ( @@ -51785,15 +51785,19 @@ /area/storage/tech) "lbA" = ( /obj/structure/table, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 9 }, -/obj/item/stamp/qm, -/obj/structure/disposalpipe/segment, +/obj/item/folder{ + pixel_y = 2 + }, +/obj/item/toy/figure/qm{ + pixel_x = 8 + }, +/obj/item/lighter/zippo/qm{ + pixel_x = -9; + pixel_y = 4 + }, /turf/simulated/floor/plasteel, /area/quartermaster/qm) "lbG" = ( @@ -52065,7 +52069,10 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 5 }, -/obj/structure/disposalpipe/segment, +/obj/structure/disposalpipe/segment{ + dir = 1; + icon_state = "pipe-c" + }, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "brown" @@ -67366,13 +67373,14 @@ }, /area/medical/medbay) "pzy" = ( +/obj/machinery/photocopier, /obj/machinery/requests_console{ - department = "Cargo Bay"; - departmentType = 2; + announcementConsole = 1; + department = "Quartermaster's Desk"; + departmentType = 5; name = "Quartermaster Requests Console"; pixel_x = 30 }, -/obj/machinery/photocopier, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "brown" @@ -76904,16 +76912,15 @@ /turf/simulated/floor/wood/fancy/light, /area/security/hos) "smd" = ( -/obj/machinery/firealarm{ - dir = 1; - pixel_y = -24 +/obj/machinery/computer/security/mining{ + dir = 8 }, -/obj/machinery/light, -/obj/machinery/disposal, -/obj/structure/disposalpipe/trunk{ - dir = 1 +/obj/structure/extinguisher_cabinet{ + pixel_x = 25 }, +/obj/machinery/light, /turf/simulated/floor/plasteel{ + dir = 4; icon_state = "brown" }, /area/quartermaster/qm) @@ -79504,7 +79511,6 @@ /turf/simulated/floor/plating/asteroid/ancient, /area/mine/unexplored/cere/orbiting) "tdr" = ( -/obj/structure/disposalpipe/segment, /turf/simulated/floor/plasteel, /area/quartermaster/qm) "tdA" = ( @@ -81882,6 +81888,14 @@ }, /area/medical/medbay) "tLh" = ( +/obj/machinery/disposal, +/obj/structure/disposalpipe/trunk{ + dir = 8 + }, +/obj/machinery/firealarm{ + dir = 1; + pixel_y = 24 + }, /turf/simulated/floor/plasteel{ dir = 5; icon_state = "brown" @@ -84396,6 +84410,9 @@ }, /area/medical/medbay) "uyz" = ( +/obj/item/twohanded/required/kirbyplants{ + icon_state = "plant-21" + }, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "brown" @@ -89135,14 +89152,10 @@ /area/toxins/misc_lab) "vVF" = ( /obj/structure/table, -/obj/item/reagent_containers/food/drinks/mug/serv, /obj/item/qm_quest_tablet{ pixel_y = 15 }, -/obj/structure/disposalpipe/segment{ - dir = 1; - icon_state = "pipe-c" - }, +/obj/machinery/keycard_auth, /turf/simulated/floor/plasteel, /area/quartermaster/qm) "vVG" = ( @@ -89357,6 +89370,16 @@ /obj/machinery/vending/snack, /turf/simulated/floor/carpet/arcade, /area/crew_quarters/arcade) +"vYW" = ( +/obj/machinery/alarm{ + dir = 8; + pixel_x = 24 + }, +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "darkbrown" + }, +/area/quartermaster/office) "vZe" = ( /obj/effect/spawner/random_barrier/possibly_welded_airlock, /turf/simulated/floor/plating, @@ -146030,7 +146053,7 @@ cpW kda czZ acc -gxk +tdr tdr smd iIK @@ -146795,7 +146818,7 @@ ayP bTv cbG lOl -nPo +vYW ecS scI ayP diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm index e9b40cb59a8..539642f3135 100644 --- a/_maps/map_files/cyberiad/cyberiad.dmm +++ b/_maps/map_files/cyberiad/cyberiad.dmm @@ -8126,7 +8126,6 @@ dir = 8; pixel_x = 24 }, -/obj/structure/disposalpipe/segment, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "brown" @@ -45315,9 +45314,6 @@ /turf/simulated/floor/carpet, /area/ntrep) "ckx" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, /obj/structure/cable{ icon_state = "4-8" }, @@ -45327,6 +45323,10 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, +/obj/structure/disposalpipe/segment{ + dir = 2; + icon_state = "pipe-c" + }, /turf/simulated/floor/plasteel, /area/quartermaster/qm) "cky" = ( @@ -45418,9 +45418,6 @@ }, /area/medical/medbreak) "ckG" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, /obj/structure/cable{ icon_state = "4-8" }, @@ -45430,9 +45427,6 @@ /turf/simulated/floor/plasteel, /area/quartermaster/qm) "ckH" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, /obj/structure/cable{ icon_state = "4-8" }, @@ -72086,7 +72080,14 @@ pixel_x = -3; pixel_y = 7 }, -/obj/item/megaphone, +/obj/item/pen{ + pixel_x = -1; + pixel_y = 8 + }, +/obj/item/pen/red{ + pixel_x = -1; + pixel_y = 8 + }, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "brown" @@ -72347,16 +72348,7 @@ /area/maintenance/server) "gRy" = ( /obj/structure/table, -/obj/item/folder/yellow, -/obj/item/pen{ - pixel_x = 4; - pixel_y = 4 - }, -/obj/item/pen/red{ - pixel_x = 2; - pixel_y = 6 - }, -/obj/item/lighter/zippo/qm, +/obj/machinery/keycard_auth, /turf/simulated/floor/plasteel, /area/quartermaster/qm) "gRL" = ( @@ -74456,7 +74448,6 @@ name = "east station intercom (General)"; pixel_x = 28 }, -/obj/structure/disposalpipe/segment, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "brown" @@ -74626,10 +74617,11 @@ /area/space) "juC" = ( /obj/machinery/requests_console{ - department = "Cargo Bay"; - departmentType = 2; - name = "Cargo Requests Console"; - pixel_x = -30 + department = "Quartermaster's Desk"; + departmentType = 5; + name = "Quartermaster Requests Console"; + pixel_x = -30; + announcementConsole = 1 }, /turf/simulated/floor/plasteel{ dir = 8; @@ -77493,10 +77485,6 @@ }, /area/maintenance/engineering) "myU" = ( -/obj/structure/disposalpipe/segment{ - dir = 8; - icon_state = "pipe-c" - }, /obj/machinery/power/apc{ dir = 4; name = "east bump"; @@ -77811,6 +77799,26 @@ /area/maintenance/apmaint) "mSV" = ( /obj/structure/closet/secure_closet/quartermaster, +/obj/item/extraction_pack, +/obj/item/fulton_core, +/obj/item/flash, +/obj/item/clipboard, +/obj/item/cartridge/quartermaster{ + pixel_x = -4; + pixel_y = 7 + }, +/obj/item/cartridge/quartermaster{ + pixel_x = 6; + pixel_y = 5 + }, +/obj/item/cartridge/quartermaster, +/obj/item/megaphone{ + pixel_x = 7 + }, +/obj/item/stamp/qm{ + pixel_x = 7; + pixel_y = 3 + }, /turf/simulated/floor/plasteel{ icon_state = "brown" }, @@ -78257,7 +78265,7 @@ /turf/simulated/floor/plating, /area/security/permabrig) "npo" = ( -/obj/machinery/computer/security/mining, +/obj/machinery/computer/card/minor/qm, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "brown" @@ -78659,19 +78667,20 @@ /area/security/warden) "nRV" = ( /obj/structure/table, -/obj/item/cartridge/quartermaster{ - pixel_x = 6; - pixel_y = 5 - }, -/obj/item/cartridge/quartermaster, -/obj/item/cartridge/quartermaster{ - pixel_x = -4; - pixel_y = 7 - }, /obj/machinery/light{ dir = 4 }, -/obj/item/toy/figure/qm, +/obj/item/folder/yellow{ + pixel_y = -4 + }, +/obj/item/toy/figure/qm{ + pixel_y = 3; + pixel_x = 8 + }, +/obj/item/lighter/zippo/qm{ + pixel_x = -8; + pixel_y = 2 + }, /turf/simulated/floor/plasteel{ dir = 6; icon_state = "brown" @@ -82462,11 +82471,10 @@ /turf/simulated/floor/plasteel, /area/quartermaster/storage) "sdq" = ( -/obj/machinery/disposal, -/obj/structure/disposalpipe/trunk, /obj/machinery/light{ dir = 4 }, +/obj/machinery/computer/security/mining, /turf/simulated/floor/plasteel{ dir = 5; icon_state = "brown" @@ -82546,11 +82554,14 @@ }, /area/chapel/main) "sin" = ( -/obj/structure/closet, /obj/machinery/light_switch{ name = "west light switch"; pixel_x = -24 }, +/obj/structure/disposalpipe/trunk{ + dir = 1 + }, +/obj/machinery/disposal, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "brown" @@ -84603,7 +84614,6 @@ /turf/simulated/floor/plating, /area/maintenance/xenozoo) "uGn" = ( -/obj/structure/disposalpipe/segment, /obj/machinery/camera{ c_tag = "Quartermaster's Office"; dir = 8 @@ -84936,11 +84946,10 @@ /area/civilian/vacantoffice) "uWw" = ( /obj/structure/table, -/obj/item/clipboard, -/obj/item/stamp/qm, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/item/qm_quest_tablet{ - pixel_x = -12 + pixel_x = 1; + pixel_y = 6 }, /turf/simulated/floor/plasteel, /area/quartermaster/qm) diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index a3da9670e78..2b80286e577 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -178,6 +178,16 @@ #define COMSIG_ATOM_ORBIT_STOP "atom_orbit_stop" ///from base of atom/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) #define COMSIG_ATOM_HITBY "atom_hitby" +/// Called when an atom is sharpened or dulled. +#define COMSIG_ATOM_UPDATE_SHARPNESS "atom_update_sharpness" + +// Attack signals. These should share the returned flags, to standardize the attack chain. +// The chain currently works like: +// tool_act -> pre_attackby -> target.attackby (item.attack) -> afterattack +// You can use these signal responses to cancel the attack chain at a certain point from most attack signal types. + /// This response cancels the attack chain entirely. If sent early, it might cause some later effects to be skipped. + #define COMPONENT_CANCEL_ATTACK_CHAIN (1<<0) + ///////////////// ///from base of atom/attack_ghost(): (mob/dead/observer/ghost) #define COMSIG_ATOM_ATTACK_GHOST "atom_attack_ghost" @@ -583,8 +593,8 @@ ///from base of obj/item/attack_obj(): (/obj, /mob) #define COMSIG_ITEM_ATTACK_OBJ "item_attack_obj" #define COMPONENT_NO_ATTACK_OBJ (1<<0) -///from base of obj/item/pre_attack(): (atom/target, mob/user, params) -#define COMSIG_ITEM_PRE_ATTACK "item_pre_attack" +///from base of obj/item/pre_attackby(): (atom/target, mob/user, params) +#define COMSIG_ITEM_PRE_ATTACKBY "item_pre_attackby" #define COMPONENT_NO_ATTACK (1<<0) ///from base of obj/item/afterattack(): (atom/target, mob/user, params) #define COMSIG_ITEM_AFTERATTACK "item_afterattack" @@ -611,6 +621,7 @@ #define COMPONENT_BLOCK_MARK_RETRIEVAL (1<<0) ///from base of obj/item/hit_reaction(): (list/args) #define COMSIG_ITEM_HIT_REACT "item_hit_react" + #define COMPONENT_BLOCK_SUCCESSFUL (1 << 0) ///called on item when crossed by something (): (/atom/movable, mob/living/crossed) #define COMSIG_ITEM_WEARERCROSSED "wearer_crossed" ///called on item when microwaved (): (obj/machinery/microwave/M) @@ -701,6 +712,9 @@ ///called in /obj/item/gun/process_fire (user, target, params, zone_override) #define COMSIG_MOB_FIRED_GUN "mob_fired_gun" +///called in /obj/item/gun/process_fire (user, target) +#define COMSIG_GUN_FIRED "gun_fired" + // /obj/item/grenade signals ///called in /obj/item/gun/process_fire (user, target, params, zone_override) @@ -756,6 +770,13 @@ #define COMSIG_HUMAN_APPLY_OVERLAY "living_apply_overlay" ///From mob/living/carbon/human/do_suicide() #define COMSIG_HUMAN_SUICIDE_ACT "human_suicide_act" +///From mob/living/carbon/human/regenerate_icons() +#define COMSIG_HUMAN_REGENERATE_ICONS "human_regenerate_icons" + + +///from /mob/living/carbon/human/proc/check_shields(): (atom/hit_by, damage, attack_text, attack_type, armour_penetration, damage_type) +#define COMSIG_HUMAN_CHECK_SHIELDS "human_check_shields" + #define SHIELD_BLOCK (1<<0) // /datum/species signals ///from datum/species/on_species_gain(): (datum/species/new_species, datum/species/old_species) diff --git a/code/__DEFINES/genetics.dm b/code/__DEFINES/genetics.dm index 967da0aa783..f6e6490cbff 100644 --- a/code/__DEFINES/genetics.dm +++ b/code/__DEFINES/genetics.dm @@ -161,3 +161,4 @@ #define EXOTIC_COLOR "exotic_blood_colour" #define NO_OBESITY "no_obesity" #define RUNIC_MIND "runic_mind" +#define REPEATSURGERY "repeat_syrgery" diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index a8d393bf5ce..130e5fc509a 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -133,6 +133,7 @@ GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list( GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( /turf/space, /turf/simulated/floor/chasm, + /turf/simulated/openspace, ))) #define isgroundlessturf(A) (is_type_in_typecache(A, GLOB.turfs_without_ground)) diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index 5632075185f..9c0b1eec468 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -47,6 +47,7 @@ #define TARGET_DEPT_MED 3 #define TARGET_DEPT_SCI 4 #define TARGET_DEPT_ENG 5 +#define TARGET_DEPT_SUP 6 // These are used by supermatter and supermatter monitor program, mostly for UI updating purposes. Higher should always be worse! // These are warning defines, they should trigger before the state, not after. diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 27c9b9a4011..980e45b5c8d 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -332,7 +332,7 @@ #define isexternalorgan(A) (istype((A), /obj/item/organ/external)) -#define hasorgans(A) (ishuman(A)) +#define hasorgans(A) (iscarbon(A)) #define is_admin(user) (check_rights(R_ADMIN, 0, (user)) != 0) diff --git a/code/__DEFINES/surgery_defines.dm b/code/__DEFINES/surgery_defines.dm new file mode 100644 index 00000000000..7929df03ced --- /dev/null +++ b/code/__DEFINES/surgery_defines.dm @@ -0,0 +1,75 @@ +/// Defines for surgery and other stuff. + + +// Used in surgery step to determine how blood should be spread to the doc +/// Don't splash any blood. +#define SURGERY_BLOODSPREAD_NONE 0 +/// Cover the surgeon's hands in blood. +#define SURGERY_BLOODSPREAD_HANDS 1 +/// Cover the surgeon's body in blood. +#define SURGERY_BLOODSPREAD_FULLBODY 2 + +// The type of surgeries that an initiator can start. +// Note that this doesn't apply for surgeries applied on missing organs. +/// An initiator with this can start surgeries on organic organs. Make sure that anything that can be sharp gets this as well. +#define SURGERY_INITIATOR_ORGANIC 1 +/// An initiator with this can start surgeries on robotic organs. +#define SURGERY_INITIATOR_ROBOTIC 2 + +// How "open" an organ is. + +/// Closed up. +#define ORGAN_CLOSED 0 + +// Different defines for different organ types, though both can still reference ORGAN_CLOSED +/// An organic limb that's been opened, at least once. +#define ORGAN_ORGANIC_OPEN 1 +/// An organ that's encased, probably with bone, where that casing has been cut through. +#define ORGAN_ORGANIC_ENCASED_OPEN 2 + +/// Synthetic organ that's been unscrewed. +#define ORGAN_SYNTHETIC_LOOSENED 3 +/// Synthetic organ that's had its panel opened. +#define ORGAN_SYNTHETIC_OPEN 4 + +// Return defines for surgery steps + +/// Return this from begin_step() to abort the step and not try the surgery. +#define SURGERY_BEGINSTEP_ABORT (-1) + +/// Return this from begin_step() to skip the current step entirely and proceed to the next one. +/// Use this if you would end up leaving someone in an invalid state. +#define SURGERY_BEGINSTEP_SKIP (1) + +// Return these from end_step/fail_step to indicate the next move + +/// The surgery step was not completed for some reason, and the next action will again be on this step. +#define SURGERY_STEP_INCOMPLETE 0 +/// The surgery step was completed, and the surgery should continue to the next step. +#define SURGERY_STEP_CONTINUE 1 +/// This step will automatically be retried without question as long as this is returned. +/// Be very cautious with this one! Make sure that any flow where this is used has an exit condition where something else will be returned. +/// Otherwise, the user will be stuck in a loop! +#define SURGERY_STEP_RETRY_ALWAYS 2 +/// This surgery step will be conditionally retried, so long as the surgery step's can_repeat() proc returns TRUE. +/// Otherwise, it'll behave just like SURGERY_STEP_INCOMPLETE. +#define SURGERY_STEP_RETRY 3 + + +// Return values for surgery_step.initiate(). +// Before you ask, yes, we need another definition for surgery steps here, since these control how we will act on the attack-chain +// side of things. +// Unless you're changing the mechanics of the surgery attack chain, you almost surely don't want to use these, and should +// instead be using the above SURGERY_STEP_X defines. + +/// The surgery initiation isn't even going to be started. If you're working with the attack chain, this is probably what you'll be using. +#define SURGERY_INITIATE_CONTINUE_CHAIN 0 + +/// The surgery initiaition was a success. We're advancing the current surgery. +#define SURGERY_INITIATE_SUCCESS 1 + +/// The surgery initiation was interrupted, or for some reason never completed. We don't want to return FALSE to the attack chain, though. +#define SURGERY_INITIATE_FAILURE 2 + +/// The surgery never reached (or finished) the do_after. Go back to the state we were in before this even happened. +#define SURGERY_INITIATE_INTERRUPTED 3 diff --git a/code/__DEFINES/tools.dm b/code/__DEFINES/tools.dm index 97cdbd60abb..4e9b6571a4e 100644 --- a/code/__DEFINES/tools.dm +++ b/code/__DEFINES/tools.dm @@ -1,3 +1,4 @@ +// Tool tools #define TOOL_CROWBAR "crowbar" #define TOOL_MULTITOOL "multitool" #define TOOL_SCREWDRIVER "screwdriver" @@ -5,6 +6,30 @@ #define TOOL_WRENCH "wrench" #define TOOL_WELDER "welder" +// Surgery tools +#define TOOL_RETRACTOR "retractor" +#define TOOL_HEMOSTAT "hemostat" +#define TOOL_CAUTERY "cautery" +#define TOOL_DRILL "drill" +#define TOOL_SCALPEL "scalpel" +#define TOOL_SAW "saw" +#define TOOL_BONESET "bonesetter" +#define TOOL_BONEGEL "bonegel" +#define TOOL_FIXOVEIN "fixovein" + +GLOBAL_LIST_INIT(surgery_tool_behaviors, list( + TOOL_RETRACTOR, + TOOL_HEMOSTAT, + TOOL_CAUTERY, + TOOL_DRILL, + TOOL_SCALPEL, + TOOL_SAW, + TOOL_BONESET, + TOOL_BONEGEL, + TOOL_FIXOVEIN, + TOOL_SCREWDRIVER +)) + #define MIN_TOOL_SOUND_DELAY 20 //Crowbar messages diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index cfcfdde07f4..f09a743cf33 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -66,12 +66,16 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// We have some form of forced gravity acting on us #define TRAIT_FORCED_GRAVITY "forced_gravity" -//item traits +//***** ITEM TRAITS *****// #define TRAIT_CMAGGED "cmagged" /// The items needs two hands to be carried #define TRAIT_NEEDS_TWO_HANDS "needstwohands" /// Properly wielded two handed item #define TRAIT_WIELDED "wielded" +/// A surgical tool; when in hand in help intent (and with a surgery in progress) won't attack the user +#define TRAIT_SURGICAL "surgical_tool" +/// An advanced surgical tool. If a surgical tool has this flag, it will be able to automatically repeat steps until they succeed. +#define TRAIT_ADVANCED_SURGICAL "advanced_surgical" ///Movement type traits for movables. See elements/movetype_handler.dm #define TRAIT_MOVE_GROUND "move_ground" diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index 7b0f506c512..43ebbe3faa8 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -39,6 +39,9 @@ #define DNA_TRAIT "dna_trait" +/// Traits applied to a silicon mob by their model. +#define ROBOT_TRAIT "robot_trait" + /// A trait gained from a mob's leap action, like the leaper #define LEAPING_TRAIT "leaping" diff --git a/code/__HELPERS/level_check.dm b/code/__HELPERS/level_check.dm index c49843cd7ea..2e8ad669f79 100644 --- a/code/__HELPERS/level_check.dm +++ b/code/__HELPERS/level_check.dm @@ -5,3 +5,16 @@ // For expansion later /proc/atoms_share_level(atom/A, atom/B) return A && B && A.z == B.z + +/** + * Checks if source_loc and checking_loc is both on the station, or on the same z level. + * This is because the station's several levels aren't considered the same z, so multi-z stations need this special case. + * returns TRUE if connection is valid, FALSE otherwise. + */ +/proc/is_valid_z_level(turf/source_loc, turf/checking_loc) + // if we're both on "station", regardless of multi-z, we'll pass by. + if(is_station_level(source_loc.z) && is_station_level(checking_loc.z)) + return TRUE + if(source_loc.z == checking_loc.z) + return TRUE + return FALSE diff --git a/code/__HELPERS/path.dm b/code/__HELPERS/path.dm index c75c5f9eb3c..7e3d4ded657 100644 --- a/code/__HELPERS/path.dm +++ b/code/__HELPERS/path.dm @@ -453,7 +453,7 @@ * * target - path end loc. * * flags - additional pass_flags, used to determine passability on each step. */ -/proc/is_path_exist(atom/source, atom/target, flags, include_source_loc = FALSE) +/proc/is_path_exist(atom/source, atom/target, flags, include_source_loc = FALSE, exclude_mobs = TRUE) if(!source || !target) return FALSE var/list/path_turfs = get_line(source, target) @@ -469,7 +469,7 @@ if(flags) dummy.pass_flags |= flags for(var/turf/turf as anything in path_turfs) - if(turf.is_blocked_turf(source_atom = dummy)) + if(turf.is_blocked_turf(exclude_mobs = exclude_mobs, source_atom = dummy)) qdel(dummy) return FALSE dummy.loc = turf diff --git a/code/__HELPERS/tool_helpers.dm b/code/__HELPERS/tool_helpers.dm index 15c8e60591e..79a49d724bb 100644 --- a/code/__HELPERS/tool_helpers.dm +++ b/code/__HELPERS/tool_helpers.dm @@ -49,3 +49,9 @@ istype(W, /obj/item/bonegel) || \ istype(W, /obj/item/bonesetter) ) + +/proc/is_surgery_tool_by_behavior(obj/item/W) + if(!istype(W)) + return FALSE + var/tool_behavior = W.tool_behaviour + return tool_behavior in GLOB.surgery_tool_behaviors diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index d086899ec63..bb748b8216f 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -404,44 +404,46 @@ Returns 1 if the chain up to the area contains the given typepath // returns the turf located at the map edge in the specified direction relative to A // used for mass driver -/proc/get_edge_target_turf(var/atom/A, var/direction) - - var/turf/target = locate(A.x, A.y, A.z) - if(!A || !target) +/proc/get_edge_target_turf(atom/target_atom, direction) + var/turf/target = locate(target_atom.x, target_atom.y, target_atom.z) + if(!target_atom || !target) return 0 - //since NORTHEAST == NORTH & EAST, etc, doing it this way allows for diagonal mass drivers in the future + //since NORTHEAST == NORTH|EAST, etc, doing it this way allows for diagonal mass drivers in the future //and isn't really any more complicated - // Note diagonal directions won't usually be accurate + var/x = target_atom.x + var/y = target_atom.y if(direction & NORTH) - target = locate(target.x, world.maxy, target.z) - if(direction & SOUTH) - target = locate(target.x, 1, target.z) + y = world.maxy + else if(direction & SOUTH) //you should not have both NORTH and SOUTH in the provided direction + y = 1 if(direction & EAST) - target = locate(world.maxx, target.y, target.z) - if(direction & WEST) - target = locate(1, target.y, target.z) - - return target + x = world.maxx + else if(direction & WEST) + x = 1 + if(ISDIAGONALDIR(direction)) //let's make sure it's accurately-placed for diagonals + var/lowest_distance_to_map_edge = min(abs(x - target_atom.x), abs(y - target_atom.y)) + return get_ranged_target_turf(target_atom, direction, lowest_distance_to_map_edge) + return locate(x,y,target_atom.z) // returns turf relative to A in given direction at set range // result is bounded to map size // note range is non-pythagorean // used for disposal system -/proc/get_ranged_target_turf(var/atom/A, var/direction, var/range) +/proc/get_ranged_target_turf(atom/target_atom, direction, range) - var/x = A.x - var/y = A.y + var/x = target_atom.x + var/y = target_atom.y if(direction & NORTH) y = min(world.maxy, y + range) - if(direction & SOUTH) + else if(direction & SOUTH) y = max(1, y - range) if(direction & EAST) x = min(world.maxx, x + range) - if(direction & WEST) + else if(direction & WEST) //if you have both EAST and WEST in the provided direction, then you're gonna have issues x = max(1, x - range) - return locate(x,y,A.z) + return locate(x,y,target_atom.z) // returns turf relative to A offset in dx and dy tiles diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 40d372e5a7f..6f5ed439095 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -237,7 +237,8 @@ animals lunging, etc. */ /mob/proc/RangedAttack(atom/A, params) - SEND_SIGNAL(src, COMSIG_MOB_ATTACK_RANGED, A, params) + if(SEND_SIGNAL(src, COMSIG_MOB_ATTACK_RANGED, A, params) & COMPONENT_CANCEL_ATTACK_CHAIN) + return TRUE /* Restrained ClickOn diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm index 52090daff6e..5aec86b01bd 100644 --- a/code/_onclick/drag_drop.dm +++ b/code/_onclick/drag_drop.dm @@ -110,7 +110,7 @@ to inform the game this action was expected and its fine drag_start = world.time drag_details = modifiers.Copy() mouseParams = params - mouse_location_UID = over_location?.UID() + mouse_location_UID = istype(over_location) ? over_location.UID() : null mouse_object_UID = over_object?.UID() if(selected_target[1] && over_object?.IsAutoclickable()) selected_target[1] = over_object diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 5627e413a40..a8f26361fd4 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -14,11 +14,15 @@ // Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown. /obj/item/proc/attack_self(mob/user) - if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user) & COMPONENT_NO_INTERACT) + var/signal_ret = SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user) + if(signal_ret & COMPONENT_NO_INTERACT) return - return + if(signal_ret & COMPONENT_CANCEL_ATTACK_CHAIN) + return TRUE /obj/item/proc/pre_attackby(atom/A, mob/living/user, params) //do stuff before attackby! + if(SEND_SIGNAL(src, COMSIG_ITEM_PRE_ATTACKBY, A, user, params) & COMPONENT_CANCEL_ATTACK_CHAIN) + return TRUE if(is_hot(src) && A.reagents && !ismob(A)) to_chat(user, "You heat [A] with [src].") A.reagents.temperature_reagents(is_hot(src)) @@ -40,29 +44,11 @@ return I.attack(src, user) /obj/item/proc/attack(mob/living/target, mob/living/user, def_zone) - SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, target, user) + if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, target, user) & COMPONENT_CANCEL_ATTACK_CHAIN) + return TRUE SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK, target, user) if(flags & (NOBLUDGEON)) return FALSE - if(can_operate(target)) //Checks if mob is lying down on table for surgery - if(istype(src,/obj/item/robot_parts))//popup override for direct attach - if(!attempt_initiate_surgery(src, target, user,1)) - return FALSE - else - return TRUE - if(istype(src,/obj/item/organ/external)) - var/obj/item/organ/external/E = src - if(E.is_robotic()) // Robot limbs are less messy to attach - if(!attempt_initiate_surgery(src, target, user,1)) - return FALSE - else - return TRUE - var/obj/item/organ/external/O = target.get_organ(user.zone_selected) - if((is_sharp(src) || (isscrewdriver(src) && O?.is_robotic())) && user.a_intent == INTENT_HELP) - if(!attempt_initiate_surgery(src, target, user)) - return FALSE - else - return TRUE if (check_item_eat(target, user)) return FALSE diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index c36065b2a2f..37063d0ada9 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -59,10 +59,10 @@ examinate(A) /atom/proc/attack_ghost(mob/dead/observer/user) - if(!istype(user)) - return FALSE + if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_GHOST, user) & COMPONENT_CANCEL_ATTACK_CHAIN) + return TRUE if(user.client) - if(user.gas_scan && atmos_scan(user=user, target=src, silent=TRUE)) + if(user.gas_scan && atmos_scan(user = user, target = src, silent = TRUE)) return TRUE return FALSE @@ -75,7 +75,7 @@ robot_healthscan(user, src) else if(ishuman(src)) healthscan(user, src, 1, TRUE) - . = ..() + return ..() // --------------------------------------- // And here are some good things for free: diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index 9079fb77430..63a3ccf3626 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -32,8 +32,8 @@ /atom/proc/attack_hand(mob/user) - SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_HAND, user) - return + if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_HAND, user) & COMPONENT_CANCEL_ATTACK_CHAIN) + return TRUE /* /mob/living/carbon/human/RestrainedClickOn(var/atom/A) -- Handled by carbons diff --git a/code/datums/components/shielded.dm b/code/datums/components/shielded.dm new file mode 100644 index 00000000000..116c643e51b --- /dev/null +++ b/code/datums/components/shielded.dm @@ -0,0 +1,192 @@ +/** + * The shielded component causes the parent item to nullify a certain number of attacks against the wearer, see: shielded vests. + */ + +/datum/component/shielded + /// The person currently wearing us + var/mob/living/wearer + /// How many charges we can have max, and how many we start with + var/max_charges + /// How many charges we currently have + var/current_charges + /// How long we have to avoid being hit to replenish charges. If set to 0, we never recharge lost charges + var/recharge_start_delay = 20 SECONDS + /// Once we go unhit long enough to recharge, we replenish charges this often. The floor is effectively 1 second, AKA how often SSdcs processes + var/charge_increment_delay = 1 SECONDS + /// How many charges we recover on each charge increment + var/charge_recovery = 1 + /// What .dmi we're pulling the shield icon from + var/shield_icon_file = 'icons/effects/effects.dmi' + /// What icon is used when someone has a functional shield up + var/shield_icon = "shield-old" + /// Do we still shield if we're being held in-hand? If FALSE, it needs to be equipped to a slot to work + var/shield_inhand = FALSE + /// Should the shield lose charges equal to the damage dealt by a hit? + var/lose_multiple_charges = FALSE + /// Should the shield's alpha change to show its remaining charge + var/show_charge_as_alpha = FALSE + /// The item we use for recharging + var/recharge_path + /// The cooldown tracking when we were last hit + COOLDOWN_DECLARE(recently_hit_cd) + /// The cooldown tracking when we last replenished a charge + COOLDOWN_DECLARE(charge_add_cd) + /// A callback for the sparks/message that play when a charge is used, see [/datum/component/shielded/proc/default_run_hit_callback] + var/datum/callback/on_hit_effects + ///The visual effect + var/mutable_appearance/shield + +/datum/component/shielded/Initialize(max_charges = 3, recharge_start_delay = 20 SECONDS, charge_increment_delay = 1 SECONDS, charge_recovery = 1, lose_multiple_charges = FALSE, show_charge_as_alpha = FALSE, recharge_path = null, starting_charges = null, shield_icon_file = 'icons/effects/effects.dmi', shield_icon = "shield-old", shield_inhand = FALSE, run_hit_callback) + if(!isitem(parent) || max_charges <= 0) + return COMPONENT_INCOMPATIBLE + + src.max_charges = max_charges + src.recharge_start_delay = recharge_start_delay + src.charge_increment_delay = charge_increment_delay + src.charge_recovery = charge_recovery + src.lose_multiple_charges = lose_multiple_charges + src.show_charge_as_alpha = show_charge_as_alpha + src.recharge_path = recharge_path + src.shield_icon_file = shield_icon_file + src.shield_icon = shield_icon + src.shield_inhand = shield_inhand + src.on_hit_effects = run_hit_callback || CALLBACK(src, PROC_REF(default_run_hit_callback)) + if(isnull(starting_charges)) + current_charges = max_charges + else + current_charges = starting_charges + if(recharge_start_delay) + START_PROCESSING(SSdcs, src) + +/datum/component/shielded/Destroy(force, silent) + if(wearer) + shield_icon = "broken" + UnregisterSignal(wearer, COMSIG_ATOM_UPDATE_OVERLAYS) + wearer.regenerate_icons() + wearer = null + on_hit_effects = null + return ..() + +/datum/component/shielded/RegisterWithParent() + RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equipped)) + RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(lost_wearer)) + RegisterSignal(parent, COMSIG_ITEM_HIT_REACT, PROC_REF(on_hit_react)) + var/atom/shield = parent + if(ismob(shield.loc)) + var/mob/holder = shield.loc + if(holder.is_in_hands(parent) && !shield_inhand) + return + set_wearer(holder) + +/datum/component/shielded/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED, COMSIG_ITEM_HIT_REACT)) + var/atom/shield = parent + if(shield.loc == wearer) + lost_wearer(src, wearer) + +// Handle recharging, if we want to +/datum/component/shielded/process(seconds_per_tick) + if(current_charges >= max_charges) + STOP_PROCESSING(SSdcs, src) + return + + if(!COOLDOWN_FINISHED(src, recently_hit_cd)) + return + if(!COOLDOWN_FINISHED(src, charge_add_cd)) + return + + var/obj/item/item_parent = parent + COOLDOWN_START(src, charge_add_cd, charge_increment_delay) + adjust_charge(charge_recovery) // set the number of charges to current + recovery per increment, clamped from zero to max_charges + playsound(item_parent, 'sound/magic/charge.ogg', 50, TRUE) + if(current_charges == max_charges) + playsound(item_parent, 'sound/machines/ding.ogg', 50, TRUE) + +/datum/component/shielded/proc/adjust_charge(change) + current_charges = clamp(current_charges + change, 0, max_charges) + if(wearer) + apply_shield_overlay() + +/// Check if we've been equipped to a valid slot to shield +/datum/component/shielded/proc/on_equipped(datum/source, mob/user, slot) + SIGNAL_HANDLER + + if((slot == SLOT_HUD_LEFT_HAND || slot == SLOT_HUD_RIGHT_HAND) && !shield_inhand) + lost_wearer(source, user) + return + set_wearer(user) + +/// Either we've been dropped or our wearer has been QDEL'd. Either way, they're no longer our problem +/datum/component/shielded/proc/lost_wearer(datum/source, mob/user) + SIGNAL_HANDLER + + if(wearer) + UnregisterSignal(wearer, list(COMSIG_HUMAN_REGENERATE_ICONS, COMSIG_PARENT_QDELETING)) + wearer.cut_overlay(shield) + wearer.regenerate_icons() + wearer = null + +/datum/component/shielded/proc/set_wearer(mob/user) + if(wearer == user) + return + wearer = user + RegisterSignal(wearer, COMSIG_HUMAN_REGENERATE_ICONS, PROC_REF(apply_shield_overlay)) + RegisterSignal(wearer, COMSIG_PARENT_QDELETING, PROC_REF(lost_wearer)) + if(current_charges) + apply_shield_overlay() + +/// Used to draw the shield overlay on the wearer +/datum/component/shielded/proc/apply_shield_overlay() + SIGNAL_HANDLER //COMSIG_HUMAN_REGENERATE_ICONS + wearer.cut_overlay(shield) + var/mutable_appearance/shield_appearance = mutable_appearance(shield_icon_file, (current_charges > 0 ? shield_icon : "broken"), MOB_LAYER + 0.01) + if(show_charge_as_alpha) + shield_appearance.alpha = (current_charges/max_charges)*255 + wearer.add_overlay(shield_appearance) + shield = shield_appearance + +/** + * This proc fires when we're hit, and is responsible for checking if we're charged, then deducting one + returning that we're blocking if so. + * It then runs the callback in [/datum/component/shielded/var/on_hit_effects] which handles the messages/sparks (so the visuals) + */ +/datum/component/shielded/proc/on_hit_react(datum/source, mob/living/carbon/human/owner, atom/movable/hitby, damage, attack_type) + SIGNAL_HANDLER + + COOLDOWN_START(src, recently_hit_cd, recharge_start_delay) + + if(current_charges <= 0) + return + . = COMPONENT_BLOCK_SUCCESSFUL + + var/charge_loss = 1 // how many charges do we lose + + if(istype(hitby, /obj/item/projectile)) + var/obj/item/projectile/P = hitby + if(P.shield_buster) + charge_loss = 3 + if(lose_multiple_charges) + charge_loss = damage //Double dip if health based instead + + if(lose_multiple_charges) // if the shield has health like damage we'll lose charges equal to the damage of the hit + charge_loss += damage + + adjust_charge(-charge_loss) + wearer.update_appearance(UPDATE_ICON) + + INVOKE_ASYNC(src, PROC_REF(actually_run_hit_callback), owner, hitby, current_charges) + + if(!recharge_start_delay) // if recharge_start_delay is 0, we don't recharge + return + + START_PROCESSING(SSdcs, src) // if we DO recharge, start processing so we can do that + +/// The wrapper to invoke the on_hit callback, so we don't have to worry about blocking in the signal handler +/datum/component/shielded/proc/actually_run_hit_callback(mob/living/owner, attack_text, current_charges) + on_hit_effects.Invoke(owner, attack_text, current_charges) + +/// Default on_hit proc, since cult robes are stupid and have different descriptions/sparks +/datum/component/shielded/proc/default_run_hit_callback(mob/living/owner, attack_text, current_charges) + do_sparks(2, TRUE, owner) + owner.visible_message(span_danger("[owner]'s shields deflect [attack_text] in a shower of sparks!")) + if(current_charges <= 0) + owner.visible_message(span_warning("[owner]'s shield overloads!")) diff --git a/code/datums/components/surgery_initiator.dm b/code/datums/components/surgery_initiator.dm new file mode 100644 index 00000000000..e5b11c7c1a0 --- /dev/null +++ b/code/datums/components/surgery_initiator.dm @@ -0,0 +1,316 @@ + +/** + * # Surgery Initiator + * + * Allows an item to start (or prematurely stop) a surgical operation. + */ +/datum/component/surgery_initiator + /// If present, this surgery TYPE will be attempted when the item is used. + /// Useful for things like limb reattachments that don't need a scalpel. + var/datum/surgery/forced_surgery + + /// If true, the initial step will be cancellable by just using the tool again. Should be FALSE for any tool that actually has a first surgery step. + var/can_cancel_before_first = FALSE + + /// If true, can be used with a cautery in the off-hand to cancel a surgery. + var/can_cancel = TRUE + + /// If true, can start surgery on someone while they're standing up. + /// Seeing as how we really don't support this (yet), it's much nicer to selectively enable this if we want it. + var/can_start_on_stander = FALSE + + /// Bitfield for the types of surgeries that this can start. + /// Note that in cases where organs are missing, this will be ignored. + /// Also, note that for anything sharp, SURGERY_INITIATOR_ORGANIC should be set as well. + var/valid_starting_types = SURGERY_INITIATOR_ORGANIC + + // Replace any other surgery initiator + dupe_type = /datum/component/surgery_initiator + +/** + * Create a new surgery initiating component. + * + * Arguments: + * * forced_surgery - (optional) the surgery that will be started when the parent is used on a mob. + */ +/datum/component/surgery_initiator/Initialize(datum/surgery/forced_surgery) + . = ..() + if(!isitem(parent)) + return COMPONENT_INCOMPATIBLE + + src.forced_surgery = forced_surgery + +/datum/component/surgery_initiator/RegisterWithParent() + RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(initiate_surgery_moment)) + RegisterSignal(parent, COMSIG_ATOM_UPDATE_SHARPNESS, PROC_REF(on_parent_sharpness_change)) + +/datum/component/surgery_initiator/UnregisterFromParent() + UnregisterSignal(parent, COMSIG_ITEM_ATTACK) + UnregisterSignal(parent, COMSIG_ATOM_UPDATE_SHARPNESS) + +/// Keep tabs on the attached item's sharpness. +/// This component gets added in atoms when they're made sharp as well. +/datum/component/surgery_initiator/proc/on_parent_sharpness_change() + SIGNAL_HANDLER // COMSIG_ATOM_UPDATE_SHARPNESS + var/obj/item/P = parent + if(!P.sharp) + RemoveComponent() + qdel(src) + +/// Does the surgery initiation. +/datum/component/surgery_initiator/proc/initiate_surgery_moment(datum/source, atom/target, mob/user) + SIGNAL_HANDLER // COMSIG_ITEM_ATTACK + if(!isliving(user)) + return + var/mob/living/L = target + if(!user.Adjacent(target)) + return + if(user.a_intent != INTENT_HELP) + return + if(!(L.lying_angle || L.resting || L.stat) && !can_start_on_stander) + return + if(!(L.lying_angle || L.resting || L.stat) && !on_operable_surface(L)) + return + if(iscarbon(target)) + var/mob/living/carbon/C = target + var/obj/item/organ/external/affected = C.get_organ(user.zone_selected) + if(affected) + if((affected.status & ORGAN_ROBOT) && !(valid_starting_types & SURGERY_INITIATOR_ROBOTIC)) + return + if(!(affected.status & ORGAN_ROBOT) && !(valid_starting_types & SURGERY_INITIATOR_ORGANIC)) + return + + if(L.has_status_effect(STATUS_EFFECT_SUMMONEDGHOST)) + to_chat(user, span_notice("You realise that a ghost probably doesn't have any useful organs.")) + return //no cult ghost surgery please + INVOKE_ASYNC(src, PROC_REF(do_initiate_surgery_moment), target, user) + // This signal is actually part of the attack chain, so it needs to return true to stop it + return TRUE + +/// Meat and potatoes of starting surgery. +/datum/component/surgery_initiator/proc/do_initiate_surgery_moment(mob/living/target, mob/user) + var/datum/surgery/current_surgery + + // Check if we've already got a surgery on our target zone + for(var/i_one in target.surgeries) + var/datum/surgery/surgeryloop = i_one + if(surgeryloop.location == user.zone_selected) + current_surgery = surgeryloop + break + + if(!isnull(current_surgery) && !current_surgery.step_in_progress) + var/datum/surgery_step/current_step = current_surgery.get_surgery_step() + if(current_step.try_op(user, target, user.zone_selected, parent, current_surgery) == SURGERY_INITIATE_SUCCESS) + return + if(istype(parent, /obj/item/scalpel/laser/manager/debug)) + return + if(attempt_cancel_surgery(current_surgery, target, user)) + return + + if(!isnull(current_surgery) && current_surgery.step_in_progress) + return + + var/list/available_surgeries = get_available_surgeries(user, target) + + var/datum/surgery/procedure + + if(!length(available_surgeries)) + if(target.lying_angle || target.resting || target.stat) + to_chat(user, span_notice("There aren't any surgeries you can perform there right now.")) + else + to_chat(user, span_notice("You can't perform any surgeries there while [target] is standing.")) + return + + // if we have a surgery that should be performed regardless with this item, + // make sure it's available to be done + if(forced_surgery) + for(var/datum/surgery/S in available_surgeries) + if(istype(S, forced_surgery)) + procedure = S + break + else + procedure = tgui_input_list(user, "Begin which procedure?", "Surgery", available_surgeries) + + if(!procedure) + return + + return try_choose_surgery(user, target, procedure) + +/datum/component/surgery_initiator/proc/get_available_surgeries(mob/user, mob/living/target) + var/list/available_surgeries = list() + for(var/datum/surgery/surgery in GLOB.surgeries_list) + if(surgery.abstract && !istype(surgery, forced_surgery)) // no choosing abstract surgeries, though they can be forced + continue + if(!is_type_in_list(target, surgery.target_mobtypes)) + continue + if(length(surgery.target_speciestypes) && !is_type_in_list(target.dna.species, surgery.target_speciestypes)) + continue + if(length(surgery.restricted_speciestypes) && is_type_in_list(target.dna.species, surgery.restricted_speciestypes)) + continue + if(!target.can_run_surgery(surgery, user)) + continue + + available_surgeries |= surgery + + return available_surgeries + +/// Does the surgery de-initiation. +/datum/component/surgery_initiator/proc/attempt_cancel_surgery(datum/surgery/the_surgery, mob/living/patient, mob/user) + var/selected_zone = user.zone_selected + /// We haven't even started yet. Any surgery can be cancelled at this point. + if(the_surgery.step_number == 1) + patient.surgeries -= the_surgery + user.visible_message( + span_notice("[user] stops the surgery on [patient]'s [parse_zone(selected_zone)] with [parent]."), + span_notice("You stop the surgery on [patient]'s [parse_zone(selected_zone)] with [parent]."), + ) + + qdel(the_surgery) + return TRUE + + if(!the_surgery.can_cancel) + return + + // Don't make a forced surgery implement cancel a surgery. + if(istype(the_surgery, forced_surgery)) + return + + var/obj/item/close_tool + var/obj/item/other_hand = user.get_inactive_hand() + + var/is_robotic = !the_surgery.requires_organic_bodypart + var/datum/surgery_step/chosen_close_step + var/skip_surgery = FALSE // if true, don't even run an operation, just end the surgery. + + if(!the_surgery.requires_bodypart) + // special behavior here; if it doesn't require a bodypart just check if there's a limb there or not. + // this is a little bit gross and I do apologize + if(iscarbon(patient)) + var/mob/living/carbon/C = patient + var/obj/item/organ/external/affected = C.get_organ(user.zone_selected) + if(!affected) + skip_surgery = TRUE + + else + // uh there's no reason this should be hit but let's be safe LOL + skip_surgery = TRUE + + if(!skip_surgery) + if(is_robotic) + chosen_close_step = new /datum/surgery_step/robotics/external/close_hatch/premature() + else + chosen_close_step = new /datum/surgery_step/generic/cauterize/premature() + + if(skip_surgery) + close_tool = user.get_active_hand() // sure, just something so that it isn't null + else if(isrobot(user)) + if(!is_robotic) + // borgs need to be able to finish surgeries with just the laser scalpel, no special checks here. + close_tool = parent + else + close_tool = locate(/obj/item/crowbar) in user.get_all_slots() + if(!close_tool) + to_chat(user, span_warning("You need a prying tool in an inactive slot to stop the surgery!")) + return TRUE + + else if(other_hand) + for(var/key in chosen_close_step.allowed_tools) + if(ispath(key) && istype(other_hand, key) || other_hand.tool_behaviour == key) + close_tool = other_hand + break + + if(!close_tool) + to_chat(user, span_warning("You need a [is_robotic ? "prying": "cauterizing"] tool in your inactive hand to stop the surgery!")) + return TRUE + + if(skip_surgery || chosen_close_step.try_op(user, patient, selected_zone, close_tool, the_surgery) == SURGERY_INITIATE_SUCCESS) + // logging in case people wonder why they're cut up inside + log_attack(user, patient, "Prematurely finished \a [the_surgery] surgery.") + qdel(chosen_close_step) + patient.surgeries -= the_surgery + qdel(the_surgery) + + // always return TRUE here so we don't continue the surgery chain and try to start a new surgery with our tool. + return TRUE + +/datum/component/surgery_initiator/proc/can_start_surgery(mob/user, mob/living/target) + if(!user.Adjacent(target)) + return FALSE + + // The item was moved somewhere else + if(!(parent in user)) + to_chat(user, span_warning("You cannot start an operation if you aren't holding the tool anymore.")) + return FALSE + + // While we were choosing, another surgery was started at the same location + for(var/datum/surgery/surgery in target.surgeries) + if(surgery.location == user.zone_selected) + to_chat(user, span_warning("There's already another surgery in progress on their [parse_zone(surgery.location)].")) + return FALSE + + return TRUE + +/datum/component/surgery_initiator/proc/try_choose_surgery(mob/user, mob/living/target, datum/surgery/surgery) + if(!can_start_surgery(user, target)) + return + + var/obj/item/organ/affecting_limb + + var/selected_zone = user.zone_selected + + if(iscarbon(target)) + var/mob/living/carbon/carbon_target = target + affecting_limb = carbon_target.get_organ(check_zone(selected_zone)) + + if(surgery.requires_bodypart == isnull(affecting_limb)) + if(surgery.requires_bodypart) + to_chat(user, span_warning("The patient has no [parse_zone(selected_zone)]!")) + else + to_chat(user, span_warning("The patient has \a [parse_zone(selected_zone)]!")) + + return + + if(!isnull(affecting_limb) && (surgery.is_organ_noncompatible(affecting_limb))) + to_chat(user, span_warning("That's not the right type of limb for this operation!")) + return + + if(surgery.lying_required && !on_operable_surface(target)) + to_chat(user, span_notice("Patient must be lying down for this operation.")) + return + + if(target == user && !surgery.self_operable) + to_chat(user, span_notice("You can't perform that operation on yourself!")) + return + + if(!surgery.can_start(user, target)) + to_chat(user, span_warning("Can't start the surgery!")) + return + + if(surgery_needs_exposure(surgery, target, selected_zone)) + to_chat(user, span_warning("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) + + show_starting_message(user, target, procedure) + + log_attack(user, target, "operated on (OPERATION TYPE: [procedure.name]) (TARGET AREA: [selected_zone])") + +/datum/component/surgery_initiator/proc/surgery_needs_exposure(datum/surgery/surgery, mob/living/target, selected_zone) + return !surgery.ignore_clothes && !get_location_accessible(target, selected_zone) + +/// Handle to allow for easily overriding the message shown +/datum/component/surgery_initiator/proc/show_starting_message(mob/user, mob/living/target, datum/surgery/procedure) + user.visible_message( + span_notice("[user] holds [parent] over [target]'s [parse_zone(user.zone_selected)] to prepare for surgery."), + span_notice("You hold [parent] over [target]'s [parse_zone(user.zone_selected)] to prepare for \an [procedure.name]."), + ) + +/datum/component/surgery_initiator/limb + can_cancel = FALSE // don't let a leg cancel a surgery + +/datum/component/surgery_initiator/robo + valid_starting_types = SURGERY_INITIATOR_ROBOTIC + +/datum/component/surgery_initiator/robo/sharp + valid_starting_types = SURGERY_INITIATOR_ORGANIC | SURGERY_INITIATOR_ROBOTIC diff --git a/code/datums/components/twohanded.dm b/code/datums/components/twohanded.dm index 9178ce59c91..be0717abecf 100644 --- a/code/datums/components/twohanded.dm +++ b/code/datums/components/twohanded.dm @@ -241,7 +241,7 @@ if(sharpening) parent_item.force += sharpening.damage_increase if(sharp_when_wielded) - parent_item.sharp = TRUE + parent_item.set_sharpness(TRUE) var/original_name = parent_item.name parent_item.name = "[original_name] (Wielded)" @@ -308,7 +308,7 @@ else parent_item.force = force_unwielded if(sharp_when_wielded) - parent_item.sharp = FALSE + parent_item.set_sharpness(FALSE) // update the items name to remove the wielded status var/sf = findtext(parent_item.name, " (Wielded)", -10) // 10 == length(" (Wielded)") diff --git a/code/datums/spell_targeting/spell_targeting.dm b/code/datums/spell_targeting/spell_targeting.dm index 611401591cb..94dec5da2ba 100644 --- a/code/datums/spell_targeting/spell_targeting.dm +++ b/code/datums/spell_targeting/spell_targeting.dm @@ -87,5 +87,5 @@ SHOULD_CALL_PARENT(TRUE) return istype(target, allowed_type) && (include_user || target != user) && \ spell.valid_target(target, user) && (!check_if_in_range || (target in view_or_range(range, use_turf_of_user ? get_turf(user) : user, selection_type))) \ - && (!use_obstacle_check || is_path_exist(user, target, PASSTABLE)) + && (!use_obstacle_check || is_path_exist(user, target, PASSTABLE|PASSFENCE)) diff --git a/code/datums/spells/charge_up_bounce.dm b/code/datums/spells/charge_up_bounce.dm index 58e1bf8c5ad..4d245264fd6 100644 --- a/code/datums/spells/charge_up_bounce.dm +++ b/code/datums/spells/charge_up_bounce.dm @@ -69,7 +69,7 @@ if(bounces >= 1) var/list/possible_targets = list() for(var/mob/living/M in view(targeting.range, target)) - if(user == M || target == M || !is_path_exist(target, M, PASSTABLE)) + if(user == M || target == M || !is_path_exist(target, M, PASSTABLE|PASSFENCE)) continue possible_targets += M if(!length(possible_targets)) diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm index b6d1b66acd6..d1a480c1581 100644 --- a/code/datums/weather/weather_types/ash_storm.dm +++ b/code/datums/weather/weather_types/ash_storm.dm @@ -95,8 +95,12 @@ update_audio() /datum/weather/ash_storm/end() - . = ..() + for(var/turf/simulated/floor/plating/asteroid/basalt/basalt as anything in GLOB.dug_up_basalt) + if(!(basalt.loc in impacted_areas) || !(basalt.z in impacted_z_levels)) + continue + basalt.refill_dug() update_audio() + return ..() /datum/weather/ash_storm/proc/is_ash_immune(atom/L) while(L && !isturf(L)) diff --git a/code/game/gamemodes/antag_paradise/antag_paradise.dm b/code/game/gamemodes/antag_paradise/antag_paradise.dm index a84b7a267df..8f53e8e3ee3 100644 --- a/code/game/gamemodes/antag_paradise/antag_paradise.dm +++ b/code/game/gamemodes/antag_paradise/antag_paradise.dm @@ -97,7 +97,7 @@ if(ROLE_MALF_AI) if(special_antag_amount) - var/datum/mind/special_antag = roundstart ? safepick(get_players_for_role(ROLE_MALF_AI)) : safepick(get_alive_players_for_role(ROLE_MALF_AI)) + var/datum/mind/special_antag = roundstart ? safepick(get_players_for_role(ROLE_MALF_AI, req_job_rank = JOB_TITLE_AI)) : safepick(get_alive_players_for_role(ROLE_MALF_AI, req_job_rank = JOB_TITLE_AI)) if(special_antag) special_antag.restricted_roles = (restricted_jobs|protected_jobs|protected_jobs_AI) special_antag.restricted_roles -= JOB_TITLE_AI diff --git a/code/game/gamemodes/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm index 92e20b965ef..acd2919c105 100644 --- a/code/game/gamemodes/blob/blobs/blob_mobs.dm +++ b/code/game/gamemodes/blob/blobs/blob_mobs.dm @@ -194,15 +194,18 @@ nightvision = 8 lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE move_resist = MOVE_FORCE_OVERPOWERING - var/magpulse = 1 -/mob/living/simple_animal/hostile/blob/blobbernaut/mob_negates_gravity() - return magpulse + +/mob/living/simple_animal/hostile/blob/blobbernaut/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_NEGATES_GRAVITY, INNATE_TRAIT) + /mob/living/simple_animal/hostile/blob/blobbernaut/experience_pressure_difference(pressure_difference, direction) - if(!magpulse) + if(!HAS_TRAIT(src, TRAIT_NEGATES_GRAVITY)) return ..() + /mob/living/simple_animal/hostile/blob/blobbernaut/Life(seconds, times_fired) if(stat != DEAD && (getBruteLoss() || getFireLoss())) // Heal on blob structures if(locate(/obj/structure/blob) in get_turf(src)) diff --git a/code/game/gamemodes/cult/blood_magic.dm b/code/game/gamemodes/cult/blood_magic.dm index 184414d4814..fc66de39c03 100644 --- a/code/game/gamemodes/cult/blood_magic.dm +++ b/code/game/gamemodes/cult/blood_magic.dm @@ -660,36 +660,35 @@ /obj/item/melee/blood_magic/empower/afterattack(atom/target, mob/user, proximity_flag, click_parameters) if(proximity_flag) - //Shielded suit + // Shielded suit if(istype(target, /obj/item/clothing/suit/hooded/cultrobes/cult_shield)) - var/obj/item/clothing/suit/hooded/cultrobes/cult_shield/C = target - if(C.current_charges < 3) - uses-- - to_chat(user, "You empower [target] with blood, recharging its shields!") - playsound(user, 'sound/magic/cult_spell.ogg', 25, TRUE) - C.current_charges = 3 - C.shield_state = "shield-cult" - user.update_inv_wear_suit() // The only way a suit can be clicked on is if its on the floor, in the users bag, or on the user, so we will play it safe if it is on the user. - else + var/datum/component/shielded/shield = target.GetComponent(/datum/component/shielded) + if(shield.current_charges >= 3) to_chat(user, "[target] is already at full charge!") return - - //Plasteel to runed metal - else if(istype(target, /obj/item/cult_shift)) + uses-- + to_chat(user, "You empower [target] with blood, recharging its shields!") + playsound(user, 'sound/magic/cult_spell.ogg', 25, TRUE) + shield.current_charges = 3 + user.update_appearance(UPDATE_ICON) + return ..() + + // Plasteel to runed metal + if(istype(target, /obj/item/cult_shift)) var/obj/item/cult_shift/S = target - if(S.uses < 4) - uses-- - to_chat(user, "You empower [target] with blood, recharging its ability to shift!") - playsound(user, 'sound/magic/cult_spell.ogg', 25, TRUE) - S.uses = 4 - S.update_icon(UPDATE_ICON_STATE) - else + if(S.uses >= 4) to_chat(user, "[target] is already at full charge!") return - else - to_chat(user, "The spell will not work on [target]!") - return - ..() + uses-- + to_chat(user, "You empower [target] with blood, recharging its ability to shift!") + playsound(user, 'sound/magic/cult_spell.ogg', 25, TRUE) + S.uses = 4 + S.update_icon(UPDATE_ICON_STATE) + return ..() + + to_chat(user, "The spell will not work on [target]!") + return ..() + //Blood Rite: Absorb blood to heal cult members or summon weapons /obj/item/melee/blood_magic/manipulator diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index 7169468ccc0..c4bc748b9e2 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -168,9 +168,6 @@ body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS allowed = list(/obj/item/tome, /obj/item/melee/cultblade) hoodtype = /obj/item/clothing/head/hooded/cult_hoodie - var/current_charges = 3 - var/shield_state = "shield-cult" - var/shield_on = "shield-cult" species_restricted = null sprite_sheets = list( SPECIES_VULPKANIN = 'icons/mob/clothing/species/vulpkanin/suit.dmi' @@ -197,21 +194,15 @@ user.Confused(20 SECONDS) user.Weaken(10 SECONDS) -/obj/item/clothing/suit/hooded/cultrobes/cult_shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(current_charges) - owner.visible_message("[attack_text] is deflected in a burst of blood-red sparks!") - current_charges-- - playsound(loc, "sparks", 100, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - new /obj/effect/temp_visual/cult/sparks(get_turf(owner)) - if(!current_charges) - owner.visible_message("The runed shield around [owner] suddenly disappears!") - shield_state = "broken" - owner.update_inv_wear_suit() - return TRUE - return FALSE +/obj/item/clothing/suit/hooded/cultrobes/cult_shield/setup_shielding() + AddComponent(/datum/component/shielded, recharge_start_delay = 0 SECONDS, shield_icon_file = 'icons/effects/cult_effects.dmi', shield_icon = "shield-cult", run_hit_callback = CALLBACK(src, PROC_REF(shield_damaged))) -/obj/item/clothing/suit/hooded/cultrobes/cult_shield/special_overlays() - return mutable_appearance('icons/effects/cult_effects.dmi', shield_state, MOB_LAYER + 0.01) +/// A proc for callback when the shield breaks, since cult robes are stupid and have different effects +/obj/item/clothing/suit/hooded/cultrobes/cult_shield/proc/shield_damaged(mob/living/wearer, attack_text, new_current_charges) + wearer.visible_message("[attack_text] is deflected in a burst of blood-red sparks!") + new /obj/effect/temp_visual/cult/sparks(get_turf(wearer)) + if(new_current_charges == 0) + wearer.visible_message("The runed shield around [wearer] suddenly disappears!") /obj/item/clothing/suit/hooded/cultrobes/flagellant_robe name = "flagellant's robes" @@ -432,18 +423,17 @@ flags = NODROP | DROPDEL /obj/item/clothing/head/hooded/culthood/alt/ghost - flags = NODROP | DROPDEL + flags = NODROP -/obj/item/clothing/suit/cultrobesghost +/obj/item/clothing/suit/hooded/cultrobes/alt/ghost name = "ghostly cult robes" desc = "A set of ethereal armored robes worn by the undead followers of a cult." - icon_state = "cultrobesalt" - item_state = "cultrobesalt" body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS allowed = list(/obj/item/tome, /obj/item/melee/cultblade) armor = list(melee = 50, bullet = 30, laser = 50, energy = 20, bomb = 25, bio = 10, rad = 0, fire = 10, acid = 10) flags_inv = HIDEJUMPSUIT flags = NODROP | DROPDEL + hoodtype = /obj/item/clothing/head/hooded/culthood/alt/ghost /obj/item/clothing/shoes/cult/ghost @@ -456,10 +446,10 @@ name = "Cultist Ghost" uniform = /obj/item/clothing/under/color/black/ghost - suit = /obj/item/clothing/suit/cultrobesghost + suit = /obj/item/clothing/suit/hooded/cultrobes/alt/ghost shoes = /obj/item/clothing/shoes/cult/ghost - head = /obj/item/clothing/head/hooded/culthood/alt/ghost r_hand = /obj/item/melee/cultblade/ghost + toggle_helmet = TRUE /obj/item/shield/mirror name = "mirror shield" diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 09597bf85df..98abd884a4d 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -281,7 +281,7 @@ * Returns a list of player minds who had the antagonist role set to yes, regardless of recomended_enemies. * Jobbans and restricted jobs are checked. Species lock and prefered species are checked. List is already shuffled. */ -/datum/game_mode/proc/get_players_for_role(role, list/prefered_species) +/datum/game_mode/proc/get_players_for_role(role, list/prefered_species, req_job_rank) var/list/players = list() var/list/candidates = list() @@ -289,7 +289,7 @@ for(var/mob/new_player/player in GLOB.player_list) if(!player.client || !player.ready || !player.has_valid_preferences() \ || jobban_isbanned(player, "Syndicate") || jobban_isbanned(player, role) \ - || !player_old_enough_antag(player.client, role) || player.client.prefs?.skip_antag \ + || !player_old_enough_antag(player.client, role, req_job_rank) || player.client.prefs?.skip_antag \ || !(role in player.client.prefs.be_special)) continue @@ -317,7 +317,7 @@ /** * Works like get_players_for_role, but for alive mobs. */ -/datum/game_mode/proc/get_alive_players_for_role(role, list/preferred_species) +/datum/game_mode/proc/get_alive_players_for_role(role, list/preferred_species, req_job_rank) var/list/players = list() var/list/candidates = list() @@ -325,7 +325,7 @@ for(var/mob/living/carbon/human/player in GLOB.alive_mob_list) if(!player.client \ || jobban_isbanned(player, "Syndicate") || jobban_isbanned(player, role) \ - || !player_old_enough_antag(player.client, role) || player.client.prefs?.skip_antag \ + || !player_old_enough_antag(player.client, role, req_job_rank) || player.client.prefs?.skip_antag \ || !(role in player.client.prefs.be_special)) continue diff --git a/code/game/gamemodes/miniantags/abduction/abduction_surgery.dm b/code/game/gamemodes/miniantags/abduction/abduction_surgery.dm index a9f835f1b20..c03953e5c17 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction_surgery.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction_surgery.dm @@ -1,23 +1,30 @@ /datum/surgery/organ_extraction name = "Experimental Dissection" - steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/open_encased/saw, /datum/surgery_step/open_encased/retract, /datum/surgery_step/internal/extract_organ, /datum/surgery_step/internal/gland_insert, /datum/surgery_step/glue_bone, /datum/surgery_step/set_bone, /datum/surgery_step/finish_bone, /datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/open_encased/saw, + /datum/surgery_step/open_encased/retract, + /datum/surgery_step/internal/extract_organ, + /datum/surgery_step/internal/gland_insert, + /datum/surgery_step/glue_bone, + /datum/surgery_step/set_bone, + /datum/surgery_step/finish_bone, + /datum/surgery_step/generic/cauterize + ) possible_locs = list(BODY_ZONE_CHEST) + requires_organic_bodypart = TRUE + requires_bodypart = TRUE /datum/surgery/organ_extraction/can_start(mob/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(!ishuman(user)) + . = ..() + if(!.) return FALSE - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(target_zone) - if(!affected) - return FALSE - if(affected.is_robotic()) - return FALSE var/mob/living/carbon/human/H = user // You must either: Be of the abductor species, or contain an abductor implant - if((isabductor(H) || (locate(/obj/item/implant/abductor) in H))) - return TRUE - return FALSE + if(!(isabductor(H) || (locate(/obj/item/implant/abductor) in H))) + return FALSE /datum/surgery_step/internal/extract_organ name = "remove heart" @@ -41,17 +48,17 @@ IC.remove(target, ORGAN_MANIPULATION_NOEFFECT) IC.forceMove(get_turf(target)) user.put_in_hands(IC, ignore_anim = FALSE) - return TRUE + return SURGERY_STEP_CONTINUE if(NO_INTORGANS in AB.dna.species.species_traits) user.visible_message("[user] prepares [target]'s [target_zone] for further dissection!", "You prepare [target]'s [target_zone] for further dissection.") - return TRUE + return SURGERY_STEP_CONTINUE else to_chat(user, "You don't find anything in [target]'s [target_zone]!") - return TRUE + return SURGERY_STEP_CONTINUE /datum/surgery_step/internal/extract_organ/fail_step(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) user.visible_message("[user]'s hand slips, failing to extract anything!", "Your hand slips, failing to extract anything!") - return FALSE + return SURGERY_STEP_RETRY /datum/surgery_step/internal/gland_insert name = "insert gland" @@ -67,35 +74,26 @@ user.drop_from_active_hand() var/obj/item/organ/internal/heart/gland/gland = tool gland.insert(target, ORGAN_MANIPULATION_ABDUCTOR) - return TRUE + return SURGERY_STEP_CONTINUE /datum/surgery_step/internal/gland_insert/fail_step(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) user.visible_message("[user]'s hand slips, failing to insert the gland!", "Your hand slips, failing to insert the gland!") - return FALSE + return SURGERY_STEP_RETRY //IPC Gland Surgery// /datum/surgery/organ_extraction/synth name = "Experimental Robotic Dissection" - steps = list(/datum/surgery_step/robotics/external/unscrew_hatch,/datum/surgery_step/robotics/external/open_hatch,/datum/surgery_step/internal/extract_organ/synth,/datum/surgery_step/internal/gland_insert,/datum/surgery_step/robotics/external/close_hatch) + steps = list( + /datum/surgery_step/robotics/external/unscrew_hatch, + /datum/surgery_step/robotics/external/open_hatch, + /datum/surgery_step/internal/extract_organ/synth, + /datum/surgery_step/internal/gland_insert, + /datum/surgery_step/robotics/external/close_hatch + ) possible_locs = list(BODY_ZONE_CHEST) - requires_organic_bodypart = 0 - -/datum/surgery/organ_extraction/synth/can_start(mob/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(!ishuman(user)) - return FALSE - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(target_zone) - if(!affected) - return FALSE - if(!affected.is_robotic()) - return FALSE - var/mob/living/carbon/human/H = user - // You must either: Be of the abductor species, or contain an abductor implant - if((isabductor(H) || (locate(/obj/item/implant/abductor) in H))) - return TRUE - return FALSE + requires_organic_bodypart = FALSE + requires_bodypart = TRUE /datum/surgery_step/internal/extract_organ/synth name = "remove cell" diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index cc99ba3825a..73b57f000db 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -12,7 +12,7 @@ /datum/game_mode/revolution name = "revolution" config_tag = "revolution" - restricted_jobs = list(JOB_TITLE_OFFICER, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_LAWYER, JOB_TITLE_AI, JOB_TITLE_CYBORG, JOB_TITLE_CAPTAIN, JOB_TITLE_HOP, JOB_TITLE_HOS, JOB_TITLE_CHIEF, JOB_TITLE_RD, JOB_TITLE_CMO, JOB_TITLE_BLUESHIELD, JOB_TITLE_REPRESENTATIVE, JOB_TITLE_PILOT, JOB_TITLE_JUDGE, JOB_TITLE_BRIGDOC) + restricted_jobs = list(JOB_TITLE_OFFICER, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_LAWYER, JOB_TITLE_AI, JOB_TITLE_CYBORG, JOB_TITLE_CAPTAIN, JOB_TITLE_HOP, JOB_TITLE_QUARTERMASTER, JOB_TITLE_HOS, JOB_TITLE_CHIEF, JOB_TITLE_RD, JOB_TITLE_CMO, JOB_TITLE_BLUESHIELD, JOB_TITLE_REPRESENTATIVE, JOB_TITLE_PILOT, JOB_TITLE_JUDGE, JOB_TITLE_BRIGDOC) required_players = 20 required_enemies = 1 recommended_enemies = 3 @@ -25,7 +25,7 @@ /////////////////////////// /datum/game_mode/revolution/announce() to_chat(world, "The current game mode is - Revolution!") - to_chat(world, "Some crewmembers are attempting to start a revolution!
\nRevolutionaries - Kill the Captain, HoP, HoS, CE, RD and CMO. Involve other employees (excluding the heads of staff, and security officers) in to the revolution. Protect your leaders.
\nPersonnel - Protect the heads of staff. Kill the leaders of the revolution, and brainwash the other revolutionaries (by implantiong them with mindshield implants).
") + to_chat(world, "Some crewmembers are attempting to start a revolution!
\nRevolutionaries - Kill the Captain, HoP, HoS, QM, CE, RD and CMO. Involve other employees (excluding the heads of staff, and security officers) in to the revolution. Protect your leaders.
\nPersonnel - Protect the heads of staff. Kill the leaders of the revolution, and brainwash the other revolutionaries (by implantiong them with mindshield implants).
") /////////////////////////////////////////// //Магический спелл для приглашения в реву// @@ -336,7 +336,7 @@ for(var/thing in GLOB.human_list) var/mob/living/carbon/human/player = thing if(player.stat == DEAD && player.mind?.assigned_role) - if(player.mind.assigned_role in list(JOB_TITLE_CAPTAIN, JOB_TITLE_HOS, JOB_TITLE_HOP, JOB_TITLE_CHIEF, JOB_TITLE_RD)) + if(player.mind.assigned_role in list(JOB_TITLE_CAPTAIN, JOB_TITLE_HOS, JOB_TITLE_HOP, JOB_TITLE_QUARTERMASTER, JOB_TITLE_CHIEF, JOB_TITLE_RD, JOB_TITLE_CMO)) scoreboard.score_dead_command++ @@ -367,7 +367,7 @@ var/mob/living/carbon/human/player = thing if(player.mind) var/role = player.mind.assigned_role - if(role in list(JOB_TITLE_CAPTAIN, JOB_TITLE_HOS, JOB_TITLE_HOP, JOB_TITLE_CHIEF, JOB_TITLE_RD)) + if(role in list(JOB_TITLE_CAPTAIN, JOB_TITLE_HOS, JOB_TITLE_HOP, JOB_TITLE_QUARTERMASTER, JOB_TITLE_CHIEF, JOB_TITLE_RD, JOB_TITLE_CMO)) if(player.stat != DEAD) comcount++ else diff --git a/code/game/gamemodes/steal_items.dm b/code/game/gamemodes/steal_items.dm index 2a58e0ddbe3..21d49f19644 100644 --- a/code/game/gamemodes/steal_items.dm +++ b/code/game/gamemodes/steal_items.dm @@ -555,7 +555,7 @@ GLOBAL_LIST_INIT(ungibbable_items_types, get_ungibbable_items_types()) id = "animal_paperwork" typepath = /mob/living/simple_animal/pet/sloth/paperwork name = "ленивца по кличке Paperwork" - protected_jobs = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_HOP, JOB_TITLE_CARGOTECH) + protected_jobs = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH) /datum/theft_objective/animal/slugcat id = "animal_slugcat" @@ -591,7 +591,7 @@ GLOBAL_LIST_INIT(ungibbable_items_types, get_ungibbable_items_types()) id = "animal_mars" typepath = /mob/living/simple_animal/hostile/gorilla/cargo_domestic/mars name = "гориллу по кличке Марс" - protected_jobs = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_HOP, JOB_TITLE_CARGOTECH) + protected_jobs = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH) //========================== @@ -785,6 +785,7 @@ GLOBAL_LIST_INIT(ungibbable_items_types, get_ungibbable_items_types()) /obj/item/encryptionkey/heads/ce, /obj/item/encryptionkey/heads/cmo, /obj/item/encryptionkey/heads/hop, + /obj/item/encryptionkey/heads/qm, /obj/item/encryptionkey/heads/ntrep, /obj/item/encryptionkey/heads/magistrate, /obj/item/encryptionkey/heads/blueshield, diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index dfe88ef3776..6a651c2aeef 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -47,7 +47,7 @@ restricted_jobs += protected_jobs var/list/possible_traitors = get_players_for_role(ROLE_TRAITOR) - var/list/possible_malfs = get_players_for_role(ROLE_MALF_AI) + var/list/possible_malfs = get_players_for_role(ROLE_MALF_AI, req_job_rank = JOB_TITLE_AI) var/malf_AI_candidate if(length(possible_malfs)) diff --git a/code/game/jobs/job/supervisor.dm b/code/game/jobs/job/supervisor.dm index 490140dc01b..481ef1f110c 100644 --- a/code/game/jobs/job/supervisor.dm +++ b/code/game/jobs/job/supervisor.dm @@ -76,14 +76,14 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, - ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, - ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, ACCESS_CLOWN, ACCESS_MIME, ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, - ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, - ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, ACCESS_CLOWN, ACCESS_MIME, ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) money_factor = 9 outfit = /datum/outfit/job/hop diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm index 864cf8b5871..2de09e7e28b 100644 --- a/code/game/jobs/job/support.dm +++ b/code/game/jobs/job/support.dm @@ -6,11 +6,11 @@ total_positions = 1 spawn_positions = 1 is_supply = 1 - supervisors = "the head of personnel" - department_head = list(JOB_TITLE_HOP) + supervisors = "the captain" + department_head = list(JOB_TITLE_CAPTAIN) selection_color = "#9f8545" - access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) + access = list(ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_HEADS_VAULT, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_HEADS, ACCESS_SEC_DOORS, ACCESS_EVA, ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_HEADS_VAULT, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_HEADS, ACCESS_SECURITY, ACCESS_EVA, ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) min_age_allowed = 30 exp_requirements = 3000 exp_type = EXP_TYPE_CREW @@ -23,7 +23,7 @@ uniform = /obj/item/clothing/under/rank/cargo shoes = /obj/item/clothing/shoes/brown - l_ear = /obj/item/radio/headset/headset_cargo + l_ear = /obj/item/radio/headset/heads/qm glasses = /obj/item/clothing/glasses/sunglasses l_pocket = /obj/item/lighter/zippo/qm id = /obj/item/card/id/qm @@ -44,7 +44,7 @@ spawn_positions = 2 is_supply = 1 supervisors = "the quartermaster" - department_head = list(JOB_TITLE_HOP) + department_head = list(JOB_TITLE_QUARTERMASTER) selection_color = "#e2dbc8" access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM) @@ -71,7 +71,7 @@ spawn_positions = 8 is_supply = 1 supervisors = "the quartermaster" - department_head = list(JOB_TITLE_HOP) + department_head = list(JOB_TITLE_QUARTERMASTER) selection_color = "#e2dbc8" access = list(ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) minimal_access = list(ACCESS_MINING, ACCESS_MINT, ACCESS_MINING_STATION, ACCESS_MAILSORTING, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm index 757d383b3fb..18ffb0318fa 100644 --- a/code/game/jobs/jobs.dm +++ b/code/game/jobs/jobs.dm @@ -6,6 +6,7 @@ GLOBAL_LIST_INIT(command_positions, list( JOB_TITLE_CAPTAIN, JOB_TITLE_HOP, JOB_TITLE_HOS, + JOB_TITLE_QUARTERMASTER, JOB_TITLE_CHIEF, JOB_TITLE_RD, JOB_TITLE_CMO, @@ -83,13 +84,12 @@ GLOBAL_LIST_INIT(support_positions, list( )) GLOBAL_LIST_INIT(supply_positions, list( - JOB_TITLE_HOP, JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH, JOB_TITLE_MINER )) -GLOBAL_LIST_INIT(service_positions, (list(JOB_TITLE_HOP) + (support_positions - supply_positions))) +GLOBAL_LIST_INIT(service_positions, (support_positions - supply_positions)) GLOBAL_LIST_INIT(civilian_positions, list( JOB_TITLE_CIVILIAN diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index a8f26402c75..4c3a6828d40 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -8,7 +8,7 @@ use_power = IDLE_POWER_USE idle_power_usage = 1 active_power_usage = 5 - var/mob/living/carbon/human/patient + var/mob/living/carbon/patient var/obj/machinery/computer/operating/computer buckle_lying = -1 var/no_icon_updates = FALSE //set this to TRUE if you don't want the icons ever changing @@ -49,9 +49,9 @@ * Updates the `patient` var to be the mob occupying the table */ /obj/machinery/optable/proc/update_patient() - var/mob/living/carbon/human/M = locate(/mob/living/carbon/human, loc) - if(M && M.lying_angle) - patient = M + var/mob/living/carbon/patient_carbon = locate(/mob/living/carbon, loc) + if(patient_carbon && patient_carbon.lying_angle) + patient = patient_carbon else patient = null if(!no_icon_updates) @@ -98,7 +98,7 @@ set name = "Climb On Table" set category = "Object" set src in oview(1) - if(usr.stat || !ishuman(usr) || usr.restrained() || !check_table()) + if(usr.stat || !iscarbon(usr) || usr.restrained() || !check_table()) return take_patient(usr, usr) diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index 33d7da32b0e..a84083f375b 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -63,7 +63,7 @@ /obj/machinery/computer/operating/ui_data(mob/user) var/list/data = list() - var/mob/living/carbon/human/occupant + var/mob/living/carbon/occupant if(table) occupant = table.patient data["hasOccupant"] = occupant ? 1 : 0 @@ -110,31 +110,42 @@ occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 if(ishuman(occupant) && !(NO_BLOOD in occupant.dna.species.species_traits)) + var/mob/living/carbon/human/H = occupant occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) occupantData["hasBlood"] = 1 occupantData["bloodLevel"] = round(occupant.blood_volume) - occupantData["bloodMax"] = occupant.max_blood - occupantData["bloodPercent"] = round(100*(occupant.blood_volume/occupant.max_blood), 0.01) //copy pasta ends here + occupantData["bloodMax"] = H.max_blood + occupantData["bloodPercent"] = round(100*(occupant.blood_volume/H.max_blood), 0.01) //copy pasta ends here occupantData["bloodType"] = occupant.dna.blood_type - if(occupant.surgeries.len) + if(length(occupant.surgeries)) occupantData["inSurgery"] = 1 occupantData["surgeries"] = list() for(var/datum/surgery/procedure in occupant.surgeries) var/datum/surgery_step/surgery_step = procedure.get_surgery_step() + var/list/surgery_desc = list("[capitalize(surgery_step.get_step_information(procedure))]") + if(surgery_step.repeatable) + var/datum/surgery_step/next = procedure.get_surgery_next_step() + if(next) + surgery_desc += " or [capitalize(next.get_step_information(procedure))]" + var/obj/item/organ/organ + if(ishuman(occupant)) + var/mob/living/carbon/human/H = occupant + organ = H.bodyparts_by_name[procedure.location] occupantData["surgeries"] += list(list( - "bodypartName" = capitalize(procedure.location), + "bodypartName" = capitalize(organ?.name || procedure.location), "surgeryName" = capitalize(procedure.name), - "stepName" = capitalize(surgery_step.name) + "stepName" = surgery_desc.Join("") )) + data["occupant"] = occupantData - data["verbose"]=verbose - data["oxyAlarm"]=oxyAlarm - data["choice"]=choice - data["health"]=healthAnnounce - data["crit"]=crit - data["healthAlarm"]=healthAlarm - data["oxy"]=oxy + data["verbose"] = verbose + data["oxyAlarm"] = oxyAlarm + data["choice"] = choice + data["health"] = healthAnnounce + data["crit"] = crit + data["healthAlarm"] = healthAlarm + data["oxy"] = oxy return data @@ -197,7 +208,12 @@ if(isNewPatient) atom_say("Обнаружен новый пациент, загрузка показаний") - atom_say("[table.patient], группа крови [table.patient.dna.blood_type], [patientStatus]") + var/blood_type_msg + if(ishuman(table.patient)) + blood_type_msg = table.patient.dna.blood_type + else + blood_type_msg = "\[ОШИБКА: НЕИЗВЕСТНО\]" + atom_say("[table.patient], группа крови [blood_type_msg], [patientStatus]") SStgui.update_uis(src) patientStatusHolder = table.patient.stat currentPatient = table.patient diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index a45a05b872a..88c9f69c0f4 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -151,6 +151,11 @@ build_path = /obj/machinery/computer/card/minor/cmo target_dept = TARGET_DEPT_MED +/obj/item/circuitboard/card/minor/qm + board_name = "Supply ID Computer" + build_path = /obj/machinery/computer/card/minor/qm + target_dept = TARGET_DEPT_SUP + /obj/item/circuitboard/card/minor/rd board_name = "Science ID Computer" build_path = /obj/machinery/computer/card/minor/rd diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index b5000c6542f..982580bac54 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -43,6 +43,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) /datum/job/cyborg, /datum/job/captain, /datum/job/hop, + /datum/job/qm, /datum/job/hos, /datum/job/chief_engineer, /datum/job/rd, @@ -771,6 +772,14 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) req_access = list(ACCESS_HOS) circuit = /obj/item/circuitboard/card/minor/hos +/obj/machinery/computer/card/minor/qm + name = "supply management console" + target_dept = TARGET_DEPT_SUP + icon_screen = "idqm" + light_color = COLOR_BROWN_ORANGE + req_access = list(ACCESS_QM) + circuit = /obj/item/circuitboard/card/minor/qm + /obj/machinery/computer/card/minor/cmo name = "medical management console" target_dept = TARGET_DEPT_MED diff --git a/code/game/machinery/tcomms/core.dm b/code/game/machinery/tcomms/core.dm index 5a7570a6d7e..224cb022923 100644 --- a/code/game/machinery/tcomms/core.dm +++ b/code/game/machinery/tcomms/core.dm @@ -36,6 +36,14 @@ . = ..() link_password = GenerateKey() reachable_zlevels |= loc.z + var/turf/above = GET_TURF_ABOVE(loc) + while(above) + reachable_zlevels |= above.z + above = GET_TURF_ABOVE(above) + var/turf/below = GET_TURF_BELOW(loc) + while(below) + reachable_zlevels |= below.z + below = GET_TURF_BELOW(below) component_parts += new /obj/item/circuitboard/tcomms/core(null) if(check_power_on()) active = TRUE @@ -121,6 +129,15 @@ reachable_zlevels = list() // Add itself as a reachable Z-level reachable_zlevels |= loc.z + // add adjacent zlevels above and below + var/turf/above = GET_TURF_ABOVE(loc) + while(above) + reachable_zlevels |= above.z + above = GET_TURF_ABOVE(above) + var/turf/below = GET_TURF_BELOW(loc) + while(below) + reachable_zlevels |= below.z + below = GET_TURF_BELOW(below) // Add all the linked relays in for(var/obj/machinery/tcomms/relay/R in linked_relays) // Only if the relay is active diff --git a/code/game/machinery/tcomms/nttc.dm b/code/game/machinery/tcomms/nttc.dm index 6c70f5f7862..57fd7d7e198 100644 --- a/code/game/machinery/tcomms/nttc.dm +++ b/code/game/machinery/tcomms/nttc.dm @@ -87,7 +87,7 @@ JOB_TITLE_MIME = "srvradio", ) /// List of Command jobs - var/list/heads = list(JOB_TITLE_CAPTAIN, JOB_TITLE_HOP, JOB_TITLE_REPRESENTATIVE, JOB_TITLE_BLUESHIELD, JOB_TITLE_CHIEF, JOB_TITLE_CMO, JOB_TITLE_RD, JOB_TITLE_HOS, JOB_TITLE_JUDGE, JOB_TITLE_AI, "Syndicate Research Director", "Syndicate Comms Officer") + var/list/heads = list(JOB_TITLE_CAPTAIN, JOB_TITLE_HOP, JOB_TITLE_QUARTERMASTER, JOB_TITLE_REPRESENTATIVE, JOB_TITLE_BLUESHIELD, JOB_TITLE_CHIEF, JOB_TITLE_CMO, JOB_TITLE_RD, JOB_TITLE_HOS, JOB_TITLE_JUDGE, JOB_TITLE_AI, "Syndicate Research Director", "Syndicate Comms Officer") /// List of ERT jobs var/list/ert_jobs = list("Emergency Response Team Officer", "Emergency Response Team Engineer", "Emergency Response Team Medic", "Emergency Response Team Leader", "Emergency Response Team Member") /// List of CentComm jobs diff --git a/code/game/objects/effects/mapping_helpers.dm b/code/game/objects/effects/mapping_helpers.dm index 48d1f34a833..d62bb678996 100644 --- a/code/game/objects/effects/mapping_helpers.dm +++ b/code/game/objects/effects/mapping_helpers.dm @@ -8,10 +8,16 @@ /obj/effect/baseturf_helper/Initialize(mapload) . = ..() + return INITIALIZE_HINT_LATELOAD + +/obj/effect/baseturf_helper/LateInitialize() + initialize_replacements() + +/obj/effect/baseturf_helper/proc/initialize_replacements() var/area/thearea = get_area(src) for(var/turf/T in get_area_turfs(thearea, z)) replace_baseturf(T) - return INITIALIZE_HINT_QDEL + qdel(src) /obj/effect/baseturf_helper/proc/replace_baseturf(turf/thing) if(thing.baseturf != thing.type) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index b0fcf0c41e4..64b6121f5b1 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -485,10 +485,10 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/g /obj/item/proc/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - SEND_SIGNAL(src, COMSIG_ITEM_HIT_REACT, args) - if(prob(final_block_chance)) + var/signal_result = (SEND_SIGNAL(src, COMSIG_ITEM_HIT_REACT, owner, hitby, damage, attack_type) & COMPONENT_BLOCK_SUCCESSFUL) + prob(final_block_chance) + if(signal_result != 0) owner.visible_message(span_danger("[owner] blocks [attack_text] with [src]!")) - return TRUE + return signal_result return FALSE diff --git a/code/game/objects/items/control_wand.dm b/code/game/objects/items/control_wand.dm index 955495c33a2..9080e7e98ba 100644 --- a/code/game/objects/items/control_wand.dm +++ b/code/game/objects/items/control_wand.dm @@ -192,9 +192,9 @@ region_access = list(REGION_MEDBAY) /obj/item/door_remote/civillian - name = "civillian door remote" + name = "civilian door remote" icon_state = "gangtool-white" - region_access = list(REGION_GENERAL, REGION_SUPPLY) + region_access = list(REGION_GENERAL) additional_access = list(ACCESS_HOP) /obj/item/door_remote/centcomm diff --git a/code/game/objects/items/devices/autopsy.dm b/code/game/objects/items/devices/autopsy.dm index 9042d5eda79..f2f101fe992 100644 --- a/code/game/objects/items/devices/autopsy.dm +++ b/code/game/objects/items/devices/autopsy.dm @@ -154,7 +154,7 @@ if(!istype(M)) return - if(!can_operate(M)) + if(!on_operable_surface(M)) return if(target_UID != M.UID()) diff --git a/code/game/objects/items/devices/handheld_defib.dm b/code/game/objects/items/devices/handheld_defib.dm index a0fe0173d78..8c6a4d523b3 100644 --- a/code/game/objects/items/devices/handheld_defib.dm +++ b/code/game/objects/items/devices/handheld_defib.dm @@ -54,11 +54,9 @@ if(!istype(H)) return ..() if(istype(I, /obj/item/clothing/suit/space) && !shield_ignore) - blocked = TRUE if(istype(I, /obj/item/clothing/suit/space/hardsuit)) - var/obj/item/clothing/suit/space/hardsuit/HardS = I - if(HardS.shield) - HardS.shield.hit_reaction(user, src, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + var/obj/item/clothing/suit/space/hardsuit/hardsuit = I + blocked = hardsuit.hit_reaction(user, src, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) if(cooldown) to_chat(user, span_warning("[src] is still charging!")) return diff --git a/code/game/objects/items/devices/radio/beacon.dm b/code/game/objects/items/devices/radio/beacon.dm index 992d4ab7b95..d36145af389 100644 --- a/code/game/objects/items/devices/radio/beacon.dm +++ b/code/game/objects/items/devices/radio/beacon.dm @@ -283,9 +283,9 @@ /obj/item/radio/beacon/syndicate/bundle/attack_self(mob/user) if(!user) return - var/bundle_name = tgui_input_list(user, "Available Bundles", "Bundle Selection", selected) used = TRUE - if(!bundle_name) + var/bundle_name = tgui_input_list(user, "Available Bundles", "Bundle Selection", selected) + if(!bundle_name || QDELING(user) || QDELING(src)) return if(bundle_name == "Random") bundle_name = pick(unselected) diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm index 5e473c09092..d515691453d 100644 --- a/code/game/objects/items/devices/radio/encryptionkey.dm +++ b/code/game/objects/items/devices/radio/encryptionkey.dm @@ -142,7 +142,12 @@ /obj/item/encryptionkey/heads/hop name = "Head of Personnel's Encryption Key" icon_state = "hop_cypherkey" - channels = list("Supply" = 1, "Service" = 1, "Security" = 0, "Command" = 1) + channels = list("Service" = 1, "Security" = 0, "Command" = 1) + +/obj/item/encryptionkey/heads/qm + name = "Quartermaster's Encryption Key" + icon_state = "cargo_cypherkey" + channels = list("Supply" = 1, "Command" = 1) /obj/item/encryptionkey/heads/ntrep name = "Nanotrasen Representative's Encryption Key" @@ -164,11 +169,6 @@ name = "Mining Radio Encryption Key" icon_state = "mine_cypherkey" channels = list("Mining" = 1) - -/obj/item/encryptionkey/heads/qm - name = "Quartermaster's Encryption Key" - icon_state = "qm_cypherkey" - channels = list("Cargo" = 1, "Mining" = 1) */ /obj/item/encryptionkey/headset_cargo name = "Supply Radio Encryption Key" diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index 6bb441b021b..7b2630682db 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -290,6 +290,13 @@ item_state = "headset" ks2type = /obj/item/encryptionkey/heads/hop +/obj/item/radio/headset/heads/qm + name = "quartermaster's headset" + desc = "Smelling of tobacco and gunpowder, this headset has likely seen many backroom deals." + icon_state = "com_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/heads/qm + /obj/item/radio/headset/headset_cargo name = "supply radio headset" desc = "A cheap model of working modular intercom headset for a cargo, that fits over the head. Takes encryption keys. Won't protect ears from flashbangs and loud noises." diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index 4f620091b3e..c44fbd3eb95 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -23,6 +23,8 @@ else name = "robot [initial(name)]" + AddComponent(/datum/component/surgery_initiator/limb, forced_surgery = /datum/surgery/attach_robotic_limb) + /obj/item/robot_parts/attack_self(mob/user) var/choice = tgui_input_list(user, "Select the company appearance for this limb", "Limb Company Selection", GLOB.selectable_robolimbs) if(!choice) diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index caaacd02cd6..39a935add00 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -162,17 +162,17 @@ if(!..()) return FALSE - if(robot.magpulse) + if(HAS_TRAIT_FROM(robot, TRAIT_NEGATES_GRAVITY, ROBOT_TRAIT)) return FALSE - robot.magpulse = TRUE + ADD_TRAIT(robot, TRAIT_NEGATES_GRAVITY, ROBOT_TRAIT) return TRUE /obj/item/borg/upgrade/magboots/deactivate(mob/living/silicon/robot/robot, mob/user) if(!..()) return FALSE - robot.magpulse = initial(robot.magpulse) + REMOVE_TRAIT(robot, TRAIT_NEGATES_GRAVITY, ROBOT_TRAIT) return TRUE /obj/item/borg/upgrade/disablercooler diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 24dae468401..4d34ca6312e 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -147,7 +147,7 @@ var/mob/living/carbon/human/H = M var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected) - if(affecting.open == FALSE) + if(affecting.open == ORGAN_CLOSED) affecting.germ_level = 0 if(stop_bleeding) @@ -208,7 +208,7 @@ var/mob/living/carbon/human/H = M var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected) - if(affecting.open == FALSE) + if(affecting.open == ORGAN_CLOSED) affecting.germ_level = 0 heal(H, user) diff --git a/code/game/objects/items/tools/crowbar.dm b/code/game/objects/items/tools/crowbar.dm index f0d636716c6..73463fb0ae8 100644 --- a/code/game/objects/items/tools/crowbar.dm +++ b/code/game/objects/items/tools/crowbar.dm @@ -86,6 +86,10 @@ ru_names = list(NOMINATIVE = "челюсти жизни", GENITIVE = "челюстей жизни", DATIVE = "челюстям жизни", ACCUSATIVE = "челюсти жизни", INSTRUMENTAL = "челюстями жизни", PREPOSITIONAL = "челюстях жизни") var/airlock_open_time = 100 // Time required to open powered airlocks +/obj/item/crowbar/power/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_ADVANCED_SURGICAL, ROUNDSTART_TRAIT) + /obj/item/crowbar/power/suicide_act(mob/user) user.visible_message("[user] помеща[pluralize_ru(user.gender,"ет","ют")] свою голову между лезвиями [src.declent_ru(GENITIVE)]. Похоже, [genderize_ru(user.gender,"он","она","оно","они")] пыта[pluralize_ru(user.gender,"ется","ются")] использовать [src.declent_ru(ACCUSATIVE)] для самоубийства!") playsound(loc, 'sound/items/jaws_pry.ogg', 50, 1, -1) diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/screwdriver.dm index bb4f6188106..8e990ac585a 100644 --- a/code/game/objects/items/tools/screwdriver.dm +++ b/code/game/objects/items/tools/screwdriver.dm @@ -23,6 +23,10 @@ tool_behaviour = TOOL_SCREWDRIVER var/random_color = TRUE //if the screwdriver uses random coloring +/obj/item/screwdriver/Initialize(mapload) + . = ..() + AddComponent(/datum/component/surgery_initiator/robo) + /obj/item/screwdriver/nuke name = "screwdriver" desc = "A screwdriver with an ultra thin tip." @@ -93,6 +97,10 @@ toolspeed = 0.25 random_color = FALSE +/obj/item/screwdriver/power/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_ADVANCED_SURGICAL, ROUNDSTART_TRAIT) + /obj/item/screwdriver/power/suicide_act(mob/user) user.visible_message("[user] is putting [src] to [user.p_their()] temple. It looks like [user.p_theyre()] trying to commit suicide!") return BRUTELOSS diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index 6ed07d65d04..ec533d69a85 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -164,6 +164,7 @@ item_color = null w_class = WEIGHT_CLASS_NORMAL light_color = LIGHT_COLOR_WHITE + tool_behaviour = TOOL_SAW /obj/item/melee/energy/sword/cyborg/saw/New() ..() diff --git a/code/game/objects/items/weapons/whetstone.dm b/code/game/objects/items/weapons/whetstone.dm index 8ea39908dd0..969e36f1d7b 100644 --- a/code/game/objects/items/weapons/whetstone.dm +++ b/code/game/objects/items/weapons/whetstone.dm @@ -38,7 +38,7 @@ return if(!requires_sharpness) - I.sharp = TRUE + set_sharpness(TRUE) user.visible_message(span_warning("[user] sharpens [I] with [src]!"), \ span_warning("You sharpen [I], making it much more deadly than before.")) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index a8f53ddccb3..77449d723f8 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -54,6 +54,8 @@ armor = getArmor() else if(!istype(armor, /datum/armor)) stack_trace("Invalid type [armor.type] found in .armor during /obj Initialize()") + if(sharp) + AddComponent(/datum/component/surgery_initiator) /obj/Topic(href, href_list, nowindow = FALSE, datum/ui_state/state = GLOB.default_state) // Calling Topic without a corresponding window open causes runtime errors @@ -290,6 +292,16 @@ //just override it to return TRUE in your object if you want to use it through spawn menu return +/// Set whether the item should be sharp or not +/obj/proc/set_sharpness(new_sharp_val) + if(sharp == new_sharp_val) + return + sharp = new_sharp_val + SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_SHARPNESS) + if(!sharp && new_sharp_val) + AddComponent(/datum/component/surgery_initiator) + + /obj/proc/force_eject_occupant(mob/target) // This proc handles safely removing occupant mobs from the object if they must be teleported out (due to being SSD/AFK, by admin teleport, etc) or transformed. // In the event that the object doesn't have an overriden version of this proc to do it, log a runtime so one can be added. diff --git a/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm b/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm index 072f7dfe36b..5252ad65008 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm @@ -22,6 +22,7 @@ /obj/structure/closet/secure_closet/quartermaster/populate_contents() new /obj/item/storage/backpack/cargo(src) new /obj/item/radio/headset/headset_cargo(src) + new /obj/item/radio/headset/heads/qm(src) new /obj/item/tank/internals/emergency_oxygen(src) new /obj/item/clothing/mask/gas(src) new /obj/item/clothing/glasses/meson(src) diff --git a/code/game/turfs/simulated/floor/asteroid.dm b/code/game/turfs/simulated/floor/asteroid.dm index 405fdb9a91c..26bb77c6115 100644 --- a/code/game/turfs/simulated/floor/asteroid.dm +++ b/code/game/turfs/simulated/floor/asteroid.dm @@ -26,9 +26,8 @@ /turf/simulated/floor/plating/asteroid/proc/getDug() new digResult(src, 5) - icon_plating = "[environment_type]_dug" - icon_state = "[environment_type]_dug" dug = TRUE + update_icon(UPDATE_ICON_STATE) /turf/simulated/floor/plating/asteroid/proc/can_dig(mob/user) if(!dug) @@ -36,6 +35,23 @@ if(user) to_chat(user, span_notice("Looks like someone has dug here already.")) +///Refills the previously dug tile +/turf/simulated/floor/plating/asteroid/proc/refill_dug() + dug = FALSE + update_icon(UPDATE_ICON_STATE) + +/turf/simulated/floor/plating/asteroid/update_icon_state() + if(dug) + icon_plating = "[environment_type]_dug" + icon_state = "[environment_type]_dug" + else + icon_plating = initial(icon_plating) + if(prob(floor_variance)) + icon_state = "[environment_type][rand(0,12)]" + else + icon_state = initial(icon_state) + + /turf/simulated/floor/plating/asteroid/try_replace_tile(obj/item/stack/tile/T, mob/user, params) return @@ -110,6 +126,9 @@ /turf/simulated/floor/plating/asteroid/welder_act(mob/user, obj/item/I) return +/// Used by ashstorms to replenish basalt tiles that have been dug up without going through all of them. +GLOBAL_LIST_EMPTY(dug_up_basalt) + /turf/simulated/floor/plating/asteroid/basalt name = "volcanic floor" baseturf = /turf/simulated/floor/plating/asteroid/basalt @@ -119,6 +138,15 @@ floor_variance = 15 digResult = /obj/item/stack/ore/glass/basalt +/turf/simulated/floor/plating/asteroid/basalt/refill_dug() + . = ..() + GLOB.dug_up_basalt -= src + set_basalt_light(src) + +/turf/simulated/floor/plating/asteroid/basalt/Destroy() + GLOB.dug_up_basalt -= src + return ..() + /turf/simulated/floor/plating/asteroid/basalt/lava //lava underneath baseturf = /turf/simulated/floor/plating/lava/smooth @@ -142,6 +170,7 @@ /turf/simulated/floor/plating/asteroid/basalt/getDug() set_light_on(FALSE) + GLOB.dug_up_basalt |= src return ..() /proc/set_basalt_light(turf/simulated/floor/B) diff --git a/code/game/turfs/simulated/floor/chasm.dm b/code/game/turfs/simulated/floor/chasm.dm index 372d0af65a3..42ec1d3c2bb 100644 --- a/code/game/turfs/simulated/floor/chasm.dm +++ b/code/game/turfs/simulated/floor/chasm.dm @@ -110,7 +110,7 @@ new /obj/structure/lattice/catwalk/fireproof(src) if(istype(C, /obj/item/twohanded/fishingrod)) var/obj/item/twohanded/fishingrod/rod = C - if(!rod.wielded) + if(!HAS_TRAIT(rod, TRAIT_WIELDED)) to_chat(user, span_warning("You need to wield the rod in both hands before you can fish in the chasm!")) return user.visible_message(span_warning("[user] throws a fishing rod into the chasm and tries to catch something!"), @@ -118,10 +118,11 @@ span_notice("You hear the sound of a fishing rod.")) playsound(rod, 'sound/effects/fishing_rod_throw.ogg', 30) if(do_after(user, 6 SECONDS, target = src)) - if(!rod.wielded) + if(!HAS_TRAIT(rod, TRAIT_WIELDED)) return - var/atom/parent = src - var/list/fishing_contents = parent.GetAllContents() + var/list/fishing_contents = list() + for(var/turf/simulated/floor/chasm/chasm in range(4, src)) + fishing_contents += chasm.GetAllContents() if(!length(fishing_contents)) to_chat(user, span_warning("There's nothing here!")) return @@ -130,6 +131,7 @@ M.forceMove(get_turf(user)) UnregisterSignal(M, COMSIG_LIVING_REVIVE) found = TRUE + break if(found) to_chat(user, span_warning("You reel in something!")) playsound(rod, 'sound/effects/fishing_rod_catch.ogg', 30) @@ -172,8 +174,7 @@ return FALSE if(ishuman(AM)) var/mob/living/carbon/human/H = AM - if(istype(H.belt, /obj/item/wormhole_jaunter)) - var/obj/item/wormhole_jaunter/J = H.belt + for(var/obj/item/wormhole_jaunter/J in H.GetAllContents()) //To freak out any bystanders visible_message(span_boldwarning("[H] falls into [src]!")) J.chasm_react(H) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 231b785ec0d..9407c0bcf83 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -6,7 +6,7 @@ vis_flags = VIS_INHERIT_ID|VIS_INHERIT_PLANE // Important for interaction with and visualization of openspace. var/intact = TRUE - var/turf/baseturf = /turf/space + var/turf/baseturf = /turf/baseturf_bottom /// negative for faster, positive for slower var/slowdown = 0 /// It's a check that determines if the turf is transparent to reveal the stuff(pipes, safe, cables and e.t.c.) without looking on intact @@ -774,37 +774,6 @@ C.Weaken(3 SECONDS) -/turf/proc/CanEnter(atom/mover, exclude_mobs = FALSE, list/ignore_atoms, type_list = FALSE) - var/border_dir = get_dir(src, mover) - - if(!CanPass(mover, border_dir)) - return FALSE - - if(isturf(mover.loc)) - var/movement_dir = get_dir(mover, src) - for(var/obj/obstacle in mover.loc) - if(obstacle == mover) - continue - if(!obstacle.CanExit(mover, movement_dir)) - return FALSE - - var/list/large_dense = contents.Copy() - if(length(ignore_atoms)) - for(var/thing in large_dense) - if(!type_list && (thing in large_dense)) - large_dense -= thing - else if(type_list && is_type_in_list(thing, ignore_atoms)) - large_dense -= thing - - for(var/atom/movable/obstacle in large_dense) - if(ismob(obstacle) && exclude_mobs) - continue - if(!obstacle.CanPass(mover, border_dir)) - return FALSE - - return TRUE - - /** * Check whether the specified turf is blocked by something dense inside it with respect to a specific atom. * @@ -814,21 +783,28 @@ * * Arguments: * * exclude_mobs - If `TRUE`, ignores dense mobs on the turf. - * * source_atom - If this is not null, will check whether any contents on the turf can block this atom specifically. Also ignores itself on the turf. + * * source_atom - If this is not null, will check whether any contents on the turf can block this atom specifically. Also ignores itself on the turf. Also if source atom is in turf contents proc will check if it can exit. * * ignore_atoms - Check will ignore any atoms in this list. Useful to prevent an atom from blocking itself on the turf. * * type_list - are we checking for types of atoms to ignore and not physical atoms */ -/turf/proc/is_blocked_turf(exclude_mobs = FALSE, source_atom = null, list/ignore_atoms, type_list = FALSE) +/turf/proc/is_blocked_turf(exclude_mobs = FALSE, atom/source_atom = null, list/ignore_atoms, type_list = FALSE) if(density) return TRUE - if(locate(/mob/living/silicon/ai) in src) //Prevents jaunting onto the AI core cheese, AI should always block a turf due to being a dense mob even when unanchored + // Prevents jaunting onto the AI core cheese, AI should always block a turf due to being a dense mob even when unanchored + if(locate(/mob/living/silicon/ai) in contents) return TRUE - if(source_atom && !CanEnter(source_atom, exclude_mobs, ignore_atoms, type_list)) - return TRUE + // in case of source_atom we are also checking if it can exit its turf contents + if(source_atom && isturf(source_atom.loc) && source_atom.loc != src) + var/movement_dir = get_dir(source_atom, src) + for(var/obj/obstacle in source_atom.loc) + if(obstacle == source_atom) + continue + if(!obstacle.CanExit(source_atom, movement_dir)) + return TRUE - for(var/atom/movable/movable_content as anything in src) + for(var/atom/movable/movable_content as anything in contents) // We don't want to block ourselves if((movable_content == source_atom)) continue @@ -842,8 +818,8 @@ // If the thing is dense AND we're including mobs or the thing isn't a mob AND if there's a source atom and // it cannot pass through the thing on the turf, we consider the turf blocked. if(movable_content.density && (!exclude_mobs || !ismob(movable_content))) - //if(source_atom && movable_content.CanPass(source_atom, get_dir(src, source_atom))) - // continue + if(source_atom && movable_content.CanPass(source_atom, get_dir(src, source_atom))) + continue return TRUE - return FALSE + return FALSE diff --git a/code/modules/antagonists/space_ninja/suit/suit.dm b/code/modules/antagonists/space_ninja/suit/suit.dm index ed2f97e9a3f..22f971b81e0 100644 --- a/code/modules/antagonists/space_ninja/suit/suit.dm +++ b/code/modules/antagonists/space_ninja/suit/suit.dm @@ -99,6 +99,7 @@ var/ninja_martial = FALSE /// Встроенный в костюм джетпак jetpack = /obj/item/tank/jetpack/suit/ninja + jetpack_upgradable = TRUE /// UI stuff /// /// Флаги отвечающие за то - показываем мы или нет интерфейс заряда и концентрации ниндзя diff --git a/code/modules/client/preference/loadout/loadout_uniform.dm b/code/modules/client/preference/loadout/loadout_uniform.dm index d4ee08f98f6..3cc9f6735d2 100644 --- a/code/modules/client/preference/loadout/loadout_uniform.dm +++ b/code/modules/client/preference/loadout/loadout_uniform.dm @@ -54,6 +54,10 @@ display_name = "executive suit" path = /obj/item/clothing/under/suit_jacket/really_black +/datum/gear/uniform/suit/amish_suit + display_name = "amish suit" + path = /obj/item/clothing/under/sl_suit + /datum/gear/uniform/chaps display_name = "chaps, select" path = /obj/item/clothing/under/red_chaps diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm index eb62c08a8c2..4e6aa60d173 100644 --- a/code/modules/client/preference/preferences.dm +++ b/code/modules/client/preference/preferences.dm @@ -32,12 +32,20 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts ROLE_DEVIL = 14 )) -/proc/player_old_enough_antag(client/C, role) +/proc/player_old_enough_antag(client/C, role, req_job_rank) if(available_in_days_antag(C, role)) - return 0 //available_in_days>0 = still some days required = player not old enough + return FALSE //available_in_days>0 = still some days required = player not old enough if(role_available_in_playtime(C, role)) - return 0 //available_in_playtime>0 = still some more playtime required = they are not eligible - return 1 + return FALSE //available_in_playtime>0 = still some more playtime required = they are not eligible + if(!req_job_rank) + return TRUE + var/datum/job/job = SSjobs.GetJob(req_job_rank) + if(!job) + stack_trace("Invalid job title: [req_job_rank]") + return FALSE + if(job.available_in_playtime(C)) + return TRUE + /proc/available_in_days_antag(client/C, role) if(!C) diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index c7c2e6af238..ee16bfc3bdf 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -614,6 +614,19 @@ BLIND // can't see anything SPECIES_STOK = 'icons/mob/clothing/species/monkey/suit.dmi' ) +/obj/item/clothing/suit/Initialize(mapload) + . = ..() + setup_shielding() + +/** + * Wrapper proc to apply shielding through AddComponent(). + * Called in /obj/item/clothing/suit/Initialize(). + * Override with an AddComponent(/datum/component/shielded, args) call containing the desired shield statistics. + * See /datum/component/shielded documentation for a description of the arguments + **/ +/obj/item/clothing/suit/proc/setup_shielding() + return + //Proc that opens and closes jackets. /obj/item/clothing/suit/proc/adjustsuit(mob/user) if(ignore_suitadjust) @@ -742,6 +755,7 @@ BLIND // can't see anything species_restricted = list("exclude", SPECIES_WRYN, "lesser form") faction_restricted = list("ashwalker") var/obj/item/tank/jetpack/suit/jetpack = null + var/jetpack_upgradable = FALSE /obj/item/clothing/suit/space/Initialize(mapload) @@ -782,6 +796,9 @@ BLIND // can't see anything /obj/item/clothing/suit/space/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/tank/jetpack/suit)) + if(!jetpack_upgradable) + to_chat(user, span_warning("There is no slot for jetpack upgrade in [src]")) + return if(jetpack) to_chat(user, span_warning("[src] already has a jetpack installed.")) return diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index 67c71ad6c75..000e6ca069f 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -73,6 +73,7 @@ item_state = "bullethelmet" armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) dog_fashion = null + flags = BLOCKHAIR | HEADBANGPROTECT flags_inv = HIDEHEADSETS | HIDEGLASSES | HIDEMASK flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH flash_protect = 1 diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index 55b15cefbdd..ae1df4b66df 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -139,6 +139,7 @@ var/obj/item/clothing/head/helmet/space/hardsuit/helmet actions_types = list(/datum/action/item_action/toggle_helmet) var/helmettype = /obj/item/clothing/head/helmet/space/hardsuit + jetpack_upgradable = TRUE light_on = FALSE hide_tail_by_species = list(SPECIES_VOX , SPECIES_VULPKANIN , SPECIES_UNATHI, SPECIES_ASHWALKER_BASIC, SPECIES_ASHWALKER_SHAMAN, SPECIES_DRACONOID, SPECIES_TAJARAN) species_restricted = list("exclude", SPECIES_WRYN, "lesser form") diff --git a/code/modules/clothing/upgrade_modules/hardsuit_shield_module/hardsuit.dm b/code/modules/clothing/upgrade_modules/hardsuit_shield_module/hardsuit.dm index 524a7bd823c..1d8081821fe 100644 --- a/code/modules/clothing/upgrade_modules/hardsuit_shield_module/hardsuit.dm +++ b/code/modules/clothing/upgrade_modules/hardsuit_shield_module/hardsuit.dm @@ -1,56 +1,18 @@ -/obj/item/clothing/suit/space/hardsuit - var/obj/item/hardsuit_shield/shield = null - -/obj/item/clothing/suit/space/hardsuit/Initialize(mapload) - . = ..() - if(shield && ispath(shield)) - shield = new shield(src) - shield.hardsuit = src /obj/item/clothing/suit/space/hardsuit/attackby(obj/item/I, mob/user, params) . = ..() - if(istype(I, /obj/item/hardsuit_shield)) - var/obj/item/hardsuit_shield/new_shield = I - if(shield) - to_chat(user, "[src] already has a shield installed.") - return - if(src == user.get_item_by_slot(SLOT_HUD_OUTER_SUIT)) - to_chat(user, "You cannot install the upgrade to [src] while wearing it.") - return - if(user.drop_transfer_item_to_loc(new_shield, src)) - shield = new_shield - shield.hardsuit = src - to_chat(user, "You successfully install the shield upgrade into [src].") - - -/obj/item/clothing/suit/space/hardsuit/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(shield) - var/blocked = shield.hit_reaction(owner, hitby, attack_text, final_block_chance, damage, attack_type) - if(blocked) - return TRUE - . = ..() - -/obj/item/clothing/suit/space/hardsuit/Destroy() - if(shield) - STOP_PROCESSING(SSobj, shield) - return ..() - -/obj/item/clothing/suit/space/hardsuit/special_overlays() - . = ..() - if(shield) - return mutable_appearance('icons/effects/effects.dmi', shield.shield_state, MOB_LAYER + 0.01) - -/obj/item/clothing/suit/space/hardsuit/multitool_act(mob/user, obj/item/I) - . = ..() - if(shield) - shield.multitool_act(user, I) - -//////Shielded Hardsuits - -/obj/item/clothing/suit/space/hardsuit/shielded - name = "shielded hardsuit" - desc = "A hardsuit with built in energy shielding. Will rapidly recharge when not under fire." - shield = /obj/item/hardsuit_shield + if(!istype(I, /obj/item/hardsuit_shield)) + return + var/obj/item/hardsuit_shield/new_shield = I + if(user.get_item_by_slot(SLOT_HUD_OUTER_SUIT) == src) + to_chat(user, "You cannot install the upgrade to [src] while wearing it.") + return + var/datum/component/shielded/shielded = GetComponent(/datum/component/shielded) + if(istype(shielded)) + to_chat(user, "[src] already has a shield installed.") + return + new_shield.attach_to_suit(src) + to_chat(user, "You successfully install the shield upgrade into [src].") //////Syndicate Version @@ -58,21 +20,23 @@ desc = "An advanced hardsuit with built in energy shielding and jetpack." helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/shielded jetpack = /obj/item/tank/jetpack/suit - shield = /obj/item/hardsuit_shield/syndi resistance_flags = ACID_PROOF armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 20, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) +/obj/item/clothing/suit/space/hardsuit/syndi/shielded/setup_shielding() + AddComponent(/datum/component/shielded, shield_icon = "shield-red") + /obj/item/clothing/head/helmet/space/hardsuit/syndi/shielded desc = "An advanced hardsuit helmet with built in energy shielding." resistance_flags = ACID_PROOF armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 20, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) //////Wizard Versions -/obj/item/clothing/suit/space/hardsuit/wizard/shielded - shield = /obj/item/hardsuit_shield/wizard +/obj/item/clothing/suit/space/hardsuit/wizard/shielded/setup_shielding() + AddComponent(/datum/component/shielded, max_charges = 15, recharge_start_delay = 0 SECONDS) -/obj/item/clothing/suit/space/hardsuit/wizard/arch/shielded - shield = /obj/item/hardsuit_shield/wizard/arch +/obj/item/clothing/suit/space/hardsuit/wizard/arch/shielded/setup_shielding() + AddComponent(/datum/component/shielded, max_charges = 15, recharge_start_delay = 1 SECONDS, charge_increment_delay = 1 SECONDS) /obj/item/wizard_armour_charge name = "battlemage shield charges" @@ -85,14 +49,14 @@ if(!istype(W)) to_chat(user, "The rune can only be used on battlemage armour!") return - if(!W.shield) + var/datum/component/shielded/shielded = W.GetComponent(/datum/component/shielded) + if(!istype(shielded)) to_chat(user, "No shield detected on this armour!") return if(W == user.get_item_by_slot(SLOT_HUD_OUTER_SUIT)) to_chat(user, "You cannot replenish charges to [W] while wearing it.") return - W.shield.current_charges += 8 - W.shield.shield_state = "[W.shield.shield_on]" + shielded.current_charges += 8 playsound(loc, 'sound/magic/charge.ogg', 50, TRUE) - to_chat(user, "You charge [W]. It can now absorb [W.shield.current_charges] hits.") + to_chat(user, "You charge [W]. It can now absorb [shielded.current_charges] hits.") qdel(src) diff --git a/code/modules/clothing/upgrade_modules/hardsuit_shield_module/hardsuit_shield.dm b/code/modules/clothing/upgrade_modules/hardsuit_shield_module/hardsuit_shield.dm index f134cff22e4..f684112a7d2 100644 --- a/code/modules/clothing/upgrade_modules/hardsuit_shield_module/hardsuit_shield.dm +++ b/code/modules/clothing/upgrade_modules/hardsuit_shield_module/hardsuit_shield.dm @@ -6,82 +6,44 @@ var/obj/item/clothing/suit/space/hardsuit/hardsuit = null var/current_charges = 3 var/max_charges = 3 //How many charges total the shielding has - var/recharge_delay = 200 //How long after we've been shot before we can start recharging. 20 seconds here + var/recharge_delay = 20 SECONDS //How long after we've been shot before we can start recharging. 20 seconds here var/recharge_cooldown = 0 //Time since we've last been shot - var/recharge_rate = 1 //How quickly the shield recharges once it starts charging - var/shield_state = "shield-old" - var/shield_on = "shield-old" + var/recharge_rate = 1 SECONDS //How quickly the shield recharges once it starts charging + var/shield_on_icon = "shield-old" var/allowed_to_change_color = FALSE /obj/item/hardsuit_shield/syndi allowed_to_change_color = TRUE - shield_state = "shield-red" - shield_on = "shield-red" + shield_on_icon = "shield-red" /obj/item/hardsuit_shield/wizard current_charges = 15 max_charges = 15 recharge_cooldown = INFINITY recharge_rate = 0 - shield_state = "shield-red" - shield_on = "shield-red" + shield_on_icon = "shield-red" /obj/item/hardsuit_shield/wizard/arch recharge_cooldown = 0 - recharge_rate = 1 + recharge_rate = 1 SECONDS + +/obj/item/hardsuit_shield/proc/attach_to_suit(obj/item/clothing/suit/space/hardsuit/hardsuit) + hardsuit.AddComponent(/datum/component/shielded, max_charges = src.max_charges, shield_icon = shield_on_icon, recharge_start_delay = recharge_delay, charge_increment_delay = recharge_rate, starting_charges = src.current_charges) + qdel(src) /obj/item/hardsuit_shield/multitool_act(mob/user, obj/item/I) if(!allowed_to_change_color) - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(shield_state == "broken") - to_chat(user, "You can't interface with the hardsuit's software if the shield's broken!") - return + return FALSE + if(!I.use_tool(src, user, 1 SECONDS, volume = I.tool_volume)) + return FALSE - if(shield_state == "shield-red") - shield_state = "shield-old" - shield_on = "shield-old" + if(shield_on_icon == "shield-red") + shield_on_icon = "shield-old" to_chat(user, "You roll back the hardsuit's software, changing the shield's color!") else - shield_state = "shield-red" - shield_on = "shield-red" - to_chat(user, "You update the hardsuit's hardware, changing back the shield's color to red.") - - user.update_inv_wear_suit() - -/obj/item/hardsuit_shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(!hardsuit) - return FALSE - recharge_cooldown = world.time + recharge_delay - if(current_charges > 0) - do_sparks(2, 1, src) - owner.visible_message("[owner]'s shields deflect [attack_text] in a shower of sparks!") - current_charges-- - if(recharge_rate) - START_PROCESSING(SSobj, src) - if(current_charges <= 0) - owner.visible_message("[owner]'s shield overloads!") - shield_state = "broken" - owner.update_inv_wear_suit() - return TRUE - return FALSE - -/obj/item/hardsuit_shield/process() - if(world.time > recharge_cooldown && current_charges < max_charges) - current_charges = clamp((current_charges + recharge_rate), 0, max_charges) - playsound(loc, 'sound/magic/charge.ogg', 50, TRUE) - if(current_charges == max_charges) - playsound(loc, 'sound/machines/ding.ogg', 50, TRUE) - STOP_PROCESSING(SSobj, src) - shield_state = "[shield_on]" - if(hardsuit && ishuman(hardsuit.loc)) - var/mob/living/carbon/human/C = hardsuit.loc - C.update_inv_wear_suit() - -/obj/item/hardsuit_shield/Destroy() - STOP_PROCESSING(SSobj, src) - . = ..() + shield_on_icon = "shield-red" + to_chat(user, "You update the hardsuit's hardware, changing the shield's color to red.") + return TRUE /obj/item/storage/box/ert_hardsuit_shield_upgrade name = "Hardsuit Shield Upgrade Box" diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm index 2e87ae945c2..cdf61498a00 100644 --- a/code/modules/hydroponics/hydroitemdefines.dm +++ b/code/modules/hydroponics/hydroitemdefines.dm @@ -200,7 +200,7 @@ hitsound = "swing_hit" //Collapse sound (blade sheath) playsound(src.loc, 'sound/weapons/blade_sheath.ogg', 50, 1) //Sound credit to Q.K. of Freesound.org - sharp = extend + set_sharpness(extend) update_icon(UPDATE_ICON_STATE) update_equipped_item() add_fingerprint(user) diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm index 5ec49f4a778..18dda0d0db2 100644 --- a/code/modules/mining/equipment/explorer_gear.dm +++ b/code/modules/mining/equipment/explorer_gear.dm @@ -74,6 +74,17 @@ armor = list("melee" = 70, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe, /obj/item/twohanded/kinetic_crusher, /obj/item/hierophant_club, /obj/item/twohanded/fireaxe/boneaxe) jetpack = /obj/item/tank/jetpack/suit + jetpack_upgradable = TRUE + + sprite_sheets = list( + SPECIES_VULPKANIN = 'icons/mob/clothing/species/vulpkanin/suit.dmi', + SPECIES_TAJARAN = 'icons/mob/clothing/species/tajaran/suit.dmi', + SPECIES_UNATHI = 'icons/mob/clothing/species/unathi/suit.dmi', + SPECIES_DRACONOID = 'icons/mob/clothing/species/unathi/suit.dmi', + SPECIES_VOX = 'icons/mob/clothing/species/vox/suit.dmi', + SPECIES_DRASK = 'icons/mob/clothing/species/drask/suit.dmi', + SPECIES_GREY = 'icons/mob/clothing/species/grey/suit.dmi', + ) /obj/item/clothing/suit/space/hostile_environment/Initialize(mapload) @@ -106,6 +117,16 @@ armor = list("melee" = 70, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) resistance_flags = FIRE_PROOF | LAVA_PROOF | ACID_PROOF + sprite_sheets = list( + SPECIES_VULPKANIN = 'icons/mob/clothing/species/vulpkanin/helmet.dmi', + SPECIES_TAJARAN = 'icons/mob/clothing/species/tajaran/helmet.dmi', + SPECIES_UNATHI = 'icons/mob/clothing/species/unathi/helmet.dmi', + SPECIES_DRACONOID = 'icons/mob/clothing/species/unathi/helmet.dmi', + SPECIES_VOX = 'icons/mob/clothing/species/vox/helmet.dmi', + SPECIES_DRASK = 'icons/mob/clothing/species/drask/helmet.dmi', + SPECIES_GREY = 'icons/mob/clothing/species/grey/helmet.dmi', + ) + /obj/item/clothing/head/helmet/space/hostile_environment/Initialize(mapload) . = ..() diff --git a/code/modules/mining/equipment/wormhole_jaunter.dm b/code/modules/mining/equipment/wormhole_jaunter.dm index 7fba5c5cde5..0efd5196686 100644 --- a/code/modules/mining/equipment/wormhole_jaunter.dm +++ b/code/modules/mining/equipment/wormhole_jaunter.dm @@ -1,7 +1,7 @@ /**********************Jaunter**********************/ /obj/item/wormhole_jaunter name = "wormhole jaunter" - desc = "A single use device harnessing outdated wormhole technology, Nanotrasen has since turned its eyes to bluespace for more accurate teleportation. The wormholes it creates are unpleasant to travel through, to say the least.\nThanks to modifications provided by the Free Golems, this jaunter can be worn on the belt to provide protection from chasms." + desc = "A single use device harnessing outdated wormhole technology, Nanotrasen has since turned its eyes to bluespace for more accurate teleportation. The wormholes it creates are unpleasant to travel through, to say the least.\nThanks to modifications provided by the Free Golems, this jaunter provides protection from chasms." icon = 'icons/obj/items.dmi' icon_state = "Jaunter" item_state = "electronic" @@ -53,11 +53,9 @@ qdel(src) /obj/item/wormhole_jaunter/proc/chasm_react(mob/user) - if(user.get_item_by_slot(SLOT_HUD_BELT) == src) - to_chat(user, "Your [name] activates, saving you from the chasm!") - activate(user, FALSE) - else - to_chat(user, "[src] is not attached to your belt, preventing it from saving you from the chasm. RIP.") + to_chat(user, "Your [name] activates, saving you from the chasm!") + SSblackbox.record_feedback("tally", "jaunter", 1, "Chasm") // chasm automatic activation + activate(user, FALSE) /obj/item/wormhole_jaunter/emag_act(mob/user) if(!emagged) @@ -123,14 +121,15 @@ destinations += BT var/turf/T = get_turf(src) if(istype(T, /turf/simulated/floor/chasm/straight_down/lava_land_surface)) - for(var/obj/effect/abstract/chasm_storage/C in T) - var/found_mob = FALSE - for(var/mob/M in C) - found_mob = TRUE - do_teleport(M, pick(destinations)) - if(found_mob) - new /obj/effect/temp_visual/thunderbolt(T) //Visual feedback it worked. - playsound(src, 'sound/magic/lightningbolt.ogg', 100, TRUE) + for(var/turf/simulated/floor/chasm/straight_down/lava_land_surface/chasm_turfs in range(5, T)) + for(var/obj/effect/abstract/chasm_storage/C in chasm_turfs) + var/found_mob = FALSE + for(var/mob/M in C) + found_mob = TRUE + do_teleport(M, pick(destinations)) + if(found_mob) + new /obj/effect/temp_visual/thunderbolt(chasm_turfs) //Visual feedback it worked. + playsound(src, 'sound/magic/lightningbolt.ogg', 100, TRUE) qdel(src) else var/list/portal_turfs = list() diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index 9a9756ef401..f7e379eee11 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -56,7 +56,7 @@ if(stage == 5 && prob(50)) for(var/datum/surgery/S in owner.surgeries) - if(S.location == BODY_ZONE_CHEST && istype(S.get_surgery_step(), /datum/surgery_step/internal/manipulate_organs)) + if(S.location == BODY_ZONE_CHEST && S.organ_to_manipulate.open >= ORGAN_ORGANIC_OPEN) AttemptGrow(FALSE) return AttemptGrow() diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index a544c352ee2..462e6eff793 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -39,11 +39,11 @@ wetlevel = min(wetlevel + 1,5) /mob/living/carbon/attackby(obj/item/I, mob/user, params) - if(lying_angle && surgeries.len) - if(user != src && user.a_intent == INTENT_HELP) + if(length(surgeries)) + if(user.a_intent == INTENT_HELP) for(var/datum/surgery/S in surgeries) - if(S.next_step(user, src, I)) - return 1 + if(S.next_step(user, src)) + return TRUE return ..() /mob/living/carbon/attack_hand(mob/living/carbon/human/user) @@ -62,8 +62,8 @@ if(user.a_intent == INTENT_HELP) for(var/datum/surgery/S in surgeries) if(S.next_step(user, src)) - return 1 - return 0 + return TRUE + return FALSE /mob/living/carbon/attack_slime(mob/living/simple_animal/slime/M) if(..()) //successful slime attack diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 526261329ca..184a655dbf3 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -231,6 +231,9 @@ else if(bodypart.is_splinted()) wound_flavor_text[limb_zone] = "[p_they(TRUE)] [p_have()] a splint on [p_their()] [bodypart.name]!\n" + else if(!bodypart.properly_attached) + wound_flavor_text[limb_zone] = "[p_their(TRUE)] [bodypart.name] is barely attached!\n" + if(bodypart.open) if(bodypart.is_robotic()) msg += "The maintenance hatch on [p_their()] [ignore_limb_branding(limb_zone)] is open!\n" diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index b50c03b82d2..6b706657c5d 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1021,8 +1021,8 @@ else if(affecting.is_robotic()) . = FALSE fail_msg = "That limb is robotic." - // affecting.open = 2 after scalpel->hemostat->retractor - else if((PIERCEIMMUNE in dna.species.species_traits) && !ignore_pierceimmune && affecting.open < 2) + // affecting.open = ORGAN_ORGANIC_ENCASED_OPEN after scalpel->hemostat->retractor + else if((PIERCEIMMUNE in dna.species.species_traits) && !ignore_pierceimmune && affecting.open < ORGAN_ORGANIC_ENCASED_OPEN) . = FALSE else switch(target_zone) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 9a7149cff73..0cbd4919279 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -87,7 +87,7 @@ emp_act var/obj/item/organ/external/S = bodyparts_by_name[user.zone_selected] if(!S) return - if(!S.is_robotic() || S.open == 2) + if(!S.is_robotic() || S.open == ORGAN_SYNTHETIC_OPEN) return . = TRUE if(S.brute_dam > ROBOLIMB_SELF_REPAIR_CAP) @@ -218,30 +218,32 @@ emp_act //End Here -/mob/living/carbon/human/proc/check_shields(atom/AM, var/damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0, shields_penetration = 0) +/mob/living/carbon/human/proc/check_shields(atom/AM, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0, shields_penetration = 0) var/block_chance_modifier = round(damage / -3) - shields_penetration if(l_hand && !istype(l_hand, /obj/item/clothing)) var/final_block_chance = l_hand.block_chance - (clamp((armour_penetration-l_hand.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example if(l_hand.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return 1 + return TRUE if(r_hand && !istype(r_hand, /obj/item/clothing)) var/final_block_chance = r_hand.block_chance - (clamp((armour_penetration-r_hand.armour_penetration)/2,0,100)) + block_chance_modifier //Need to reset the var so it doesn't carry over modifications between attempts if(r_hand.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return 1 + return TRUE if(wear_suit) var/final_block_chance = wear_suit.block_chance - (clamp((armour_penetration-wear_suit.armour_penetration)/2,0,100)) + block_chance_modifier if(wear_suit.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return 1 + return TRUE if(neck) var/final_block_chance = neck.block_chance - (clamp((armour_penetration-neck.armour_penetration)/2,0,100)) + block_chance_modifier if(neck.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return 1 + return TRUE if(w_uniform) var/final_block_chance = w_uniform.block_chance - (clamp((armour_penetration-w_uniform.armour_penetration)/2,0,100)) + block_chance_modifier if(w_uniform.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return 1 - return 0 + return TRUE + if(SEND_SIGNAL(src, COMSIG_HUMAN_CHECK_SHIELDS, AM, attack_text, 0, damage, attack_type) & SHIELD_BLOCK) + return TRUE + return FALSE /mob/living/carbon/human/proc/check_martial_art_defense(mob/living/carbon/human/defender, mob/living/carbon/human/attacker, obj/item/I, visible_message, self_message) if(mind && mind.martial_art) diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 9b1926e06ea..e53cd633b65 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -1,6 +1,6 @@ /mob/living/carbon/human/Moved(atom/OldLoc, Dir, Forced = FALSE) . = ..() - if(!OldLoc.has_gravity() && has_gravity()) + if((!OldLoc || !OldLoc.has_gravity()) && has_gravity()) thunk() diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm index 3f3054ca851..8a70578041d 100644 --- a/code/modules/mob/living/carbon/human/human_organs.dm +++ b/code/modules/mob/living/carbon/human/human_organs.dm @@ -59,7 +59,7 @@ if(!bodypart.can_grasp || bodypart.is_splinted()) continue - if(bodypart.is_traumatized()) + if(bodypart.is_traumatized() || !bodypart.properly_attached) if(bodypart.limb_zone == BODY_ZONE_L_ARM || bodypart.limb_zone == BODY_ZONE_PRECISE_L_HAND) if(!l_hand) continue @@ -72,6 +72,13 @@ continue var/emote_scream = pick("крич[pluralize_ru(gender,"ит","ат")] от боли и ", "изда[pluralize_ru(gender,"ёт","ют")] резкий крик и ", "вскрикива[pluralize_ru(gender,"ет","ют")] и ") + if(!bodypart.properly_attached && has_pain()) + visible_message( + span_warning("[src] [emote_scream]броса[pluralize_ru(gender,"ет","ют")] предмет, который держал[genderize_ru(gender,"","а","о","и")] в [bodypart.declent_ru(PREPOSITIONAL)]!"), + span_userdanger("Вы чувствуете острую боль, пронизывающую [bodypart.name], которая лишь немного прикреплена к [bodypart.amputation_point], вам нужно прикрепить [bodypart.declent_ru(GENITIVE)] хирургическим путем, прежде чем вы сможете что-либо держать!") + ) + continue + custom_emote(EMOTE_VISIBLE, "[(has_pain()) ? emote_scream : "" ]броса[pluralize_ru(gender,"ет","ют")] предмет, который держал[genderize_ru(gender,"","а","о","и")] в [bodypart.declent_ru(PREPOSITIONAL)]!") else if(bodypart.is_malfunctioning()) diff --git a/code/modules/mob/living/carbon/human/species/abductor.dm b/code/modules/mob/living/carbon/human/species/abductor.dm index e8c3653c6ec..cae2bf02521 100644 --- a/code/modules/mob/living/carbon/human/species/abductor.dm +++ b/code/modules/mob/living/carbon/human/species/abductor.dm @@ -18,7 +18,7 @@ meat_type = /obj/item/reagent_containers/food/snacks/meat/humanoid/grey - species_traits = list(NO_BLOOD, NO_BREATHE, VIRUSIMMUNE, NOGUNS, NO_HUNGER, NO_EXAMINE) + species_traits = list(NO_BLOOD, NO_BREATHE, VIRUSIMMUNE, NOGUNS, NO_HUNGER, NO_EXAMINE, REPEATSURGERY) dies_at_threshold = TRUE taste_sensitivity = TASTE_SENSITIVITY_NO_TASTE diff --git a/code/modules/mob/living/carbon/human/species/slime.dm b/code/modules/mob/living/carbon/human/species/slime.dm index 7f1b3c78402..fbfd1e5e0a3 100644 --- a/code/modules/mob/living/carbon/human/species/slime.dm +++ b/code/modules/mob/living/carbon/human/species/slime.dm @@ -254,7 +254,7 @@ // Grah this line will leave a "not used" warning, in spite of the fact that the new() proc WILL do the thing. // Bothersome. var/obj/item/organ/external/new_limb = new limb_path(H) - new_limb.open = 0 // This is just so that the compiler won't think that new_limb is unused, because the compiler is horribly stupid. + new_limb.open = ORGAN_CLOSED // This is just so that the compiler won't think that new_limb is unused, because the compiler is horribly stupid. H.adjustBruteLoss(stored_brute) H.adjustFireLoss(stored_burn) H.update_body() diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 46fdab4fb8c..6b264ec57ab 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -549,6 +549,8 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) add_overlay(get_emissive_block()) update_halo_layer() update_fire() + SEND_SIGNAL(src, COMSIG_HUMAN_REGENERATE_ICONS) + /* --------------------------------------- */ diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 477dbf05b7a..3b6775385f8 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -18,7 +18,6 @@ has_camera = FALSE req_access = list(ACCESS_ENGINE, ACCESS_ROBOTICS) ventcrawler = VENTCRAWLER_ALWAYS - magpulse = 1 mob_size = MOB_SIZE_SMALL pull_force = MOVE_FORCE_VERY_WEAK // Can only drag small items modules_break = FALSE @@ -62,6 +61,8 @@ add_language(LANGUAGE_DRONE_BINARY, 1) add_language(LANGUAGE_DRONE, 1) + + // Disable the microphone wire on Drones if(radio) radio.wires.cut(WIRE_RADIO_TRANSMIT) @@ -105,6 +106,11 @@ update_icons() +/mob/living/silicon/robot/drone/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_NEGATES_GRAVITY, ROBOT_TRAIT) + + /mob/living/silicon/robot/drone/Destroy() for(var/datum/action/innate/hide/drone/hide in actions) hide.Remove(src) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 2cd82ea93f3..22c09c80bef 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -111,7 +111,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD) var/default_cell_type = /obj/item/stock_parts/cell/high - var/magpulse = 0 var/ionpulse = 0 // Jetpack-like effect. var/ionpulse_on = 0 // Jetpack-like effect. @@ -397,7 +396,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(camera && ("Robots" in camera.network)) camera.network.Add("Engineering") - magpulse = 1 + ADD_TRAIT(src, TRAIT_NEGATES_GRAVITY, ROBOT_TRAIT) if("Janitor") module = new /obj/item/robot_module/janitor(src) @@ -1569,7 +1568,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( has_camera = FALSE req_access = list(ACCESS_CENT_SPECOPS) ionpulse = 1 - magpulse = 1 pdahide = 1 eye_protection = 2 // Immunity to flashes and the visual part of flashbangs ear_protection = 1 // Immunity to the audio part of flashbangs @@ -1586,6 +1584,12 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( see_reagents = TRUE has_transform_animation = TRUE + +/mob/living/silicon/robot/deathsquad/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_NEGATES_GRAVITY, ROBOT_TRAIT) + + /mob/living/silicon/robot/deathsquad/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null) laws = new /datum/ai_laws/deathsquad module = new /obj/item/robot_module/deathsquad(src) @@ -1650,7 +1654,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( limited_modules = list("Combat", "Engineering", "Medical") damage_protection = 5 // Reduce all incoming damage by this number eprefix = "Gamma" - magpulse = 1 + + +/mob/living/silicon/robot/ert/gamma/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_NEGATES_GRAVITY, ROBOT_TRAIT) /mob/living/silicon/robot/destroyer @@ -1664,7 +1672,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( has_camera = FALSE req_access = list(ACCESS_CENT_SPECOPS) ionpulse = 1 - magpulse = 1 pdahide = 1 eye_protection = 2 // Immunity to flashes and the visual part of flashbangs ear_protection = 1 // Immunity to the audio part of flashbangs @@ -1675,6 +1682,12 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( see_reagents = TRUE drain_act_protected = TRUE + +/mob/living/silicon/robot/destroyer/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_NEGATES_GRAVITY, ROBOT_TRAIT) + + /mob/living/silicon/robot/destroyer/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null) aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) additional_law_channels["Binary"] = get_language_prefix(LANGUAGE_BINARY) diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index 8faf5015d3c..61608b8675b 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -6,12 +6,8 @@ return 0 -/mob/living/silicon/robot/mob_negates_gravity() - return magpulse - - /mob/living/silicon/robot/experience_pressure_difference(pressure_difference, direction) - if(!magpulse) + if(!HAS_TRAIT(src, TRAIT_NEGATES_GRAVITY)) return ..() /mob/living/silicon/robot/get_pull_push_speed_modifier(current_delay) 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 4eaddd155de..63689c9b728 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 @@ -59,7 +59,6 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) turns_per_move = 3 // number of turns before AI-controlled spiders wander around. No effect on actual player or AI movement speed! move_to_delay = 6 speed = 0 - var/magpulse = 1 // AI spider speed at chasing down targets. Higher numbers mean slower speed. Divide 20 (server tick rate / second) by this to get tiles/sec. //SPECIAL @@ -141,6 +140,12 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) var/spider_growinstantly = FALSE var/spider_debug = FALSE + +/mob/living/simple_animal/hostile/poison/terror_spider/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_NEGATES_GRAVITY, INNATE_TRAIT) + + // -------------------------------------------------------------------------------- // --------------------- TERROR SPIDERS: SHARED ATTACK CODE ----------------------- // -------------------------------------------------------------------------------- @@ -429,12 +434,8 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) return TRUE -/mob/living/simple_animal/hostile/poison/terror_spider/mob_negates_gravity() - return magpulse - - /mob/living/simple_animal/hostile/poison/terror_spider/experience_pressure_difference(pressure_difference, direction) - if(!magpulse) + if(!HAS_TRAIT(src, TRAIT_NEGATES_GRAVITY)) return ..() /obj/item/projectile/terrorspider diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index 8502141bbe7..2fc2507c150 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -239,7 +239,6 @@ if(on) on = FALSE force = initial(force) - sharp = FALSE w_class = initial(w_class) name = initial(name) attack_verb = list() @@ -252,7 +251,6 @@ else on = TRUE force = 18 - sharp = TRUE w_class = WEIGHT_CLASS_NORMAL name = "energy dagger" attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") @@ -262,6 +260,7 @@ playsound(user, 'sound/weapons/saberon.ogg', 3, TRUE) to_chat(user, span_warning("[src] is now active.")) set_light_on(TRUE) + set_sharpness(on) update_icon(UPDATE_ICON_STATE) diff --git a/code/modules/power/cable_coil.dm b/code/modules/power/cable_coil.dm index 1f5d9ab0e5e..86355fc1914 100644 --- a/code/modules/power/cable_coil.dm +++ b/code/modules/power/cable_coil.dm @@ -157,7 +157,7 @@ return ..() var/obj/item/organ/external/target_organ = target.get_organ(check_zone(user.zone_selected)) - if(!target_organ || !target_organ.is_robotic() || user.a_intent != INTENT_HELP || target_organ.open == 2) + if(!target_organ || !target_organ.is_robotic() || user.a_intent != INTENT_HELP || target_organ.open == ORGAN_SYNTHETIC_OPEN) return ..() if(target_organ.burn_dam > ROBOLIMB_SELF_REPAIR_CAP) diff --git a/code/modules/power/cable_multiz.dm b/code/modules/power/cable_multiz.dm index 514104b3266..98127a7213d 100644 --- a/code/modules/power/cable_multiz.dm +++ b/code/modules/power/cable_multiz.dm @@ -104,8 +104,12 @@ return var/obj/structure/cable/multiz/above = locate(/obj/structure/cable/multiz) in (GET_TURF_ABOVE(our_turf)) + if(above) + P_list += above // get that which were connected above var/obj/structure/cable/multiz/below = locate(/obj/structure/cable/multiz) in (GET_TURF_BELOW(our_turf)) - P_list += power_list(loc, src, 0, 0, cable_only = 1)//... and on turf + if(below) + P_list += below // and below... + P_list += power_list(loc, src, 0, 0, cable_only = 1)//... and on turf ourselves if(P_list.len == 0 && !above && !below)//If we so happened to be alone cable, not connected to anything, including above and below. powernet.remove_cable(src) // So we gonna just delete ourself @@ -118,7 +122,3 @@ // queue it to rebuild SSmachines.deferred_powernet_rebuilds += O - if(above) - SSmachines.deferred_powernet_rebuilds += above[1] - if(below) - SSmachines.deferred_powernet_rebuilds += below[1] diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index c207d70e183..477d8c3fc08 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -429,7 +429,9 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne var/sound/alert_sound = sound('sound/effects/alert.ogg') for(var/mob/shaked as anything in GLOB.mob_list) var/turf/mob_turf = get_turf(shaked) - if(!mob_turf || our_turf.z != mob_turf.z) + if(!istype(mob_turf)) + continue + if(!is_valid_z_level(our_turf, mob_turf)) continue if(isliving(shaked)) var/mob/living/living_shaked = shaked @@ -453,12 +455,20 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne var/turf/our_turf = get_turf(src) if(!our_turf) return - if(!GLOB.gravity_generators["[our_turf.z]"]) - GLOB.gravity_generators["[our_turf.z]"] = list() - if(on) - GLOB.gravity_generators["[our_turf.z]"] |= src + var/list/z_list = list() + // Multi-Z, station gravity generator generates gravity on all STATION_LEVEL z-levels. + if(check_level_trait(our_turf.z, STATION_LEVEL)) + for(var/z in levels_by_trait(STATION_LEVEL)) + z_list += z else - GLOB.gravity_generators["[our_turf.z]"] -= src + z_list += our_turf.z + for(var/z in z_list) + if(!GLOB.gravity_generators["[z]"]) + GLOB.gravity_generators["[z]"] = list() + if(on) + GLOB.gravity_generators["[z]"] |= src + else + GLOB.gravity_generators["[z]"] -= src // Misc diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 359b8f25636..fcdc738e919 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -201,6 +201,8 @@ mob.apply_effect(rads, IRRADIATE) explode() emergency_lighting(0) + //It's kinda pointless to process atmos on destroyed (qdel'ed) crystal + return if(damage > warning_point && world.timeofday > last_zap) last_zap = world.timeofday + rand(80,200) @@ -208,8 +210,7 @@ //Ok, get the air from the turf var/datum/gas_mixture/env = L.return_air() - - //Remove gas from surrounding area + //And, get part of that air var/datum/gas_mixture/removed = env.remove(gasefficency * env.total_moles()) //ensure that damage doesn't increase too quickly due to super high temperatures resulting from no coolant, for example. We dont want the SM exploding before anyone can react. @@ -221,10 +222,18 @@ else damage_archived = damage + if(!removed) + //Placeholder, which representates vacuum + removed = new + damage = max(0, damage + between(-DAMAGE_RATE_LIMIT, (removed.temperature - CRITICAL_TEMPERATURE) / 150, damage_inc_limit)) //Maxes out at 100% oxygen pressure - oxygen = clamp((removed.oxygen - (removed.nitrogen * NITROGEN_RETARDATION_FACTOR)) / removed.total_moles(), 0, 1) + if(!removed.total_moles()) + oxygen = 0 + else + //Result of this formula is undefined if we (total moles of removed) -> 0. So, let's roll with zero if no gas was removed. + oxygen = clamp((removed.oxygen - (removed.nitrogen * NITROGEN_RETARDATION_FACTOR)) / removed.total_moles(), 0, 1) var/temp_factor var/equilibrium_power @@ -238,25 +247,28 @@ icon_state = base_icon_state temp_factor = ((equilibrium_power / DECAY_FACTOR) ** 3) / 800 - power = max((removed.temperature * temp_factor) * oxygen + power, 0) + power = round(max((removed.temperature * temp_factor) * oxygen + power, 0), 0.01) - var/device_energy = power * REACTION_POWER_MODIFIER + var/device_energy = round(power * REACTION_POWER_MODIFIER, 0.01) - var/heat_capacity = removed.heat_capacity() + var/old_heat_capacity = removed.heat_capacity() - removed.toxins += max(device_energy / PLASMA_RELEASE_MODIFIER, 0) + if(device_energy) + removed.toxins += max(device_energy / PLASMA_RELEASE_MODIFIER, 0) + removed.oxygen += max((device_energy + removed.temperature - T0C) / OXYGEN_RELEASE_MODIFIER, 0) - removed.oxygen += max((device_energy + removed.temperature - T0C) / OXYGEN_RELEASE_MODIFIER, 0) + var/heat_capacity = removed.heat_capacity() var/thermal_power = THERMAL_RELEASE_MODIFIER * device_energy if(debug) - var/heat_capacity_new = removed.heat_capacity() visible_message("[src]: Releasing [round(thermal_power)] W.") - visible_message("[src]: Releasing additional [round((heat_capacity_new - heat_capacity)*removed.temperature)] W with exhaust gasses.") + visible_message("[src]: Releasing additional [round((heat_capacity - old_heat_capacity)*removed.temperature)] W with exhaust gasses.") - removed.temperature += (device_energy) + //deltaT = deltaQ / heat_capacity (deltaQ equals thermal_power) + //We are assuming here, that volume does not change here + removed.temperature += (thermal_power / heat_capacity) - removed.temperature = max(0, min(removed.temperature, 10000)) + removed.temperature = max(0, removed.temperature) env.merge(removed) @@ -282,6 +294,7 @@ l.apply_effect(rads, IRRADIATE) power -= (power/DECAY_FACTOR)**3 + handle_admin_warnings() return 1 diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index c4d41fbae78..78875b6c832 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -269,6 +269,7 @@ if(semicd) return + SEND_SIGNAL(src, COMSIG_GUN_FIRED, user, target) var/sprd = 0 var/randomized_gun_spread = 0 if(spread) diff --git a/code/modules/reagents/chemistry/recipes.dm b/code/modules/reagents/chemistry/recipes.dm index c5d7ba10773..0f20f455dce 100644 --- a/code/modules/reagents/chemistry/recipes.dm +++ b/code/modules/reagents/chemistry/recipes.dm @@ -71,9 +71,14 @@ continue //stop pulling smoke and hotspots please if(X && !X.anchored && X.move_resist <= MOVE_FORCE_DEFAULT) if(setting_type) - X.throw_at(T, 20 + round(volume * 2), 1 + round(volume / 10)) + X.throw_at(T, 4 + round(volume / 10), 10 + round(volume / 10)) else - X.throw_at(get_edge_target_turf(T, get_dir(T, X)), 20 + round(volume * 2), 1 + round(volume / 10)) + var/throwdir + if(get_turf(X) == T) + throwdir = pick(GLOB.alldirs) + else + throwdir = get_dir(T, X) + X.throw_at(get_edge_target_turf(T, throwdir), 4 + round(volume / 10), 10 + round(volume / 10)) /proc/goonchem_vortex_weak(turf/T, setting_type, volume) if(setting_type) diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm index 3509716c4c0..da2350d8240 100644 --- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm +++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm @@ -105,6 +105,13 @@ /datum/chemical_reaction/sorium_explosion/on_reaction(datum/reagents/holder, created_volume) var/turf/T = get_turf(holder.my_atom) + if(ismob(holder.my_atom)) + var/mob/living/carbon/victim = holder.my_atom + victim.adjustBruteLoss(created_volume) + victim.adjustStaminaLoss(created_volume) + victim.adjustToxLoss(created_volume) + to_chat(victim, span_danger("You feel like you are being torn apart!")) + if(!T) return goonchem_vortex(T, 0, created_volume) diff --git a/code/modules/reagents/reagent_containers/iv_bag.dm b/code/modules/reagents/reagent_containers/iv_bag.dm index 59dac5c7218..00f3dfd2657 100644 --- a/code/modules/reagents/reagent_containers/iv_bag.dm +++ b/code/modules/reagents/reagent_containers/iv_bag.dm @@ -72,8 +72,8 @@ end_processing() return - // injection_limb.open = 2 after scalpel->hemostat->retractor - if((PIERCEIMMUNE in injection_target.dna.species.species_traits) && injection_limb.open < 2) + // injection_limb.open = ORGAN_ORGANIC_ENCASED_OPEN after scalpel->hemostat->retractor + if((PIERCEIMMUNE in injection_target.dna.species.species_traits) && injection_limb.open < ORGAN_ORGANIC_ENCASED_OPEN) end_processing() return diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 396e71ddbc6..2bb6ea89898 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -489,14 +489,14 @@ /obj/machinery/disposal/proc/flush() flushing = TRUE flush_animation() - var/obj/structure/disposalholder/H = new() // virtual holder object which actually - // travels through the pipes. - manage_wrapping(H) sleep(10) if(last_sound + DISPOSAL_SOUND_COOLDOWN < world.time) playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) last_sound = world.time sleep(5) // wait for animation to finish + var/obj/structure/disposalholder/H = new(src) // virtual holder object which actually + // travels through the pipes. + manage_wrapping(H) H.init(src) // copy the contents of disposer to holder air_contents = new() // The holder just took our gas; replace it H.start(src) // start the holder processing movement diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 9ab82d748f7..cb12d598fb5 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -518,7 +518,7 @@ T1.shuttleRotate(rotation) var/turf/new_ceiling = GET_TURF_ABOVE(T1) // Do it before atmos readjust. - if(new_ceiling) + if(new_ceiling && (isspaceturf(new_ceiling) || isopenspaceturf(new_ceiling))) //Check for open one, not wall // generate ceiling new_ceiling.ChangeTurf(/turf/simulated/floor/engine/hull/ceiling) diff --git a/code/modules/surgery/abstract_steps.dm b/code/modules/surgery/abstract_steps.dm new file mode 100644 index 00000000000..48a0fc7e2ff --- /dev/null +++ b/code/modules/surgery/abstract_steps.dm @@ -0,0 +1,357 @@ +/** + * This file consists of how we manage somewhat non-linear surgeries. + * Essentially what goes on here is that we have some "proxy" surgery steps that get inserted into existing surgeries at key points. + * Depending on the tool used after that point, the proxy step chooses the next step(s) that should be executed. + * + * These proxy steps use a list of surgeries, so a full procedure can be inserted into an existing surgery. Just make sure that the user's state + * after an intermediate surgery is (in the context of the surgery) identical to before the intermediate surgery. Don't heal things that will + * need to be healed during the surgery, for example. + * + * Adding a new intermediate surgery: + * - Define a new intermediate surgery datum with the list of steps that you want to inject. This forms one surgery "branch". + * - Define a new proxy surgery step with branches containing the typepath of your new surgery datum. + * - Insert that surgery step into an existing surgery. + */ + +#define SURGERY_TOOL_HAND "hand" +#define SURGERY_TOOL_ANY "any" + +/** + * A partial surgery that consists of a few steps that may be found in the middle of another operation. + * An existing surgery can yield to an intermediate surgery for a few steps by way of a proxy surgery_step. + */ +/datum/surgery/intermediate + abstract = TRUE + +/** + * Here's the special sauce: a surgery step that can pretend to be a few different surgery steps. + * These proxy steps will, depending on the tool that's used, either continue to the next surgery step, or temporarily spin off a new surgery + * by adding new steps to the current surgery. + */ +/datum/surgery_step/proxy + name = "Intermediate Operation" + /// Optional surgery TYPES that we can branch out to + /// Note that these must not share any starting tools. + var/list/branches = list() + + /// Initialized versions of types specified in branches. + /// Don't fill this yourself, instead fill branches with surgery TYPES. + var/list/datum/surgery/branches_init = list() + + /// These tools are just...special cases. + /// If we're using one of these tools and there's a tool conflict with the original surgery, + /// just ignore any branches and continue with the original surgery. + var/list/overriding_tools = list( + /obj/item/scalpel/laser/manager // IMS + ) + + /// Whether or not we should add ourselves as a step after we run a branch. This doesn't apply to failures, those will always add ourselves after. + var/insert_self_after = TRUE + +/datum/surgery_step/proxy/New() + if(length(branches_init)) + CRASH("Proxy surgery [src] was given some initialized branches. Branching steps must be specified in var/branches, not var/branches_init.") + + for(var/branch_type in branches) + if(!ispath(branch_type, /datum/surgery)) + CRASH("proxy surgery [src] was given a branch type [branch_type] that isn't a subtype of /datum/surgery!") + var/datum/surgery/new_surgery = new branch_type() + // Add our proxy step as well so we can choose to perform multiple branches after we finish this one. + new_surgery.steps.Add(src) + branches_init.Add(new branch_type()) + + ..() + +/datum/surgery_step/proxy/Destroy(force, ...) + QDEL_LIST(branches_init) + return ..() + +/datum/surgery_step/proxy/get_step_information(datum/surgery/surgery) + var/datum/surgery_step/cur = surgery.get_surgery_next_step() + var/step_names = list() + for(var/datum/surgery/surg in branches_init) + step_names += surg.get_surgery_step() + step_names += cur // put this one on the end + + return english_list(step_names, "Nothing...? If you see this, tell a coder.", ", or ") + +/datum/surgery_step/proxy/try_op(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + + var/list/starting_tools = list() + + // pull this out separately. We don't want to move ahead with an any item surgery unless we've exhausted all of our + // other options. + // There will also only ever be one possibility for this in a surgery step (unless someone screwed up) + var/datum/surgery/possible_any_surgery + var/datum/surgery/next_surgery + + var/datum/surgery_step/next_surgery_step = surgery.get_surgery_next_step() + var/datum/surgery_step/first_step + + // Check the tools that all of our branches expect to use, and see if any of them match the current tool. + // sanity checks first though! Make sure we don't have any tool conflicts. + // A tool should only lead to one surgery step. + + // (If there's a tool that could be used for a few different steps, though, make sure it's put into overriding steps) + + for(var/datum/surgery/S in branches_init) + first_step = S.get_surgery_step() + + if(!tool && first_step.accept_hand) + if(SURGERY_TOOL_HAND in starting_tools) + CRASH("[src] was provided with multiple branches that allow an empty hand.") + next_surgery = S // if there's no tool, just proceed forward. + starting_tools.Add(SURGERY_TOOL_HAND) + + else if(first_step.accept_any_item) + if(SURGERY_TOOL_ANY in starting_tools) + CRASH("[src] was provided with multiple branches that allow any tool.") + possible_any_surgery = S + starting_tools.Add(SURGERY_TOOL_ANY) + + + for(var/allowed in first_step.allowed_tools) + if(ispath(allowed) && istype(tool, allowed) || (tool && istype(tool) && tool.tool_behaviour == allowed)) + next_surgery = S + if(allowed in starting_tools && !(allowed in overriding_tools)) + CRASH("[src] was provided with multiple branches that start with tool [allowed].") + else + starting_tools.Add(allowed) + + // if we didn't set our next surgery (defined by the tool in use), check to see if a catch-all like accept hand or any item work + if(!next_surgery) + if((SURGERY_TOOL_ANY in starting_tools) && tool) + next_surgery = possible_any_surgery + + + // If this is set to true, the tool in use will force the next step in the main surgery. + var/overridden_tool = FALSE + + // Also check the next surgery step. + if(!isnull(next_surgery_step)) + + if(istype(next_surgery_step, /datum/surgery_step/proxy)) + // It might make sense to support this, and I think the flow could work (just treating them like a single step, sorta) + // but I think for simplicity's sake it's better to just say no + CRASH("[src] was followed by another proxy surgery step [next_surgery_step] in [surgery].") + + if((SURGERY_TOOL_HAND in starting_tools) && next_surgery_step.accept_hand) + CRASH("[src] has a conflict with the next main step [next_surgery_step] in surgery [surgery]: both require an open hand.") + + if((SURGERY_TOOL_ANY in starting_tools) && next_surgery_step.accept_any_item) + CRASH("[src] has a conflict with the next main step [next_surgery_step] in surgery [surgery]: both accept any item.") + + if(!tool && next_surgery_step.accept_hand && !(SURGERY_TOOL_HAND in starting_tools)) + next_surgery = surgery + + for(var/allowed in next_surgery_step.allowed_tools) + // debug IMS stuff, check it here so it forces the next surgery if it's being used + if(istype(tool, /obj/item/scalpel/laser/manager/debug)) + if(!ispath(allowed) && (allowed in GLOB.surgery_tool_behaviors)) + next_surgery = surgery + next_surgery_step.allowed_tools[tool.type] = 100 + next_surgery_step.implement_type = tool.type + overridden_tool = TRUE + break + + if(allowed in starting_tools) + if(allowed in overriding_tools) + overridden_tool = TRUE + break + else + CRASH("[src] has a tool conflict ([allowed]) with the next step [next_surgery_step] in the surgery it was called from ([surgery])") + + if(tool && istype(tool) && (ispath(allowed) && istype(tool, allowed) || tool.tool_behaviour == allowed)) + next_surgery = surgery + + // Check if we might allow this under the any item rule if it doesn't fit into any other category. We don't want to accidentally miss a tool conflict. + if(tool && next_surgery_step.accept_any_item && !(SURGERY_TOOL_ANY in starting_tools)) + next_surgery = surgery + + if(!next_surgery) + // If the tool used doesn't work for any branch, just ignore it. + return FALSE + + if(overridden_tool || next_surgery == surgery || !next_surgery) + // Continue along with the original surgery. + return try_next_step(user, target, target_zone, tool, surgery, null, TRUE, TRUE) + + if(!target.can_run_surgery(next_surgery, user)) + // Make sure the target can support the surgery. + // note that this should be run before can_start + return TRUE + + if(!next_surgery.can_start(user, target)) + // If we wouldn't be able to start the next surgery anyway, don't move past this step. + // Let them try other tools if necessary. + return TRUE + + return try_next_step(user, target, target_zone, tool, surgery, next_surgery.steps) + +/** + * Test the next step, but don't fully commit to it unless it completes successfully. + * If the next step doesn't fully complete (such as being interrupted or failing), we'll insert ourselves again to bring us back + * to the "base" state. + * If it does, we'll add the subsequent steps to the surgery and continue down the expected branch. If you complete the surgery step, it + * means you've committed to what comes next. + * Part of the motivation behind this is that I don't want to mutate a surgery retroactively. We can insert, but we shouldn't be changing anything + * behind us. + * + * Arguments: + * * next_surgery_steps - the steps for the branching surgery to add to the current surgery. If there's no branching surgery (or this would continue the main surgery) ignore this. + * * override_adding_self - If true, then regardless of the value of insert_self_after, we won't add ourselves in as another step. + * * readd_step_on_fail - If true, when we fail a step we'll add the failed step again after the proxy surgery. This is necessary for main surgeries. + * (for other arguments, see try_op()) + */ +/datum/surgery_step/proxy/proc/try_next_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/running_surgery, list/next_surgery_steps, override_adding_self, readd_step_on_fail) + + var/list/following_steps = list() + + if(length(next_surgery_steps)) + + // add the first step from the following surgery into the surgery list, to make it the next step. + running_surgery.steps.Insert(running_surgery.step_number + 1, next_surgery_steps[1]) + + // grab the remaining steps to possibly insert after this surgery, depending on what we're doing + // skip the current step though, since if our try_op works, we've completed it. + following_steps = next_surgery_steps.Copy() + following_steps.Cut(1, 2) + + var/datum/surgery_step/next_step = running_surgery.get_surgery_step(add_number = 1) + var/step_status = next_step.try_op(user, target, target_zone, tool, running_surgery) + running_surgery.step_number++ /// after for operation comp correct update + + if(step_status != SURGERY_INITIATE_SUCCESS) + // always add ourselves after a failure so someone can make a different choice. + running_surgery.steps.Insert(running_surgery.step_number + 1, type) + running_surgery.step_number++ + + // Since we've already bumped up the step count, if we tried the main branch in the surgery and failed it, we need to add both + // the proxy step and the main step to keep them both as options. + if(readd_step_on_fail) + running_surgery.steps.Insert(running_surgery.step_number + 1, next_step.type) + + else + // Insert the steps in our intermediate surgery into the current surgery. + // This is how we keep our surgeries still technically linear. + if(insert_self_after && !override_adding_self) + // add ourselves afterwards as well so we can repeat this step + following_steps.Add(type) + + // insert at the current step number since we're not trying to bump it up + running_surgery.steps.Insert(running_surgery.step_number, following_steps) + + + return step_status + + +// Some intermediate surgeries +/datum/surgery/intermediate/bleeding + // don't worry about these names, they won't appear anywhere. + name = "Internal Bleeding (abstract)" + desc = "An intermediate surgery to fix internal bleeding while a patient is undergoing another procedure." + steps = list(/datum/surgery_step/fix_vein) + possible_locs = list( + BODY_ZONE_CHEST, + BODY_ZONE_HEAD, + BODY_ZONE_L_ARM, + BODY_ZONE_PRECISE_L_HAND, + BODY_ZONE_R_ARM, + BODY_ZONE_PRECISE_R_HAND, + BODY_ZONE_R_LEG, + BODY_ZONE_PRECISE_R_FOOT, + BODY_ZONE_L_LEG, + BODY_ZONE_PRECISE_L_FOOT, + BODY_ZONE_PRECISE_GROIN, + BODY_ZONE_TAIL, + BODY_ZONE_WING, + ) + +/datum/surgery/intermediate/bleeding/can_start(mob/user, mob/living/carbon/target) + . = ..() + if(!.) + return FALSE + var/mob/living/carbon/human/H = target + var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) + if(affected.has_internal_bleeding()) + return TRUE + // Normally, adding to_chat to can_start is poor practice since this gets called when listing surgery steps. + // It's alright for intermediate surgeries, though, since they never get called like that. + to_chat(user, span_warning("The veins in [target]'s [affected] seem to be in perfect condition, they don't need mending.")) + return FALSE + +/datum/surgery/intermediate/mendbone + name = "Mend Bone (abstract)" + desc = "An intermediate surgery to mend bones while a patient is undergoing another procedure." + steps = list(/datum/surgery_step/glue_bone, /datum/surgery_step/set_bone, /datum/surgery_step/finish_bone) + possible_locs = list( + BODY_ZONE_CHEST, + BODY_ZONE_HEAD, + BODY_ZONE_L_ARM, + BODY_ZONE_PRECISE_L_HAND, + BODY_ZONE_R_ARM, + BODY_ZONE_PRECISE_R_HAND, + BODY_ZONE_R_LEG, + BODY_ZONE_PRECISE_R_FOOT, + BODY_ZONE_L_LEG, + BODY_ZONE_PRECISE_L_FOOT, + BODY_ZONE_PRECISE_GROIN, + BODY_ZONE_TAIL, + BODY_ZONE_WING, + ) + +/datum/surgery/intermediate/mendbone/plasma + name = "Plasma Mend Bone (abstract)" + desc = "An intermediate surgery to mend bones while a patient is undergoing another procedure." + steps = list(/datum/surgery_step/glue_bone/plasma) + + +/datum/surgery/intermediate/mendbone/can_start(mob/user, mob/living/carbon/target) + . = ..() + if(!.) + return FALSE + var/mob/living/carbon/human/H = target + var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) + if(!affected) + return FALSE + if(affected.cannot_break) + return FALSE + if(affected.has_fracture()) + return TRUE + else + to_chat(user, span_warning("The bones in [target]'s [affected] look fully intact, they don't need mending.")) + return FALSE + +/// Proxy surgery step to allow healing bleeding and mending bones. +/// Should be added into surgeries just after the first three standard steps. +/datum/surgery_step/proxy/open_organ + name = "mend internal bleeding or mend bone (proxy)" + branches = list( + /datum/surgery/intermediate/bleeding, + /datum/surgery/intermediate/mendbone + ) + +/datum/surgery_step/proxy/open_organ/plasma + name = "mend internal bleeding or mend plasma bone (proxy)" + branches = list( + /datum/surgery/intermediate/bleeding, + /datum/surgery/intermediate/mendbone/plasma + ) + +/// Mend IB without healing bones +/datum/surgery_step/proxy/ib + name = "mend internal bleeding (proxy)" + branches = list( + /datum/surgery/intermediate/bleeding + ) + +/// The robotic equivalent +/datum/surgery_step/proxy/robotics/repair_limb + name = "Repair Limb (proxy)" + branches = list( + /datum/surgery/intermediate/robotics/repair/burn, + /datum/surgery/intermediate/robotics/repair/brute + ) + +#undef SURGERY_TOOL_ANY +#undef SURGERY_TOOL_HAND diff --git a/code/modules/surgery/bones.dm b/code/modules/surgery/bones.dm index 77132470d84..5cbc9dccb82 100644 --- a/code/modules/surgery/bones.dm +++ b/code/modules/surgery/bones.dm @@ -3,11 +3,20 @@ // BONE SURGERY // ////////////////////////////////////////////////////////////////// ///Surgery Datums + /datum/surgery/bone_repair name = "Bone Repair" - steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/glue_bone, /datum/surgery_step/set_bone, /datum/surgery_step/finish_bone, /datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/ib, // Only proxy IB here + /datum/surgery_step/glue_bone, + /datum/surgery_step/set_bone, + /datum/surgery_step/finish_bone, + /datum/surgery_step/generic/cauterize + ) possible_locs = list( - BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, @@ -16,14 +25,39 @@ BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, - BODY_ZONE_PRECISE_GROIN, BODY_ZONE_TAIL, BODY_ZONE_WING, ) + restricted_speciestypes = list(/datum/species/plasmaman) + +/datum/surgery/bone_repair/non_hitin + name = "Bone Repair" + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/ib, // Only proxy IB here + /datum/surgery_step/glue_bone, + /datum/surgery_step/set_bone, + /datum/surgery_step/finish_bone, + /datum/surgery_step/generic/cauterize + ) + possible_locs = list( + BODY_ZONE_CHEST, + BODY_ZONE_PRECISE_GROIN, + ) + restricted_speciestypes = list(/datum/species/plasmaman, /datum/species/wryn, /datum/species/kidan) /datum/surgery/bone_repair/plasmaman name = "Plasmaman Bone Repair" - steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/glue_bone/plasma, /datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/ib, // Only proxy IB here + /datum/surgery_step/glue_bone/plasma, + /datum/surgery_step/generic/cauterize + ) possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_L_ARM, @@ -38,64 +72,81 @@ BODY_ZONE_TAIL, BODY_ZONE_WING, ) + target_speciestypes = list(/datum/species/plasmaman) + restricted_speciestypes = null + +/datum/surgery/bone_repair/insect + name = "Insectoid Bone Repair" + steps = list( + /datum/surgery_step/open_encased/saw, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/proxy/ib, // Only proxy IB here + /datum/surgery_step/glue_bone, + /datum/surgery_step/set_bone, + /datum/surgery_step/finish_bone, + /datum/surgery_step/generic/cauterize + ) + possible_locs = list( + BODY_ZONE_CHEST, + BODY_ZONE_PRECISE_GROIN, + ) + target_speciestypes = list(/datum/species/wryn, /datum/species/kidan) + restricted_speciestypes = null /datum/surgery/bone_repair/skull name = "Skull Repair" - steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/glue_bone, /datum/surgery_step/mend_skull, /datum/surgery_step/finish_bone, /datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/ib, + /datum/surgery_step/glue_bone, + /datum/surgery_step/set_bone/mend_skull, + /datum/surgery_step/finish_bone, + /datum/surgery_step/generic/cauterize + ) possible_locs = list(BODY_ZONE_HEAD) + restricted_speciestypes = list(/datum/species/plasmaman) /datum/surgery/bone_repair/plasmaman/skull name = "Plasmaman Skull Repair" - steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/glue_bone/plasma, /datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/ib, // Only proxy IB here + /datum/surgery_step/glue_bone/plasma, + /datum/surgery_step/generic/cauterize + ) possible_locs = list(BODY_ZONE_HEAD) + target_speciestypes = list(/datum/species/plasmaman) + restricted_speciestypes = null /datum/surgery/bone_repair/can_start(mob/user, mob/living/carbon/target) - if(istype(target,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(isplasmaman(H)) - return 0 - if(!affected) - return 0 - if(affected.is_robotic()) - return 0 - if(affected.cannot_break) - return 0 - if(affected.has_fracture()) - return 1 - return 1 - -/datum/surgery/bone_repair/plasmaman/can_start(mob/user, mob/living/carbon/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(!isplasmaman(H)) - return 0 - if(!affected) - return 0 - if(affected.is_robotic()) - return 0 - if(affected.cannot_break) - return 0 - if(affected.has_fracture()) - return 1 - if(isplasmaman(H)) - return 1 - return 0 - + . = ..() + if(!.) + return FALSE + var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) + if(affected.cannot_break) + return FALSE + if(!affected.has_fracture()) + return FALSE //surgery steps /datum/surgery_step/glue_bone name = "mend bone" allowed_tools = list( - /obj/item/bonegel = 100, \ - /obj/item/screwdriver = 90 + TOOL_BONEGEL = 100, + TOOL_SCREWDRIVER = 90 ) - can_infect = 1 - blood_level = 1 + can_infect = TRUE + blood_level = SURGERY_BLOODSPREAD_HANDS - time = 24 + time = 2.4 SECONDS /datum/surgery_step/glue_bone/plasma name = "mend bone" @@ -103,147 +154,147 @@ allowed_tools = list( /obj/item/stack/sheet/mineral/plasma = 100 ) - can_infect = 1 - blood_level = 1 - - time = 10 + can_infect = TRUE + blood_level = SURGERY_BLOODSPREAD_HANDS -/datum/surgery_step/glue_bone/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - return affected && !affected.is_robotic() && !(affected.cannot_break) + time = 1 SECONDS /datum/surgery_step/glue_bone/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] starts applying medication to the damaged bones in [target]'s [affected.name] with \the [tool]." , \ - "You start applying medication to the damaged bones in [target]'s [affected.name] with \the [tool].") + user.visible_message( + "[user] starts applying medication to the damaged bones in [target]'s [affected.name] with \the [tool].", + "You start applying medication to the damaged bones in [target]'s [affected.name] with \the [tool]." + ) target.custom_pain("Something in your [affected.name] is causing you a lot of pain!") - ..() + return ..() /datum/surgery_step/glue_bone/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message(" [user] applies some [tool] to [target]'s bone in [affected.name]", \ - " You apply some [tool] to [target]'s bone in [affected.name] with \the [tool].") - - return 1 + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + span_notice("[user] applies some [tool] to [target]'s bone in [affected.name]"), + span_notice("You apply some [tool] to [target]'s bone in [affected.name] with \the [tool].") + ) + return SURGERY_STEP_CONTINUE /datum/surgery_step/glue_bone/plasma/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message(" [user] applies some [tool] to [target]'s bone in [affected.name]. You see the plasma flowing through the bones, reattaching them!", \ - " You apply some [tool] to [target]'s bone in [affected.name] with \the [tool]. You see the plasma flowing through the bones, reattaching them!") - affected.mend_fracture() - return 1 + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message( + span_notice("[user] applies some [tool] to [target]'s bone in [affected.name]. You see the plasma flowing through the bones, reattaching them!"), \ + span_notice("You apply some [tool] to [target]'s bone in [affected.name] with \the [tool]. You see the plasma flowing through the bones, reattaching them!") + ) + affected.mend_fracture() + return SURGERY_STEP_CONTINUE /datum/surgery_step/glue_bone/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message(" [user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!" , \ - " Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!") - return 0 + var/obj/item/organ/external/affected = target.get_organ(target_zone) + + user.visible_message( + span_warning("[user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!"), + span_warning("Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!") + ) + affected.receive_damage(3) + return SURGERY_STEP_RETRY /datum/surgery_step/set_bone name = "set bone" allowed_tools = list( - /obj/item/bonesetter = 100, \ - /obj/item/wrench = 90 \ + TOOL_BONESET = 100, + TOOL_WRENCH = 90 ) - time = 32 - -/datum/surgery_step/set_bone/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - return affected && !affected.is_robotic() + time = 3.2 SECONDS /datum/surgery_step/set_bone/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] is beginning to set the bone in [target]'s [affected.name] in place with \the [tool]." , \ - "You are beginning to set the bone in [target]'s [affected.name] in place with \the [tool].") + user.visible_message( + "[user] is beginning to set the bone in [target]'s [affected.name] in place with \the [tool].", + "You are beginning to set the bone in [target]'s [affected.name] in place with \the [tool]." + ) target.custom_pain("The pain in your [affected.name] is going to make you pass out!") - ..() + return ..() /datum/surgery_step/set_bone/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(affected.has_fracture()) - user.visible_message(" [user] sets the bone in [target]'s [affected.name] in place with \the [tool].", \ - " You set the bone in [target]'s [affected.name] in place with \the [tool].") - return 1 - else - user.visible_message(" [user] sets the bone in [target]'s [affected.name] in place with \the [tool].", \ - " You set the bone in [target]'s [affected.name] in place with \the [tool].") - return 1 + user.visible_message( + span_notice("[user] sets the bone in [target]'s [affected.name] in place with \the [tool]."), + span_notice("You set the bone in [target]'s [affected.name] in place with \the [tool].") + ) + return SURGERY_STEP_CONTINUE /datum/surgery_step/set_bone/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message(" [user]'s hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!" , \ - " Your hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!") + user.visible_message( + span_warning("[user]'s hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!"), + span_warning("Your hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!") + ) affected.receive_damage(5) - return 0 + return SURGERY_STEP_RETRY -/datum/surgery_step/mend_skull +/datum/surgery_step/set_bone/mend_skull name = "mend skull" - allowed_tools = list( - /obj/item/bonesetter = 100, \ - /obj/item/wrench = 90 \ +/datum/surgery_step/set_bone/mend_skull/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + user.visible_message( + "[user] is beginning piece together [target]'s skull with \the [tool].", + "You are beginning piece together [target]'s skull with \the [tool]." ) + return ..() - time = 32 - -/datum/surgery_step/mend_skull/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - return affected && !affected.is_robotic() && affected.limb_zone == BODY_ZONE_HEAD - -/datum/surgery_step/mend_skull/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user] is beginning piece together [target]'s skull with \the [tool]." , \ - "You are beginning piece together [target]'s skull with \the [tool].") - ..() - -/datum/surgery_step/mend_skull/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) +/datum/surgery_step/set_bone/mend_skull/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message(" [user] sets [target]'s [affected.encased] with \the [tool]." , \ - " You set [target]'s [affected.encased] with \the [tool].") + user.visible_message( + span_notice("[user] sets [target]'s [affected.encased] with \the [tool]."), + span_notice("You set [target]'s [affected.encased] with \the [tool].") + ) - return 1 + return SURGERY_STEP_CONTINUE -/datum/surgery_step/mend_skull/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) +/datum/surgery_step/set_bone/mend_skull/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, damaging [target]'s face with \the [tool]!" , \ - "Your hand slips, damaging [target]'s face with \the [tool]!") - var/obj/item/organ/external/head/h = affected - h.receive_damage(10) - h.disfigure() - return 0 + user.visible_message( + span_warning("[user]'s hand slips, damaging [target]'s face with \the [tool]!"), + span_warning(">Your hand slips, damaging [target]'s face with \the [tool]!") + ) + var/obj/item/organ/external/head/H = affected + H.receive_damage(10) + H.disfigure() + return SURGERY_STEP_RETRY /datum/surgery_step/finish_bone name = "medicate bones" allowed_tools = list( - /obj/item/bonegel = 100, \ - /obj/item/screwdriver = 90 + TOOL_BONEGEL = 100, + TOOL_SCREWDRIVER = 90 ) - can_infect = 1 - blood_level = 1 - - time = 24 + can_infect = TRUE + blood_level = SURGERY_BLOODSPREAD_HANDS -/datum/surgery_step/finish_bone/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - return affected && !affected.is_robotic() + time = 2.4 SECONDS /datum/surgery_step/finish_bone/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] starts to finish mending the damaged bones in [target]'s [affected.name] with \the [tool].", \ - "You start to finish mending the damaged bones in [target]'s [affected.name] with \the [tool].") - ..() + user.visible_message( + "[user] starts to finish mending the damaged bones in [target]'s [affected.name] with \the [tool].", + "You start to finish mending the damaged bones in [target]'s [affected.name] with \the [tool]." + ) + return ..() -/datum/surgery_step/finish_bone/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/finish_bone/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 mended the damaged bones in [target]'s [affected.name] with \the [tool]." , \ - " You have mended the damaged bones in [target]'s [affected.name] with \the [tool]." ) + user.visible_message( + span_notice("[user] has mended the damaged bones in [target]'s [affected.name] with \the [tool]."), + span_notice("You have mended the damaged bones in [target]'s [affected.name] with \the [tool].") + ) affected.mend_fracture() - return TRUE + return SURGERY_STEP_CONTINUE /datum/surgery_step/finish_bone/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message(" [user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!" , \ - " Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!") - return 0 + user.visible_message( + span_warning("[user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!"), + span_warning("Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!") + ) + affected.receive_damage(3) + return SURGERY_STEP_RETRY diff --git a/code/modules/surgery/cavity_implant.dm b/code/modules/surgery/cavity_implant.dm index a52912e9d6e..39b38fa88b5 100644 --- a/code/modules/surgery/cavity_implant.dm +++ b/code/modules/surgery/cavity_implant.dm @@ -1,23 +1,65 @@ /datum/surgery/cavity_implant name = "Cavity Implant/Removal" - steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/open_encased/saw, - /datum/surgery_step/open_encased/retract, /datum/surgery_step/cavity/make_space,/datum/surgery_step/cavity/place_item,/datum/surgery_step/cavity/close_space,/datum/surgery_step/open_encased/close,/datum/surgery_step/glue_bone, /datum/surgery_step/set_bone,/datum/surgery_step/finish_bone,/datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/ib, // just do IB here since we're sawing the bone anyway + /datum/surgery_step/open_encased/saw, + /datum/surgery_step/open_encased/retract, + /datum/surgery_step/cavity/make_space, + /datum/surgery_step/proxy/cavity_manipulation, + /datum/surgery_step/cavity/close_space, + /datum/surgery_step/open_encased/close, + /datum/surgery_step/glue_bone, + /datum/surgery_step/set_bone, + /datum/surgery_step/finish_bone, + /datum/surgery_step/generic/cauterize + ) possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_HEAD, ) + restricted_speciestypes = list(/datum/species/kidan, /datum/species/wryn, /datum/species/plasmaman) /datum/surgery/cavity_implant/soft name = "Cavity Implant/Removal" - steps = list(/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/cavity/make_space,/datum/surgery_step/cavity/place_item,/datum/surgery_step/cavity/close_space,/datum/surgery_step/generic/cauterize) + desc = "Implant an object into a cavity not protected by bone." + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/ib, // just do IB here since we're sawing the bone anyway + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/cavity/make_space, + /datum/surgery_step/proxy/cavity_manipulation, + /datum/surgery_step/cavity/close_space, + /datum/surgery_step/generic/cauterize + ) possible_locs = list(BODY_ZONE_PRECISE_GROIN) /datum/surgery/cavity_implant/insect name = "Insectoid Cavity Implant/Removal" - steps = list(/datum/surgery_step/open_encased/saw, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/retract_skin, - /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/cavity/make_space,/datum/surgery_step/cavity/place_item,/datum/surgery_step/cavity/close_space,/datum/surgery_step/open_encased/close,/datum/surgery_step/glue_bone, /datum/surgery_step/set_bone,/datum/surgery_step/finish_bone,/datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/open_encased/saw, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/proxy/ib, + /datum/surgery_step/cavity/make_space, + /datum/surgery_step/proxy/cavity_manipulation, + /datum/surgery_step/cavity/close_space, + /datum/surgery_step/open_encased/close, + /datum/surgery_step/glue_bone, + /datum/surgery_step/set_bone, + /datum/surgery_step/finish_bone, + /datum/surgery_step/generic/cauterize + ) + target_speciestypes = list(/datum/species/kidan, /datum/species/wryn) + restricted_speciestypes = null possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_HEAD, @@ -26,85 +68,114 @@ /datum/surgery/cavity_implant/plasmaman name = "Plasmaman Cavity Implant/Removal" - steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/open_encased/saw, - /datum/surgery_step/open_encased/retract, /datum/surgery_step/cavity/make_space,/datum/surgery_step/cavity/place_item,/datum/surgery_step/cavity/close_space,/datum/surgery_step/open_encased/close,/datum/surgery_step/glue_bone/plasma,/datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/ib, // just do IB here since we're sawing the bone anyway + /datum/surgery_step/open_encased/saw, + /datum/surgery_step/open_encased/retract, + /datum/surgery_step/cavity/make_space, + /datum/surgery_step/proxy/cavity_manipulation, + /datum/surgery_step/cavity/close_space, + /datum/surgery_step/open_encased/close, + /datum/surgery_step/glue_bone/plasma, + /datum/surgery_step/generic/cauterize + ) possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_HEAD, ) + target_speciestypes = list(/datum/species/plasmaman) + restricted_speciestypes = null /datum/surgery/cavity_implant/plasmaman/soft - steps = list(/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/cavity/make_space,/datum/surgery_step/cavity/place_item,/datum/surgery_step/cavity/close_space,/datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/ib, // just do IB here since we're sawing the bone anyway + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/cavity/make_space, + /datum/surgery_step/proxy/cavity_manipulation, + /datum/surgery_step/cavity/close_space, + /datum/surgery_step/generic/cauterize + ) possible_locs = list(BODY_ZONE_PRECISE_GROIN) /datum/surgery/cavity_implant/synth name = "Robotic Cavity Implant/Removal" - steps = list(/datum/surgery_step/robotics/external/unscrew_hatch,/datum/surgery_step/robotics/external/open_hatch,/datum/surgery_step/cavity/place_item,/datum/surgery_step/robotics/external/close_hatch) - requires_organic_bodypart = 0 + requires_organic_bodypart = FALSE + steps = list( + /datum/surgery_step/robotics/external/unscrew_hatch, + /datum/surgery_step/robotics/external/open_hatch, + /datum/surgery_step/proxy/cavity_manipulation/robotic, + /datum/surgery_step/cavity/close_space, + /datum/surgery_step/robotics/external/close_hatch + ) possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN, ) -/datum/surgery/cavity_implant/can_start(mob/user, mob/living/carbon/human/target) - var/mob/living/carbon/human/H = target - if(iskidan(H) || iswryn(H) || isplasmaman(H)) - return FALSE - if(!istype(target)) - return FALSE - var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) - if(!affected) - return FALSE - if(affected.is_robotic()) - return FALSE - return TRUE +/datum/surgery/cavity_implant/synth + name = "Robotic Cavity Implant/Removal" + steps = list( + /datum/surgery_step/robotics/external/unscrew_hatch, + /datum/surgery_step/robotics/external/open_hatch, + /datum/surgery_step/proxy/cavity_manipulation/robotic, + /datum/surgery_step/robotics/external/close_hatch + ) + possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN) + requires_organic_bodypart = FALSE + +/datum/surgery_step/proxy/cavity_manipulation + name = "Cavity Manipulation (proxy)" + branches = list( + /datum/surgery/intermediate/open_cavity/implant, + /datum/surgery/intermediate/open_cavity/extract, + /datum/surgery/intermediate/bleeding + ) -/datum/surgery/cavity_implant/insect/can_start(mob/user, mob/living/carbon/human/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(!affected) - return FALSE - if(affected.is_robotic()) - return FALSE - if(!affected.encased) - return FALSE - if(iswryn(H) || iskidan(H)) - return TRUE - return FALSE - -/datum/surgery/cavity_implant/plasmaman/can_start(mob/user, mob/living/carbon/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(!affected) - return FALSE - if(affected.is_robotic()) - return FALSE - if(isplasmaman(H)) - return TRUE - return FALSE - -/datum/surgery/cavity_implant/synth/can_start(mob/user, mob/living/carbon/human/target) - if(!istype(target)) - return FALSE - var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) - if(!affected) - return FALSE - return affected.is_robotic() + insert_self_after = TRUE + +/datum/surgery_step/proxy/cavity_manipulation/robotic + name = "Robotic Cavity Manipulation (proxy)" + branches = list( + /datum/surgery/intermediate/open_cavity/implant/robotic, + /datum/surgery/intermediate/open_cavity/extract/robotic + ) -/datum/surgery_step/cavity - priority = 1 +/datum/surgery/intermediate/open_cavity + possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD) + +/datum/surgery/intermediate/open_cavity/implant + name = "implant object" + steps = list( + /datum/surgery_step/cavity/place_item + ) + +/datum/surgery/intermediate/open_cavity/extract + name = "extract object" + steps = list( + /datum/surgery_step/cavity/remove_item + ) + +/datum/surgery/intermediate/open_cavity/implant/robotic + requires_organic_bodypart = FALSE + +/datum/surgery/intermediate/open_cavity/extract/robotic + requires_organic_bodypart = FALSE /datum/surgery_step/cavity/proc/get_max_wclass(obj/item/organ/external/affected) switch(affected.limb_zone) if(BODY_ZONE_HEAD) - return 1 + return WEIGHT_CLASS_TINY if(BODY_ZONE_CHEST) - return 3 + return WEIGHT_CLASS_NORMAL if(BODY_ZONE_PRECISE_GROIN) - return 2 + return WEIGHT_CLASS_SMALL return 0 /datum/surgery_step/cavity/proc/get_cavity(obj/item/organ/external/affected) @@ -117,148 +188,196 @@ return "abdominal" return "" -/datum/surgery_step/cavity/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/cavity/proc/get_item_inside(obj/item/organ/external/affected) + var/obj/item/extracting + for(var/obj/item/I in affected.contents) + if(!istype(I, /obj/item/organ)) + extracting = I + break + + if(!extracting && affected.hidden) + extracting = affected.hidden + + return extracting + +/datum/surgery_step/cavity/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) - user.visible_message(" [user]'s hand slips, scraping around inside [target]'s [affected.name] with \the [tool]!", \ - " Your hand slips, scraping around inside [target]'s [affected.name] with \the [tool]!") + user.visible_message( + span_warning("[user]'s hand slips, scraping around inside [target]'s [affected.name] with \the [tool]!"), + span_warning("Your hand slips, scraping around inside [target]'s [affected.name] with \the [tool]!") + ) affected.receive_damage(20) + return SURGERY_STEP_RETRY /datum/surgery_step/cavity/make_space name = "make cavity space" allowed_tools = list( - /obj/item/surgicaldrill = 100, \ - /obj/item/pen = 90, \ - /obj/item/stack/rods = 60 + TOOL_DRILL = 100, + /obj/item/screwdriver/power = 90, + /obj/item/pen = 90, + /obj/item/stack/rods = 60 ) - time = 54 + time = 5.4 SECONDS -/datum/surgery_step/cavity/make_space/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/cavity/make_space/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 making some space inside [target]'s [get_cavity(affected)] cavity with \the [tool].", \ - "You start making some space inside [target]'s [get_cavity(affected)] cavity with \the [tool]." ) + user.visible_message( + "[user] starts making some space inside [target]'s [get_cavity(affected)] cavity with \the [tool].", + "You start making some space inside [target]'s [get_cavity(affected)] cavity with \the [tool]." + ) target.custom_pain("The pain in your chest is living hell!") - ..() + return ..() -/datum/surgery_step/cavity/make_space/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/cavity/make_space/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) - user.visible_message(" [user] makes some space inside [target]'s [get_cavity(affected)] cavity with \the [tool].", \ - " You make some space inside [target]'s [get_cavity(affected)] cavity with \the [tool]." ) + user.visible_message( + span_notice(" [user] makes some space inside [target]'s [get_cavity(affected)] cavity with \the [tool]."), + span_notice(" You make some space inside [target]'s [get_cavity(affected)] cavity with \the [tool].") + ) - return 1 + return SURGERY_STEP_CONTINUE /datum/surgery_step/cavity/close_space name = "close cavity space" allowed_tools = list( - /obj/item/scalpel/laser = 100, \ - /obj/item/cautery = 100, \ - /obj/item/clothing/mask/cigarette = 90, \ - /obj/item/lighter = 60, \ - /obj/item/weldingtool = 30 + /obj/item/scalpel/laser = 100, + TOOL_CAUTERY = 100, + /obj/item/clothing/mask/cigarette = 90, + /obj/item/lighter = 60, + TOOL_WELDER = 30 ) - time = 24 + time = 2.4 SECONDS -/datum/surgery_step/cavity/close_space/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/cavity/close_space/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 mending [target]'s [get_cavity(affected)] cavity wall with \the [tool].", \ - "You start mending [target]'s [get_cavity(affected)] cavity wall with \the [tool]." ) + user.visible_message( + "[user] starts mending [target]'s [get_cavity(affected)] cavity wall with \the [tool].", + "You start mending [target]'s [get_cavity(affected)] cavity wall with \the [tool]." + ) target.custom_pain("The pain in your chest is living hell!") - ..() + return ..() -/datum/surgery_step/cavity/close_space/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/cavity/close_space/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) - user.visible_message(" [user] mends [target]'s [get_cavity(affected)] cavity walls with \the [tool].", \ - " You mend [target]'s [get_cavity(affected)] cavity walls with \the [tool]." ) - - return 1 - + user.visible_message( + span_notice(" [user] mends [target]'s [get_cavity(affected)] cavity walls with \the [tool]."), + span_notice(" You mend [target]'s [get_cavity(affected)] cavity walls with \the [tool].") + ) -/datum/surgery_step/cavity/place_item - name = "implant/extract object" - accept_hand = 1 - accept_any_item = 1 - var/obj/item/IC = null - allowed_tools = list(/obj/item = 100) + return SURGERY_STEP_CONTINUE - time = 32 +/datum/surgery_step/cavity/remove_item + name = "extract object" + accept_hand = TRUE -/datum/surgery_step/cavity/place_item/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(!ishuman(target)) - return FALSE - var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(!affected) - to_chat(user, "\The [target] lacks a [parse_zone(target_zone)]!") - return FALSE - if(tool) - var/can_fit = !affected.hidden && tool.w_class <= get_max_wclass(affected) - if(!can_fit) - to_chat(user, "\The [tool] won't fit in \The [affected.name]!") - return FALSE +/datum/surgery_step/cavity/remove_item/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + // Check even if there isn't anything inside + user.visible_message( + "[user] checks for items in [target]'s [target_zone].", + span_notice("You check for items in [target]'s [target_zone]...") + ) return ..() -/datum/surgery_step/cavity/place_item/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/cavity/remove_item/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/obj/item/extracting var/obj/item/organ/external/affected = target.get_organ(target_zone) + for(var/obj/item/I in affected.contents) if(!istype(I, /obj/item/organ)) - IC = I + extracting = I break - if(istype(tool,/obj/item/cautery)) - to_chat(user, "You prepare to close the cavity wall.") - else if(tool) - user.visible_message("[user] starts putting \the [tool] inside [target]'s [get_cavity(affected)] cavity.", \ - "You start putting \the [tool] inside [target]'s [get_cavity(affected)] cavity." ) - else if(IC) - user.visible_message("[user] checks for items in [target]'s [target_zone].", "You check for items in [target]'s [target_zone]...") - else //no internal items..but we still need a message! - user.visible_message("[user] checks for items in [target]'s [target_zone].", "You check for items in [target]'s [target_zone]...") - target.custom_pain("The pain in your [target_zone] is living hell!") - ..() + if(!extracting && affected.hidden) + extracting = affected.hidden -/datum/surgery_step/cavity/place_item/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) + if(!extracting) + to_chat(user, span_warning("You don't find anything in [target]'s [target_zone].")) + return SURGERY_STEP_CONTINUE + user.visible_message( + span_notice("[user] pulls [extracting] out of [target]'s [target_zone]!"), + span_notice("You pull [extracting] out of [target]'s [target_zone].") + ) + user.put_in_hands(extracting, ignore_anim = FALSE) + affected.hidden = null + return SURGERY_STEP_CONTINUE + +/datum/surgery_step/cavity/remove_item/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( + span_warning("[user] grabs onto something else by mistake, damaging it!."), + span_warning("You grab onto something else inside [target]'s [get_cavity(affected)] cavity by mistake, damaging it!") + ) + + affected.receive_damage(rand(3,7)) + + return SURGERY_STEP_INCOMPLETE + +/datum/surgery_step/cavity/place_item + name = "implant object" + accept_any_item = TRUE + + time = 3.2 SECONDS + +/datum/surgery_step/cavity/place_item/tool_check(mob/user, obj/item/tool) if(istype(tool, /obj/item/disk/nuclear)) - to_chat(user, "Central command would kill you if you implanted the disk into someone.") - return FALSE//fail + to_chat(user, span_warning("Central command would kill you if you implanted the disk into someone.")) + return FALSE var/obj/item/disk/nuclear/datdisk = locate() in tool if(datdisk) - to_chat(user, "Central command would kill you if you implanted the disk into someone. Even if in a box. Especially in a box.") - return FALSE//fail + to_chat(user, span_warning("Central Command would kill you if you implanted the disk into someone. Especially if in a [tool].")) + return FALSE - if(istype(tool,/obj/item/organ)) - to_chat(user, "This isn't the type of surgery for organ transplants!") - return FALSE//fail + if(istype(tool, /obj/item/organ)) + to_chat(user, span_warning("This isn't the type of surgery for organ transplants!")) + return FALSE if(!user.can_unEquip(tool)) - to_chat(user, "[tool] is stuck to your hand, you can't put it in [target]!") + to_chat(user, span_warning("[tool] is stuck to your hand!")) + return FALSE + + if(istype(tool, /obj/item/cautery)) + // Pass it to the next step return FALSE - if(istype(tool,/obj/item/cautery)) - return TRUE//god this is ugly.... - else if(tool) - if(IC) - to_chat(user, "There seems to be something in there already!") - return TRUE - else - user.visible_message(" [user] puts \the [tool] inside [target]'s [get_cavity(affected)] cavity.", \ - " You put \the [tool] inside [target]'s [get_cavity(affected)] cavity." ) - if((tool.w_class > get_max_wclass(affected) / 2 && prob(50)) && affected.internal_bleeding()) - to_chat(user, " You tear some vessels trying to fit the object in the cavity.") - user.drop_transfer_item_to_loc(tool, affected) - affected.hidden = tool - tool.forceMove(affected) - return TRUE - else - if(IC) - user.visible_message("[user] pulls [IC] out of [target]'s [target_zone]!", "You pull [IC] out of [target]'s [target_zone].") - IC.forceMove(get_turf(target)) - user.put_in_hands(IC, ignore_anim = FALSE) - affected.hidden = null - return TRUE - else - to_chat(user, "You don't find anything in [target]'s [target_zone].") - return FALSE + return TRUE + + +/datum/surgery_step/cavity/place_item/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/can_fit = !affected.hidden && tool.w_class <= get_max_wclass(affected) + if(!can_fit) + to_chat(user, span_warning("\The [tool] won't fit in \the [affected]!")) + return SURGERY_BEGINSTEP_SKIP + + user.visible_message( + "[user] starts putting \the [tool] inside [target]'s [get_cavity(affected)] cavity.", + "You start putting \the [tool] inside [target]'s [get_cavity(affected)] cavity." + ) + target.custom_pain("The pain in your [target_zone] is living hell!") + return ..() + +/datum/surgery_step/cavity/place_item/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) + if(get_item_inside(affected)) + to_chat(user, span_notice("There seems to be something in there already!")) + return SURGERY_STEP_CONTINUE + + user.visible_message( + span_notice("[user] puts \the [tool] inside [target]'s [get_cavity(affected)] cavity."), + span_notice("You put \the [tool] inside [target]'s [get_cavity(affected)] cavity.") + ) + if((tool.w_class > get_max_wclass(affected) / 2 && prob(50) && !affected.is_robotic())) + user.visible_message( + span_warning("[user] tears some blood vessels trying to fit the object in the cavity!"), + span_danger("You tear some blood vessels trying to fit the object into the cavity!"), + span_warning("You hear some gentle tearing.")) + affected.internal_bleeding() + user.drop_transfer_item_to_loc(tool, target) + affected.hidden = tool + return SURGERY_STEP_CONTINUE diff --git a/code/modules/surgery/core_removal.dm b/code/modules/surgery/core_removal.dm index 98b6f98fe1d..eb2be007ddb 100644 --- a/code/modules/surgery/core_removal.dm +++ b/code/modules/surgery/core_removal.dm @@ -1,7 +1,7 @@ /datum/surgery/core_removal name = "core removal" steps = list(/datum/surgery_step/slime/cut_flesh, /datum/surgery_step/slime/extract_core) - allowed_mob = list(/mob/living/simple_animal/slime) + target_mobtypes = list(/mob/living/simple_animal/slime) possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_HEAD, @@ -18,58 +18,71 @@ BODY_ZONE_WING, ) -/datum/surgery_step/slime +/datum/surgery/core_removal/can_start(mob/user, mob/living/carbon/target) + . = ..() + return . && target.stat == DEAD -/datum/surgery_step/slime/is_valid_target(mob/living/simple_animal/slime/target) - return istype(target, /mob/living/simple_animal/slime) -/datum/surgery_step/slime/can_use(mob/living/user, mob/living/simple_animal/slime/target, target_zone, obj/item/tool) - return istype(target) && target.stat == DEAD +/datum/surgery_step/slime /datum/surgery_step/slime/cut_flesh - allowed_tools = list(/obj/item/scalpel = 100, /obj/item/melee/energy/sword = 75, /obj/item/kitchen/knife = 65, /obj/item/shard = 45) - time = 16 + allowed_tools = list( + TOOL_SCALPEL = 100, + /obj/item/melee/energy/sword = 75, + /obj/item/kitchen/knife = 65, + /obj/item/shard = 45 + ) + time = 1.6 SECONDS /datum/surgery_step/slime/cut_flesh/begin_step(mob/user, mob/living/simple_animal/slime/target, target_zone, obj/item/tool) user.visible_message("[user] starts cutting through [target]'s flesh with \the [tool].", "You start cutting through [target]'s flesh with \the [tool].") + return ..() /datum/surgery_step/slime/cut_flesh/end_step(mob/living/user, mob/living/simple_animal/slime/target, target_zone, obj/item/tool) - user.visible_message(" [user] cuts through [target]'s flesh with \the [tool].", - " You cut through [target]'s flesh with \the [tool], revealing its silky innards.") - return TRUE + user.visible_message(span_notice("[user] cuts through [target]'s flesh with \the [tool]."), + span_notice(" You cut through [target]'s flesh with \the [tool], revealing its silky innards.")) + return SURGERY_STEP_CONTINUE /datum/surgery_step/slime/cut_flesh/fail_step(mob/living/user, mob/living/simple_animal/slime/target, target_zone, obj/item/tool) - user.visible_message(" [user]'s hand slips, tearing [target]'s flesh with \the [tool]!", \ - " Your hand slips, tearing [target]'s flesh with \the [tool]!") - return FALSE + user.visible_message( + span_warning("[user]'s hand slips, tearing [target]'s flesh with \the [tool]!"), + span_warning("Your hand slips, tearing [target]'s flesh with \the [tool]!") + ) + return SURGERY_STEP_RETRY /datum/surgery_step/slime/extract_core name = "extract core" - allowed_tools = list(/obj/item/hemostat = 100, /obj/item/crowbar = 100) - time = 16 + allowed_tools = list(TOOL_HEMOSTAT = 100, TOOL_CROWBAR = 100) + time = 1.6 SECONDS /datum/surgery_step/slime/extract_core/begin_step(mob/user, mob/living/simple_animal/slime/target, target_zone, obj/item/tool) - user.visible_message("[user] begins to extract a core from [target].", - "You begin to extract a core from [target]...") + user.visible_message( + span_notice("[user] begins to extract a core from [target]."), + span_notice("You begin to extract a core from [target]...") + ) + return ..() + /datum/surgery_step/slime/extract_core/end_step(mob/user, mob/living/simple_animal/slime/slime, target_zone, obj/item/tool) if(slime.cores > 0) slime.cores-- - user.visible_message("[user] successfully extracts a core from [slime]!", - "You successfully extract a core from [slime]. [slime.cores] core\s remaining.") + user.visible_message(span_notice("[user] successfully extracts a core from [slime]!"), + span_notice("You successfully extract a core from [slime]. [slime.cores] core\s remaining.")) new slime.coretype(slime.loc) if(slime.cores <= 0) slime.icon_state = "[slime.colour] baby slime dead-nocore" - return TRUE + return SURGERY_STEP_CONTINUE else - return FALSE + return SURGERY_STEP_INCOMPLETE else - to_chat(user, "There aren't any cores left in [slime]!") - return TRUE + to_chat(user, span_warning("There aren't any cores left in [slime]!")) + return SURGERY_STEP_CONTINUE /datum/surgery_step/slime/extract_core/fail_step(mob/living/user, mob/living/simple_animal/slime/target, target_zone, obj/item/tool) - user.visible_message(" [user]'s hand slips, tearing [target]'s flesh with \the [tool]!", \ - " Your hand slips, tearing [target]'s flesh with \the [tool]!") - return FALSE + user.visible_message( + span_warning("[user]'s hand slips, tearing [target]'s flesh with \the [tool]!"), + span_warning("Your hand slips, tearing [target]'s flesh with \the [tool]!") + ) + return SURGERY_STEP_RETRY diff --git a/code/modules/surgery/dental_implant.dm b/code/modules/surgery/dental_implant.dm index 9d2f1c1ed39..db926fbaece 100644 --- a/code/modules/surgery/dental_implant.dm +++ b/code/modules/surgery/dental_implant.dm @@ -3,32 +3,35 @@ steps = list(/datum/surgery_step/generic/drill, /datum/surgery_step/insert_pill) possible_locs = list(BODY_ZONE_PRECISE_MOUTH) -/datum/surgery/dental_implant/can_start(mob/user, mob/living/carbon/target) - if(istype(target,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = target - if(!H.check_has_mouth()) - return 0 - return 1 +/datum/surgery/dental_implant/can_start(mob/user, mob/living/carbon/human/target) + . = ..() + if(!.) + return FALSE + if(!target.check_has_mouth()) + return FALSE /datum/surgery_step/insert_pill name = "insert pill" allowed_tools = list(/obj/item/reagent_containers/food/pill = 100) - time = 16 + time = 1.6 SECONDS /datum/surgery_step/insert_pill/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) - user.visible_message("[user] begins to wedge \the [tool] in [target]'s [parse_zone(target_zone)].", "You begin to wedge [tool] in [target]'s [parse_zone(target_zone)]...") - ..() + user.visible_message( + "[user] begins to wedge \the [tool] in [target]'s [parse_zone(target_zone)].", + span_notice("You begin to wedge [tool] in [target]'s [parse_zone(target_zone)]...") + ) + return ..() /datum/surgery_step/insert_pill/end_step(mob/living/user, mob/living/carbon/target, target_zone, var/obj/item/reagent_containers/food/pill/tool, datum/surgery/surgery) if(!istype(tool)) - return 0 + return SURGERY_STEP_INCOMPLETE var/dental_implants = 0 for(var/obj/item/reagent_containers/food/pill in target.contents) // Can't give them more than 4 dental implants. dental_implants++ if(dental_implants >= 4) - user.visible_message("[user] pulls \the [tool] back out of [target]'s [parse_zone(target_zone)]!", "You pull \the [tool] back out of [target]'s [parse_zone(target_zone)], there wans't enough room...") - return 0 + user.visible_message("[user] pulls \the [tool] back out of [target]'s [parse_zone(target_zone)]!", span_notice("You pull \the [tool] back out of [target]'s [parse_zone(target_zone)], there wans't enough room...")) + return SURGERY_STEP_INCOMPLETE user.drop_transfer_item_to_loc(tool, target) @@ -36,8 +39,8 @@ P.name = "Activate Pill ([tool.name])" P.Grant(target) - user.visible_message("[user] wedges \the [tool] into [target]'s [parse_zone(target_zone)]!", "You wedge [tool] into [target]'s [parse_zone(target_zone)].") - return 1 + user.visible_message("[user] wedges \the [tool] into [target]'s [parse_zone(target_zone)]!", span_notice("You wedge [tool] into [target]'s [parse_zone(target_zone)].")) + return SURGERY_STEP_CONTINUE /datum/action/item_action/hands_free/activate_pill name = "Activate Pill" @@ -45,7 +48,7 @@ /datum/action/item_action/hands_free/activate_pill/Trigger(left_click = TRUE) if(!..()) return - to_chat(owner, "You grit your teeth and burst the implanted [target]!") + to_chat(owner, span_caution("You grit your teeth and burst the implanted [target]!")) add_attack_logs(owner, owner, "Swallowed implanted [target]") if(target.reagents.total_volume) target.reagents.reaction(owner, REAGENT_INGEST) diff --git a/code/modules/surgery/encased.dm b/code/modules/surgery/encased.dm index 0949aee22f0..6741b5048f3 100644 --- a/code/modules/surgery/encased.dm +++ b/code/modules/surgery/encased.dm @@ -3,206 +3,171 @@ // GENERIC RIBCAGE SURGERY // ////////////////////////////////////////////////////////////////// /datum/surgery_step/open_encased - priority = 2 - can_infect = 1 - blood_level = 1 - -/datum/surgery_step/open_encased/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - - if(!hasorgans(target)) - return 0 - - var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(!affected) - return 0 - if(affected.is_robotic()) - return 0 - return 1 - + can_infect = TRUE + blood_level = SURGERY_BLOODSPREAD_HANDS /datum/surgery_step/open_encased/saw name = "saw bone" allowed_tools = list( - /obj/item/circular_saw = 100, \ - /obj/item/melee/energy/sword/cyborg/saw = 100, \ - /obj/item/hatchet = 90, \ - /obj/item/wirecutters = 70 + TOOL_SAW = 100, + /obj/item/hatchet = 90, + /obj/item/wirecutters = 70 ) - time = 54 - -/datum/surgery_step/open_encased/saw/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) + time = 5.4 SECONDS - if(!hasorgans(target)) - return +/datum/surgery_step/open_encased/saw/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 cut through [target]'s [affected.encased] with \the [tool].", \ - "You begin to cut through [target]'s [affected.encased] with \the [tool].") + user.visible_message( + "[user] begins to cut through [target]'s [affected.encased] with \the [tool].", + "You begin to cut through [target]'s [affected.encased] with \the [tool]." + ) target.custom_pain("Something hurts horribly in your [affected.name]!") - ..() + return ..() -/datum/surgery_step/open_encased/saw/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - - if(!hasorgans(target)) - return +/datum/surgery_step/open_encased/saw/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 cut [target]'s [affected.encased] open with \the [tool].", \ - " You have cut [target]'s [affected.encased] open with \the [tool].") + user.visible_message( + span_notice("[user] has cut [target]'s [affected.encased] open with \the [tool]."), + span_notice("You have cut [target]'s [affected.encased] open with \the [tool].") + ) + affected.open = ORGAN_ORGANIC_ENCASED_OPEN affected.fracture(silent = TRUE) - affected.open = 2.5 - return 1 + return SURGERY_STEP_CONTINUE -/datum/surgery_step/open_encased/saw/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - - if(!hasorgans(target)) - return +/datum/surgery_step/open_encased/saw/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, cracking [target]'s [affected.encased] with \the [tool]!" , \ - " Your hand slips, cracking [target]'s [affected.encased] with \the [tool]!" ) + user.visible_message( + span_warning("[user]'s hand slips, cracking [target]'s [affected.encased] with \the [tool]!"), + span_warning("Your hand slips, cracking [target]'s [affected.encased] with \the [tool]!") + ) affected.receive_damage(20) affected.fracture() - return 0 + return SURGERY_STEP_RETRY /datum/surgery_step/open_encased/retract name = "retract bone" allowed_tools = list( - /obj/item/scalpel/laser/manager = 100, \ - /obj/item/retractor = 100, \ - /obj/item/crowbar = 90 + /obj/item/scalpel/laser/manager = 100, + TOOL_RETRACTOR = 100, + TOOL_CROWBAR = 90 ) - time = 24 + time = 2.4 SECONDS -/datum/surgery_step/open_encased/retract/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - - if(!hasorgans(target)) - return +/datum/surgery_step/open_encased/retract/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 force open the [affected.encased] in [target]'s [affected.name] with \the [tool]." - var/self_msg = "You start to force open the [affected.encased] in [target]'s [affected.name] with \the [tool]." - user.visible_message(msg, self_msg) + user.visible_message( + "[user] starts to force open the [affected.encased] in [target]'s [affected.name] with \the [tool].", + "You start to force open the [affected.encased] in [target]'s [affected.name] with \the [tool]." + ) target.custom_pain("Something hurts horribly in your [affected.name]!") - ..() + return ..() -/datum/surgery_step/open_encased/retract/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/open_encased/retract/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - if(!hasorgans(target)) - return var/obj/item/organ/external/affected = target.get_organ(target_zone) - var/msg = " [user] forces open [target]'s [affected.encased] with \the [tool]." - var/self_msg = " You force open [target]'s [affected.encased] with \the [tool]." - user.visible_message(msg, self_msg) - - affected.open = 3 + user.visible_message( + span_notice("[user] forces open [target]'s [affected.encased] with \the [tool]."), + span_notice("You force open [target]'s [affected.encased] with \the [tool].") + ) - return 1 + affected.open = ORGAN_ORGANIC_ENCASED_OPEN -/datum/surgery_step/open_encased/retract/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) + return SURGERY_STEP_CONTINUE - if(!hasorgans(target)) - return +/datum/surgery_step/open_encased/retract/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, cracking [target]'s [affected.encased]!" - var/self_msg = " Your hand slips, cracking [target]'s [affected.encased]!" - user.visible_message(msg, self_msg) + user.visible_message( + span_warning("[user]'s hand slips, cracking [target]'s [affected.encased]!"), + span_warning("Your hand slips, cracking [target]'s [affected.encased]!") + ) affected.receive_damage(20) affected.fracture() - return 0 + return SURGERY_STEP_RETRY /datum/surgery_step/open_encased/close name = "unretract bone" //i suck at names okay? give me a new one allowed_tools = list( - /obj/item/scalpel/laser/manager = 100, \ - /obj/item/retractor = 100, \ - /obj/item/crowbar = 90 + /obj/item/scalpel/laser/manager = 100, + TOOL_RETRACTOR = 100, + TOOL_CROWBAR = 90 ) - time = 24 + time = 2.4 SECONDS -/datum/surgery_step/open_encased/close/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - - if(!hasorgans(target)) - return +/datum/surgery_step/open_encased/close/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 bending [target]'s [affected.encased] back into place with \the [tool]." - var/self_msg = "You start bending [target]'s [affected.encased] back into place with \the [tool]." - user.visible_message(msg, self_msg) + user.visible_message( + "[user] starts bending [target]'s [affected.encased] back into place with \the [tool].", + "You start bending [target]'s [affected.encased] back into place with \the [tool]." + ) target.custom_pain("Something hurts horribly in your [affected.name]!") - ..() + return ..() -/datum/surgery_step/open_encased/close/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - - if(!hasorgans(target)) - return +/datum/surgery_step/open_encased/close/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] bends [target]'s [affected.encased] back into place with \the [tool]." - var/self_msg = " You bend [target]'s [affected.encased] back into place with \the [tool]." - user.visible_message(msg, self_msg) - - affected.open = 2.5 - - return 1 + user.visible_message( + span_notice("[user] bends [target]'s [affected.encased] back into place with \the [tool]."), + span_notice("You bend [target]'s [affected.encased] back into place with \the [tool].") + ) -/datum/surgery_step/open_encased/close/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) + return SURGERY_STEP_CONTINUE - if(!hasorgans(target)) - return +/datum/surgery_step/open_encased/close/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, bending [target]'s [affected.encased] the wrong way!" - var/self_msg = " Your hand slips, bending [target]'s [affected.encased] the wrong way!" - user.visible_message(msg, self_msg) + user.visible_message( + span_warning("[user]'s hand slips, bending [target]'s [affected.encased] the wrong way!"), + span_warning("Your hand slips, bending [target]'s [affected.encased] the wrong way!") + ) affected.receive_damage(20) affected.fracture() - return 0 + return SURGERY_STEP_RETRY /datum/surgery_step/open_encased/mend name = "mend bone" allowed_tools = list( - /obj/item/bonegel = 100, \ - /obj/item/screwdriver = 90 + TOOL_BONEGEL = 100, + TOOL_SCREWDRIVER = 90 ) - time = 24 - -/datum/surgery_step/open_encased/mend/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) + time = 2.4 SECONDS - if(!hasorgans(target)) - return +/datum/surgery_step/open_encased/mend/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 applying \the [tool] to [target]'s [affected.encased]." - var/self_msg = "You start applying \the [tool] to [target]'s [affected.encased]." - user.visible_message(msg, self_msg) + user.visible_message( + "[user] starts applying \the [tool] to [target]'s [affected.encased].", + "You start applying \the [tool] to [target]'s [affected.encased]." + ) target.custom_pain("Something hurts horribly in your [affected.name]!") - ..() - -/datum/surgery_step/open_encased/mend/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) + return ..() - if(!hasorgans(target)) - return +/datum/surgery_step/open_encased/mend/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] applied \the [tool] to [target]'s [affected.encased]." - var/self_msg = " You applied \the [tool] to [target]'s [affected.encased]." - user.visible_message(msg, self_msg) + user.visible_message( + span_notice("[user] applied \the [tool] to [target]'s [affected.encased]."), + span_notice("You applied \the [tool] to [target]'s [affected.encased].") + ) affected.mend_fracture() - affected.open = 2 + affected.open = ORGAN_ORGANIC_OPEN - return 1 + return SURGERY_STEP_CONTINUE diff --git a/code/modules/surgery/generic.dm b/code/modules/surgery/generic.dm index 220c9d8bcac..ecad13ed6e2 100644 --- a/code/modules/surgery/generic.dm +++ b/code/modules/surgery/generic.dm @@ -3,244 +3,285 @@ // COMMON STEPS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/datum/surgery_step/generic/ - can_infect = 1 - -/datum/surgery_step/generic/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(!hasorgans(target)) - return 0 - var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(affected == null) - return 0 - if(affected.is_robotic()) - return 0 - return 1 - +/datum/surgery_step/generic + can_infect = TRUE /datum/surgery_step/generic/cut_open name = "make incision" allowed_tools = list( - /obj/item/scalpel = 100, \ - /obj/item/kitchen/knife = 90, \ - /obj/item/shard = 60, \ - /obj/item/scissors = 12, \ - /obj/item/twohanded/chainsaw = 1, \ - /obj/item/claymore = 6, \ - /obj/item/melee/energy/ = 6, \ - /obj/item/pen/edagger = 6, \ + TOOL_SCALPEL = 100, + /obj/item/kitchen/knife = 90, + /obj/item/shard = 60, + /obj/item/scissors = 12, + /obj/item/twohanded/chainsaw = 1, + /obj/item/claymore = 6, + /obj/item/melee/energy = 6, + /obj/item/pen/edagger = 6, ) - time = 16 + 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 \the [tool].", \ - "You start the incision on [target]'s [affected.name] with \the [tool].") - target.custom_pain("You feel a horrible pain as if from a sharp knife in your [affected.name]!") - ..() +/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( + span_notice("[user] starts the incision on [target]'s [affected.name] with \the [tool]."), + span_notice("You start the incision on [target]'s [affected.name] with \the [tool].") + ) + target.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) +/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(" [user] has made an incision on [target]'s [affected.name] with \the [tool].", \ - " You have made an incision on [target]'s [affected.name] with \the [tool].",) - affected.open = 1 - return 1 + user.visible_message( + span_notice("[user] has made an incision on [target]'s [affected.name] with \the [tool]."), + span_notice("You have made an incision on [target]'s [affected.name] with \the [tool].") + ) + affected.open = ORGAN_ORGANIC_OPEN + 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) +/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 \the [tool]!", \ - " Your hand slips, slicing open [target]'s [affected.name] in a wrong spot with \the [tool]!") + user.visible_message( + span_warning("[user]'s hand slips, slicing open [target]'s [affected.name] in a wrong spot with \the [tool]!"), + span_warning("Your hand slips, slicing open [target]'s [affected.name] in a wrong spot with \the [tool]!") + ) affected.receive_damage(10) - return 0 + return SURGERY_STEP_RETRY /datum/surgery_step/generic/clamp_bleeders name = "clamp bleeders" allowed_tools = list( - /obj/item/scalpel/laser = 100, \ - /obj/item/hemostat = 100, \ - /obj/item/stack/cable_coil = 90, \ - /obj/item/stack/sheet/sinew = 90, \ - /obj/item/assembly/mousetrap = 25 + TOOL_HEMOSTAT = 100, + /obj/item/scalpel/laser = 100, + /obj/item/stack/cable_coil = 90, + /obj/item/stack/sheet/sinew = 90, + /obj/item/assembly/mousetrap = 25 ) - time = 24 + time = 2.4 SECONDS -/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].") - target.custom_pain("The pain in your [affected.name] is maddening!") - ..() +/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]." + ) + target.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) +/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].") + user.visible_message( + span_notice("[user] clamps bleeders in [target]'s [affected.name] with \the [tool]."), + span_notice("You clamp bleeders in [target]'s [affected.name] with \the [tool].") + ) spread_germs_to_organ(affected, user, tool) - return 1 + 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) +/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]!",) + user.visible_message( + span_warning("[user]'s hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!"), + span_warning("Your hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!") + ) affected.receive_damage(10) - return 0 + return SURGERY_STEP_RETRY /datum/surgery_step/generic/retract_skin name = "retract skin" allowed_tools = list( - /obj/item/scalpel/laser/manager = 100, \ - /obj/item/retractor = 100, \ - /obj/item/crowbar = 90, \ - /obj/item/kitchen/utensil/fork = 60 + TOOL_RETRACTOR = 100, + /obj/item/scalpel/laser/manager = 100, + /obj/item/retractor = 100, + /obj/item/crowbar = 90, + /obj/item/kitchen/utensil/fork = 60 ) - time = 24 + 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) +/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 == "groin") + 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) target.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) +/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]." + var/msg = span_notice("[user] keeps the incision open on [target]'s [affected.name] with \the [tool].") + var/self_msg = span_notice("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 == "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]." + msg = span_notice("[user] keeps the ribcage open on [target]'s torso with \the [tool].") + self_msg = span_notice("You keep the ribcage open on [target]'s torso with \the [tool].") + if(target_zone == BODY_ZONE_PRECISE_GROIN) + msg = span_notice("[user] keeps the incision open on [target]'s lower abdomen with \the [tool].") + self_msg = span_notice("You keep the incision open on [target]'s lower abdomen with \the [tool].") user.visible_message(msg, self_msg) - affected.open = 2 - return 1 + 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) +/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]!" + var/msg = span_warning("[user]'s hand slips, tearing the edges of incision on [target]'s [affected.name] with \the [tool]!") + var/self_msg = span_warning("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 == "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]!" + msg = span_warning("[user]'s hand slips, damaging several organs [target]'s torso with \the [tool]!") + self_msg = span_warning("Your hand slips, damaging several organs [target]'s torso with \the [tool]!") + if(target_zone == BODY_ZONE_PRECISE_GROIN) + msg = span_warning("[user]'s hand slips, damaging several organs [target]'s lower abdomen with \the [tool]") + self_msg = span_warning("Your hand slips, damaging several organs [target]'s lower abdomen with \the [tool]!") user.visible_message(msg, self_msg) - target.apply_damage(12, BRUTE, affected, sharp = 1) - return 0 + target.apply_damage(12, BRUTE, affected, sharp = TRUE) + return SURGERY_STEP_RETRY /datum/surgery_step/generic/cauterize name = "cauterize incision" allowed_tools = list( - /obj/item/scalpel/laser = 100, \ - /obj/item/cautery = 100, \ - /obj/item/clothing/mask/cigarette = 90, \ - /obj/item/lighter = 60, \ - /obj/item/weldingtool = 30, \ - /obj/item/flashlight/flare/torch = 30 + /obj/item/scalpel/laser = 100, + TOOL_CAUTERY = 100, + /obj/item/clothing/mask/cigarette = 90, + /obj/item/lighter = 60, + TOOL_WELDER = 30, + /obj/item/flashlight/flare/torch = 30 ) - time = 24 + time = 2.4 SECONDS -/datum/surgery_step/generic/cauterize/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/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].") + 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]." + ) target.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) +/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].") - affected.open = 0 + user.visible_message( + span_notice("[user] cauterizes the incision on [target]'s [affected.name] with \the [tool]."), + span_notice("You cauterize the incision on [target]'s [affected.name] with \the [tool].") + ) + affected.open = ORGAN_CLOSED affected.germ_level = 0 - return 1 + 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) +/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]!") + user.visible_message( + span_warning("[user]'s hand slips, leaving a small burn on [target]'s [affected.name] with \the [tool]!"), + span_warning("Your hand slips, leaving a small burn on [target]'s [affected.name] with \the [tool]!") + ) target.apply_damage(3, BURN, affected) - return 0 + return SURGERY_STEP_RETRY + +/datum/surgery_step/generic/cauterize/premature + name = "cauterize incision (premature)" + +/datum/surgery_step/generic/cauterize/premature/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].", + // give a little heads up to the surgeon that they're stopping the surgery prematurely in case that wasn't the intention. + "[span_warning("You are interrupting the current surgery")], beginning to cauterize the incision on [target]'s [affected.name] with \the [tool]." + ) + target.custom_pain("Your [affected.name] is being burned!") + return ..() + //drill bone /datum/surgery_step/generic/drill name = "drill bone" - allowed_tools = list(/obj/item/surgicaldrill = 100, /obj/item/pickaxe/drill = 60, /obj/item/mecha_parts/mecha_equipment/drill = 60, /obj/item/screwdriver = 20) - time = 30 + allowed_tools = list( + TOOL_DRILL = 100, + /obj/item/screwdriver/power = 80, + /obj/item/pickaxe/drill = 60, + /obj/item/mecha_parts/mecha_equipment/drill = 60, + /obj/item/screwdriver = 20 + ) + 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)]...") - ..() + user.visible_message( + "[user] begins to drill into the bone in [target]'s [parse_zone(target_zone)].", + span_notice("You begin to drill into the bone in [target]'s [parse_zone(target_zone)]...") + ) + 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)].") - return 1 + user.visible_message("[user] drills into [target]'s [parse_zone(target_zone)]!", span_notice("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( + span_warning("[user]'s [tool] doesn't get a firm grip and tears at the bone in [target]'s [parse_zone(target_zone)]!"), + span_warning("Your [tool] doesn't get a firm grip and tears at the bone in [target]'s [parse_zone(target_zone)]!") + ) + + affected.receive_damage(15) + return SURGERY_STEP_RETRY /datum/surgery_step/generic/amputate name = "amputate limb" allowed_tools = list( - /obj/item/circular_saw = 100, \ - /obj/item/melee/energy/sword/cyborg/saw = 100, \ - /obj/item/hatchet = 90, \ - /obj/item/melee/arm_blade = 75 + TOOL_SAW = 100, + /obj/item/hatchet = 90, + /obj/item/melee/arm_blade = 75 ) - time = 100 - -/datum/surgery_step/generic/amputate/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(target_zone == BODY_ZONE_PRECISE_EYES) //there are specific steps for eye surgery - return FALSE - if(!hasorgans(target)) - return FALSE - var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(affected == null) - return FALSE - return !affected.cannot_amputate + 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) +/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].") + 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]." + ) target.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) +/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].") + user.visible_message( + span_notice("[user] amputates [target]'s [affected.name] at the [affected.amputation_point] with \the [tool]."), + span_notice("You amputate [target]'s [affected.name] with \the [tool].") + ) 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) - if(istype(thing,/obj/item)) + var/atom/movable/thing = affected.droplimb(1, DROPLIMB_SHARP) + + if(istype(target) && target.has_pain()) + // okay if you can feel your arm getting chopped off you aren't gonna be singing + to_chat(target, span_userdanger("Your [affected] goes completely numb at the [affected.amputation_point]!")) + target.emote("scream") + if(isitem(thing)) thing.forceMove(get_turf(target)) user.put_in_hands(thing, ignore_anim = FALSE) - return 1 + 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) +/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]!") + user.visible_message( + span_warning("[user]'s hand slips, sawing through the bone in [target]'s [affected.name] with \the [tool]!"), + span_warning("Your hand slips, sawing through the bone in [target]'s [affected.name] with \the [tool]!") + ) affected.receive_damage(30) affected.fracture() - return 0 + return SURGERY_STEP_RETRY diff --git a/code/modules/surgery/helpers.dm b/code/modules/surgery/helpers.dm index 755a2d07017..a859c886ecf 100644 --- a/code/modules/surgery/helpers.dm +++ b/code/modules/surgery/helpers.dm @@ -1,140 +1,34 @@ -/proc/attempt_initiate_surgery(obj/item/I, mob/living/M, mob/user, var/override ) - if(istype(M)) - var/mob/living/carbon/human/H - var/obj/item/organ/external/affecting - var/selected_zone = user.zone_selected - var/list/cautery_tools = list( - /obj/item/scalpel/laser = 100, \ - /obj/item/cautery = 100, \ - /obj/item/clothing/mask/cigarette = 90, \ - /obj/item/lighter = 60, \ - /obj/item/weldingtool = 30, \ - /obj/item/flashlight/flare/torch = 30 - ) - if(istype(M, /mob/living/carbon/human)) - H = M - affecting = H.get_organ(check_zone(selected_zone)) - - if(can_operate(M) || isslime(M)) //if they're prone or a slime - var/datum/surgery/current_surgery - for(var/datum/surgery/S in M.surgeries) - if(S.location == selected_zone) - current_surgery = S - - if(!current_surgery) - var/list/all_surgeries = GLOB.surgeries_list.Copy() - var/list/available_surgeries = list() - - for(var/datum/surgery/S in all_surgeries) - if(!S.possible_locs.Find(selected_zone)) - continue - if(affecting && S.requires_organic_bodypart && affecting.is_robotic()) - continue - if(!S.can_start(user, M)) - continue - - for(var/path in S.allowed_mob) - if(istype(M, path)) - // If there are multiple surgeries with the same name, - // prepare to cry - available_surgeries[S.name] = S - break - - if(override) - var/datum/surgery/S - if(istype(I,/obj/item/robot_parts)) - S = available_surgeries["Apply Robotic Prosthetic"] - if(istype(I,/obj/item/organ/external)) - var/obj/item/organ/external/E = I - if(E.is_robotic()) - S = available_surgeries["Synthetic Limb Reattachment"] - if(S) - var/datum/surgery/procedure = new S.type - if(procedure) - procedure.location = selected_zone - M.surgeries += procedure - procedure.organ_ref = affecting - procedure.next_step(user, M, I) - - else - var/P = tgui_input_list(user, "Begin which procedure?", "Surgery", available_surgeries) - if(P && user && user.Adjacent(M) && (I in user)) - var/datum/surgery/S = available_surgeries[P] - var/datum/surgery/procedure = new S.type - if(procedure) - procedure.location = selected_zone - M.surgeries += procedure - procedure.organ_ref = affecting - user.visible_message("[user] prepares to operate on [M]'s [parse_zone(selected_zone)].", \ - "You prepare to operate on [M]'s [parse_zone(selected_zone)].") - - else if(!current_surgery.step_in_progress && ishuman(M)) //early surgery cautery - var/datum/surgery_step/generic/cauterize/C = new - if(current_surgery.status == 1) - M.surgeries -= current_surgery - to_chat(user, "You stop the surgery.") - qdel(current_surgery) - - else if(current_surgery.can_cancel) - var/cautery_chance = 0 - var/obj/item/cautery_tool = null - - if(isrobot(user)) - if(istype(I, /obj/item/scalpel/laser)) - cautery_chance = 100 - cautery_tool = I - - else - for(var/T in cautery_tools) - if(istype(user.get_inactive_hand(), T)) - cautery_chance = cautery_tools[T] - cautery_tool = user.get_inactive_hand() - - if(cautery_chance) - C.begin_step(user, H, selected_zone, cautery_tool, current_surgery) - if(do_after(user, C.time * cautery_tool.toolspeed * gettoolspeedmod(user), target = M)) - if(!isrobot(user)) - cautery_chance *= get_location_modifier(H) - cautery_chance *= get_pain_modifier(H) - if(prob(cautery_chance)) - C.end_step(user, H, selected_zone, cautery_tool, current_surgery) - M.surgeries -= current_surgery - qdel(current_surgery) - else - C.fail_step(user, H, selected_zone, cautery_tool, current_surgery) - - else if(!isrobot(user)) - to_chat(user, "You need to hold a cautery or equivalent in your inactive hand to stop the surgery in progress.") - - return 1 - return 0 - - -/proc/get_pain_modifier(mob/living/carbon/human/M) //returns modfier to make surgery harder if patient is conscious and feels pain - if(M.stat) //stat=0 if CONSCIOUS, 1=UNCONSCIOUS and 2=DEAD. Operating on dead people is easy, too. Just sleeping won't work, though. +/proc/get_pain_modifier(mob/living/carbon/human/target) //returns modfier to make surgery harder if patient is conscious and feels pain + if(target.stat == DEAD) // Operating on dead people is easy return 1 - if(NO_PAIN in M.dna.species.species_traits)//if you don't feel pain, you can hold still + var/datum/status_effect/incapacitating/sleeping/S = target.IsSleeping() + if(target.stat == UNCONSCIOUS && S) + // Either unconscious due to something other than sleep, + // or "sleeping" due to being hard knocked out (N2O or similar), rather than just napping. + // Either way, not easily woken up. return 1 - if(M.reagents.has_reagent("hydrocodone"))//really good pain killer + if(NO_PAIN in target.dna.species.species_traits)//if you don't feel pain, you can hold still + return 1 + if(target.reagents.has_reagent("hydrocodone"))//really good pain killer return 0.99 - if(M.reagents.has_reagent("morphine"))//Just as effective as Hydrocodone, but has an addiction chance + if(target.reagents.has_reagent("morphine"))//Just as effective as Hydrocodone, but has an addiction chance return 0.99 - if(M.reagents.has_reagent("syntmorphine")) + if(target.reagents.has_reagent("syntmorphine")) return 0.99 - var/drunk = M.get_drunkenness() + var/drunk = target.get_drunkenness() if(drunk >= 80)//really damn drunk return 0.95 if(drunk >= 40)//pretty drunk return 0.9 - if(M.reagents.has_reagent("sal_acid")) //it's better than nothing, as far as painkillers go. + if(target.reagents.has_reagent("sal_acid")) //it's better than nothing, as far as painkillers go. return 0.85 if(drunk >= 15)//a little drunk return 0.85 return 0.8 //20% failure chance -/proc/get_location_modifier(mob/M) - var/turf/T = get_turf(M) +/proc/get_location_modifier(mob/target) + var/turf/T = get_turf(target) if(locate(/obj/machinery/optable, T)) return 1 else if(locate(/obj/structure/table, T)) @@ -144,17 +38,16 @@ else return 0.5 -//check if mob is lying down on something we can operate him on. -/proc/can_operate(mob/living/carbon/M) - if(locate(/obj/machinery/optable, M.loc) && (M.lying_angle || M.resting)) +//check if mob is lying down on something we can operate on. +/proc/on_operable_surface(mob/living/carbon/target) + if(locate(/obj/machinery/optable, target.loc) && (target.lying_angle || target.resting || target.stat)) return TRUE - if(locate(/obj/structure/bed, M.loc) && (M.buckled || M.lying_angle || M.IsWeakened() || M.IsStunned() || M.IsParalyzed() || M.IsSleeping() || M.stat)) + if(locate(/obj/structure/bed, target.loc) && (target.buckled || target.lying_angle || target.IsWeakened() || target.IsStunned() || target.IsParalyzed() || target.IsSleeping() || target.stat)) return TRUE - if(locate(/obj/structure/table, M.loc) && (M.lying_angle || M.IsWeakened() || M.IsStunned() || M.IsParalyzed() || M.IsSleeping() || M.stat)) + if(locate(/obj/structure/table, target.loc) && (target.lying_angle || target.IsWeakened() || target.IsStunned() || target.IsParalyzed() || target.IsSleeping() || target.stat)) return TRUE return FALSE - /** * Called when a limb containing this object is placed back on a body. * @@ -165,3 +58,26 @@ /atom/movable/proc/attempt_become_organ(obj/item/organ/external/parent, mob/living/carbon/human/target) return FALSE +/// Check to see if a surgical operation proposed on ourselves is valid or not. We are the target of the surgery +/mob/living/proc/can_run_surgery(datum/surgery/surgery, mob/surgeon, obj/item/organ/external/affecting) + if(!affecting) + // try to pull it if it isn't passed in (it's a parameter mostly for optimization purposes) + affecting = get_organ(check_zone(surgeon.zone_selected)) + + if(!surgery.possible_locs.Find(surgeon.zone_selected)) + return + if(affecting) + if(!surgery.requires_bodypart) + return + if((surgery.is_organ_noncompatible(affecting))) + return + else if(surgery.requires_bodypart) //mob with no limb in surgery zone when we need a limb + return + if(surgery.lying_required && !(lying_angle || resting || stat)) + return + if(!surgery.self_operable && src == surgeon) + return + if(!surgery.can_start(surgeon, src)) + return + + return TRUE diff --git a/code/modules/surgery/implant_removal.dm b/code/modules/surgery/implant_removal.dm index a7d756ec9a6..42a988f9344 100644 --- a/code/modules/surgery/implant_removal.dm +++ b/code/modules/surgery/implant_removal.dm @@ -4,87 +4,110 @@ /datum/surgery/implant_removal name = "Implant Removal" - steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin,/datum/surgery_step/extract_implant,/datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/open_organ, + /datum/surgery_step/extract_implant, + /datum/surgery_step/generic/cauterize + ) possible_locs = list(BODY_ZONE_CHEST) + requires_organic_bodypart = TRUE + restricted_speciestypes = list(/datum/species/kidan, /datum/species/wryn, /datum/species/plasmaman) + +/datum/surgery/implant_removal/plasmamans + name = "Implant Removal" + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/open_organ/plasma, + /datum/surgery_step/extract_implant, + /datum/surgery_step/generic/cauterize + ) + target_speciestypes = list(/datum/species/plasmaman) + restricted_speciestypes = null + /datum/surgery/implant_removal/insect name = "Insectoid Implant Removal" - steps = list(/datum/surgery_step/open_encased/saw, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/retract_skin, - /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/extract_implant, /datum/surgery_step/glue_bone, /datum/surgery_step/set_bone,/datum/surgery_step/finish_bone,/datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/open_encased/saw, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/proxy/open_organ, + /datum/surgery_step/extract_implant, + /datum/surgery_step/glue_bone, + /datum/surgery_step/set_bone, + /datum/surgery_step/finish_bone, + /datum/surgery_step/generic/cauterize + ) + target_speciestypes = list(/datum/species/kidan, /datum/species/wryn) + restricted_speciestypes = null /datum/surgery/implant_removal/synth name = "Implant Removal" - steps = list(/datum/surgery_step/robotics/external/unscrew_hatch,/datum/surgery_step/robotics/external/open_hatch,/datum/surgery_step/extract_implant/synth,/datum/surgery_step/robotics/external/close_hatch) - possible_locs = list(BODY_ZONE_CHEST) - requires_organic_bodypart = 0 - -/datum/surgery/implant_removal/can_start(mob/user, mob/living/carbon/human/target) - var/mob/living/carbon/human/H = target - if(iskidan(H) || iswryn(H)) - return FALSE - if(!istype(target)) - return FALSE - var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) - if(!affected) - return FALSE - if(affected.is_robotic()) - return FALSE - return TRUE - -/datum/surgery/implant_removal/insect/can_start(mob/user, mob/living/carbon/human/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(!affected) - return FALSE - if(affected.is_robotic()) - return FALSE - if(!affected.encased) - return FALSE - if(iswryn(H) || iskidan(H)) - return TRUE - return FALSE - -/datum/surgery/implant_removal/synth/can_start(mob/user, mob/living/carbon/human/target) - if(!istype(target)) - return FALSE - var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) - if(!affected) - return FALSE - if(!affected.is_robotic()) - return FALSE - - return TRUE + steps = list( + /datum/surgery_step/robotics/external/unscrew_hatch, + /datum/surgery_step/robotics/external/open_hatch, + /datum/surgery_step/proxy/robotics/repair_limb, + /datum/surgery_step/extract_implant/synth, + /datum/surgery_step/robotics/external/close_hatch + ) + requires_organic_bodypart = FALSE /datum/surgery_step/extract_implant name = "extract implant" - allowed_tools = list(/obj/item/hemostat = 100, /obj/item/crowbar = 65) - time = 64 + allowed_tools = list(TOOL_HEMOSTAT = 100, TOOL_CROWBAR = 65) + time = 6.4 SECONDS + repeatable = TRUE var/obj/item/implant/I = null + var/max_times_to_check = 5 /datum/surgery_step/extract_implant/synth - allowed_tools = list(/obj/item/multitool = 100, /obj/item/hemostat = 65, /obj/item/crowbar = 50) + allowed_tools = list(/obj/item/multitool = 100, TOOL_HEMOSTAT = 65, TOOL_CROWBAR = 50) + +/datum/surgery_step/extract_implant/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) -/datum/surgery_step/implant_removal/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) - user.visible_message(" [user]'s hand slips, scraping around inside [target]'s [affected.name] with \the [tool]!", \ - " Your hand slips, scraping around inside [target]'s [affected.name] with \the [tool]!") - affected.receive_damage(20) -/datum/surgery_step/extract_implant/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) + if(times_repeated >= max_times_to_check) + user.visible_message( + span_notice("[user] seems to have had enough and stops checking inside [target]."), + span_notice("There doesn't seem to be anything inside, you've checked enough times.") + ) + return SURGERY_BEGINSTEP_SKIP + I = locate(/obj/item/implant) in target - user.visible_message("[user] starts poking around inside [target]'s [affected.name] with \the [tool].", \ - "You start poking around inside [target]'s [affected.name] with \the [tool]." ) + user.visible_message( + "[user] starts poking around inside [target]'s [affected.name] with \the [tool].", + "You start poking around inside [target]'s [affected.name] with \the [tool]." + ) target.custom_pain("The pain in your [affected.name] is living hell!") - ..() + return ..() + +/datum/surgery_step/extract_implant/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( + span_warning("[user] grips onto [target]'s [affected.name] by mistake, tearing it!"), + span_warning("You think you've found something, but you've grabbed onto [target]'s [affected.name] instead, damaging it!") + ) + affected.receive_damage(20) + return SURGERY_STEP_RETRY -/datum/surgery_step/extract_implant/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/extract_implant/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) I = locate(/obj/item/implant) in target - if(I && (target_zone == BODY_ZONE_CHEST)) //implant removal only works on the chest. - user.visible_message("[user] takes something out of [target]'s [affected.name] with \the [tool].", \ - "You take [I] out of [target]'s [affected.name]s with \the [tool]." ) + if(I && prob(80)) //implant removal only works on the chest. + user.visible_message( + span_notice("[user] takes something out of [target]'s [affected.name] with \the [tool]."), + span_notice("You take \an [I] out of [target]'s [affected.name]s with \the [tool].") + ) I.removed(target) @@ -101,10 +124,12 @@ case.imp = I I.forceMove(case) case.update_icon() - user.visible_message("[user] places [I] into [case]!", "You place [I] into [case].") + user.visible_message("[user] places [I] into [case]!", span_notice("You place [I] into [case].")) else qdel(I) else - user.visible_message(" [user] could not find anything inside [target]'s [affected.name], and pulls \the [tool] out.", \ - "You could not find anything inside [target]'s [affected.name].") - return TRUE + user.visible_message( + span_notice("[user] could not find anything inside [target]'s [affected.name], and pulls \the [tool] out."), + span_notice("You could not find anything inside [target]'s [affected.name].") + ) + return SURGERY_STEP_CONTINUE diff --git a/code/modules/surgery/limb_augmentation.dm b/code/modules/surgery/limb_augmentation.dm index c18ed49395c..7b3c78b24fe 100644 --- a/code/modules/surgery/limb_augmentation.dm +++ b/code/modules/surgery/limb_augmentation.dm @@ -1,6 +1,12 @@ /datum/surgery/limb_augmentation name = "Augment Limb" - steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/augment) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/open_organ, + /datum/surgery_step/augment + ) possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_HEAD, @@ -13,39 +19,39 @@ ) /datum/surgery/limb_augmentation/can_start(mob/user, mob/living/carbon/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(!affected) - return 0 - if(affected.has_fracture()) //The arm has to be in prime condition to augment it. - return 0 - if(affected.is_robotic()) - return 0 - return 1 + . = ..() + if(!.) + return FALSE + var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) + if(affected.has_fracture()) //The arm has to be in prime condition to augment it. + return FALSE /datum/surgery_step/augment name = "augment limb with robotic part" allowed_tools = list(/obj/item/robot_parts = 100) - time = 32 + time = 3.2 SECONDS -/datum/surgery_step/augment/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) +/datum/surgery_step/augment/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/robot_parts/p = tool if(p.part) if(!(target_zone in p.part)) - to_chat(user, "[tool] cannot be used to augment this limb!") - return 0 - return 1 + to_chat(user, span_warning("[tool] cannot be used to augment this limb!")) + return SURGERY_BEGINSTEP_ABORT -/datum/surgery_step/augment/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] starts augmenting [affected] with [tool].", "You start augmenting [affected] with [tool].") + user.visible_message( + "[user] starts augmenting [affected] with [tool].", + "You start augmenting [affected] with [tool]." + ) + return ..() /datum/surgery_step/augment/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/robot_parts/L = tool var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] has finished augmenting [affected] with [tool].", \ - "You augment [affected] with [tool].") + user.visible_message( + span_notice("[user] has finished augmenting [affected] with [tool]."), + span_notice("You augment [affected] with [tool].") + ) if(L.part) for(var/part_name in L.part) @@ -61,6 +67,6 @@ qdel(tool) - affected.open = 0 + affected.open = ORGAN_CLOSED affected.germ_level = 0 - return 1 + return SURGERY_STEP_CONTINUE diff --git a/code/modules/surgery/limb_reattach.dm b/code/modules/surgery/limb_reattach.dm index b50763af853..383e276f545 100644 --- a/code/modules/surgery/limb_reattach.dm +++ b/code/modules/surgery/limb_reattach.dm @@ -20,25 +20,24 @@ BODY_ZONE_TAIL, BODY_ZONE_WING, ) + requires_organic_bodypart = TRUE + cancel_on_organ_change = FALSE /datum/surgery/amputation/can_start(mob/user, mob/living/carbon/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(!affected) - return 0 - if(affected.is_robotic()) - return 0 - if(affected.cannot_amputate) - return 0 - - return 1 + . = ..() + if(!.) + return FALSE + var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) + if(affected.cannot_amputate) + return FALSE + return TRUE /datum/surgery/reattach name = "Limb Reattachment" - steps = list(/datum/surgery_step/limb/attach,/datum/surgery_step/limb/connect) + steps = list(/datum/surgery_step/limb/attach, /datum/surgery_step/limb/connect) + requires_bodypart = FALSE possible_locs = list( BODY_ZONE_HEAD, BODY_ZONE_L_ARM, @@ -53,24 +52,34 @@ BODY_ZONE_TAIL, BODY_ZONE_WING, ) + cancel_on_organ_change = FALSE /datum/surgery/reattach/can_start(mob/user, mob/living/carbon/target) + . = ..() + if(!.) + return FALSE if(ishuman(target)) var/mob/living/carbon/human/H = target var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) if(ismachineperson(target) && user.zone_selected != BODY_ZONE_TAIL) // RIP bi-centennial man - return 0 + return FALSE if(ismachineperson(target) && user.zone_selected != BODY_ZONE_WING) // RIP bi-centennial man - return 0 + return FALSE if(!affected) - return 1 - return 0 + return TRUE + // make sure they can actually have this limb + var/list/organ_data = target.dna.species.has_limbs["[user.zone_selected]"] + return !isnull(organ_data) + + return FALSE /datum/surgery/reattach_synth name = "Synthetic Limb Reattachment" + requires_bodypart = FALSE steps = list(/datum/surgery_step/limb/attach/robo) + abstract = TRUE // these shouldn't show in the list; they'll be replaced by attach_robotic_limb possible_locs = list( BODY_ZONE_HEAD, BODY_ZONE_L_ARM, @@ -86,19 +95,11 @@ BODY_ZONE_WING, ) -/datum/surgery/reattach_synth/can_start(mob/user, mob/living/carbon/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(!affected) - return 1 - - return 0 - - /datum/surgery/robo_attach name = "Apply Robotic Prosthetic" + requires_bodypart = FALSE steps = list(/datum/surgery_step/limb/mechanize) + abstract = TRUE possible_locs = list( BODY_ZONE_HEAD, BODY_ZONE_L_ARM, @@ -114,91 +115,106 @@ BODY_ZONE_WING, ) -/datum/surgery/robo_attach/can_start(mob/user, mob/living/carbon/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(!affected) - return 1 +/datum/surgery_step/proxy/robo_limb_attach + name = "apply robotic limb (proxy)" + branches = list( + /datum/surgery/robo_attach, + /datum/surgery/reattach_synth + ) + insert_self_after = FALSE - return 0 +/datum/surgery/attach_robotic_limb + name = "Apply Robotic or Synthetic Limb" + requires_bodypart = FALSE + steps = list( + /datum/surgery_step/proxy/robo_limb_attach + ) + cancel_on_organ_change = FALSE + possible_locs = list( + BODY_ZONE_HEAD, + BODY_ZONE_L_ARM, + BODY_ZONE_PRECISE_L_HAND, + BODY_ZONE_R_ARM, + BODY_ZONE_PRECISE_R_HAND, + BODY_ZONE_R_LEG, + BODY_ZONE_PRECISE_R_FOOT, + BODY_ZONE_L_LEG, + BODY_ZONE_PRECISE_L_FOOT, + BODY_ZONE_PRECISE_GROIN, + BODY_ZONE_TAIL, + BODY_ZONE_WING, + ) -/datum/surgery_step/limb/ - can_infect = 0 -/datum/surgery_step/limb/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - if(!hasorgans(target)) - return 0 - var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(affected) - return 0 - var/list/organ_data = target.dna.species.has_limbs["[target_zone]"] - if(target_zone == BODY_ZONE_TAIL) - return TRUE - if(target_zone == BODY_ZONE_WING) - return TRUE - return !isnull(organ_data) + +/datum/surgery_step/limb + can_infect = FALSE /datum/surgery_step/limb/attach name = "attach limb" allowed_tools = list(/obj/item/organ/external = 100) - time = 32 + time = 3.2 SECONDS + +/datum/surgery_step/limb/attach/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) -/datum/surgery_step/limb/attach/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - if(!..()) - return 0 - if(!istype(tool, /obj/item/organ/external)) - return 0 var/obj/item/organ/external/E = tool if(target.get_organ(E.limb_zone)) // This catches attaching an arm to a missing hand while the arm is still there - to_chat(user, "[target] already has an [E.name]!") - return 0 + to_chat(user, span_warning("[target] already has an [E.name]!")) + return SURGERY_BEGINSTEP_ABORT if(E.limb_zone != target_zone) // This ensures you must be aiming at the appropriate location to attach // this limb. (Can't aim at a missing foot to re-attach a missing arm) - to_chat(user, "The [E.name] does not go there.") - return 0 + to_chat(user, span_warning("The [E.name] does not go there.")) + return SURGERY_BEGINSTEP_ABORT if(!target.get_organ(E.parent_organ_zone)) - to_chat(user, "cannot attach a [E.name] because there is no limb to attach to!") - return 0 + to_chat(user, span_warning("cannot attach a [E.name] because there is no limb to attach to!")) + return SURGERY_BEGINSTEP_ABORT if(!is_correct_limb(E, target)) - to_chat(user, "This is not the correct limb type for this surgery!") - return 0 - - return 1 - - -/datum/surgery_step/limb/attach/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/obj/item/organ/external/E = tool - user.visible_message("[user] starts attaching [E.name] to [target]'s [E.amputation_point].", \ - "You start attaching [E.name] to [target]'s [E.amputation_point].") + to_chat(user, span_warning("This is not the correct limb type for this surgery!")) + return SURGERY_BEGINSTEP_ABORT + var/list/organ_data = target.dna.species.has_limbs["[user.zone_selected]"] + if(isnull(organ_data)) + to_chat(user, span_warning("[target.dna.species] don't have the anatomy for [E.name]!")) + return SURGERY_BEGINSTEP_ABORT + + user.visible_message( + "[user] starts attaching [E.name] to [target]'s [E.amputation_point].", + "You start attaching [E.name] to [target]'s [E.amputation_point]." + ) + return ..() /datum/surgery_step/limb/attach/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/E = tool - user.visible_message("[user] has attached [target]'s [E.name] to the [E.amputation_point].", \ - "You have attached [target]'s [E.name] to the [E.amputation_point].") + user.visible_message( + span_notice("[user] has attached [target]'s [E.name] to the [E.amputation_point]."), + span_notice("You have attached [target]'s [E.name] to the [E.amputation_point].") + ) attach_limb(user, target, E) - return 1 + return SURGERY_STEP_CONTINUE /datum/surgery_step/limb/attach/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/E = tool - user.visible_message("[user]'s hand slips, damaging [target]'s [E.amputation_point]!", \ - "Your hand slips, damaging [target]'s [E.amputation_point]!") - target.apply_damage(10, BRUTE, null, sharp = 1) - return 0 + user.visible_message( + span_alert("[user]'s hand slips, damaging [target]'s [E.amputation_point]!"), + span_alert("Your hand slips, damaging [target]'s [E.amputation_point]!") + ) + target.apply_damage(10, BRUTE, null, sharp = TRUE) + return SURGERY_STEP_RETRY /datum/surgery_step/limb/attach/proc/is_correct_limb(obj/item/organ/external/E, mob/living/carbon/human/target) if(E.is_robotic()) - return 0 + return FALSE if(target.dna.species.type == /datum/species/kidan && istype(E, /obj/item/organ/external/head) && !istype(E, /obj/item/organ/external/head/kidan)) - return 0 - return 1 + return FALSE + return TRUE /datum/surgery_step/limb/attach/proc/attach_limb(mob/living/user, mob/living/carbon/human/target, obj/item/organ/external/E) user.drop_item_ground(E) E.replaced(target) + if(!E.is_robotic()) + E.properly_attached = FALSE target.update_body() target.updatehealth() target.UpdateDamageIcon() @@ -228,65 +244,70 @@ /datum/surgery_step/limb/connect name = "connect limb" allowed_tools = list( - /obj/item/hemostat = 100, \ - /obj/item/stack/cable_coil = 90, \ - /obj/item/stack/sheet/sinew = 90, \ - /obj/item/assembly/mousetrap = 25 + TOOL_HEMOSTAT = 100, + /obj/item/stack/cable_coil = 90, + /obj/item/stack/sheet/sinew = 90, + /obj/item/assembly/mousetrap = 25 ) - can_infect = 1 + can_infect = TRUE - time = 32 - - -/datum/surgery_step/limb/connect/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - if(!hasorgans(target)) - return 0 - return 1 + time = 3.2 SECONDS /datum/surgery_step/limb/connect/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/E = target.get_organ(target_zone) - user.visible_message("[user] starts connecting tendons and muscles in [target]'s [E.amputation_point] with [tool].", \ - "You start connecting tendons and muscle in [target]'s [E.amputation_point].") + user.visible_message( + "[user] starts connecting tendons and muscles in [target]'s [E.amputation_point] with [tool].", + "You start connecting tendons and muscle in [target]'s [E.amputation_point]." + ) + return ..() /datum/surgery_step/limb/connect/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/E = target.get_organ(target_zone) - user.visible_message("[user] has connected tendons and muscles in [target]'s [E.amputation_point] with [tool].", \ - "You have connected tendons and muscles in [target]'s [E.amputation_point] with [tool].") + user.visible_message( + span_notice("[user] has connected tendons and muscles in [target]'s [E.amputation_point] with [tool]."), + span_notice("You have connected tendons and muscles in [target]'s [E.amputation_point] with [tool].") + ) + E.properly_attached = TRUE target.update_body() target.updatehealth() target.UpdateDamageIcon() - return 1 + return SURGERY_STEP_CONTINUE /datum/surgery_step/limb/connect/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/E = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, damaging [target]'s [E.amputation_point]!", \ - "Your hand slips, damaging [target]'s [E.amputation_point]!") - target.apply_damage(10, BRUTE, null, sharp = 1) - return 0 + user.visible_message( + span_alert("[user]'s hand slips, damaging [target]'s [E.amputation_point]!"), + span_alert("Your hand slips, damaging [target]'s [E.amputation_point]!") + ) + target.apply_damage(10, BRUTE, null, sharp = TRUE) + return SURGERY_STEP_RETRY /datum/surgery_step/limb/mechanize name = "apply robotic prosthetic" allowed_tools = list(/obj/item/robot_parts = 100) - time = 32 - -/datum/surgery_step/limb/mechanize/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - if(..()) - var/obj/item/robot_parts/p = tool - if(p.part) - if(!(target_zone in p.part)) - to_chat(user, "\The [tool] does not go there!") - return 0 - return isnull(target.get_organ(target_zone)) + time = 3.2 SECONDS /datum/surgery_step/limb/mechanize/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user] starts attaching \the [tool] to [target].", \ - "You start attaching \the [tool] to [target].") + + var/obj/item/robot_parts/P = tool + if(P.part) + if(!(target_zone in P.part)) + to_chat(user, span_warning("\The [tool] does not go there!")) + return SURGERY_BEGINSTEP_ABORT + + user.visible_message( + "[user] starts attaching \the [tool] to [target].", + "You start attaching \the [tool] to [target]." + ) + return ..() /datum/surgery_step/limb/mechanize/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/robot_parts/L = tool - user.visible_message("[user] has attached \the [tool] to [target].", \ - "You have attached \the [tool] to [target].") + user.visible_message( + span_notice("[user] has attached \the [tool] to [target]."), + span_notice("You have attached \the [tool] to [target].") + ) if(L.part) for(var/part_name in L.part) @@ -306,10 +327,12 @@ qdel(tool) - return 1 + return SURGERY_STEP_CONTINUE /datum/surgery_step/limb/mechanize/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user]'s hand slips, damaging [target]'s flesh!", \ - "Your hand slips, damaging [target]'s flesh!") - target.apply_damage(10, BRUTE, null, sharp = 1) - return 0 + user.visible_message( + span_alert("[user]'s hand slips, damaging [target]'s flesh!"), + span_alert("Your hand slips, damaging [target]'s flesh!") + ) + target.apply_damage(10, BRUTE, null, sharp = TRUE) + return SURGERY_STEP_RETRY diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index 4c76ff9a28e..5516aa345a3 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -32,8 +32,8 @@ /obj/item/organ/internal/cyberimp/arm/examine(mob/user) . = ..() - . += "[src] is assembled in the [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm configuration." - . += "You can use a screwdriver to reassemble it." + . += span_notice("[src] is assembled in the [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm configuration.") + . += span_info("You can use a screwdriver to reassemble it.") /obj/item/organ/internal/cyberimp/arm/screwdriver_act(mob/user, obj/item/I) . = TRUE @@ -44,7 +44,7 @@ else parent_organ_zone = BODY_ZONE_R_ARM slot = parent_organ_zone + "_device" - to_chat(user, "You modify [src] to be installed on the [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.") + to_chat(user, span_notice("You modify [src] to be installed on the [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.")) update_transform() /obj/item/organ/internal/cyberimp/arm/insert(mob/living/carbon/arm_owner, special = ORGAN_MANIPULATION_DEFAULT) @@ -73,7 +73,7 @@ if(emp_proof) return if(prob(15/severity) && owner) - to_chat(owner, "[src] is hit by EMP!") + to_chat(owner, span_warning("[src] is hit by EMP!")) // give the owner an idea about why his implant is glitching Retract() ..() @@ -100,9 +100,9 @@ if(!active_item || (active_item in src)) return FALSE - owner.visible_message("[owner] retracts [active_item] back into [owner.p_their()] [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.", - "[active_item] snaps back into your [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.", - "You hear a short mechanical noise.") + owner.visible_message(span_notice("[owner] retracts [active_item] back into [owner.p_their()] [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm."), + span_notice("[active_item] snaps back into your [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm."), + span_italics("You hear a short mechanical noise.")) owner.drop_item_ground(active_item, force = TRUE, silent = TRUE) active_item.forceMove(src) @@ -127,27 +127,27 @@ if(arm_item) if(!owner.drop_item_ground(arm_item)) - to_chat(owner, "Your [arm_item] interferes with [src]!") + to_chat(owner, span_warning("Your [arm_item] interferes with [src]!")) return else - to_chat(owner, "You drop [arm_item] to activate [src]!") + to_chat(owner, span_notice("You drop [arm_item] to activate [src]!")) if(parent_organ_zone == BODY_ZONE_R_ARM ? !owner.put_in_r_hand(active_item) : !owner.put_in_l_hand(active_item)) - to_chat(owner, "Your [src] fails to activate!") + to_chat(owner, span_warning("Your [src] fails to activate!")) return // Activate the hand that now holds our item. if(parent_organ_zone == BODY_ZONE_R_ARM ? owner.hand : !owner.hand) owner.swap_hand() - owner.visible_message("[owner] extends [active_item] from [owner.p_their()] [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.", - "You extend [active_item] from your [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.", - "You hear a short mechanical noise.") + owner.visible_message(span_notice("[owner] extends [active_item] from [owner.p_their()] [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm."), + span_notice("You extend [active_item] from your [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm."), + span_italics("You hear a short mechanical noise.")) playsound(get_turf(owner), src.sound_on, 50, 1) /obj/item/organ/internal/cyberimp/arm/ui_action_click() if(crit_fail || (!active_item && !contents.len)) - to_chat(owner, "The implant doesn't respond. It seems to be broken...") + to_chat(owner, span_warning("The implant doesn't respond. It seems to be broken...")) return // You can emag the arm-mounted implant by activating it while holding emag in it's hand. @@ -187,9 +187,9 @@ return if(prob(30/severity) && owner && !crit_fail) Retract() - owner.visible_message("A loud bang comes from [owner]\'s [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm!") + owner.visible_message(span_danger("A loud bang comes from [owner]\'s [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm!")) playsound(get_turf(owner), 'sound/weapons/flashbang.ogg', 100, 1) - to_chat(owner, "You feel an explosion erupt inside your [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm as your implant breaks!") + to_chat(owner, span_userdanger("You feel an explosion erupt inside your [parent_organ_zone == BODY_ZONE_R_ARM ? "right" : "left"] arm as your implant breaks!")) owner.adjust_fire_stacks(20) owner.IgniteMob() owner.adjustFireLoss(25) @@ -240,7 +240,7 @@ /obj/item/organ/internal/cyberimp/arm/toolset/emag_act(mob/user) if(!(locate(/obj/item/kitchen/knife/combat/cyborg) in items_list)) if(user) - to_chat(user, "You unlock [src]'s integrated knife!") + to_chat(user, span_notice("You unlock [src]'s integrated knife!")) items_list += new /obj/item/kitchen/knife/combat/cyborg(src) return TRUE return FALSE @@ -427,7 +427,7 @@ /obj/item/organ/internal/cyberimp/arm/power_cord/surgeryize() if(crit_fail && owner) - to_chat(owner, "Your [src] feels functional again.") + to_chat(owner, span_notice("Your [src] feels functional again.")) crit_fail = FALSE @@ -443,7 +443,7 @@ if(!istype(target, /obj/machinery/power/apc) || !ishuman(user) || !proximity_flag) return ..() if(drawing_power) - to_chat(user, "You're already charging.") + to_chat(user, span_warning("You're already charging.")) return user.changeNext_move(CLICK_CD_MELEE) var/obj/machinery/power/apc/A = target @@ -451,42 +451,42 @@ if(H.get_int_organ(/obj/item/organ/internal/cell)) if(A.emagged || A.stat & BROKEN) do_sparks(3, 1, A) - to_chat(H, "The APC power currents surge erratically, damaging your chassis!") + to_chat(H, span_warning("The APC power currents surge erratically, damaging your chassis!")) H.adjustFireLoss(10,0) else if(A.cell && A.cell.charge > 0) if(H.nutrition >= NUTRITION_LEVEL_WELL_FED) - to_chat(user, "You are already fully charged!") + to_chat(user, span_warning("You are already fully charged!")) else INVOKE_ASYNC(src, PROC_REF(powerdraw_loop), A, H) else - to_chat(user, "There is no charge to draw from that APC.") + to_chat(user, span_warning("There is no charge to draw from that APC.")) else - to_chat(user, "You lack a cell in which to store charge!") + to_chat(user, span_warning("You lack a cell in which to store charge!")) /obj/item/apc_powercord/proc/powerdraw_loop(obj/machinery/power/apc/A, mob/living/carbon/human/H) - H.visible_message("[H] inserts a power connector into \the [A].", "You begin to draw power from \the [A].") + H.visible_message(span_notice("[H] inserts a power connector into \the [A]."), span_notice("You begin to draw power from \the [A].")) drawing_power = TRUE while(do_after(H, 10, target = A)) if(loc != H) - to_chat(H, "You must keep your connector out while charging!") + to_chat(H, span_warning("You must keep your connector out while charging!")) break if(A.cell.charge == 0) - to_chat(H, "\The [A] has no more charge.") + to_chat(H, span_warning("\The [A] has no more charge.")) break A.charging = APC_IS_CHARGING if(A.cell.charge >= 500) H.adjust_nutrition(50) A.cell.charge -= 500 - to_chat(H, "You siphon off some of the stored charge for your own use.") + to_chat(H, span_notice("You siphon off some of the stored charge for your own use.")) else H.adjust_nutrition(A.cell.charge * 0.1) A.cell.charge = 0 - to_chat(H, "You siphon off the last of \the [A]'s charge.") + to_chat(H, span_notice("You siphon off the last of \the [A]'s charge.")) break if(H.nutrition > NUTRITION_LEVEL_WELL_FED) - to_chat(H, "You are now fully charged.") + to_chat(H, span_notice("You are now fully charged.")) break - H.visible_message("[H] unplugs from \the [A].", "You unplug from \the [A].") + H.visible_message(span_notice("[H] unplugs from \the [A]."), span_notice("You unplug from \the [A].")) drawing_power = FALSE /obj/item/organ/internal/cyberimp/arm/telebaton diff --git a/code/modules/surgery/organs/augments_eyes.dm b/code/modules/surgery/organs/augments_eyes.dm index a960f314841..a39d005590c 100644 --- a/code/modules/surgery/organs/augments_eyes.dm +++ b/code/modules/surgery/organs/augments_eyes.dm @@ -23,7 +23,7 @@ if(istype(H) && eye_colour) H.update_body() //Apply our eye colour to the target. if(aug_message && !special) - to_chat(owner, "[aug_message]") + to_chat(owner, span_notice("[aug_message]")) M.update_sight() /obj/item/organ/internal/cyberimp/eyes/remove(mob/living/carbon/M, special = ORGAN_MANIPULATION_DEFAULT) @@ -45,7 +45,7 @@ if(severity > 1) if(prob(10 * severity)) return - to_chat(owner, "Static obfuscates your vision!") + to_chat(owner, span_warning("Static obfuscates your vision!")) owner.flash_eyes(visual = 1) /obj/item/organ/internal/cyberimp/eyes/meson diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm index 5ee672fd3ce..3b508a7e65a 100644 --- a/code/modules/surgery/organs/augments_internal.dm +++ b/code/modules/surgery/organs/augments_internal.dm @@ -32,7 +32,7 @@ return var/stun_amount = (5 + (severity-1 ? 0 : 5)) STATUS_EFFECT_CONSTANT owner.Stun(stun_amount) - to_chat(owner, "Your body seizes up!") + to_chat(owner, span_warning("Your body seizes up!")) return stun_amount @@ -69,7 +69,7 @@ r_hand_ignore = FALSE if(!l_hand_obj && !r_hand_obj) - to_chat(owner, "You are not holding any items, your hands relax...") + to_chat(owner, span_notice("You are not holding any items, your hands relax...")) active = 0 else var/msg = 0 @@ -77,14 +77,14 @@ msg += !r_hand_ignore && r_hand_obj ? 2 : 0 switch(msg) if(1) - to_chat(owner, "Your left hand's grip tightens.") + to_chat(owner, span_notice("Your left hand's grip tightens.")) if(2) - to_chat(owner, "Your right hand's grip tightens.") + to_chat(owner, span_notice("Your right hand's grip tightens.")) if(3) - to_chat(owner, "Both of your hand's grips tighten.") + to_chat(owner, span_notice("Both of your hand's grips tighten.")) else release_items() - to_chat(owner, "Your hands relax...") + to_chat(owner, span_notice("Your hands relax...")) l_hand_obj = null r_hand_obj = null @@ -101,12 +101,12 @@ if(L_item) A = pick(oview(range)) L_item.throw_at(A, range, 2) - to_chat(owner, "Your left arm spasms and throws the [L_item.name]!") + to_chat(owner, span_notice("Your left arm spasms and throws the [L_item.name]!")) l_hand_obj = null if(R_item) A = pick(oview(range)) R_item.throw_at(A, range, 2) - to_chat(owner, "Your right arm spasms and throws the [R_item.name]!") + to_chat(owner, span_notice("Your right arm spasms and throws the [R_item.name]!")) r_hand_obj = null /obj/item/organ/internal/cyberimp/brain/anti_drop/proc/release_items() @@ -174,13 +174,13 @@ if(owner.stat == UNCONSCIOUS && cooldown == FALSE) owner.AdjustSleeping(-200 SECONDS) owner.AdjustParalysis(-200 SECONDS) - to_chat(owner, "You feel a rush of energy course through your body!") + to_chat(owner, span_notice("You feel a rush of energy course through your body!")) cooldown = TRUE addtimer(CALLBACK(src, PROC_REF(sleepy_timer_end)), 50) /obj/item/organ/internal/cyberimp/brain/anti_sleep/proc/sleepy_timer_end() cooldown = FALSE - to_chat(owner, "You hear a small beep in your head as your Neural Jumpstarter finishes recharging.") + to_chat(owner, span_notice("You hear a small beep in your head as your Neural Jumpstarter finishes recharging.")) /obj/item/organ/internal/cyberimp/brain/anti_sleep/emp_act(severity) . = ..() @@ -235,15 +235,15 @@ if(emp_proof) return if(owner && active) - to_chat(owner, "Your translator's safeties trigger, it is now turned off.") + to_chat(owner, span_notice("Your translator's safeties trigger, it is now turned off.")) active = FALSE /obj/item/organ/internal/cyberimp/brain/speech_translator/ui_action_click() if(owner && !active) - to_chat(owner, "You turn on your translator implant.") + to_chat(owner, span_notice("You turn on your translator implant.")) active = TRUE else if(owner && active) - to_chat(owner, "You turn off your translator implant.") + to_chat(owner, span_notice("You turn off your translator implant.")) active = FALSE //[[[[MOUTH]]]] @@ -262,7 +262,7 @@ if(emp_proof) return if(prob(60/severity) && owner) - to_chat(owner, "Your breathing tube suddenly closes!") + to_chat(owner, span_warning("Your breathing tube suddenly closes!")) owner.AdjustLoseBreath(4 SECONDS) //[[[[CHEST]]]] @@ -295,7 +295,7 @@ if(!owner || emp_proof) return owner.reagents.add_reagent("????",poison_amount / severity) //food poisoning - to_chat(owner, "You feel like your insides are burning.") + to_chat(owner, span_warning("You feel like your insides are burning.")) /obj/item/organ/internal/cyberimp/chest/nutriment/plus name = "Nutriment pump implant PLUS" @@ -330,7 +330,7 @@ return if(owner.nutrition <= hunger_threshold) synthesizing = TRUE - to_chat(owner, "You feel less hungry...") + to_chat(owner, span_notice("You feel less hungry...")) owner.adjust_nutrition(50) addtimer(CALLBACK(src, PROC_REF(synth_cool)), 50) @@ -341,7 +341,7 @@ if(!owner || emp_proof) return owner.reagents.add_reagent("????",poison_amount / severity) //food poisoning - to_chat(owner, "You feel like your insides are burning.") + to_chat(owner, span_warning("You feel like your insides are burning.")) /obj/item/organ/internal/cyberimp/chest/nutriment_old/plus name = "Nutriment pump implant PLUS" @@ -420,7 +420,7 @@ return H.set_heartattack(FALSE) if(H.stat == CONSCIOUS) - to_chat(H, "You feel your heart beating again!") + to_chat(H, span_notice("You feel your heart beating again!")) //BOX O' IMPLANTS diff --git a/code/modules/surgery/organs/augments_legs.dm b/code/modules/surgery/organs/augments_legs.dm index 94cc1cae3f0..0c77e3bd1c3 100644 --- a/code/modules/surgery/organs/augments_legs.dm +++ b/code/modules/surgery/organs/augments_legs.dm @@ -56,7 +56,7 @@ parent_organ_zone = BODY_ZONE_R_LEG transform = null SetSlot() - to_chat(user, "You modify [src] to be installed on the [parent_organ_zone == BODY_ZONE_R_LEG ? "right" : "left"] leg.") + to_chat(user, span_notice("You modify [src] to be installed on the [parent_organ_zone == BODY_ZONE_R_LEG ? "right" : "left"] leg.")) /obj/item/organ/internal/cyberimp/leg/insert(mob/living/carbon/M, special = ORGAN_MANIPULATION_DEFAULT) @@ -132,7 +132,7 @@ if(!IsAvailable()) return if(recharging_time > world.time) - to_chat(owner, "The boot's internal propulsion needs to recharge still!") + to_chat(owner, span_warning("The boot's internal propulsion needs to recharge still!")) return var/atom/target = get_edge_target_turf(owner, owner.dir) //gets the user's direction @@ -144,10 +144,10 @@ if(owner.throw_at(target, jumpdistance, jumpspeed, spin = FALSE, diagonals_first = TRUE, callback = after_jump_callback)) last_jump = after_jump_callback playsound(owner.loc, 'sound/effects/stealthoff.ogg', 50, TRUE, 1) - owner.visible_message("[usr] dashes forward into the air!") + owner.visible_message(span_warning("[usr] dashes forward into the air!")) recharging_time = world.time + recharging_rate else - to_chat(owner, "Something prevents you from dashing forward!") + to_chat(owner, span_warning("Something prevents you from dashing forward!")) after_jump(owner) diff --git a/code/modules/surgery/organs/autoimplanter.dm b/code/modules/surgery/organs/autoimplanter.dm index a2489cc334e..b3238d72472 100644 --- a/code/modules/surgery/organs/autoimplanter.dm +++ b/code/modules/surgery/organs/autoimplanter.dm @@ -28,20 +28,20 @@ /obj/item/autoimplanter/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/organ/internal/cyberimp)) if(storedorgan) - to_chat(user, "[src] already has an implant stored.") + to_chat(user, span_notice("[src] already has an implant stored.")) return if(!user.temporarily_remove_item_from_inventory(I)) return I.forceMove(src) storedorgan = I - to_chat(user, "You insert the [I] into [src].") + to_chat(user, span_notice("You insert the [I] into [src].")) else if(I.tool_behaviour == TOOL_SCREWDRIVER) if(!storedorgan) - to_chat(user, "There's no implant in [src] for you to remove.") + to_chat(user, span_notice("There's no implant in [src] for you to remove.")) else storedorgan.forceMove(get_turf(user)) storedorgan = null - to_chat(user, "You remove the [storedorgan] from [src].") + to_chat(user, span_notice("You remove the [storedorgan] from [src].")) playsound(get_turf(user), I.usesound, 50, 1) /obj/item/autoimplanter/oneuse @@ -58,10 +58,10 @@ if(I.tool_behaviour == TOOL_SCREWDRIVER) storedorgan.forceMove(get_turf(user)) storedorgan = null - to_chat(user, "You remove the [storedorgan] from [src].") + to_chat(user, span_notice("You remove the [storedorgan] from [src].")) playsound(get_turf(user), I.usesound, 50, 1) user.temporarily_remove_item_from_inventory(src, force = TRUE) - visible_message("[src] beeps ominously, and a moment later it bursts up in flames.") + visible_message(span_warning("[src] beeps ominously, and a moment later it bursts up in flames.")) new /obj/effect/decal/cleanable/ash(get_turf(src)) qdel(src) . = ..() @@ -96,11 +96,11 @@ uses-- if(uses == 0) user.drop_from_active_hand() - visible_message("[src] beeps ominously, and a moment later it bursts up in flames.") + visible_message(span_warning("[src] beeps ominously, and a moment later it bursts up in flames.")) new /obj/effect/decal/cleanable/ash(get_turf(src)) qdel(src) /obj/item/autoimplanter/traitor/examine(mob/user) . = ..() if(uses) - . += "There are [uses] uses left." + . += span_notice("There are [uses] uses left.") diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm index 34aee712d97..e19c79756c0 100644 --- a/code/modules/surgery/organs/blood.dm +++ b/code/modules/surgery/organs/blood.dm @@ -14,7 +14,7 @@ /mob/living/carbon/human/proc/resume_bleeding() bleedsuppress = FALSE if(stat != DEAD && bleed_rate) - to_chat(src, "The blood soaks through your bandage.") + to_chat(src, span_warning("The blood soaks through your bandage.")) // Takes care blood loss and regeneration /mob/living/carbon/human/handle_blood() @@ -35,18 +35,18 @@ switch(blood_volume) if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) if(prob(5)) - to_chat(src, "You feel [word].") + to_chat(src, span_warning("You feel [word].")) apply_damage_type(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.014, 1), dna.species.blood_damage_type) if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY) apply_damage_type(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.028, 1), dna.species.blood_damage_type) if(prob(5)) EyeBlurry(12 SECONDS) - to_chat(src, "You feel very [word].") + to_chat(src, span_warning("You feel very [word].")) if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD) apply_damage_type(5, dna.species.blood_damage_type) if(prob(15)) Paralyse(rand(2 SECONDS, 6 SECONDS)) - to_chat(src, "You feel extremely [word].") + to_chat(src, span_warning("You feel extremely [word].")) if(-INFINITY to BLOOD_VOLUME_SURVIVE) death() diff --git a/code/modules/surgery/organs/body_egg.dm b/code/modules/surgery/organs/body_egg.dm index c99b517e317..39f7a1ae124 100644 --- a/code/modules/surgery/organs/body_egg.dm +++ b/code/modules/surgery/organs/body_egg.dm @@ -8,7 +8,7 @@ /obj/item/organ/internal/body_egg/on_find(mob/living/finder) ..() - to_chat(finder, "You found an unknown alien organism in [owner]'s [parent_organ_zone]!") + to_chat(finder, span_warning("You found an unknown alien organism in [owner]'s [parent_organ_zone]!")) /obj/item/organ/internal/body_egg/insert(mob/living/carbon/M, special = ORGAN_MANIPULATION_DEFAULT) diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm index 005ac0c47cf..86884d1aebc 100644 --- a/code/modules/surgery/organs/heart.dm +++ b/code/modules/surgery/organs/heart.dm @@ -31,7 +31,7 @@ /obj/item/organ/internal/heart/attack_self(mob/user) ..() if(is_dead()) - to_chat(user, "You can't restart a dead heart.") + to_chat(user, span_warning("You can't restart a dead heart.")) return if(!beating) Restart() @@ -79,7 +79,7 @@ /obj/item/organ/internal/heart/cursed/attack(mob/living/carbon/human/H, mob/living/carbon/human/user, obj/target) if(H == user && istype(H)) if(NO_BLOOD in H.dna.species.species_traits) - to_chat(H, "[src] is not compatible with your form!") + to_chat(H, span_userdanger("[src] is not compatible with your form!")) return playsound(user,'sound/effects/singlebeat.ogg', 40, 1) user.drop_item_ground(src) @@ -93,7 +93,7 @@ var/mob/living/carbon/human/H = owner if(!(NO_BLOOD in H.dna.species.species_traits)) H.blood_volume = max(H.blood_volume - blood_loss, 0) - to_chat(H, "You have to keep pumping your blood!") + to_chat(H, span_userdanger("You have to keep pumping your blood!")) if(H.client) H.client.color = "red" //bloody screen so real else @@ -102,7 +102,7 @@ /obj/item/organ/internal/heart/cursed/insert(mob/living/carbon/M, special = ORGAN_MANIPULATION_DEFAULT) ..() if(owner) - to_chat(owner, "Your heart has been replaced with a cursed one, you have to pump this one manually otherwise you'll die!") + to_chat(owner, span_userdanger("Your heart has been replaced with a cursed one, you have to pump this one manually otherwise you'll die!")) /datum/action/item_action/organ_action/cursed_heart name = "pump your blood" @@ -114,12 +114,12 @@ var/obj/item/organ/internal/heart/cursed/cursed_heart = target if(world.time < (cursed_heart.last_pump + (cursed_heart.pump_delay - 10))) //no spam - to_chat(owner, "Too soon!") + to_chat(owner, span_userdanger("Too soon!")) return cursed_heart.last_pump = world.time playsound(owner,'sound/effects/singlebeat.ogg',40,1) - to_chat(owner, "Your heart beats.") + to_chat(owner, span_notice("Your heart beats.")) var/mob/living/carbon/human/H = owner if(istype(H)) @@ -156,16 +156,16 @@ return if(!is_dead() && !attempted_restart && !beating) - to_chat(owner, "Your [name] detects a cardiac event and attempts to return to its normal rhythm!") + to_chat(owner, span_warning("Your [name] detects a cardiac event and attempts to return to its normal rhythm!")) if(prob(20) && emagged) attempted_restart = TRUE Restart() - addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "Your [name] returns to its normal rhythm!"), 30) + addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, span_warning("Your [name] returns to its normal rhythm!")), 30) addtimer(CALLBACK(src, PROC_REF(recharge)), 200) else if(prob(10)) attempted_restart = TRUE Restart() - addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "Your [name] returns to its normal rhythm!"), 30) + addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, span_warning("Your [name] returns to its normal rhythm!")), 30) addtimer(CALLBACK(src, PROC_REF(recharge)), 300) else attempted_restart = TRUE @@ -173,21 +173,21 @@ addtimer(CALLBACK(src, PROC_REF(recharge)), 200) else addtimer(CALLBACK(src, PROC_REF(recharge)), 300) - addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "Your [name] fails to return to its normal rhythm!"), 30) + addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, span_warning("Your [name] fails to return to its normal rhythm!")), 30) if(!is_dead() && !attempted_restart && owner.HasDisease(/datum/disease/critical/heart_failure)) - to_chat(owner, "Your [name] detects a cardiac event and attempts to return to its normal rhythm!") + to_chat(owner, span_warning("Your [name] detects a cardiac event and attempts to return to its normal rhythm!")) if(prob(40) && emagged) attempted_restart = TRUE for(var/datum/disease/critical/heart_failure/HF in owner.diseases) HF.cure() - addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "Your [name] returns to its normal rhythm!"), 30) + addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, span_warning("Your [name] returns to its normal rhythm!")), 30) addtimer(CALLBACK(src, PROC_REF(recharge)), 200) else if(prob(25)) attempted_restart = TRUE for(var/datum/disease/critical/heart_failure/HF in owner.diseases) HF.cure() - addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "Your [name] returns to its normal rhythm!"), 30) + addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, span_warning("Your [name] returns to its normal rhythm!")), 30) addtimer(CALLBACK(src, PROC_REF(recharge)), 200) else attempted_restart = TRUE @@ -195,7 +195,7 @@ addtimer(CALLBACK(src, PROC_REF(recharge)), 200) else addtimer(CALLBACK(src, PROC_REF(recharge)), 300) - addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "Your [name] fails to return to its normal rhythm!"), 30) + addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, span_warning("Your [name] fails to return to its normal rhythm!")), 30) if(!is_dead()) var/boost = emagged ? 2 : 1 @@ -219,12 +219,12 @@ if(!emagged) add_attack_logs(user, src, "emagged") if(user) - to_chat(user, "You disable the safeties on [src]") + to_chat(user, span_warning("You disable the safeties on [src]")) emagged = TRUE else add_attack_logs(user, src, "un-emagged") if(user) - to_chat(user, "You re-enable the safeties on [src]") + to_chat(user, span_warning("You re-enable the safeties on [src]")) emagged = FALSE @@ -246,29 +246,29 @@ var/numLow = round(intensity / 20) if(emagged && !is_dead()) if(prob(numHigh)) - to_chat(owner, "Your [name] spasms violently!") + to_chat(owner, span_warning("Your [name] spasms violently!")) owner.adjustBruteLoss(numHigh) if(prob(numHigh)) - to_chat(owner, "Your [name] shocks you painfully!") + to_chat(owner, span_warning("Your [name] shocks you painfully!")) owner.adjustFireLoss(numHigh) if(prob(numMid)) - to_chat(owner, "Your [name] lurches awkwardly!") + to_chat(owner, span_warning("Your [name] lurches awkwardly!")) var/datum/disease/critical/heart_failure/D = new D.Contract(owner) if(prob(numMid)) - to_chat(owner, "Your [name] stops beating!") + to_chat(owner, span_danger("Your [name] stops beating!")) Stop() if(prob(numLow)) - to_chat(owner, "Your [name] shuts down!") + to_chat(owner, span_danger("Your [name] shuts down!")) necrotize() else if(!emagged && !is_dead()) if(prob(numMid)) - to_chat(owner, "Your [name] spasms violently!") + to_chat(owner, span_warning("Your [name] spasms violently!")) owner.adjustBruteLoss(numMid) if(prob(numMid)) - to_chat(owner, "Your [name] shocks you painfully!") + to_chat(owner, span_warning("Your [name] shocks you painfully!")) owner.adjustFireLoss(numMid) if(prob(numLow)) - to_chat(owner, "Your [name] lurches awkwardly!") + to_chat(owner, span_warning("Your [name] lurches awkwardly!")) var/datum/disease/critical/heart_failure/D = new D.Contract(owner) diff --git a/code/modules/surgery/organs/liver.dm b/code/modules/surgery/organs/liver.dm index 3df7b08afa5..387893635af 100644 --- a/code/modules/surgery/organs/liver.dm +++ b/code/modules/surgery/organs/liver.dm @@ -8,7 +8,7 @@ /obj/item/organ/internal/liver/on_life() if(germ_level > INFECTION_LEVEL_ONE) if(prob(1)) - to_chat(owner, " Your skin itches.") + to_chat(owner, span_warning(" Your skin itches.")) if(germ_level > INFECTION_LEVEL_TWO) if(prob(1)) owner.vomit() diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm index 6dcf2c343ad..56a12bd03fa 100644 --- a/code/modules/surgery/organs/lungs.dm +++ b/code/modules/surgery/organs/lungs.dm @@ -284,7 +284,7 @@ H.apply_damage_type(TC * CM * cold_damage_types[D], D) if(breath_temperature < cold_level_1_threshold) if(prob(20)) - to_chat(H, "You feel [cold_message] in your [name]!") + to_chat(H, span_warning("You feel [cold_message] in your [name]!")) if(!(HEATRES in H.mutations) && !(RESISTHOT in species_traits)) // HEAT DAMAGE var/HM = abs(H.dna.species.heatmod) @@ -300,7 +300,7 @@ H.apply_damage_type(TH * HM * heat_damage_types[D], D) if(breath_temperature > heat_level_1_threshold) if(prob(20)) - to_chat(H, "You feel [hot_message] in your [name]!") + to_chat(H, span_warning("You feel [hot_message] in your [name]!")) /obj/item/organ/internal/lungs/prepare_eat() var/obj/S = ..() @@ -349,7 +349,7 @@ /obj/item/organ/internal/lungs/cybernetic/examine(mob/user) . = ..() - . += "[src] is configured for [species_state] standards of atmosphere." + . += span_notice("[src] is configured for [species_state] standards of atmosphere.") /obj/item/organ/internal/lungs/cybernetic/multitool_act(mob/user, obj/item/I) . = TRUE @@ -361,7 +361,7 @@ safe_oxygen_max = safe_toxins_max safe_nitro_min = 16 oxy_damage_type = TOX - to_chat(user, "You configure [src] to replace vox lungs.") + to_chat(user, span_notice("You configure [src] to replace vox lungs.")) species_state = "vox" if("vox") // from vox to plasmamen safe_oxygen_max = initial(safe_oxygen_max) @@ -369,13 +369,13 @@ safe_toxins_max = 0 safe_nitro_min = initial(safe_nitro_min) oxy_damage_type = OXY - to_chat(user, "You configure [src] to replace plasmamen lungs.") + to_chat(user, span_notice("You configure [src] to replace plasmamen lungs.")) species_state = "plasmamen" if("plasmamen") // from plasmamen to human safe_oxygen_min = initial(safe_oxygen_min) safe_toxins_min = initial(safe_toxins_min) safe_toxins_max = initial(safe_toxins_max) - to_chat(user, "You configure [src] back to default settings.") + to_chat(user, span_notice("You configure [src] back to default settings.")) species_state = "human" /obj/item/organ/internal/lungs/cybernetic/upgraded diff --git a/code/modules/surgery/organs/organ.dm b/code/modules/surgery/organs/organ.dm index 2605be660ce..c1661417dda 100644 --- a/code/modules/surgery/organs/organ.dm +++ b/code/modules/surgery/organs/organ.dm @@ -330,6 +330,7 @@ if(!istype(owner)) return + SEND_SIGNAL(owner, COMSIG_CARBON_LOSE_ORGAN, src) owner.internal_organs -= src var/obj/item/organ/external/affected = owner.get_organ(parent_organ_zone) diff --git a/code/modules/surgery/organs/organ_external.dm b/code/modules/surgery/organs/organ_external.dm index c38f7827fe0..3acca8bb437 100644 --- a/code/modules/surgery/organs/organ_external.dm +++ b/code/modules/surgery/organs/organ_external.dm @@ -100,6 +100,8 @@ var/broken_description /// Descriptive string used in amputation var/amputation_point + /// If the organ has been properly attached or not. Limbs on mobs and robotic ones + var/properly_attached = FALSE light_system = MOVABLE_LIGHT light_on = FALSE @@ -114,8 +116,23 @@ if(ishuman(holder)) replaced(holder) sync_colour_to_human(holder) + properly_attached = TRUE + + if(is_robotic()) + // These can just be slapped on. + properly_attached = TRUE + get_icon() + // so you can just smack the limb onto a guy to start the "surgery" + var/application_surgery + if(!is_robotic()) + application_surgery = /datum/surgery/reattach + else + application_surgery = /datum/surgery/reattach_synth + + AddComponent(/datum/component/surgery_initiator/limb, forced_surgery = application_surgery) + /obj/item/organ/external/Destroy() if(parent) @@ -156,6 +173,8 @@ owner = target forceMove(owner) + if(iscarbon(owner)) + SEND_SIGNAL(owner, COMSIG_CARBON_GAIN_ORGAN, src) if(LAZYLEN(embedded_objects)) owner.throw_alert("embeddedobject", /obj/screen/alert/embeddedobject) @@ -411,7 +430,7 @@ This function completely restores a damaged organ to perfect condition. perma_injury = 0 brute_dam = 0 burn_dam = 0 - open = 0 //Closing all wounds. + open = ORGAN_CLOSED //Closing all wounds. disfigured = FALSE // handle internal organs @@ -670,6 +689,7 @@ Note that amputating the affected organ does in fact remove the infection from t if(organ_spilled && !silent) organ_owner.visible_message(span_danger("[organ_owner]'s internal organs spill out onto the floor!")) + open = ORGAN_ORGANIC_OPEN return TRUE @@ -941,6 +961,9 @@ Note that amputating the affected organ does in fact remove the infection from t // This is so surgery isn't kaput, let's see how this does encased = null + // override the existing initiator + AddComponent(/datum/component/surgery_initiator/limb, forced_surgery = /datum/surgery/reattach_synth) + if(istext(company)) set_company(company) diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 85a5ce3c1cc..f2c8466801c 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -260,7 +260,7 @@ else if(light_count < 2 && obj_integrity < max_integrity) //Heal in the dark obj_integrity++ if(obj_integrity <= 0) - visible_message("[src] collapses in on itself!") + visible_message(span_warning("[src] collapses in on itself!")) qdel(src) diff --git a/code/modules/surgery/organs/pain.dm b/code/modules/surgery/organs/pain.dm index e60354c6140..09716bd4867 100644 --- a/code/modules/surgery/organs/pain.dm +++ b/code/modules/surgery/organs/pain.dm @@ -1,21 +1,26 @@ #define MIN_SHOCK_REDUCTION 50 //The minimum amount of shock reduction in reagents for absence of pain -/mob/living/carbon/human +/mob/living/carbon var/last_pain_message = "" var/next_pain_time = 0 +/** + * Whether or not a mob can feel pain. + * + * Returns TRUE if the mob can feel pain, FALSE otherwise + */ /mob/proc/has_pain() if(stat) return FALSE return TRUE -/mob/living/carbon/human/has_pain() +/mob/living/carbon/has_pain() . = ..() if(!.) return FALSE - if(NO_PAIN in dna.species.species_traits) + if(dna?.species && (NO_PAIN in dna.species.species_traits)) return FALSE if(shock_reduction() >= MIN_SHOCK_REDUCTION) return FALSE @@ -23,7 +28,7 @@ // partname is the name of a body part // amount is a num from 1 to 100 -/mob/living/carbon/human/proc/pain(partname, amount) +/mob/living/carbon/proc/pain(partname, amount) if(reagents.has_reagent("sal_acid")) return if(!has_pain()) @@ -45,11 +50,11 @@ // message is the custom message to be displayed -/mob/living/carbon/human/proc/custom_pain(message) +/mob/living/carbon/proc/custom_pain(message) if(!has_pain()) return - var/msg = "[message]" + var/msg = span_userdanger("[message]") // Anti message spam checks if(msg && ((msg != last_pain_message) || (world.time >= next_pain_time))) diff --git a/code/modules/surgery/organs/parasites.dm b/code/modules/surgery/organs/parasites.dm index e61741b80f2..1f6b229e424 100644 --- a/code/modules/surgery/organs/parasites.dm +++ b/code/modules/surgery/organs/parasites.dm @@ -22,14 +22,14 @@ if(prob(12)) owner.reagents.add_reagent("histamine", 5) if(5) - to_chat(owner, "You feel like something is tearing its way out of your skin...") + to_chat(owner, span_danger("You feel like something is tearing its way out of your skin...")) owner.reagents.add_reagent("histamine", 10) if(prob(30)) owner.emote("scream") var/spiders = rand(3,5) for(var/i in 1 to spiders) new/obj/structure/spider/spiderling(get_turf(owner)) - owner.visible_message("[owner] bursts open! Holy fuck!") + owner.visible_message(span_danger("[owner] bursts open! Holy fuck!")) owner.gib() /obj/item/organ/internal/body_egg/spider_eggs/remove(mob/living/carbon/M, special = ORGAN_MANIPULATION_DEFAULT) @@ -112,7 +112,7 @@ owner.adjustBruteLoss(80) owner.Paralyse(20 SECONDS) owner.SetConfused(40 SECONDS) - to_chat(owner, "A strange prickling sensation moves across your skin... then suddenly the whole world seems to spin around you!") + to_chat(owner, span_warning("A strange prickling sensation moves across your skin... then suddenly the whole world seems to spin around you!")) if(infection_completed && !QDELETED(src)) qdel(src) diff --git a/code/modules/surgery/organs/subtypes/kidan.dm b/code/modules/surgery/organs/subtypes/kidan.dm index e054efffecb..d257a7ec0e7 100644 --- a/code/modules/surgery/organs/subtypes/kidan.dm +++ b/code/modules/surgery/organs/subtypes/kidan.dm @@ -26,21 +26,21 @@ /obj/item/organ/internal/lantern/ui_action_click() if(toggle_biolum()) if(glowing) - owner.visible_message("[owner] starts to glow!", "You enable your bioluminescence.") + owner.visible_message(span_notice("[owner] starts to glow!"), span_notice("You enable your bioluminescence.")) else - owner.visible_message("[owner] fades to dark.", "You disable your bioluminescence.") + owner.visible_message(span_notice("[owner] fades to dark."), span_notice("You disable your bioluminescence.")) /obj/item/organ/internal/lantern/on_life() ..() if(glowing)//i hate this but i couldnt figure out a better way if(owner.nutrition < KIDAN_LANTERN_MINHUNGER) toggle_biolum(1) - to_chat(owner, "You're too hungry to be bioluminescent!") + to_chat(owner, span_warning("You're too hungry to be bioluminescent!")) return if(owner.stat) toggle_biolum(1) - owner.visible_message("[owner] fades to dark.") + owner.visible_message(span_notice("[owner] fades to dark.")) return owner.set_nutrition(max(owner.nutrition - KIDAN_LANTERN_HUNGERCOST, KIDAN_LANTERN_HUNGERCOST)) @@ -63,11 +63,11 @@ /obj/item/organ/internal/lantern/proc/toggle_biolum(statoverride) if(!statoverride && owner.incapacitated()) - to_chat(owner, "You cannot alter your bioluminescence in your current state.") + to_chat(owner, span_warning("You cannot alter your bioluminescence in your current state.")) return 0 if(!statoverride && owner.nutrition < KIDAN_LANTERN_MINHUNGER) - to_chat(owner, "You're too hungry to be bioluminescent!") + to_chat(owner, span_warning("You're too hungry to be bioluminescent!")) return 0 if(!colour) diff --git a/code/modules/surgery/organs/subtypes/machine.dm b/code/modules/surgery/organs/subtypes/machine.dm index 5b7f0c5e1f5..aa7ab941793 100644 --- a/code/modules/surgery/organs/subtypes/machine.dm +++ b/code/modules/surgery/organs/subtypes/machine.dm @@ -191,5 +191,5 @@ /obj/item/organ/internal/ears/microphone/remove(mob/living/user, special = ORGAN_MANIPULATION_DEFAULT) if(!special) - to_chat(owner, "BZZZZZZZZZZZZZZT! Microphone error!") + to_chat(owner, span_userdanger("BZZZZZZZZZZZZZZT! Microphone error!")) . = ..() diff --git a/code/modules/surgery/organs/subtypes/skrell.dm b/code/modules/surgery/organs/subtypes/skrell.dm index 53d70098854..0e78932c68a 100644 --- a/code/modules/surgery/organs/subtypes/skrell.dm +++ b/code/modules/surgery/organs/subtypes/skrell.dm @@ -42,8 +42,8 @@ ..() var/obj/item/organ/external/head/head = owner.get_organ(BODY_ZONE_HEAD) if(pocket.contents.len && !findtextEx(head.h_style, "Tentacles")) - owner.visible_message("Something falls from [owner]'s head!", - "Something falls from your head!") + owner.visible_message(span_notice("Something falls from [owner]'s head!"), + span_notice("Something falls from your head!")) empty_contents() /obj/item/organ/internal/headpocket/ui_action_click() diff --git a/code/modules/surgery/organs/subtypes/standard.dm b/code/modules/surgery/organs/subtypes/standard.dm index e1dd879ed04..16d4a61f2d2 100644 --- a/code/modules/surgery/organs/subtypes/standard.dm +++ b/code/modules/surgery/organs/subtypes/standard.dm @@ -26,7 +26,7 @@ owner?.adjustStaminaLoss(20) if(2) owner?.adjustStaminaLoss(10) - to_chat(owner, "Ваш [name] выходит из строя, вызывая усталость!") + to_chat(owner, span_userdanger("Ваш [name] выходит из строя, вызывая усталость!")) /obj/item/organ/external/groin name = "lower body" @@ -61,7 +61,7 @@ var/hand = (limb_zone == BODY_ZONE_L_ARM) ? owner.l_hand : owner.r_hand if(hand && owner.can_unEquip(hand)) owner.drop_item_ground(hand) - to_chat(owner, "Ваш [name] выходит из строя, бросая то что держал!") + to_chat(owner, span_userdanger("Ваш [name] выходит из строя, бросая то что держал!")) owner.custom_emote(EMOTE_VISIBLE, "роня%(ет,ют)% предмет, %(его,её,его,их)% рука выходит из строя!") /obj/item/organ/external/arm/right @@ -129,10 +129,10 @@ if(!owner || !is_robotic() || emp_proof || !tough) // Augmented legs and feet make the user drop to the floor on EMP. return if(owner.IsWeakened()) - to_chat(owner, "Ваш [name] выходит из строя, не давая вам встать!") + to_chat(owner, span_userdanger("Ваш [name] выходит из строя, не давая вам встать!")) owner.custom_emote(EMOTE_VISIBLE, "не мо%(жет,гут)% встать, %(его,её,его,их)% нога выходит из строя!") else - to_chat(owner, "Ваш [name] выходит из строя, заставив вас упасть на пол!") + to_chat(owner, span_userdanger("Ваш [name] выходит из строя, заставив вас упасть на пол!")) owner.custom_emote(EMOTE_VISIBLE, "пада%(ет,ют)% на пол, %(его,её,его,их)% нога выходит из строя!") switch(severity) if(1) @@ -248,10 +248,10 @@ if(!owner || !is_robotic() || emp_proof || !tough) // Augmented legs and feet make the user drop to the floor on EMP. return if(owner.IsWeakened()) - to_chat(owner, "Ваш [name] выходит из строя, не давая вам встать!") + to_chat(owner, span_userdanger("Ваш [name] выходит из строя, не давая вам встать!")) owner.custom_emote(EMOTE_VISIBLE, "не мо%(жет,гут)% встать, %(его,её,его,их)% ступня выходит из строя!") else - to_chat(owner, "Ваш [name] выходит из строя, падая на пол!") + to_chat(owner, span_userdanger("Ваш [name] выходит из строя, падая на пол!")) owner.custom_emote(EMOTE_VISIBLE, "пада%(ет,ют)% на пол, %(его,её,его,их)% ступня выходит из строя!") switch(severity) if(1) @@ -342,7 +342,7 @@ var/hand = (limb_zone == BODY_ZONE_L_ARM) ? owner.l_hand : owner.r_hand if(hand && owner.can_unEquip(hand)) owner.drop_item_ground(hand) - to_chat(owner, "Ваш [name] выходит из строя, dropping what it was holding!") + to_chat(owner, span_userdanger("Ваш [name] выходит из строя, dropping what it was holding!")) owner.custom_emote(EMOTE_VISIBLE, "роня%(ет,ют)% предмет, %(его,её,его,их)% кисть выходит из строя!") @@ -424,11 +424,11 @@ . = ..() if(in_range(user, src) || istype(user, /mob/dead/observer)) if(!contents.len) - . += "Выглядит пустой." + . += span_notice("Выглядит пустой.") else - . += "Выглядит относительно целой, внутри что-то есть." + . += span_notice("Выглядит относительно целой, внутри что-то есть.") else - . += "Вы должны подойти ближе, чтобы осмотреть это." + . += span_notice("Вы должны подойти ближе, чтобы осмотреть это.") /obj/item/organ/external/head/proc/handle_alt_icon() if(alt_head && GLOB.alt_heads_list[alt_head]) @@ -459,7 +459,7 @@ owner.AdjustConfused(60 SECONDS) if(2) owner.AdjustConfused(40 SECONDS) - to_chat(owner, "Ваш [name] выходит из строя, перегружая ваше управление!") + to_chat(owner, span_userdanger("Ваш [name] выходит из строя, перегружая ваше управление!")) /obj/item/organ/external/tail limb_zone = BODY_ZONE_TAIL diff --git a/code/modules/surgery/organs/subtypes/wryn.dm b/code/modules/surgery/organs/subtypes/wryn.dm index 945d2d8a66a..7f55f77fe5d 100644 --- a/code/modules/surgery/organs/subtypes/wryn.dm +++ b/code/modules/surgery/organs/subtypes/wryn.dm @@ -26,7 +26,7 @@ owner.adjustWax(10) owner.set_nutrition(owner.nutrition - 25) if(prob(10)) - to_chat(owner, "Вы чувствуете лёгкое бурление в восковых железах.") + to_chat(owner, span_notice("Вы чувствуете лёгкое бурление в восковых железах.")) /obj/item/organ/internal/wryn/glands/insert(mob/living/carbon/M, special = ORGAN_MANIPULATION_DEFAULT) ..() @@ -55,11 +55,10 @@ if(do_after(usr, 50, target = usr)) if(locate(/obj/structure/wryn/wax) in get_turf(owner)) - to_chat(owner, "Место уже занято!") + to_chat(owner, span_notice("Место уже занято!")) return host.adjustWax(-50) - for(var/mob/O in viewers(host, null)) - O.show_message(text("[host] выделяет кучу воска и формирует из неё [choice]!"), 1) + host.visible_message(span_alert("[host] выделяет кучу воска и формирует из неё [choice]!")) switch(choice) if("соты") new /obj/structure/wryn/wax/wall(host.loc) @@ -67,7 +66,7 @@ new /obj/structure/wryn/wax/window(host.loc) else - to_chat(owner, "Не хватает воска!") + to_chat(owner, span_notice("Не хватает воска!")) return @@ -82,14 +81,13 @@ if(host.getWax() >= 25) if(do_after(usr, 10, target = usr)) if(locate(/obj/structure/wryn/floor) in get_turf(owner)) - to_chat(owner, "Пол здесь уже готов.") + to_chat(owner, span_notice("Пол здесь уже готов.")) return host.adjustWax(-25) - for(var/mob/O in viewers(owner, null)) - O.show_message(text("[owner] выделяет кучу воска и формирует из неё пол!"), 1) + host.visible_message(span_alert("[owner] выделяет кучу воска и формирует из неё пол!")) new /obj/structure/wryn/floor(owner.loc) else - to_chat(owner, "Не хватает воска!") + to_chat(owner, span_notice("Не хватает воска!")) return /datum/action/innate/toggle_producing diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index 010d156f414..fc418443180 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -1,19 +1,44 @@ -#define GHETTO_DISINFECT_AMOUNT 5 //Amount of units to transfer from the container to the organs during ghetto surgery disinfection step +/// Amount of units to transfer from the container to the organs during disinfection step. +#define GHETTO_DISINFECT_AMOUNT 5 +/// Amount of mito necessary to revive an organ +#define MITO_REVIVAL_COST 5 + + /datum/surgery/organ_manipulation name = "Organ Manipulation" - steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/open_encased/saw, - /datum/surgery_step/open_encased/retract, /datum/surgery_step/internal/manipulate_organs, /datum/surgery_step/glue_bone, /datum/surgery_step/set_bone,/datum/surgery_step/finish_bone,/datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/open_organ, + /datum/surgery_step/open_encased/saw, + /datum/surgery_step/open_encased/retract, + /datum/surgery_step/proxy/manipulate_organs, + /datum/surgery_step/internal/manipulate_organs/finish, + /datum/surgery_step/glue_bone, + /datum/surgery_step/set_bone, + /datum/surgery_step/finish_bone, + /datum/surgery_step/proxy/open_organ, + /datum/surgery_step/generic/cauterize + ) possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_HEAD, ) - requires_organic_bodypart = 1 + requires_organic_bodypart = TRUE + requires_bodypart = TRUE + restricted_speciestypes = list(/datum/species/kidan, /datum/species/wryn, /datum/species/plasmaman) /datum/surgery/organ_manipulation/soft possible_locs = list(BODY_ZONE_PRECISE_GROIN, BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH) - steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/internal/manipulate_organs,/datum/surgery_step/generic/cauterize) - requires_organic_bodypart = 1 + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/manipulate_organs, + /datum/surgery_step/generic/cauterize + ) /datum/surgery/organ_manipulation_boneless name = "Organ Manipulation" @@ -29,18 +54,37 @@ BODY_ZONE_R_LEG, BODY_ZONE_TAIL, ) - steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/internal/manipulate_organs,/datum/surgery_step/generic/cauterize) - requires_organic_bodypart = 1 + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/manipulate_organs, + /datum/surgery_step/generic/cauterize + ) + requires_organic_bodypart = TRUE /datum/surgery/organ_manipulation/plasmaman name = "Plasmaman Organ Manipulation" - steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/open_encased/saw, - /datum/surgery_step/open_encased/retract, /datum/surgery_step/internal/manipulate_organs, /datum/surgery_step/glue_bone/plasma, /datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/open_organ/plasma, + /datum/surgery_step/open_encased/saw, + /datum/surgery_step/open_encased/retract, + /datum/surgery_step/proxy/manipulate_organs, + /datum/surgery_step/internal/manipulate_organs/finish, + /datum/surgery_step/glue_bone/plasma, + /datum/surgery_step/proxy/open_organ/plasma, + /datum/surgery_step/generic/cauterize + ) possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_HEAD, ) - requires_organic_bodypart = 1 + requires_organic_bodypart = TRUE + target_speciestypes = list(/datum/species/plasmaman) + restricted_speciestypes = null /datum/surgery/organ_manipulation/plasmaman/soft possible_locs = list( @@ -48,27 +92,53 @@ BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, ) - steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/internal/manipulate_organs,/datum/surgery_step/generic/cauterize) - requires_organic_bodypart = 1 + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/manipulate_organs, + /datum/surgery_step/generic/cauterize + ) /datum/surgery/organ_manipulation/insect name = "Insectoid Organ Manipulation" - steps = list(/datum/surgery_step/open_encased/saw, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/retract_skin, - /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/internal/manipulate_organs, /datum/surgery_step/glue_bone, /datum/surgery_step/set_bone,/datum/surgery_step/finish_bone,/datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/open_encased/saw, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/proxy/open_organ, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/proxy/manipulate_organs, + /datum/surgery_step/internal/manipulate_organs/finish, + /datum/surgery_step/glue_bone, + /datum/surgery_step/set_bone, + /datum/surgery_step/finish_bone, + /datum/surgery_step/proxy/open_organ, + /datum/surgery_step/generic/cauterize + ) possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN, ) - requires_organic_bodypart = 1 + requires_organic_bodypart = TRUE + target_speciestypes = list(/datum/species/kidan, /datum/species/wryn) + restricted_speciestypes = null /datum/surgery/organ_manipulation/insect/soft possible_locs = list( BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, ) - steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/internal/manipulate_organs,/datum/surgery_step/generic/cauterize) - requires_organic_bodypart = 1 + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/manipulate_organs, + /datum/surgery_step/generic/cauterize + ) + requires_organic_bodypart = TRUE /datum/surgery/organ_manipulation/alien name = "Alien Organ Manipulation" @@ -79,510 +149,617 @@ BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, ) - allowed_mob = 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/internal/manipulate_organs) + requires_bodypart = FALSE // xenos just don't have "bodyparts" + target_mobtypes = list(/mob/living/carbon/alien/humanoid) + restricted_speciestypes = null + + 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(istype(target,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(isplasmaman(H) || iskidan(H) || iswryn(H)) - return FALSE - if(!affected) - // I'd like to see you do surgery on LITERALLY NOTHING - return FALSE - if(affected.is_robotic()) - return FALSE + . = ..() + if(!.) + return FALSE + if(ishuman(target)) //aliens pass it + var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) if(!affected.encased) //no bone, problem. return FALSE - return TRUE /datum/surgery/organ_manipulation_boneless/can_start(mob/user, mob/living/carbon/target) - if(istype(target,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) + . = ..() + if(!.) + return FALSE + var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) + if(affected && affected.encased) //no bones no problem. + return FALSE - if(affected && affected.is_robotic()) - return FALSE//no operating on robotic limbs in an organic surgery - if(!affected) - // I'd like to see you do surgery on LITERALLY NOTHING - return FALSE - if(affected && affected.encased) //no bones no problem. - return FALSE - return TRUE +// Intermediate steps for branching organ manipulation. +/datum/surgery/intermediate/manipulate + requires_bodypart = TRUE -/datum/surgery/organ_manipulation/alien/can_start(mob/user, mob/living/carbon/target) - if(istype(target,/mob/living/carbon/alien/humanoid)) - return TRUE - else return FALSE + 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) -/datum/surgery/organ_manipulation/plasmaman/can_start(mob/user, mob/living/carbon/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(!affected) - return FALSE - if(affected.is_robotic()) - return FALSE - if(!affected.encased) - return FALSE - if(isplasmaman(H)) - return TRUE - return FALSE +// All these surgeries are necessary for slotting into proxy steps -/datum/surgery/organ_manipulation/insect/can_start(mob/user, mob/living/carbon/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(!affected) - return FALSE - if(affected.is_robotic()) - return FALSE - if(!affected.encased) - return FALSE - if(iswryn(H) || iskidan(H)) - return TRUE - return FALSE +/datum/surgery/intermediate/manipulate/extract + steps = list(/datum/surgery_step/internal/manipulate_organs/extract) + +/datum/surgery/intermediate/manipulate/implant + steps = list(/datum/surgery_step/internal/manipulate_organs/implant) + +/datum/surgery/intermediate/manipulate/mend + steps = list(/datum/surgery_step/internal/manipulate_organs/mend) + +/datum/surgery/intermediate/manipulate/clean + steps = list(/datum/surgery_step/internal/manipulate_organs/clean) + +/// The surgery step to trigger this whole situation +/datum/surgery_step/proxy/manipulate_organs + name = "Manipulate Organs (proxy)" + branches = list( + /datum/surgery/intermediate/manipulate/extract, + /datum/surgery/intermediate/manipulate/implant, + /datum/surgery/intermediate/manipulate/mend, + /datum/surgery/intermediate/manipulate/clean, + /datum/surgery/intermediate/bleeding + ) + +/datum/surgery_step/proxy/manipulate_organs/soft + name = "Manipulate Organs Soft (proxy)" + branches = list( + /datum/surgery/intermediate/manipulate/extract, + /datum/surgery/intermediate/manipulate/implant, + /datum/surgery/intermediate/manipulate/mend, + /datum/surgery/intermediate/manipulate/clean, + /datum/surgery/intermediate/bleeding + ) + +// 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 + +/datum/surgery/intermediate/manipulate/clean/xeno + requires_bodypart = FALSE + +/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 + ) // Internal surgeries. /datum/surgery_step/internal - priority = 2 - can_infect = 1 - blood_level = 1 + can_infect = TRUE + blood_level = SURGERY_BLOODSPREAD_HANDS +/** + * Get an internal list of organs for a zone (or an external organ). + * + * Helper function since we end up calling this a ton to work with carbons + */ +/datum/surgery_step/internal/proc/get_organ_list(target_zone, mob/living/carbon/target, obj/item/organ/external/affected) + var/list/organs -/datum/surgery_step/internal/manipulate_organs - name = "manipulate organs" - allowed_tools = list(/obj/item/organ/internal = 100, /obj/item/reagent_containers/food/snacks/organ = 0) - var/implements_extract = list(/obj/item/hemostat = 100, /obj/item/kitchen/utensil/fork = 70) - var/implements_mend = list(/obj/item/stack/medical/bruise_pack = 20,/obj/item/stack/medical/bruise_pack/advanced = 100,/obj/item/stack/nanopaste = 100) - var/implements_clean = list(/obj/item/reagent_containers/dropper = 100, - /obj/item/reagent_containers/syringe = 100, - /obj/item/reagent_containers/glass/bottle = 90, - /obj/item/reagent_containers/food/drinks/drinkingglass = 85, - /obj/item/reagent_containers/food/drinks/bottle = 80, - /obj/item/reagent_containers/glass/beaker = 75, - /obj/item/reagent_containers/spray = 60, - /obj/item/reagent_containers/glass/bucket = 50) - - //Finish is just so you can close up after you do other things. - var/implements_finsh = list(/obj/item/scalpel/laser/manager = 100,/obj/item/retractor = 100 ,/obj/item/crowbar = 90) - var/current_type - var/obj/item/organ/internal/I = null - var/obj/item/organ/external/affected = null - time = 64 - -/datum/surgery_step/internal/manipulate_organs/New() - ..() - allowed_tools = allowed_tools + implements_extract + implements_mend + implements_clean + implements_finsh - - -/datum/surgery_step/internal/manipulate_organs/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) - I = null - var/mob/living/carbon/human/H - if(ishuman(target)) - H = target - affected = H.get_organ(target_zone) - - if(is_int_organ(tool)) - current_type = "insert" - I = tool - - if(target_zone != I.parent_organ_zone || target.get_organ_slot(I.slot)) - to_chat(user, "There is no room for [I] in [target]'s [parse_zone(target_zone)]!") - return -1 - - if((RUNIC_MIND in target.dna.species.species_traits) && istype(I, /obj/item/organ/internal/brain) && !istype(I, /obj/item/organ/internal/brain/golem)) - to_chat(user, "There is no room for [I] in [target]'s [parse_zone(target_zone)]!") - return -1 - - if(I.damage > (I.max_damage * 0.75)) - to_chat(user, "[I] is in no state to be transplanted.") - return -1 - - if(target.get_int_organ(I) && !affected) - to_chat(user, "[target] already has [I].") - return -1 + if(istype(affected)) + organs = affected.internal_organs + else + organs = target.get_organs_zone(target_zone) - if(affected) - user.visible_message("[user] starts transplanting [tool] into [target]'s [affected.name].", \ - "You start transplanting [tool] into [target]'s [affected.name].") - H.custom_pain("Someone's rooting around in your [affected.name]!") - else - user.visible_message("[user] starts transplanting [tool] into [target]'s [parse_zone(target_zone)].", \ - "You start transplanting [tool] into [target]'s [parse_zone(target_zone)].") + return organs - else if(implement_type in implements_clean) - current_type = "clean" +/datum/surgery_step/internal/manipulate_organs + time = 6.4 SECONDS - if(!istype(tool, /obj/item/reagent_containers)) - return +/datum/surgery_step/internal/manipulate_organs/mend + name = "mend organs" + allowed_tools = list( + /obj/item/stack/medical/bruise_pack/advanced = 100, + /obj/item/stack/medical/bruise_pack = 20, + /obj/item/stack/nanopaste = 100 + ) - var/obj/item/reagent_containers/C = tool +/datum/surgery_step/internal/manipulate_organs/mend/proc/get_tool_name(obj/item/tool) + var/tool_name = "[tool]" + if(istype(tool, /obj/item/stack/medical/bruise_pack)) + tool_name = "the bandaid" + if(istype(tool, /obj/item/stack/medical/bruise_pack/advanced)) + tool_name = "regenerative membrane" + else if(istype(tool, /obj/item/stack/nanopaste)) + tool_name = "[tool.name]" //what else do you call nanopaste medically? + + return tool_name + +/datum/surgery_step/internal/manipulate_organs/mend/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/tool_name = get_tool_name(tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + + if(!hasorgans(target)) + to_chat(user, "They do not have organs to mend!") + // note that we want to return skip here so we can go "back" to the proxy step + return SURGERY_BEGINSTEP_SKIP + + var/any_organs_damaged = FALSE + + for(var/obj/item/organ/internal/organ as anything in get_organ_list(target_zone, target, affected)) + if(!organ.damage) + continue + any_organs_damaged = TRUE + var/can_treat_robotic = organ.is_robotic() && istype(tool, /obj/item/stack/nanopaste) + var/can_treat_organic = !organ.is_robotic() && !istype(tool, /obj/item/stack/nanopaste) + if(can_treat_robotic || can_treat_organic) + if(organ.is_dead()) + to_chat(user, span_warning("You can't treat [organ]! Dead organs can't be treated with [tool_name]!")) + continue + user.visible_message( + "[user] starts treating damage to [target]'s [organ.name] with [tool_name].", + "You start treating damage to [target]'s [organ.name] with [tool_name]." + ) + if(can_treat_organic && !organ.sterile) + spread_germs_to_organ(organ, user, tool) + else + to_chat(user, "[organ] can't be treated with [tool_name].") - if(C.reagents.has_reagent("mitocholide", 5)) - var/list/dead_organs = list() + if(!any_organs_damaged) + return SURGERY_BEGINSTEP_SKIP - for(var/obj/item/organ/internal/organ as anything in affected.internal_organs) - if(organ.is_dead()) - dead_organs[organ] = organ.name - if(dead_organs.len >= 1) - if(dead_organs.len == 1) - I = dead_organs[1] - else - I = input("Choose organ to rejuvenate:", "Rejuvenation", null, null) as null|anything in dead_organs - if(istype(C,/obj/item/reagent_containers/syringe)) - user.visible_message("[user] begins injecting [tool] into [target]'s [I.name].", \ - "You begin injecting [tool] into [target]'s [I.name].") - else - user.visible_message("[user] starts pouring some of [tool] over [target]'s [I.name].", \ - "You start pouring some of [tool] over [target]'s [I.name].") - else - user.visible_message("[user] notices that no dead organs in [target]'s [affected.name].", \ - "You notice that no dead organs in [target]'s [affected.name].") - return FALSE - if(C.reagents.has_reagent("mitocholide")) - user.visible_message("[user] notices there is not enough mitocholide in [tool].", \ - "You notice there is not enough mitocholide in [tool].") - return FALSE - if(C.reagents.total_volume <= 0) //end_step handles if there is not enough reagent - user.visible_message("[user] notices [tool] is empty.", "You notice [tool] is empty.") - return FALSE + if(affected) + var/mob/living/carbon/patient = target + patient.custom_pain("The pain in your [affected.name] is living hell!") - for(var/obj/item/organ/internal/organ as anything in affected.internal_organs) - var/msg = "[user] starts pouring some of [tool] over [target]'s [organ.name]." - var/self_msg = "You start pouring some of [tool] over [target]'s [organ.name]." - if(istype(C,/obj/item/reagent_containers/syringe)) - msg = "[user] begins injecting [tool] into [target]'s [organ.name]." - self_msg = "You begin injecting [tool] into [target]'s [organ.name]." - user.visible_message(msg, self_msg) - if(H && affected) - H.custom_pain("Something burns horribly in your [affected.name]!") - - else if(implement_type in implements_finsh) - //same as surgery step /datum/surgery_step/open_encased/close/ - current_type = "finish" - - if(affected && affected.encased) - var/msg = "[user] starts bending [target]'s [affected.encased] back into place with [tool]." - var/self_msg = "You start bending [target]'s [affected.encased] back into place with [tool]." - user.visible_message(msg, self_msg) - else - var/msg = "[user] starts pulling [target]'s skin back into place with [tool]." - var/self_msg = "You start pulling [target]'s skin back into place with [tool]." - user.visible_message(msg, self_msg) + return ..() - if(H && affected) - H.custom_pain("Something hurts horribly in your [affected.name]!") - - else if(implement_type in implements_extract) - current_type = "extract" - var/list/organs = target.get_organs_zone(target_zone) - var/mob/living/simple_animal/borer/B = target.has_brain_worms() - if(target_zone == BODY_ZONE_HEAD && B) - user.visible_message("[user] begins to extract [B] from [target]'s [parse_zone(target_zone)].", - "You begin to extract [B] from [target]'s [parse_zone(target_zone)]...") - return TRUE - if(!organs.len) - to_chat(user, "There are no removeable organs in [target]'s [parse_zone(target_zone)]!") - return -1 - - for(var/obj/item/organ/internal/O as anything in organs) - if(O.unremovable) +/datum/surgery_step/internal/manipulate_organs/mend/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/tool_name = get_tool_name(tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(!hasorgans(target)) + return SURGERY_STEP_INCOMPLETE + + for(var/obj/item/organ/internal/organ as anything in get_organ_list(target_zone, target, affected)) + var/treated_robotic = organ.is_robotic() && istype(tool, /obj/item/stack/nanopaste) + var/treated_organic = !organ.is_robotic() && !istype(tool, /obj/item/stack/nanopaste) + if(treated_robotic || treated_organic) + if(organ.is_dead()) continue - O.on_find(user) - organs -= O - organs[O.name] = O - - I = tgui_input_list(user, "Remove which organ?", "Surgery", organs) - if(I && user && target && user.Adjacent(target) && user.get_active_hand() == tool) - I = organs[I] - if(!I) - return -1 - user.visible_message("[user] starts to separate [target]'s [I] with [tool].", \ - "You start to separate [target]'s [I] with [tool] for removal.") - if(H && affected) - H.custom_pain("The pain in your [affected.name] is living hell!") - else - return -1 - - else if(implement_type in implements_mend) - current_type = "mend" - var/tool_name = "[tool]" - if(istype(tool, /obj/item/stack/medical/bruise_pack)) - tool_name = "the bandaid" - if(istype(tool, /obj/item/stack/medical/bruise_pack/advanced)) - tool_name = "regenerative membrane" - else if(istype(tool, /obj/item/stack/nanopaste)) - tool_name = "[tool.name]" //what else do you call nanopaste medically? - - if(!hasorgans(target)) - to_chat(user, "They do not have organs to mend!") - return - - for(var/obj/item/organ/internal/organ as anything in affected.internal_organs) if(organ.damage) - var/can_treat_robotic = organ.is_robotic() && istype(tool, /obj/item/stack/nanopaste) - var/can_treat_organic = !organ.is_robotic() && !istype(tool, /obj/item/stack/nanopaste) - if(can_treat_robotic || can_treat_organic) - if(organ.is_dead()) - to_chat(user, "You can't treat [organ]! Dead organs can't be treated with [tool_name]!") - continue - user.visible_message("[user] starts treating damage to [target]'s [organ.name] with [tool_name].", \ - "You start treating damage to [target]'s [organ.name] with [tool_name].") - if(can_treat_organic && !organ.sterile) - spread_germs_to_organ(organ, user, tool) - else - to_chat(user, "[organ] can't be treated with [tool_name].") + user.visible_message( + span_notice("[user] treats damage to [target]'s [organ.name] with [tool_name]."), + span_notice("You treat damage to [target]'s [organ.name] with [tool_name].") + ) + organ.damage = 0 + organ.surgeryize() + + return SURGERY_STEP_CONTINUE + +/datum/surgery_step/internal/manipulate_organs/mend/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(!hasorgans(target)) + return SURGERY_STEP_INCOMPLETE + var/obj/item/organ/external/affected = target.get_organ(target_zone) + + user.visible_message( + span_warning("[user]'s hand slips, getting mess and tearing the inside of [target]'s [parse_zone(target_zone)] with [tool]!"), + span_warning("Your hand slips, getting mess and tearing the inside of [target]'s [parse_zone(target_zone)] with [tool]!") + ) - else - to_chat(user, "[organ] does not appear to be damaged.") + var/dam_amt = 2 - if(affected) + if(istype(tool, /obj/item/stack/medical/bruise_pack/advanced)) + target.adjustToxLoss(5) + + else if(istype(tool, /obj/item/stack/medical/bruise_pack) || istype(tool, /obj/item/stack/nanopaste)) + dam_amt = 5 + target.adjustToxLoss(10) + affected?.receive_damage(5) + + for(var/obj/item/organ/internal/organ as anything in get_organ_list(target_zone, target, affected)) + if(organ.damage && !(organ.tough)) + organ.receive_damage(dam_amt,0) + + return SURGERY_STEP_RETRY + +/datum/surgery_step/internal/manipulate_organs/extract + name = "extract organ" + allowed_tools = list( + TOOL_HEMOSTAT = 100, + /obj/item/kitchen/utensil/fork = 70 + ) + + var/obj/item/organ/internal/extracting = null + +/datum/surgery_step/internal/manipulate_organs/extract/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/list/organs = target.get_organs_zone(target_zone) + if(!length(organs)) + to_chat(user, span_notice("There are no removeable organs in [target]'s [parse_zone(target_zone)]!")) + return SURGERY_BEGINSTEP_SKIP + + for(var/obj/item/organ/internal/organ as anything in organs) + if(organ.unremovable) + continue + organ.on_find(user) + organs -= organ + organs[organ.name] = organ + + var/obj/item/organ/internal/I = tgui_input_list(user, "Remove which organ?", "Surgery", organs) + if(I && user && target && user.Adjacent(target) && user.get_active_hand() == tool) + extracting = organs[I] + if(!extracting) + return SURGERY_BEGINSTEP_SKIP + user.visible_message( + "[user] starts to separate [target]'s [I] with [tool].", + "You start to separate [target]'s [I] with [tool] for removal." + ) + var/mob/living/carbon/human/H = target + var/obj/item/organ/affected = H.get_organ(user.zone_selected) + if(H && affected) H.custom_pain("The pain in your [affected.name] is living hell!") + else + return SURGERY_BEGINSTEP_SKIP + + return ..() + +/datum/surgery_step/internal/manipulate_organs/extract/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(!extracting || extracting.owner != target) + user.visible_message( + span_notice("[user] can't seem to extract anything from [target]'s [parse_zone(target_zone)]!"), + span_notice("You can't extract anything from [target]'s [parse_zone(target_zone)]!") + ) + return SURGERY_STEP_CONTINUE + + + var/mob/living/simple_animal/borer/B = target.has_brain_worms() + if(target_zone == BODY_ZONE_HEAD && B && B.host == target) + user.visible_message( + "[user] successfully extracts [B] from [target]'s [parse_zone(target_zone)]!", + span_notice("You successfully extract [B] from [target]'s [parse_zone(target_zone)].") + ) + add_attack_logs(user, target, "Surgically removed [B]. INTENT: [uppertext(user.a_intent)]") + B.leave_host() + return SURGERY_STEP_CONTINUE + + user.visible_message( + span_notice("[user] has separated and extracts [target]'s [extracting] with [tool]."), + span_notice("You have separated and extracted [target]'s [extracting] with [tool].") + ) - else if(istype(tool, /obj/item/reagent_containers/food/snacks/organ)) - to_chat(user, "[tool] was bitten by someone! It's too damaged to use!") - return -1 - - ..() - -/datum/surgery_step/internal/manipulate_organs/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(current_type == "mend") - var/tool_name = "[tool]" - if(istype(tool, /obj/item/stack/medical/bruise_pack)) - tool_name = "the bandaid" - if(istype(tool, /obj/item/stack/medical/bruise_pack/advanced)) - tool_name = "regenerative membrane" - if(istype(tool, /obj/item/stack/nanopaste)) - tool_name = "[tool.name]" //what else do you call nanopaste medically? - - if(!hasorgans(target)) - return - - for(var/obj/item/organ/internal/organ as anything in affected.internal_organs) - var/treated_robotic = organ.is_robotic() && istype(tool, /obj/item/stack/nanopaste) - var/treated_organic = !organ.is_robotic() && !istype(tool, /obj/item/stack/nanopaste) - if(treated_robotic || treated_organic) - if(organ.is_dead()) - continue - if(organ.damage) - user.visible_message("[user] treats damage to [target]'s [organ.name] with [tool_name].", \ - "You treat damage to [target]'s [organ.name] with [tool_name].") - organ.damage = 0 - organ.surgeryize() - - else if(current_type == "insert") - I = tool - if(!user.drop_item_ground(I)) - to_chat(user, "[I] is stuck to your hand, you can't put it in [target]!") - return FALSE - I.insert(target) - if(istype(I, /obj/item/organ/internal/cyberimp)) - add_attack_logs(user, target, "Surgically inserted [I]([I.type])", ATKLOG_ALMOSTALL) - spread_germs_to_organ(I, user, tool) + add_attack_logs(user, target, "Surgically removed [extracting.name]. INTENT: [uppertext(user.a_intent)]") + spread_germs_to_organ(extracting, user, tool) + var/obj/item/thing = extracting.remove(target) + if(!QDELETED(thing)) // some "organs", like egg infections, can have I.remove(target) return null, and so we can't use "thing" in that case + if(istype(thing)) + thing.forceMove(get_turf(target)) + user.put_in_hands(thing, ignore_anim = FALSE) + else + thing.forceMove(get_turf(target)) + target.update_icons() + + return SURGERY_STEP_CONTINUE + +/datum/surgery_step/internal/manipulate_organs/extract/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(extracting && extracting.owner == target) + var/mob/living/carbon/human/H = target + var/obj/item/organ/affected = H.get_organ(user.zone_selected) if(affected) - user.visible_message("[user] has transplanted [tool] into [target]'s [affected.name].", - "You have transplanted [tool] into [target]'s [affected.name].") - else - user.visible_message("[user] has transplanted [tool] into [target]'s [parse_zone(target_zone)].", - "You have transplanted [tool] into [target]'s [parse_zone(target_zone)].") - - else if(current_type == "extract") - var/mob/living/simple_animal/borer/B = target.has_brain_worms() - if(target_zone == BODY_ZONE_HEAD && B && B.host == target) - user.visible_message("[user] successfully extracts [B] from [target]'s [parse_zone(target_zone)]!", - "You successfully extract [B] from [target]'s [parse_zone(target_zone)].") - add_attack_logs(user, target, "Surgically removed [B]. INTENT: [uppertext(user.a_intent)]") - B.leave_host() - return FALSE - if(I && I.owner == target) - user.visible_message("[user] has separated and extracts [target]'s [I] with [tool].", - "You have separated and extracted [target]'s [I] with [tool].") - - add_attack_logs(user, target, "Surgically removed [I.name]. INTENT: [uppertext(user.a_intent)]") - spread_germs_to_organ(I, user, tool) - var/obj/item/thing = I.remove(target) - if(!QDELETED(thing)) // some "organs", like egg infections, can have I.remove(target) return null, and so we can't use "thing" in that case - if(istype(thing)) - thing.forceMove(get_turf(target)) - user.put_in_hands(thing, ignore_anim = FALSE) - else - thing.forceMove(get_turf(target)) - target.update_icons() + user.visible_message( + span_warning("[user]'s hand slips, damaging [target]'s [affected.name] with [tool]!"), + span_warning("Your hand slips, damaging [target]'s [affected.name] with [tool]!") + ) + affected.receive_damage(20) else - user.visible_message("[user] can't seem to extract anything from [target]'s [parse_zone(target_zone)]!", - "You can't extract anything from [target]'s [parse_zone(target_zone)]!") - - else if(current_type == "clean") - if(!hasorgans(target)) - return - if(!istype(tool,/obj/item/reagent_containers)) - return - - var/obj/item/reagent_containers/C = tool - var/datum/reagents/R = C.reagents - - if(R.has_reagent("mitocholide", 5)) - if(I == null) - user.visible_message("[user] didn't find dead organs in [target]'s [affected.name]", \ - "You didn't find dead organs in [target]'s [affected.name]") - return FALSE - I.rejuvenate() - R.remove_reagent("mitocholide", 5) - if(istype(C,/obj/item/reagent_containers/syringe)) - user.visible_message("[user] has injected [tool] into [target]'s [I.name].", - "You have injected [tool] into [target]'s [I.name].") - else - user.visible_message("[user] has poured some of [tool] over [target]'s [I.name].", - "You have poured some of [tool] over [target]'s [I.name].") - return FALSE - else if(C.reagents.has_reagent("mitocholide")) - user.visible_message("[user] notices there is not enough mitocholide in [tool].", \ - "You notice there is not enough mitocholide in [tool].") - return FALSE + user.visible_message( + span_warning("[user]'s hand slips, damaging [target]'s [parse_zone(target_zone)] with [tool]!"), + span_warning("Your hand slips, damaging [target]'s [parse_zone(target_zone)] with [tool]!") + ) + return SURGERY_STEP_RETRY + else + user.visible_message( + "[user] can't seem to extract anything from [target]'s [parse_zone(target_zone)]!", + span_notice("You can't extract anything from [target]'s [parse_zone(target_zone)]!") + ) + return SURGERY_STEP_CONTINUE + +/datum/surgery_step/internal/manipulate_organs/implant + name = "implant an organ" + allowed_tools = list( + /obj/item/organ/internal = 100, + /obj/item/reagent_containers/food/snacks/organ = 0 // there for the flavor text + ) - var/ethanol = 0 //how much alcohol is in the thing - var/spaceacillin = 0 //how much actual antibiotic is in the thing +/datum/surgery_step/internal/manipulate_organs/implant/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(istype(tool, /obj/item/reagent_containers/food/snacks/organ)) + to_chat(user, span_warning("[tool] was bitten by someone! It's too damaged to use!")) + return SURGERY_BEGINSTEP_SKIP - if(R.reagent_list.len) - for(var/datum/reagent/consumable/ethanol/alcohol in R.reagent_list) - ethanol += alcohol.alcohol_perc * 300 - ethanol /= R.reagent_list.len + var/obj/item/organ/external/affected = target.get_organ(target_zone) - spaceacillin = R.get_reagent_amount("spaceacillin") + var/obj/item/organ/internal/organ = tool + if(!istype(organ)) + // dunno how you got here but okay + return SURGERY_BEGINSTEP_SKIP + if(target_zone != organ.parent_organ_zone || target.get_organ_slot(organ.slot)) + to_chat(user, span_notice("There is no room for [organ] in [target]'s [parse_zone(target_zone)]!")) + return SURGERY_BEGINSTEP_SKIP - for(var/obj/item/organ/internal/organ as anything in affected.internal_organs) - if(R.total_volume < GHETTO_DISINFECT_AMOUNT) - user.visible_message("[user] notices there is not enough in [tool].", \ - "You notice there is not enough in [tool].") - return FALSE - if(organ.germ_level < INFECTION_LEVEL_ONE / 2) - to_chat(user, "[organ.name] does not appear to be infected.") - if(organ.germ_level >= INFECTION_LEVEL_ONE / 2) - if(spaceacillin >= GHETTO_DISINFECT_AMOUNT) - organ.germ_level = 0 - else - organ.germ_level = max(organ.germ_level-ethanol, 0) - if(istype(C,/obj/item/reagent_containers/syringe)) - user.visible_message("[user] has injected [tool] into [target]'s [organ.name].", - "You have injected [tool] into [target]'s [organ.name].") - else - user.visible_message("[user] has poured some of [tool] over [target]'s [organ.name].", - "You have poured some of [tool] over [target]'s [organ.name].") - R.trans_to(target, GHETTO_DISINFECT_AMOUNT) - R.reaction(target, REAGENT_INGEST) - - else if(current_type == "finish") - if(affected && affected.encased) - var/msg = "[user] bends [target]'s [affected.encased] back into place with [tool]." - var/self_msg = "You bend [target]'s [affected.encased] back into place with [tool]." - user.visible_message(msg, self_msg) - affected.open = 2.5 - else - var/msg = "[user] pulls [target]'s flesh back into place with [tool]." - var/self_msg = "You pull [target]'s flesh back into place with [tool]." - user.visible_message(msg, self_msg) + if((RUNIC_MIND in target.dna.species.species_traits) && istype(organ, /obj/item/organ/internal/brain) && !istype(organ, /obj/item/organ/internal/brain/golem)) + to_chat(user, span_notice("There is no room for [organ] in [target]'s [parse_zone(target_zone)]!")) + return SURGERY_BEGINSTEP_SKIP - return TRUE + if(organ.damage > (organ.max_damage * 0.75)) + to_chat(user, span_notice("[organ] is in no state to be transplanted.")) + return SURGERY_BEGINSTEP_SKIP - return FALSE + if(target.get_int_organ(organ) && !affected) + to_chat(user, span_warning("[target] already has [organ].")) + return SURGERY_BEGINSTEP_SKIP -/datum/surgery_step/internal/manipulate_organs/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(current_type == "mend") - if(!hasorgans(target)) - return + if(affected) + user.visible_message( + "[user] starts transplanting [tool] into [target]'s [affected.name].", + "You start transplanting [tool] into [target]'s [affected.name]." + ) + var/mob/living/carbon/human/H = target + H.custom_pain("Someone's rooting around in your [affected.name]!") + else + user.visible_message( + "[user] starts transplanting [tool] into [target]'s [parse_zone(target_zone)].", + "You start transplanting [tool] into [target]'s [parse_zone(target_zone)]." + ) + 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) + var/obj/item/organ/internal/I = tool + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(!istype(tool)) + return SURGERY_STEP_INCOMPLETE + if(!user.drop_item_ground(I)) + to_chat(user, span_warning("[I] is stuck to your hand, you can't put it in [target]!")) + return SURGERY_STEP_INCOMPLETE + I.insert(target) + if(istype(I, /obj/item/organ/internal/cyberimp)) + add_attack_logs(user, target, "Surgically inserted [I]([I.type])", ATKLOG_ALMOSTALL) + spread_germs_to_organ(I, user, tool) + + if(affected) + user.visible_message(span_notice("[user] has transplanted [tool] into [target]'s [affected.name]."), + span_notice(" You have transplanted [tool] into [target]'s [affected.name].")) + else + user.visible_message(span_notice("[user] has transplanted [tool] into [target]'s [parse_zone(target_zone)]."), + span_notice(" You have transplanted [tool] into [target]'s [parse_zone(target_zone)].")) + + return SURGERY_STEP_CONTINUE + +/datum/surgery_step/internal/manipulate_organs/implant/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message( + span_warning("[user]'s hand slips, damaging [tool]!"), + span_warning("Your hand slips, damaging [tool]!") + ) + var/obj/item/organ/internal/I = tool + if(istype(I) && !I.tough) + I.receive_damage(rand(3,5),0) - user.visible_message("[user]'s hand slips, getting mess and tearing the inside of [target]'s [affected.name] with [tool]!", \ - "Your hand slips, getting mess and tearing the inside of [target]'s [affected.name] with [tool]!") + return SURGERY_STEP_RETRY - var/dam_amt = 2 - if(istype(tool, /obj/item/stack/medical/bruise_pack/advanced)) - target.adjustToxLoss(5) +/datum/surgery_step/internal/manipulate_organs/clean + name = "clean and/or revive organs" + allowed_tools = list( + /obj/item/reagent_containers/dropper = 100, + /obj/item/reagent_containers/syringe = 100, + /obj/item/reagent_containers/glass/bottle = 90, + /obj/item/reagent_containers/food/drinks/drinkingglass = 85, + /obj/item/reagent_containers/food/drinks/bottle = 80, + /obj/item/reagent_containers/glass/beaker = 75, + /obj/item/reagent_containers/spray = 60, + /obj/item/reagent_containers/glass/bucket = 50 + ) - else if(istype(tool, /obj/item/stack/medical/bruise_pack) || istype(tool, /obj/item/stack/nanopaste)) - dam_amt = 5 - target.adjustToxLoss(10) - affected.receive_damage(5) +/datum/surgery_step/internal/manipulate_organs/clean/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) - for(var/obj/item/organ/internal/organ as anything in affected.internal_organs) - if(organ.damage && !organ.tough) - organ.receive_damage(dam_amt, 0) + var/obj/item/reagent_containers/container = tool - return FALSE + var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) - else if(current_type == "insert") - user.visible_message("[user]'s hand slips, damaging [tool]!", \ - "Your hand slips, damaging [tool]!") - var/obj/item/organ/internal/I = tool - if(istype(I) && !I.tough) - I.receive_damage(rand(3,5),0) + for(var/obj/item/organ/internal/organ as anything in get_organ_list(target_zone, target, affected)) + if(container.reagents.total_volume <= 0) //end_step handles if there is not enough reagent + user.visible_message( + "[user] notices [tool] is empty.", + "You notice [tool] is empty." + ) + return SURGERY_BEGINSTEP_SKIP - return FALSE + var/msg = "[user] starts pouring some of [tool] over [target]'s [organ.name]." + var/self_msg = "You start pouring some of [tool] over [target]'s [organ.name]." + if(istype(container, /obj/item/reagent_containers/syringe)) + msg = "[user] begins injecting [tool] into [target]'s [organ.name]." + self_msg = "You begin injecting [tool] into [target]'s [organ.name]." + user.visible_message(msg, self_msg) + target.custom_pain("Something burns horribly in your [affected.name]!") - else if(current_type == "clean") - if(!hasorgans(target)) - return - if(!istype(tool,/obj/item/reagent_containers)) - return + return ..() - var/obj/item/reagent_containers/C = tool - var/datum/reagents/R = C.reagents - var/ethanol = 0 //how much alcohol is in the thing +/datum/surgery_step/internal/manipulate_organs/clean/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(!hasorgans(target)) + return SURGERY_STEP_INCOMPLETE + if(!istype(tool, /obj/item/reagent_containers)) + return SURGERY_STEP_INCOMPLETE - if(R.reagent_list.len) - for(var/datum/reagent/consumable/ethanol/alcohol in R.reagent_list) - ethanol += alcohol.alcohol_perc * 300 - ethanol /= C.reagents.reagent_list.len + var/obj/item/organ/external/affected = target.get_organ(target_zone) - for(var/obj/item/organ/internal/organ as anything in affected.internal_organs) - organ.germ_level = max(organ.germ_level - ethanol, 0) - organ.receive_damage(rand(4,8), 0) + var/obj/item/reagent_containers/C = tool + var/datum/reagents/R = C.reagents - R.trans_to(target, GHETTO_DISINFECT_AMOUNT * 10) - R.reaction(target, REAGENT_INGEST) + var/ethanol = 0 //how much alcohol is in the thing + var/spaceacillin = 0 //how much actual antibiotic is in the thing + var/mito_tot = 0 // same for mito, thanks farie - user.visible_message("[user]'s hand slips, splashing the contents of [tool] all over [target]'s [affected.name] incision!", \ - "Your hand slips, splashing the contents of [tool] all over [target]'s [affected.name] incision!") - return FALSE - else if(current_type == "extract") - if(I && I.owner == target) - if(affected) - user.visible_message("[user]'s hand slips, damaging [target]'s [affected.name] with [tool]!", \ - "Your hand slips, damaging [target]'s [affected.name] with [tool]!") - affected.receive_damage(20) - else - user.visible_message("[user]'s hand slips, damaging [target]'s [parse_zone(target_zone)] with [tool]!", \ - "Your hand slips, damaging [target]'s [parse_zone(target_zone)] with [tool]!") - else - user.visible_message("[user] can't seem to extract anything from [target]'s [parse_zone(target_zone)]!", - "You can't extract anything from [target]'s [parse_zone(target_zone)]!") - return FALSE + if(length(R.reagent_list)) + for(var/datum/reagent/consumable/ethanol/alcohol in R.reagent_list) + ethanol += alcohol.alcohol_perc * 300 + ethanol /= length(R.reagent_list) + + mito_tot = R.get_reagent_amount("mitocholide") + spaceacillin = R.get_reagent_amount("spaceacillin") - else if(current_type == "finish") - if(affected && affected.encased) - var/msg = "[user]'s hand slips, bending [target]'s [affected.encased] the wrong way!" - var/self_msg = "Your hand slips, bending [target]'s [affected.encased] the wrong way!" - user.visible_message(msg, self_msg) - affected.fracture() + + for(var/obj/item/organ/internal/organ as anything in get_organ_list(target_zone, target, affected)) + if(organ.germ_level < INFECTION_LEVEL_ONE / 2 && !(organ.is_dead())) // not dead, don't need to inject mito either + to_chat(user, "[organ] does not appear to need chemical treatment.") + continue + if(!spaceacillin && !ethanol && !mito_tot) + to_chat(user, span_warning("[C] doesn't have anything in it that would be worth applying!")) + break + var/success = FALSE + if(organ.germ_level >= INFECTION_LEVEL_ONE / 2) + // spacecillin completely cures infections if there is enough, ethanol just reduces the infection strength by the amount used. + if(spaceacillin || ethanol) + if(spaceacillin >= GHETTO_DISINFECT_AMOUNT) + organ.germ_level = 0 + else + organ.germ_level = max(organ.germ_level-ethanol, 0) + success = TRUE // we actually injected some chemicals + + else if(!(organ.is_dead())) // Not dead and got nothing to disinfect the organ with. Don't waste the other chems + to_chat(user, span_warning("[organ] does appear mildly infected but [C] does not seem to contain disinfectants. You decide to not inject the chemicals into [organ].")) + continue + + var/mito_trans + if(mito_tot && (organ.is_dead()) && !organ.is_robotic()) + mito_trans = min(mito_tot, C.amount_per_transfer_from_this / length(R.reagent_list)) // How much mito is actually transfered + success = TRUE + if(!success) + to_chat(user, span_warning("[C] does not seem to have the chemicals needed to clean [organ]. You decide against wasting chemicals.")) + continue + + // now try actually injecting. + + if(istype(C, /obj/item/reagent_containers/syringe)) + user.visible_message( + span_notice("[user] has injected [tool] into [target]'s [organ.name]."), + span_notice("You have injected [tool] into [target]'s [organ.name].") + ) else - var/msg = "[user]'s hand slips, tearing the skin!" - var/self_msg = "Your hand slips, tearing skin!" - user.visible_message(msg, self_msg) - if(affected) - affected.receive_damage(20) - return FALSE + user.visible_message( + span_notice("[user] has poured some of [tool] over [target]'s [organ.name]."), + span_notice("You have poured some of [tool] over [target]'s [organ.name].") + ) + + R.reaction(target, REAGENT_INGEST, R.total_volume / C.amount_per_transfer_from_this) + R.trans_to(target, C.amount_per_transfer_from_this) + + if(mito_trans) + mito_tot -= mito_trans + if(organ.is_robotic()) // Get out cyborg people + continue + if(mito_trans >= MITO_REVIVAL_COST) + organ.rejuvenate() // Just like splashing it onto it + user.visible_message(span_warning("\The [organ] seems to regain its lively luster!")) + else + to_chat(user, span_warning("[organ] does not seem to respond to the amount of mitocholide inside the injection. Try injecting more next time.")) + + return SURGERY_STEP_CONTINUE + + +/datum/surgery_step/internal/manipulate_organs/clean/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(!istype(tool, /obj/item/reagent_containers)) + return SURGERY_STEP_INCOMPLETE + var/obj/item/organ/external/affected = target.get_organ(target_zone) + var/obj/item/reagent_containers/C = tool + var/datum/reagents/R = C.reagents + var/ethanol = 0 //how much alcohol is in the thing + + if(length(R.reagent_list)) + for(var/datum/reagent/consumable/ethanol/alcohol in R.reagent_list) + ethanol += alcohol.alcohol_perc * 300 + ethanol /= length(C.reagents.reagent_list) + for(var/obj/item/organ/internal/organ as anything in target.get_organs_zone(target_zone)) + organ.germ_level = max(organ.germ_level-ethanol, 0) + organ.receive_damage(rand(4, 8), 0) + + R.trans_to(target, GHETTO_DISINFECT_AMOUNT * 10) + R.reaction(target, REAGENT_INGEST) + + user.visible_message( + span_warning("[user]'s hand slips, splashing the contents of [tool] all over [target][affected ? "'s [affected.name]" : ""] incision!"), + span_warning("Your hand slips, splashing the contents of [tool] all over [target][affected ? "'s [affected.name]" : ""] incision!") + ) + // continue here since we want to keep moving in the surgery + return SURGERY_STEP_CONTINUE + +// FINISH +/datum/surgery_step/internal/manipulate_organs/finish + name = "finish manipulation" + allowed_tools = list( + /obj/item/scalpel/laser/manager = 100, + TOOL_RETRACTOR = 100, + TOOL_CROWBAR = 90 + ) - return FALSE +/datum/surgery_step/internal/manipulate_organs/finish/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/mob/living/carbon/human/H = target + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(affected && affected.encased) + var/msg = "[user] starts bending [target]'s [affected.encased] back into place with [tool]." + var/self_msg = "You start bending [target]'s [affected.encased] back into place with [tool]." + user.visible_message(msg, self_msg) + else + var/msg = "[user] starts pulling [target]'s skin back into place with [tool]." + var/self_msg = "You start pulling [target]'s skin back into place with [tool]." + user.visible_message(msg, self_msg) + + if(H && affected) + H.custom_pain("Something hurts horribly in your [affected.name]!") + + return ..() + +/datum/surgery_step/internal/manipulate_organs/finish/end_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) + var/msg + var/self_msg + if(affected && affected.encased) + msg = span_notice("[user] bends [target]'s [affected.encased] back into place with [tool].") + self_msg = span_notice("You bend [target]'s [affected.encased] back into place with [tool].") + affected.open = ORGAN_ORGANIC_ENCASED_OPEN + else + msg = span_notice("[user] pulls [target]'s flesh back into place with [tool].") + self_msg = span_notice("You pull [target]'s flesh back into place with [tool].") + user.visible_message(msg, self_msg) + return SURGERY_STEP_CONTINUE + +/datum/surgery_step/internal/manipulate_organs/finish/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) + var/msg + var/self_msg + if(affected && affected.encased) + msg = span_warning("[user]'s hand slips, bending [target]'s [affected.encased] the wrong way!") + self_msg = span_warning("Your hand slips, bending [target]'s [affected.encased] the wrong way!") + affected.fracture() + else + msg = span_warning("[user]'s hand slips, tearing the skin!") + self_msg = span_warning("Your hand slips, tearing skin!") + if(affected) + affected.receive_damage(20) + user.visible_message(msg, self_msg) + return SURGERY_STEP_RETRY ////////////////////////////////////////////////////////////////// // SPESHUL AYLIUM STUPS // @@ -591,73 +768,90 @@ /datum/surgery_step/saw_carapace name = "saw carapace" allowed_tools = list( - /obj/item/circular_saw = 100, \ - /obj/item/melee/energy/sword/cyborg/saw = 100, \ - /obj/item/hatchet = 90, \ - /obj/item/wirecutters = 70 + TOOL_SAW = 100, + /obj/item/melee/energy/sword/cyborg/saw = 100, + /obj/item/hatchet = 90, + /obj/item/wirecutters = 70 ) - time = 54 + time = 5.4 SECONDS + + +/datum/surgery_step/saw_carapace/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message( + "[user] begins to cut through [target]'s [target_zone] with [tool].", + "You begin to cut through [target]'s [target_zone] with [tool]." + ) + return ..() -/datum/surgery_step/saw_carapace/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery) - user.visible_message("[user] begins to cut through [target]'s [target_zone] with [tool].", \ - "You begin to cut through [target]'s [target_zone] with [tool].") - ..() +/datum/surgery_step/saw_carapace/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) -/datum/surgery_step/saw_carapace/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery) - user.visible_message("[user] has cut [target]'s [target_zone] open with [tool].", \ - "You have cut [target]'s [target_zone] open with [tool].") - return TRUE + user.visible_message( + span_notice("[user] has cut [target]'s [target_zone] open with [tool]."), + span_notice("You have cut [target]'s [target_zone] open with [tool].") + ) + return SURGERY_STEP_CONTINUE -/datum/surgery_step/saw_carapace/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery) - user.visible_message("[user]'s hand slips, cracking [target]'s [target_zone] with [tool]!", \ - "Your hand slips, cracking [target]'s [target_zone] with [tool]!") - return FALSE +/datum/surgery_step/saw_carapace/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + + user.visible_message( + span_warning("[user]'s hand slips, cracking [target]'s [target_zone] with [tool]!"), + span_warning("Your hand slips, cracking [target]'s [target_zone] with [tool]!") + ) + return SURGERY_STEP_RETRY /datum/surgery_step/cut_carapace name = "cut carapace" allowed_tools = list( - /obj/item/scalpel = 100, \ - /obj/item/kitchen/knife = 90, \ - /obj/item/shard = 60, \ - /obj/item/scissors = 12, \ - /obj/item/twohanded/chainsaw = 1, \ - /obj/item/claymore = 6, \ - /obj/item/melee/energy/ = 6, \ - /obj/item/pen/edagger = 6, \ + TOOL_SCALPEL = 100, + /obj/item/kitchen/knife = 90, + /obj/item/shard = 60, + /obj/item/scissors = 12, + /obj/item/twohanded/chainsaw = 1, + /obj/item/claymore = 6, + /obj/item/melee/energy = 6, + /obj/item/pen/edagger = 6 ) - time = 16 + time = 1.6 SECONDS + +/datum/surgery_step/cut_carapace/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) -/datum/surgery_step/cut_carapace/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery) - user.visible_message("[user] starts the incision on [target]'s [target_zone] with [tool].", \ + user.visible_message( + "[user] starts the incision on [target]'s [target_zone] with [tool].", "You start the incision on [target]'s [target_zone] with [tool].") - ..() + return ..() -/datum/surgery_step/cut_carapace/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery) - user.visible_message("[user] has made an incision on [target]'s [target_zone] with [tool].", \ - "You have made an incision on [target]'s [target_zone] with [tool].",) - return TRUE +/datum/surgery_step/cut_carapace/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) -/datum/surgery_step/cut_carapace/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery) - user.visible_message("[user]'s hand slips, slicing open [target]'s [target_zone] in a wrong spot with [tool]!", \ - "Your hand slips, slicing open [target]'s [target_zone] in a wrong spot with [tool]!") - return FALSE + user.visible_message( + span_notice("[user] has made an incision on [target]'s [target_zone] with [tool]."), + span_notice("You have made an incision on [target]'s [target_zone] with [tool].") + ) + return SURGERY_STEP_CONTINUE + +/datum/surgery_step/cut_carapace/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + + user.visible_message( + span_warning("[user]'s hand slips, slicing open [target]'s [target_zone] in a wrong spot with [tool]!"), + span_warning("Your hand slips, slicing open [target]'s [target_zone] in a wrong spot with [tool]!") + ) + return SURGERY_STEP_RETRY /datum/surgery_step/retract_carapace name = "retract carapace" allowed_tools = list( - /obj/item/scalpel/laser/manager = 100, \ - /obj/item/retractor = 100, \ - /obj/item/crowbar = 90, \ - /obj/item/kitchen/utensil/fork = 60 + /obj/item/scalpel/laser/manager = 100, + TOOL_RETRACTOR = 100, + /obj/item/crowbar = 90, + /obj/item/kitchen/utensil/fork = 60 ) - time = 24 + time = 2.4 SECONDS -/datum/surgery_step/retract_carapace/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/retract_carapace/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) var/msg = "[user] starts to pry open the incision on [target]'s [target_zone] with [tool]." var/self_msg = "You start to pry open the incision on [target]'s [target_zone] with [tool]." if(target_zone == BODY_ZONE_CHEST) @@ -667,28 +861,80 @@ msg = "[user] starts to pry open the incision and rearrange the organs in [target]'s lower abdomen with [tool]." self_msg = "You start to pry open the incision and rearrange the organs in [target]'s lower abdomen with [tool]." user.visible_message(msg, self_msg) - ..() + return ..() /datum/surgery_step/retract_carapace/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery) - var/msg = "[user] keeps the incision open on [target]'s [target_zone] with [tool]." - var/self_msg = "You keep the incision open on [target]'s [target_zone] with [tool]." + var/msg = span_notice("[user] keeps the incision open on [target]'s [target_zone] with [tool].") + var/self_msg = span_notice("You keep the incision open on [target]'s [target_zone] with [tool].") if(target_zone == BODY_ZONE_CHEST) - msg = "[user] keeps the ribcage open on [target]'s torso with [tool]." - self_msg = "You keep the ribcage open on [target]'s torso with [tool]." + msg = span_notice("[user] keeps the ribcage open on [target]'s torso with [tool].") + self_msg = span_notice("You keep the ribcage open on [target]'s torso with [tool].") if(target_zone == BODY_ZONE_PRECISE_GROIN) - msg = "[user] keeps the incision open on [target]'s lower abdomen with [tool]." - self_msg = "You keep the incision open on [target]'s lower abdomen with [tool]." + msg = span_notice("[user] keeps the incision open on [target]'s lower abdomen with [tool].") + self_msg = span_notice("You keep the incision open on [target]'s lower abdomen with [tool].") user.visible_message(msg, self_msg) - return TRUE + return SURGERY_STEP_CONTINUE /datum/surgery_step/generic/retract_carapace/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery) - var/msg = "[user]'s hand slips, tearing the edges of incision on [target]'s [target_zone] with [tool]!" - var/self_msg = "Your hand slips, tearing the edges of incision on [target]'s [target_zone] with [tool]!" + var/msg = span_warning("[user]'s hand slips, tearing the edges of incision on [target]'s [target_zone] with [tool]!") + var/self_msg = span_warning("Your hand slips, tearing the edges of incision on [target]'s [target_zone] with [tool]!") if(target_zone == BODY_ZONE_CHEST) - msg = "[user]'s hand slips, damaging several organs [target]'s torso with [tool]!" - self_msg = "Your hand slips, damaging several organs [target]'s torso with [tool]!" + msg = span_warning("[user]'s hand slips, damaging several organs [target]'s torso with [tool]!") + self_msg = span_warning("Your hand slips, damaging several organs [target]'s torso with [tool]!") if(target_zone == BODY_ZONE_PRECISE_GROIN) - msg = "[user]'s hand slips, damaging several organs [target]'s lower abdomen with [tool]" - self_msg = "Your hand slips, damaging several organs [target]'s lower abdomen with [tool]!" + msg = span_warning("[user]'s hand slips, damaging several organs [target]'s lower abdomen with [tool]") + self_msg = span_warning("Your hand slips, damaging several organs [target]'s lower abdomen with [tool]!") user.visible_message(msg, self_msg) - return FALSE + return SURGERY_STEP_RETRY + +// redefine cauterize for every step because of course it relies on get_organ() +/datum/surgery_step/generic/seal_carapace/ + name = "seal carapace" + + allowed_tools = list( + /obj/item/scalpel/laser = 100, + TOOL_CAUTERY = 100, + /obj/item/clothing/mask/cigarette = 90, + /obj/item/lighter = 60, + TOOL_WELDER = 30 + ) + + time = 2.4 SECONDS + +/datum/surgery_step/generic/seal_carapace/proc/zone_name(target_zone) + var/zone = target_zone + + if(target_zone == BODY_ZONE_CHEST) + zone = "torso" + else if(target_zone == BODY_ZONE_PRECISE_GROIN) + zone = "lower abdomen" + + return zone + +/datum/surgery_step/generic/seal_carapace/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/zone = zone_name(target_zone) + user.visible_message( + "[user] is beginning to cauterize the incision on [target]'s [zone] with \the [tool].", + "You are beginning to cauterize the incision on [target]'s [zone] with \the [tool]." + ) + target.custom_pain("Your [zone] is being burned!") + return ..() + +/datum/surgery_step/generic/seal_carapace/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/zone = zone_name(target_zone) + user.visible_message( + span_notice("[user] cauterizes the incision on [target]'s [zone] with \the [tool]."), + span_notice("You cauterize the incision on [target]'s [zone] with \the [tool].") + ) + return SURGERY_STEP_CONTINUE + +/datum/surgery_step/generic/seal_carapace/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/zone = zone_name(target_zone) + user.visible_message( + span_warning("[user]'s hand slips, leaving a small burn on [target]'s [zone] with \the [tool]!"), + span_warning("Your hand slips, leaving a small burn on [target]'s [zone] with \the [tool]!") + ) + target.apply_damage(3, BURN, target_zone) + return SURGERY_STEP_RETRY + +#undef MITO_REVIVAL_COST diff --git a/code/modules/surgery/other.dm b/code/modules/surgery/other.dm index d09b0305832..7e2d7742faf 100644 --- a/code/modules/surgery/other.dm +++ b/code/modules/surgery/other.dm @@ -24,7 +24,52 @@ /datum/surgery/bleeding name = "Internal Bleeding" - steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders,/datum/surgery_step/generic/retract_skin,/datum/surgery_step/fix_vein,/datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/open_organ, + /datum/surgery_step/generic/cauterize + ) + possible_locs = list( + BODY_ZONE_L_ARM, + BODY_ZONE_PRECISE_L_HAND, + BODY_ZONE_R_ARM, + BODY_ZONE_PRECISE_R_HAND, + BODY_ZONE_R_LEG, + BODY_ZONE_PRECISE_R_FOOT, + BODY_ZONE_L_LEG, + BODY_ZONE_PRECISE_L_FOOT, + BODY_ZONE_TAIL, + BODY_ZONE_WING, + ) + restricted_speciestypes = list(/datum/species/plasmaman) + +/datum/surgery/bleeding/special + name = "Internal Bleeding" + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/open_organ, + /datum/surgery_step/generic/cauterize + ) + possible_locs = list( + BODY_ZONE_CHEST, + BODY_ZONE_HEAD, + BODY_ZONE_PRECISE_GROIN, + ) + restricted_speciestypes = list(/datum/species/wryn, /datum/species/kidan, /datum/species/plasmaman) + +/datum/surgery/bleeding/plasmaman + name = "Plasmaman Internal Bleeding" + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/open_organ/plasma, + /datum/surgery_step/generic/cauterize + ) possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_HEAD, @@ -40,10 +85,43 @@ BODY_ZONE_TAIL, BODY_ZONE_WING, ) + target_speciestypes = list(/datum/species/plasmaman) + restricted_speciestypes = null + +/datum/surgery/bleeding/insect + name = "Insectoid Internal Bleeding" + steps = list( + /datum/surgery_step/open_encased/saw, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/ib, + /datum/surgery_step/glue_bone, + /datum/surgery_step/set_bone, + /datum/surgery_step/finish_bone, + /datum/surgery_step/generic/cauterize + ) + possible_locs = list( + BODY_ZONE_CHEST, + BODY_ZONE_HEAD, + BODY_ZONE_PRECISE_GROIN, + ) + target_speciestypes = list(/datum/species/wryn, /datum/species/kidan) + restricted_speciestypes = null /datum/surgery/debridement name = "Debridement" - steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders,/datum/surgery_step/generic/retract_skin,/datum/surgery_step/fix_dead_tissue,/datum/surgery_step/treat_necrosis,/datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/open_organ, + /datum/surgery_step/fix_dead_tissue, + /datum/surgery_step/treat_necrosis, + /datum/surgery_step/generic/cauterize + ) possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_HEAD, @@ -59,141 +137,119 @@ BODY_ZONE_TAIL, BODY_ZONE_WING, ) - -/datum/surgery/infection/can_start(mob/user, mob/living/carbon/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(!affected) - return 0 - if(affected.is_robotic()) - return 0 - return 1 - return 0 + requires_organic_bodypart = TRUE /datum/surgery/bleeding/can_start(mob/user, mob/living/carbon/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(!affected) - return 0 + . = ..() + if(!.) + return FALSE + var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) + if(affected.has_internal_bleeding()) + return TRUE + return FALSE - if(affected.has_internal_bleeding()) - return 1 - return 0 /datum/surgery/debridement/can_start(mob/user, mob/living/carbon/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - - if(!hasorgans(target)) - return 0 - - if(!affected) - return 0 - - if(!affected.is_dead()) - return 0 - - return 1 + . = ..() + if(!.) + return FALSE + var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) + if(!affected.is_dead()) + return FALSE + return TRUE - return 0 /datum/surgery_step/fix_vein name = "mend internal bleeding" allowed_tools = list( - /obj/item/FixOVein = 100, \ - /obj/item/stack/cable_coil = 90, \ - /obj/item/stack/sheet/sinew = 90 + TOOL_FIXOVEIN = 100, + /obj/item/stack/cable_coil = 90, + /obj/item/stack/sheet/sinew = 90 ) - can_infect = 1 - blood_level = 1 + can_infect = TRUE + blood_level = SURGERY_BLOODSPREAD_HANDS - time = 32 + time = 3.2 SECONDS -/datum/surgery_step/fix_vein/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/fix_vein/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) - if(!affected) - return 0 + if(!affected.has_internal_bleeding()) + to_chat(user, span_notice("The veins in [affected] seem to be in perfect shape!")) + return SURGERY_BEGINSTEP_SKIP - return affected.has_internal_bleeding() + user.visible_message( + "[user] starts patching the damaged vein in [target]'s [affected.name] with \the [tool].", + "You start patching the damaged vein in [target]'s [affected.name] with \the [tool]." + ) + target.custom_pain("The pain in your [affected.name] is unbearable!") + return ..() -/datum/surgery_step/fix_vein/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 patching the damaged vein in [target]'s [affected.name] with \the [tool]." , \ - "You start patching the damaged vein in [target]'s [affected.name] with \the [tool].") - target.custom_pain("The pain in [affected.name] is unbearable!") - ..() -/datum/surgery_step/fix_vein/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/fix_vein/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 patched the damaged vein in [target]'s [affected.name] with \the [tool].", \ - " You have patched the damaged vein in [target]'s [affected.name] with \the [tool].") + user.visible_message( + span_notice("[user] has patched the damaged vein in [target]'s [affected.name] with \the [tool]."), + span_notice("You have patched the damaged vein in [target]'s [affected.name] with \the [tool].") + ) affected.stop_internal_bleeding() if(ishuman(user) && prob(40)) var/mob/living/carbon/human/U = user U.bloody_hands(target, 0) - return 1 + return SURGERY_STEP_CONTINUE -/datum/surgery_step/fix_vein/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/fix_vein/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, smearing [tool] in the incision in [target]'s [affected.name]!" , \ - " Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!") + user.visible_message( + span_warning("[user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!"), + span_warning("Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!") + ) affected.receive_damage(5, 0) - return 0 + return SURGERY_STEP_RETRY /datum/surgery_step/fix_dead_tissue //Debridement name = "remove dead tissue" allowed_tools = list( - /obj/item/scalpel = 100, \ - /obj/item/kitchen/knife = 90, \ - /obj/item/shard = 60, \ + TOOL_SCALPEL = 100, + /obj/item/kitchen/knife = 90, + /obj/item/shard = 60 ) - can_infect = 1 - blood_level = 1 - - time = 16 - -/datum/surgery_step/fix_dead_tissue/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(!hasorgans(target)) - return 0 - - var/obj/item/organ/external/affected = target.get_organ(target_zone) - - if(!affected) - return 0 + can_infect = TRUE + blood_level = SURGERY_BLOODSPREAD_HANDS - if(!affected.is_dead()) - return 0 - return 1 + time = 1.6 SECONDS -/datum/surgery_step/fix_dead_tissue/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/fix_dead_tissue/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 cutting away necrotic tissue in [target]'s [affected.name] with \the [tool]." , \ - "You start cutting away necrotic tissue in [target]'s [affected.name] with \the [tool].") + user.visible_message( + "[user] starts cutting away necrotic tissue in [target]'s [affected.name] with \the [tool].", + "You start cutting away necrotic tissue in [target]'s [affected.name] with \the [tool]." + ) target.custom_pain("The pain in [affected.name] is unbearable!") - ..() + return ..() -/datum/surgery_step/fix_dead_tissue/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/fix_dead_tissue/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 cut away necrotic tissue in [target]'s [affected.name] with \the [tool].", \ - " You have cut away necrotic tissue in [target]'s [affected.name] with \the [tool].") - affected.open = 3 + user.visible_message( + span_notice("[user] has cut away necrotic tissue in [target]'s [affected.name] with \the [tool]."), + span_notice("You have cut away necrotic tissue in [target]'s [affected.name] with \the [tool].") + ) + affected.open = ORGAN_ORGANIC_OPEN - return 1 + return SURGERY_STEP_CONTINUE -/datum/surgery_step/fix_dead_tissue/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/fix_dead_tissue/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 an artery inside [target]'s [affected.name] with \the [tool]!", \ - " Your hand slips, slicing an artery inside [target]'s [affected.name] with \the [tool]!") + user.visible_message( + span_warning("[user]'s hand slips, slicing an artery inside [target]'s [affected.name] with \the [tool]!"), + span_warning("Your hand slips, slicing an artery inside [target]'s [affected.name] with \the [tool]!") + ) affected.receive_damage(20) - return 0 + return SURGERY_STEP_RETRY /datum/surgery_step/treat_necrosis name = "treat necrosis" @@ -207,47 +263,46 @@ /obj/item/reagent_containers/glass/bucket = 50 ) - can_infect = 0 - blood_level = 0 + can_infect = FALSE + blood_level = SURGERY_BLOODSPREAD_NONE - time = 24 + time = 2.4 SECONDS -/datum/surgery_step/treat_necrosis/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(!istype(tool, /obj/item/reagent_containers)) - return 0 +/datum/surgery_step/treat_necrosis/tool_check(mob/user, obj/item/tool) + . = ..() + if(!.) + return FALSE var/obj/item/reagent_containers/container = tool if(!container.reagents.has_reagent("mitocholide")) - user.visible_message("[user] looks at \the [tool] and ponders." , \ - "You are not sure if \the [tool] contains mitocholide to treat the necrosis.") - return 0 + user.visible_message( + "[user] looks at \the [tool] and ponders.", + "You are not sure if \the [tool] contains the mitocholide necessary to treat the necrosis.") + return FALSE - if(!hasorgans(target)) - return 0 +/datum/surgery_step/treat_necrosis/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) - if(!affected.is_dead()) - return 0 - return 1 -/datum/surgery_step/treat_necrosis/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 applying medication to the affected tissue in [target]'s [affected.name] with \the [tool]." , \ - "You start applying medication to the affected tissue in [target]'s [affected.name] with \the [tool].") + if(!(affected.is_dead())) + to_chat(user, span_warning("The [affected] seems to already be in fine condition!")) + return SURGERY_BEGINSTEP_SKIP + + user.visible_message( + "[user] starts applying medication to the affected tissue in [target]'s [affected.name] with \the [tool].", + "You start applying medication to the affected tissue in [target]'s [affected.name] with \the [tool]." + ) target.custom_pain("Something in your [affected.name] is causing you a lot of pain!") - ..() + return ..() -/datum/surgery_step/treat_necrosis/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/treat_necrosis/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) - if(!istype(tool, /obj/item/reagent_containers)) - return 0 - var/obj/item/reagent_containers/container = tool - var/mitocholide = 0 + var/mitocholide = FALSE if(container.reagents.has_reagent("mitocholide")) - mitocholide = 1 + mitocholide = TRUE var/trans = container.reagents.trans_to(target, container.amount_per_transfer_from_this) if(trans > 0) @@ -256,44 +311,60 @@ if(mitocholide) affected.unnecrotize() - user.visible_message(" [user] applies [trans] units of the solution to affected tissue in [target]'s [affected.name]", \ - " You apply [trans] units of the solution to affected tissue in [target]'s [affected.name] with \the [tool].") + user.visible_message( + span_notice("[user] applies [trans] units of the solution to affected tissue in [target]'s [affected.name]"), + span_notice("You apply [trans] units of the solution to affected tissue in [target]'s [affected.name] with \the [tool].") + ) - return 1 + return SURGERY_STEP_CONTINUE -/datum/surgery_step/treat_necrosis/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/treat_necrosis/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) if(!istype(tool, /obj/item/reagent_containers)) - return + return SURGERY_STEP_INCOMPLETE var/obj/item/reagent_containers/container = tool var/trans = container.reagents.trans_to(target, container.amount_per_transfer_from_this) container.reagents.reaction(target, REAGENT_INGEST) //technically it's contact, but the reagents are being applied to internal tissue - user.visible_message(" [user]'s hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!" , \ - " Your hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!") + user.visible_message( + span_warning("[user]'s hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!"), + span_warning("Your hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!") + ) //no damage or anything, just wastes medicine - + return SURGERY_STEP_RETRY ////////////////////////////////////////////////////////////////// // Dethrall Shadowling // ////////////////////////////////////////////////////////////////// /datum/surgery/remove_thrall name = "Remove Shadow Tumor" - steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/internal/dethrall, /datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/internal/dethrall, + /datum/surgery_step/generic/cauterize + ) possible_locs = list( BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_PRECISE_GROIN, ) + requires_organic_bodypart = TRUE /datum/surgery/remove_thrall/synth name = "Debug Shadow Tumor" - steps = list(/datum/surgery_step/robotics/external/unscrew_hatch,/datum/surgery_step/robotics/external/open_hatch,/datum/surgery_step/internal/dethrall,/datum/surgery_step/robotics/external/close_hatch) - requires_organic_bodypart = 0 + steps = list( + /datum/surgery_step/robotics/external/unscrew_hatch, + /datum/surgery_step/robotics/external/open_hatch, + /datum/surgery_step/internal/dethrall, + /datum/surgery_step/robotics/external/close_hatch + ) + requires_organic_bodypart = FALSE possible_locs = list( BODY_ZONE_HEAD, BODY_ZONE_CHEST, @@ -301,62 +372,41 @@ ) /datum/surgery/remove_thrall/can_start(mob/user, mob/living/carbon/human/target) - if(!istype(target)) - return 0 - if(!is_thrall(target)) - return 0 - var/obj/item/organ/internal/brain/B = target.get_int_organ(/obj/item/organ/internal/brain) - var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) - if(!B) - // No brain to remove the tumor from - return 0 - if(affected.is_robotic()) - return 0 - if(!LAZYIN(affected.internal_organs, B)) - return 0 - return 1 - -/datum/surgery/remove_thrall/synth/can_start(mob/user, mob/living/carbon/human/target) - if(!istype(target)) - return 0 + . = ..() + if(!.) + return FALSE if(!is_thrall(target)) - return 0 + return FALSE var/obj/item/organ/internal/brain/B = target.get_int_organ(/obj/item/organ/internal/brain) var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) if(!B) // No brain to remove the tumor from - return 0 - if(!affected.is_robotic()) - return 0 + return FALSE if(!LAZYIN(affected.internal_organs, B)) - return 0 - return 1 + return FALSE + return TRUE /datum/surgery_step/internal/dethrall name = "cleanse contamination" allowed_tools = list(/obj/item/flash = 100, /obj/item/flashlight/pen = 80, /obj/item/flashlight = 40) - blood_level = 0 - time = 30 - -/datum/surgery_step/internal/dethrall/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(!..()) - return 0 - if(!is_thrall(target)) - return 0 - return 1 + blood_level = SURGERY_BLOODSPREAD_NONE + time = 3 SECONDS /datum/surgery_step/internal/dethrall/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) var/obj/item/organ/internal/brain = target.get_organ_slot(INTERNAL_ORGAN_BRAIN) - user.visible_message("[user] reaches into [target]'s head with [tool].", "You begin aligning [tool]'s light to the tumor on [target]'s brain...") - to_chat(target, "A small part of your [brain.parent_organ_zone] pulses with agony as the light impacts it.") - ..() + user.visible_message( + "[user] reaches into [target]'s head with [tool].", + span_notice("You begin aligning [tool]'s light to the tumor on [target]'s brain...") + ) + to_chat(target, span_boldannounce("A small part of your [brain.parent_organ_zone] pulses with agony as the light impacts it.")) + return ..() /datum/surgery_step/internal/dethrall/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) if(isshadowlinglesser(target)) //Empowered thralls cannot be deconverted - to_chat(target, "NOT LIKE THIS!") - user.visible_message("[target] suddenly slams upward and knocks down [user]!", \ - "[target] suddenly bolts up and slams you with tremendous force!") + to_chat(target, span_shadowling("NOT LIKE THIS!")) + user.visible_message(span_warning("[target] suddenly slams upward and knocks down [user]!"), \ + span_userdanger("[target] suddenly bolts up and slams you with tremendous force!")) user.StopResting() //Remove all stuns user.SetSleeping(0) user.SetStunned(0) @@ -371,16 +421,17 @@ S.Weaken(16 SECONDS) S.apply_damage(20, BRUTE) playsound(S, 'sound/effects/bang.ogg', 50, 1) - return 0 + return SURGERY_STEP_INCOMPLETE var/obj/item/organ/internal/brain/B = target.get_int_organ(/obj/item/organ/internal/brain) var/obj/item/organ/external/E = target.get_organ(check_zone(B.parent_organ_zone)) - user.visible_message("[user] shines light onto the tumor in [target]'s [E]!", "You cleanse the contamination from [target]'s brain!") + user.visible_message("[user] shines light onto the tumor in [target]'s [E]!", span_notice("You cleanse the contamination from [target]'s brain!")) if(target.vision_type) //Turns off their darksight if it's still active. - to_chat(target, "Your eyes are suddenly wrought with immense pain as your darksight is forcibly dismissed!") + to_chat(target, span_boldannounce("Your eyes are suddenly wrought with immense pain as your darksight is forcibly dismissed!")) target.set_sight(null) SSticker.mode.remove_thrall(target.mind, 0) - target.visible_message("A strange black mass falls from [target]'s [E]!") + target.visible_message(span_warning("A strange black mass falls from [target]'s [E]!")) var/obj/item/organ/thing = new /obj/item/organ/internal/shadowtumor(get_turf(target)) thing.update_DNA(target.dna) user.put_in_hands(thing, ignore_anim = FALSE) - return 1 + return SURGERY_STEP_CONTINUE + diff --git a/code/modules/surgery/plastic_surgery.dm b/code/modules/surgery/plastic_surgery.dm index 3b2a8d271f1..0a46407f99a 100644 --- a/code/modules/surgery/plastic_surgery.dm +++ b/code/modules/surgery/plastic_surgery.dm @@ -1,32 +1,33 @@ /datum/surgery/plastic_surgery name = "Plastic Surgery" - steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/reshape_face, /datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/reshape_face, + /datum/surgery_step/generic/cauterize + ) possible_locs = list(BODY_ZONE_HEAD) - -/datum/surgery/plastic_surgery/can_start(mob/user, mob/living/carbon/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/head/head = H.get_organ(user.zone_selected) - if(!head) - return FALSE - if(head.is_robotic()) - return FALSE - return TRUE + requires_organic_bodypart = TRUE /datum/surgery_step/reshape_face name = "reshape face" - allowed_tools = list(/obj/item/scalpel = 100, /obj/item/kitchen/knife = 50, /obj/item/wirecutters = 35) - time = 64 + allowed_tools = list(TOOL_SCALPEL = 100, /obj/item/kitchen/knife = 50, /obj/item/wirecutters = 35) + time = 6.4 SECONDS /datum/surgery_step/reshape_face/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - user.visible_message("[user] begins to alter [target]'s appearance.", "You begin to alter [target]'s appearance...") + user.visible_message("[user] begins to alter [target]'s appearance.", span_notice("You begin to alter [target]'s appearance...")) + return ..() /datum/surgery_step/reshape_face/end_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) var/obj/item/organ/external/head/head = target.get_organ(target_zone) var/species_names = target.dna.species.name if(head.undisfigure()) - user.visible_message("[user] successfully restores [target]'s appearance!", "You successfully restore [target]'s appearance.") + user.visible_message( + "[user] successfully restores [target]'s appearance!", + span_notice("You successfully restore [target]'s appearance.") + ) else var/list/names = list() var/list_size = 10 @@ -65,14 +66,16 @@ var/oldname = target.real_name target.real_name = chosen_name var/newname = target.real_name //something about how the code handles names required that I use this instead of target.real_name - user.visible_message("[user] alters [oldname]'s appearance completely, [target.p_they()] [target.p_are()] now [newname]!", "You alter [oldname]'s appearance completely, [target.p_they()] [target.p_are()] now [newname].") + user.visible_message("[user] alters [oldname]'s appearance completely, [target.p_they()] [target.p_are()] now [newname]!", span_notice("You alter [oldname]'s appearance completely, [target.p_they()] [target.p_are()] now [newname].")) target.sec_hud_set_ID() - return TRUE + return SURGERY_STEP_CONTINUE /datum/surgery_step/reshape_face/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) var/obj/item/organ/external/head/head = target.get_organ(target_zone) - user.visible_message(" [user]'s hand slips, tearing skin on [target]'s face with [tool]!", \ - " Your hand slips, tearing skin on [target]'s face with [tool]!") + user.visible_message( + span_warning("[user]'s hand slips, tearing skin on [target]'s face with [tool]!"), + span_warning("Your hand slips, tearing skin on [target]'s face with [tool]!") + ) target.apply_damage(10, BRUTE, head, sharp = TRUE) - return FALSE + return SURGERY_STEP_RETRY diff --git a/code/modules/surgery/plastic_surgery_robotics.dm b/code/modules/surgery/plastic_surgery_robotics.dm index 6ff9d57194a..1b781370690 100644 --- a/code/modules/surgery/plastic_surgery_robotics.dm +++ b/code/modules/surgery/plastic_surgery_robotics.dm @@ -1,32 +1,29 @@ /datum/surgery/plastic_surgery_robotics name = "Name Changing Procedure" - steps = list(/datum/surgery_step/robotics/external/unscrew_hatch,/datum/surgery_step/robotics/external/open_hatch,/datum/surgery_step/reshape_face_robotics,/datum/surgery_step/robotics/external/close_hatch) + steps = list( + /datum/surgery_step/robotics/external/unscrew_hatch, + /datum/surgery_step/robotics/external/open_hatch, + /datum/surgery_step/reshape_face_robotics, + /datum/surgery_step/robotics/external/close_hatch + ) possible_locs = list(BODY_ZONE_HEAD) - requires_organic_bodypart = 0 + requires_organic_bodypart = FALSE -/datum/surgery/plastic_surgery_robotics/can_start(mob/user, mob/living/carbon/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/head/affected = H.get_organ(user.zone_selected) - if(!affected) - return FALSE - if(!affected.is_robotic()) - return FALSE - return TRUE /datum/surgery_step/reshape_face_robotics name = "Alter robot's name" - allowed_tools = list(/obj/item/multitool = 100, /obj/item/screwdriver = 55, /obj/item/kitchen/knife = 20, /obj/item/scalpel = 25) - time = 64 + allowed_tools = list(/obj/item/multitool = 100, /obj/item/screwdriver = 55, /obj/item/kitchen/knife = 20, TOOL_SCALPEL = 25) + time = 6.4 SECONDS /datum/surgery_step/reshape_face_robotics/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - user.visible_message("[user] begins to alter [target]'s appearance.", "You begin to alter [target]'s appearance...") + user.visible_message("[user] begins to alter [target]'s appearance.", span_notice("You begin to alter [target]'s appearance...")) + return ..() /datum/surgery_step/reshape_face_robotics/end_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) var/obj/item/organ/external/head/head = target.get_organ(target_zone) var/species_names = target.dna.species.name if(head.undisfigure()) - user.visible_message("[user] successfully restores [target]'s appearance!", "You successfully restore [target]'s appearance.") + user.visible_message("[user] successfully restores [target]'s appearance!", span_notice("You successfully restore [target]'s appearance.")) else var/list/names = list() var/list_size = 10 @@ -65,13 +62,13 @@ var/oldname = target.real_name target.real_name = chosen_name var/newname = target.real_name //something about how the code handles names required that I use this instead of target.real_name - user.visible_message("[user] alters [oldname]'s appearance completely, [target.p_they()] [target.p_are()] now [newname]!", "You alter [oldname]'s appearance completely, [target.p_they()] [target.p_are()] now [newname].") + user.visible_message("[user] alters [oldname]'s appearance completely, [target.p_they()] [target.p_are()] now [newname]!", span_notice("You alter [oldname]'s appearance completely, [target.p_they()] [target.p_are()] now [newname].")) target.sec_hud_set_ID() - return TRUE + return SURGERY_STEP_CONTINUE /datum/surgery_step/reshape_face_robotics/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) var/obj/item/organ/external/head/head = target.get_organ(target_zone) - user.visible_message(" [user]'s hand slips, tearing chassis on [target]'s head with [tool]!", \ - " Your hand slips, tearing chassis on [target]'s head with [tool]!") + user.visible_message(span_warning("[user]'s hand slips, tearing chassis on [target]'s head with [tool]!"), + span_warning("Your hand slips, tearing chassis on [target]'s head with [tool]!")) target.apply_damage(10, BRUTE, head) - return FALSE + return SURGERY_STEP_RETRY diff --git a/code/modules/surgery/remove_embedded_object.dm b/code/modules/surgery/remove_embedded_object.dm index 32040825eb0..524ef602d77 100644 --- a/code/modules/surgery/remove_embedded_object.dm +++ b/code/modules/surgery/remove_embedded_object.dm @@ -1,6 +1,13 @@ /datum/surgery/embedded_removal name = "Removal of Embedded Objects" - steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/remove_object,/datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/proxy/open_organ, + /datum/surgery_step/remove_object, + /datum/surgery_step/generic/cauterize + ) possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_HEAD, @@ -18,55 +25,50 @@ ) /datum/surgery/embedded_removal/synth - steps = list(/datum/surgery_step/robotics/external/unscrew_hatch,/datum/surgery_step/robotics/external/open_hatch,/datum/surgery_step/remove_object,/datum/surgery_step/robotics/external/close_hatch) - requires_organic_bodypart = 0 - -/datum/surgery/embedded_removal/can_start(mob/user, mob/living/carbon/human/target) - if(!istype(target)) - return 0 - var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) - if(!affected) - return 0 - if(affected.is_robotic()) - return 0 - return 1 + steps = list( + /datum/surgery_step/robotics/external/unscrew_hatch, + /datum/surgery_step/robotics/external/open_hatch, + /datum/surgery_step/remove_object, + /datum/surgery_step/robotics/external/close_hatch + ) + requires_organic_bodypart = FALSE -/datum/surgery/embedded_removal/synth/can_start(mob/user, mob/living/carbon/human/target) - if(!istype(target)) - return 0 +/datum/surgery/embedded_removal/can_start(mob/user, mob/living/carbon/target) + . = ..() + if(!.) + return FALSE var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) - if(!affected) - return 0 - if(!affected.is_robotic()) - return 0 - - return 1 + if(!length(affected.embedded_objects)) + return FALSE /datum/surgery_step/remove_object name = "Remove Embedded Objects" - time = 32 - accept_hand = 1 + time = 3.2 SECONDS + accept_hand = TRUE var/obj/item/organ/external/L = null + repeatable = TRUE /datum/surgery_step/remove_object/begin_step(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) - L = surgery.organ_ref + L = surgery.organ_to_manipulate if(L) - user.visible_message("[user] looks for objects embedded in [target]'s [parse_zone(user.zone_selected)].", "You look for objects embedded in [target]'s [parse_zone(user.zone_selected)]...") + user.visible_message("[user] looks for objects embedded in [target]'s [parse_zone(user.zone_selected)].", span_notice("You look for objects embedded in [target]'s [parse_zone(user.zone_selected)]...")) else - user.visible_message("[user] looks for [target]'s [parse_zone(user.zone_selected)].", "You look for [target]'s [parse_zone(user.zone_selected)]...") + user.visible_message("[user] looks for [target]'s [parse_zone(user.zone_selected)].", span_notice("You look for [target]'s [parse_zone(user.zone_selected)]...")) + return ..() /datum/surgery_step/remove_object/end_step(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) if(L) - if(ishuman(target)) - var/objects_removed = L.remove_all_embedded_objects() - if(objects_removed) - user.visible_message("[user] sucessfully removes [objects_removed] embedded objects from [target]'s [L.name]!", "You successfully remove [objects_removed] embedded objects from [target]'s [L.name].") - else - to_chat(user, "You find no objects embedded in [target]'s [L.name]!") + var/objects_removed = L.remove_all_embedded_objects() + if(objects_removed) + user.visible_message("[user] sucessfully removes [objects_removed] embedded objects from [target]'s [L.name]!", span_notice("You successfully remove [objects_removed] embedded objects from [target]'s [L.name].")) + else + to_chat(user, span_warning("You find no objects embedded in [target]'s [L.name]!")) else - to_chat(user, "You can't find [target]'s [parse_zone(user.zone_selected)], let alone any objects embedded in it!") + to_chat(user, span_warning("You can't find [target]'s [parse_zone(user.zone_selected)], let alone any objects embedded in it!")) + + return SURGERY_STEP_CONTINUE - return 1 +// this could use a fail_step... diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index 7df38bc9b5d..258c01c7b47 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -3,10 +3,21 @@ // COMMON STEPS // ////////////////////////////////////////////////////////////////// -/datum/surgery/cybernetic_repair + +/datum/surgery_step/proxy/robotics + +/datum/surgery/robotics + requires_organic_bodypart = FALSE + +/datum/surgery/robotics/cybernetic_repair name = "Cybernetic Repair" - steps = list(/datum/surgery_step/robotics/external/unscrew_hatch,/datum/surgery_step/robotics/external/open_hatch,/datum/surgery_step/robotics/external/repair) - requires_organic_bodypart = 0 + steps = list( + /datum/surgery_step/robotics/external/unscrew_hatch, + /datum/surgery_step/robotics/external/open_hatch, + /datum/surgery_step/proxy/robotics/repair_limb, + /datum/surgery_step/robotics/external/close_hatch + ) + requires_organic_bodypart = FALSE possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_HEAD, @@ -23,10 +34,15 @@ BODY_ZONE_WING, ) -/datum/surgery/cybernetic_repair/internal +/datum/surgery/robotics/cybernetic_repair/internal name = "Internal Component Manipulation" - steps = list(/datum/surgery_step/robotics/external/unscrew_hatch,/datum/surgery_step/robotics/external/open_hatch,/datum/surgery_step/robotics/manipulate_robotic_organs) - requires_organic_bodypart = 0 + steps = list( + /datum/surgery_step/robotics/external/unscrew_hatch, + /datum/surgery_step/robotics/external/open_hatch, + // burn/brute are squished into here as well + /datum/surgery_step/proxy/robotics/manipulate_organs, + /datum/surgery_step/robotics/external/close_hatch + ) possible_locs = list( BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, @@ -39,10 +55,9 @@ BODY_ZONE_R_LEG, ) -/datum/surgery/cybernetic_amputation +/datum/surgery/robotics/cybernetic_amputation name = "Robotic Limb Amputation" steps = list(/datum/surgery_step/robotics/external/amputate) - requires_organic_bodypart = 0 possible_locs = list( BODY_ZONE_CHEST, BODY_ZONE_HEAD, @@ -59,583 +74,632 @@ BODY_ZONE_WING, ) -/datum/surgery/cybernetic_repair/can_start(mob/user, mob/living/carbon/target) - - if(istype(target,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(!affected) - return 0 - if(!affected.is_robotic()) - return 0 - return 1 - /datum/surgery/cybernetic_amputation/can_start(mob/user, mob/living/carbon/target) - if(istype(target,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(!affected) - return 0 - if(!affected.is_robotic()) - return 0 + . = ..() + if(.) + var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) if(affected.cannot_amputate) - return 0 - return 1 + return FALSE + +/datum/surgery/robotics/cybernetic_customization + name = "Cybernetic Appearance Customization" + steps = list( + /datum/surgery_step/robotics/external/unscrew_hatch, + /datum/surgery_step/robotics/external/open_hatch, + /datum/surgery_step/robotics/external/customize_appearance, + /datum/surgery_step/robotics/external/close_hatch + ) + possible_locs = list( + BODY_ZONE_HEAD, + BODY_ZONE_CHEST, + BODY_ZONE_L_ARM, + BODY_ZONE_PRECISE_L_HAND, + BODY_ZONE_R_ARM, + BODY_ZONE_PRECISE_R_HAND, + BODY_ZONE_R_LEG, + BODY_ZONE_PRECISE_R_FOOT, + BODY_ZONE_L_LEG, + BODY_ZONE_PRECISE_L_FOOT, + BODY_ZONE_PRECISE_GROIN, + BODY_ZONE_TAIL, + BODY_ZONE_WING, + ) + +// Intermediate repair surgeries, for fixing up internal maladies mid-surgery. + +/datum/surgery/intermediate/robotics + requires_bodypart = TRUE + requires_organic_bodypart = FALSE + +/datum/surgery/intermediate/robotics/repair + possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_GROIN) + +/datum/surgery/intermediate/robotics/repair/burn + steps = list(/datum/surgery_step/robotics/external/repair/burn) + +/datum/surgery/intermediate/robotics/repair/brute + steps = list(/datum/surgery_step/robotics/external/repair/brute) + +// Manipulate organs sub-surgeries + +/datum/surgery/intermediate/robotics/manipulate_organs + possible_locs = list( + BODY_ZONE_PRECISE_EYES, + BODY_ZONE_PRECISE_MOUTH, + BODY_ZONE_CHEST, + BODY_ZONE_HEAD, + BODY_ZONE_PRECISE_GROIN, + BODY_ZONE_L_ARM, + BODY_ZONE_R_ARM) + +/datum/surgery/intermediate/robotics/manipulate_organs/extract + steps = list(/datum/surgery_step/robotics/manipulate_robotic_organs/extract) + +/datum/surgery/intermediate/robotics/manipulate_organs/implant + steps = list(/datum/surgery_step/robotics/manipulate_robotic_organs/implant) + +/datum/surgery/intermediate/robotics/manipulate_organs/mend + steps = list(/datum/surgery_step/robotics/manipulate_robotic_organs/mend) + +/datum/surgery/intermediate/robotics/manipulate_organs/install_mmi + steps = list(/datum/surgery_step/robotics/manipulate_robotic_organs/install_mmi) + possible_locs = list(BODY_ZONE_CHEST) -//to do, moar surgerys or condense down ala manipulate organs. /datum/surgery_step/robotics can_infect = 0 -/datum/surgery_step/robotics/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(!istype(target)) - return 0 - if(!hasorgans(target)) - return 0 - var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(affected == null) - return 0 - return 1 +/datum/surgery_step/robotics/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + . = ..() + if(tool && tool.tool_behaviour) + tool.play_tool_sound(user, 30) /datum/surgery_step/robotics/external - -/datum/surgery_step/robotics/external/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(!..()) - return 0 - var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(!affected.is_robotic()) - return 0 - return 1 + name = "external robotics surgery" /datum/surgery_step/robotics/external/unscrew_hatch name = "unscrew hatch" allowed_tools = list( - /obj/item/screwdriver = 100, + TOOL_SCREWDRIVER = 100, /obj/item/coin = 50, /obj/item/kitchen/knife = 50 ) - time = 16 - -/datum/surgery_step/robotics/external/unscrew_hatch/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(..()) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(!affected) - return 0 - return 1 + 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) +/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].") - ..() + 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]." + ) + 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) +/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].",) - affected.open = 1 - return 1 + user.visible_message( + span_notice("[user] has opened the maintenance hatch on [target]'s [affected.name] with \the [tool]."), + span_notice("You have opened the maintenance hatch on [target]'s [affected.name] with \the [tool].") + ) + 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) +/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].") - return 0 + user.visible_message( + span_warning("[user]'s [tool.name] slips, failing to unscrew [target]'s [affected.name]."), + span_warning("Your [tool] slips, failing to unscrew [target]'s [affected.name].") + ) + return SURGERY_STEP_RETRY /datum/surgery_step/robotics/external/open_hatch name = "open hatch" allowed_tools = list( - /obj/item/retractor = 100, - /obj/item/crowbar = 100, - /obj/item/kitchen/utensil/ = 50 + TOOL_RETRACTOR = 100, + TOOL_CROWBAR = 100, + /obj/item/kitchen/utensil = 50 ) - time = 24 - -/datum/surgery_step/robotics/external/open_hatch/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(..()) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(!affected) - return 0 - return 1 + 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) +/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].") - ..() + 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]." + ) + 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) +/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]." ) - affected.open = 2 - return 1 + user.visible_message( + span_notice("[user] opens the maintenance hatch on [target]'s [affected.name] with \the [tool]."), + span_notice("You open the maintenance hatch on [target]'s [affected.name] with \the [tool].") + ) + 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) +/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].") - return 0 + user.visible_message( + span_warning("[user]'s [tool.name] slips, failing to open the hatch on [target]'s [affected.name]."), + span_warning("Your [tool] slips, failing to open the hatch on [target]'s [affected.name].") + ) + return SURGERY_STEP_RETRY /datum/surgery_step/robotics/external/close_hatch name = "close hatch" allowed_tools = list( - /obj/item/retractor = 100, - /obj/item/crowbar = 100, + TOOL_RETRACTOR = 100, + TOOL_CROWBAR = 100, /obj/item/kitchen/utensil = 50 ) - time = 24 + time = 2.4 SECONDS -/datum/surgery_step/robotics/external/close_hatch/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(..()) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(!affected) - return 0 - return 1 +/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]." + ) + return ..() -/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) +/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] 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].") - ..() + user.visible_message( + span_notice("[user] closes and secures the hatch on [target]'s [affected.name] with \the [tool]."), + span_notice("You close and secure the hatch on [target]'s [affected.name] with \the [tool].") + ) + tool.play_tool_sound(target) + affected.open = ORGAN_CLOSED + return SURGERY_STEP_CONTINUE -/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) +/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] 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].") - affected.open = 0 - return 1 + user.visible_message(span_warning(" [user]'s [tool.name] slips, failing to close the hatch on [target]'s [affected.name]."), + span_warning(" Your [tool.name] slips, failing to close the hatch on [target]'s [affected.name].")) + return SURGERY_STEP_RETRY + +/datum/surgery_step/robotics/external/close_hatch/premature + name = "close hatch prematurely" -/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) +/datum/surgery_step/robotics/external/close_hatch/premature/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]'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].") - return 0 + user.visible_message( + "[user] begins to close and secure the hatch on [target]'s [affected.name] with \the [tool].", + span_warning("You are interrupting the current surgery, beginning to close and secure the hatch on [target]'s [affected.name] with \the [tool].") + ) + return ..() + /datum/surgery_step/robotics/external/repair - name = "repair damage internally" - allowed_tools = list() + name = "repair damage" + time = 3.2 SECONDS - var/list/implements_finish = list( - /obj/item/retractor = 100, - /obj/item/crowbar = 100, - /obj/item/kitchen/utensil = 50 - ) - var/list/implements_heal_burn = list( +/datum/surgery_step/robotics/external/repair/burn + name = "repair burn damage" + allowed_tools = list( /obj/item/stack/cable_coil = 100 ) - var/list/implements_heal_brute = list( - /obj/item/weldingtool = 100, - /obj/item/gun/energy/plasmacutter = 50 + +/datum/surgery_step/robotics/external/repair/burn/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/obj/item/stack/cable_coil/C = tool + if(!(affected.burn_dam > 0)) + to_chat(user, span_warning("\The [affected] does not have any burn damage!")) + return SURGERY_BEGINSTEP_SKIP + if(!istype(C)) + return SURGERY_BEGINSTEP_SKIP + if(C.get_amount() < 3) + to_chat(user, span_warning("You need three or more cable pieces to repair this damage.")) + return SURGERY_BEGINSTEP_SKIP + C.use(3) + user.visible_message( + "[user] begins to splice new cabling into [target]'s [affected.name].", + "You begin to splice new cabling into [target]'s [affected.name]." ) - var/current_type - time = 32 + return ..() -/datum/surgery_step/robotics/external/repair/New() - ..() - allowed_tools = implements_heal_burn + implements_heal_brute + implements_finish -/datum/surgery_step/robotics/external/repair/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/robotics/external/repair/burn/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) - if(!affected) - return -1 - - if(implement_type in implements_heal_burn) - current_type = "burn" - var/obj/item/stack/cable_coil/C = tool - if(!(affected.burn_dam > 0)) - to_chat(user, "The [affected] does not have any burn damage!") - return -1 - if(!istype(C)) - return -1 - if(!C.get_amount() >= 3) - to_chat(user, "You need three or more cable pieces to repair this damage.") - return -1 - C.use(3) - user.visible_message("[user] begins to splice new cabling into [target]'s [affected.name]." , \ - "You begin to splice new cabling into [target]'s [affected.name].") - - else if(implement_type in implements_heal_brute) - current_type = "brute" - if(!(affected.brute_dam > 0 || affected.is_disfigured())) - to_chat(user, "The [affected] does not require welding repair!") - return -1 - if(tool.tool_behaviour == TOOL_WELDER) - if(!tool.use(1)) - return -1 - user.visible_message("[user] begins to patch damage to [target]'s [affected.name]'s support structure with \the [tool]." , \ - "You begin to patch damage to [target]'s [affected.name]'s support structure with \the [tool].") - - else if(implement_type in implements_finish) - current_type = "finish" - 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].") + user.visible_message( + span_notice(" [user] finishes splicing cable into [target]'s [affected.name]."), + span_notice(" You finishes splicing new cable into [target]'s [affected.name].") + ) + affected.heal_damage(0, rand(30, 50), 1, 1) + if(affected.burn_dam) + return SURGERY_STEP_RETRY_ALWAYS else - log_runtime(EXCEPTION("Invalid tool: '[implement_type]'"), src) - return -1 - ..() + return SURGERY_STEP_CONTINUE + +/datum/surgery_step/robotics/external/repair/burn/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( + span_warning(" [user] causes a short circuit in [target]'s [affected.name]!"), + span_warning(" You cause a short circuit in [target]'s [affected.name]!") + ) + target.apply_damage(rand(5, 10), BURN, affected) + return SURGERY_STEP_RETRY -/datum/surgery_step/robotics/external/repair/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/robotics/external/repair/brute + name = "repair brute damage" + allowed_tools = list( + TOOL_WELDER = 100, + /obj/item/gun/energy/plasmacutter = 50 + ) + +/datum/surgery_step/robotics/external/repair/brute/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) - switch(current_type) - if("brute") - user.visible_message(" [user] finishes patching damage to [target]'s [affected.name] with \the [tool].", \ - " You finish patching damage to [target]'s [affected.name] with \the [tool].") - affected.heal_damage(rand(30,50), 0, TRUE, TRUE) - affected.undisfigure() - if("burn") - user.visible_message(" [user] finishes splicing cable into [target]'s [affected.name].", \ - " You finishes splicing new cable into [target]'s [affected.name].") - affected.heal_damage(0, rand(30,50), TRUE, TRUE) - if("finish") - 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].") - affected.open = 0 - return 1 - return 0 - -/datum/surgery_step/robotics/external/repair/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) + if(!(affected.brute_dam > 0 || affected.is_disfigured())) + to_chat(user, span_warning("\The [affected] does not require welding repair!")) + return SURGERY_BEGINSTEP_SKIP + if(tool.tool_behaviour == TOOL_WELDER) + if(!tool.use(1)) + return SURGERY_BEGINSTEP_SKIP + user.visible_message( + "[user] begins to patch damage to [target]'s [affected.name]'s support structure with \the [tool].", + "You begin to patch damage to [target]'s [affected.name]'s support structure with \the [tool]." + ) + return ..() + + +/datum/surgery_step/robotics/external/repair/brute/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( + span_notice(" [user] finishes patching damage to [target]'s [affected.name] with \the [tool]."), + span_notice(" You finish patching damage to [target]'s [affected.name] with \the [tool].") + ) + affected.heal_damage(rand(30, 50), 0, 1, 1) + affected.undisfigure() + if(affected.brute_dam) + // Keep trying until there's nothing left to patch up. + return SURGERY_STEP_RETRY_ALWAYS + else + return SURGERY_STEP_CONTINUE + +/datum/surgery_step/robotics/external/repair/brute/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) - switch(current_type) - if("brute") - user.visible_message(" [user]'s [tool.name] slips, damaging the internal structure of [target]'s [affected.name].", - " Your [tool.name] slips, damaging the internal structure of [target]'s [affected.name].") - target.apply_damage(rand(5,10), BURN, affected) - if("burn") - user.visible_message(" [user] causes a short circuit in [target]'s [affected.name]!", - " You cause a short circuit in [target]'s [affected.name]!") - target.apply_damage(rand(5,10), BURN, affected) - if("finish") - 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].") - return 0 - -///////condenseing remove/extract/repair here. ///////////// + user.visible_message( + span_warning("[user]'s [tool.name] slips, damaging the internal structure of [target]'s [affected.name]."), + span_warning("Your [tool.name] slips, damaging the internal structure of [target]'s [affected.name].") + ) + target.apply_damage(rand(5, 10), BURN, affected) + return SURGERY_STEP_RETRY + +/datum/surgery_step/proxy/robotics/manipulate_organs + name = "manipulate robotic organs (proxy)" + + branches = list( + /datum/surgery/intermediate/robotics/manipulate_organs/extract, + /datum/surgery/intermediate/robotics/manipulate_organs/implant, + /datum/surgery/intermediate/robotics/manipulate_organs/install_mmi, + /datum/surgery/intermediate/robotics/manipulate_organs/mend, + /datum/surgery/intermediate/robotics/repair/brute, + /datum/surgery/intermediate/robotics/repair/burn + ) + + /datum/surgery_step/robotics/manipulate_robotic_organs + time = 3.2 SECONDS - name = "internal part manipulation" - allowed_tools = list(/obj/item/mmi = 100) - var/implements_extract = list(/obj/item/multitool = 100) - var/implements_mend = list( /obj/item/stack/nanopaste = 100,/obj/item/bonegel = 30, /obj/item/screwdriver = 70) - var/implements_insert = list(/obj/item/organ/internal = 100) - var/implements_finish =list(/obj/item/retractor = 100,/obj/item/crowbar = 100,/obj/item/kitchen/utensil = 50) - var/current_type - var/obj/item/organ/internal/I = null - var/obj/item/organ/external/affected = null - time = 32 -/datum/surgery_step/robotics/manipulate_robotic_organs/New() - ..() - allowed_tools = allowed_tools + implements_extract + implements_mend + implements_insert + implements_finish +/datum/surgery_step/robotics/manipulate_robotic_organs/mend + name = "Mend cybernetic organs" + allowed_tools = list( + /obj/item/stack/nanopaste = 100, + TOOL_BONEGEL = 30, + TOOL_SCREWDRIVER = 70 + ) +/datum/surgery_step/robotics/manipulate_robotic_organs/mend/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(!hasorgans(target)) + return + var/obj/item/organ/external/affected = target.get_organ(target_zone) + var/found_damaged_organ = FALSE + for(var/obj/item/organ/internal/organ as anything in affected.internal_organs) + if(organ.damage && organ.is_robotic()) + user.visible_message( + "[user] starts mending the damage to [target]'s [organ.name]'s mechanisms.", + "You start mending the damage to [target]'s [organ.name]'s mechanisms." + ) + found_damaged_organ = TRUE + if(!found_damaged_organ) + to_chat(user, "There are no damaged components in [affected].") + return SURGERY_BEGINSTEP_SKIP -/datum/surgery_step/robotics/manipulate_robotic_organs/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) + target.custom_pain("The pain in your [affected.name] is living hell!") + return ..() - I = null - affected = target.get_organ(target_zone) - if(implement_type in implements_insert) - current_type = "insert" - var/obj/item/organ/internal/I = tool - if(!I.is_robotic()) - to_chat(user, "You can only implant cybernetic organs.") +/datum/surgery_step/robotics/manipulate_robotic_organs/mend/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(!hasorgans(target)) + return + var/obj/item/organ/external/affected = target.get_organ(target_zone) + for(var/obj/item/organ/internal/organ as anything in affected.internal_organs) + if(organ.damage && organ.is_robotic()) + user.visible_message( + span_notice(" [user] repairs [target]'s [organ.name] with [tool]."), + span_notice(" You repair [target]'s [organ.name] with [tool].") + ) + organ.damage = 0 + organ.surgeryize() + return ..() + +/datum/surgery_step/robotics/manipulate_robotic_organs/mend/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) - if(target_zone != I.parent_organ_zone || target.get_organ_slot(I.slot)) - to_chat(user, "There is no room for [I] in [target]'s [parse_zone(target_zone)]!") - return -1 + user.visible_message( + span_warning("[user]'s hand slips, gumming up the mechanisms inside of [target]'s [affected.name] with \the [tool]!"), + span_warning("Your hand slips, gumming up the mechanisms inside of [target]'s [affected.name] with \the [tool]!") + ) - if((RUNIC_MIND in target.dna.species.species_traits) && istype(I, /obj/item/organ/internal/brain) && !istype(I, /obj/item/organ/internal/brain/golem)) - to_chat(user, "There is no room for [I] in [target]'s [parse_zone(target_zone)]!") - return -1 + target.adjustToxLoss(5) + affected.receive_damage(5) - if(I.damage > (I.max_damage * 0.75)) - to_chat(user, " \The [I] is in no state to be transplanted.") - return -1 + for(var/obj/item/organ/internal/organ as anything in affected.internal_organs) + organ.receive_damage(rand(3,5),0) + return SURGERY_STEP_RETRY - if(target.get_int_organ(I) && !affected) - to_chat(user, " \The [target] already has [I].") - return -1 - user.visible_message("[user] begins reattaching [target]'s [tool].", \ - "You start reattaching [target]'s [tool].") - target.custom_pain("Someone's rooting around in your [affected.name]!") - else if(istype(tool,/obj/item/mmi)) - current_type = "install" +/datum/surgery_step/robotics/manipulate_robotic_organs/implant + name = "Implant cybernetic organ" + allowed_tools = list( + /obj/item/organ/internal = 100 + ) - if(target_zone != BODY_ZONE_CHEST) - to_chat(user, " You must target the chest cavity.") +/datum/surgery_step/robotics/manipulate_robotic_organs/implant/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/obj/item/organ/internal/organ = tool + var/obj/item/organ/external/affected = target.get_organ(target_zone) - return -1 - var/obj/item/mmi/M = tool + if(!organ.is_robotic()) + to_chat(user, span_notice("You can only implant cybernetic organs.")) + return SURGERY_BEGINSTEP_SKIP + if(target_zone != organ.parent_organ_zone || target.get_organ_slot(organ.slot)) + to_chat(user, span_notice("There is no room for [organ] in [target]'s [parse_zone(target_zone)]!")) + return SURGERY_BEGINSTEP_SKIP - if(!affected) - return -1 + if(organ.damage > (organ.max_damage * 0.75)) + to_chat(user, span_notice(" \The [organ] is in no state to be transplanted.")) + return SURGERY_BEGINSTEP_SKIP - if(!istype(M)) - return -1 + if(target.get_int_organ(organ) && !affected) + to_chat(user, span_warning("[target] already has [organ].")) + return SURGERY_BEGINSTEP_SKIP - if(!M.brainmob || !M.brainmob.client || !M.brainmob.ckey || M.brainmob.stat >= DEAD) - to_chat(user, "That brain is not usable.") - return -1 + user.visible_message( + "[user] begins reattaching [target]'s [tool].", + "You start reattaching [target]'s [tool]." + ) + if(affected) + target.custom_pain("Someone's rooting around in your [affected.name]!") + return ..() - if(!affected.is_robotic()) - to_chat(user, "You cannot install a computer brain into a meat enclosure.") - return -1 +/datum/surgery_step/robotics/manipulate_robotic_organs/implant/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/obj/item/organ/internal/I = tool - if(!target.dna.species) - to_chat(user, "You have no idea what species this person is. Report this on the bug tracker.") - return -1 + if(!user.can_unEquip(I)) + to_chat(user, span_warning("[I] is stuck to your hand, you can't put it in [target]!")) + return SURGERY_STEP_INCOMPLETE - if(!target.dna.species.has_organ[INTERNAL_ORGAN_BRAIN]) - to_chat(user, "You're pretty sure [target.dna.species.name_plural] don't normally have a brain.") - return -1 + user.drop_from_active_hand() + I.insert(target) + user.visible_message( + span_notice("[user] has reattached [target]'s [I]."), + span_notice("You have reattached [target]'s [I].") + ) + return ..() - if(target.get_int_organ(/obj/item/organ/internal/brain/)) - to_chat(user, "Your subject already has a brain.") - return -1 +/datum/surgery_step/robotics/manipulate_robotic_organs/implant/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message( + span_warning("[user]'s hand slips, disconnecting \the [tool]."), + span_warning("Your hand slips, disconnecting \the [tool].") + ) + return SURGERY_STEP_RETRY - user.visible_message("[user] starts installing \the [tool] into [target]'s [affected.name].", \ - "You start installing \the [tool] into [target]'s [affected.name].") - else if(implement_type in implements_extract) - current_type = "extract" - var/list/organs = target.get_organs_zone(target_zone) - if(!(affected && affected.is_robotic())) - return -1 - if(!organs.len) - to_chat(user, "There is no removeable organs in [target]'s [parse_zone(target_zone)]!") - return -1 - else - for(var/obj/item/organ/internal/O as anything in organs) - if(O.unremovable) - continue - O.on_find(user) - organs -= O - organs[O.name] = O +/datum/surgery_step/robotics/manipulate_robotic_organs/extract + name = "extract cybernetic organ" + allowed_tools = list(TOOL_MULTITOOL = 100) + var/obj/item/organ/internal/I + +/datum/surgery_step/robotics/manipulate_robotic_organs/extract/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/list/organs = target.get_organs_zone(target_zone) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(!(affected && affected.is_robotic())) + return SURGERY_BEGINSTEP_SKIP + if(!length(organs)) + to_chat(user, span_notice("There is no removeable organs in [target]'s [parse_zone(target_zone)]!")) + return SURGERY_BEGINSTEP_SKIP + else + for(var/obj/item/organ/internal/O as anything in organs) + if(O.unremovable) + continue + O.on_find(user) + organs -= O + organs[O.name] = O I = tgui_input_list(user, "Remove which organ?", "Surgery", organs) - if(I && user && target && user.Adjacent(target) && user.get_active_hand() == tool) - I = organs[I] - if(!I) return -1 - user.visible_message("[user] starts to decouple [target]'s [I] with \the [tool].", \ - "You start to decouple [target]'s [I] with \the [tool]." ) - - target.custom_pain("The pain in your [affected.name] is living hell!") - else - return -1 - - else if(implement_type in implements_mend) - current_type = "mend" - if(!hasorgans(target)) - return - var/obj/item/organ/external/affected = target.get_organ(target_zone) - - var/found_damaged_organ = FALSE - for(var/obj/item/organ/internal/organ as anything in affected.internal_organs) - if(organ.damage && organ.is_robotic()) - user.visible_message("[user] starts mending the damage to [target]'s [organ.name]'s mechanisms.", \ - "You start mending the damage to [target]'s [organ.name]'s mechanisms.") - found_damaged_organ = TRUE - - if(!found_damaged_organ) - to_chat(user, "There are no damaged components in [affected].") - return -1 - - target.custom_pain("The pain in your [affected.name] is living hell!") - - else if(implement_type in implements_finish) - current_type = "finish" - 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].") - - - ..() - -/datum/surgery_step/robotics/manipulate_robotic_organs/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(current_type == "mend") - - if(!hasorgans(target)) - return - for(var/obj/item/organ/internal/organ as anything in affected.internal_organs) - if(organ.damage && organ.is_robotic()) - user.visible_message(" [user] repairs [target]'s [organ.name] with [tool].", \ - " You repair [target]'s [organ.name] with [tool]." ) - organ.damage = 0 - organ.surgeryize() - else if(current_type == "insert") - var/obj/item/organ/internal/I = tool - - if(!user.can_unEquip(I)) - to_chat(user, "[I] is stuck to your hand, you can't put it in [target]!") - return 0 - - user.drop_from_active_hand() - I.insert(target) - user.visible_message(" [user] has reattached [target]'s [I]." , \ - " You have reattached [target]'s [I].") - - else if(current_type == "install") - user.visible_message(" [user] has installed \the [tool] into [target]'s [affected.name].", \ - " You have installed \the [tool] into [target]'s [affected.name].") - - var/obj/item/mmi/M = tool - - user.drop_item_ground(tool) - M.attempt_become_organ(affected,target) - - else if(current_type == "extract") - if(I && I.owner == target) - user.visible_message(" [user] has decoupled [target]'s [I] with \the [tool]." , \ - " You have decoupled [target]'s [I] with \the [tool].") - - add_attack_logs(user, target, "Surgically removed [I.name]. INTENT: [uppertext(user.a_intent)]") - spread_germs_to_organ(I, user) - var/obj/item/thing = I.remove(target) - if(!istype(thing)) - thing.forceMove(get_turf(target)) - else - thing.forceMove(get_turf(target)) - user.put_in_hands(thing, ignore_anim = FALSE) + if(I && user && target && user.Adjacent(target) && user.get_active_hand() == tool) + I = organs[I] + if(!I) + return SURGERY_BEGINSTEP_SKIP + user.visible_message( + "[user] starts to decouple [target]'s [I] with \the [tool].", + "You start to decouple [target]'s [I] with \the [tool]." + ) + + target.custom_pain("The pain in your [affected.name] is living hell!") else - user.visible_message("[user] can't seem to extract anything from [target]'s [parse_zone(target_zone)]!", - "You can't extract anything from [target]'s [parse_zone(target_zone)]!") - else if(current_type == "finish") - 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].") - affected.open = 0 - affected.germ_level = 0 - return 1 - return 0 - -/datum/surgery_step/robotics/manipulate_robotic_organs/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - - if(current_type == "mend") - if(!hasorgans(target)) - return - var/obj/item/organ/external/affected = target.get_organ(target_zone) - - user.visible_message(" [user]'s hand slips, gumming up the mechanisms inside of [target]'s [affected.name] with \the [tool]!", \ - " Your hand slips, gumming up the mechanisms inside of [target]'s [affected.name] with \the [tool]!") - - target.adjustToxLoss(5) - affected.receive_damage(5) - - for(var/obj/item/organ/internal/organ as anything in affected.internal_organs) - organ.receive_damage(rand(3,5),0) - - else if(current_type == "insert") - user.visible_message(" [user]'s hand slips, disconnecting \the [tool].", \ - " Your hand slips, disconnecting \the [tool].") - - else if(current_type == "extract") - user.visible_message(" [user]'s hand slips, disconnecting \the [tool].", \ - " Your hand slips, disconnecting \the [tool].") - - else if(current_type == "install") - user.visible_message(" [user]'s hand slips!.", \ - " Your hand slips!") - else if(current_type == "finish") - 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].") - return 0 + return SURGERY_BEGINSTEP_SKIP + + return ..() + +/datum/surgery_step/robotics/manipulate_robotic_organs/extract/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(!I || I.owner != target) + user.visible_message( + "[user] can't seem to extract anything from [target]'s [parse_zone(target_zone)]!", + span_notice("You can't extract anything from [target]'s [parse_zone(target_zone)]!") + ) + return SURGERY_STEP_CONTINUE + + user.visible_message( + span_notice(" [user] has decoupled [target]'s [I] with \the [tool]."), + span_notice(" You have decoupled [target]'s [I] with \the [tool].") + ) + + add_attack_logs(user, target, "Surgically removed [I.name]. INTENT: [uppertext(user.a_intent)]") + spread_germs_to_organ(I, user) + var/obj/item/thing = I.remove(target) + if(QDELETED(thing)) + return ..() + if(!istype(thing)) + thing.forceMove(get_turf(target)) + else + thing.forceMove(get_turf(target)) + user.put_in_hands(thing, ignore_anim = FALSE) + return ..() + +/datum/surgery_step/robotics/manipulate_robotic_organs/extract/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message( + span_warning(" [user]'s hand slips, disconnecting \the [tool]."), + span_warning(" Your hand slips, disconnecting \the [tool].") + ) + + return SURGERY_STEP_RETRY + + +/datum/surgery_step/robotics/manipulate_robotic_organs/install_mmi + name = "insert robotic brain" + allowed_tools = list(/obj/item/mmi = 100) + +/datum/surgery_step/robotics/manipulate_robotic_organs/install_mmi/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(target_zone != BODY_ZONE_CHEST) + to_chat(user, span_notice(" You must target the chest cavity.")) + + return SURGERY_BEGINSTEP_SKIP + + var/obj/item/organ/external/affected = target.get_organ(target_zone) + var/obj/item/mmi/M = tool + + if(!affected) + return SURGERY_BEGINSTEP_SKIP + + if(!istype(M)) + return SURGERY_BEGINSTEP_SKIP + + if(!M.brainmob || !M.brainmob.client || !M.brainmob.ckey || M.brainmob.stat >= DEAD) + to_chat(user, span_danger("That brain is not usable.")) + return SURGERY_BEGINSTEP_SKIP + + if(!affected.is_robotic()) + to_chat(user, span_danger("You cannot install a computer brain into a meat enclosure.")) + return SURGERY_BEGINSTEP_SKIP + + if(!target.dna.species) + to_chat(user, span_danger("You have no idea what species this person is. Report this on the bug tracker.")) + return SURGERY_BEGINSTEP_SKIP + if(!target.dna.species.has_organ["brain"]) + to_chat(user, span_danger("You're pretty sure [target.dna.species.name_plural] don't normally have a brain.")) + return SURGERY_BEGINSTEP_SKIP + + if(target.get_int_organ(/obj/item/organ/internal/brain)) + to_chat(user, span_danger("Your subject already has a brain.")) + return SURGERY_BEGINSTEP_SKIP + + user.visible_message( + "[user] starts installing \the [tool] into [target]'s [affected.name].", + "You start installing \the [tool] into [target]'s [affected.name]." + ) + return ..() + + +/datum/surgery_step/robotics/manipulate_robotic_organs/install_mmi/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( + span_notice("[user] has installed \the [tool] into [target]'s [affected.name]."), + span_notice("You have installed \the [tool] into [target]'s [affected.name].") + ) + + var/obj/item/mmi/M = tool + + user.drop_item_ground(tool) + M.attempt_become_organ(affected, target) + return ..() + +/datum/surgery_step/robotics/manipulate_robotic_organs/install_mmi/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message( + span_warning("[user]'s hand slips!."), + span_warning("Your hand slips!") + ) + return SURGERY_STEP_RETRY /datum/surgery_step/robotics/external/amputate name = "remove robotic limb" allowed_tools = list( - /obj/item/multitool = 100) + TOOL_MULTITOOL = 100 + ) - time = 100 + 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) +/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 \the [tool].", \ - "You start to decouple [target]'s [affected.name] with \the [tool]." ) + user.visible_message( + "[user] starts to decouple [target]'s [affected.name] with \the [tool].", + "You start to decouple [target]'s [affected.name] with \the [tool]." + ) target.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) +/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 \the [tool]." , \ - " You have decoupled [target]'s [affected.name] with \the [tool].") + user.visible_message( + span_notice("[user] has decoupled [target]'s [affected.name] with \the [tool]."), + span_notice("You have decoupled [target]'s [affected.name] with \the [tool].") + ) 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(istype(thing,/obj/item)) + var/atom/movable/thing = affected.droplimb(1, DROPLIMB_SHARP) + if(isitem(thing)) thing.forceMove(get_turf(target)) user.put_in_hands(thing, ignore_anim = FALSE) - return 1 - -/datum/surgery_step/robotics/external/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) + return SURGERY_STEP_CONTINUE - user.visible_message(" [user]'s hand slips!", \ - " Your hand slips!") - return 0 +/datum/surgery_step/robotics/external/amputate/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) -/datum/surgery/cybernetic_customization - name = "Cybernetic Appearance Customization" - steps = list(/datum/surgery_step/robotics/external/unscrew_hatch, /datum/surgery_step/robotics/external/customize_appearance) - possible_locs = list( - BODY_ZONE_HEAD, - BODY_ZONE_CHEST, - BODY_ZONE_L_ARM, - BODY_ZONE_PRECISE_L_HAND, - BODY_ZONE_R_ARM, - BODY_ZONE_PRECISE_R_HAND, - BODY_ZONE_R_LEG, - BODY_ZONE_PRECISE_R_FOOT, - BODY_ZONE_L_LEG, - BODY_ZONE_PRECISE_L_FOOT, - BODY_ZONE_PRECISE_GROIN, - BODY_ZONE_TAIL, - BODY_ZONE_WING, + user.visible_message( + span_warning("[user]'s hand slips!"), + span_warning("Your hand slips!") ) - requires_organic_bodypart = FALSE - -/datum/surgery/cybernetic_customization/can_start(mob/user, mob/living/carbon/human/target) - if(ishuman(target)) - var/obj/item/organ/external/affected = target.get_organ(user.zone_selected) - if(!affected) - return FALSE - if(!affected.is_robotic()) - return FALSE - return TRUE + return SURGERY_STEP_RETRY /datum/surgery_step/robotics/external/customize_appearance name = "reprogram limb" - allowed_tools = list(/obj/item/multitool = 100) - time = 48 - -/datum/surgery_step/robotics/external/customize_appearance/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - if(..()) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(!affected) - return FALSE - return TRUE + allowed_tools = list(TOOL_MULTITOOL = 100) + time = 4.8 SECONDS -/datum/surgery_step/robotics/external/customize_appearance/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/robotics/external/customize_appearance/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 reprogram the appearance of [target]'s [affected.name] with [tool]." , \ - "You begin to reprogram the appearance of [target]'s [affected.name] with [tool].") - ..() + user.visible_message( + "[user] begins to reprogram the appearance of [target]'s [affected.name] with [tool].", + "You begin to reprogram the appearance of [target]'s [affected.name] with [tool]." + ) + return ..() /datum/surgery_step/robotics/external/customize_appearance/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) var/chosen_appearance = tgui_input_list(user, "Select the company appearance for this limb.", "Limb Company Selection", GLOB.selectable_robolimbs) if(!chosen_appearance) - return FALSE + return SURGERY_STEP_INCOMPLETE var/obj/item/organ/external/affected = target.get_organ(target_zone) affected.robotize(company = chosen_appearance, convert_all = FALSE) if(istype(affected, /obj/item/organ/external/head)) @@ -645,13 +709,15 @@ target.update_body() target.updatehealth() target.UpdateDamageIcon() - user.visible_message(" [user] reprograms the appearance of [target]'s [affected.name] with [tool].", \ - " You reprogram the appearance of [target]'s [affected.name] with [tool].") - affected.open = 0 - return TRUE + user.visible_message( + span_notice("[user] reprograms the appearance of [target]'s [affected.name] with [tool]."), + span_notice("You reprogram the appearance of [target]'s [affected.name] with [tool].") + ) + affected.open = ORGAN_CLOSED + return SURGERY_STEP_CONTINUE -/datum/surgery_step/robotics/external/customize_appearance/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/robotics/external/customize_appearance/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 reprogram [target]'s [affected.name].", - " Your [tool.name] slips, failing to reprogram [target]'s [affected.name].") - return FALSE + user.visible_message(span_warning(" [user]'s [tool.name] slips, failing to reprogram [target]'s [affected.name]."), + span_warning(" Your [tool.name] slips, failing to reprogram [target]'s [affected.name].")) + return SURGERY_STEP_RETRY diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index f441bb5fe4c..31689404598 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -1,103 +1,336 @@ ///Datum Surgery Helpers// + + /datum/surgery + /// Name of the surgery var/name - var/status = 1 + /// Description of the surgery + var/desc + /// How far along we are in a surgery being performed. + var/step_number = 1 + /// Surgical steps that go into performing this procedure var/list/steps = list() - /* - var/eyes = 0 - var/face = 0 - var/appendix = 0 - var/ribcage = 0 - var/head_reattach = 0 //Steps in a surgery - */ - - var/can_cancel = 1 - var/step_in_progress = 0 - var/list/in_progress = list() //Actively performing a Surgery - var/location = BODY_ZONE_CHEST //Surgery location - var/requires_organic_bodypart = 1 //Prevents you from performing an operation on robotic limbs - var/list/possible_locs = list() //Multiple locations -- c0 - var/obj/item/organ/organ_ref //Operable body part - var/current_organ = "organ" - var/list/allowed_mob = list(/mob/living/carbon/human) + /// Whether this surgery can be stopped after the first step with a cautery + var/can_cancel = TRUE + /// If we're currently performing a step + var/step_in_progress = FALSE + /// Location of the surgery + var/location = BODY_ZONE_CHEST + /// Whether we can perform the surgery on a robotic limb + var/requires_organic_bodypart = TRUE //Prevents you from performing an operation on robotic limbs + /// Whether you need to remove clothes to perform the surgery + var/ignore_clothes = TRUE + /// Body locations this surgery can be performed on + var/list/possible_locs = list() + /// Surgery is only available if the target bodypart is present (or if it's missing) + var/requires_bodypart = TRUE + /// Surgery step speed modifier + var/speed_modifier = 0 + /// Some surgeries might work on limbs that don't really exist (like chainsaw arms or flashlight eyes) + var/requires_real_bodypart = TRUE + /// Does the victim (patient) need to be lying down? + var/lying_required = TRUE + /// Can the surgery be performed on yourself? + var/self_operable = FALSE + /// Don't show this surgery if this type exists. Set to /datum/surgery if you want to hide a "base" surgery. + var/replaced_by + /// Mobs on which this can be performed + var/list/target_mobtypes = list(/mob/living/carbon/human) + /// Species on which this can be performed + var/list/target_speciestypes + /// Species on which this cannot be performed + var/list/restricted_speciestypes + /// Target of the surgery + var/mob/living/carbon/target + /// Body part the surgery is currently being performed on. Useful for checking to see if the organ desired is still in the body after the surgery has begun. + var/obj/item/organ/external/organ_to_manipulate + /// Whether or not this should be a selectable surgery at all + var/abstract = FALSE + /// Whether this surgery should be cancelled when an organ change happens. (removed if requires bodypart, or added if doesn't require bodypart) + var/cancel_on_organ_change = TRUE + + +/datum/surgery/New(atom/surgery_target, surgery_location, surgery_bodypart) + ..() + if(!surgery_target) + return + target = surgery_target + target.surgeries += src + if(surgery_location) + location = surgery_location + if(!surgery_bodypart) + return + organ_to_manipulate = surgery_bodypart + if(cancel_on_organ_change) + if(requires_bodypart) + RegisterSignal(surgery_target, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(on_organ_remove)) + else + RegisterSignal(surgery_target, COMSIG_CARBON_GAIN_ORGAN, PROC_REF(on_organ_insert)) + +/datum/surgery/Destroy() + if(target) + target.surgeries -= src + target = null + organ_to_manipulate = null + return ..() + +/// Get whether the target organ is non-compatible with the current surgery. +/datum/surgery/proc/is_organ_noncompatible(obj/item/organ/external/affecting) + if(!affecting || !istype(affecting)) + return TRUE + return requires_organic_bodypart && affecting.is_robotic() || !requires_organic_bodypart && !affecting.is_robotic() + +/** + * Whether or not we can start this surgery. + * If this returns FALSE, this surgery won't show up in the list. + */ /datum/surgery/proc/can_start(mob/user, mob/living/carbon/target) - // if 0 surgery wont show up in list - // put special restrictions here - return 1 + SHOULD_CALL_PARENT(TRUE) + if(replaced_by == /datum/surgery) + return FALSE + + return TRUE +/** + * Try to perform the next step in the current operation. + * This gets called in the attack chain, and as such returning FALSE in here means that the target + * will be hit with whatever's in your hand. + * + * The return is passed to the attack chain, so return TRUE to stop any sort of afterattack. + */ +/datum/surgery/proc/next_step(mob/user, mob/living/carbon/target) + if(location != user.zone_selected) + return FALSE -/datum/surgery/proc/next_step(mob/user, mob/living/carbon/target, obj/item/tool) - if(step_in_progress) return + if(step_in_progress) + return FALSE - var/datum/surgery_step/S = get_surgery_step() - if(S) - if(S.try_op(user, target, user.zone_selected, tool, src)) - return 1 - return 0 + if(lying_required && !on_operable_surface(target)) + return FALSE -/datum/surgery/proc/get_surgery_step() - var/step_type = steps[status] + if(!self_operable && user == target) + return FALSE + + var/datum/surgery_step/step = get_surgery_step() + if(step) + var/obj/item/tool = user.get_active_hand() + if(step.try_op(user, target, user.zone_selected, tool, src)) + return TRUE + // If it's a surgery initiator, make sure it calls its attack chain down the line. + // Make sure this comes after the operation though, especially for things like scalpels + if(tool && tool.GetComponent(/datum/component/surgery_initiator)) + return FALSE + if(tool && HAS_TRAIT(tool, TRAIT_SURGICAL)) + to_chat(user, span_warning("This step requires a different tool!")) + return TRUE + return FALSE + +/** + * Get the current surgery step we're on + */ +/datum/surgery/proc/get_surgery_step(add_number = 0) + var/step_type = steps[clamp(step_number + add_number, 1, length(steps))] return new step_type +/** + * Get the next step in the current surgery, or null if we're on the last one. + */ +/datum/surgery/proc/get_surgery_next_step() + if(step_number < length(steps)) + var/step_type = steps[step_number + 1] + return new step_type + else + return null /datum/surgery/proc/complete(mob/living/carbon/human/target) target.surgeries -= src qdel(src) +/** + * Handle an organ's insertion or removal mid-surgery. + * If cancel_on_organ_change is true, then this will cancel the surgery in certain cases. + */ +/datum/surgery/proc/handle_organ_state_change(mob/living/carbon/organ_owner, obj/item/organ/external/organ, insert) + SIGNAL_HANDLER // only called from signals anyway, better safe than sorry + if(!istype(organ_owner) || !istype(organ) || !cancel_on_organ_change) // only fire this on external organs + return + + if(requires_bodypart && organ != organ_to_manipulate) // we removed a different organ + return + + if((requires_bodypart && !insert) || (!requires_bodypart && insert)) + add_attack_logs(null, organ_owner, "had [src] canceled by organ [insert ? "insertion" : "removal"]") + qdel(src) + +/datum/surgery/proc/on_organ_insert(mob/living/carbon/organ_owner, obj/item/organ/external/organ) + SIGNAL_HANDLER // COMSIG_CARBON_GAIN_ORGAN + handle_organ_state_change(organ_owner, organ, TRUE) + +/datum/surgery/proc/on_organ_remove(mob/living/carbon/organ_owner, obj/item/organ/external/organ) + SIGNAL_HANDLER // COMSIG_CARBON_LOSE_ORGAN + handle_organ_state_change(organ_owner, organ, FALSE) /* SURGERY STEPS */ /datum/surgery_step - var/priority = 0 //steps with higher priority would be attempted first - - // type path referencing tools that can be used for this step, and how well are they suited for it + var/name + /// Type path of tools that can be used to complete this step. Format is `path = probability of success`. + /// If the tool has a specific surgery tooltype, you can use that as a key as well. var/list/allowed_tools = null + /// The current type of implement from allowed_tools in use. This has to be stored, as the typepath of the tool might not match the list type (such as if we're using tool behavior) var/implement_type = null + /// does the surgery step require an open hand? If true, ignores implements. Compatible with accept_any_item. + var/accept_hand = FALSE + /// Does the surgery step accept any item? If true, ignores implements. Compatible with accept_hand. + var/accept_any_item = FALSE + /// duration of the step + var/time = 1 SECONDS + /// Is this step repeatable by using the same tool again after it's finished? + /// Make sure it isn't the last step, or it's used in a cancellable surgery. Otherwise, you might get stuck in a loop! + var/repeatable = FALSE + /// List of chems needed in the mob to complete the step. Even on success, this step will have no effect if the required chems aren't in the mob. + var/list/chems_needed = list() + /// Do we require any of the needed chems, or all of them? + var/require_all_chems = TRUE + /// Whether silicons ignore any probabilities (and are therefore "perfect" surgeons) + var/silicons_ignore_prob = FALSE + /// How many times this step has been automatically repeated. + var/times_repeated = 0 - // duration of the step - var/time = 10 + // evil infection stuff that will make everyone hate me - var/name - var/accept_hand = 0 //does the surgery step require an open hand? If true, ignores implements. Compatible with accept_any_item. - var/accept_any_item = 0 + /// Whether this surgery step can cause an infection. + var/can_infect = FALSE + /// How much blood this step can get on surgeon. See SURGERY_BLOODSPREAD_* defines + var/blood_level = SURGERY_BLOODSPREAD_NONE - // evil infection stuff that will make everyone hate me - var/can_infect = 0 - //How much blood this step can get on surgeon. 1 - hands, 2 - full body. - var/blood_level = 0 +/** + * Whether or not the tool being used is usable for the surgery. + * Checks both the tool itself as well as any tool behaviors defined in allowed_tools. + * Arguments: + * * user - User handling the tool. + * * tool - The tool (or item) being used in this surgery step. + * Returns TRUE if the tool can be used, or FALSE otherwise + */ +/datum/surgery_step/proc/is_valid_tool(mob/living/user, obj/item/tool) -/datum/surgery_step/proc/try_op(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) - var/success = 0 + var/success = FALSE if(accept_hand) if(!tool) - success = 1 + success = TRUE + if(isrobot(user) && istype(tool, /obj/item/gripper/medical)) + success = TRUE + if(accept_any_item) - if(tool && tool_quality(tool)) - success = 1 - else - for(var/path in allowed_tools) - if(istype(tool, path)) - implement_type = path - if(tool_quality(tool)) - success = 1 + if(tool && tool_check(user, tool)) + success = TRUE + else if(tool) + if(istype(tool, /obj/item/scalpel/laser/manager/debug)) + // ok this is a meme but only use it if we'd actually be replacing a tool + for(var/key in allowed_tools) + if(!ispath(key) && (key in GLOB.surgery_tool_behaviors)) + allowed_tools[tool.type] = 100 + implement_type = tool.type + success = TRUE + break + + for(var/key in allowed_tools) + var/match = FALSE + if(ispath(key) && istype(tool, key)) + match = TRUE + else if(tool.tool_behaviour == key) + match = TRUE + + if(match) + implement_type = key + if(tool_check(user, tool)) + success = TRUE + break + + return success - if(success) +/** + * Try to perform an operation on a user. + * Arguments: + * * user - The user performing the surgery. + * * target - The user on whom the surgery is being performed. + * * target_zone - the zone the user is targeting for the surgery. + * * tool - The object that the user is using to perform the surgery (optional) + * * surgery - The surgery being performed. + * Returns TRUE if the step was a success, or FALSE if the step can't be performed for some reason. + */ +/datum/surgery_step/proc/try_op(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + + if(is_valid_tool(user, tool)) if(target_zone == surgery.location) - initiate(user, target, target_zone, tool, surgery) - return 1//returns 1 so we don't stab the guy in the dick or wherever. - return 0 + if(get_location_accessible(target, target_zone) || surgery.ignore_clothes) + return initiate(user, target, target_zone, tool, surgery) + to_chat(user, span_warning("You need to expose [target]'s [parse_zone(target_zone)] before you can perform surgery on it!")) + return SURGERY_INITIATE_FAILURE //returns TRUE so we don't stab the guy in the dick or wherever. + + if(repeatable) + // you can continuously, manually, perform a step, so long as you continue to use the correct tool. + // if you use the wrong tool, though, it'll try to start the next surgery step with the tool you have in your hand. + // Note that this separate from returning the surgery_step_* defines in end/fail_step, which will automatically retry the surgery. + var/datum/surgery_step/next_step = surgery.get_surgery_next_step() + next_step.times_repeated = times_repeated + 1 + if(next_step) + surgery.step_number++ + if(next_step.try_op(user, target, user.zone_selected, user.get_active_hand(), surgery)) + return SURGERY_INITIATE_SUCCESS + else + surgery.step_number-- + + return SURGERY_INITIATE_CONTINUE_CHAIN -/datum/surgery_step/proc/initiate(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) - if(!can_use(user, target, target_zone, tool, surgery)) - return - surgery.step_in_progress = 1 +/** + * Determines whether or not this surgery step can repeat if its end/fail steps returned SURGERY_STEP_RETRY. + * + * Arguments: + * * user - mob performing the surgery + * * target - mob the surgery is being performed on + * * target_zone - body zone of the surgery + * * tool - tool used for the surgery + * * surgery - the operation this surgery step is a part of + * + * If this returns TRUE, the step will automatically retry. If not, the user will have to manually start the step again. + * + */ +/datum/surgery_step/proc/can_repeat(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(tool && istype(tool) && HAS_TRAIT(tool, TRAIT_ADVANCED_SURGICAL)) + return TRUE + if(REPEATSURGERY in user.dna?.species.species_traits) + return TRUE + return FALSE - var/speed_mod = 1 +/** + * Initiate and really perform the surgery itself. + * This includes the main do-after and the checking of probabilities for successful surgeries. + * If try_to_fail is TRUE, then this surgery will be deliberately failed out of. + * + * Returns TRUE if the surgery should proceed to the next step, or FALSE otherwise. + */ +/datum/surgery_step/proc/initiate(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, try_to_fail = FALSE) - if(begin_step(user, target, target_zone, tool, surgery) == -1) - surgery.step_in_progress = 0 - return + surgery.step_in_progress = TRUE + + var/speed_mod = 1 + var/advance = FALSE + var/retry = FALSE + var/prob_success = 100 + + var/begin_step_result = begin_step(user, target, target_zone, tool, surgery) + if(begin_step_result == SURGERY_BEGINSTEP_ABORT) + surgery.step_in_progress = FALSE + return SURGERY_INITIATE_FAILURE + if(begin_step_result == SURGERY_BEGINSTEP_SKIP) + surgery.step_number++ + if(surgery.step_number > length(surgery.steps)) + surgery.complete(target) + + surgery.step_in_progress = FALSE + return SURGERY_INITIATE_SUCCESS if(tool) speed_mod = tool.toolspeed * gettoolspeedmod(user) @@ -105,85 +338,167 @@ if(is_species(user, /datum/species/unathi/ashwalker/shaman))//shaman is slightly better at surgeries speed_mod *= 0.9 - if(do_after(user, time * speed_mod, target = target)) - var/advance = 0 - var/prob_chance = 100 + // Using an unoptimal tool slows down your surgery + var/implement_speed_mod = 1 + if(implement_type) + 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 + var/modded_time = time * speed_mod + + if(slowdown_immune(user)) + modded_time = time + + 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(implement_type) //this means it isn't a require nd or any item step. - prob_chance = allowed_tools[implement_type] - prob_chance *= get_location_modifier(target) + if(!do_after(user, modded_time, target = target)) + surgery.step_in_progress = FALSE + return SURGERY_INITIATE_INTERRUPTED + var/chem_check_result = chem_check(target) + var/pain_mod = deal_pain(user, target, target_zone, tool, surgery) + prob_success *= pain_mod - if(!ispath(surgery.steps[surgery.status], /datum/surgery_step/robotics))//Repairing robotic limbs doesn't hurt, and neither does cutting someone out of a rig - if(ishuman(target)) - var/mob/living/carbon/human/H = target //typecast to human - prob_chance *= get_pain_modifier(H)//operating on conscious people is hard. + var/step_result - if(prob(prob_chance) || isrobot(user)) - if(end_step(user, target, target_zone, tool, surgery)) - advance = 1 + if((prob(prob_success) || silicons_ignore_prob && isrobot(user)) && chem_check_result && !try_to_fail) + step_result = end_step(user, target, target_zone, tool, surgery) + else + step_result = fail_step(user, target, target_zone, tool, surgery) + switch(step_result) + if(SURGERY_STEP_CONTINUE) + advance = TRUE + if(SURGERY_STEP_RETRY_ALWAYS) + retry = TRUE + if(SURGERY_STEP_RETRY) + if(can_repeat(user, target, target_zone, tool, surgery)) + retry = TRUE + + if(retry) + // if at first you don't succeed... + return .(user, target, target_zone, tool, surgery, try_to_fail) + + // Bump the surgery status + // if it's repeatable, don't let it truly "complete" though + if(advance && !repeatable) + surgery.step_number++ + if(surgery.step_number > length(surgery.steps)) + surgery.complete(target) + + surgery.step_in_progress = FALSE + if(advance) + return SURGERY_INITIATE_SUCCESS + return SURGERY_INITIATE_FAILURE + +/** + * Try to inflict pain during a surgery, a surgeon's dream come true. + * This will wake up the user if they're voluntarily sleeping. + * + * Returns the success rate that the user's amount of pain would deal, while also handling extra pain behavior. + */ +/datum/surgery_step/proc/deal_pain(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, try_to_fail = FALSE) + . = 1 + if(!surgery.requires_organic_bodypart) + return + if(!ishuman(target)) + return + + var/mob/living/carbon/human/H = target + var/pain_mod = get_pain_modifier(H) + + // don't let people sit on the optable and sleep verb + var/datum/status_effect/incapacitating/sleeping/S = H.IsSleeping() + if(S) + H.SetSleeping(0) // wake up people who are napping through the surgery + if(pain_mod < 0.95) + to_chat(H, span_danger("The surgery on your [parse_zone(target_zone)] is agonizingly painful, and rips you out of your shallow slumber!")) else - if(fail_step(user, target, target_zone, tool, surgery)) - advance = 1 - - if(advance) - surgery.status++ - if(surgery.status > surgery.steps.len) - surgery.complete(target) - - surgery.step_in_progress = 0 - -//returns how well tool is suited for this step -/datum/surgery_step/proc/tool_quality(obj/item/tool) - for(var/T in allowed_tools) - if(istype(tool,T)) - return allowed_tools[T] - return 0 - -// Checks if this step applies to the user mob at all -/datum/surgery_step/proc/is_valid_target(mob/living/carbon/human/target) - if(!hasorgans(target)) - return FALSE + // Still wake people up, but they shouldn't be as alarmed. + to_chat(H, span_warning("The surgery being performed on your [parse_zone(target_zone)] wakes you up.")) + return pain_mod //operating on conscious people is hard. + +/** + * Get whether the tool should be usable in its current state. Useful for checks to see if a welder is on, for example. + * + * Arguments: + * * user - The user using the tool. + * * tool - The tool in use. + * + */ +/datum/surgery_step/proc/tool_check(mob/user, obj/item/tool) return TRUE -// checks whether this step can be applied with the given user and target -/datum/surgery_step/proc/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - return 1 +/** + * Check for mobs that would be immune to surgery slowdowns/speedups. + */ +/datum/surgery_step/proc/slowdown_immune(mob/living/user) + if(isrobot(user)) + return TRUE + return FALSE // does stuff to begin the step, usually just printing messages. Moved germs transfering and bloodying here too -/datum/surgery_step/proc/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) +/datum/surgery_step/proc/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + SHOULD_CALL_PARENT(TRUE) if(ishuman(target)) var/obj/item/organ/external/affected = target.get_organ(target_zone) if(can_infect && affected) spread_germs_to_organ(affected, user, tool) - if(ishuman(user) && !(istype(target,/mob/living/carbon/alien)) && prob(60)) + if(ishuman(user) && !istype(target, /mob/living/carbon/alien) && prob(60)) var/mob/living/carbon/human/H = user - if(blood_level) - H.bloody_hands(target,0) - if(blood_level > 1) - H.bloody_body(target,0) + switch(blood_level) + if(SURGERY_BLOODSPREAD_HANDS) + H.bloody_hands(target, 0) + if(SURGERY_BLOODSPREAD_FULLBODY) + H.bloody_body(target) return -// does stuff to end the step, which is normally print a message + do whatever this step changes -/datum/surgery_step/proc/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - return +/** + * Finish a surgery step, performing anything that runs on the tail-end of a successful surgery. + * This runs if the surgery step passes the probability check, and therefore is a success. + * + * Should return SURGERY_STEP_CONTINUE to advance the surgery, though may return SURGERY_STEP_INCOMPLETE to keep the surgery step from advancing. + */ +/datum/surgery_step/proc/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + return SURGERY_STEP_CONTINUE -// stuff that happens when the step fails -/datum/surgery_step/proc/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - return null +/** + * Play out the failure state of a surgery step. + * This runs if the surgery step fails the probability check, the right chems weren't present, or if the user deliberately failed the surgery. + * + * Should return SURGERY_STEP_INCOMPLETE to prevent the surgery step from advancing, though may return SURGERY_STEP_CONTINUE to advance to the next step regardless. + */ +/datum/surgery_step/proc/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + return SURGERY_STEP_INCOMPLETE -/proc/spread_germs_to_organ(obj/item/organ/E, mob/living/carbon/human/user, obj/item/tool) - if(!istype(user) || !istype(E) || E.is_robotic() || E.sterile) +/** + * Get the action that will be performed during this surgery step, in context of the surgery it is a part of. + * + * * surgery - A surgery in progress. + */ +/datum/surgery_step/proc/get_step_information(datum/surgery/surgery) + return name + +/** + * Spread some nasty germs to an organ. + * + * * target_organ - The organ to try spreading germs to. + * * user - The user who's manipulating the organ. + * * tool - The tool the user is using to mess with the organ. + */ +/proc/spread_germs_to_organ(obj/item/organ/target_organ, mob/living/carbon/human/user, obj/item/tool) + if(!istype(user) || !istype(target_organ) || target_organ.is_robotic() || target_organ.sterile) return var/germ_level = user.germ_level - //germ spread from surgeon touching the patient + // germ spread from surgeon touching the patient if(user.gloves) germ_level = user.gloves.germ_level - E.germ_level = max(germ_level, E.germ_level) - spread_germs_by_incision(E, tool) //germ spread from environement to patient - + target_organ.germ_level = max(germ_level, target_organ.germ_level) + spread_germs_by_incision(target_organ, tool) //germ spread from environement to patient /** * Spread germs directly from a tool. @@ -218,19 +533,20 @@ E.germ_level += germs - -/proc/sort_surgeries() - var/gap = GLOB.surgery_steps.len - var/swapped = 1 - while(gap > 1 || swapped) - swapped = 0 - if(gap > 1) - gap = round(gap / 1.247330950103979) - if(gap < 1) - gap = 1 - for(var/i = 1; gap + i <= GLOB.surgery_steps.len; i++) - var/datum/surgery_step/l = GLOB.surgery_steps[i] //Fucking hate - var/datum/surgery_step/r = GLOB.surgery_steps[gap+i] //how lists work here - if(l.priority < r.priority) - GLOB.surgery_steps.Swap(i, gap + i) - swapped = 1 +/** + * Check that the target contains the chems we expect them to. + */ +/datum/surgery_step/proc/chem_check(mob/living/target) + if(!LAZYLEN(chems_needed)) + return TRUE + + if(require_all_chems) + . = TRUE + for(var/reagent in chems_needed) + if(!target.reagents.has_reagent(reagent)) + return FALSE + else + . = FALSE + for(var/reagent in chems_needed) + if(target.reagents.has_reagent(reagent)) + return TRUE diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm index f412d8f26f2..1e47b52c0a5 100644 --- a/code/modules/surgery/tools.dm +++ b/code/modules/surgery/tools.dm @@ -8,6 +8,11 @@ flags = CONDUCT w_class = WEIGHT_CLASS_SMALL origin_tech = "materials=1;biotech=1" + tool_behaviour = TOOL_RETRACTOR + +/obj/item/retractor/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT) /obj/item/retractor/augment desc = "Micro-mechanical manipulator for retracting stuff." @@ -25,6 +30,11 @@ w_class = WEIGHT_CLASS_TINY origin_tech = "materials=1;biotech=1" attack_verb = list("attacked", "pinched") + tool_behaviour = TOOL_HEMOSTAT + +/obj/item/hemostat/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT) /obj/item/hemostat/augment desc = "Tiny servos power a pair of pincers to stop bleeding." @@ -41,6 +51,11 @@ w_class = WEIGHT_CLASS_TINY origin_tech = "materials=1;biotech=1" attack_verb = list("burnt") + tool_behaviour = TOOL_CAUTERY + +/obj/item/cautery/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT) /obj/item/cautery/augment desc = "A heated element that cauterizes wounds." @@ -60,10 +75,15 @@ w_class = WEIGHT_CLASS_NORMAL origin_tech = "materials=1;biotech=1" attack_verb = list("drilled") + tool_behaviour = TOOL_DRILL + +/obj/item/surgicaldrill/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT) /obj/item/surgicaldrill/suicide_act(mob/user) - to_chat(viewers(user), pick("[user] is pressing [src] to [user.p_their()] temple and activating it! It looks like [user.p_theyre()] trying to commit suicide.", - "[user] is pressing [src] to [user.p_their()] chest and activating it! It looks like [user.p_theyre()] trying to commit suicide.")) + to_chat(viewers(user), pick(span_suicide("[user] is pressing [src] to [user.p_their()] temple and activating it! It looks like [user.p_theyre()] trying to commit suicide."), + span_suicide("[user] is pressing [src] to [user.p_their()] chest and activating it! It looks like [user.p_theyre()] trying to commit suicide."))) return BRUTELOSS /obj/item/surgicaldrill/augment @@ -92,11 +112,18 @@ origin_tech = "materials=1;biotech=1" attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") hitsound = 'sound/weapons/bladeslice.ogg' + tool_behaviour = TOOL_SCALPEL + +/obj/item/scalpel/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT) + AddComponent(/datum/component/surgery_initiator) + /obj/item/scalpel/suicide_act(mob/user) - to_chat(viewers(user), pick("[user] is slitting [user.p_their()] wrists with [src]! It looks like [user.p_theyre()] trying to commit suicide.", - "[user] is slitting [user.p_their()] throat with [src]! It looks like [user.p_theyre()] trying to commit suicide.", - "[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku.")) + to_chat(viewers(user), pick(span_suicide("[user] is slitting [user.p_their()] wrists with [src]! It looks like [user.p_theyre()] trying to commit suicide."), + span_suicide("[user] is slitting [user.p_their()] throat with [src]! It looks like [user.p_theyre()] trying to commit suicide."), + span_suicide("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku."))) return BRUTELOSS @@ -138,6 +165,22 @@ icon_state = "scalpel_manager_on" toolspeed = 0.2 +/obj/item/scalpel/laser/manager/Initialize(mapload) + . = ..() + // this one can automatically retry its steps, too! + ADD_TRAIT(src, TRAIT_ADVANCED_SURGICAL, ROUNDSTART_TRAIT) + +/obj/item/scalpel/laser/manager/debug + name = "debug IMS" + desc = "A wonder of modern medicine. This tool functions as any other sort of surgery tool, and finishes in only a fraction of the time. Hey, how'd you get your hands on this, anyway?" + toolspeed = 0.01 + +/obj/item/scalpel/laser/manager/debug/attack_self(mob/user) + . = ..() + toolspeed = toolspeed == 0.5 ? 0.01 : 0.5 + to_chat(user, "[src] is now set to toolspeed [toolspeed]") + playsound(src, 'sound/effects/pop.ogg', 50, 0) //Change the mode + /obj/item/circular_saw name = "circular saw" desc = "For heavy duty cutting." @@ -157,6 +200,11 @@ materials = list(MAT_METAL=10000, MAT_GLASS=6000) origin_tech = "biotech=1;combat=1" attack_verb = list("attacked", "slashed", "sawed", "cut") + tool_behaviour = TOOL_SAW + +/obj/item/circular_saw/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT) /obj/item/circular_saw/augment desc = "A small but very fast spinning saw. Edges dulled to prevent accidental cutting inside of the surgeon." @@ -174,6 +222,11 @@ w_class = WEIGHT_CLASS_SMALL throwforce = 1.0 origin_tech = "materials=1;biotech=1" + tool_behaviour = TOOL_BONEGEL + +/obj/item/bonegel/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT) /obj/item/bonegel/augment toolspeed = 0.5 @@ -187,6 +240,11 @@ throwforce = 1.0 origin_tech = "materials=1;biotech=1" w_class = WEIGHT_CLASS_SMALL + tool_behaviour = TOOL_FIXOVEIN + +/obj/item/FixOVein/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT) /obj/item/FixOVein/augment toolspeed = 0.5 @@ -203,6 +261,11 @@ w_class = WEIGHT_CLASS_SMALL attack_verb = list("attacked", "hit", "bludgeoned") origin_tech = "materials=1;biotech=1" + tool_behaviour = TOOL_BONESET + +/obj/item/bonesetter/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT) /obj/item/bonesetter/augment toolspeed = 0.5 diff --git a/code/modules/surgery/vocal_cords_surgery.dm b/code/modules/surgery/vocal_cords_surgery.dm index 65df2949707..6c41a1f95a6 100644 --- a/code/modules/surgery/vocal_cords_surgery.dm +++ b/code/modules/surgery/vocal_cords_surgery.dm @@ -6,38 +6,42 @@ //Surgery for organics /datum/surgery/vocal_cords_surgery name = "Vocal Cords Tuning Surgery" - steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/tune_vocal_cords, /datum/surgery_step/generic/cauterize) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/tune_vocal_cords, + /datum/surgery_step/generic/cauterize + ) + possible_locs = list(BODY_ZONE_PRECISE_MOUTH) /datum/surgery/vocal_cords_surgery/can_start(mob/user, mob/living/carbon/target) - if(!ishuman(target)) + . = ..() + if(!.) return FALSE - var/mob/living/carbon/human/H = target - if(!H.check_has_mouth()) + if(requires_organic_bodypart && !H.check_has_mouth()) return FALSE - return TRUE - /datum/surgery_step/tune_vocal_cords name = "tune vocal cords" - allowed_tools = list(/obj/item/scalpel = 100, /obj/item/kitchen/knife = 50, /obj/item/wirecutters = 35) - time = 64 + allowed_tools = list(TOOL_SCALPEL = 100, /obj/item/kitchen/knife = 50, /obj/item/wirecutters = 35) + time = 6.4 SECONDS var/target_vocal = "vocal cords" /datum/surgery_step/tune_vocal_cords/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) - user.visible_message("[user] begins to tune [target]'s vocals.", "You begin to tune [target]'s vocals...") + user.visible_message("[user] begins to tune [target]'s vocals.", span_notice("You begin to tune [target]'s vocals...")) ..() /datum/surgery_step/tune_vocal_cords/end_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) target.change_voice(user, TRUE) - user.visible_message("[user] tunes [target]'s vocals completely!", "You tune [target]'s vocals completely.") + user.visible_message("[user] tunes [target]'s vocals completely!", span_notice("You tune [target]'s vocals completely.")) return TRUE /datum/surgery_step/tune_vocal_cords/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) var/obj/item/organ/external/head/head = target.get_organ(target_zone) - user.visible_message(" [user]'s hand slips, tearing [target_vocal] in [target]'s throat with [tool]!", \ - " Your hand slips, tearing [target_vocal] in [target]'s throat with [tool]!") + user.visible_message(span_warning("[user]'s hand slips, tearing [target_vocal] in [target]'s throat with [tool]!"), \ + span_warning("Your hand slips, tearing [target_vocal] in [target]'s throat with [tool]!")) target.tts_seed = SStts.get_random_seed(target) target.apply_damage(10, BRUTE, head, sharp = TRUE) return FALSE @@ -45,22 +49,15 @@ //Surgery for IPC /datum/surgery/vocal_cords_surgery/ipc name = "Microphone Setup Operation" - steps = list(/datum/surgery_step/robotics/external/unscrew_hatch, /datum/surgery_step/robotics/external/open_hatch, /datum/surgery_step/tune_vocal_cords/ipc, /datum/surgery_step/robotics/external/close_hatch) + steps = list( + /datum/surgery_step/robotics/external/unscrew_hatch, + /datum/surgery_step/robotics/external/open_hatch, + /datum/surgery_step/tune_vocal_cords/ipc, + /datum/surgery_step/robotics/external/close_hatch + ) requires_organic_bodypart = FALSE -/datum/surgery/vocal_cords_surgery/ipc/can_start(mob/user, mob/living/carbon/target) - if(!ishuman(target)) - return FALSE - - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/head/affected = H.get_organ(user.zone_selected) - if(!affected) - return FALSE - if(!affected.is_robotic()) - return FALSE - return TRUE - /datum/surgery_step/tune_vocal_cords/ipc name = "microphone setup" - allowed_tools = list(/obj/item/multitool = 100, /obj/item/screwdriver = 55, /obj/item/kitchen/knife = 20, /obj/item/scalpel = 25) + allowed_tools = list(/obj/item/multitool = 100, /obj/item/screwdriver = 55, /obj/item/kitchen/knife = 20, TOOL_SCALPEL = 25) target_vocal = "microphone" diff --git a/icons/mob/clothing/species/drask/helmet.dmi b/icons/mob/clothing/species/drask/helmet.dmi index a87086313af..e3789b335f9 100644 Binary files a/icons/mob/clothing/species/drask/helmet.dmi and b/icons/mob/clothing/species/drask/helmet.dmi differ diff --git a/icons/mob/clothing/species/drask/suit.dmi b/icons/mob/clothing/species/drask/suit.dmi index 800a7d1d3d3..3c7a064e95b 100644 Binary files a/icons/mob/clothing/species/drask/suit.dmi and b/icons/mob/clothing/species/drask/suit.dmi differ diff --git a/icons/mob/clothing/species/grey/head.dmi b/icons/mob/clothing/species/grey/head.dmi index c452fa8e25c..466e1cd726d 100644 Binary files a/icons/mob/clothing/species/grey/head.dmi and b/icons/mob/clothing/species/grey/head.dmi differ diff --git a/icons/mob/clothing/species/grey/helmet.dmi b/icons/mob/clothing/species/grey/helmet.dmi index d9798e939bd..96e5c15ed44 100644 Binary files a/icons/mob/clothing/species/grey/helmet.dmi and b/icons/mob/clothing/species/grey/helmet.dmi differ diff --git a/icons/mob/clothing/species/grey/suit.dmi b/icons/mob/clothing/species/grey/suit.dmi index fa9c02c252d..eee6a706a0c 100644 Binary files a/icons/mob/clothing/species/grey/suit.dmi and b/icons/mob/clothing/species/grey/suit.dmi differ diff --git a/icons/mob/clothing/species/monkey/head.dmi b/icons/mob/clothing/species/monkey/head.dmi index 1fc5e3aca89..6b7d1dcb7bc 100644 Binary files a/icons/mob/clothing/species/monkey/head.dmi and b/icons/mob/clothing/species/monkey/head.dmi differ diff --git a/icons/mob/clothing/species/tajaran/helmet.dmi b/icons/mob/clothing/species/tajaran/helmet.dmi index dd7a6fd0166..e601aea8fd8 100644 Binary files a/icons/mob/clothing/species/tajaran/helmet.dmi and b/icons/mob/clothing/species/tajaran/helmet.dmi differ diff --git a/icons/mob/clothing/species/tajaran/suit.dmi b/icons/mob/clothing/species/tajaran/suit.dmi index 05cb40b48f5..0be4b917fbf 100644 Binary files a/icons/mob/clothing/species/tajaran/suit.dmi and b/icons/mob/clothing/species/tajaran/suit.dmi differ diff --git a/icons/mob/clothing/species/unathi/helmet.dmi b/icons/mob/clothing/species/unathi/helmet.dmi index da6d6591425..66ebde6a830 100644 Binary files a/icons/mob/clothing/species/unathi/helmet.dmi and b/icons/mob/clothing/species/unathi/helmet.dmi differ diff --git a/icons/mob/clothing/species/unathi/suit.dmi b/icons/mob/clothing/species/unathi/suit.dmi index 4da168bf083..c1f029376ed 100644 Binary files a/icons/mob/clothing/species/unathi/suit.dmi and b/icons/mob/clothing/species/unathi/suit.dmi differ diff --git a/icons/mob/clothing/species/vox/helmet.dmi b/icons/mob/clothing/species/vox/helmet.dmi index 39f4e1c65a4..eed57c01199 100644 Binary files a/icons/mob/clothing/species/vox/helmet.dmi and b/icons/mob/clothing/species/vox/helmet.dmi differ diff --git a/icons/mob/clothing/species/vox/suit.dmi b/icons/mob/clothing/species/vox/suit.dmi index 026becf2657..a424b5eb8d4 100644 Binary files a/icons/mob/clothing/species/vox/suit.dmi and b/icons/mob/clothing/species/vox/suit.dmi differ diff --git a/icons/mob/clothing/species/vulpkanin/helmet.dmi b/icons/mob/clothing/species/vulpkanin/helmet.dmi index 47cf250e3aa..f7f7cf76e97 100644 Binary files a/icons/mob/clothing/species/vulpkanin/helmet.dmi and b/icons/mob/clothing/species/vulpkanin/helmet.dmi differ diff --git a/icons/mob/clothing/species/vulpkanin/suit.dmi b/icons/mob/clothing/species/vulpkanin/suit.dmi index 7c282e0e2cd..2e9faa45fad 100644 Binary files a/icons/mob/clothing/species/vulpkanin/suit.dmi and b/icons/mob/clothing/species/vulpkanin/suit.dmi differ diff --git a/icons/mob/clothing/suit.dmi b/icons/mob/clothing/suit.dmi index 0b691343d68..c2d9ddf3951 100644 Binary files a/icons/mob/clothing/suit.dmi and b/icons/mob/clothing/suit.dmi differ diff --git a/icons/mob/corgi_head.dmi b/icons/mob/corgi_head.dmi index 7492b40833d..9e3f08d051d 100644 Binary files a/icons/mob/corgi_head.dmi and b/icons/mob/corgi_head.dmi differ diff --git a/icons/obj/machines/computer.dmi b/icons/obj/machines/computer.dmi index 79202db1365..8117b3ea2a6 100644 Binary files a/icons/obj/machines/computer.dmi and b/icons/obj/machines/computer.dmi differ diff --git a/paradise.dme b/paradise.dme index 6db503b1613..3bb25aabdd9 100644 --- a/paradise.dme +++ b/paradise.dme @@ -114,6 +114,7 @@ #include "code\__DEFINES\station_goals.dm" #include "code\__DEFINES\status_effects.dm" #include "code\__DEFINES\subsystems.dm" +#include "code\__DEFINES\surgery_defines.dm" #include "code\__DEFINES\tgs.dm" #include "code\__DEFINES\tgui.dm" #include "code\__DEFINES\tools.dm" @@ -396,10 +397,12 @@ #include "code\datums\components\proximity_monitor.dm" #include "code\datums\components\radioactivity.dm" #include "code\datums\components\sharpening.dm" +#include "code\datums\components\shielded.dm" #include "code\datums\components\slippery.dm" #include "code\datums\components\spawner.dm" #include "code\datums\components\spooky.dm" #include "code\datums\components\squeak.dm" +#include "code\datums\components\surgery_initiator.dm" #include "code\datums\components\swarming.dm" #include "code\datums\components\twohanded.dm" #include "code\datums\diseases\_disease.dm" @@ -2880,6 +2883,7 @@ #include "code\modules\station_goals\brs\brs_stationary_scanner.dm" #include "code\modules\store\items.dm" #include "code\modules\store\store.dm" +#include "code\modules\surgery\abstract_steps.dm" #include "code\modules\surgery\bones.dm" #include "code\modules\surgery\cavity_implant.dm" #include "code\modules\surgery\core_removal.dm" diff --git a/strings/sillytips.txt b/strings/sillytips.txt index 2a7e8872535..ed06ae29b52 100644 --- a/strings/sillytips.txt +++ b/strings/sillytips.txt @@ -10,7 +10,6 @@ Cleanbot. Иногда раунд будет просто неудачным. C'est la vie. Это игра, которая постоянно совершенствуется. Ожидайте, что каждый день что-то будет добавляться, удаляться, исправляться и ломаться. Забавно пытаться предсказать режим раунда по советам раунда. -Квартирмейстер не является главой персонала и никогда им не будет. Ваш спрайт представляет ваш хитбокс, так что афро делает вас более легким для убийства. На какие жертвы мы идем ради стиля. Иногда админы просто делают что-то. Смиритесь с этим. Ремейк никогда не выйдет. diff --git a/tgui/packages/tgui/interfaces/common/CrewManifest.js b/tgui/packages/tgui/interfaces/common/CrewManifest.js index 56dfe8dd093..f8520d06bfb 100644 --- a/tgui/packages/tgui/interfaces/common/CrewManifest.js +++ b/tgui/packages/tgui/interfaces/common/CrewManifest.js @@ -13,6 +13,7 @@ const HeadRoles = [ "Chief Medical Officer", "Research Director", "Head of Personnel", + "Quartermaster", ]; // Head colour check. Abbreviated to save on 80 char @@ -22,22 +23,15 @@ const HCC = role => { return "green"; } - // Return yellow if its the qm - if (role === "Quartermaster") { - return "yellow"; - } - // Return orange if its a regular person return "orange"; }; // Head bold check. Abbreviated to save on 80 char const HBC = role => { - // Return true if they are a head, or a QM - if ((HeadRoles.indexOf(role) !== -1) || role === "Quartermaster") { + // Return true if they are a head + if (HeadRoles.indexOf(role) !== -1) { return true; - } else { - return false; } }; diff --git a/tgui/packages/tgui/public/tgui.bundle.js b/tgui/packages/tgui/public/tgui.bundle.js index 3c83078af8b..1bb26be7977 100644 --- a/tgui/packages/tgui/public/tgui.bundle.js +++ b/tgui/packages/tgui/public/tgui.bundle.js @@ -59,7 +59,7 @@ var a=/-o$/,c=function(e){var t=e.name,n=e.size,c=e.spin,l=e.className,s=e.style * @copyright 2020 Aleksej Komarov * @license MIT */ -var s=function(e){var t,n;function s(t){var n;n=e.call(this,t)||this;var i=t.value;return n.inputRef=(0,o.createRef)(),n.state={value:i,dragging:!1,editing:!1,internalValue:null,origin:null,suppressingFlicker:!1},n.flickerTimer=null,n.suppressFlicker=function(){var e=n.props.suppressFlicker;e>0&&(n.setState({suppressingFlicker:!0}),clearTimeout(n.flickerTimer),n.flickerTimer=setTimeout((function(){return n.setState({suppressingFlicker:!1})}),e))},n.handleDragStart=function(e){var t=n.props.value;n.state.editing||(document.body.style["pointer-events"]="none",n.ref=e.target,n.setState({dragging:!1,origin:e.screenY,value:t,internalValue:t}),n.timer=setTimeout((function(){n.setState({dragging:!0})}),250),n.dragInterval=setInterval((function(){var t=n.state,o=t.dragging,r=t.value,i=n.props.onDrag;o&&i&&i(e,r)}),500),document.addEventListener("mousemove",n.handleDragMove),document.addEventListener("mouseup",n.handleDragEnd))},n.handleDragMove=function(e){var t=n.props,o=t.minValue,i=t.maxValue,a=t.step,c=t.stepPixelSize;n.setState((function(t){var n=Object.assign({},t),l=n.origin-e.screenY;if(t.dragging){var s=Number.isFinite(o)?o%a:0;n.internalValue=(0,r.clamp)(n.internalValue+l*a/c,o-a,i+a),n.value=(0,r.clamp)(n.internalValue-n.internalValue%a+s,o,i),n.origin=e.screenY}else Math.abs(l)>4&&(n.dragging=!0);return n}))},n.handleDragEnd=function(e){var t=n.props,o=t.onChange,r=t.onDrag,i=n.state,a=i.dragging,c=i.value,l=i.internalValue;if(document.body.style["pointer-events"]="auto",clearTimeout(n.timer),clearInterval(n.dragInterval),n.setState({dragging:!1,editing:!a,origin:null}),document.removeEventListener("mousemove",n.handleDragMove),document.removeEventListener("mouseup",n.handleDragEnd),a)n.suppressFlicker(),o&&o(e,c),r&&r(e,c);else if(n.inputRef){var s=n.inputRef.current;s.value=l;try{s.focus(),s.select()}catch(d){}}},n}return n=e,(t=s).prototype=Object.create(n.prototype),t.prototype.constructor=t,t.__proto__=n,s.prototype.render=function(){var e=this,t=this.state,n=t.dragging,s=t.editing,d=t.value,u=t.suppressingFlicker,p=this.props,m=p.className,h=p.fluid,f=p.animated,C=p.value,g=p.unit,N=p.minValue,b=p.maxValue,V=p.height,v=p.width,x=p.lineHeight,y=p.fontSize,k=p.format,w=p.onChange,_=p.onDrag,B=C;(n||u)&&(B=d);var S=function(e){return(0,o.createVNode)(1,"div","NumberInput__content",e+(g?" "+g:""),0,{unselectable:a.IS_IE8})},L=f&&!n&&!u&&(0,o.createComponentVNode)(2,c.AnimatedNumber,{value:B,format:k,children:S})||S(k?k(B):B);return(0,o.createComponentVNode)(2,l.Box,{className:(0,i.classes)(["NumberInput",h&&"NumberInput--fluid",m]),minWidth:v,minHeight:V,lineHeight:x,fontSize:y,onMouseDown:this.handleDragStart,children:[(0,o.createVNode)(1,"div","NumberInput__barContainer",(0,o.createVNode)(1,"div","NumberInput__bar",null,1,{style:{height:(0,r.clamp)((B-N)/(b-N)*100,0,100)+"%"}}),2),L,(0,o.createVNode)(64,"input","NumberInput__input",null,1,{style:{display:s?undefined:"none",height:V,"line-height":x,"font-size":y},onBlur:function(t){if(s){var n=(0,r.clamp)(t.target.value,N,b);e.setState({editing:!1,value:n}),e.suppressFlicker(),w&&w(t,n),_&&_(t,n)}},onKeyDown:function(t){if(13===t.keyCode){var n=(0,r.clamp)(t.target.value,N,b);return e.setState({editing:!1,value:n}),e.suppressFlicker(),w&&w(t,n),void(_&&_(t,n))}27!==t.keyCode||e.setState({editing:!1})}},null,this.inputRef)]})},s}(o.Component);t.NumberInput=s,s.defaultHooks=i.pureComponentHooks,s.defaultProps={minValue:-Infinity,maxValue:+Infinity,step:1,stepPixelSize:1,suppressFlicker:50}},function(e,t,n){"use strict";var o=n(66);e.exports=new o({explicit:[n(499),n(500),n(501)]})},function(e,t,n){"use strict";t.__esModule=!0,t.BeakerContents=void 0;var o=n(0),r=n(2),i=n(523),a=function(e){var t=e.beakerLoaded,n=e.beakerContents,i=void 0===n?[]:n,a=e.buttons;return(0,o.createComponentVNode)(2,r.Box,{children:[!t&&(0,o.createComponentVNode)(2,r.Box,{color:"label",children:"No beaker loaded."})||0===i.length&&(0,o.createComponentVNode)(2,r.Box,{color:"label",children:"Beaker is empty."}),i.map((function(e,t){return(0,o.createComponentVNode)(2,r.Box,{width:"100%",children:[(0,o.createComponentVNode)(2,r.Box,{color:"label",display:"inline",verticalAlign:"middle",children:[(n=e.volume,n+" unit"+(1===n?"":"s"))," of ",e.name]}),!!a&&(0,o.createComponentVNode)(2,r.Box,{float:"right",display:"inline",children:a(e,t)}),(0,o.createComponentVNode)(2,r.Box,{clear:"both"})]},e.name);var n}))]})};t.BeakerContents=a,a.propTypes={beakerLoaded:i.bool,beakerContents:i.array,buttons:i.arrayOf(i.element)}},function(e,t,n){"use strict";t.__esModule=!0,t.CrewManifest=void 0;var o=n(0),r=n(1),i=n(2),a=n(18),c=n(43).COLORS.department,l=["Captain","Head of Security","Chief Engineer","Chief Medical Officer","Research Director","Head of Personnel"],s=function(e){return-1!==l.indexOf(e)||"Quartermaster"===e},d=function(e){return e.length>0&&(0,o.createComponentVNode)(2,i.Table,{children:[(0,o.createComponentVNode)(2,i.Table.Row,{header:!0,color:"white",children:[(0,o.createComponentVNode)(2,i.Table.Cell,{width:"50%",children:"Name"}),(0,o.createComponentVNode)(2,i.Table.Cell,{width:"35%",children:"Rank"}),(0,o.createComponentVNode)(2,i.Table.Cell,{width:"15%",children:"Active"})]}),e.map((function(e){return(0,o.createComponentVNode)(2,i.Table.Row,{color:(t=e.rank,-1!==l.indexOf(t)?"green":"Quartermaster"===t?"yellow":"orange"),bold:s(e.rank),children:[(0,o.createComponentVNode)(2,i.Table.Cell,{children:(0,a.decodeHtmlEntities)(e.name)}),(0,o.createComponentVNode)(2,i.Table.Cell,{children:(0,a.decodeHtmlEntities)(e.rank)}),(0,o.createComponentVNode)(2,i.Table.Cell,{children:e.active})]},e.name+e.rank);var t}))]})};t.CrewManifest=function(e,t){var n;(0,r.useBackend)(t).act;e.data?n=e.data:n=(0,r.useBackend)(t).data;var a=n.manifest,l=a.heads,s=a.pro,u=a.sec,p=a.eng,m=a.med,h=a.sci,f=a.ser,C=a.sup,g=a.misc;return(0,o.createComponentVNode)(2,i.Box,{children:[(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.command,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Command"})}),level:2,children:d(l)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.procedure,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Procedure"})}),level:2,children:d(s)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.security,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Security"})}),level:2,children:d(u)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.engineering,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Engineering"})}),level:2,children:d(p)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.medical,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Medical"})}),level:2,children:d(m)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.science,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Science"})}),level:2,children:d(h)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.service,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Service"})}),level:2,children:d(f)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.supply,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Supply"})}),level:2,children:d(C)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Misc"})}),level:2,children:d(g)})]})}},function(e,t,n){"use strict";t.__esModule=!0,t.TemporaryNotice=void 0;var o=n(0),r=n(1),i=n(2);t.TemporaryNotice=function(e,t){var n,a=(0,r.useBackend)(t),c=a.act,l=a.data.temp;if(l){var s=((n={})[l.style]=!0,n);return(0,o.normalizeProps)((0,o.createComponentVNode)(2,i.NoticeBox,Object.assign({},s,{children:[(0,o.createComponentVNode)(2,i.Box,{display:"inline-block",verticalAlign:"middle",children:l.text}),(0,o.createComponentVNode)(2,i.Button,{icon:"times-circle",float:"right",onClick:function(){return c("cleartemp")}}),(0,o.createComponentVNode)(2,i.Box,{clear:"both"})]})))}}},function(e,t,n){"use strict";var o=n(9),r=n(5),i=n(108);e.exports=!o&&!r((function(){return 7!=Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a}))},function(e,t,n){"use strict";var o=n(6),r=n(109),i=o["__core-js_shared__"]||r("__core-js_shared__",{});e.exports=i},function(e,t,n){"use strict";var o=n(6),r=n(110),i=o.WeakMap;e.exports="function"==typeof i&&/native code/.test(r(i))},function(e,t,n){"use strict";var o=n(19),r=n(112),i=n(22),a=n(14);e.exports=function(e,t){for(var n=r(t),c=a.f,l=i.f,s=0;sl;)o(c,n=t[l++])&&(~i(s,n)||s.push(n));return s}},function(e,t,n){"use strict";var o=n(115);e.exports=o&&!Symbol.sham&&"symbol"==typeof Symbol.iterator},function(e,t,n){"use strict";var o=n(9),r=n(14),i=n(10),a=n(73);e.exports=o?Object.defineProperties:function(e,t){i(e);for(var n,o=a(t),c=o.length,l=0;c>l;)r.f(e,n=o[l++],t[n]);return e}},function(e,t,n){"use strict";var o=n(39);e.exports=o("document","documentElement")},function(e,t,n){"use strict";var o=n(28),r=n(55).f,i={}.toString,a="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];e.exports.f=function(e){return a&&"[object Window]"==i.call(e)?function(e){try{return r(e)}catch(t){return a.slice()}}(e):r(o(e))}},function(e,t,n){"use strict";var o=n(13);t.f=o},function(e,t,n){"use strict";var o=n(16),r=n(46),i=n(12),a=Math.min;e.exports=[].copyWithin||function(e,t){var n=o(this),c=i(n.length),l=r(e,c),s=r(t,c),d=arguments.length>2?arguments[2]:undefined,u=a((d===undefined?c:r(d,c))-s,c-l),p=1;for(s0;)s in n?n[l]=n[s]:delete n[l],l+=p,s+=p;return n}},function(e,t,n){"use strict";var o=n(60),r=n(12),i=n(56);e.exports=function a(e,t,n,c,l,s,d,u){for(var p,m=l,h=0,f=!!d&&i(d,u,3);h0&&o(p))m=a(e,t,p,r(p.length),m,s-1)-1;else{if(m>=9007199254740991)throw TypeError("Exceed the acceptable array length");e[m]=p}m++}h++}return m}},function(e,t,n){"use strict";var o=n(10);e.exports=function(e,t,n,r){try{return r?t(o(n)[0],n[1]):t(n)}catch(a){var i=e["return"];throw i!==undefined&&o(i.call(e)),a}}},function(e,t,n){"use strict";var o=n(28),r=n(49),i=n(76),a=n(37),c=n(121),l=a.set,s=a.getterFor("Array Iterator");e.exports=c(Array,"Array",(function(e,t){l(this,{type:"Array Iterator",target:o(e),index:0,kind:t})}),(function(){var e=s(this),t=e.target,n=e.kind,o=e.index++;return!t||o>=t.length?(e.target=undefined,{value:undefined,done:!0}):"keys"==n?{value:o,done:!1}:"values"==n?{value:t[o],done:!1}:{value:[o,t[o]],done:!1}}),"values"),i.Arguments=i.Array,r("keys"),r("values"),r("entries")},function(e,t,n){"use strict";var o,r,i,a=n(38),c=n(32),l=n(19),s=n(13),d=n(40),u=s("iterator"),p=!1;[].keys&&("next"in(i=[].keys())?(r=a(a(i)))!==Object.prototype&&(o=r):p=!0),o==undefined&&(o={}),d||l(o,u)||c(o,u,(function(){return this})),e.exports={IteratorPrototype:o,BUGGY_SAFARI_ITERATORS:p}},function(e,t,n){"use strict";var o=n(8);e.exports=function(e){if(!o(e)&&null!==e)throw TypeError("Can't set "+String(e)+" as a prototype");return e}},function(e,t,n){"use strict";var o=n(28),r=n(33),i=n(12),a=n(41),c=n(27),l=Math.min,s=[].lastIndexOf,d=!!s&&1/[1].lastIndexOf(1,-0)<0,u=a("lastIndexOf"),p=c("indexOf",{ACCESSORS:!0,1:0}),m=d||!u||!p;e.exports=m?function(e){if(d)return s.apply(this,arguments)||0;var t=o(this),n=i(t.length),a=n-1;for(arguments.length>1&&(a=l(a,r(arguments[1]))),a<0&&(a=n+a);a>=0;a--)if(a in t&&t[a]===e)return a||0;return-1}:s},function(e,t,n){"use strict";var o=n(33),r=n(12);e.exports=function(e){if(e===undefined)return 0;var t=o(e),n=r(t);if(t!==n)throw RangeError("Wrong length or index");return n}},function(e,t,n){"use strict";var o=n(34),r=n(8),i=[].slice,a={},c=function(e,t,n){if(!(t in a)){for(var o=[],r=0;r1?arguments[1]:undefined,3);t=t?t.next:n.first;)for(o(t.value,t.key,this);t&&t.removed;)t=t.previous},has:function(e){return!!g(this,e)}}),i(d.prototype,n?{get:function(e){var t=g(this,e);return t&&t.value},set:function(e,t){return C(this,0===e?0:e,t)}}:{add:function(e){return C(this,e=0===e?0:e,e)}}),u&&o(d.prototype,"size",{get:function(){return m(this).size}}),d},setStrong:function(e,t,n){var o=t+" Iterator",r=f(t),i=f(o);s(e,t,(function(e,t){h(this,{type:o,target:e,state:r(e),kind:t,last:undefined})}),(function(){for(var e=i(this),t=e.kind,n=e.last;n&&n.removed;)n=n.previous;return e.target&&(e.last=n=n?n.next:e.state.first)?"keys"==t?{value:n.key,done:!1}:"values"==t?{value:n.value,done:!1}:{value:[n.key,n.value],done:!1}:(e.target=undefined,{value:undefined,done:!0})}),n?"entries":"values",!n,!0),d(t)}}},function(e,t,n){"use strict";var o=Math.log;e.exports=Math.log1p||function(e){return(e=+e)>-1e-8&&e<1e-8?e-e*e/2:o(1+e)}},function(e,t,n){"use strict";var o=n(8),r=Math.floor;e.exports=function(e){return!o(e)&&isFinite(e)&&r(e)===e}},function(e,t,n){"use strict";var o=n(6),r=n(63).trim,i=n(93),a=o.parseInt,c=/^[+-]?0[Xx]/,l=8!==a(i+"08")||22!==a(i+"0x16");e.exports=l?function(e,t){var n=r(String(e));return a(n,t>>>0||(c.test(n)?16:10))}:a},function(e,t,n){"use strict";var o=n(9),r=n(73),i=n(28),a=n(83).f,c=function(e){return function(t){for(var n,c=i(t),l=r(c),s=l.length,d=0,u=[];s>d;)n=l[d++],o&&!a.call(c,n)||u.push(e?[n,c[n]]:c[n]);return u}};e.exports={entries:c(!0),values:c(!1)}},function(e,t,n){"use strict";e.exports=Object.is||function(e,t){return e===t?0!==e||1/e==1/t:e!=e&&t!=t}},function(e,t,n){"use strict";var o=n(6);e.exports=o.Promise},function(e,t,n){"use strict";var o=n(85);e.exports=/(iphone|ipod|ipad).*applewebkit/i.test(o)},function(e,t,n){"use strict";var o,r,i,a,c,l,s,d,u=n(6),p=n(22).f,m=n(35),h=n(127).set,f=n(173),C=u.MutationObserver||u.WebKitMutationObserver,g=u.process,N=u.Promise,b="process"==m(g),V=p(u,"queueMicrotask"),v=V&&V.value;v||(o=function(){var e,t;for(b&&(e=g.domain)&&e.exit();r;){t=r.fn,r=r.next;try{t()}catch(n){throw r?a():i=undefined,n}}i=undefined,e&&e.enter()},b?a=function(){g.nextTick(o)}:C&&!f?(c=!0,l=document.createTextNode(""),new C(o).observe(l,{characterData:!0}),a=function(){l.data=c=!c}):N&&N.resolve?(s=N.resolve(undefined),d=s.then,a=function(){d.call(s,o)}):a=function(){h.call(u,o)}),e.exports=v||function(e){var t={fn:e,next:undefined};i&&(i.next=t),r||(r=t,a()),i=t}},function(e,t,n){"use strict";var o=n(10),r=n(8),i=n(176);e.exports=function(e,t){if(o(e),r(t)&&t.constructor===e)return t;var n=i.f(e);return(0,n.resolve)(t),n.promise}},function(e,t,n){"use strict";var o=n(34),r=function(e){var t,n;this.promise=new e((function(e,o){if(t!==undefined||n!==undefined)throw TypeError("Bad Promise constructor");t=e,n=o})),this.resolve=o(t),this.reject=o(n)};e.exports.f=function(e){return new r(e)}},function(e,t,n){"use strict";var o=n(4),r=n(96);o({target:"RegExp",proto:!0,forced:/./.exec!==r},{exec:r})},function(e,t,n){"use strict";var o=n(85);e.exports=/Version\/10\.\d+(\.\d+)?( Mobile\/\w+)? Safari\//.test(o)},function(e,t,n){"use strict";var o=n(376);e.exports=function(e,t){var n=o(e);if(n%t)throw RangeError("Wrong offset");return n}},function(e,t,n){"use strict";var o=n(16),r=n(12),i=n(119),a=n(118),c=n(56),l=n(11).aTypedArrayConstructor;e.exports=function(e){var t,n,s,d,u,p,m=o(e),h=arguments.length,f=h>1?arguments[1]:undefined,C=f!==undefined,g=i(m);if(g!=undefined&&!a(g))for(p=(u=g.call(m)).next,m=[];!(d=p.call(u)).done;)m.push(d.value);for(C&&h>2&&(f=c(f,arguments[2],2)),n=r(m.length),s=new(l(this))(n),t=0;n>t;t++)s[t]=C?f(m[t],t):m[t];return s}},function(e,t,n){"use strict";var o=n(77),r=n(59).getWeakData,i=n(10),a=n(8),c=n(62),l=n(79),s=n(21),d=n(19),u=n(37),p=u.set,m=u.getterFor,h=s.find,f=s.findIndex,C=0,g=function(e){return e.frozen||(e.frozen=new N)},N=function(){this.entries=[]},b=function(e,t){return h(e.entries,(function(e){return e[0]===t}))};N.prototype={get:function(e){var t=b(this,e);if(t)return t[1]},has:function(e){return!!b(this,e)},set:function(e,t){var n=b(this,e);n?n[1]=t:this.entries.push([e,t])},"delete":function(e){var t=f(this.entries,(function(t){return t[0]===e}));return~t&&this.entries.splice(t,1),!!~t}},e.exports={getConstructor:function(e,t,n,s){var u=e((function(e,o){c(e,u,t),p(e,{type:t,id:C++,frozen:undefined}),o!=undefined&&l(o,e[s],e,n)})),h=m(t),f=function(e,t,n){var o=h(e),a=r(i(t),!0);return!0===a?g(o).set(t,n):a[o.id]=n,e};return o(u.prototype,{"delete":function(e){var t=h(this);if(!a(e))return!1;var n=r(e);return!0===n?g(t)["delete"](e):n&&d(n,t.id)&&delete n[t.id]},has:function(e){var t=h(this);if(!a(e))return!1;var n=r(e);return!0===n?g(t).has(e):n&&d(n,t.id)}}),o(u.prototype,n?{get:function(e){var t=h(this);if(a(e)){var n=r(e);return!0===n?g(t).get(e):n?n[t.id]:undefined}},set:function(e,t){return f(this,e,t)}}:{add:function(e){return f(this,e,!0)}}),u}}},function(e,t,n){"use strict";t.__esModule=!0,t.setupHotReloading=t.sendLogEntry=void 0;t.sendLogEntry=function(e,t){};t.setupHotReloading=function(){0}},function(e,t,n){"use strict";t.__esModule=!0,t.resizeStartHandler=t.dragStartHandler=t.setupDrag=void 0;var o=n(418),r=n(23);function i(e,t,n,o,r,i,a){try{var c=e[i](a),l=c.value}catch(s){return void n(s)}c.done?t(l):Promise.resolve(l).then(o,r)} +var s=function(e){var t,n;function s(t){var n;n=e.call(this,t)||this;var i=t.value;return n.inputRef=(0,o.createRef)(),n.state={value:i,dragging:!1,editing:!1,internalValue:null,origin:null,suppressingFlicker:!1},n.flickerTimer=null,n.suppressFlicker=function(){var e=n.props.suppressFlicker;e>0&&(n.setState({suppressingFlicker:!0}),clearTimeout(n.flickerTimer),n.flickerTimer=setTimeout((function(){return n.setState({suppressingFlicker:!1})}),e))},n.handleDragStart=function(e){var t=n.props.value;n.state.editing||(document.body.style["pointer-events"]="none",n.ref=e.target,n.setState({dragging:!1,origin:e.screenY,value:t,internalValue:t}),n.timer=setTimeout((function(){n.setState({dragging:!0})}),250),n.dragInterval=setInterval((function(){var t=n.state,o=t.dragging,r=t.value,i=n.props.onDrag;o&&i&&i(e,r)}),500),document.addEventListener("mousemove",n.handleDragMove),document.addEventListener("mouseup",n.handleDragEnd))},n.handleDragMove=function(e){var t=n.props,o=t.minValue,i=t.maxValue,a=t.step,c=t.stepPixelSize;n.setState((function(t){var n=Object.assign({},t),l=n.origin-e.screenY;if(t.dragging){var s=Number.isFinite(o)?o%a:0;n.internalValue=(0,r.clamp)(n.internalValue+l*a/c,o-a,i+a),n.value=(0,r.clamp)(n.internalValue-n.internalValue%a+s,o,i),n.origin=e.screenY}else Math.abs(l)>4&&(n.dragging=!0);return n}))},n.handleDragEnd=function(e){var t=n.props,o=t.onChange,r=t.onDrag,i=n.state,a=i.dragging,c=i.value,l=i.internalValue;if(document.body.style["pointer-events"]="auto",clearTimeout(n.timer),clearInterval(n.dragInterval),n.setState({dragging:!1,editing:!a,origin:null}),document.removeEventListener("mousemove",n.handleDragMove),document.removeEventListener("mouseup",n.handleDragEnd),a)n.suppressFlicker(),o&&o(e,c),r&&r(e,c);else if(n.inputRef){var s=n.inputRef.current;s.value=l;try{s.focus(),s.select()}catch(d){}}},n}return n=e,(t=s).prototype=Object.create(n.prototype),t.prototype.constructor=t,t.__proto__=n,s.prototype.render=function(){var e=this,t=this.state,n=t.dragging,s=t.editing,d=t.value,u=t.suppressingFlicker,p=this.props,m=p.className,h=p.fluid,f=p.animated,C=p.value,g=p.unit,N=p.minValue,b=p.maxValue,V=p.height,v=p.width,x=p.lineHeight,y=p.fontSize,k=p.format,w=p.onChange,_=p.onDrag,B=C;(n||u)&&(B=d);var S=function(e){return(0,o.createVNode)(1,"div","NumberInput__content",e+(g?" "+g:""),0,{unselectable:a.IS_IE8})},L=f&&!n&&!u&&(0,o.createComponentVNode)(2,c.AnimatedNumber,{value:B,format:k,children:S})||S(k?k(B):B);return(0,o.createComponentVNode)(2,l.Box,{className:(0,i.classes)(["NumberInput",h&&"NumberInput--fluid",m]),minWidth:v,minHeight:V,lineHeight:x,fontSize:y,onMouseDown:this.handleDragStart,children:[(0,o.createVNode)(1,"div","NumberInput__barContainer",(0,o.createVNode)(1,"div","NumberInput__bar",null,1,{style:{height:(0,r.clamp)((B-N)/(b-N)*100,0,100)+"%"}}),2),L,(0,o.createVNode)(64,"input","NumberInput__input",null,1,{style:{display:s?undefined:"none",height:V,"line-height":x,"font-size":y},onBlur:function(t){if(s){var n=(0,r.clamp)(t.target.value,N,b);e.setState({editing:!1,value:n}),e.suppressFlicker(),w&&w(t,n),_&&_(t,n)}},onKeyDown:function(t){if(13===t.keyCode){var n=(0,r.clamp)(t.target.value,N,b);return e.setState({editing:!1,value:n}),e.suppressFlicker(),w&&w(t,n),void(_&&_(t,n))}27!==t.keyCode||e.setState({editing:!1})}},null,this.inputRef)]})},s}(o.Component);t.NumberInput=s,s.defaultHooks=i.pureComponentHooks,s.defaultProps={minValue:-Infinity,maxValue:+Infinity,step:1,stepPixelSize:1,suppressFlicker:50}},function(e,t,n){"use strict";var o=n(66);e.exports=new o({explicit:[n(499),n(500),n(501)]})},function(e,t,n){"use strict";t.__esModule=!0,t.BeakerContents=void 0;var o=n(0),r=n(2),i=n(523),a=function(e){var t=e.beakerLoaded,n=e.beakerContents,i=void 0===n?[]:n,a=e.buttons;return(0,o.createComponentVNode)(2,r.Box,{children:[!t&&(0,o.createComponentVNode)(2,r.Box,{color:"label",children:"No beaker loaded."})||0===i.length&&(0,o.createComponentVNode)(2,r.Box,{color:"label",children:"Beaker is empty."}),i.map((function(e,t){return(0,o.createComponentVNode)(2,r.Box,{width:"100%",children:[(0,o.createComponentVNode)(2,r.Box,{color:"label",display:"inline",verticalAlign:"middle",children:[(n=e.volume,n+" unit"+(1===n?"":"s"))," of ",e.name]}),!!a&&(0,o.createComponentVNode)(2,r.Box,{float:"right",display:"inline",children:a(e,t)}),(0,o.createComponentVNode)(2,r.Box,{clear:"both"})]},e.name);var n}))]})};t.BeakerContents=a,a.propTypes={beakerLoaded:i.bool,beakerContents:i.array,buttons:i.arrayOf(i.element)}},function(e,t,n){"use strict";t.__esModule=!0,t.CrewManifest=void 0;var o=n(0),r=n(1),i=n(2),a=n(18),c=n(43).COLORS.department,l=["Captain","Head of Security","Chief Engineer","Chief Medical Officer","Research Director","Head of Personnel","Quartermaster"],s=function(e){if(-1!==l.indexOf(e))return!0},d=function(e){return e.length>0&&(0,o.createComponentVNode)(2,i.Table,{children:[(0,o.createComponentVNode)(2,i.Table.Row,{header:!0,color:"white",children:[(0,o.createComponentVNode)(2,i.Table.Cell,{width:"50%",children:"Name"}),(0,o.createComponentVNode)(2,i.Table.Cell,{width:"35%",children:"Rank"}),(0,o.createComponentVNode)(2,i.Table.Cell,{width:"15%",children:"Active"})]}),e.map((function(e){return(0,o.createComponentVNode)(2,i.Table.Row,{color:(t=e.rank,-1!==l.indexOf(t)?"green":"orange"),bold:s(e.rank),children:[(0,o.createComponentVNode)(2,i.Table.Cell,{children:(0,a.decodeHtmlEntities)(e.name)}),(0,o.createComponentVNode)(2,i.Table.Cell,{children:(0,a.decodeHtmlEntities)(e.rank)}),(0,o.createComponentVNode)(2,i.Table.Cell,{children:e.active})]},e.name+e.rank);var t}))]})};t.CrewManifest=function(e,t){var n;(0,r.useBackend)(t).act;e.data?n=e.data:n=(0,r.useBackend)(t).data;var a=n.manifest,l=a.heads,s=a.pro,u=a.sec,p=a.eng,m=a.med,h=a.sci,f=a.ser,C=a.sup,g=a.misc;return(0,o.createComponentVNode)(2,i.Box,{children:[(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.command,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Command"})}),level:2,children:d(l)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.procedure,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Procedure"})}),level:2,children:d(s)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.security,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Security"})}),level:2,children:d(u)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.engineering,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Engineering"})}),level:2,children:d(p)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.medical,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Medical"})}),level:2,children:d(m)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.science,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Science"})}),level:2,children:d(h)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.service,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Service"})}),level:2,children:d(f)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{backgroundColor:c.supply,m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Supply"})}),level:2,children:d(C)}),(0,o.createComponentVNode)(2,i.Section,{title:(0,o.createComponentVNode)(2,i.Box,{m:-1,pt:1,pb:1,children:(0,o.createComponentVNode)(2,i.Box,{ml:1,textAlign:"center",fontSize:1.4,children:"Misc"})}),level:2,children:d(g)})]})}},function(e,t,n){"use strict";t.__esModule=!0,t.TemporaryNotice=void 0;var o=n(0),r=n(1),i=n(2);t.TemporaryNotice=function(e,t){var n,a=(0,r.useBackend)(t),c=a.act,l=a.data.temp;if(l){var s=((n={})[l.style]=!0,n);return(0,o.normalizeProps)((0,o.createComponentVNode)(2,i.NoticeBox,Object.assign({},s,{children:[(0,o.createComponentVNode)(2,i.Box,{display:"inline-block",verticalAlign:"middle",children:l.text}),(0,o.createComponentVNode)(2,i.Button,{icon:"times-circle",float:"right",onClick:function(){return c("cleartemp")}}),(0,o.createComponentVNode)(2,i.Box,{clear:"both"})]})))}}},function(e,t,n){"use strict";var o=n(9),r=n(5),i=n(108);e.exports=!o&&!r((function(){return 7!=Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a}))},function(e,t,n){"use strict";var o=n(6),r=n(109),i=o["__core-js_shared__"]||r("__core-js_shared__",{});e.exports=i},function(e,t,n){"use strict";var o=n(6),r=n(110),i=o.WeakMap;e.exports="function"==typeof i&&/native code/.test(r(i))},function(e,t,n){"use strict";var o=n(19),r=n(112),i=n(22),a=n(14);e.exports=function(e,t){for(var n=r(t),c=a.f,l=i.f,s=0;sl;)o(c,n=t[l++])&&(~i(s,n)||s.push(n));return s}},function(e,t,n){"use strict";var o=n(115);e.exports=o&&!Symbol.sham&&"symbol"==typeof Symbol.iterator},function(e,t,n){"use strict";var o=n(9),r=n(14),i=n(10),a=n(73);e.exports=o?Object.defineProperties:function(e,t){i(e);for(var n,o=a(t),c=o.length,l=0;c>l;)r.f(e,n=o[l++],t[n]);return e}},function(e,t,n){"use strict";var o=n(39);e.exports=o("document","documentElement")},function(e,t,n){"use strict";var o=n(28),r=n(55).f,i={}.toString,a="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];e.exports.f=function(e){return a&&"[object Window]"==i.call(e)?function(e){try{return r(e)}catch(t){return a.slice()}}(e):r(o(e))}},function(e,t,n){"use strict";var o=n(13);t.f=o},function(e,t,n){"use strict";var o=n(16),r=n(46),i=n(12),a=Math.min;e.exports=[].copyWithin||function(e,t){var n=o(this),c=i(n.length),l=r(e,c),s=r(t,c),d=arguments.length>2?arguments[2]:undefined,u=a((d===undefined?c:r(d,c))-s,c-l),p=1;for(s0;)s in n?n[l]=n[s]:delete n[l],l+=p,s+=p;return n}},function(e,t,n){"use strict";var o=n(60),r=n(12),i=n(56);e.exports=function a(e,t,n,c,l,s,d,u){for(var p,m=l,h=0,f=!!d&&i(d,u,3);h0&&o(p))m=a(e,t,p,r(p.length),m,s-1)-1;else{if(m>=9007199254740991)throw TypeError("Exceed the acceptable array length");e[m]=p}m++}h++}return m}},function(e,t,n){"use strict";var o=n(10);e.exports=function(e,t,n,r){try{return r?t(o(n)[0],n[1]):t(n)}catch(a){var i=e["return"];throw i!==undefined&&o(i.call(e)),a}}},function(e,t,n){"use strict";var o=n(28),r=n(49),i=n(76),a=n(37),c=n(121),l=a.set,s=a.getterFor("Array Iterator");e.exports=c(Array,"Array",(function(e,t){l(this,{type:"Array Iterator",target:o(e),index:0,kind:t})}),(function(){var e=s(this),t=e.target,n=e.kind,o=e.index++;return!t||o>=t.length?(e.target=undefined,{value:undefined,done:!0}):"keys"==n?{value:o,done:!1}:"values"==n?{value:t[o],done:!1}:{value:[o,t[o]],done:!1}}),"values"),i.Arguments=i.Array,r("keys"),r("values"),r("entries")},function(e,t,n){"use strict";var o,r,i,a=n(38),c=n(32),l=n(19),s=n(13),d=n(40),u=s("iterator"),p=!1;[].keys&&("next"in(i=[].keys())?(r=a(a(i)))!==Object.prototype&&(o=r):p=!0),o==undefined&&(o={}),d||l(o,u)||c(o,u,(function(){return this})),e.exports={IteratorPrototype:o,BUGGY_SAFARI_ITERATORS:p}},function(e,t,n){"use strict";var o=n(8);e.exports=function(e){if(!o(e)&&null!==e)throw TypeError("Can't set "+String(e)+" as a prototype");return e}},function(e,t,n){"use strict";var o=n(28),r=n(33),i=n(12),a=n(41),c=n(27),l=Math.min,s=[].lastIndexOf,d=!!s&&1/[1].lastIndexOf(1,-0)<0,u=a("lastIndexOf"),p=c("indexOf",{ACCESSORS:!0,1:0}),m=d||!u||!p;e.exports=m?function(e){if(d)return s.apply(this,arguments)||0;var t=o(this),n=i(t.length),a=n-1;for(arguments.length>1&&(a=l(a,r(arguments[1]))),a<0&&(a=n+a);a>=0;a--)if(a in t&&t[a]===e)return a||0;return-1}:s},function(e,t,n){"use strict";var o=n(33),r=n(12);e.exports=function(e){if(e===undefined)return 0;var t=o(e),n=r(t);if(t!==n)throw RangeError("Wrong length or index");return n}},function(e,t,n){"use strict";var o=n(34),r=n(8),i=[].slice,a={},c=function(e,t,n){if(!(t in a)){for(var o=[],r=0;r1?arguments[1]:undefined,3);t=t?t.next:n.first;)for(o(t.value,t.key,this);t&&t.removed;)t=t.previous},has:function(e){return!!g(this,e)}}),i(d.prototype,n?{get:function(e){var t=g(this,e);return t&&t.value},set:function(e,t){return C(this,0===e?0:e,t)}}:{add:function(e){return C(this,e=0===e?0:e,e)}}),u&&o(d.prototype,"size",{get:function(){return m(this).size}}),d},setStrong:function(e,t,n){var o=t+" Iterator",r=f(t),i=f(o);s(e,t,(function(e,t){h(this,{type:o,target:e,state:r(e),kind:t,last:undefined})}),(function(){for(var e=i(this),t=e.kind,n=e.last;n&&n.removed;)n=n.previous;return e.target&&(e.last=n=n?n.next:e.state.first)?"keys"==t?{value:n.key,done:!1}:"values"==t?{value:n.value,done:!1}:{value:[n.key,n.value],done:!1}:(e.target=undefined,{value:undefined,done:!0})}),n?"entries":"values",!n,!0),d(t)}}},function(e,t,n){"use strict";var o=Math.log;e.exports=Math.log1p||function(e){return(e=+e)>-1e-8&&e<1e-8?e-e*e/2:o(1+e)}},function(e,t,n){"use strict";var o=n(8),r=Math.floor;e.exports=function(e){return!o(e)&&isFinite(e)&&r(e)===e}},function(e,t,n){"use strict";var o=n(6),r=n(63).trim,i=n(93),a=o.parseInt,c=/^[+-]?0[Xx]/,l=8!==a(i+"08")||22!==a(i+"0x16");e.exports=l?function(e,t){var n=r(String(e));return a(n,t>>>0||(c.test(n)?16:10))}:a},function(e,t,n){"use strict";var o=n(9),r=n(73),i=n(28),a=n(83).f,c=function(e){return function(t){for(var n,c=i(t),l=r(c),s=l.length,d=0,u=[];s>d;)n=l[d++],o&&!a.call(c,n)||u.push(e?[n,c[n]]:c[n]);return u}};e.exports={entries:c(!0),values:c(!1)}},function(e,t,n){"use strict";e.exports=Object.is||function(e,t){return e===t?0!==e||1/e==1/t:e!=e&&t!=t}},function(e,t,n){"use strict";var o=n(6);e.exports=o.Promise},function(e,t,n){"use strict";var o=n(85);e.exports=/(iphone|ipod|ipad).*applewebkit/i.test(o)},function(e,t,n){"use strict";var o,r,i,a,c,l,s,d,u=n(6),p=n(22).f,m=n(35),h=n(127).set,f=n(173),C=u.MutationObserver||u.WebKitMutationObserver,g=u.process,N=u.Promise,b="process"==m(g),V=p(u,"queueMicrotask"),v=V&&V.value;v||(o=function(){var e,t;for(b&&(e=g.domain)&&e.exit();r;){t=r.fn,r=r.next;try{t()}catch(n){throw r?a():i=undefined,n}}i=undefined,e&&e.enter()},b?a=function(){g.nextTick(o)}:C&&!f?(c=!0,l=document.createTextNode(""),new C(o).observe(l,{characterData:!0}),a=function(){l.data=c=!c}):N&&N.resolve?(s=N.resolve(undefined),d=s.then,a=function(){d.call(s,o)}):a=function(){h.call(u,o)}),e.exports=v||function(e){var t={fn:e,next:undefined};i&&(i.next=t),r||(r=t,a()),i=t}},function(e,t,n){"use strict";var o=n(10),r=n(8),i=n(176);e.exports=function(e,t){if(o(e),r(t)&&t.constructor===e)return t;var n=i.f(e);return(0,n.resolve)(t),n.promise}},function(e,t,n){"use strict";var o=n(34),r=function(e){var t,n;this.promise=new e((function(e,o){if(t!==undefined||n!==undefined)throw TypeError("Bad Promise constructor");t=e,n=o})),this.resolve=o(t),this.reject=o(n)};e.exports.f=function(e){return new r(e)}},function(e,t,n){"use strict";var o=n(4),r=n(96);o({target:"RegExp",proto:!0,forced:/./.exec!==r},{exec:r})},function(e,t,n){"use strict";var o=n(85);e.exports=/Version\/10\.\d+(\.\d+)?( Mobile\/\w+)? Safari\//.test(o)},function(e,t,n){"use strict";var o=n(376);e.exports=function(e,t){var n=o(e);if(n%t)throw RangeError("Wrong offset");return n}},function(e,t,n){"use strict";var o=n(16),r=n(12),i=n(119),a=n(118),c=n(56),l=n(11).aTypedArrayConstructor;e.exports=function(e){var t,n,s,d,u,p,m=o(e),h=arguments.length,f=h>1?arguments[1]:undefined,C=f!==undefined,g=i(m);if(g!=undefined&&!a(g))for(p=(u=g.call(m)).next,m=[];!(d=p.call(u)).done;)m.push(d.value);for(C&&h>2&&(f=c(f,arguments[2],2)),n=r(m.length),s=new(l(this))(n),t=0;n>t;t++)s[t]=C?f(m[t],t):m[t];return s}},function(e,t,n){"use strict";var o=n(77),r=n(59).getWeakData,i=n(10),a=n(8),c=n(62),l=n(79),s=n(21),d=n(19),u=n(37),p=u.set,m=u.getterFor,h=s.find,f=s.findIndex,C=0,g=function(e){return e.frozen||(e.frozen=new N)},N=function(){this.entries=[]},b=function(e,t){return h(e.entries,(function(e){return e[0]===t}))};N.prototype={get:function(e){var t=b(this,e);if(t)return t[1]},has:function(e){return!!b(this,e)},set:function(e,t){var n=b(this,e);n?n[1]=t:this.entries.push([e,t])},"delete":function(e){var t=f(this.entries,(function(t){return t[0]===e}));return~t&&this.entries.splice(t,1),!!~t}},e.exports={getConstructor:function(e,t,n,s){var u=e((function(e,o){c(e,u,t),p(e,{type:t,id:C++,frozen:undefined}),o!=undefined&&l(o,e[s],e,n)})),h=m(t),f=function(e,t,n){var o=h(e),a=r(i(t),!0);return!0===a?g(o).set(t,n):a[o.id]=n,e};return o(u.prototype,{"delete":function(e){var t=h(this);if(!a(e))return!1;var n=r(e);return!0===n?g(t)["delete"](e):n&&d(n,t.id)&&delete n[t.id]},has:function(e){var t=h(this);if(!a(e))return!1;var n=r(e);return!0===n?g(t).has(e):n&&d(n,t.id)}}),o(u.prototype,n?{get:function(e){var t=h(this);if(a(e)){var n=r(e);return!0===n?g(t).get(e):n?n[t.id]:undefined}},set:function(e,t){return f(this,e,t)}}:{add:function(e){return f(this,e,!0)}}),u}}},function(e,t,n){"use strict";t.__esModule=!0,t.setupHotReloading=t.sendLogEntry=void 0;t.sendLogEntry=function(e,t){};t.setupHotReloading=function(){0}},function(e,t,n){"use strict";t.__esModule=!0,t.resizeStartHandler=t.dragStartHandler=t.setupDrag=void 0;var o=n(418),r=n(23);function i(e,t,n,o,r,i,a){try{var c=e[i](a),l=c.value}catch(s){return void n(s)}c.done?t(l):Promise.resolve(l).then(o,r)} /** * @file * @copyright 2020 Aleksej Komarov