diff --git a/code/datums/supplypacks/medical.dm b/code/datums/supplypacks/medical.dm index 5596d95f03..e345b18505 100644 --- a/code/datums/supplypacks/medical.dm +++ b/code/datums/supplypacks/medical.dm @@ -334,4 +334,18 @@ contains = list(/obj/item/device/defib_kit = 2) cost = 30 containertype = /obj/structure/closet/crate/medical - containername = "Defibrillator crate" \ No newline at end of file + containername = "Defibrillator crate" + +/datum/supply_pack/med/distillery + name = "Chemical distiller crate" + contains = list(/obj/machinery/portable_atmospherics/powered/reagent_distillery = 1) + cost = 175 + containertype = /obj/structure/largecrate + containername = "Chemical distiller crate" + +/datum/supply_pack/med/advdistillery + name = "Industrial Chemical distiller crate" + contains = list(/obj/machinery/portable_atmospherics/powered/reagent_distillery/industrial = 1) + cost = 250 + containertype = /obj/structure/largecrate + containername = "Industrial Chemical distiller crate" diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index 8778b4e5f3..085c070f7d 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -149,6 +149,8 @@ var/power_losses var/last_power_draw = 0 var/obj/item/weapon/cell/cell + var/use_cell = TRUE + var/removeable_cell = TRUE /obj/machinery/portable_atmospherics/powered/powered() if(use_power) //using area power @@ -158,7 +160,7 @@ return 0 /obj/machinery/portable_atmospherics/powered/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/weapon/cell)) + if(use_cell && istype(I, /obj/item/weapon/cell)) if(cell) to_chat(user, "There is already a power cell installed.") return @@ -173,7 +175,7 @@ power_change() return - if(I.is_screwdriver()) + if(I.is_screwdriver() && removeable_cell) if(!cell) to_chat(user, "There is no power cell installed.") return diff --git a/code/game/machinery/frame.dm b/code/game/machinery/frame.dm index 88bf735f53..0baa68cf22 100644 --- a/code/game/machinery/frame.dm +++ b/code/game/machinery/frame.dm @@ -102,6 +102,12 @@ circuit = /obj/item/weapon/circuitboard/grinder frame_size = 3 +/datum/frame/frame_types/reagent_distillery + name = "Distillery" + frame_class = FRAME_CLASS_MACHINE + circuit = /obj/item/weapon/circuitboard/distiller + frame_size = 4 + /datum/frame/frame_types/display name = "Display" frame_class = FRAME_CLASS_DISPLAY diff --git a/code/game/objects/items/weapons/circuitboards/frame.dm b/code/game/objects/items/weapons/circuitboards/frame.dm index 63308e6caa..6b59d13ea5 100644 --- a/code/game/objects/items/weapons/circuitboards/frame.dm +++ b/code/game/objects/items/weapons/circuitboards/frame.dm @@ -170,6 +170,15 @@ /obj/item/weapon/stock_parts/gear = 1, /obj/item/weapon/reagent_containers/glass/beaker/large = 1) +/obj/item/weapon/circuitboard/distiller + build_path = /obj/machinery/portable_atmospherics/powered/reagent_distillery + board_type = new /datum/frame/frame_types/reagent_distillery + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 1, + /obj/item/weapon/stock_parts/micro_laser = 1, + /obj/item/weapon/stock_parts/motor = 2, + /obj/item/weapon/stock_parts/gear = 1) + /obj/item/weapon/circuitboard/teleporter_hub name = T_BOARD("teleporter hub") build_path = /obj/machinery/teleport/hub diff --git a/code/modules/mob/_modifiers/medical.dm b/code/modules/mob/_modifiers/medical.dm new file mode 100644 index 0000000000..46a8877d1f --- /dev/null +++ b/code/modules/mob/_modifiers/medical.dm @@ -0,0 +1,17 @@ +/* + * Modifiers caused by chemicals or organs specifically. + */ + +/datum/modifier/cryogelled + name = "cryogelled" + desc = "Your body begins to freeze." + mob_overlay_state = "chilled" + + on_created_text = "You feel like you're going to freeze! It's hard to move." + on_expired_text = "You feel somewhat warmer and more mobile now." + stacks = MODIFIER_STACK_ALLOWED + + slowdown = 0.1 + evasion = -5 + attack_speed_percent = 1.1 + disable_duration_percent = 1.05 diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index dee4cabfa6..a481cfaf33 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -281,6 +281,28 @@ var/obj/item/weapon/reagent_containers/food/condiment/P = new/obj/item/weapon/reagent_containers/food/condiment(src.loc) reagents.trans_to_obj(P,50) + else if (href_list["createpatch"]) + if(reagents.total_volume < 1) //Sanity checking. + return + + var/name = sanitizeSafe(input(usr,"Name:","Name your patch!","[reagents.get_master_reagent_name()] ([round(reagents.total_volume)]u)") as null|text, MAX_NAME_LEN) + + if(!name) //Blank name (sanitized to nothing, or left empty) or cancel + return + + if(reagents.total_volume < 1) //Sanity checking. + return + var/obj/item/weapon/reagent_containers/pill/patch/P = new/obj/item/weapon/reagent_containers/pill/patch(src.loc) + if(!name) name = reagents.get_master_reagent_name() + P.name = "[name] patch" + P.pixel_x = rand(-7, 7) //random position + P.pixel_y = rand(-7, 7) + + reagents.trans_to_obj(P, 60) + if(src.loaded_pill_bottle) + if(loaded_pill_bottle.contents.len < loaded_pill_bottle.max_storage_space) + P.loc = loaded_pill_bottle + else if(href_list["pill_sprite"]) pillsprite = href_list["pill_sprite"] else if(href_list["bottle_sprite"]) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm index e2724f0647..d1213eb783 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm @@ -16,6 +16,28 @@ M.add_chemical_effect(CE_STABLE, 15) M.add_chemical_effect(CE_PAINKILLER, 10) +/datum/reagent/inaprovaline/topical + name = "Inaprovalaze" + id = "inaprovalaze" + description = "Inaprovalaze is a topical variant of Inaprovaline." + taste_description = "bitterness" + reagent_state = LIQUID + color = "#00BFFF" + overdose = REAGENTS_OVERDOSE * 2 + metabolism = REM * 0.5 + scannable = 1 + touch_met = REM * 0.75 + +/datum/reagent/inaprovaline/topical/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + if(alien != IS_DIONA) + ..() + M.adjustToxLoss(2 * removed) + +/datum/reagent/inaprovaline/topical/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + if(alien != IS_DIONA) + M.add_chemical_effect(CE_STABLE, 20) + M.add_chemical_effect(CE_PAINKILLER, 12) + /datum/reagent/bicaridine name = "Bicaridine" id = "bicaridine" @@ -51,6 +73,33 @@ if(W.damage <= 0) O.wounds -= W +/datum/reagent/bicaridine/topical + name = "Bicaridaze" + id = "bicaridaze" + description = "Bicaridaze is a topical variant of the chemical Bicaridine." + taste_description = "bitterness" + taste_mult = 3 + reagent_state = LIQUID + color = "#BF0000" + overdose = REAGENTS_OVERDOSE * 0.75 + scannable = 1 + touch_met = REM * 0.75 + +/datum/reagent/bicaridine/topical/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + var/chem_effective = 1 + if(alien == IS_SLIME) + chem_effective = 0.75 + if(alien != IS_DIONA) + ..(M, alien, removed * chem_effective) + M.adjustToxLoss(2 * removed) + +/datum/reagent/bicaridine/topical/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + var/chem_effective = 1 + if(alien == IS_SLIME) + chem_effective = 0.75 + if(alien != IS_DIONA) + M.heal_organ_damage(6 * removed * chem_effective, 0) + /datum/reagent/kelotane name = "Kelotane" id = "kelotane" @@ -87,6 +136,33 @@ if(alien != IS_DIONA) M.heal_organ_damage(0, 8 * removed * chem_effective) //VOREStation edit +/datum/reagent/dermaline/topical + name = "Dermalaze" + id = "dermalaze" + description = "Dermalaze is a topical variant of the chemical Dermaline." + taste_description = "bitterness" + taste_mult = 1.5 + reagent_state = LIQUID + color = "#FF8000" + overdose = REAGENTS_OVERDOSE * 0.4 + scannable = 1 + touch_met = REM * 0.75 + +/datum/reagent/dermaline/topical/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + var/chem_effective = 1 + if(alien == IS_SLIME) + chem_effective = 0.75 + if(alien != IS_DIONA) + ..(M, alien, removed * chem_effective) + M.adjustToxLoss(2 * removed) + +/datum/reagent/dermaline/topical/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + var/chem_effective = 1 + if(alien == IS_SLIME) + chem_effective = 0.75 + if(alien != IS_DIONA) + M.heal_organ_damage(0, 12 * removed * chem_effective) + /datum/reagent/dylovene name = "Dylovene" id = "anti_toxin" @@ -203,6 +279,10 @@ M.heal_organ_damage(1.5 * removed, 1.5 * removed * chem_effective) M.adjustToxLoss(-1.5 * removed * chem_effective) +/datum/reagent/tricordrazine/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + if(alien != IS_DIONA) + affect_blood(M, alien, removed * 0.4) + /datum/reagent/cryoxadone name = "Cryoxadone" id = "cryoxadone" @@ -865,6 +945,9 @@ to_chat(M, "Your senses feel unfocused, and divided.") M.add_chemical_effect(CE_ANTIBIOTIC, dose >= overdose ? ANTIBIO_OD : ANTIBIO_NORM) +/datum/reagent/spaceacillin/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + affect_blood(M, alien, removed * 0.8) // Not 100% as effective as injections, though still useful. + /datum/reagent/corophizine name = "Corophizine" id = "corophizine" diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Modifiers.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Modifiers.dm new file mode 100644 index 0000000000..d9a7483ec1 --- /dev/null +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Modifiers.dm @@ -0,0 +1,58 @@ +/* + * Modifier-applying chemicals. + */ + +/datum/reagent/modapplying + name = "brute juice" + id = "berserkmed" + description = "A liquid that is capable of causing a prolonged state of heightened aggression and durability." + taste_description = "metal" + reagent_state = LIQUID + color = "#ff5555" + metabolism = REM + + var/modifier_to_add = /datum/modifier/berserk + var/modifier_duration = 2 SECONDS // How long, per unit dose, will this last? + +/datum/reagent/modapplying/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + if(alien == IS_DIONA) + return + M.add_modifier(modifier_to_add, dose * modifier_duration) + +/datum/reagent/modapplying/cryofluid + name = "cryogenic slurry" + id = "cryoslurry" + description = "An incredibly strange liquid that rapidly absorbs thermal energy from materials it contacts." + taste_description = "siberian hellscape" + color = "#4CDBDB" + metabolism = REM * 0.5 + + modifier_to_add = /datum/modifier/cryogelled + modifier_duration = 3 SECONDS + +/datum/reagent/modapplying/cryofluid/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + ..(M, alien, removed) + M.bodytemperature -= removed * 20 + +/datum/reagent/modapplying/cryofluid/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) + affect_blood(M, alien, removed * 2.5) + +/datum/reagent/modapplying/cryofluid/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + affect_blood(M, alien, removed * 0.6) + +/datum/reagent/modapplying/cryofluid/touch_mob(var/mob/M, var/amount) + if(isliving(M)) + var/mob/living/L = M + for(var/I = 1 to rand(1, round(amount + 1))) + L.add_modifier(modifier_to_add, amount * rand(modifier_duration / 2, modifier_duration * 2)) + return + +/datum/reagent/modapplying/cryofluid/touch_turf(var/turf/T, var/amount) + if(istype(T, /turf/simulated/floor/water) && prob(amount)) + T.visible_message("\The [T] crackles loudly as the cryogenic fluid causes it to boil away, leaving behind a hard layer of ice.") + T.ChangeTurf(/turf/simulated/floor/outdoors/ice, 1, 1, TRUE) + else + if(istype(T, /turf/simulated)) + var/turf/simulated/S = T + S.freeze_floor() + return diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm index 79304733dd..32ed5bc81f 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm @@ -11,6 +11,7 @@ metabolism = REM * 0.25 // 0.05 by default. Hopefully enough to get some help, or die horribly, whatever floats your boat filtered_organs = list(O_LIVER, O_KIDNEYS) var/strength = 4 // How much damage it deals per unit + var/skin_danger = 0.2 // The multiplier for how effective the toxin is when making skin contact. /datum/reagent/toxin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(strength && alien != IS_DIONA) @@ -23,6 +24,9 @@ M.heal_organ_damage((10/strength) * removed, (10/strength) * removed) //Doses of toxins below 10 units, and 10 strength, are capable of providing useful compounds for repair. M.adjustToxLoss(strength * removed) +/datum/reagent/toxin/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + affect_blood(M, alien, removed * 0.2) + /datum/reagent/toxin/plasticide name = "Plasticide" id = "plasticide" @@ -58,6 +62,7 @@ reagent_state = LIQUID color = "#005555" strength = 8 + skin_danger = 0.4 /datum/reagent/toxin/neurotoxic_protein/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) ..() @@ -121,6 +126,7 @@ color = "#9D14DB" strength = 30 touch_met = 5 + skin_danger = 1 /datum/reagent/toxin/phoron/touch_mob(var/mob/living/L, var/amount) if(istype(L)) diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index 5b37335647..40cd1b7123 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -34,6 +34,7 @@ var/reaction_sound = 'sound/effects/bubbles.ogg' var/log_is_important = 0 // If this reaction should be considered important for logging. Important recipes message admins when mixed, non-important ones just log to file. + /datum/chemical_reaction/proc/can_happen(var/datum/reagents/holder) //check that all the required reagents are present if(!holder.has_all_reagents(required_reagents)) diff --git a/code/modules/reagents/distilling/Distilling-Recipes.dm b/code/modules/reagents/distilling/Distilling-Recipes.dm new file mode 100644 index 0000000000..f7fae18178 --- /dev/null +++ b/code/modules/reagents/distilling/Distilling-Recipes.dm @@ -0,0 +1,170 @@ +/datum/chemical_reaction/distilling +// name = null +// id = null +// result = null +// required_reagents = list() +// catalysts = list() +// inhibitors = list() +// result_amount = 0 + + //how far the reaction proceeds each time it is processed. Used with either REACTION_RATE or HALF_LIFE macros. + reaction_rate = HALF_LIFE(6) + + //if less than 1, the reaction will be inhibited if the ratio of products/reagents is too high. + //0.5 = 50% yield -> reaction will only proceed halfway until products are removed. +// yield = 1.0 + + //If limits on reaction rate would leave less than this amount of any reagent (adjusted by the reaction ratios), + //the reaction goes to completion. This is to prevent reactions from going on forever with tiny reagent amounts. +// min_reaction = 2 + + mix_message = "The solution churns." + reaction_sound = 'sound/effects/slosh.ogg' + +// log_is_important = 0 // If this reaction should be considered important for logging. Important recipes message admins when mixed, non-important ones just log to file. + + var/list/temp_range = list(T0C, T20C) + var/temp_shift = 0 // How much the temperature changes when the reaction occurs. + +/datum/chemical_reaction/distilling/can_happen(var/datum/reagents/holder) + //check that all the required reagents are present + if(!holder.has_all_reagents(required_reagents)) + return 0 + + //check that all the required catalysts are present in the required amount + if(!holder.has_all_reagents(catalysts)) + return 0 + + //check that none of the inhibitors are present in the required amount + if(holder.has_any_reagent(inhibitors)) + return 0 + + if(!istype(holder.my_atom, /obj/item/weapon/reagent_containers/glass/distilling)) + return 0 + + else // Super special temperature check. + var/obj/item/weapon/reagent_containers/glass/distilling/D = holder.my_atom + var/obj/machinery/portable_atmospherics/powered/reagent_distillery/RD = D.Master + if(RD.current_temp < temp_range[1] || RD.current_temp > temp_range[2]) + return 0 + + return 1 + +/datum/chemical_reaction/distilling/on_reaction(var/datum/reagents/holder, var/created_volume) + if(istype(holder.my_atom, /obj/item/weapon/reagent_containers/glass/distilling)) + var/obj/item/weapon/reagent_containers/glass/distilling/D = holder.my_atom + var/obj/machinery/portable_atmospherics/powered/reagent_distillery/RD = D.Master + RD.current_temp += temp_shift + return + +// Subtypes // + +// Biomass +/datum/chemical_reaction/distilling/biomass + name = "Distilling Biomass" + id = "distill_biomass" + result = "biomass" + required_reagents = list("blood" = 1, "sugar" = 1, "phoron" = 0.5) + result_amount = 1 // 40 units per sheet, requires actually using the machine, and having blood to spare. + + temp_range = list(T20C + 80, T20C + 130) + temp_shift = -2 + +// Medicinal +/datum/chemical_reaction/distilling/inaprovalaze + name = "Distilling Inaprovalaze" + id = "distill_inaprovalaze" + result = "inaprovalaze" + required_reagents = list("inaprovaline" = 2, "foaming_agent" = 1) + result_amount = 2 + + reaction_rate = HALF_LIFE(10) + + temp_range = list(T0C + 100, T0C + 120) + +/datum/chemical_reaction/distilling/bicaridaze + name = "Distilling Bicaridaze" + id = "distill_bicaridaze" + result = "bicaridaze" + required_reagents = list("bicaridine" = 2, "foaming_agent" = 1) + result_amount = 2 + + reaction_rate = HALF_LIFE(10) + + temp_range = list(T0C + 110, T0C + 130) + +/datum/chemical_reaction/distilling/dermalaze + name = "Distilling Dermalaze" + id = "distill_dermalaze" + result = "dermalaze" + required_reagents = list("dermaline" = 2, "foaming_agent" = 1) + result_amount = 2 + + reaction_rate = HALF_LIFE(10) + + temp_range = list(T0C + 115, T0C + 130) + +// Alcohol +/datum/chemical_reaction/distilling/beer + name = "Distilling Beer" + id = "distill_beer" + result = "beer" + required_reagents = list("nutriment" = 1, "water" = 1, "sugar" = 1) + result_amount = 2 + + reaction_rate = HALF_LIFE(30) + + temp_range = list(T20C, T20C + 2) + +/datum/chemical_reaction/distilling/ale + name = "Distilling Ale" + id = "distill_ale" + result = "ale" + required_reagents = list("nutriment" = 1, "beer" = 1) + inhibitors = list("water" = 1) + result_amount = 2 + + reaction_rate = HALF_LIFE(30) + + temp_shift = 0.5 + temp_range = list(T0C + 7, T0C + 13) + +// Unique +/datum/chemical_reaction/distilling/berserkjuice + name = "Distilling Brute Juice" + id = "distill_brutejuice" + result = "berserkmed" + required_reagents = list("biomass" = 1, "hyperzine" = 3, "synaptizine" = 2, "phoron" = 1) + result_amount = 3 + + temp_range = list(T0C + 600, T0C + 700) + temp_shift = 4 + +/datum/chemical_reaction/distilling/berserkjuice/on_reaction(var/datum/reagents/holder, var/created_volume) + ..() + + if(prob(1)) + var/turf/T = get_turf(holder.my_atom) + explosion(T, -1, rand(-1, 1), rand(1,2), rand(3,5)) + return + +/datum/chemical_reaction/distilling/cryogel + name = "Distilling Cryogellatin" + id = "distill_cryoslurry" + result = "cryoslurry" + required_reagents = list("frostoil" = 7, "enzyme" = 3, "plasticide" = 3, "foaming_agent" = 2) + inhibitors = list("water" = 5) + result_amount = 1 + + temp_range = list(0, 15) + temp_shift = 20 + +/datum/chemical_reaction/distilling/cryogel/on_reaction(var/datum/reagents/holder, var/created_volume) + ..() + + if(prob(1)) + var/turf/T = get_turf(holder.my_atom) + var/datum/effect/effect/system/smoke_spread/frost/F = new (holder.my_atom) + F.set_up(6, 0, T) + F.start() + return diff --git a/code/modules/reagents/distilling/distilling.dm b/code/modules/reagents/distilling/distilling.dm new file mode 100644 index 0000000000..b86bcc7435 --- /dev/null +++ b/code/modules/reagents/distilling/distilling.dm @@ -0,0 +1,317 @@ + +/* + * Distillery, used for over-time temperature-based mixes. + */ + +/obj/machinery/portable_atmospherics/powered/reagent_distillery + name = "chemical distillery" + desc = "A complex machine utilizing state-of-the-art components to mix chemicals at different temperatures." + use_power = 1 + + icon = 'icons/obj/machines/reagent.dmi' + icon_state = "distiller" + var/base_state // The string var used in update icon for overlays, either set manually or initialized. + + power_rating = 3000 + power_losses = 240 + + var/on = FALSE + + var/target_temp = T20C + + var/max_temp = T20C + 300 + var/min_temp = T0C - 10 + + var/current_temp = T20C + + var/use_atmos = FALSE // If true, this machine will be connectable to ports, and use gas mixtures as the source of heat, rather than its internal controls. + + var/static/radial_examine = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_examine") + var/static/radial_use = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_use") + + var/static/radial_pump = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_pump") + + var/static/radial_eject_input = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject_input") + var/static/radial_eject_output = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject_output") + + var/static/radial_adjust_temp = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_temp") + + var/static/radial_install_input = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_add") + var/static/radial_install_output = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_add") + + var/static/radial_inspectgauges = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_lookat") + + var/static/radial_mix = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_mix") + +// Overlay holders so we don't have to constantly remake them. + var/image/overlay_output_beaker + var/image/overlay_input_beaker + var/image/overlay_off + var/image/overlay_ready + var/image/overlay_cooling + var/image/overlay_heating + var/image/overlay_dumping + var/image/overlay_connected + +// Our unique beaker, used in its unique recipes to ensure things can only react inside this machine and minimize oddities from trying to transfer to a machine and back. + var/obj/item/weapon/reagent_containers/glass/distilling/Reservoir + + var/obj/item/weapon/reagent_containers/glass/InputBeaker + var/obj/item/weapon/reagent_containers/glass/OutputBeaker + +// A multiplier for the production amount. This should really only ever be lower than one, otherwise you end up with duping. + var/efficiency = 1 + +/obj/item/weapon/reagent_containers/glass/distilling + name = "distilling chamber" + desc = "You should not be seeing this." + volume = 600 + + var/obj/machinery/portable_atmospherics/powered/reagent_distillery/Master + +/obj/item/weapon/reagent_containers/glass/distilling/Destroy() + Master = null + ..() + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/Initialize() + ..() + + Reservoir = new (src) + Reservoir.Master = src + + if(!base_state) + base_state = icon_state + + setup_overlay_vars() + + update_icon() + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/proc/setup_overlay_vars() + overlay_output_beaker = image(icon = src.icon, icon_state = "[base_state]-output") + overlay_input_beaker = image(icon = src.icon, icon_state = "[base_state]-input") + overlay_off = image(icon = src.icon, icon_state = "[base_state]-bad") + overlay_ready = image(icon = src.icon, icon_state = "[base_state]-good") + overlay_cooling = image(icon = src.icon, icon_state = "[base_state]-cool") + overlay_heating = image(icon = src.icon, icon_state = "[base_state]-heat") + overlay_dumping = image(icon = src.icon, icon_state = "[base_state]-dump") + overlay_connected = image(icon = src.icon, icon_state = "[base_state]-connector") + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/Destroy() + qdel(Reservoir) + Reservoir = null + if(InputBeaker) + qdel(InputBeaker) + InputBeaker = null + if(OutputBeaker) + qdel(OutputBeaker) + OutputBeaker = null + + ..() + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/attack_hand(mob/user) + var/list/options = list() + options["examine"] = radial_examine + options["use"] = radial_use + options["inspect gauges"] = radial_inspectgauges + options["pulse agitator"] = radial_mix + + if(InputBeaker) + options["eject input"] = radial_eject_input + if(OutputBeaker) + options["eject output"] = radial_eject_output + + if(!use_atmos) + options["adjust temp"] = radial_adjust_temp + + if(length(options) < 1) + return + + var/list/choice = list() + if(length(options) == 1) + for(var/key in options) + choice = key + else + choice = show_radial_menu(user, src, options, require_near = !issilicon(user)) + + switch(choice) + if("examine") + examine(user) + + if("use") + if(powered()) + on = !on + to_chat(user, "You turn \the [src] [on ? "on" : "off"].") + + if("inspect gauges") + to_chat(user, "\The [src]'s gauges read:") + if(!use_atmos) + to_chat(user, "- Target Temperature: [target_temp]") + to_chat(user, "- Temperature: [current_temp]") + + if("pulse agitator") + to_chat(user, "You press \the [src]'s chamber agitator button.") + if(on) + visible_message("\The [src] rattles to life.") + Reservoir.reagents.handle_reactions() + else + spawn(1 SECOND) + to_chat(user, "Nothing happens..") + + if("eject input") + if(InputBeaker) + InputBeaker.forceMove(get_turf(src)) + InputBeaker = null + + if("eject output") + if(OutputBeaker) + OutputBeaker.forceMove(get_turf(src)) + OutputBeaker = null + + if("adjust temp") + target_temp = input("Choose a target temperature.", "Temperature.", T20C) as num + target_temp = CLAMP(target_temp, min_temp, max_temp) + + update_icon() + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/attackby(obj/item/weapon/W as obj, mob/user as mob) + var/list/options = list() + if(istype(W, /obj/item/weapon/reagent_containers/glass)) + if(!InputBeaker) + options["install input"] = radial_install_input + if(!OutputBeaker) + options["install output"] = radial_install_output + + if(!options || !options.len) + update_icon() + return ..() + + var/list/choice = list() + if(length(options) == 1) + for(var/key in options) + choice = key + else + choice = show_radial_menu(user, src, options, require_near = TRUE) // No telekinetics. + + switch(choice) + if("install input") + if(!InputBeaker) + user.drop_from_inventory(W) + W.add_fingerprint(user) + W.forceMove(src) + InputBeaker = W + + if("install output") + if(!OutputBeaker) + user.drop_from_inventory(W) + W.add_fingerprint(user) + W.forceMove(src) + OutputBeaker = W + + update_icon() + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/use_power(var/amount, var/chan = -1) + last_power_draw = amount + if(use_cell && cell && cell.charge) + var/cellcharge = cell.charge + cell.use(amount) + + var/celldifference = max(0, cellcharge - cell.charge) + + amount = celldifference + + var/area/A = get_area(src) + if(!A || !isarea(A)) + return + if(chan == -1) + chan = power_channel + A.use_power(amount, chan) + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/process() + ..() + + var/run_pump = FALSE + + if(InputBeaker || OutputBeaker) + run_pump = TRUE + + var/avg_temp = 0 + var/avg_pressure = 0 + + if(connected_port && connected_port.network.line_members.len) + var/list/members = list() + var/datum/pipe_network/Net = connected_port.network + members = Net.line_members.Copy() + + for(var/datum/pipeline/Line in members) + avg_pressure += Line.air.return_pressure() + avg_temp += Line.air.temperature + + avg_temp /= members.len + avg_pressure /= members.len + + if(!powered()) + on = FALSE + + if(!on || (use_atmos && (!connected_port || avg_pressure < 1000))) + current_temp = round((current_temp + T20C) / 2) + + else if(on) + if(!use_atmos) + if(current_temp != round(target_temp)) + var/shift_mod = 0 + if(current_temp < target_temp) + shift_mod = 1 + else if(current_temp > target_temp) + shift_mod = -1 + current_temp = CLAMP(round((current_temp + 1 * shift_mod) + (rand(-5, 5) / 10)), min_temp, max_temp) + use_power(power_rating * CELLRATE) + else if(connected_port && avg_pressure > 1000) + current_temp = round((current_temp + avg_temp) / 2) + else if(!run_pump) + visible_message("\The [src]'s motors wind down.") + on = FALSE + + if(InputBeaker && Reservoir.reagents.total_volume < Reservoir.reagents.maximum_volume) + InputBeaker.reagents.trans_to_holder(Reservoir.reagents, amount = rand(10,20)) + + if(OutputBeaker && OutputBeaker.reagents.total_volume < OutputBeaker.reagents.maximum_volume) + use_power(power_rating * CELLRATE * 0.5) + Reservoir.reagents.trans_to_holder(OutputBeaker.reagents, amount = rand(1, 5)) + + update_icon() + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/update_icon() + ..() + cut_overlays() + + if(InputBeaker) + add_overlay(overlay_input_beaker) + + if(OutputBeaker) + add_overlay(overlay_output_beaker) + + if(on) + if(OutputBeaker && OutputBeaker.reagents.total_volume < OutputBeaker.reagents.maximum_volume) + add_overlay(overlay_dumping) + else if(current_temp == round(target_temp)) + add_overlay(overlay_ready) + else if(current_temp < target_temp) + add_overlay(overlay_heating) + else + add_overlay(overlay_cooling) + + else + add_overlay(overlay_off) + + if(connected_port) + add_overlay(overlay_connected) + +/* + * Subtypes + */ + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/industrial + name = "industrial chemical distillery" + desc = "A gas-operated variant of a chemical distillery. Able to reach much higher, and lower, temperatures through the use of treated gas." + + use_atmos = TRUE diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index 92e01c8ede..3c9fdcba6f 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -44,7 +44,8 @@ /obj/machinery/smartfridge/, /obj/machinery/biogenerator, /obj/structure/frame, - /obj/machinery/radiocarbon_spectrometer + /obj/machinery/radiocarbon_spectrometer, + /obj/machinery/portable_atmospherics/powered/reagent_distillery ) /obj/item/weapon/reagent_containers/glass/Initialize() diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm new file mode 100644 index 0000000000..007669286e --- /dev/null +++ b/code/modules/reagents/reagent_containers/patch.dm @@ -0,0 +1,82 @@ + +/* + * Patches. A subtype of pills, in order to inherit the possible future produceability within chem-masters, and dissolving. + */ + +/obj/item/weapon/reagent_containers/pill/patch + name = "patch" + desc = "A patch." + icon = 'icons/obj/chemical.dmi' + icon_state = null + item_state = "pill" + + base_state = "patch" + + possible_transfer_amounts = null + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + volume = 60 + + var/pierce_material = FALSE // If true, the patch can be used through thick material. + +/obj/item/weapon/reagent_containers/pill/patch/attack(mob/M as mob, mob/user as mob) + var/mob/living/L = user + + if(M == L) + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(check_zone(L.zone_sel.selecting)) + if(!affecting) + to_chat(user, "The limb is missing!") + return + if(affecting.status >= ORGAN_ROBOT) + to_chat(user, "\The [src] won't work on a robotic limb!") + return + + if(!H.can_inject(user, FALSE, L.zone_sel.selecting, pierce_material)) + to_chat(user, "\The [src] can't be applied through such a thick material!") + return + + to_chat(H, "\The [src] is placed on your [affecting].") + M.drop_from_inventory(src) //icon update + if(reagents.total_volume) + reagents.trans_to_mob(M, reagents.total_volume, CHEM_TOUCH) + qdel(src) + return 1 + + else if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(check_zone(L.zone_sel.selecting)) + if(!affecting) + to_chat(user, "The limb is missing!") + return + if(affecting.status >= ORGAN_ROBOT) + to_chat(user, "\The [src] won't work on a robotic limb!") + return + + if(!H.can_inject(user, FALSE, L.zone_sel.selecting, pierce_material)) + to_chat(user, "\The [src] can't be applied through such a thick material!") + return + + user.visible_message("[user] attempts to place \the [src] onto [H]`s [affecting].") + + user.setClickCooldown(user.get_attack_speed(src)) + if(!do_mob(user, M)) + return + + user.drop_from_inventory(src) //icon update + user.visible_message("[user] applies \the [src] to [H].") + + var/contained = reagentlist() + add_attack_logs(user,M,"Applied a patch containing [contained]") + + to_chat(H, "\The [src] is placed on your [affecting].") + M.drop_from_inventory(src) //icon update + + if(reagents.total_volume) + reagents.trans_to_mob(M, reagents.total_volume, CHEM_TOUCH) + qdel(src) + + return 1 + + return 0 \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index c0b647e53b..5ca47a6d3b 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -7,6 +7,9 @@ icon = 'icons/obj/chemical.dmi' icon_state = null item_state = "pill" + + var/base_state = "pill" + possible_transfer_amounts = null w_class = ITEMSIZE_TINY slot_flags = SLOT_EARS @@ -15,7 +18,7 @@ /obj/item/weapon/reagent_containers/pill/Initialize() . = ..() if(!icon_state) - icon_state = "pill[rand(1, 4)]" //preset pills only use colour changing or unique icons + icon_state = "[base_state][rand(1, 4)]" //preset pills only use colour changing or unique icons /obj/item/weapon/reagent_containers/pill/attack(mob/M as mob, mob/user as mob) if(M == user) diff --git a/icons/mob/radial.dmi b/icons/mob/radial.dmi index cfdd0e549a..da1f69840c 100644 Binary files a/icons/mob/radial.dmi and b/icons/mob/radial.dmi differ diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi index e229d9e42d..34bf88828a 100644 Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ diff --git a/icons/obj/machines/reagent.dmi b/icons/obj/machines/reagent.dmi new file mode 100644 index 0000000000..d11f34270d Binary files /dev/null and b/icons/obj/machines/reagent.dmi differ diff --git a/nano/templates/chem_master.tmpl b/nano/templates/chem_master.tmpl index b8b8300b4a..f1b981ba6e 100644 --- a/nano/templates/chem_master.tmpl +++ b/nano/templates/chem_master.tmpl @@ -67,6 +67,7 @@ {{:helper.link('Create pill (60 units max)', null, {'createpill' : 1})}} {{:helper.link('Create multiple pills', null, {'createpill_multiple' : 1})}} {{:helper.link('Create bottle (60 units max)', null, {'createbottle' : 1})}} + {{:helper.link('Create patch (60 units max)', null, {'createpatch' : 1})}}