diff --git a/code/datums/components/plumbing/_plumbing.dm b/code/datums/components/plumbing/_plumbing.dm index 6ceefcbccb8..d0636a0719b 100644 --- a/code/datums/components/plumbing/_plumbing.dm +++ b/code/datums/components/plumbing/_plumbing.dm @@ -21,9 +21,13 @@ var/recipient_reagents_holder ///How do we apply the new reagents to the receiver? Generally doesn't matter, but some stuff, like people, does care if its injected or whatevs var/methods + ///What color is our demand connect? Also it's not auto-colored so you'll have to make new sprites if its anything other than red, blue, yellow or green + var/demand_color = "red" + ///What color is our supply connect? Also, refrain from pointlessly using non-standard colors unless it's really funny or something + var/supply_color = "blue" ///turn_connects is for wheter or not we spin with the object to change our pipes -/datum/component/plumbing/Initialize(start=TRUE, _turn_connects=TRUE, _ducting_layer) +/datum/component/plumbing/Initialize(start=TRUE, _turn_connects=TRUE, _ducting_layer, datum/reagents/custom_receiver) if(!ismovable(parent)) return COMPONENT_INCOMPATIBLE @@ -31,12 +35,16 @@ ducting_layer = ducting_layer var/atom/movable/AM = parent - if(!AM.reagents) + if(!AM.reagents && !custom_receiver) return COMPONENT_INCOMPATIBLE + reagents = AM.reagents turn_connects = _turn_connects - recipient_reagents_holder = AM.reagents + if(custom_receiver) + recipient_reagents_holder = custom_receiver + else + recipient_reagents_holder = AM.reagents RegisterSignal(parent, list(COMSIG_MOVABLE_MOVED,COMSIG_PARENT_PREQDELETED), .proc/disable) RegisterSignal(parent, list(COMSIG_OBJ_DEFAULT_UNFASTEN_WRENCH), .proc/toggle_active) @@ -145,9 +153,9 @@ var/color var/direction if(D & initial(demand_connects)) - color = "red" //red because red is mean and it takes + color = demand_color else if(D & initial(supply_connects)) - color = "blue" //blue is nice and gives + color = supply_color else continue @@ -164,10 +172,10 @@ direction = "west" if(turn_connects) - I = image('icons/obj/plumbing/plumbers.dmi', "[direction]-[color]", layer = AM.layer - 1) + I = image('icons/obj/plumbing/connects.dmi', "[direction]-[color]", layer = AM.layer - 1) else - I = image('icons/obj/plumbing/plumbers.dmi', "[direction]-[color]-s", layer = AM.layer - 1) //color is not color as in the var, it's just the name of the icon_state + I = image('icons/obj/plumbing/connects.dmi', "[direction]-[color]-s", layer = AM.layer - 1) //color is not color as in the var, it's just the name of the icon_state I.dir = D I.pixel_x = duct_x @@ -308,3 +316,21 @@ /datum/component/plumbing/tank demand_connects = WEST supply_connects = EAST + +#define READY 2 +///Baby component for the buffer plumbing machine +/datum/component/plumbing/buffer + demand_connects = WEST + supply_connects = EAST + +/datum/component/plumbing/buffer/Initialize(start=TRUE, _turn_connects=TRUE, _ducting_layer, datum/reagents/custom_receiver) + if(!istype(parent, /obj/machinery/plumbing/buffer)) + return COMPONENT_INCOMPATIBLE + + return ..() + +/datum/component/plumbing/buffer/can_give(amount, reagent, datum/ductnet/net) + var/obj/machinery/plumbing/buffer/buffer = parent + return (buffer.mode == READY) ? ..() : FALSE + +#undef READY diff --git a/code/datums/components/plumbing/reaction_chamber.dm b/code/datums/components/plumbing/reaction_chamber.dm index 07ee12b108e..2366c0dbfe1 100644 --- a/code/datums/components/plumbing/reaction_chamber.dm +++ b/code/datums/components/plumbing/reaction_chamber.dm @@ -1,6 +1,6 @@ /datum/component/plumbing/reaction_chamber - demand_connects = WEST - supply_connects = EAST + demand_connects = NORTH + supply_connects = SOUTH /datum/component/plumbing/reaction_chamber/Initialize(start=TRUE, _turn_connects=TRUE) . = ..() @@ -10,25 +10,18 @@ /datum/component/plumbing/reaction_chamber/can_give(amount, reagent, datum/ductnet/net) . = ..() var/obj/machinery/plumbing/reaction_chamber/RC = parent - if(!. || !RC.emptying) + if(!. || !RC.emptying || reagents.is_reacting == TRUE) return FALSE /datum/component/plumbing/reaction_chamber/send_request(dir) var/obj/machinery/plumbing/reaction_chamber/RC = parent - if(RC.emptying || !LAZYLEN(RC.required_reagents)) + if(RC.emptying) + return + + process_request(amount = min(MACHINE_REAGENT_TRANSFER, RC.target_volume - reagents.total_volume), dir = dir) + + if(RC.target_volume > round(reagents.total_volume, CHEMICAL_VOLUME_ROUNDING)) //not enough yet return - for(var/RT in RC.required_reagents) - var/has_reagent = FALSE - for(var/A in reagents.reagent_list) - var/datum/reagent/RD = A - if(RT == RD.type) - has_reagent = TRUE - if(RD.volume < RC.required_reagents[RT]) - process_request(min(RC.required_reagents[RT] - RD.volume, MACHINE_REAGENT_TRANSFER) , RT, dir) - return - if(!has_reagent) - process_request(min(RC.required_reagents[RT], MACHINE_REAGENT_TRANSFER), RT, dir) - return reagents.flags &= ~NO_REACT reagents.handle_reactions() @@ -37,8 +30,24 @@ //everything for every chemical removed, wich isn't a good option either. RC.on_reagent_change(reagents) //We need to check it now, because some reactions leave nothing left. +///Special connect that we currently use for reaction chambers. Being used so we can keep certain inputs seperate, like into a special internal acid container +/datum/component/plumbing/acidic_input + demand_connects = WEST + demand_color = "yellow" + + ducting_layer = SECOND_DUCT_LAYER + +/datum/component/plumbing/acidic_input/send_request(dir) + process_request(amount = MACHINE_REAGENT_TRANSFER, reagent = /datum/reagent/reaction_agent/acidic_buffer, dir = dir) + +///Special connect that we currently use for reaction chambers. Being used so we can keep certain inputs seperate, like into a special internal base container +/datum/component/plumbing/alkaline_input + demand_connects = EAST + demand_color = "green" + + ducting_layer = SECOND_DUCT_LAYER + +/datum/component/plumbing/alkaline_input/send_request(dir) + process_request(amount = MACHINE_REAGENT_TRANSFER, reagent = /datum/reagent/reaction_agent/basic_buffer, dir = dir) + -/datum/component/plumbing/reaction_chamber/transfer_to(datum/component/plumbing/target, amount, reagent, datum/ductnet/net) - if(reagents.is_reacting == TRUE) //Let the thing react in peace - return - return ..() diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm index aee6fcbeaa0..a3f36d3f485 100644 --- a/code/game/objects/items/RCD.dm +++ b/code/game/objects/items/RCD.dm @@ -1021,9 +1021,10 @@ RLD /obj/machinery/plumbing/tank = 20, /obj/machinery/plumbing/synthesizer = 15, /obj/machinery/plumbing/reaction_chamber = 15, - /obj/machinery/plumbing/acclimator = 10, + /obj/machinery/plumbing/buffer = 10, /obj/machinery/plumbing/pill_press = 20, //Above are the most common machinery which is shown on the first cycle. Keep new additions below THIS line + /obj/machinery/plumbing/acclimator = 10, /obj/machinery/plumbing/bottler = 50, /obj/machinery/plumbing/disposer = 10, /obj/machinery/plumbing/fermenter = 30, diff --git a/code/game/objects/structures/lavaland/geyser.dm b/code/game/objects/structures/lavaland/geyser.dm index fb1c6aa1d0e..22bee6024d7 100644 --- a/code/game/objects/structures/lavaland/geyser.dm +++ b/code/game/objects/structures/lavaland/geyser.dm @@ -76,8 +76,7 @@ var/target_layer = DUCT_LAYER_DEFAULT ///Assoc list for possible layers - var/list/layers = list("First Layer" = FIRST_DUCT_LAYER, "Second Layer" = SECOND_DUCT_LAYER, "Default Layer" = DUCT_LAYER_DEFAULT, - "Fourth Layer" = FOURTH_DUCT_LAYER, "Fifth Layer" = FIFTH_DUCT_LAYER) + var/list/layers = list("Alternate Layer" = SECOND_DUCT_LAYER, "Default Layer" = DUCT_LAYER_DEFAULT) /obj/item/plunger/attack_obj(obj/O, mob/living/user, params) if(layer_mode) diff --git a/code/modules/plumbing/ducts.dm b/code/modules/plumbing/ducts.dm index a98d6413295..8a3399c9f2b 100644 --- a/code/modules/plumbing/ducts.dm +++ b/code/modules/plumbing/ducts.dm @@ -89,10 +89,10 @@ All the important duct code: if(istype(AM, /obj/machinery/duct)) return connect_duct(AM, direction, ignore_color) - var/plumber = AM.GetComponent(/datum/component/plumbing) - if(!plumber) - return - return connect_plumber(plumber, direction) + for(var/plumber in AM.GetComponents(/datum/component/plumbing)) + if(!plumber) //apparently yes it will be null hahahaasahsdvashufv + return + . += connect_plumber(plumber, direction) //so that if one is true, all is true. beautiful. ///connect to a duct /obj/machinery/duct/proc/connect_duct(obj/machinery/duct/D, direction, ignore_color) @@ -402,8 +402,7 @@ All the important duct code: ///Default layer of our duct var/duct_layer = "Default Layer" ///Assoc index with all the available layers. yes five might be a bit much. Colors uses a global by the way - var/list/layers = list("First Layer" = FIRST_DUCT_LAYER, "Second Layer" = SECOND_DUCT_LAYER, "Default Layer" = DUCT_LAYER_DEFAULT, - "Fourth Layer" = FOURTH_DUCT_LAYER, "Fifth Layer" = FIFTH_DUCT_LAYER) + var/list/layers = list("Alternate Layer" = SECOND_DUCT_LAYER, "Default Layer" = DUCT_LAYER_DEFAULT) /obj/item/stack/ducts/examine(mob/user) . = ..() diff --git a/code/modules/plumbing/plumbers/plumbing_buffer.dm b/code/modules/plumbing/plumbers/plumbing_buffer.dm new file mode 100644 index 00000000000..4cece32aff6 --- /dev/null +++ b/code/modules/plumbing/plumbers/plumbing_buffer.dm @@ -0,0 +1,122 @@ +#define UNREADY 0 +#define IDLE 1 +#define READY 2 + +/obj/machinery/plumbing/buffer + name = "automatic buffer" + desc = "A chemical holding tank that waits for neighbouring automatic buffers to complete before allowing a withdrawal. Connect/reset by screwdrivering" + icon_state = "buffer" + buffer = 200 + + var/datum/buffer_net/buffer_net + var/activation_volume = 100 + var/mode + +/obj/machinery/plumbing/buffer/Initialize(mapload, bolt) + . = ..() + + AddComponent(/datum/component/plumbing/buffer, bolt) + +/obj/machinery/plumbing/buffer/create_reagents(max_vol, flags) + . = ..() + RegisterSignal(reagents, list(COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_REM_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_CLEAR_REAGENTS, COMSIG_REAGENTS_REACTED), .proc/on_reagent_change) + RegisterSignal(reagents, COMSIG_PARENT_QDELETING, .proc/on_reagents_del) + +/// Handles properly detaching signal hooks. +/obj/machinery/plumbing/buffer/proc/on_reagents_del(datum/reagents/reagents) + SIGNAL_HANDLER + UnregisterSignal(reagents, list(COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_REM_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_CLEAR_REAGENTS, COMSIG_REAGENTS_REACTED, COMSIG_PARENT_QDELETING)) + return NONE + +/obj/machinery/plumbing/buffer/proc/on_reagent_change() + if(!buffer_net) + return + if(reagents.total_volume >= activation_volume && mode == UNREADY) + mode = IDLE + buffer_net.check_active() + + else if(reagents.total_volume < activation_volume && mode != UNREADY) + mode = UNREADY + buffer_net.check_active() + +/obj/machinery/plumbing/buffer/update_icon() + . = ..() + icon_state = initial(icon_state) + if(buffer_net) + switch(mode) + if(UNREADY) + icon_state += "_red" + if(IDLE) + icon_state += "_yellow" + if(READY) + icon_state += "_green" + +/obj/machinery/plumbing/buffer/proc/attempt_connect() + + for(var/direction in GLOB.cardinals) + var/turf/T = get_step(src, direction) + for(var/atom/movable/movable in T) + if(istype(movable, /obj/machinery/plumbing/buffer)) + var/obj/machinery/plumbing/buffer/neighbour = movable + if(neighbour.buffer_net != buffer_net) + neighbour.buffer_net?.destruct() + //we could put this on a proc, but its so simple I dont think its worth the overhead + buffer_net.buffer_list += neighbour + neighbour.buffer_net = buffer_net + neighbour.attempt_connect() //technically this would runtime if you made about 200~ buffers + + add_overlay(icon_state + "_alert") + addtimer(CALLBACK(src, /atom/.proc/cut_overlay, icon_state + "_alert"), 20) + +/obj/machinery/plumbing/buffer/attack_hand_secondary(mob/user, modifiers) + . = ..() + + var/new_volume = input(user, "Enter new activation threshold", "Beepityboop", activation_volume) as num|null + if(!new_volume) + return + + activation_volume = round(clamp(new_volume, 0, buffer)) + to_chat(user, "New activation threshold is now [activation_volume].") + +/obj/machinery/plumbing/buffer/attackby(obj/item/item, mob/user, params) + if(item.tool_behaviour == TOOL_SCREWDRIVER) + to_chat(user, "You reset the automatic buffer.") + + //reset the net + buffer_net?.destruct() + buffer_net = new() + LAZYADD(buffer_net.buffer_list, src) + + attempt_connect() + else + return . = ..() + +/obj/machinery/plumbing/buffer/doMove(destination) + . = ..() + buffer_net?.destruct() + +/datum/buffer_net + var/list/obj/machinery/plumbing/buffer/buffer_list + +/datum/buffer_net/proc/destruct() + for(var/obj/machinery/plumbing/buffer/buffer in buffer_list) + buffer.buffer_net = null + buffer_list.Cut() + qdel(src) + +/datum/buffer_net/proc/check_active() + var/ready = TRUE + for(var/obj/machinery/plumbing/buffer/buffer in buffer_list) + if(buffer.mode == UNREADY) + ready = FALSE + break + for(var/obj/machinery/plumbing/buffer/buffer in buffer_list) + if(buffer.mode == READY && !ready) + buffer.mode = IDLE + else if(buffer.mode == IDLE && ready) + buffer.mode = READY + buffer.update_icon() + +#undef UNREADY +#undef IDLE +#undef READY diff --git a/code/modules/plumbing/plumbers/reaction_chamber.dm b/code/modules/plumbing/plumbers/reaction_chamber.dm index 75cfcfca2eb..1f4f0446c5f 100644 --- a/code/modules/plumbing/plumbers/reaction_chamber.dm +++ b/code/modules/plumbing/plumbers/reaction_chamber.dm @@ -6,10 +6,12 @@ buffer = 200 reagent_flags = TRANSPARENT | NO_REACT - /**list of set reagents that the reaction_chamber allows in, and must all be present before mixing is enabled. - * example: list(/datum/reagent/water = 20, /datum/reagent/fuel/oil = 50) - */ - var/list/required_reagents = list() + ///At what volume do we start reacting? + var/target_volume = 200 + ///If above this pH, we start dumping buffer into it + var/acidic_limit = 9 + ///If below this pH, we start dumping buffer into it + var/alkaline_limit = 5 ///our reagent goal has been reached, so now we lock our inputs and start emptying var/emptying = FALSE @@ -17,11 +19,21 @@ var/target_temperature = 300 ///cool/heat power var/heater_coefficient = 0.05 //same lvl as acclimator + ///Beaker that holds the acidic buffer. I don't want to deal with snowflaking so it's just a seperate thing. It's a small (50u) beaker + var/obj/item/reagent_containers/glass/beaker/acidic_beaker + ///beaker that holds the alkaline buffer. + var/obj/item/reagent_containers/glass/beaker/alkaline_beaker /obj/machinery/plumbing/reaction_chamber/Initialize(mapload, bolt) . = ..() AddComponent(/datum/component/plumbing/reaction_chamber, bolt) + acidic_beaker = new (src) + alkaline_beaker = new (src) + + AddComponent(/datum/component/plumbing/acidic_input, bolt, custom_receiver = acidic_beaker) + AddComponent(/datum/component/plumbing/alkaline_input, bolt, custom_receiver = alkaline_beaker) + /obj/machinery/plumbing/reaction_chamber/create_reagents(max_vol, flags) . = ..() RegisterSignal(reagents, list(COMSIG_REAGENTS_REM_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_CLEAR_REAGENTS, COMSIG_REAGENTS_REACTED), .proc/on_reagent_change) @@ -43,6 +55,11 @@ return NONE /obj/machinery/plumbing/reaction_chamber/process(delta_time) + if(!reagents.is_reacting && reagents.ph < alkaline_limit) + alkaline_beaker.reagents.trans_to(reagents, 1 * delta_time) + if(!reagents.is_reacting && reagents.ph > acidic_limit) + acidic_beaker.reagents.trans_to(reagents, 1 * delta_time) + if(!emptying) //suspend heating/cooling during emptying phase reagents.adjust_thermal_energy((target_temperature - reagents.chem_temp) * heater_coefficient * delta_time * SPECIFIC_HEAT_DEFAULT * reagents.total_volume) //keep constant with chem heater reagents.handle_reactions() @@ -62,17 +79,15 @@ /obj/machinery/plumbing/reaction_chamber/ui_data(mob/user) var/list/data = list() - var/list/text_reagents = list() - for(var/A in required_reagents) //make a list where the key is text, because that looks alot better in the ui than a typepath - var/datum/reagent/R = A - text_reagents[initial(R.name)] = required_reagents[R] - data["reagents"] = text_reagents data["emptying"] = emptying data["temperature"] = round(reagents.chem_temp, 0.1) data["ph"] = round(reagents.ph, 0.01) data["targetTemp"] = target_temperature data["isReacting"] = reagents.is_reacting + data["reagentQuantity"] = target_volume + data["reagentAcidic"] = acidic_limit + data["reagentAlkaline"] = alkaline_limit return data /obj/machinery/plumbing/reaction_chamber/ui_act(action, params) @@ -81,16 +96,6 @@ return . = TRUE switch(action) - if("remove") - var/reagent = get_chem_id(params["chem"]) - if(reagent) - required_reagents.Remove(reagent) - if("add") - var/input_reagent = get_chem_id(params["chem"]) - if(input_reagent && !required_reagents.Find(input_reagent)) - var/input_amount = text2num(params["amount"]) - if(input_amount) - required_reagents[input_reagent] = input_amount if("temperature") var/target = params["target"] if(text2num(target) != null) @@ -98,3 +103,10 @@ . = TRUE if(.) target_temperature = clamp(target, 0, 1000) + if("volume") + target_volume = round(text2num(params["target"])) + if("acidic") + acidic_limit = round(text2num(params["target"])) + if("alkaline") + alkaline_limit = round(text2num(params["target"])) + diff --git a/icons/obj/plumbing/connects.dmi b/icons/obj/plumbing/connects.dmi new file mode 100644 index 00000000000..32277ffac65 Binary files /dev/null and b/icons/obj/plumbing/connects.dmi differ diff --git a/icons/obj/plumbing/plumbers.dmi b/icons/obj/plumbing/plumbers.dmi index e45942fc24f..35adac58da2 100644 Binary files a/icons/obj/plumbing/plumbers.dmi and b/icons/obj/plumbing/plumbers.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 0d26e61c299..4eb9f0caf7e 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2828,6 +2828,7 @@ #include "code\modules\plumbing\plumbers\filter.dm" #include "code\modules\plumbing\plumbers\grinder_chemical.dm" #include "code\modules\plumbing\plumbers\pill_press.dm" +#include "code\modules\plumbing\plumbers\plumbing_buffer.dm" #include "code\modules\plumbing\plumbers\pumps.dm" #include "code\modules\plumbing\plumbers\reaction_chamber.dm" #include "code\modules\plumbing\plumbers\splitters.dm" diff --git a/tgui/packages/tgui/interfaces/ChemReactionChamber.js b/tgui/packages/tgui/interfaces/ChemReactionChamber.js index 62d5ec94800..a63341b5d51 100644 --- a/tgui/packages/tgui/interfaces/ChemReactionChamber.js +++ b/tgui/packages/tgui/interfaces/ChemReactionChamber.js @@ -7,22 +7,17 @@ import { round, toFixed } from 'common/math'; export const ChemReactionChamber = (props, context) => { const { act, data } = useBackend(context); - const [ - reagentName, - setReagentName, - ] = useLocalState(context, 'reagentName', ''); - const [ - reagentQuantity, - setReagentQuantity, - ] = useLocalState(context, 'reagentQuantity', 1); + const { emptying, temperature, ph, targetTemp, isReacting, + reagentQuantity, + reagentAcidic, + reagentAlkaline, } = data; - const reagents = data.reagents || []; return ( { {(_, value) => ( - {
{ )}> - - setReagentName(value)} /> - - - setReagentQuantity(value)} /> - -