Merge branch 'master' of https://github.com/tgstation/tgstation into upstream-2025-11-12

This commit is contained in:
Roxy
2025-11-12 16:44:13 -05:00
379 changed files with 37535 additions and 33147 deletions
@@ -267,6 +267,8 @@
.["displayedUnits"] = cell.charge ? (cell.charge / power_cost) : 0
.["displayedMaxUnits"] = cell.maxcharge / power_cost
.["showpH"] = isnull(recording_recipe) ? show_ph : FALSE //virtual beakers have no ph to compute & display
var/obj/item/held_item = user.get_active_held_item()
.["hasBeakerInHand"] = held_item?.is_chem_container() || FALSE
var/list/chemicals = list()
var/is_hallucinating = FALSE
@@ -359,6 +361,13 @@
replace_beaker(ui.user)
return TRUE
if("insert")
var/obj/item/reagent_containers/container = ui.user.get_active_held_item()
if(container?.can_insert_container(ui.user, src))
replace_beaker(ui.user, container)
return TRUE
if("dispense_recipe")
if(!is_operational || QDELETED(cell))
return
@@ -464,20 +473,19 @@
return ITEM_INTERACT_BLOCKING
/obj/machinery/chem_dispenser/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(is_reagent_container(tool) && !(tool.item_flags & ABSTRACT) && tool.is_open_container())
//SKYRAT EDIT ADDITION START - CHEMISTRY QOL
var/obj/item/reagent_containers/container = tool
if(customTransferAmount)
transferAmounts -= customTransferAmount
transferAmounts = container.possible_transfer_amounts
//SKYRAT EDIT ADDITION END
if(!user.transferItemToLoc(tool, src))
return ITEM_INTERACT_BLOCKING
replace_beaker(user, tool)
ui_interact(user)
return ITEM_INTERACT_SUCCESS
if(!tool.can_insert_container(user, src))
return NONE
//SKYRAT EDIT ADDITION START - CHEMISTRY QOL
var/obj/item/reagent_containers/container = tool
if(customTransferAmount)
transferAmounts -= customTransferAmount
transferAmounts = container.possible_transfer_amounts
//SKYRAT EDIT ADDITION END
if(!replace_beaker(user, tool))
return ITEM_INTERACT_BLOCKING
return NONE
ui_interact(user)
return ITEM_INTERACT_SUCCESS
/obj/machinery/chem_dispenser/get_cell()
return cell
@@ -541,15 +549,25 @@
parts_rating += servo.tier
power_cost = max(new_power_cost, 0.1 KILO WATTS)
/**
* Insert, remove, replace the existig beaker. Returns TRUE on success.
* Arguments:
*
* * mob/living/user - the player trying to replace the beaker
* * obj/item/reagent_containers/new_beaker - the beaker we are trying to insert, swap with existing or remove if null
*/
/obj/machinery/chem_dispenser/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker)
if(!user)
return FALSE
if(beaker)
if(!QDELETED(beaker))
try_put_in_hand(beaker, user)
beaker = null
if(new_beaker)
if(!QDELETED(new_beaker))
if(!user.transferItemToLoc(new_beaker, src))
update_appearance(UPDATE_OVERLAYS)
return FALSE
beaker = new_beaker
update_appearance()
update_appearance(UPDATE_OVERLAYS)
return TRUE
/obj/machinery/chem_dispenser/on_deconstruction(disassembled)
@@ -46,10 +46,10 @@
if(istype(held_item, /obj/item/reagent_containers/dropper) || istype(held_item, /obj/item/reagent_containers/syringe))
context[SCREENTIP_CONTEXT_LMB] = "Inject"
return CONTEXTUAL_SCREENTIP_SET
if(is_reagent_container(held_item) && held_item.is_open_container())
if(held_item.is_chem_container())
context[SCREENTIP_CONTEXT_LMB] = "Replace beaker"
return CONTEXTUAL_SCREENTIP_SET
else if(is_reagent_container(held_item) && held_item.is_open_container())
else if(held_item.is_chem_container())
context[SCREENTIP_CONTEXT_LMB] = "Insert beaker"
return CONTEXTUAL_SCREENTIP_SET
@@ -90,22 +90,22 @@
heater_coefficient *= micro_laser.tier
/obj/machinery/chem_heater/item_interaction(mob/living/user, obj/item/held_item, list/modifiers)
if(user.combat_mode && !is_reagent_container(held_item) && !held_item.is_open_container() || (held_item.item_flags & ABSTRACT) || (held_item.flags_1 & HOLOGRAM_1) || !user.can_perform_action(src, ALLOW_SILICON_REACH | FORBID_TELEKINESIS_REACH))
return NONE
if(!QDELETED(beaker))
if(istype(held_item, /obj/item/reagent_containers/dropper) || istype(held_item, /obj/item/reagent_containers/syringe))
var/obj/item/reagent_containers/injector = held_item
injector.interact_with_atom(beaker, user, modifiers)
return ITEM_INTERACT_SUCCESS
if(is_reagent_container(held_item) && held_item.is_open_container())
if(replace_beaker(user, held_item))
ui_interact(user)
balloon_alert(user, "beaker added")
return ITEM_INTERACT_SUCCESS
if(!held_item.can_insert_container(user, src))
return NONE
return NONE
if(!replace_beaker(user, held_item))
return ITEM_INTERACT_BLOCKING
ui_interact(user)
balloon_alert(user, "beaker added")
return ITEM_INTERACT_SUCCESS
/obj/machinery/chem_heater/wrench_act(mob/living/user, obj/item/tool)
if(user.combat_mode)
@@ -147,7 +147,7 @@
return attack_hand_secondary(user, modifiers)
/**
* Replace or eject the beaker inside this machine
* Replace or eject the beaker inside this machine. Returns TRUE on success.
* Arguments
* * mob/living/user - the player operating this machine
* * obj/item/reagent_containers/new_beaker - the new beaker to replace the current one if not null else it will just eject
@@ -160,12 +160,12 @@
if(!QDELETED(new_beaker))
if(!user.transferItemToLoc(new_beaker, src))
update_appearance()
update_appearance(UPDATE_ICON_STATE)
return FALSE
beaker = new_beaker
RegisterSignal(beaker.reagents, COMSIG_REAGENTS_REACTION_STEP, PROC_REF(on_reaction_step))
update_appearance()
update_appearance(UPDATE_ICON_STATE)
return TRUE
@@ -221,6 +221,8 @@
.["targetTemp"] = target_temperature
.["isActive"] = on
.["upgradeLevel"] = heater_coefficient * 10
var/obj/item/held_item = user.get_active_held_item()
.["hasBeakerInHand"] = held_item?.is_chem_container() || FALSE
var/list/beaker_data = null
var/chem_temp = 0
@@ -322,6 +324,13 @@
//Eject doesn't turn it off, so you can preheat for beaker swapping
return replace_beaker(ui.user)
if("insert")
var/obj/item/reagent_containers/container = ui.user.get_active_held_item()
if(container?.can_insert_container(ui.user, src))
replace_beaker(ui.user, container)
return TRUE
if("acidBuffer")
var/target = params["target"]
if(!target)
@@ -61,7 +61,7 @@
if(isnull(held_item) || (held_item.item_flags & ABSTRACT) || (held_item.flags_1 & HOLOGRAM_1))
return
if(is_reagent_container(held_item))
if(held_item.is_chem_container())
if(QDELETED(beaker1))
context[SCREENTIP_CONTEXT_LMB] = "Insert input beaker"
else
@@ -146,26 +146,22 @@
cms_coefficient /= laser.tier
/obj/machinery/chem_mass_spec/item_interaction(mob/living/user, obj/item/item, list/modifiers)
if((item.item_flags & ABSTRACT) || (item.flags_1 & HOLOGRAM_1) || !can_interact(user) || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH))
if(processing_reagents)
balloon_alert(user, "still processing!")
return ITEM_INTERACT_BLOCKING
if(!item.can_insert_container(user, src))
return NONE
if(is_reagent_container(item) && item.is_open_container())
if(processing_reagents)
balloon_alert(user, "still processing!")
return ITEM_INTERACT_BLOCKING
var/is_right_clicking = LAZYACCESS(modifiers, RIGHT_CLICK)
if(!replace_beaker(user, !is_right_clicking, item))
return ITEM_INTERACT_BLOCKING
var/obj/item/reagent_containers/beaker = item
if(!user.transferItemToLoc(beaker, src))
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You add [item] to [is_right_clicking ? "output" : "input"] slot."))
update_appearance()
ui_interact(user)
var/is_right_clicking = LAZYACCESS(modifiers, RIGHT_CLICK)
replace_beaker(user, !is_right_clicking, beaker)
to_chat(user, span_notice("You add [beaker] to [is_right_clicking ? "output" : "input"] slot."))
update_appearance()
ui_interact(user)
return ITEM_INTERACT_SUCCESS
return NONE
return ITEM_INTERACT_SUCCESS
/obj/machinery/chem_mass_spec/wrench_act(mob/living/user, obj/item/tool)
. = ITEM_INTERACT_BLOCKING
@@ -222,7 +218,7 @@
return smallest ? FLOOR(result, 50) : CEILING(result, 50)
/*
* Replaces a beaker in the machine, either input or output
* Replaces a beaker in the machine, either input or output. Returns TRUE on success.
* Arguments
*
* * user - The one bonking the machine
@@ -235,17 +231,29 @@
if(is_input) //replace input beaker
if(!QDELETED(beaker1))
try_put_in_hand(beaker1, user)
beaker1 = new_beaker
lower_mass_range = calculate_mass(smallest = TRUE)
upper_mass_range = calculate_mass(smallest = FALSE)
estimate_time()
if(!QDELETED(new_beaker))
if(!user.transferItemToLoc(new_beaker, src))
update_appearance(UPDATE_OVERLAYS)
return FALSE
beaker1 = new_beaker
lower_mass_range = calculate_mass(smallest = TRUE)
upper_mass_range = calculate_mass(smallest = FALSE)
estimate_time()
else //replace output beaker
if(!QDELETED(beaker2))
try_put_in_hand(beaker2, user)
beaker2 = new_beaker
log.Cut()
if(!QDELETED(new_beaker))
if(!user.transferItemToLoc(new_beaker, src))
update_appearance(UPDATE_OVERLAYS)
return FALSE
update_appearance()
beaker2 = new_beaker
log.Cut()
update_appearance(UPDATE_OVERLAYS)
return TRUE
///Computes time to purity reagents
/obj/machinery/chem_mass_spec/proc/estimate_time()
@@ -287,6 +295,8 @@
.["processing"] = processing_reagents
.["eta"] = delay_time - progress_time
.["peakHeight"] = 0
var/obj/item/held_item = user.get_active_held_item()
.["hasBeakerInHand"] = held_item?.is_chem_container() || FALSE
//input reagents
var/list/beaker1Data = null
@@ -426,6 +436,20 @@
replace_beaker(ui.user, FALSE)
return TRUE
if("insert1")
var/obj/item/reagent_containers/container = ui.user.get_active_held_item()
if(container?.can_insert_container(ui.user, src))
replace_beaker(ui.user, TRUE, container)
return TRUE
if("insert2")
var/obj/item/reagent_containers/container = ui.user.get_active_held_item()
if(container?.can_insert_container(ui.user, src))
replace_beaker(ui.user, FALSE, container)
return TRUE
/obj/machinery/chem_mass_spec/click_alt(mob/living/user)
if(processing_reagents)
balloon_alert(user, "still processing!")
@@ -61,7 +61,7 @@
. = CONTEXTUAL_SCREENTIP_SET
return .
if(is_reagent_container(held_item) && held_item.is_open_container())
if(held_item.is_chem_container())
if(!QDELETED(beaker))
context[SCREENTIP_CONTEXT_LMB] = "Replace beaker"
else
@@ -179,18 +179,13 @@
return containers
/obj/machinery/chem_master/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(user.combat_mode && !is_reagent_container(tool) && !tool.is_open_container() || (tool.item_flags & ABSTRACT) || (tool.flags_1 & HOLOGRAM_1) || !can_interact(user) || !user.can_perform_action(src, ALLOW_SILICON_REACH | FORBID_TELEKINESIS_REACH))
if(!tool.can_insert_container(user, src))
return NONE
if(!replace_beaker(user, tool))
return ITEM_INTERACT_BLOCKING
if(is_reagent_container(tool) && tool.is_open_container())
replace_beaker(user, tool)
if(!panel_open)
ui_interact(user)
return ITEM_INTERACT_SUCCESS
else
return ITEM_INTERACT_BLOCKING
return NONE
ui_interact(user)
return ITEM_INTERACT_SUCCESS
/obj/machinery/chem_master/wrench_act(mob/living/user, obj/item/tool)
if(user.combat_mode)
@@ -230,7 +225,7 @@
return ITEM_INTERACT_SUCCESS
/**
* Insert, remove, replace the existig beaker
* Insert, remove, replace the existig beaker. Returns TRUE on success.
* Arguments
*
* * mob/living/user - the player trying to replace the beaker
@@ -241,10 +236,16 @@
if(!QDELETED(beaker))
try_put_in_hand(beaker, user)
if(!QDELETED(new_beaker) && user.transferItemToLoc(new_beaker, src))
if(!QDELETED(new_beaker))
if(!user.transferItemToLoc(new_beaker, src))
update_appearance(UPDATE_OVERLAYS)
return FALSE
beaker = new_beaker
update_appearance(UPDATE_OVERLAYS)
return TRUE
/obj/machinery/chem_master/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
@@ -302,6 +303,8 @@
.["printingProgress"] = printing_progress
.["printingTotal"] = printing_total
.["selectedPillDuration"] = pill_layers
var/obj/item/held_item = user.get_active_held_item()
.["hasBeakerInHand"] = held_item?.is_chem_container() || FALSE
//contents of source beaker
var/list/beaker_data = null
@@ -428,6 +431,13 @@
replace_beaker(ui.user)
return TRUE
if("insert")
var/obj/item/reagent_containers/container = ui.user.get_active_held_item()
if(container?.can_insert_container(ui.user, src))
replace_beaker(ui.user, container)
return TRUE
if("transfer")
if(is_printing)
say("The buffer is locked while printing.")
@@ -333,7 +333,7 @@
/datum/wound/burn/flesh/tea_life_process()
// Sanitizes and heals, but with a limit
flesh_healing = (flesh_healing > 0.1) ? flesh_healing : flesh_healing + 0.02
infestation_rate = max(infestation_rate - 0.005, 0)
infection_rate = max(infection_rate - 0.005, 0)
return TRUE
/datum/reagent/consumable/lemonade
@@ -516,8 +516,8 @@
/datum/wound/burn/flesh/on_salt(reac_volume)
// Slightly sanitizes and disinfects, but also increases infestation rate (some bacteria are aided by salt), and decreases flesh healing (can damage the skin from moisture absorption)
sanitization += VALUE_PER(0.4, 30) * reac_volume
infestation -= max(VALUE_PER(0.3, 30) * reac_volume, 0)
infestation_rate += VALUE_PER(0.12, 30) * reac_volume
infection -= max(VALUE_PER(0.3, 30) * reac_volume, 0)
infection_rate += VALUE_PER(0.12, 30) * reac_volume
flesh_healing -= max(VALUE_PER(5, 30) * reac_volume, 0)
to_chat(victim, span_notice("The salt bits seep in and stick to [LOWER_TEXT(src)], painfully irritating the skin! After a few moments, it feels marginally better."))
@@ -682,7 +682,7 @@
/datum/wound/burn/flesh/on_flour(reac_volume)
to_chat(victim, span_notice("The flour seeps into [LOWER_TEXT(src)], spiking you with intense pain! That probably wasn't a good idea..."))
sanitization -= min(0, 1)
infestation += 0.2
infection += 0.2
return
/datum/reagent/consumable/flour/expose_turf(turf/exposed_turf, reac_volume)
@@ -786,7 +786,7 @@
/datum/wound/burn/flesh/on_starch(reac_volume, mob/living/carbon/carbies)
to_chat(carbies, span_notice("The slimey starch seeps into [LOWER_TEXT(src)], spiking you with intense pain! That probably wasn't a good idea..."))
sanitization -= min(0, 0.5)
infestation += 0.1
infection += 0.1
return
/datum/reagent/consumable/corn_syrup
@@ -295,8 +295,8 @@
/datum/wound/burn/flesh/on_saltwater(reac_volume)
// Similar but better stats from normal salt.
sanitization += VALUE_PER(0.6, 30) * reac_volume
infestation -= max(VALUE_PER(0.5, 30) * reac_volume, 0)
infestation_rate += VALUE_PER(0.07, 30) * reac_volume
infection -= max(VALUE_PER(0.5, 30) * reac_volume, 0)
infection_rate += VALUE_PER(0.07, 30) * reac_volume
to_chat(victim, span_notice("The salt water splashes over [LOWER_TEXT(src)], soaking up the... miscellaneous fluids. It feels somewhat better afterwards."))
return
@@ -77,9 +77,12 @@
e.start(holder.my_atom)
holder.clear_reagents()
///Amount of meth required to make a crystal
#define METH_REQUIRED (10)
/datum/chemical_reaction/meth_crystal //Since the meth is a cooled pharmaceutical solvent, this precipitates it into a solid.
results = list(/datum/reagent/consumable/ethanol = 1) //we don't care what this reagent is. We only need to record its purity
required_reagents = list(/datum/reagent/drug/methamphetamine = 10, /datum/reagent/toxin/acid = 2)
required_reagents = list(/datum/reagent/drug/methamphetamine = METH_REQUIRED, /datum/reagent/toxin/acid = 2)
mob_react = FALSE
reaction_flags = REACTION_INSTANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_DRUG | REACTION_TAG_ORGAN | REACTION_TAG_DAMAGING
@@ -94,15 +97,18 @@
new_crystal.pixel_y = rand(-6, 6)
new_crystal.color = gradient("#FAFAFA", "#78C8FA", dummy_reagent.creation_purity)
var/meth_amt = 10
new_crystal.reagents.add_reagent(/datum/reagent/drug/methamphetamine, meth_amt)
var/imp_amt = round(meth_amt * (1 - dummy_reagent.creation_purity) * 0.25, 0.1)
var/datum/reagents/crystal_reagents = new_crystal.reagents
crystal_reagents.clear_reagents()
crystal_reagents.add_reagent(/datum/reagent/drug/methamphetamine, METH_REQUIRED)
var/imp_amt = METH_REQUIRED * (1 - dummy_reagent.creation_purity) * 0.25
if(imp_amt > 0)
new_crystal.reagents.add_reagent(/datum/reagent/consumable/failed_reaction, imp_amt)
crystal_reagents.add_reagent(/datum/reagent/consumable/failed_reaction, imp_amt)
dummy_reagent.volume = 0
holder.update_total()
#undef METH_REQUIRED
/datum/chemical_reaction/bath_salts
results = list(/datum/reagent/drug/bath_salts = 7)
required_reagents = list(/datum/reagent/toxin/bad_food = 1, /datum/reagent/saltpetre = 1, /datum/reagent/consumable/nutriment = 1, /datum/reagent/space_cleaner = 1, /datum/reagent/consumable/enzyme = 1, /datum/reagent/consumable/tea = 1, /datum/reagent/mercury = 1)
@@ -212,12 +212,19 @@
/datum/chemical_reaction/emp_pulse/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
//pretending this reaction took two ingredients and not three for its effects
var/turf/turf = get_turf(holder.my_atom)
var/two_thirds = created_volume / 1.5
var/location = get_turf(holder.my_atom)
var/lastkey = holder.my_atom.fingerprintslast
// 100 created volume = 4 heavy range & 7 light range. A few tiles smaller than traitor EMP grandes.
// 200 created volume = 8 heavy range & 14 light range. 4 tiles larger than traitor EMP grenades.
empulse(location, round(two_thirds / 12), round(two_thirds / 7), 1)
empulse(location, round(two_thirds / 12), round(two_thirds / 7), 1, emp_source = src)
holder.clear_reagents()
if(lastkey)
var/mob/toucher = get_mob_by_key(lastkey)
toucher.log_message("triggered EMP reaction at [AREACOORD(turf)].", LOG_GAME, log_globally = FALSE)
..()
/datum/chemical_reaction/beesplosion
required_reagents = list(/datum/reagent/consumable/honey = 1, /datum/reagent/medicine/strange_reagent = 1, /datum/reagent/uranium/radium = 1)
@@ -264,7 +264,12 @@
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_SLIME | REACTION_TAG_DANGEROUS
/datum/chemical_reaction/slime/slimeoverload/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
empulse(get_turf(holder.my_atom), 3, 5)
var/turf/turf = get_turf(holder.my_atom)
var/lastkey = holder.my_atom.fingerprintslast
empulse(get_turf(holder.my_atom), 3, 5, emp_source = src)
if(lastkey)
var/mob/toucher = get_mob_by_key(lastkey)
toucher.log_message("triggered EMP reaction at [AREACOORD(turf)].", LOG_GAME, log_globally = FALSE)
..()
/datum/chemical_reaction/slime/slimecell
@@ -430,3 +430,6 @@
SEND_SIGNAL(src, COMSIG_REAGENTS_CUP_TRANSFER_FROM, target)
target.update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/item/reagent_containers/is_chem_container()
return is_open_container() && !(item_flags & ABSTRACT) && !(flags_1 & HOLOGRAM_1)