From 5da39ab9ee2803addcb5ac525f9268dcbe622587 Mon Sep 17 00:00:00 2001 From: Nidvex250 <66303093+Nidvex250@users.noreply.github.com> Date: Sat, 20 Apr 2024 08:07:14 -0700 Subject: [PATCH] Hypospray and ICSpawning ports (#27279) * Hypospray Port The fancy new GAG enabled hyposprays. * [MIRROR] ICSpawning Module: Quirks, Loadout, and BST RPED buff Ports CliffracerX's work which allows quirks and/or loadout items when using the ICSpawning (ctrl+clicking a ghost) * oops got rid of that accidental absolute nested path thingy. (I think) --- code/_globalvars/lists/reagents.dm | 8 +- code/game/gamemodes/objective_items.dm | 6 +- code/game/objects/items/storage/belt.dm | 1 + .../crates_lockers/closets/secure/medical.dm | 2 +- .../modules/GAGS/greyscale_configs.dm | 15 ++ .../GAGS/json_configs/items/hypokit.json | 41 +++ .../GAGS/json_configs/items/hypospray.json | 78 ++++++ .../GAGS/json_configs/items/hypovial.json | 28 ++ .../code/armament_datums/deforest_medical.dm | 4 + .../hyposprays/code/autolathe_designs.dm | 123 ++++++++- .../modules/hyposprays/code/hypospray_kits.dm | 210 ++++++++++++--- .../modules/hyposprays/code/hyposprays_II.dm | 122 ++++++--- .../modules/hyposprays/code/hypovials.dm | 179 +++++++++++-- .../hyposprays/code/vending_hypospray.dm | 2 +- .../modules/hyposprays/icons/hypokits.dmi | Bin 988 -> 1835 bytes .../hyposprays/icons/hypospray_fillings.dmi | Bin 584 -> 671 bytes .../modules/hyposprays/icons/hyposprays.dmi | Bin 1485 -> 2612 bytes .../modules/hyposprays/icons/vials.dmi | Bin 2227 -> 3360 bytes modular_skyrat/modules/hyposprays/readme.md | 8 +- .../modules/icspawning/code/observer.dm | 38 ++- .../modules/icspawning/code/standard.dm | 250 ++++++++++++++++++ .../loadout_items/loadout_datum_pocket.dm | 17 +- .../modules/time_clock/code/console_tgui.dm | 2 +- 23 files changed, 1007 insertions(+), 127 deletions(-) create mode 100644 modular_skyrat/modules/GAGS/json_configs/items/hypokit.json create mode 100644 modular_skyrat/modules/GAGS/json_configs/items/hypospray.json create mode 100644 modular_skyrat/modules/GAGS/json_configs/items/hypovial.json diff --git a/code/_globalvars/lists/reagents.dm b/code/_globalvars/lists/reagents.dm index 47b1008aa8a..44ac6d574cd 100644 --- a/code/_globalvars/lists/reagents.dm +++ b/code/_globalvars/lists/reagents.dm @@ -36,10 +36,10 @@ GLOBAL_LIST_INIT(reagent_containers, list( /obj/item/reagent_containers/pill/patch/style )), // SKYRAT EDIT ADDITION START - CAT_HYPOS = list( - /obj/item/reagent_containers/cup/vial/small, - /obj/item/reagent_containers/cup/vial/large, - ), + CAT_HYPOS = typecacheof(list( + /obj/item/reagent_containers/cup/vial/small/style, + /obj/item/reagent_containers/cup/vial/large/style, + )), CAT_DARTS = typecacheof(list( /obj/item/reagent_containers/syringe/smartdart )), diff --git a/code/game/gamemodes/objective_items.dm b/code/game/gamemodes/objective_items.dm index 46b8ea1640d..64b3e4420cb 100644 --- a/code/game/gamemodes/objective_items.dm +++ b/code/game/gamemodes/objective_items.dm @@ -385,7 +385,7 @@ //name = "the hypospray" // ORIGINAL name = "the Chief Medical Officer's hypospray" // SKYRAT EDIT CHANGE //targetitem = /obj/item/reagent_containers/hypospray/cmo // ORIGINAL - targetitem = /obj/item/hypospray/mkii/cmo // SKYRAT EDIT CHANGE + targetitem = /obj/item/hypospray/mkii/deluxe/cmo // SKYRAT EDIT CHANGE excludefromjob = list(JOB_CHIEF_MEDICAL_OFFICER) item_owner = list(JOB_CHIEF_MEDICAL_OFFICER) exists_on_map = TRUE @@ -393,8 +393,8 @@ steal_hint = "The Chief Medical Officer's personal medical injector. \ Usually found amongst their medical supplies on their person, in their belt, or otherwise in their locker." -/obj/item/hypospray/mkii/cmo/add_stealing_item_objective() // SKYRAT EDIT CHANGE - return add_item_to_steal(src, /obj/item/hypospray/mkii/cmo) // SKYRAT EDIT CHANGE +/obj/item/hypospray/mkii/deluxe/cmo/add_stealing_item_objective() // SKYRAT EDIT CHANGE + return add_item_to_steal(src, /obj/item/hypospray/mkii/deluxe/cmo) // SKYRAT EDIT CHANGE /datum/objective_item/steal/nukedisc name = "the nuclear authentication disk" diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index 6249814e0a4..451dc82c89c 100644 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -257,6 +257,7 @@ /obj/item/hemostat, /obj/item/holosign_creator/medical, /obj/item/hypospray/mkii, //SKYRAT EDIT ADDITION - HYPOSPRAYS + /obj/item/storage/hypospraykit/, //SKYRAT EDIT ADDITION - HYPOSPRAYS /obj/item/implant, /obj/item/implantcase, /obj/item/implanter, diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm index 5f2c694ed21..e5eed4cb295 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm @@ -102,7 +102,7 @@ // Traitor steal objective //new /obj/item/reagent_containers/hypospray/cmo(src) - ORIGINAL - new /obj/item/storage/hypospraykit/cmo(src) //SKYRAT EDIT ADDITION - New Hyposprays + new /obj/item/storage/hypospraykit/cmo/preloaded(src) //SKYRAT EDIT ADDITION - New Hyposprays /obj/structure/closet/secure_closet/animal name = "animal control locker" diff --git a/modular_skyrat/modules/GAGS/greyscale_configs.dm b/modular_skyrat/modules/GAGS/greyscale_configs.dm index 14a7dc30f78..b98825dbb52 100644 --- a/modular_skyrat/modules/GAGS/greyscale_configs.dm +++ b/modular_skyrat/modules/GAGS/greyscale_configs.dm @@ -1489,6 +1489,21 @@ digi name = "Wargame Hologram Projector" icon_file = 'modular_skyrat/modules/wargame_projectors/icons/projectors_and_holograms.dmi' json_config = 'modular_skyrat/modules/GAGS/json_configs/wargame_holoprojector/projector.json' + +/datum/greyscale_config/hypokit + name = "Hypospray Kit" + icon_file = 'modular_skyrat/modules/hyposprays/icons/hypokits.dmi' + json_config = 'modular_skyrat/modules/GAGS/json_configs/items/hypokit.json' + +/datum/greyscale_config/hypospray_mkii + name = "Hypospray Mk. II" + icon_file = 'modular_skyrat/modules/hyposprays/icons/hyposprays.dmi' + json_config = 'modular_skyrat/modules/GAGS/json_configs/items/hypospray.json' + +/datum/greyscale_config/hypovial + name = "Hypovial" + icon_file = 'modular_skyrat/modules/hyposprays/icons/vials.dmi' + json_config = 'modular_skyrat/modules/GAGS/json_configs/items/hypovial.json' // Evil primitive catgirl clothing diff --git a/modular_skyrat/modules/GAGS/json_configs/items/hypokit.json b/modular_skyrat/modules/GAGS/json_configs/items/hypokit.json new file mode 100644 index 00000000000..efe26705b17 --- /dev/null +++ b/modular_skyrat/modules/GAGS/json_configs/items/hypokit.json @@ -0,0 +1,41 @@ +{ + "custom-mini": [ + { + "type": "icon_state", + "icon_state": "standard-box", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "standard-face", + "blend_mode": "overlay", + "color_ids": [ 1 ] + } + ], + "deluxe-custom-mini": [ + { + "type": "icon_state", + "icon_state": "deluxe-box-normal", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "deluxe-face", + "blend_mode": "overlay", + "color_ids": [ 1 ] + } + ], + "tactical-custom-mini": [ + { + "type": "icon_state", + "icon_state": "deluxe-box-tactical", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "deluxe-face", + "blend_mode": "overlay", + "color_ids": [ 1 ] + } + ] +} diff --git a/modular_skyrat/modules/GAGS/json_configs/items/hypospray.json b/modular_skyrat/modules/GAGS/json_configs/items/hypospray.json new file mode 100644 index 00000000000..55c5c71c3bb --- /dev/null +++ b/modular_skyrat/modules/GAGS/json_configs/items/hypospray.json @@ -0,0 +1,78 @@ +{ + "hypo2_normal": [ + { + "type": "icon_state", + "icon_state": "hypo2_normal", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "hypo2_accent1", + "blend_mode": "overlay", + "color_ids": [ 1 ] + }, + { + "type": "icon_state", + "icon_state": "hypo2_accent2", + "blend_mode": "overlay", + "color_ids": [ 2 ] + } + ], + "hypo2_deluxe": [ + { + "type": "icon_state", + "icon_state": "hypo2_deluxe", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "hypo2_accent1", + "blend_mode": "overlay", + "color_ids": [ 1 ] + }, + { + "type": "icon_state", + "icon_state": "hypo2_accent2", + "blend_mode": "overlay", + "color_ids": [ 2 ] + } + ], + "hypo2_cmo": [ + { + "type": "icon_state", + "icon_state": "hypo2_cmo", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "hypo2_accent1", + "blend_mode": "overlay", + "color_ids": [ 1 ] + }, + { + "type": "icon_state", + "icon_state": "hypo2_accent2", + "blend_mode": "overlay", + "color_ids": [ 2 ] + } + ], + "hypo2_tactical": [ + { + "type": "icon_state", + "icon_state": "hypo2_tactical", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "hypo2_accent1", + "blend_mode": "overlay", + "color_ids": [ 1 ] + }, + { + "type": "icon_state", + "icon_state": "hypo2_accent2", + "blend_mode": "overlay", + "color_ids": [ 2 ] + } + ] +} diff --git a/modular_skyrat/modules/GAGS/json_configs/items/hypovial.json b/modular_skyrat/modules/GAGS/json_configs/items/hypovial.json new file mode 100644 index 00000000000..99492b822e2 --- /dev/null +++ b/modular_skyrat/modules/GAGS/json_configs/items/hypovial.json @@ -0,0 +1,28 @@ +{ + "hypovial": [ + { + "type": "icon_state", + "icon_state": "hypovial-glass", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "hypovial-body", + "blend_mode": "overlay", + "color_ids": [ 1 ] + } + ], + "hypoviallarge": [ + { + "type": "icon_state", + "icon_state": "hypoviallarge-glass", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "hypoviallarge-body", + "blend_mode": "overlay", + "color_ids": [ 1 ] + } + ] +} diff --git a/modular_skyrat/modules/company_imports/code/armament_datums/deforest_medical.dm b/modular_skyrat/modules/company_imports/code/armament_datums/deforest_medical.dm index 1dd538cc6d1..e10f73a8c17 100644 --- a/modular_skyrat/modules/company_imports/code/armament_datums/deforest_medical.dm +++ b/modular_skyrat/modules/company_imports/code/armament_datums/deforest_medical.dm @@ -194,6 +194,10 @@ /datum/armament_entry/company_import/deforest/equipment/medigun_upgrade item_type = /obj/item/device/custom_kit/medigun_fastcharge cost = PAYCHECK_COMMAND * 2 + +/datum/armament_entry/company_import/deforest/equipment/hypospray_upgrade + item_type = /obj/item/device/custom_kit/deluxe_hypo2 + cost = PAYCHECK_COMMAND * 2 /datum/armament_entry/company_import/deforest/equipment/afad item_type = /obj/item/gun/medbeam/afad diff --git a/modular_skyrat/modules/hyposprays/code/autolathe_designs.dm b/modular_skyrat/modules/hyposprays/code/autolathe_designs.dm index 6933041c786..babae7961fa 100644 --- a/modular_skyrat/modules/hyposprays/code/autolathe_designs.dm +++ b/modular_skyrat/modules/hyposprays/code/autolathe_designs.dm @@ -1,21 +1,128 @@ -/datum/design/hypoviallarge - name = "Large Hypovial" - id = "large_hypovial" - build_type = AUTOLATHE | PROTOLATHE +/datum/design/hypovial + name = "Hypovial" + id = "hypovial" + build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE materials = list( - /datum/material/iron = SHEET_MATERIAL_AMOUNT, + /datum/material/glass = SHEET_MATERIAL_AMOUNT * 0.5, + /datum/material/iron = SHEET_MATERIAL_AMOUNT * 0.5, ) - build_path = /obj/item/reagent_containers/cup/vial/large + build_path = /obj/item/reagent_containers/cup/vial/small category = list( RND_CATEGORY_INITIAL, RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_MEDICAL, ) departmental_flags = DEPARTMENT_BITFLAG_MEDICAL +/datum/techweb_node/basic_medical/New() + design_ids += list( + "hypovial", + ) + return ..() + +/// Large hypovials +/datum/design/hypovial/large + name = "Large Hypovial" + id = "large_hypovial" + materials = list( + /datum/material/glass = SHEET_MATERIAL_AMOUNT, + /datum/material/iron = SHEET_MATERIAL_AMOUNT * 0.5, + ) + build_path = /obj/item/reagent_containers/cup/vial/large + +/datum/design/hypokit + name = "Hypospray Kit" + id = "hypokit" + build_type = PROTOLATHE | AWAY_LATHE + materials = list( + /datum/material/plastic = SHEET_MATERIAL_AMOUNT * 3, + /datum/material/iron = SHEET_MATERIAL_AMOUNT, + ) + build_path = /obj/item/storage/hypospraykit/empty + category = list( + RND_CATEGORY_INITIAL, + RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_MEDICAL, + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL + +/datum/techweb_node/biotech/New() + design_ids += list( + "large_hypovial", + "hypokit", + ) + return ..() + +/// Hyposprays +/datum/design/hypokit/deluxe + name = "Deluxe Hypospray Kit" + id = "hypokit_deluxe" + materials = list( + /datum/material/plastic = SHEET_MATERIAL_AMOUNT * 6, + /datum/material/iron = SHEET_MATERIAL_AMOUNT * 3, + /datum/material/silver = SHEET_MATERIAL_AMOUNT * 1, + ) + build_path = /obj/item/storage/hypospraykit/cmo/empty + +/datum/design/hypomkii + name = "MkII Hypospray" + id = "hypomkii" + build_type = PROTOLATHE | AWAY_LATHE + materials = list( + /datum/material/plastic = SHEET_MATERIAL_AMOUNT * 5, + /datum/material/glass = SHEET_MATERIAL_AMOUNT * 3, + /datum/material/silver = SHEET_MATERIAL_AMOUNT, + ) + build_path = /obj/item/hypospray/mkii + category = list( + RND_CATEGORY_INITIAL, + RND_CATEGORY_TOOLS + RND_SUBCATEGORY_TOOLS_MEDICAL, + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL + +/datum/techweb_node/adv_biotech/New() + design_ids += list( + "hypokit_deluxe", + "hypomkii", + ) + return ..() + +/datum/design/hypomkii/deluxe + name = "MkII Hypospray Upgrade Kit" + id = "hypomkii_deluxe" + build_type = PROTOLATHE | AWAY_LATHE + materials = list( + /datum/material/plastic = SHEET_MATERIAL_AMOUNT * 8, + /datum/material/glass = SHEET_MATERIAL_AMOUNT * 4, + /datum/material/silver = SHEET_MATERIAL_AMOUNT * 2, + ) + build_path = /obj/item/device/custom_kit/deluxe_hypo2 + category = list( + RND_CATEGORY_INITIAL, + RND_CATEGORY_TOOLS + RND_SUBCATEGORY_TOOLS_MEDICAL, + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL + +/datum/techweb_node/alien_bio/New() + design_ids += list( + "hypomkii_deluxe", + ) + return ..() + +// Tarkon and similar get enough to work with, but if they want deluxe kits/hypos they still need to trade with the station for 'em. +/datum/techweb_node/oldstation_surgery/New() + design_ids += list( + "hypokit", + "hypomkii", + ) + return ..() + + + +/// For reasons unknown, pens are included as an autolathe design here, in the hypospray module of all places. +/// I'm not touching this unless a maint asks me to because it feels weird and haunted, like the picture of a potato that bricks Source if you remove it. /datum/design/pen name = "Pen" id = "pen" - build_type = AUTOLATHE + build_type = AUTOLATHE | AWAY_LATHE materials = list( /datum/material/iron = SMALL_MATERIAL_AMOUNT, /datum/material/glass = SMALL_MATERIAL_AMOUNT, @@ -24,4 +131,4 @@ category = list( RND_CATEGORY_INITIAL, RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_MISC, - ) + ) \ No newline at end of file diff --git a/modular_skyrat/modules/hyposprays/code/hypospray_kits.dm b/modular_skyrat/modules/hyposprays/code/hypospray_kits.dm index 25565fc7e57..487e3aa3184 100644 --- a/modular_skyrat/modules/hyposprays/code/hypospray_kits.dm +++ b/modular_skyrat/modules/hyposprays/code/hypospray_kits.dm @@ -1,31 +1,41 @@ /obj/item/storage/hypospraykit name = "hypospray kit" - desc = "It's a kit containing a hypospray and specific treatment chemical-filled vials." + desc = "A hypospray kit with foam insets for hypovials and a mounting point on the bottom." icon = 'modular_skyrat/modules/hyposprays/icons/hypokits.dmi' icon_state = "firstaid-mini" worn_icon_state = "healthanalyzer" // Get a better sprite later inhand_icon_state = "medkit" + greyscale_config = /datum/greyscale_config/hypokit lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' + // Small hypokits can be pocketed, but don't have much storage. + w_class = WEIGHT_CLASS_SMALL slot_flags = ITEM_SLOT_BELT throw_speed = 3 throw_range = 7 var/empty = FALSE var/current_case = "firstaid" var/static/list/case_designs - var/static/list/cmo_case_designs - var/cmo_case = FALSE + var/static/list/case_designs_xl + var/is_xl = FALSE + + /// Tracks if a hypospray is attached to the case or not. + var/obj/item/hypospray/mkii/attached_hypo //Code to give hypospray kits selectable paterns. /obj/item/storage/hypospraykit/examine(mob/living/user) . = ..() . += span_notice("Ctrl-Shift-Click to reskin this") + if(attached_hypo) + . += span_notice("[attached_hypo] is mounted on the bottom. Alt-Right-Click to take it off.") + else + . += span_notice("Right-Click with a hypospray to mount it.") /obj/item/storage/hypospraykit/Initialize(mapload) . = ..() if(!length(case_designs)) populate_case_designs() - atom_storage.max_slots = 12 + atom_storage.max_slots = 7 atom_storage.can_hold = typecacheof(list( /obj/item/hypospray/mkii, /obj/item/reagent_containers/cup/vial @@ -35,11 +45,26 @@ /obj/item/storage/hypospraykit/Destroy() + // a large block to stop the CI Gods smiting us & taking extra steps to try and force the CMO hypo to drop smartly + var/atom/drop_loc = drop_location(src) + if(QDELETED(drop_loc)) + drop_loc = get_turf(src) + if(QDELETED(drop_loc)) + QDEL_NULL(attached_hypo) + return ..() + // so long as it found a place to drop, run through and try to drop any indestructible items we contain for(var/obj/item in contents) - if(QDELING(loc)) - if(item.resistance_flags & INDESTRUCTIBLE) // Because we're not supposed to delete stuff that are indestructible, but I'm too lazy to do something more complex upstream now. Later, maybe. - item.forceMove(get_turf(src)) - . = ..() + if(item.resistance_flags & INDESTRUCTIBLE) + atom_storage.remove_single(null, item, drop_loc, TRUE) + // this also includes attached hypos - if indestructible, shunt it out, otherwise qdel it, and in all cases make sure we drop the ref & unregister the signal + if(attached_hypo) + if(attached_hypo.resistance_flags & INDESTRUCTIBLE) + attached_hypo.forceMove(drop_loc) + else + QDEL_NULL(attached_hypo) + attached_hypo = null + UnregisterSignal(attached_hypo, COMSIG_QDELETING) + return ..() /obj/item/storage/hypospraykit/proc/populate_case_designs() @@ -48,12 +73,65 @@ "brute" = image(icon = src.icon, icon_state = "brute-mini"), "burn" = image(icon = src.icon, icon_state = "burn-mini"), "toxin" = image(icon = src.icon, icon_state = "toxin-mini"), - "rad" = image(icon = src.icon, icon_state = "rad-mini"), - "purple" = image(icon = src.icon, icon_state = "purple-mini"), - "oxy" = image(icon = src.icon, icon_state = "oxy-mini")) - cmo_case_designs = list( - "tactical" = image(icon= src.icon, icon_state = "tactical-mini")) - cmo_case_designs += case_designs + "oxy" = image(icon = src.icon, icon_state = "oxy-mini"), + "advanced" = image(icon = src.icon, icon_state = "advanced-mini"), + "buffs" = image(icon = src.icon, icon_state = "buffs-mini"), + "custom" = image(icon = src.icon, icon_state = "standard-gags-mini")) + case_designs_xl = list( + "cmo" = image(icon = src.icon, icon_state = "cmo-mini"), + "emt" = image(icon = src.icon, icon_state = "emt-mini"), + "tactical" = image(icon = src.icon, icon_state = "tactical-mini"), + "deluxe-custom" = image(icon = src.icon, icon_state = "deluxe-gags-normal-mini"), + "tactical-custom" = image(icon = src.icon, icon_state = "deluxe-gags-tactical-mini")) + +/obj/item/storage/hypospraykit/update_overlays() + . = ..() + if(attached_hypo) + if(attached_hypo.greyscale_colors != null) //it's one of the GAGS variants + var/mutable_appearance/hypo_overlay = mutable_appearance(initial(icon), attached_hypo.icon_state) + . += hypo_overlay + var/list/split_colors = splittext(attached_hypo.greyscale_colors, "#") + var/mutable_appearance/hypo_overlay_acc1 = mutable_appearance(initial(icon), "hypo2_accent1") + hypo_overlay_acc1.color = "#[split_colors[2]]" + . += hypo_overlay_acc1 + var/mutable_appearance/hypo_overlay_acc2 = mutable_appearance(initial(icon), "hypo2_accent2") + hypo_overlay_acc2.color = "#[split_colors[3]]" + . += hypo_overlay_acc2 + else + var/mutable_appearance/hypo_overlay = mutable_appearance(initial(icon), attached_hypo.icon_state) + . += hypo_overlay + +/obj/item/storage/hypospraykit/attackby_secondary(obj/item/weapon, mob/user, params) + if(istype(weapon, /obj/item/hypospray/mkii)) + if(attached_hypo != null) + balloon_alert(user, "Mount point full! Remove [attached_hypo] first!") + else + weapon.moveToNullspace() + attached_hypo = weapon + RegisterSignal(weapon, COMSIG_QDELETING, PROC_REF(on_attached_hypo_qdel)) + balloon_alert(user, "Attached [attached_hypo].") + update_appearance() + // This stops atom_storage from hogging your right-click and opening the inventory. + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + return ..() + +/obj/item/storage/hypospraykit/alt_click_secondary(mob/user) + if(attached_hypo != null) + if(user.put_in_hands(attached_hypo)) + balloon_alert(user, "Removed [attached_hypo].") + UnregisterSignal(attached_hypo, COMSIG_QDELETING) + attached_hypo = null + update_appearance() + // Ditto here. + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + else + balloon_alert(user, "Couldn't pull the hypo!") + return ..() + +/obj/item/storage/hypospraykit/proc/on_attached_hypo_qdel() + if(attached_hypo) + attached_hypo = null + update_appearance() /obj/item/storage/hypospraykit/update_icon_state() . = ..() @@ -62,14 +140,27 @@ /obj/item/storage/hypospraykit/proc/case_menu(mob/user) if(.) return - var/casetype = cmo_case_designs - if(!src.cmo_case) - casetype = case_designs - var/choice = show_radial_menu(user, src , casetype, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 42, require_near = TRUE) + var/list/designs = case_designs + if(is_xl) + designs = case_designs_xl + var/choice = show_radial_menu(user, src, designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 42, require_near = TRUE) if(!choice) return FALSE current_case = choice update_icon() + if(findtext(current_case, "custom")) + var/atom/fake_atom = src + var/list/allowed_configs = list() + var/config = initial(fake_atom.greyscale_config) + allowed_configs += "[config]" + if(greyscale_colors == null) + greyscale_colors = "#00AAFF" + + var/datum/greyscale_modify_menu/menu = new(src, usr, allowed_configs) + menu.ui_interact(usr) + else //restore normal icon + icon = initial(icon) + greyscale_colors = null /obj/item/storage/hypospraykit/proc/check_menu(mob/user) if(!istype(user)) @@ -84,40 +175,95 @@ //END OF HYPOSPRAY CASE MENU CODE -/obj/item/storage/hypospraykit/empty - desc = "A hypospray kit with general use vials." - -/obj/item/storage/hypospraykit/empty/PopulateContents() +/obj/item/storage/hypospraykit/PopulateContents() if(empty) return new /obj/item/hypospray/mkii(src) +/obj/item/storage/hypospraykit/empty + empty = TRUE + +/// Deluxe hypokit: more storage, but you can't pocket it. /obj/item/storage/hypospraykit/cmo name = "deluxe hypospray kit" - desc = "A kit containing a deluxe hypospray and vials." - icon_state = "tactical-mini" - inhand_icon_state = "medkit-tactical" - current_case = "tactical" - cmo_case = TRUE + desc = "An extended hypospray kit with foam insets for hypovials & a mounting point on the bottom." + icon_state = "cmo-mini" + current_case = "cmo" + is_xl = TRUE + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/storage/hypospraykit/cmo/Initialize(mapload) + . = ..() + atom_storage.max_slots = 21 + atom_storage.max_total_storage = 28 //keeps a wiggle room of 7 just in case size weirdness happens /obj/item/storage/hypospraykit/cmo/PopulateContents() if(empty) return - new /obj/item/hypospray/mkii/cmo(src) + new /obj/item/hypospray/mkii/deluxe/cmo(src) + +/obj/item/storage/hypospraykit/cmo/empty + desc = "An extended hypospray kit with foam insets for hypovials & a mounting point on the bottom." + icon_state = "emt-mini" + current_case = "emt" + empty = TRUE + +/// Preloaded version: this is what goes in the locker. +/obj/item/storage/hypospraykit/cmo/preloaded + name = "CMO's deluxe hypospray kit" + desc = "The CMO's precious extended hypospray kit, preloaded with a deluxe hypospray & a handful of vials. Retains the usual insets and mounting point of smaller hypokits." + +/obj/item/storage/hypospraykit/cmo/preloaded/PopulateContents() + if(empty) + return + new /obj/item/hypospray/mkii/deluxe/cmo(src) + new /obj/item/reagent_containers/cup/vial/large/deluxe(src) new /obj/item/reagent_containers/cup/vial/large/multiver(src) new /obj/item/reagent_containers/cup/vial/large/salglu(src) new /obj/item/reagent_containers/cup/vial/large/synthflesh(src) +/// Combat hypokit +/obj/item/storage/hypospraykit/cmo/combat + name = "combat hypospray kit" + desc = "A larger tactical hypospray kit containing a combat-focused deluxe hypospray and vials." + icon_state = "tactical-mini" + current_case = "tactical" + +/obj/item/storage/hypospraykit/cmo/combat/PopulateContents() + if(empty) + return + new /obj/item/hypospray/mkii/deluxe/cmo/combat(src) + new /obj/item/reagent_containers/cup/vial/large/advbrute(src) + new /obj/item/reagent_containers/cup/vial/large/advburn(src) + new /obj/item/reagent_containers/cup/vial/large/advtox(src) + new /obj/item/reagent_containers/cup/vial/large/advoxy(src) + new /obj/item/reagent_containers/cup/vial/large/advcrit(src) + new /obj/item/reagent_containers/cup/vial/large/advomni(src) + new /obj/item/reagent_containers/cup/vial/large/numbing(src) + +/// Boxes of empty hypovials, coming in every style. /obj/item/storage/box/vials name = "box of hypovials" /obj/item/storage/box/vials/PopulateContents() - for(var/i in 1 to 7) - new /obj/item/reagent_containers/cup/vial/small( src ) + for(var/vialpath in subtypesof(/obj/item/reagent_containers/cup/vial/small/style)) + new vialpath(src) +// Ditto, just large vials. +/obj/item/storage/box/vials/deluxe + name = "box of deluxe hypovials" + +/obj/item/storage/box/vials/deluxe/PopulateContents() + for(var/vialpath in subtypesof(/obj/item/reagent_containers/cup/vial/large/style)) + new vialpath(src) + +// A box of small hypospray kits, pre-skinned to each variant to remind people what styles are available. /obj/item/storage/box/hypospray name = "box of hypospray kits" /obj/item/storage/box/hypospray/PopulateContents() - for(var/i in 1 to 4) - new /obj/item/storage/hypospraykit/empty(src) + var/list/case_designs = list("firstaid", "brute", "burn", "toxin", "oxy", "advanced", "buffs") + for(var/label in case_designs) + var/obj/item/storage/hypospraykit/newkit = new /obj/item/storage/hypospraykit(src) + newkit.current_case = label + newkit.update_icon_state() \ No newline at end of file diff --git a/modular_skyrat/modules/hyposprays/code/hyposprays_II.dm b/modular_skyrat/modules/hyposprays/code/hyposprays_II.dm index 51cc9c71472..964da32eb80 100644 --- a/modular_skyrat/modules/hyposprays/code/hyposprays_II.dm +++ b/modular_skyrat/modules/hyposprays/code/hyposprays_II.dm @@ -20,18 +20,18 @@ name = "hypospray mk.II" icon_state = "hypo2" icon = 'modular_skyrat/modules/hyposprays/icons/hyposprays.dmi' - desc = "A new development from DeForest Medical, this hypospray takes 60-unit vials as the drug supply for easy swapping." + greyscale_config = /datum/greyscale_config/hypospray_mkii + desc = "A new development from DeForest Medical, this hypospray takes 50-unit vials as the drug supply for easy swapping." w_class = WEIGHT_CLASS_TINY var/list/allowed_containers = list(/obj/item/reagent_containers/cup/vial/small) /// Is the hypospray only able to use small vials. Relates to the loaded overlays var/small_only = TRUE /// Inject or spray? var/mode = HYPO_INJECT + /// The presently-inserted vial. var/obj/item/reagent_containers/cup/vial/vial /// If the Hypospray starts with a vial, which vial does it start with? var/start_vial - /// Does the Hypospray start with a vial? - var/spawnwithvial = FALSE /// Time taken to inject others var/inject_wait = WAIT_INJECT @@ -46,54 +46,86 @@ var/quickload = TRUE /// Does it penetrate clothing? var/penetrates = null + /// Used for GAGS-ified hypos. + var/gags_bodystate = "hypo2_normal" -/obj/item/hypospray/mkii/cmo +/obj/item/hypospray/mkii/deluxe name = "hypospray mk.II deluxe" allowed_containers = list(/obj/item/reagent_containers/cup/vial/small, /obj/item/reagent_containers/cup/vial/large) - icon_state = "cmo2" - desc = "The deluxe hypospray can take larger 120-unit vials. It also acts faster and can deliver more reagents per spray." - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - start_vial = /obj/item/reagent_containers/cup/vial/large/deluxe - spawnwithvial = TRUE + icon_state = "bighypo2" + gags_bodystate = "hypo2_deluxe" + desc = "The deluxe DeForest Mk. II hypospray, able to take both 100u and 50u vials." small_only = FALSE + +// Deluxe hypo upgrade Kit +/obj/item/device/custom_kit/deluxe_hypo2 + name = "DeForest Mk. II Hypospray Deluxe Bodykit" + desc = "Upgrades the DeForest Mk. II Hypospray to support larger vials." + // don't tinker with a loaded (medi)gun. fool + from_obj = /obj/item/hypospray/mkii + to_obj = /obj/item/hypospray/mkii/deluxe + +/obj/item/device/custom_kit/deluxe_hypo2/pre_convert_check(obj/target_obj, mob/user) + var/obj/item/hypospray/mkii/our_hypo = target_obj + if(our_hypo.type in subtypesof(/obj/item/hypospray/mkii/)) + balloon_alert(user, "only works on basic mk. ii hypos!") + return FALSE + if(our_hypo.vial != null) + balloon_alert(user, "unload the vial first!") + return FALSE + return TRUE + +/obj/item/hypospray/mkii/deluxe/cmo + name = "CMO's deluxe hypospray mk.II" + icon_state = "cmo2" + gags_bodystate = "hypo2_cmo" + desc = "The CMO's prized deluxe hypospray, able to take both 100u and 50u vials, acting faster and able to deliver more reagents per spray." + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF inject_wait = DELUXE_WAIT_INJECT spray_wait = DELUXE_WAIT_SPRAY spray_self = DELUXE_SELF_SPRAY inject_self = DELUXE_SELF_INJECT penetrates = INJECT_CHECK_PENETRATE_THICK +/obj/item/hypospray/mkii/deluxe/cmo/combat + name = "combat-grade hypospray mk.II" + icon_state = "combat2" + gags_bodystate = "hypo2_tactical" + desc = "A variant of the deluxe hypospray, able to take both 100u and 50u vials, with overcharged applicators and an armor-piercing tip." + // Made non-indestructible since this is typically an admin spawn. still robust though! + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + inject_wait = COMBAT_WAIT_INJECT + spray_wait = COMBAT_WAIT_SPRAY + spray_self = COMBAT_SELF_INJECT + inject_self = COMBAT_SELF_SPRAY + penetrates = INJECT_CHECK_PENETRATE_THICK + /obj/item/hypospray/mkii/Initialize(mapload) . = ..() - if(!spawnwithvial) - update_appearance() - return - if(start_vial) - vial = new start_vial - update_appearance() - AddElement(/datum/element/update_icon_updates_onmob) /obj/item/hypospray/mkii/update_overlays() . = ..() if(!vial) return - if(!vial.reagents.total_volume) - return - var/vial_spritetype = "chem-color" - if(!small_only) - vial_spritetype += "[vial.type_suffix]" + if(vial.reagents.total_volume) + var/vial_spritetype = "chem-color" + if(!small_only) + vial_spritetype += "[vial.type_suffix]" + else + vial_spritetype += "-s" + var/mutable_appearance/chem_loaded = mutable_appearance(initial(icon), vial_spritetype) + chem_loaded.color = vial.chem_color + . += chem_loaded + if(vial.greyscale_colors != null) + var/mutable_appearance/vial_overlay = mutable_appearance(initial(icon), "[vial.icon_state]-body") + vial_overlay.color = vial.greyscale_colors + . += vial_overlay + var/mutable_appearance/vial_overlay_glass = mutable_appearance(initial(icon), "[vial.icon_state]-glass") + . += vial_overlay_glass else - vial_spritetype += "-s" - var/mutable_appearance/chem_loaded = mutable_appearance('modular_skyrat/modules/hyposprays/icons/hyposprays.dmi', vial_spritetype) - chem_loaded.color = vial.chem_color - . += chem_loaded - -/obj/item/hypospray/mkii/update_icon_state() - . = ..() - var/icon_suffix = "-s" - if(!small_only && vial) - icon_suffix = vial.type_suffix //Sets the suffix used to the correspoding vial. - icon_state = "[initial(icon_state)][vial ? "[icon_suffix]" : ""]" + var/mutable_appearance/vial_overlay = mutable_appearance(initial(icon), vial.icon_state) + . += vial_overlay /obj/item/hypospray/mkii/examine(mob/user) . = ..() @@ -101,6 +133,26 @@ . += "[vial] has [vial.reagents.total_volume]u remaining." else . += "It has no vial loaded in." + . += span_notice("Ctrl-Shift-Click to change up the colors or reset them.") + +/obj/item/hypospray/mkii/CtrlShiftClick(mob/user, obj/item/I) + var/choice = tgui_input_list(user, "GAGSify the hypo or reset to default?", "Fashion", list("GAGS", "Nope")) + if(choice == "GAGS") + icon_state = gags_bodystate + //choices go here + var/atom/fake_atom = src + var/list/allowed_configs = list() + var/config = initial(fake_atom.greyscale_config) + allowed_configs += "[config]" + if(greyscale_colors == null) + greyscale_colors = "#00AAFF#FFAA00" + + var/datum/greyscale_modify_menu/menu = new(src, usr, allowed_configs) + menu.ui_interact(usr) + else + icon_state = initial(icon_state) + icon = initial(icon) + greyscale_colors = null /obj/item/hypospray/mkii/proc/unload_hypo(obj/item/hypo, mob/user) if((istype(hypo, /obj/item/reagent_containers/cup/vial))) @@ -218,13 +270,13 @@ if(injectee != user) injectee.visible_message(span_danger("[user] is trying to [fp_verb] [injectee] with [src]!"), \ span_userdanger("[user] is trying to [fp_verb] you with [src]!")) - + var/selected_wait_time if(target == user) selected_wait_time = (mode == HYPO_INJECT) ? inject_self : spray_self else selected_wait_time = (mode == HYPO_INJECT) ? inject_wait : spray_wait - + if(!do_after(user, selected_wait_time, injectee, extra_checks = CALLBACK(injectee, /mob/living/proc/can_inject, user, user.zone_selected, penetrates))) return if(!vial.reagents.total_volume) @@ -279,4 +331,4 @@ #undef COMBAT_WAIT_SPRAY #undef COMBAT_WAIT_INJECT #undef COMBAT_SELF_SPRAY -#undef COMBAT_SELF_INJECT +#undef COMBAT_SELF_INJECT \ No newline at end of file diff --git a/modular_skyrat/modules/hyposprays/code/hypovials.dm b/modular_skyrat/modules/hyposprays/code/hypovials.dm index a4781bfea8f..4e272ca22ff 100644 --- a/modular_skyrat/modules/hyposprays/code/hypovials.dm +++ b/modular_skyrat/modules/hyposprays/code/hypovials.dm @@ -3,37 +3,74 @@ desc = "You probably shouldn't be seeing this. Shout at a coder." icon = 'modular_skyrat/modules/hyposprays/icons/vials.dmi' icon_state = "hypovial" + greyscale_config = /datum/greyscale_config/hypovial + fill_icon_state = "hypovial_fill" spillable = FALSE volume = 10 - /// The suffix of the overlay texture that the hypovial uses when loading textures. - var/type_suffix = "-s" possible_transfer_amounts = list(1,2,5,10) fill_icon_thresholds = list(10, 25, 50, 75, 100) - var/chem_color //Used for hypospray overlay + var/chem_color = "#FFFFFF" //Used for hypospray overlay + var/type_suffix = "-s" + fill_icon = 'modular_skyrat/modules/hyposprays/icons/hypospray_fillings.dmi' + current_skin = "hypovial" + + unique_reskin = list( + "Sterile" = "hypovial", + "Generic" = "hypovial-generic", + "Brute" = "hypovial-brute", + "Burn" = "hypovial-burn", + "Toxin" = "hypovial-tox", + "Oxyloss" = "hypovial-oxy", + "Crit" = "hypovial-crit", + "Buff" = "hypovial-buff", + "Custom" = "hypovial-custom", + ) + +/obj/item/reagent_containers/cup/vial/Initialize(mapload) + . = ..() + RegisterSignal(src, COMSIG_OBJ_RESKIN, PROC_REF(on_reskin)) + +/obj/item/reagent_containers/cup/vial/Destroy(force) + . = ..() + UnregisterSignal(src, COMSIG_OBJ_RESKIN) + +/obj/item/reagent_containers/cup/vial/examine(mob/user) + . = ..() + . += span_notice("Ctrl-Shift-Click to reskin or set a custom color.") + +/obj/item/reagent_containers/cup/vial/CtrlShiftClick(mob/user, obj/item/I) + current_skin = null + icon_state = initial(icon_state) + icon = initial(icon) + greyscale_colors = null + reskin_obj(user) + +/obj/item/reagent_containers/cup/vial/proc/on_reskin() + if(current_skin == "Custom") + icon_state = unique_reskin["Sterile"] + current_skin = unique_reskin["Sterile"] + var/atom/fake_atom = src + var/list/allowed_configs = list() + var/config = initial(fake_atom.greyscale_config) + allowed_configs += "[config]" + if(greyscale_colors == null) + greyscale_colors = "#FFFF00" + var/datum/greyscale_modify_menu/menu = new(src, usr, allowed_configs) + menu.ui_interact(usr) + else + icon_state = unique_reskin[current_skin] /obj/item/reagent_containers/cup/vial/update_overlays() . = ..() - if(!fill_icon_thresholds) - return - if(reagents.total_volume) - var/fill_name = fill_icon_state? fill_icon_state : icon_state - var/fill_overlay = 10 - switch(round((reagents.total_volume / volume)*100)) - if(1 to 24) - fill_overlay = 10 - if(25 to 49) - fill_overlay = 25 - if(50 to 74) - fill_overlay = 50 - if(75 to 89) - fill_overlay = 75 - if(89 to 100) - fill_overlay = 100 - var/mutable_appearance/filling = mutable_appearance('modular_skyrat/modules/hyposprays/icons/hypospray_fillings.dmi', "[fill_name][fill_overlay]") - - filling.color = mix_color_from_reagents(reagents.reagent_list) - chem_color = filling.color - . += filling + // Search the overlays for the fill overlay from reagent_containers, and nudge its layer down to have it look correct. + chem_color = "#FFFFFF" + var/list/generated_overlays = . + for(var/added_overlay in generated_overlays) + if(istype(added_overlay, /mutable_appearance)) + var/mutable_appearance/overlay_image = added_overlay + if(findtext(overlay_image.icon_state, fill_icon_state) != 0) + overlay_image.layer = layer - 0.01 + chem_color = overlay_image.color /obj/item/reagent_containers/cup/vial/Initialize(mapload) . = ..() @@ -49,28 +86,120 @@ volume = 50 possible_transfer_amounts = list(1,2,5,10,15,25,50) +/obj/item/reagent_containers/cup/vial/small/style + icon_state = "hypovial" + +//Styles +/obj/item/reagent_containers/cup/vial/small/style/generic + icon_state = "hypovial-generic" +/obj/item/reagent_containers/cup/vial/small/style/brute + icon_state = "hypovial-brute" +/obj/item/reagent_containers/cup/vial/small/style/burn + icon_state = "hypovial-burn" +/obj/item/reagent_containers/cup/vial/small/style/toxin + icon_state = "hypovial-tox" +/obj/item/reagent_containers/cup/vial/small/style/oxy + icon_state = "hypovial-oxy" +/obj/item/reagent_containers/cup/vial/small/style/crit + icon_state = "hypovial-crit" +/obj/item/reagent_containers/cup/vial/small/style/buff + icon_state = "hypovial-buff" + //Fit in CMO hypo only /obj/item/reagent_containers/cup/vial/large name = "large hypovial" icon_state = "hypoviallarge" + fill_icon_state = "hypoviallarge_fill" + current_skin = "hypoviallarge" desc = "A large, 100u capacity vial that fits only in the most deluxe hyposprays." volume = 100 - type_suffix = "-l" possible_transfer_amounts = list(1,2,5,10,20,30,40,50,100) + type_suffix = "-l" + + unique_reskin = list( + "Sterile" = "hypoviallarge", + "Generic" = "hypoviallarge-generic", + "Brute" = "hypoviallarge-brute", + "Burn" = "hypoviallarge-burn", + "Toxin" = "hypoviallarge-tox", + "Oxyloss" = "hypoviallarge-oxy", + "Crit" = "hypoviallarge-crit", + "Buff" = "hypoviallarge-buff", + "Custom" = "hypoviallarge-custom", + ) + +/obj/item/reagent_containers/cup/vial/large/style/ + icon_state = "hypoviallarge" + +//Styles +/obj/item/reagent_containers/cup/vial/large/style/generic + icon_state = "hypoviallarge-generic" +/obj/item/reagent_containers/cup/vial/large/style/brute + icon_state = "hypoviallarge-brute" +/obj/item/reagent_containers/cup/vial/large/style/burn + icon_state = "hypoviallarge-burn" +/obj/item/reagent_containers/cup/vial/large/style/toxin + icon_state = "hypoviallarge-tox" +/obj/item/reagent_containers/cup/vial/large/style/oxy + icon_state = "hypoviallarge-oxy" +/obj/item/reagent_containers/cup/vial/large/style/crit + icon_state = "hypoviallarge-crit" +/obj/item/reagent_containers/cup/vial/large/style/buff + icon_state = "hypoviallarge-buff" //Hypos that are in the CMO's kit round start /obj/item/reagent_containers/cup/vial/large/deluxe name = "deluxe hypovial" + icon_state = "hypoviallarge-buff" list_reagents = list(/datum/reagent/medicine/omnizine = 15, /datum/reagent/medicine/leporazine = 15, /datum/reagent/medicine/atropine = 15) /obj/item/reagent_containers/cup/vial/large/salglu name = "large green hypovial (salglu)" + icon_state = "hypoviallarge-oxy" list_reagents = list(/datum/reagent/medicine/salglu_solution = 50) /obj/item/reagent_containers/cup/vial/large/synthflesh name = "large orange hypovial (synthflesh)" + icon_state = "hypoviallarge-crit" list_reagents = list(/datum/reagent/medicine/c2/synthflesh = 50) /obj/item/reagent_containers/cup/vial/large/multiver name = "large black hypovial (multiver)" + icon_state = "hypoviallarge-tox" list_reagents = list(/datum/reagent/medicine/c2/multiver = 50) + +//Some bespoke helper types for preloaded combat medkits. +/obj/item/reagent_containers/cup/vial/large/advbrute + name = "Brute Heal" + icon_state = "hypoviallarge-brute" + list_reagents = list(/datum/reagent/medicine/c2/libital = 50, /datum/reagent/medicine/sal_acid = 50) + +/obj/item/reagent_containers/cup/vial/large/advburn + name = "Burn Heal" + icon_state = "hypoviallarge-burn" + list_reagents = list(/datum/reagent/medicine/c2/aiuri = 50, /datum/reagent/medicine/oxandrolone = 50) + +/obj/item/reagent_containers/cup/vial/large/advtox + name = "Toxin Heal" + icon_state = "hypoviallarge-tox" + list_reagents = list(/datum/reagent/medicine/pen_acid = 100) + +/obj/item/reagent_containers/cup/vial/large/advoxy + name = "Oxy Heal" + icon_state = "hypoviallarge-oxy" + list_reagents = list(/datum/reagent/medicine/c2/tirimol = 50, /datum/reagent/medicine/salbutamol = 50) + +/obj/item/reagent_containers/cup/vial/large/advcrit + name = "Crit Heal" + icon_state = "hypoviallarge-crit" + list_reagents = list(/datum/reagent/medicine/atropine = 100) + +/obj/item/reagent_containers/cup/vial/large/advomni + name = "All-Heal" + icon_state = "hypoviallarge-buff" + list_reagents = list(/datum/reagent/medicine/regen_jelly = 100) + +/obj/item/reagent_containers/cup/vial/large/numbing + name = "Numbing" + icon_state = "hypoviallarge-generic" + list_reagents = list(/datum/reagent/medicine/mine_salve = 50, /datum/reagent/medicine/morphine = 50) \ No newline at end of file diff --git a/modular_skyrat/modules/hyposprays/code/vending_hypospray.dm b/modular_skyrat/modules/hyposprays/code/vending_hypospray.dm index 3441c2175c6..27802b91895 100644 --- a/modular_skyrat/modules/hyposprays/code/vending_hypospray.dm +++ b/modular_skyrat/modules/hyposprays/code/vending_hypospray.dm @@ -1,3 +1,3 @@ /obj/machinery/vending/drugs/Initialize(mapload) - products[/obj/item/storage/hypospraykit/empty] = 5 + products[/obj/item/storage/hypospraykit] = 5 . = ..() diff --git a/modular_skyrat/modules/hyposprays/icons/hypokits.dmi b/modular_skyrat/modules/hyposprays/icons/hypokits.dmi index 08eae13582170cd66c0e4f9d23c3a6ee0777abb0..e2040d04047ced4d87676bd30d5963c2e481233c 100644 GIT binary patch literal 1835 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 KMNUMnLSTZB`N&@Y diff --git a/modular_skyrat/modules/hyposprays/icons/hypospray_fillings.dmi b/modular_skyrat/modules/hyposprays/icons/hypospray_fillings.dmi index 901a9fd4c45cc9f8c04340ba406a39df3e259bd5..cc0dbd1deb4ce4d5b89c4ebf2fa0a4641d4daccb 100644 GIT binary patch literal 671 zcmeAS@N?(olHy`uVBq!ia0vp^4M3d0!3HF+R#kZdsby6m5hX6E#mPmP1tppJc?=8{ zbArPPib}tK2`>2f^@*0ZuGYCT=Yuzd8eBAf@JQ#pkLJk$Ajj;{qca|7ea>q%7Ib+F zc?XH+h)yxm;{J5nNVxdXWuxLE9^NTCEP{=?yMQu0J1s7E2^9w`=Dc~;>*E=m@3rXX zGnHBS84i0`EjwLbu3Yj#aE`l@PglkNh~*V-9Sh$(D!yKGA25IY|5{e^ z#P^94H1+M*hj+YQ{fl?SG{ysx40Es(wc-02Rda6330{oq{%SEV{a=f4LebvuS6=fn zoKsbJ+$0!tmUF@s*@IVk81mk!K44ScaqEk4!Z*PfU3125x$pNLmstEn_(oKB?R{mX zq*@!h+Mm%e^&dkFZh!G^DB@u_$MAqy;!?YlvduKx&0knPM0~pw!vxXztj=u0tMbo( zxBr()c5#_=tHYWRB5{X#PT@l-pz9c>&tkZG{k zdjZDha#$SkA)Zb5gLI-Fg9USgF)A_pv)hGUzo%ivOu#_9vs3keV2w03b;b-kg3FoS of2pyN{r8>GMoiBH!>DTJBJ=4x4^0r(1*SL#Pgg&ebxsLQ0Lj1%Qvd(} literal 584 zcmeAS@N?(olHy`uVBq!ia0vp^4M3d0!3HF+R#kZdsijpR5hX6E#mPmP1tppJc?=8{ zbEcl($aTm-;MhmY%C5z8r!I+_v&o~YB;)?W1?fT^VR{Q6-L@_fQMviL>hOQ)Kl@s zlhd}X-JE=ofq^m3)5S5QV$Rz;8+i{o2rxMIoXF@nq|v~n(bLwU=%iE^U3Jr1SWuDu z^xRarY3CmoE_!YAn`wqOLm^X#u)`dNM=S#B4Hk??I2HULLU&TXu^POq-M_zX{rdCI zbD!AyfTSZCwQ~%c(v2B`>bJiZEoc$6nawG%J&WlF%e(uJLJDS!q8Wq5WQ35Vh*UZg z%*xwe^O;xw%is6E_fE%bsI#gY7^B}kxCyp;Z4~2Xw|f)aKIHsUP&&tj>FS^Q&C`A~ z8wkUEfUxK7C&vlOiMI_wt~TGo)brr60f>p}VX*S)|LYl!AM(Tqk@yFzOmzN*JADEs O4F*qFKbLh*2~7Y2k>ou9 diff --git a/modular_skyrat/modules/hyposprays/icons/hyposprays.dmi b/modular_skyrat/modules/hyposprays/icons/hyposprays.dmi index 2d6ecc535c5d42ac72fcf6806b4a7d255ca1e436..c753f5ef07a71fd71c04827cfeabd2bd25911340 100644 GIT binary patch literal 2612 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 b!WLFk{(iT1&!bH4l?Sl3cC@OoL|*<6!Y3$} literal 2227 zcmb7GdpHw%AK%y~uNX)ebk33DvbAuSdP8@jlJiG*WJipexl z6FR$9c+WCZNMc4Q!j9WkZrjeh?{l8>bl&sN`^WG1eSXjP`90su=ll8mewp5$hap;q zS^xk5;(o-HQ((_K*vQ$ZHj*l-Ku)wtesApT7G4d@kQ@P zpSY!tZ>D=C+g|Kog%2yzmt}#x7d=S@iyw+$9z+el|eKNG;9A55sQ~ zWgjdf-!Jm|jBfXArQUP~lrY7IWPNmA!^>Z_#V3BE)+Qw<9@XuL+K2+Q|Lh(l-^9$Q z5vxFLe6BUF61yhaYIchDq{5xX7OaZhE)uomEArm9qwKN)r!7ay+V8aw)jqaqj2Dp0 zOX|l+5oPxrd9PBdBgTjB%<)$*pAOerLty`?-#y5Nko|y*jHLBxX&zWgd~F*dBQw9S^6)QtUr#sqhf5dsBuvb zRq5o#sh*JDn?s2St*kWwAKdxyuw`K2M1w5h)rxM_f{rKN)46u(3%h5WfH1fGhta!8 zFgh)C`eid1WwE1U|J&k|l3T&If}gjxw!Uh@RxQmPoV%-!jIpIKmY?0|9_2RAuPWR6 z+9Wr`mU4_mnS0r=WTBDw$T;!R*0-_1%bA0(7lm(S_{m|=5WC*ENAcpgWs%@SiEV2t z*fy-sbo6ivsnjXPt3{il~*RZ}ys@*1Ab136E7RHmW0 zu&_gC(ufozwdK3;G~KDWom&4d z;nx^_#n69peOQLRWJI=1`__T6^7O(P<#COt;z;f^J&>vE;2gv22O&j-Ck7YxhpX85 zt;8*tPzDBrdokkL#IA_oYoF^zB3UeERR(swr6mB%7(2Jd*`0D=_RZ1l6fdp?Plm?v z10GDq%Im?AS;3Tt^;aLF8_c6m&6L{<&4_MODB+x)aYR;Jm$PAfvqDKy9NV0KjMc)o z7d^{z^8ByB2-q8~!z2R>1}Xucianr*T~GOOX>n%y{)E z$v&>db&=|SR7qJgPx>Xr=(?_P*AmM-O6X*01V;5HZ>S4(ua?keFT}c3Ln`FEkg;24 zA1K2TBp#^J8lu9O==NB>Uwz0w+h0Le9Q{@~6%XL;kI)SkvC}CU%Dn3idx<1=#$Xs8 zMRFWc9CC&d$FAJ zDqRM%i7IqP;mCgA;1GsF)l5k0o*QSf;O^S6XzakJ&43M1T<2WWI$sph+JTG$Y=>1~ zS)f2~_sYfX#s4qTZL*Zkjr{l^j$kIJahJpDeCEX&c*RP_APv7wDvR8a1)rikSv9;S zpwt^X*oQKjnqE7{Pm!!n*~*6%ARS5wO_qJ0Vxstdd`){>%2Oa)87`d;Q=I`o%XoZ{DP&rK9k%l)0lq#Qr^r8kiRtMV`fC-|E_b`H68SxUN5)+On>}FXJ6eZ zMENvsw+0Nf9Gy3t)j(*6MEcREA> diff --git a/modular_skyrat/modules/hyposprays/readme.md b/modular_skyrat/modules/hyposprays/readme.md index f90fe04d096..cb9a18bb299 100644 --- a/modular_skyrat/modules/hyposprays/readme.md +++ b/modular_skyrat/modules/hyposprays/readme.md @@ -1,4 +1,4 @@ -https://github.com/Skyrat-SS13/Skyrat-tg/pull/1929 +Original PR: https://github.com/Skyrat-SS13/Skyrat-tg/pull/1929 ## Title: Hyposprays @@ -11,7 +11,10 @@ Gives Medical back its Hyposprays. Oldbase stuff is good. ### TG Proc Changes: - CMO's locker now contains a new object, the CMO's Hypospray Kit. Old Hypospray has been commented out. -- Medical Doctor and Paramedic jobs now spawn with a standard Hypospray kit in backpack_contents +- Chemistry lockers now contain two boxes of Standard Hypospray Kits. +- Medical belts can carry the Mk II Hyposprays, vials, and Hypospray Kits. +- Adds CAT_HYPOS to ``code/_globalvars/lists/reagents.dm`` to allow vials to be printed from chem dispensers. +- Bluespace technicans get combat hypospray kits for QOL and testing. ### Defines: @@ -41,3 +44,4 @@ Gives Medical back its Hyposprays. Oldbase stuff is good. ### Credits: ShadeAware - Porting +CliffracerX - Tweaks & new sprites \ No newline at end of file diff --git a/modular_skyrat/modules/icspawning/code/observer.dm b/modular_skyrat/modules/icspawning/code/observer.dm index d8d5ef0065b..900a73746ab 100644 --- a/modular_skyrat/modules/icspawning/code/observer.dm +++ b/modular_skyrat/modules/icspawning/code/observer.dm @@ -36,6 +36,12 @@ if(!give_return) return + var/addquirks + if(character_option == "Selected Character") + addquirks = tgui_input_list(src, "Include quirks?", "Quirky", list("Quirks & Loadout", "Quirks Only", "Loadout Only", "Neither")) + if(!addquirks) + return + var/turf/current_turf = get_turf(user) var/mob/living/carbon/human/spawned_player = new(user) @@ -44,10 +50,20 @@ spawned_player.name = user.name spawned_player.real_name = user.real_name - var/mob/living/carbon/human/H = spawned_player - user.client?.prefs.safe_transfer_prefs_to(H) - H.dna.update_dna_identity() - + var/mob/living/carbon/human/player_as_human = spawned_player + user.client?.prefs.safe_transfer_prefs_to(player_as_human) + if(addquirks == "Quirks & Loadout" || addquirks == "Loadout Only") + if(dresscode == "Naked") + player_as_human.equip_outfit_and_loadout(new /datum/outfit(), user.client?.prefs) + else + player_as_human.equip_outfit_and_loadout(dresscode, user.client?.prefs) + else if(dresscode != "Naked") + spawned_player.equipOutfit(dresscode) + if(addquirks == "Quirks & Loadout" || addquirks == "Quirks Only") + SSquirks.AssignQuirks(player_as_human, user.client) + player_as_human.dna.update_dna_identity() + else if(dresscode != "Naked") + spawned_player.equipOutfit(dresscode) QDEL_IN(user, 1) if (teleport_option == "Bluespace") @@ -62,9 +78,6 @@ var/datum/action/cooldown/spell/return_back/return_spell = new(spawned_player) return_spell.Grant(spawned_player) - if(dresscode != "Naked") - spawned_player.equipOutfit(dresscode) - switch(teleport_option) if("Bluespace") spawned_player.forceMove(current_turf) @@ -91,8 +104,9 @@ var/list/paths = subtypesof(/datum/outfit) - typesof(/datum/outfit/job) - typesof(/datum/outfit/plasmaman) for(var/path in paths) - var/datum/outfit/O = path //not much to initalize here but whatever - outfits[initial(O.name)] = path + // Get the datum from the path so we can grab its name. + var/datum/outfit/path_as_outfit = path + outfits[initial(path_as_outfit.name)] = path var/dresscode = tgui_input_list(src, "Select outfit", "Robust quick dress shop", baseoutfits + sort_list(outfits)) @@ -128,11 +142,11 @@ if (dresscode == "Custom") var/list/custom_names = list() - for(var/datum/outfit/D in GLOB.custom_outfits) - custom_names[D.name] = D + for(var/datum/outfit/req_outfit in GLOB.custom_outfits) + custom_names[req_outfit.name] = req_outfit var/selected_name = input("Select outfit", "Robust quick dress shop") as null|anything in sort_list(custom_names) dresscode = custom_names[selected_name] if(isnull(dresscode)) return - return dresscode + return dresscode \ No newline at end of file diff --git a/modular_skyrat/modules/icspawning/code/standard.dm b/modular_skyrat/modules/icspawning/code/standard.dm index bd30194d2bf..3f15b6662a0 100644 --- a/modular_skyrat/modules/icspawning/code/standard.dm +++ b/modular_skyrat/modules/icspawning/code/standard.dm @@ -36,6 +36,16 @@ shoes = /obj/item/clothing/shoes/combat/debug id = /obj/item/card/id/advanced/debug/bst box = /obj/item/storage/box/debugtools + backpack_contents = list( + /obj/item/melee/energy/axe = 1, + /obj/item/storage/part_replacer/bluespace/tier4/bst = 1, + /obj/item/gun/magic/wand/resurrection/debug = 1, + /obj/item/gun/magic/wand/death/debug = 1, + /obj/item/debug/human_spawner = 1, + /obj/item/debug/omnitool = 1, + /obj/item/storage/box/stabilized = 1, + /obj/item/storage/hypospraykit/cmo/combat = 1, + ) /datum/outfit/admin/bst //Debug objs plus modsuit name = "Bluespace Tech (MODsuit)" @@ -44,3 +54,243 @@ shoes = /obj/item/clothing/shoes/combat/debug id = /obj/item/card/id/advanced/debug/bst box = /obj/item/storage/box/debugtools + backpack_contents = list( + /obj/item/melee/energy/axe = 1, + /obj/item/storage/part_replacer/bluespace/tier4/bst = 1, + /obj/item/gun/magic/wand/resurrection/debug = 1, + /obj/item/gun/magic/wand/death/debug = 1, + /obj/item/debug/human_spawner = 1, + /obj/item/debug/omnitool = 1, + /obj/item/storage/box/stabilized = 1, + /obj/item/storage/hypospraykit/cmo/combat = 1, + ) + +/obj/item/storage/part_replacer/bluespace/tier4/bst + name = "\improper Bluespace Tech RPED" + desc = "A specialized bluespace RPED for technicians that can manufacture stock parts on the fly. Alt-Right-Click to manufacture parts, change settings, or clear its internal storage." + /// Whether or not auto-clear is enabled + var/auto_clear = TRUE + /// List of valid types for pick_stock_part(). + var/static/list/valid_stock_part_types = list( + /obj/item/circuitboard/machine, + /obj/item/stock_parts, + /obj/item/reagent_containers/cup/beaker, + ) + +/obj/item/storage/part_replacer/bluespace/tier4/bst/Initialize(mapload) + . = ..() + atom_storage.max_slots = 1000 + atom_storage.max_total_storage = 20000 + +/// An extension to the default RPED part replacement action - if you don't have the requisite parts in the RPED already, it will spawn T4 versions to use. +/obj/item/storage/part_replacer/bluespace/tier4/bst/part_replace_action(obj/attacked_object, mob/living/user) + // We start with setting up a list of the current contents of the RPED when using auto-clear. This is used to detect new items after upgrades are applied & remove them. + var/list/old_contents = list() + var/list/inv_grab = atom_storage.return_inv(FALSE) + if(auto_clear) + old_contents = atom_storage.return_inv(FALSE) + // Once old_contents has been initialized, if needed, we check if the target object is a machine frame. + var/obj/structure/frame/attacked_frame = attacked_object + if(istype(attacked_frame, /obj/structure/frame/machine)) + var/obj/structure/frame/machine/machine_frame = attacked_frame + var/obj/item/circuitboard/machine/circuit = machine_frame.circuit + // Prioritize using the circuit's components list first, if present, to maintain consistency. + if(istype(circuit)) + spawn_parts_for_components(user, circuit.req_components) + else if(machine_frame.req_components) + spawn_parts_for_components(user, machine_frame.req_components) + else + // It's not a machine frame, so let's check if it's a regular machine. + if(ismachinery(attacked_object) && !istype(attacked_object, /obj/machinery/computer)) + var/obj/machinery/attacked_machinery = attacked_object + var/obj/item/circuitboard/machine/circuit = attacked_machinery.circuit + // If it is, we need to use the circuit's components; there's no good way to get required components off of an already-built machine. + if(istype(circuit)) + spawn_parts_for_components(user, circuit.req_components) + . = ..() + // If auto-clear is in use, + if(auto_clear) + inv_grab.Cut() + inv_grab = atom_storage.return_inv(FALSE) + for(var/obj/item/stored_item in inv_grab) + if(!(stored_item in old_contents)) + qdel(stored_item) + +/// A bespoke proc for spawning in parts +/obj/item/storage/part_replacer/bluespace/tier4/bst/proc/spawn_parts_for_components(mob/living/user, list/required_components) + // Since req_components in machineboards can list item types *OR* /datum/stock_part subtypes this gets a little complicated. + var/list/subtypes = list() + for(var/req_component in required_components) + // Start off noting how many the recipe calls for, a counter for how many matching parts have been found, and generating a list of subtypes for use in later checks. + var/parts_amount_required = required_components[req_component] + var/found_matching = 0 + subtypes = typesof(req_component) + + if(!parts_amount_required) + continue + + /// Then, check if the requested component is an object subtype - this means it's probably either materials (e.g, cables) or non-stock_part subtypes like beakers. + if(ispath(req_component, /obj/item)) + // If it's a stack, it needs special treatment. + if(ispath(req_component, /obj/item/stack)) + // Stacks generate the matching count based on how many matching stacks are in the RPED's inventory with sufficient count. + // To find stacks inside the RPED, we search its contents for anything that's a subtype of /obj/item/stack. + for(var/obj/stored_item in contents) + var/obj/item/stack/stored_item_as_stack = stored_item + if(istype(stored_item_as_stack)) + // If a stack item is found, we check if it's in the typesof list for the current requested component, and if so, mark its count. + if(stored_item_as_stack.type in subtypes) + found_matching += stored_item_as_stack.amount + // If there's enough, we can return early. + if(found_matching >= parts_amount_required) + break + // If there's not enough left, spawn enough of the appropriate type that there will be. Stacks' Initialialize accepts an amount for the newly-spawned stack to have, and will auto-split as needed. + if(found_matching < parts_amount_required) + atom_storage.attempt_insert(new req_component(src, parts_amount_required - found_matching), user, TRUE) + continue + else + // It's not a stack, which means now we have to count how many matching items are present. + for(var/obj/stored_item in contents) + if(stored_item.type in subtypes) + found_matching += 1 + // If there's enough, we can break - no need to spawn extras. + if(found_matching >= parts_amount_required) + break + // If there's still not enough, we're going to have to spawn enough in manually. + if(found_matching < parts_amount_required) + for(var/i in 1 to parts_amount_required - found_matching) + atom_storage.attempt_insert(new req_component(src), user, TRUE) + continue + + /// If it's not an obj, then it's a subtype of /datum/stock_part - or *should be*, anyway. + else if(ispath(req_component, /datum/stock_part)) + var/datum/stock_part/part_type = new req_component() + var/base_type = part_type.physical_object_base_type + // Specific machines sometimes call for specific tiers of part; give them precisely what they ask for, just in case. + if(part_type.tier > 1) + base_type = part_type.physical_object_type + // Search to see if we have enough of that exact item, and if not, we'll spawn more. + for(var/obj/stored_item in contents) + if(stored_item.type == base_type) + found_matching += 1 + // If there's enough, we can return early. + if(found_matching >= parts_amount_required) + break + // If there's still not enough, we're going to have to spawn enough in manually. + if(found_matching < parts_amount_required) + for(var/i in 1 to parts_amount_required - found_matching) + atom_storage.attempt_insert(new base_type(src), user, TRUE) + continue + else + // For everything else, just make sure we have enough valid items of the stock part's subtypes. + subtypes = typesof(base_type) + for(var/obj/stored_item in contents) + if(stored_item.type in subtypes) + found_matching += 1 + // If there's enough, we can return early. + if(found_matching >= parts_amount_required) + break + + // If there's still not enough, we're going to have to spawn enough in manually. + if(found_matching < parts_amount_required) + // Reset the subtypes list so we can pick the highest tier of part available. + subtypes = typesof(req_component) + var/highest_tier = 0 + + // Search those subtypes for the highest. This SHOULD only ever go up to 4, but that's on the assumption upstream doesn't change it. + for(var/subtype_path in subtypes) + var/datum/stock_part/sub_part = new subtype_path() + if(sub_part.tier > highest_tier) + highest_tier = sub_part.tier + base_type = sub_part.physical_object_type + + // Once the best component has been found, fill in enough remaining. + for(var/i in 1 to parts_amount_required - found_matching) + atom_storage.attempt_insert(new base_type(src), user, TRUE) + continue + + // If it's not a /datum/stock_part subtype either, something has gone wrong and devs should probably be alerted. + if(found_matching < parts_amount_required) + to_chat(user, span_notice("Something went wrong manufacturing [req_component]. Alert the devs, and let them know what machine it was!")) + +/// BSTs' special Bluespace RPED can manufacture parts on Alt-RMB, either cables, glass, machine boards, or stock parts. +/obj/item/storage/part_replacer/bluespace/tier4/bst/alt_click_secondary(mob/user) + // Ask the user what they want to make, or if they want to clear the storage. + var/spawn_selection = tgui_input_list(user, "Pick a part, or clear storage", "RPED Manufacture", list("Clear All Items", "Toggle Auto-Clear", "Cables", "Glass", "Spare T4s", "Machine Board", "Stock Part", "Beaker")) + // If they didn't cancel out of the list selection, we do things. Clear-all removes all items, auto-clear destroys left-overs after upgrades, and everything else is pretty self-explanatory. + // Machine boards and stock parts use a recursive subtype selector. + if(isnull(spawn_selection)) + return + else if(spawn_selection == "Clear All Items") + var/list/inv_grab = atom_storage.return_inv(FALSE) + for(var/obj/item/stored_item in inv_grab) + qdel(stored_item) + else if(spawn_selection == "Toggle Auto-Clear") + auto_clear = !auto_clear + to_chat(user, span_notice("The RPED will now [(auto_clear ? "destroy" : "keep")] items left-over after upgrades.")) + else if(spawn_selection == "Cables") + atom_storage.attempt_insert(new /obj/item/stack/cable_coil(src), user, TRUE) + else if(spawn_selection == "Glass") + atom_storage.attempt_insert(new /obj/item/stack/sheet/glass/fifty(src), user, TRUE) + else if(spawn_selection == "Spare T4s") + for(var/i in 1 to 10) + atom_storage.attempt_insert(new /obj/item/stock_parts/capacitor/quadratic(src), user, TRUE) + atom_storage.attempt_insert(new /obj/item/stock_parts/scanning_module/triphasic(src), user, TRUE) + atom_storage.attempt_insert(new /obj/item/stock_parts/servo/femto(src), user, TRUE) + atom_storage.attempt_insert(new /obj/item/stock_parts/micro_laser/quadultra(src), user, TRUE) + atom_storage.attempt_insert(new /obj/item/stock_parts/matter_bin/bluespace(src), user, TRUE) + atom_storage.attempt_insert(new /obj/item/stock_parts/cell/bluespace(src), user, TRUE) + else + var/subtype + if(spawn_selection == "Machine Board") + subtype = /obj/item/circuitboard/machine + else if(spawn_selection == "Stock Part") + subtype = /obj/item/stock_parts + else if(spawn_selection == "Beaker") + subtype = /obj/item/reagent_containers/cup/beaker + if(subtype) + pick_stock_part(user, FALSE, subtype) + +/// A bespoke proc for picking a subtype to spawn in a relatively user-friendly way. +/obj/item/storage/part_replacer/bluespace/tier4/bst/proc/pick_stock_part(mob/user, recurse, subtype) + // Sanity check: make sure it's actually an item, and not an atom, machine, or whatever else someone might try to feed it down the line. + if(!is_path_in_list(subtype, valid_stock_part_types)) + return + // Stores a list of pretty type names : actual paths. + var/list/items_temp = list() + // Grab the initial list of paths, NOT INCLUDING this specific path. + var/list/paths = subtypesof(subtype) + + // Simplistic check to only list top-level subtypes. + var/list/top_level_subtypes_only = list() + for(var/datum/subtype_path as anything in paths) + if(initial(subtype_path.parent_type) != subtype) + continue + top_level_subtypes_only += subtype_path + paths = top_level_subtypes_only + + // With all sub-subtypes removed, initialize the list of valid, spawnable items & their pretty names - and if this is a recursion, include the original subtype. + if(recurse) + paths += subtype + for(var/path in paths) + var/obj/path_as_obj = path + // Generates a pretty list of item names & paths, including notes for those with subtypes. When browsing subtypes, the parent won't have the (# more) note added. + if(length(subtypesof(path))) + if(path == subtype) + items_temp["[initial(path_as_obj.name)]: [path]"] = path + else + items_temp["[initial(path_as_obj.name)] (+[length(subtypesof(path))] more): [path]"] = path + else + items_temp["[initial(path_as_obj.name)]: [path]"] = path + + // Finally, once the listed is generated, ask the user what they want to spawn. + var/target_item = tgui_input_list(user, "Select Subtype", "RPED Manufacture", sort_list(items_temp)) + if(target_item) + // If they select something, and the name:path binding is valid, then either spawn it, OR, if it has subtypes, and isn't the parent type, recurse to let them pick a subtype. + if(items_temp[target_item]) + var/the_item = items_temp[target_item] + if(length(subtypesof(the_item)) && the_item != subtype) + pick_stock_part(user, TRUE, the_item) + else + for(var/i in 1 to 25) + atom_storage.attempt_insert(new the_item(src), user, TRUE) \ No newline at end of file diff --git a/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_pocket.dm b/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_pocket.dm index da283f05b73..b6feefb23fd 100644 --- a/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_pocket.dm +++ b/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_pocket.dm @@ -38,8 +38,7 @@ GLOBAL_LIST_INIT(loadout_pocket_items, generate_loadout_items(/datum/loadout_ite id_card.forceMove(wallet) if(equipper.back) - var/list/backpack_stuff = list() - equipper.back.atom_storage?.return_inv(backpack_stuff, FALSE) + var/list/backpack_stuff = equipper.back.atom_storage?.return_inv(FALSE) for(var/obj/item/thing in backpack_stuff) if(wallet.contents.len >= 3) break @@ -117,6 +116,10 @@ GLOBAL_LIST_INIT(loadout_pocket_items, generate_loadout_items(/datum/loadout_ite name = "Personal AI Device" item_path = /obj/item/pai_card +/datum/loadout_item/pocket_items/link_scryer + name = "MODlink Scryer" + item_path = /obj/item/clothing/neck/link_scryer/loaded + /datum/loadout_item/pocket_items/cigarettes name = "Cigarette Pack" item_path = /obj/item/storage/fancy/cigarettes @@ -201,6 +204,14 @@ GLOBAL_LIST_INIT(loadout_pocket_items, generate_loadout_items(/datum/loadout_ite name = "First-Aid Kit" item_path = /obj/item/storage/medkit/regular +/datum/loadout_item/pocket_items/deforest_cheesekit + name = "Civil Defense Medical Kit" + item_path = /obj/item/storage/medkit/civil_defense/stocked + +/datum/loadout_item/pocket_items/deforest_frontiermedkit + name = "Frontier Medical Kit" + item_path = /obj/item/storage/medkit/frontier/stocked + /datum/loadout_item/pocket_items/ingredients name = "Wildcard Ingredient Box" item_path = /obj/item/storage/box/ingredients/wildcard @@ -311,4 +322,4 @@ GLOBAL_LIST_INIT(loadout_pocket_items, generate_loadout_items(/datum/loadout_ite /datum/loadout_item/pocket_items/donator/vape name = "E-Cigarette" - item_path = /obj/item/clothing/mask/vape + item_path = /obj/item/clothing/mask/vape \ No newline at end of file diff --git a/modular_skyrat/modules/time_clock/code/console_tgui.dm b/modular_skyrat/modules/time_clock/code/console_tgui.dm index 0060e134a46..64b576828d0 100644 --- a/modular_skyrat/modules/time_clock/code/console_tgui.dm +++ b/modular_skyrat/modules/time_clock/code/console_tgui.dm @@ -45,7 +45,7 @@ /obj/item/mod/control/pre_equipped/safeguard, \ /obj/item/gun/energy/cell_loaded/medigun/cmo, \ /obj/item/defibrillator/compact/loaded, \ - /obj/item/storage/hypospraykit/cmo, \ + /obj/item/storage/hypospraykit/cmo/preloaded, \ /obj/item/mod/control/pre_equipped/rescue, \ /obj/item/gun/ballistic/rifle/boltaction/sporterized, \ /obj/item/clothing/glasses/hud/gun_permit/sunglasses, \