From 0f1aa3c0cad76c049c0b67fc4d3cd54b8dfbd7a3 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Wed, 22 Jul 2020 10:49:56 +1000 Subject: [PATCH 01/42] 'lolman? did you test your code?' no i did not ms. kevinz --- code/__DEFINES/dcs/signals.dm | 1 + code/__DEFINES/plumbing.dm | 9 + code/__DEFINES/reagents.dm | 4 + code/controllers/subsystem/fluid.dm | 5 + code/datums/components/plumbing/_plumbing.dm | 215 ++++++++++ .../plumbing/chemical_acclimator.dm | 21 + code/datums/components/plumbing/filter.dm | 59 +++ .../components/plumbing/reaction_chamber.dm | 44 +++ code/datums/components/plumbing/splitter.dm | 45 +++ code/datums/ductnet.dm | 60 +++ code/game/machinery/_machinery.dm | 2 + code/game/objects/items/RCD.dm | 75 ++++ code/game/objects/items/RPD.dm | 69 +++- .../items/stacks/sheets/sheet_types.dm | 1 + code/game/objects/objs.dm | 3 + code/game/objects/structures/geyser.dm | 74 ++++ code/modules/asset_cache/asset_list_items.dm | 3 +- code/modules/plumbing/ducts.dm | 371 ++++++++++++++++++ .../plumbing/plumbers/_plumb_machinery.dm | 97 +++++ code/modules/plumbing/plumbers/acclimator.dm | 105 +++++ code/modules/plumbing/plumbers/bottler.dm | 79 ++++ code/modules/plumbing/plumbers/destroyer.dm | 21 + code/modules/plumbing/plumbers/fermenter.dm | 52 +++ code/modules/plumbing/plumbers/filter.dm | 74 ++++ .../plumbing/plumbers/grinder_chemical.dm | 58 +++ code/modules/plumbing/plumbers/pill_press.dm | 107 +++++ code/modules/plumbing/plumbers/pumps.dm | 83 ++++ .../plumbing/plumbers/reaction_chamber.dm | 63 +++ code/modules/plumbing/plumbers/splitters.dm | 50 +++ code/modules/plumbing/plumbers/synthesizer.dm | 111 ++++++ code/modules/reagents/chemistry/holder.dm | 6 + code/modules/reagents/chemistry/reagents.dm | 2 +- code/modules/reagents/reagent_dispenser.dm | 37 +- .../research/designs/medical_designs.dm | 155 ++++++++ .../research/techweb/nodes/medical_nodes.dm | 17 + icons/obj/watercloset.dmi | Bin 23715 -> 16012 bytes 36 files changed, 2171 insertions(+), 7 deletions(-) create mode 100644 code/__DEFINES/plumbing.dm create mode 100644 code/controllers/subsystem/fluid.dm create mode 100644 code/datums/components/plumbing/_plumbing.dm create mode 100644 code/datums/components/plumbing/chemical_acclimator.dm create mode 100644 code/datums/components/plumbing/filter.dm create mode 100644 code/datums/components/plumbing/reaction_chamber.dm create mode 100644 code/datums/components/plumbing/splitter.dm create mode 100644 code/datums/ductnet.dm create mode 100644 code/game/objects/structures/geyser.dm create mode 100644 code/modules/plumbing/ducts.dm create mode 100644 code/modules/plumbing/plumbers/_plumb_machinery.dm create mode 100644 code/modules/plumbing/plumbers/acclimator.dm create mode 100644 code/modules/plumbing/plumbers/bottler.dm create mode 100644 code/modules/plumbing/plumbers/destroyer.dm create mode 100644 code/modules/plumbing/plumbers/fermenter.dm create mode 100644 code/modules/plumbing/plumbers/filter.dm create mode 100644 code/modules/plumbing/plumbers/grinder_chemical.dm create mode 100644 code/modules/plumbing/plumbers/pill_press.dm create mode 100644 code/modules/plumbing/plumbers/pumps.dm create mode 100644 code/modules/plumbing/plumbers/reaction_chamber.dm create mode 100644 code/modules/plumbing/plumbers/splitters.dm create mode 100644 code/modules/plumbing/plumbers/synthesizer.dm diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 1882effe38..cfd1906e82 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -270,6 +270,7 @@ #define COMSIG_OBJ_DECONSTRUCT "obj_deconstruct" //from base of obj/deconstruct(): (disassembled) #define COMSIG_OBJ_BREAK "obj_break" //from base of /obj/obj_break(): (damage_flag) #define COMSIG_OBJ_SETANCHORED "obj_setanchored" //called in /obj/structure/setAnchored(): (value) +#define COMSIG_OBJ_DEFAULT_UNFASTEN_WRENCH "obj_default_unfasten_wrench" //called exclusively in plumbing, for now #define COMSIG_OBJ_ATTACK_GENERIC "obj_attack_generic" //from base of atom/animal_attack(): (/mob/user) #define COMPONENT_STOP_GENERIC_ATTACK 1 diff --git a/code/__DEFINES/plumbing.dm b/code/__DEFINES/plumbing.dm new file mode 100644 index 0000000000..5ea7c46b42 --- /dev/null +++ b/code/__DEFINES/plumbing.dm @@ -0,0 +1,9 @@ +#define FIRST_DUCT_LAYER 1 +#define SECOND_DUCT_LAYER 2 +#define THIRD_DUCT_LAYER 4 +#define FOURTH_DUCT_LAYER 8 +#define FIFTH_DUCT_LAYER 16 + +#define DUCT_LAYER_DEFAULT THIRD_DUCT_LAYER + +#define MACHINE_REAGENT_TRANSFER 10 diff --git a/code/__DEFINES/reagents.dm b/code/__DEFINES/reagents.dm index f4beef7ee8..44e97ef345 100644 --- a/code/__DEFINES/reagents.dm +++ b/code/__DEFINES/reagents.dm @@ -57,6 +57,10 @@ #define ADD_REAGENT 2 // reagent added #define REM_REAGENT 3 // reagent removed (may still exist) + +#define PILL_STYLE_COUNT 22 //Update this if you add more pill icons or you die (literally, we'll toss a nuke at whever your ip turns up) +#define RANDOM_PILL_STYLE 22 //Dont change this one though + #define THRESHOLD_UNHUSK 50 // health threshold for synthflesh/rezadone to unhusk someone //reagent bitflags, used for altering how they works diff --git a/code/controllers/subsystem/fluid.dm b/code/controllers/subsystem/fluid.dm new file mode 100644 index 0000000000..c4fa13d693 --- /dev/null +++ b/code/controllers/subsystem/fluid.dm @@ -0,0 +1,5 @@ +PROCESSING_SUBSYSTEM_DEF(fluids) + name = "Fluids" + wait = 20 + stat_tag = "FD" //its actually Fluid Ducts + flags = SS_NO_INIT | SS_TICKER diff --git a/code/datums/components/plumbing/_plumbing.dm b/code/datums/components/plumbing/_plumbing.dm new file mode 100644 index 0000000000..22b8bafde5 --- /dev/null +++ b/code/datums/components/plumbing/_plumbing.dm @@ -0,0 +1,215 @@ +/datum/component/plumbing + ///Index with "1" = /datum/ductnet/theductpointingnorth etc. "1" being the num2text from NORTH define + var/list/datum/ductnet/ducts = list() + ///shortcut to our parents' reagent holder + var/datum/reagents/reagents + ///TRUE if we wanna add proper pipe outless under our parent object. this is pretty good if i may so so myself + var/use_overlays = TRUE + ///We can't just cut all of the parents' overlays, so we'll track them here + var/list/image/ducterlays + ///directions in wich we act as a supplier + var/supply_connects + ///direction in wich we act as a demander + var/demand_connects + ///FALSE to pretty much just not exist in the plumbing world so we can be moved, TRUE to go plumbo mode + var/active = FALSE + ///if TRUE connects will spin with the parent object visually and codually, so you can have it work in any direction. FALSE if you want it to be static + var/turn_connects = TRUE + +/datum/component/plumbing/Initialize(start=TRUE, _turn_connects=TRUE) //turn_connects for wheter or not we spin with the object to change our pipes + if(parent && !ismovableatom(parent)) + return COMPONENT_INCOMPATIBLE + var/atom/movable/AM = parent + if(!AM.reagents) + return COMPONENT_INCOMPATIBLE + reagents = AM.reagents + turn_connects = _turn_connects + + RegisterSignal(parent, list(COMSIG_MOVABLE_MOVED,COMSIG_PARENT_PREQDELETED), .proc/disable) + RegisterSignal(parent, list(COMSIG_OBJ_DEFAULT_UNFASTEN_WRENCH), .proc/toggle_active) + + if(start) + enable() + + if(use_overlays) + create_overlays() + +/datum/component/plumbing/process() + if(!demand_connects || !reagents) + STOP_PROCESSING(SSfluids, src) + return + if(reagents.total_volume < reagents.maximum_volume) + for(var/D in GLOB.cardinals) + if(D & demand_connects) + send_request(D) +///Can we be added to the ductnet? +/datum/component/plumbing/proc/can_add(datum/ductnet/D, dir) + if(!active) + return + if(!dir || !D) + return FALSE + if(num2text(dir) in ducts) + return FALSE + + return TRUE +///called from in process(). only calls process_request(), but can be overwritten for children with special behaviour +/datum/component/plumbing/proc/send_request(dir) + process_request(amount = MACHINE_REAGENT_TRANSFER, reagent = null, dir = dir) +///check who can give us what we want, and how many each of them will give us +/datum/component/plumbing/proc/process_request(amount, reagent, dir) + var/list/valid_suppliers = list() + var/datum/ductnet/net + if(!ducts.Find(num2text(dir))) + return + net = ducts[num2text(dir)] + for(var/A in net.suppliers) + var/datum/component/plumbing/supplier = A + if(supplier.can_give(amount, reagent, net)) + valid_suppliers += supplier + for(var/A in valid_suppliers) + var/datum/component/plumbing/give = A + give.transfer_to(src, amount / valid_suppliers.len, reagent, net) +///returns TRUE when they can give the specified amount and reagent. called by process request +/datum/component/plumbing/proc/can_give(amount, reagent, datum/ductnet/net) + if(amount <= 0) + return + + if(reagent) //only asked for one type of reagent + for(var/A in reagents.reagent_list) + var/datum/reagent/R = A + if(R.id == reagent) + return TRUE + else if(reagents.total_volume > 0) //take whatever + return TRUE +///this is where the reagent is actually transferred and is thus the finish point of our process() +/datum/component/plumbing/proc/transfer_to(datum/component/plumbing/target, amount, reagent, datum/ductnet/net) + if(!reagents || !target || !target.reagents) + return FALSE + if(reagent) + reagents.trans_id_to(target.parent, reagent, amount) + else + reagents.trans_to(target.parent, amount) +///We create our luxurious piping overlays/underlays, to indicate where we do what. only called once if use_overlays = TRUE in Initialize() +/datum/component/plumbing/proc/create_overlays() + var/atom/movable/AM = parent + for(var/image/I in ducterlays) + AM.overlays.Remove(I) + qdel(I) + ducterlays = list() + for(var/D in GLOB.cardinals) + var/color + var/direction + if(D & demand_connects) + color = "red" //red because red is mean and it takes + else if(D & supply_connects) + color = "blue" //blue is nice and gives + else + continue + var/image/I + if(turn_connects) + switch(D) + if(NORTH) + direction = "north" + if(SOUTH) + direction = "south" + if(EAST) + direction = "east" + if(WEST) + direction = "west" + I = image('icons/obj/plumbing/plumbers.dmi', "[direction]-[color]", layer = AM.layer - 1) + else + I = image('icons/obj/plumbing/plumbers.dmi', color, layer = AM.layer - 1) //color is not color as in the var, it's just the name + I.dir = D + AM.add_overlay(I) + ducterlays += I +///we stop acting like a plumbing thing and disconnect if we are, so we can safely be moved and stuff +/datum/component/plumbing/proc/disable() + if(!active) + return + STOP_PROCESSING(SSfluids, src) + for(var/A in ducts) + var/datum/ductnet/D = ducts[A] + D.remove_plumber(src) + active = FALSE + for(var/D in GLOB.cardinals) + if(D & (demand_connects | supply_connects)) + for(var/obj/machinery/duct/duct in get_step(parent, D)) + duct.attempt_connect() + +///settle wherever we are, and start behaving like a piece of plumbing +/datum/component/plumbing/proc/enable() + if(active) + return + update_dir() + active = TRUE + var/atom/movable/AM = parent + for(var/obj/machinery/duct/D in AM.loc) //Destroy any ducts under us. Ducts also self destruct if placed under a plumbing machine. machines disable when they get moved + if(D.anchored) //that should cover everything + D.disconnect_duct() + + if(demand_connects) + START_PROCESSING(SSfluids, src) + + for(var/D in GLOB.cardinals) + if(D & (demand_connects | supply_connects)) + for(var/atom/movable/A in get_step(parent, D)) + if(istype(A, /obj/machinery/duct)) + var/obj/machinery/duct/duct = A + duct.attempt_connect() + else + var/datum/component/plumbing/P = A.GetComponent(/datum/component/plumbing) + if(P) + direct_connect(P, D) + +/// Toggle our machinery on or off. This is called by a hook from default_unfasten_wrench with anchored as only param, so we dont have to copypaste this on every object that can move +/datum/component/plumbing/proc/toggle_active(obj/O, new_state) + if(new_state) + enable() + else + disable() +/** We update our connects only when we settle down by taking our current and original direction to find our new connects +* If someone wants it to fucking spin while connected to something go actually knock yourself out +*/ +/datum/component/plumbing/proc/update_dir() + if(!turn_connects) + return + var/atom/movable/AM = parent + var/new_demand_connects + var/new_supply_connects + var/new_dir = AM.dir + var/angle = 180 - dir2angle(new_dir) + if(new_dir == SOUTH) + demand_connects = initial(demand_connects) + supply_connects = initial(supply_connects) + else + for(var/D in GLOB.cardinals) + if(D & initial(demand_connects)) + new_demand_connects += turn(D, angle) + if(D & initial(supply_connects)) + new_supply_connects += turn(D, angle) + demand_connects = new_demand_connects + supply_connects = new_supply_connects +///Give the direction of a pipe, and it'll return wich direction it originally was when it's object pointed SOUTH +/datum/component/plumbing/proc/get_original_direction(dir) + var/atom/movable/AM = parent + return turn(dir, dir2angle(AM.dir) - 180) +//special case in-case we want to connect directly with another machine without a duct +/datum/component/plumbing/proc/direct_connect(datum/component/plumbing/P, dir) + if(!P.active) + return + var/opposite_dir = turn(dir, 180) + if(P.demand_connects & opposite_dir && supply_connects & dir || P.supply_connects & opposite_dir && demand_connects & dir) //make sure we arent connecting two supplies or demands + var/datum/ductnet/net = new() + net.add_plumber(src, dir) + net.add_plumber(P, opposite_dir) + +///has one pipe input that only takes, example is manual output pipe +/datum/component/plumbing/simple_demand + demand_connects = NORTH +///has one pipe output that only supplies. example is liquid pump and manual input pipe +/datum/component/plumbing/simple_supply + supply_connects = NORTH +///input and output, like a holding tank +/datum/component/plumbing/tank + demand_connects = WEST + supply_connects = EAST diff --git a/code/datums/components/plumbing/chemical_acclimator.dm b/code/datums/components/plumbing/chemical_acclimator.dm new file mode 100644 index 0000000000..44a1396c6d --- /dev/null +++ b/code/datums/components/plumbing/chemical_acclimator.dm @@ -0,0 +1,21 @@ +v/datum/component/plumbing/acclimator + demand_connects = WEST + supply_connects = EAST + var/obj/machinery/plumbing/acclimator/AC + +/datum/component/plumbing/acclimator/Initialize(start=TRUE, _turn_connects=TRUE) + . = ..() + if(!istype(parent, /obj/machinery/plumbing/acclimator)) + return COMPONENT_INCOMPATIBLE + AC = parent + +/datum/component/plumbing/acclimator/can_give(amount, reagent) + . = ..() + if(. && AC.emptying) + return TRUE + return FALSE +///We're overriding process and not send_request, because all process does is do the requests, so we might aswell cut out the middle man and save some code from running +/datum/component/plumbing/acclimator/process() + if(AC.emptying) + return + . = ..() diff --git a/code/datums/components/plumbing/filter.dm b/code/datums/components/plumbing/filter.dm new file mode 100644 index 0000000000..cfbfb641da --- /dev/null +++ b/code/datums/components/plumbing/filter.dm @@ -0,0 +1,59 @@ +///The magical plumbing component used by the chemical filters. The different supply connects behave differently depending on the filters set on the chemical filter +/datum/component/plumbing/filter + demand_connects = NORTH + supply_connects = SOUTH | EAST | WEST //SOUTH is straight, EAST is left and WEST is right. We look from the perspective of the insert + +/datum/component/plumbing/filter/Initialize() + . = ..() + if(!istype(parent, /obj/machinery/plumbing/filter)) + return COMPONENT_INCOMPATIBLE + +/datum/component/plumbing/filter/can_give(amount, reagent, datum/ductnet/net) + . = ..() + if(.) + var/direction + for(var/A in ducts) + if(ducts[A] == net) + direction = get_original_direction(text2num(A)) //we need it relative to the direction, so filters don't change when we turn the filter + break + if(!direction) + return FALSE + if(reagent) + if(!can_give_in_direction(direction, reagent)) + return FALSE + +/datum/component/plumbing/filter/transfer_to(datum/component/plumbing/target, amount, reagent, datum/ductnet/net) + if(!reagents || !target || !target.reagents) + return FALSE + var/direction + for(var/A in ducts) + if(ducts[A] == net) + direction = get_original_direction(text2num(A)) + break + if(reagent) + reagents.trans_id_to(target.parent, reagent, amount) + else + for(var/A in reagents.reagent_list) + var/datum/reagent/R = A + if(!can_give_in_direction(direction, R.id)) + continue + var/new_amount + if(R.volume < amount) + new_amount = amount - R.volume + reagents.trans_id_to(target.parent, R.id, amount) + amount = new_amount + if(amount <= 0) + break +///We check if the direction and reagent are valid to give. Needed for filters since different outputs have different behaviours +/datum/component/plumbing/filter/proc/can_give_in_direction(dir, reagent) + var/obj/machinery/plumbing/filter/F = parent + switch(dir) + if(SOUTH) //straight + if(!F.left.Find(reagent) && !F.right.Find(reagent)) + return TRUE + if(WEST) //right + if(F.right.Find(reagent)) + return TRUE + if(EAST) //left + if(F.left.Find(reagent)) + return TRUE diff --git a/code/datums/components/plumbing/reaction_chamber.dm b/code/datums/components/plumbing/reaction_chamber.dm new file mode 100644 index 0000000000..d0a9416e34 --- /dev/null +++ b/code/datums/components/plumbing/reaction_chamber.dm @@ -0,0 +1,44 @@ +/datum/component/plumbing/reaction_chamber + demand_connects = WEST + supply_connects = EAST + +/datum/component/plumbing/reaction_chamber/Initialize(start=TRUE, _turn_connects=TRUE) + . = ..() + if(!istype(parent, /obj/machinery/plumbing/reaction_chamber)) + return COMPONENT_INCOMPATIBLE + +/datum/component/plumbing/reaction_chamber/can_give(amount, reagent) + . = ..() + var/obj/machinery/plumbing/reaction_chamber/RC = parent + if(!. || !RC.emptying) + 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)) + return + for(var/RTid in RC.required_reagents) + var/has_reagent = FALSE + for(var/A in reagents.reagent_list) + var/datum/reagent/RD = A + if(RTid == RD.id) + has_reagent = TRUE + if(RD.volume < RC.required_reagents[RTid]) + process_request(min(RC.required_reagents[RTid] - RD.volume, MACHINE_REAGENT_TRANSFER) , RTid, dir) + return + if(!has_reagent) + process_request(min(RC.required_reagents[RTid], MACHINE_REAGENT_TRANSFER), RTid, dir) + return + + RC.reagent_flags &= ~NO_REACT + reagents.handle_reactions() + Add when everything works: + if(reagents.fermiIsReacting) + return + RC.emptying = TRUE + +/datum/component/plumbing/reaction_chamber/can_give(amount, reagent, datum/ductnet/net) + . = ..() + var/obj/machinery/plumbing/reaction_chamber/RC = parent + if(!. || !RC.emptying) + return FALSE diff --git a/code/datums/components/plumbing/splitter.dm b/code/datums/components/plumbing/splitter.dm new file mode 100644 index 0000000000..7194e8803b --- /dev/null +++ b/code/datums/components/plumbing/splitter.dm @@ -0,0 +1,45 @@ +/datum/component/plumbing/splitter + demand_connects = NORTH + supply_connects = SOUTH | EAST + +/datum/component/plumbing/splitter/Initialize() + . = ..() + if(. && !istype(parent, /obj/machinery/plumbing/splitter)) + return FALSE + +/datum/component/plumbing/splitter/can_give(amount, reagent, datum/ductnet/net) + . = ..() + if(!.) + return + . = FALSE + var/direction + for(var/A in ducts) + if(ducts[A] == net) + direction = get_original_direction(text2num(A)) + break + var/obj/machinery/plumbing/splitter/S = parent + switch(direction) + if(SOUTH) + if(S.turn_straight && S.transfer_straight <= amount) + S.turn_straight = FALSE + return TRUE + if(EAST) + if(!S.turn_straight && S.transfer_side <= amount) + S.turn_straight = TRUE + return TRUE + +/datum/component/plumbing/splitter/transfer_to(datum/component/plumbing/target, amount, reagent, datum/ductnet/net) + var/direction + for(var/A in ducts) + if(ducts[A] == net) + direction = get_original_direction(text2num(A)) + break + var/obj/machinery/plumbing/splitter/S = parent + switch(direction) + if(SOUTH) + if(amount >= S.transfer_straight) + amount = S.transfer_straight + if(EAST) + if(amount >= S.transfer_side) + amount = S.transfer_side + . = ..() diff --git a/code/datums/ductnet.dm b/code/datums/ductnet.dm new file mode 100644 index 0000000000..f81e9e608c --- /dev/null +++ b/code/datums/ductnet.dm @@ -0,0 +1,60 @@ +/datum/ductnet + var/list/suppliers = list() + var/list/demanders = list() + var/list/obj/machinery/duct/ducts = list() + + var/capacity + +/datum/ductnet/proc/add_duct(obj/machinery/duct/D) + if(!D || D in ducts) + return + ducts += D + D.duct = src + +/datum/ductnet/proc/remove_duct(obj/machinery/duct/ducting) + destroy_network(FALSE) + for(var/A in ducting.neighbours) + var/obj/machinery/duct/D = A + D.attempt_connect() //we destroyed the network, so now we tell the disconnected ducts neighbours they can start making a new ductnet + qdel(src) + +/datum/ductnet/proc/add_plumber(datum/component/plumbing/P, dir) + if(!P.can_add(src, dir)) + return + P.ducts[num2text(dir)] = src + if(dir & P.supply_connects) + suppliers += P + else if(dir & P.demand_connects) + demanders += P + +/datum/ductnet/proc/remove_plumber(datum/component/plumbing/P) + suppliers.Remove(P) //we're probably only in one of these, but Remove() is inherently sane so this is fine + demanders.Remove(P) + + for(var/dir in P.ducts) + if(P.ducts[dir] == src) + P.ducts -= dir + +/datum/ductnet/proc/assimilate(datum/ductnet/D) + ducts.Add(D.ducts) + suppliers.Add(D.suppliers) + demanders.Add(D.demanders) + for(var/A in D.suppliers + D.demanders) + var/datum/component/plumbing/P = A + for(var/s in P.ducts) + if(P.ducts[s] != D) + continue + P.ducts[s] = src //all your ducts are belong to us + for(var/A in D.ducts) + var/obj/machinery/duct/M = A + M.duct = src //forget your old master + qdel(D) + +/datum/ductnet/proc/destroy_network(delete=TRUE) + for(var/A in suppliers + demanders) + remove_plumber(A) + for(var/A in ducts) + var/obj/machinery/duct/D = A + D.duct = null + if(delete) //I don't want code to run with qdeleted objects because that can never be good, so keep this in-case the ductnet has some business left to attend to before commiting suicide + qdel(src) diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 6ef13ac36f..2ed39a21a8 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -116,6 +116,7 @@ Class Procs: var/new_occupant_dir = SOUTH //The direction the occupant will be set to look at when entering the machine. var/speed_process = FALSE // Process as fast as possible? var/obj/item/circuitboard/circuit // Circuit to be created and inserted when the machinery is created + var/wire_compatible = FALSE // For storing and overriding ui id and dimensions var/tgui_id // ID of TGUI interface var/ui_style // ID of custom TGUI style (optional) @@ -432,6 +433,7 @@ Class Procs: to_chat(user, "You [anchored ? "un" : ""]secure [src].") setAnchored(!anchored) playsound(src, 'sound/items/deconstruct.ogg', 50, 1) + SEND_SIGNAL(src, COMSIG_OBJ_DEFAULT_UNFASTEN_WRENCH, anchored) return SUCCESSFUL_UNFASTEN return FAILED_UNFASTEN return CANT_UNFASTEN diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm index 2fbb45173b..29a04237ea 100644 --- a/code/game/objects/items/RCD.dm +++ b/code/game/objects/items/RCD.dm @@ -856,6 +856,81 @@ RLD desc = "It contains the design for firelock, air alarm, fire alarm, apc circuits and crap power cells." upgrade = RCD_UPGRADE_SIMPLE_CIRCUITS +/obj/item/construction/plumbing + name = "Plumbing Constructor" + desc = "An expertly modified RCD outfitted to construct plumbing machinery. Reload with compressed matter cartridges." + icon_state = "arcd" + item_state = "oldrcd" + has_ammobar = FALSE + matter = 200 + max_matter = 200 + + ///type of the plumbing machine + var/blueprint = null + ///index, used in the attack self to get the type. stored here since it doesnt change + var/list/choices = list() + ///index, used in the attack self to get the type. stored here since it doesnt change + var/list/name_to_type = list() + /// + var/list/machinery_data = list("cost" = list(), "delay" = list()) + +/obj/item/construction/plumbing/attack_self(mob/user) + ..() + if(!choices.len) + for(var/A in subtypesof(/obj/machinery/plumbing)) + var/obj/machinery/plumbing/M = A + if(initial(M.rcd_constructable)) + choices += list(initial(M.name) = image(icon = initial(M.icon), icon_state = initial(M.icon_state))) + name_to_type[initial(M.name)] = M + machinery_data["cost"][A] = initial(M.rcd_cost) + machinery_data["delay"][A] = initial(M.rcd_delay) + + var/choice = show_radial_menu(user, src, choices, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = TRUE) + if(!check_menu(user)) + return + + blueprint = name_to_type[choice] + playsound(src, 'sound/effects/pop.ogg', 50, FALSE) + to_chat(user, "You change [name]s blueprint to '[choice]'.") + +///pretty much rcd_create, but named differently to make myself feel less bad for copypasting from a sibling-type +/obj/item/construction/plumbing/proc/create_machine(atom/A, mob/user) + if(!machinery_data || !isopenturf(A)) + return FALSE + + if(checkResource(machinery_data["cost"][blueprint], user) && blueprint) + if(do_after(user, machinery_data["delay"][blueprint], target = A)) + if(checkResource(machinery_data["cost"][blueprint], user) && canPlace(A)) + useResource(machinery_data["cost"][blueprint], user) + activate() + playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE) + new blueprint (A, FALSE, FALSE) + return TRUE + +/obj/item/construction/plumbing/proc/canPlace(turf/T) + if(!isopenturf(T)) + return FALSE + . = TRUE + for(var/obj/O in T.contents) + if(O.density) //let's not built ontop of dense stuff, like big machines and other obstacles, it kills my immershion + return FALSE + +/obj/item/construction/plumbing/afterattack(atom/A, mob/user, proximity) + . = ..() + if(!prox_check(proximity)) + return + if(istype(A, /obj/machinery/plumbing)) + var/obj/machinery/plumbing/P = A + if(P.anchored) + to_chat(user, "The [P.name] needs to be unanchored!") + return + if(do_after(user, 20, target = P)) + P.deconstruct() //Let's not substract matter + playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE) //this is just such a great sound effect + else + create_machine(A, user) + + #undef GLOW_MODE #undef LIGHT_MODE #undef REMOVE_MODE diff --git a/code/game/objects/items/RPD.dm b/code/game/objects/items/RPD.dm index 93d73b2b5f..2c8acf5a28 100644 --- a/code/game/objects/items/RPD.dm +++ b/code/game/objects/items/RPD.dm @@ -6,6 +6,7 @@ RPD #define ATMOS_CATEGORY 0 #define DISPOSALS_CATEGORY 1 #define TRANSIT_CATEGORY 2 +#define PLUMBING_CATEGORY 3 #define BUILD_MODE (1<<0) #define WRENCH_MODE (1<<1) @@ -75,6 +76,13 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( ) )) +GLOBAL_LIST_INIT(fluid_duct_recipes, list( + "Fluid Ducts" = list( + new /datum/pipe_info/plumbing("Duct", /obj/machinery/duct, PIPE_ONEDIR), + new /datum/pipe_info/plumbing/multilayer("Duct Layer-Manifold",/obj/machinery/duct/multilayered, PIPE_STRAIGHT) + ) +)) + /datum/pipe_info var/name var/icon_state @@ -175,6 +183,15 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( if(dt == PIPE_UNARY_FLIPPABLE) icon_state = "[icon_state]_preview" +/datum/pipe_info/plumbing/New(label, obj/path, dt=PIPE_UNARY) + name = label + id = path + icon_state = initial(path.icon_state) + dirtype = dt + +/datum/pipe_info/plumbing/multilayer //exists as identifier so we can see the difference between multi_layer and just ducts properly later on + + /obj/item/pipe_dispenser name = "Rapid Piping Device (RPD)" desc = "A device used to rapidly pipe things." @@ -200,15 +217,19 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( var/atmos_build_speed = 5 //deciseconds (500ms) var/disposal_build_speed = 5 var/transit_build_speed = 5 + var/plumbing_build_speed = 5 var/destroy_speed = 5 var/paint_speed = 5 var/category = ATMOS_CATEGORY var/piping_layer = PIPING_LAYER_DEFAULT + var/ducting_layer = DUCT_LAYER_DEFAULT var/datum/pipe_info/recipe var/static/datum/pipe_info/first_atmos var/static/datum/pipe_info/first_disposal var/static/datum/pipe_info/first_transit var/mode = BUILD_MODE | DESTROY_MODE | WRENCH_MODE + var/static/datum/pipe_info/first_plumbing + var/locked = FALSE //wheter we can change categories. Useful for the plumber /obj/item/pipe_dispenser/New() . = ..() @@ -256,11 +277,13 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( var/list/data = list( "category" = category, "piping_layer" = piping_layer, + "ducting_layer" = ducting_layer, "preview_rows" = recipe.get_preview(p_dir), "categories" = list(), "selected_color" = paint_color, "paint_colors" = GLOB.pipe_paint_colors, "mode" = mode + "locked" = locked ) var/list/recipes @@ -271,6 +294,8 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( recipes = GLOB.disposal_pipe_recipes if(TRANSIT_CATEGORY) recipes = GLOB.transit_tube_recipes + if(PLUMBING_CATEGORY) + recipes = GLOB.fluid_duct_recipes for(var/c in recipes) var/list/cat = recipes[c] var/list/r = list() @@ -299,6 +324,8 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( recipe = first_atmos if(TRANSIT_CATEGORY) recipe = first_transit + if(PLUMBING_CATEGORY) + recipe = first_plumbing p_dir = NORTH playeffect = FALSE if("piping_layer") @@ -469,16 +496,56 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( if(mode & WRENCH_MODE) tube.wrench_act(user, src) return - + if(PLUMBING_CATEGORY) //Plumbing. + if(!can_make_pipe) + return ..() + A = get_turf(A) + if(isclosedturf(A)) + to_chat(user, "[src]'s error light flickers; there's something in the way!") + return + to_chat(user, "You start building a fluid duct...") + playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1) + if(do_after(user, plumbing_build_speed, target = A)) + var/obj/machinery/duct/D + if(recipe.type == /datum/pipe_info/plumbing/multilayer) + var/temp_connects = NORTH + SOUTH + if(queued_p_dir == EAST) + temp_connects = EAST + WEST + D = new queued_p_type (A, TRUE, GLOB.pipe_paint_colors[paint_color], ducting_layer, temp_connects) + else + D = new queued_p_type (A, TRUE, GLOB.pipe_paint_colors[paint_color], ducting_layer) + D.add_fingerprint(usr) + if(mode & WRENCH_MODE) + D.wrench_act(user, src) else return ..() /obj/item/pipe_dispenser/proc/activate() playsound(get_turf(src), 'sound/items/deconstruct.ogg', 50, 1) +/obj/item/pipe_dispenser/plumbing + name = "Plumberinator" + desc = "A crude device to rapidly plumb things." + icon_state = "plumberer" + category = PLUMBING_CATEGORY + locked = TRUE + +/obj/item/pipe_dispenser/plumbing/Initialize() + . = ..() + spark_system = new + spark_system.set_up(5, 0, src) + spark_system.attach(src) + if(!first_plumbing) + first_plumbing = GLOB.fluid_duct_recipes[GLOB.fluid_duct_recipes[1]][1] + + recipe = first_plumbing + + + #undef ATMOS_CATEGORY #undef DISPOSALS_CATEGORY #undef TRANSIT_CATEGORY +#undef PLUMBING_CATEGORY #undef BUILD_MODE #undef DESTROY_MODE diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 0383c3f1ee..4e5ffab1e0 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -773,6 +773,7 @@ GLOBAL_LIST_INIT(plastic_recipes, list( new /datum/stack_recipe("water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/empty), \ new /datum/stack_recipe("large water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/large/empty,3), \ new /datum/stack_recipe("shower curtain", /obj/structure/curtain, 10, time = 10, one_per_turf = 1, on_floor = 1), \ + new /datum/stack_recipe("duct", /obj/item/stack/ducts,1), \ new /datum/stack_recipe("laser pointer case", /obj/item/glasswork/glass_base/laserpointer_shell, 30), \ new /datum/stack_recipe("wet floor sign", /obj/item/caution, 2))) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index fb3b1535ed..85f0066e3f 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -324,3 +324,6 @@ . = ..() if(. && ricochet_damage_mod) take_damage(P.damage * ricochet_damage_mod, P.damage_type, P.flag, 0, turn(P.dir, 180), P.armour_penetration) // pass along ricochet_damage_mod damage to the structure for the ricochet + +/obj/proc/plunger_act(obj/item/plunger/P, mob/living/user, reinforced) + return diff --git a/code/game/objects/structures/geyser.dm b/code/game/objects/structures/geyser.dm new file mode 100644 index 0000000000..1d77a3c4f8 --- /dev/null +++ b/code/game/objects/structures/geyser.dm @@ -0,0 +1,74 @@ +//If you look at the "geyser_soup" overlay icon_state, you'll see that the first frame has 25 ticks. +//That's because the first 18~ ticks are completely skipped for some ungodly weird fucking byond reason + +/obj/structure/geyser + name = "geyser" + icon = 'icons/obj/lavaland/terrain.dmi' + icon_state = "geyser" + anchored = TRUE + var/decay = 0 //reagents/tick removed + var/erupting_state = null //set to null to get it greyscaled from "[icon_state]_soup". Not very usable with the whole random thing, but more types can be added if you change the spawn prob + var/activated = FALSE //whether we are active and generating chems + var/reagent_id = "oil" + var/potency = 2 //how much reagents we add every process (2 seconds) + var/max_volume = 500 + var/start_volume = 50 + +/obj/structure/geyser/proc/start_chemming() + activated = TRUE + create_reagents(max_volume, DRAINABLE) + reagents.add_reagent(reagent_id, start_volume) + START_PROCESSING(SSfluids, src) //It's main function is to be plumbed, so use SSfluids + if(erupting_state) + icon_state = erupting_state + else + var/mutable_appearance/I = mutable_appearance('icons/obj/lavaland/terrain.dmi', "[icon_state]_soup") + I.color = mix_color_from_reagents(reagents.reagent_list) + add_overlay(I) + +/obj/structure/geyser/process() + if(activated && reagents.total_volume <= reagents.maximum_volume) //this is also evaluated in add_reagent, but from my understanding proc calls are expensive and should be avoided in continous + reagents.add_reagent(reagent_id, potency) //processes + if(potency > 0) + potency =- decay //decaying! + +/obj/structure/geyser/plunger_act(obj/item/plunger/P, mob/living/user, _reinforced) + if(!_reinforced) + to_chat(user, "The [P.name] isn't strong enough!") + return + if(activated) + to_chat(user, "The [name] is already active!") + return + + to_chat(user, "You start vigorously plunging [src]!") + if(do_after(user, 50*P.plunge_mod, target = src) && !activated) + start_chemming() + +/obj/structure/geyser/random + erupting_state = null + var/list/options = list("oil" = 2, "clf3" = 1) //formerly crudeoil, 2,1 +/obj/structure/geyser/random/Initialize() + . = ..() + reagent_id = pickweight(options) + +/obj/item/plunger + name = "plunger" + desc = "It's a plunger for plunging." + icon = 'icons/obj/watercloset.dmi' + icon_state = "plunger" + slot_flags = ITEM_SLOT_MASK + + var/plunge_mod = 1 //time*plunge_mod = total time we take to plunge an object + var/reinforced = FALSE //whether we do heavy duty stuff like geysers + +/obj/item/plunger/attack_obj(obj/O, mob/living/user) + if(!O.plunger_act(src, user, reinforced)) + return ..() + +/obj/item/plunger/reinforced + name = "reinforced plunger" + desc = " It's an M. 7 Reinforced Plunger for heavy duty plunging." + icon_state = "reinforced_plunger" + + reinforced = TRUE + plunge_mod = 0.8 diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index 57d8e55e07..e44c5e2a3f 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -266,7 +266,7 @@ name = "pipes" /datum/asset/spritesheet/pipes/register() - for (var/each in list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi')) + for (var/each in list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi', 'icons/obj/atmospherics/pipes/transit_tube.dmi', 'icons/obj/plumbing/fluid_ducts.dmi')) InsertAll("", each, GLOB.alldirs) ..() @@ -378,4 +378,3 @@ assets = list( "ghost.png" = 'html/ghost.png' ) - diff --git a/code/modules/plumbing/ducts.dm b/code/modules/plumbing/ducts.dm new file mode 100644 index 0000000000..735526511f --- /dev/null +++ b/code/modules/plumbing/ducts.dm @@ -0,0 +1,371 @@ +/* +All the important duct code: +/code/datums/components/plumbing/plumbing.dm +/code/datums/ductnet.dm +*/ +/obj/machinery/duct + name = "fluid duct" + icon = 'icons/obj/plumbing/fluid_ducts.dmi' + icon_state = "nduct" + level = 1 + ///bitfield with the directions we're connected in + var/connects + ///set to TRUE to disable smart cable behaviour + var/dumb = FALSE + ///wheter we allow our connects to be changed after initialization or not + var/lock_connects = FALSE + ///our ductnet, wich tracks what we're connected to + var/datum/ductnet/duct + ///amount we can transfer per process. note that the ductnet can carry as much as the lowest capacity duct + var/capacity = 10 + + ///the color of our duct + var/duct_color = null + ///TRUE to ignore colors, so yeah we also connect with other colors without issue + var/ignore_colors = FALSE + ///1,2,4,8,16 + var/duct_layer = DUCT_LAYER_DEFAULT + ///whether we allow our layers to be altered + var/lock_layers = FALSE + ///TRUE to let colors connect when forced with a wrench, false to just not do that at all + var/color_to_color_support = TRUE + ///wheter to even bother with plumbing code or not + var/active = TRUE + ///track ducts we're connected to. Mainly for ducts we connect to that we normally wouldn't, like different layers and colors, for when we regenerate the ducts + var/list/neighbours = list() + ///wheter we just unanchored or drop whatever is in the variable. either is safe + var/drop_on_wrench = /obj/item/stack/ducts + +/obj/machinery/duct/Initialize(mapload, no_anchor, color_of_duct, layer_of_duct = DUCT_LAYER_DEFAULT, force_connects) + . = ..() + if(no_anchor) + active = FALSE + anchored = FALSE + else if(!can_anchor()) + CRASH("Overlapping ducts detected") + qdel(src) + if(force_connects) + connects = force_connects //skip change_connects() because we're still initializing and we need to set our connects at one point + if(!lock_layers) + duct_layer = layer_of_duct + if(!ignore_colors) + duct_color = color_of_duct + if(duct_color) + add_atom_colour(duct_color, FIXED_COLOUR_PRIORITY) + handle_layer() + for(var/obj/machinery/duct/D in loc) + if(D == src) + continue + if(D.duct_layer & duct_layer) + disconnect_duct() + if(active) + attempt_connect() +///start looking around us for stuff to connect to +/obj/machinery/duct/proc/attempt_connect() + reset_connects() //All connects are gathered here again eitherway, we might aswell reset it so they properly update when reconnecting + + for(var/atom/movable/AM in loc) + var/datum/component/plumbing/P = AM.GetComponent(/datum/component/plumbing) + if(P?.active) + disconnect_duct() //let's not built under plumbing machinery + return + for(var/D in GLOB.cardinals) + if(dumb && !(D & connects)) + continue + for(var/atom/movable/AM in get_step(src, D)) + if(connect_network(AM, D)) + add_connects(D) + update_icon() +///see if whatever we found can be connected to +/obj/machinery/duct/proc/connect_network(atom/movable/AM, direction, ignore_color) + 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) +///connect to a duct +/obj/machinery/duct/proc/connect_duct(obj/machinery/duct/D, direction, ignore_color) + var/opposite_dir = turn(direction, 180) + if(!active || !D.active) + return + + if(!dumb && D.dumb && !(opposite_dir & D.connects)) + return + if(dumb && D.dumb && !(connects & D.connects)) //we eliminated a few more scenario in attempt connect + return + + if((duct == D.duct) && duct)//check if we're not just comparing two null values + add_neighbour(D) + + D.add_connects(opposite_dir) + D.update_icon() + return TRUE //tell the current pipe to also update it's sprite + if(!(D in neighbours)) //we cool + if((duct_color != D.duct_color) && !(ignore_colors || D.ignore_colors)) + return + if(!(duct_layer & D.duct_layer)) + return + + if(D.duct) + if(duct) + duct.assimilate(D.duct) + else + D.duct.add_duct(src) + else + if(duct) + duct.add_duct(D) + else + create_duct() + duct.add_duct(D) + add_neighbour(D) + D.attempt_connect()//tell our buddy its time to pass on the torch of connecting to pipes. This shouldn't ever infinitely loop since it only works on pipes that havent been inductrinated + return TRUE +///connect to a plumbing object +/obj/machinery/duct/proc/connect_plumber(datum/component/plumbing/P, direction) + var/opposite_dir = turn(direction, 180) + if(duct_layer != DUCT_LAYER_DEFAULT) //plumbing devices don't support multilayering. 3 is the default layer so we only use that. We can change this later + return FALSE + + if(!P.active) + return + + var/comp_directions = P.supply_connects + P.demand_connects //they should never, ever have supply and demand connects overlap or catastrophic failure + if(opposite_dir & comp_directions) + if(!duct) + create_duct() + duct.add_plumber(P, opposite_dir) + return TRUE +///we disconnect ourself from our neighbours. we also destroy our ductnet and tell our neighbours to make a new one +/obj/machinery/duct/proc/disconnect_duct() + anchored = FALSE + active = FALSE + if(duct) + duct.remove_duct(src) + lose_neighbours() + reset_connects(0) + update_icon() + if(ispath(drop_on_wrench) && !QDELING(src)) + new drop_on_wrench(drop_location()) + qdel(src) + +///create a new duct datum +/obj/machinery/duct/proc/create_duct() + duct = new() + duct.add_duct(src) +///add a duct as neighbour. this means we're connected and will connect again if we ever regenerate +/obj/machinery/duct/proc/add_neighbour(obj/machinery/duct/D) + if(!(D in neighbours)) + neighbours += D + if(!(src in D.neighbours)) + D.neighbours += src +///remove all our neighbours, and remove us from our neighbours aswell +/obj/machinery/duct/proc/lose_neighbours() + for(var/A in neighbours) + var/obj/machinery/duct/D = A + D.neighbours.Remove(src) + neighbours = list() +///add a connect direction +/obj/machinery/duct/proc/add_connects(new_connects) //make this a define to cut proc calls? + if(!lock_connects) + connects |= new_connects +///remove our connects +/obj/machinery/duct/proc/reset_connects() + if(!lock_connects) + connects = 0 +///get a list of the ducts we can connect to if we are dumb +/obj/machinery/duct/proc/get_adjacent_ducts() + var/list/adjacents = list() + for(var/A in GLOB.cardinals) + if(A & connects) + for(var/obj/machinery/duct/D in get_step(src, A)) + if((turn(A, 180) & D.connects) && D.active) + adjacents += D + return adjacents + +/obj/machinery/duct/update_icon() //setting connects isnt a parameter because sometimes we make more than one change, overwrite it completely or just add it to the bitfield + var/temp_icon = initial(icon_state) + for(var/D in GLOB.cardinals) + if(D & connects) + if(D == NORTH) + temp_icon += "_n" + if(D == SOUTH) + temp_icon += "_s" + if(D == EAST) + temp_icon += "_e" + if(D == WEST) + temp_icon += "_w" + icon_state = temp_icon +///update the layer we are on +/obj/machinery/duct/proc/handle_layer() + var/offset + switch(duct_layer)//it's a bitfield, but it's fine because it only works when there's one layer, and multiple layers should be handled differently + if(FIRST_DUCT_LAYER) + offset = -10 + if(SECOND_DUCT_LAYER) + offset = -5 + if(THIRD_DUCT_LAYER) + offset = 0 + if(FOURTH_DUCT_LAYER) + offset = 5 + if(FIFTH_DUCT_LAYER) + offset = 10 + pixel_x = offset + pixel_y = offset + + +/obj/machinery/duct/wrench_act(mob/living/user, obj/item/I) //I can also be the RPD + ..() + add_fingerprint(user) + I.play_tool_sound(src) + if(anchored) + user.visible_message( \ + "[user] unfastens \the [src].", \ + "You unfasten \the [src].", \ + "You hear ratcheting.") + disconnect_duct() + else if(can_anchor()) + anchored = TRUE + active = TRUE + user.visible_message( \ + "[user] fastens \the [src].", \ + "You fasten \the [src].", \ + "You hear ratcheting.") + attempt_connect() + return TRUE +///collection of all the sanity checks to prevent us from stacking ducts that shouldnt be stacked +/obj/machinery/duct/proc/can_anchor(turf/T) + if(!T) + T = get_turf(src) + for(var/obj/machinery/duct/D in T) + if(!anchored) + continue + for(var/A in GLOB.cardinals) + if(A & connects && A & D.connects) + return FALSE + return TRUE + +/obj/machinery/duct/doMove(destination) + . = ..() + disconnect_duct() + anchored = FALSE + +/obj/machinery/duct/Destroy() + disconnect_duct() + return ..() + +/obj/machinery/duct/MouseDrop_T(atom/A, mob/living/user) + if(!istype(A, /obj/machinery/duct)) + return + var/obj/machinery/duct/D = A + var/obj/item/I = user.get_active_held_item() + if(I?.tool_behaviour != TOOL_WRENCH) + to_chat(user, "You need to be holding a wrench in your active hand to do that!") + return + if(get_dist(src, D) != 1) + return + var/direction = get_dir(src, D) + if(!(direction in GLOB.cardinals)) + return + if(duct_layer != D.duct_layer) + return + + add_connects(direction) //the connect of the other duct is handled in connect_network, but do this here for the parent duct because it's not necessary in normal cases + add_neighbour(D) + connect_network(D, direction, TRUE) + update_icon() +///has a total of 5 layers and doesnt give a shit about color. its also dumb so doesnt autoconnect. +/obj/machinery/duct/multilayered + name = "duct layer-manifold" + icon = 'icons/obj/2x2.dmi' + icon_state = "multiduct" + pixel_x = -15 + pixel_y = -15 + + color_to_color_support = FALSE + duct_layer = FIRST_DUCT_LAYER | SECOND_DUCT_LAYER | THIRD_DUCT_LAYER | FOURTH_DUCT_LAYER | FIFTH_DUCT_LAYER + drop_on_wrench = null + + lock_connects = TRUE + lock_layers = TRUE + ignore_colors = TRUE + dumb = TRUE + + active = FALSE + anchored = FALSE + +/obj/machinery/duct/multilayered/Initialize(mapload, no_anchor, color_of_duct, layer_of_duct = DUCT_LAYER_DEFAULT, force_connects) + . = ..() + update_connects() + +/obj/machinery/duct/multilayered/update_icon() + return + +/obj/machinery/duct/multilayered/wrench_act(mob/living/user, obj/item/I) + . = ..() + update_connects() + +/obj/machinery/duct/multilayered/proc/update_connects() + if(dir & NORTH || dir & SOUTH) + connects = NORTH | SOUTH + else + connects = EAST | WEST + +///don't connect to other multilayered stuff because honestly it shouldnt be done and I dont wanna deal with it +/obj/machinery/duct/multilayered/connect_duct(obj/machinery/duct/D, direction, ignore_color) + if(istype(D, /obj/machinery/duct/multilayered)) + return + return ..() + +/obj/machinery/duct/multilayered/handle_layer() + return + +/obj/item/stack/ducts + name = "stack of duct" + desc = "A stack of fluid ducts." + singular_name = "duct" + icon = 'icons/obj/plumbing/fluid_ducts.dmi' + icon_state = "ducts" + w_class = WEIGHT_CLASS_TINY + novariants = FALSE + max_amount = 50 + item_flags = NOBLUDGEON + merge_type = /obj/item/stack/ducts + ///Color of our duct + var/duct_color = "grey" + ///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) + +/obj/item/stack/ducts/examine(mob/user) + . = ..() + . += "It's current color and layer are [duct_color] and [duct_layer]. Use in-hand to change." + +/obj/item/stack/ducts/attack_self(mob/user) + var/new_layer = input("Select a layer", "Layer") as null|anything in layers + if(new_layer) + duct_layer = new_layer + var/new_color = input("Select a color", "Color") as null|anything in GLOB.pipe_paint_colors + if(new_color) + duct_color = new_color + add_atom_colour(GLOB.pipe_paint_colors[new_color], FIXED_COLOUR_PRIORITY) + +/obj/item/stack/ducts/afterattack(atom/A, user, proximity) + . = ..() + if(!proximity) + return + if(istype(A, /obj/machinery/duct)) + var/obj/machinery/duct/D = A + if(!D.anchored) + add(1) + qdel(D) + if(istype(A, /turf/open) && use(1)) + var/turf/open/OT = A + new /obj/machinery/duct(OT, FALSE, GLOB.pipe_paint_colors[duct_color], layers[duct_layer]) + playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE) + +/obj/item/stack/ducts/fifty + amount = 50 diff --git a/code/modules/plumbing/plumbers/_plumb_machinery.dm b/code/modules/plumbing/plumbers/_plumb_machinery.dm new file mode 100644 index 0000000000..1ea7ab56b1 --- /dev/null +++ b/code/modules/plumbing/plumbers/_plumb_machinery.dm @@ -0,0 +1,97 @@ +/**Basic plumbing object. +* It doesn't really hold anything special, YET. +* Objects that are plumbing but not a subtype are as of writing liquid pumps and the reagent_dispenser tank +* Also please note that the plumbing component is toggled on and off by the component using a signal from default_unfasten_wrench, so dont worry about it +*/ +/obj/machinery/plumbing + name = "pipe thing" + icon = 'icons/obj/plumbing/plumbers.dmi' + icon_state = "pump" + density = TRUE + active_power_usage = 30 + use_power = ACTIVE_POWER_USE + resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF + ///Plumbing machinery is always gonna need reagents, so we might aswell put it here + var/buffer = 50 + ///Flags for reagents, like INJECTABLE, TRANSPARENT bla bla everything thats in DEFINES/reagents.dm + var/reagent_flags = TRANSPARENT + ///wheter we partake in rcd construction or not + var/rcd_constructable = TRUE + ///cost of the plumbing rcd construction + var/rcd_cost = 15 + ///delay of constructing it throught the plumbing rcd + var/rcd_delay = 10 + +/obj/machinery/plumbing/Initialize(mapload, bolt = TRUE) + . = ..() + anchored = bolt + create_reagents(buffer, reagent_flags) + AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS ) + +/obj/machinery/plumbing/proc/can_be_rotated(mob/user,rotation_type) + return TRUE + +/obj/machinery/plumbing/examine(mob/user) + . = ..() + . += "The maximum volume display reads: [reagents.maximum_volume] units." + +/obj/machinery/plumbing/wrench_act(mob/living/user, obj/item/I) + ..() + default_unfasten_wrench(user, I) + return TRUE + +/obj/machinery/plumbing/plunger_act(obj/item/plunger/P, mob/living/user, reinforced) + to_chat(user, "You start furiously plunging [name].") + if(do_after(user, 30, target = src)) + to_chat(user, "You finish plunging the [name].") + reagents.reaction(get_turf(src), TOUCH) //splash on the floor + reagents.clear_reagents() + +/obj/machinery/plumbing/welder_act(mob/living/user, obj/item/I) + . = ..() + if(anchored) + to_chat(user, "The [name] needs to be unbolted to do that!You start slicing the [name] apart.You slice the [name] apart. target_temperature && acclimate_state != COOLING) + acclimate_state = COOLING + update_icon() + if(!emptying) + if(reagents.chem_temp >= target_temperature && target_temperature + allowed_temperature_difference >= reagents.chem_temp) //cooling here + emptying = TRUE + if(reagents.chem_temp <= target_temperature && target_temperature - allowed_temperature_difference <= reagents.chem_temp) //heating here + emptying = TRUE + + reagents.adjust_thermal_energy((target_temperature - reagents.chem_temp) * heater_coefficient * SPECIFIC_HEAT_DEFAULT * reagents.total_volume) //keep constant with chem heater + reagents.handle_reactions() + +/obj/machinery/plumbing/acclimator/update_icon() + icon_state = initial(icon_state) + switch(acclimate_state) + if(COOLING) + icon_state += "_cold" + if(HEATING) + icon_state += "_hot" + +/obj/machinery/plumbing/acclimator/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ChemAcclimator", name) + ui.open() + +/obj/machinery/plumbing/acclimator/ui_data(mob/user) + var/list/data = list() + + data["enabled"] = enabled + data["chem_temp"] = reagents.chem_temp + data["target_temperature"] = target_temperature + data["allowed_temperature_difference"] = allowed_temperature_difference + data["acclimate_state"] = acclimate_state + data["max_volume"] = reagents.maximum_volume + data["reagent_volume"] = reagents.total_volume + data["emptying"] = emptying + return data + +/obj/machinery/plumbing/acclimator/ui_act(action, params) + if(..()) + return + . = TRUE + switch(action) + if("set_target_temperature") + var/target = text2num(params["temperature"]) + target_temperature = clamp(target, 0, 1000) + if("set_allowed_temperature_difference") + var/target = text2num(params["temperature"]) + allowed_temperature_difference = clamp(target, 0, 1000) + if("toggle_power") + enabled = !enabled + if("change_volume") + var/target = text2num(params["volume"]) + reagents.maximum_volume = clamp(round(target), 1, buffer) + +#undef COOLING +#undef HEATING +#undef NEUTRAL diff --git a/code/modules/plumbing/plumbers/bottler.dm b/code/modules/plumbing/plumbers/bottler.dm new file mode 100644 index 0000000000..c6595fc397 --- /dev/null +++ b/code/modules/plumbing/plumbers/bottler.dm @@ -0,0 +1,79 @@ +/obj/machinery/plumbing/bottler + name = "chemical bottler" + desc = "Puts reagents into containers, like bottles and beakers." + icon_state = "bottler" + layer = ABOVE_ALL_MOB_LAYER + reagent_flags = TRANSPARENT | DRAINABLE + rcd_cost = 50 + rcd_delay = 50 + buffer = 100 + ///how much do we fill + var/wanted_amount = 10 + ///where things are sent + var/turf/goodspot = null + ///where things are taken + var/turf/inputspot = null + ///where beakers that are already full will be sent + var/turf/badspot = null + +/obj/machinery/plumbing/bottler/Initialize(mapload, bolt) + . = ..() + AddComponent(/datum/component/plumbing/simple_demand, bolt) + setDir(dir) + +/obj/machinery/plumbing/bottler/can_be_rotated(mob/user, rotation_type) + if(anchored) + to_chat(user, "It is fastened to the floor!") + return FALSE + return TRUE + +///changes the tile array +/obj/machinery/plumbing/bottler/setDir(newdir) + . = ..() + switch(dir) + if(NORTH) + goodspot = get_step(get_turf(src), NORTH) + inputspot = get_step(get_turf(src), SOUTH) + badspot = get_step(get_turf(src), EAST) + if(SOUTH) + goodspot = get_step(get_turf(src), SOUTH) + inputspot = get_step(get_turf(src), NORTH) + badspot = get_step(get_turf(src), WEST) + if(WEST) + goodspot = get_step(get_turf(src), WEST) + inputspot = get_step(get_turf(src), EAST) + badspot = get_step(get_turf(src), NORTH) + if(EAST) + goodspot = get_step(get_turf(src), EAST) + inputspot = get_step(get_turf(src), WEST) + badspot = get_step(get_turf(src), SOUTH) + +///changing input ammount with a window +/obj/machinery/plumbing/bottler/interact(mob/user) + . = ..() + wanted_amount = clamp(round(input(user,"maximum is 100u","set ammount to fill with") as num|null, 1), 1, 100) + reagents.clear_reagents() + to_chat(user, " The [src] will now fill for [wanted_amount]u.") + +/obj/machinery/plumbing/bottler/process() + if(machine_stat & NOPOWER) + return + ///see if machine has enough to fill + if(reagents.total_volume >= wanted_amount && anchored) + var/obj/AM = pick(inputspot.contents)///pick a reagent_container that could be used + if(istype(AM, /obj/item/reagent_containers) && (!istype(AM, /obj/item/reagent_containers/hypospray/medipen))) + var/obj/item/reagent_containers/B = AM + ///see if it would overflow else inject + if((B.reagents.total_volume + wanted_amount) <= B.reagents.maximum_volume) + reagents.trans_to(B, wanted_amount, transfered_by = src) + B.forceMove(goodspot) + return + ///glass was full so we move it away + AM.forceMove(badspot) + if(istype(AM, /obj/item/slime_extract)) ///slime extracts need inject + AM.forceMove(goodspot) + reagents.trans_to(AM, wanted_amount, transfered_by = src, method = INJECT) + return + if(istype(AM, /obj/item/slimecross/industrial)) ///no need to move slimecross industrial things + reagents.trans_to(AM, wanted_amount, transfered_by = src, method = INJECT) + return diff --git a/code/modules/plumbing/plumbers/destroyer.dm b/code/modules/plumbing/plumbers/destroyer.dm new file mode 100644 index 0000000000..b61383ea4a --- /dev/null +++ b/code/modules/plumbing/plumbers/destroyer.dm @@ -0,0 +1,21 @@ +/obj/machinery/plumbing/disposer + name = "chemical disposer" + desc = "Breaks down chemicals and annihilates them." + icon_state = "disposal" + ///we remove 10 reagents per second + var/disposal_rate = 10 + +/obj/machinery/plumbing/disposer/Initialize(mapload, bolt) + . = ..() + AddComponent(/datum/component/plumbing/simple_demand, bolt) + +/obj/machinery/plumbing/disposer/process() + if(stat & NOPOWER) + return + if(reagents.total_volume) + if(icon_state != initial(icon_state) + "_working") //threw it here instead of update icon since it only has two states + icon_state = initial(icon_state) + "_working" + reagents.remove_any(disposal_rate) + else + if(icon_state != initial(icon_state)) + icon_state = initial(icon_state) diff --git a/code/modules/plumbing/plumbers/fermenter.dm b/code/modules/plumbing/plumbers/fermenter.dm new file mode 100644 index 0000000000..e13ae3feff --- /dev/null +++ b/code/modules/plumbing/plumbers/fermenter.dm @@ -0,0 +1,52 @@ +/obj/machinery/plumbing/fermenter //FULLY AUTOMATIC BEER BREWING. TRULY, THE FUTURE. + name = "chemical fermenter" + desc = "Turns plants into various types of booze." + icon_state = "fermenter" + layer = ABOVE_ALL_MOB_LAYER + reagent_flags = TRANSPARENT | DRAINABLE + rcd_cost = 30 + rcd_delay = 30 + buffer = 400 + ///input dir + var/eat_dir = SOUTH + +/obj/machinery/plumbing/fermenter/Initialize(mapload, bolt) + . = ..() + AddComponent(/datum/component/plumbing/simple_supply, bolt) + +/obj/machinery/plumbing/grinder_chemical/can_be_rotated(mob/user, rotation_type) + if(anchored) + to_chat(user, "It is fastened to the floor!") + return FALSE + return TRUE + +/obj/machinery/plumbing/fermenter/setDir(newdir) + . = ..() + eat_dir = newdir + +/obj/machinery/plumbing/fermenter/CanAllowThrough(atom/movable/AM) + . = ..() + if(!anchored) + return + var/move_dir = get_dir(loc, AM.loc) + if(move_dir == eat_dir) + return TRUE + +/obj/machinery/plumbing/fermenter/Crossed(atom/movable/AM) + . = ..() + ferment(AM) + +/// uses fermentation proc similar to fermentation barrels +/obj/machinery/plumbing/fermenter/proc/ferment(atom/AM) + if(machine_stat & NOPOWER) + return + if(reagents.holder_full()) + return + if(!isitem(AM)) + return + if(istype(AM, /obj/item/reagent_containers/food/snacks/grown)) + var/obj/item/reagent_containers/food/snacks/grown/G = AM + if(G.distill_reagent) + var/amount = G.seed.potency * 0.25 + reagents.add_reagent(G.distill_reagent, amount) + qdel(G) diff --git a/code/modules/plumbing/plumbers/filter.dm b/code/modules/plumbing/plumbers/filter.dm new file mode 100644 index 0000000000..3058120b9d --- /dev/null +++ b/code/modules/plumbing/plumbers/filter.dm @@ -0,0 +1,74 @@ +///chemical plumbing filter. If it's not filtered by left and right, it goes straight. +/obj/machinery/plumbing/filter + name = "chemical filter" + desc = "A chemical filter for filtering chemicals. The left and right outputs appear to be from the perspective of the input port." + icon_state = "filter" + density = FALSE + ///whitelist of chems id's that go to the left side. Empty to disable port + var/list/left = list() + ///whitelist of chem id's that go to the right side. Empty to disable port + var/list/right = list() + ///whitelist of chems but their name instead of path + var/list/english_left = list() + ///whitelist of chems but their name instead of path + var/list/english_right = list() + var/ui_x = 320 + var/ui_y = 310 + + +/obj/machinery/plumbing/filter/Initialize(mapload, bolt) + . = ..() + AddComponent(/datum/component/plumbing/filter, bolt) + +/obj/machinery/plumbing/filter/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + + if(!ui) + ui = new(user, src, ui_key, "chemical_filter", name, ui_x, ui_y, master_ui, state) + ui.open() + +/obj/machinery/plumbing/filter/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ChemFilter", name) + ui.open() + +/obj/machinery/plumbing/filter/ui_data(mob/user) + var/list/data = list() + data["left"] = english_left + data["right"] = english_right + return data + +/obj/machinery/plumbing/filter/ui_act(action, params) + if(..()) + return + . = TRUE + switch(action) + if("add") + var/new_chem_name = params["name"] + var/chem_id = get_chem_id(new_chem_name) + if(chem_id) + switch(params["which"]) + if("left") + if(!left.Find(chem_id)) + english_left += new_chem_name + left += chem_id + if("right") + if(!right.Find(chem_id)) + english_right += new_chem_name + right += chem_id + else + to_chat(usr, "No such known reagent exists!") + + if("remove") + var/chem_name = params["reagent"] + var/chem_id = get_chem_id(chem_name) + switch(params["which"]) + if("left") + if(english_left.Find(chem_name)) + english_left -= chem_name + left -= chem_id + if("right") + if(english_right.Find(chem_name)) + english_right -= chem_name + right -= chem_id diff --git a/code/modules/plumbing/plumbers/grinder_chemical.dm b/code/modules/plumbing/plumbers/grinder_chemical.dm new file mode 100644 index 0000000000..4fa0d9e2cc --- /dev/null +++ b/code/modules/plumbing/plumbers/grinder_chemical.dm @@ -0,0 +1,58 @@ +/obj/machinery/plumbing/grinder_chemical + name = "chemical grinder" + desc = "chemical grinder." + icon_state = "grinder_chemical" + layer = ABOVE_ALL_MOB_LAYER + reagent_flags = TRANSPARENT | DRAINABLE + rcd_cost = 30 + rcd_delay = 30 + buffer = 400 + var/eat_dir = SOUTH + +/obj/machinery/plumbing/grinder_chemical/Initialize(mapload, bolt) + . = ..() + AddComponent(/datum/component/plumbing/simple_supply, bolt) + +/obj/machinery/plumbing/grinder_chemical/can_be_rotated(mob/user, rotation_type) + if(anchored) + to_chat(user, "It is fastened to the floor!") + return FALSE + return TRUE + +/obj/machinery/plumbing/grinder_chemical/setDir(newdir) + . = ..() + eat_dir = newdir + +/obj/machinery/plumbing/grinder_chemical/CanAllowThrough(atom/movable/AM) + . = ..() + if(!anchored) + return + var/move_dir = get_dir(loc, AM.loc) + if(move_dir == eat_dir) + return TRUE + +/obj/machinery/plumbing/grinder_chemical/Crossed(atom/movable/AM) + . = ..() + grind(AM) + +/obj/machinery/plumbing/grinder_chemical/proc/grind(atom/AM) + if(machine_stat & NOPOWER) + return + if(reagents.holder_full()) + return + if(!isitem(AM)) + return + var/obj/item/I = AM + if(I.juice_results || I.grind_results) + if(I.juice_results) + I.on_juice() + reagents.add_reagent_list(I.juice_results) + if(I.reagents) + I.reagents.trans_to(src, I.reagents.total_volume, transfered_by = src) + qdel(I) + return + I.on_grind() + reagents.add_reagent_list(I.grind_results) + if(I.reagents) + I.reagents.trans_to(src, I.reagents.total_volume, transfered_by = src) + qdel(I) diff --git a/code/modules/plumbing/plumbers/pill_press.dm b/code/modules/plumbing/plumbers/pill_press.dm new file mode 100644 index 0000000000..9c2505661c --- /dev/null +++ b/code/modules/plumbing/plumbers/pill_press.dm @@ -0,0 +1,107 @@ +///We take a constant input of reagents, and produce a pill once a set volume is reached +/obj/machinery/plumbing/pill_press + name = "pill press" + desc = "A press that presses pills." + icon_state = "pill_press" + ///the minimum size a pill can be + var/minimum_pill = 5 + ///the maximum size a pill can be + var/maximum_pill = 50 + ///the size of the pill + var/pill_size = 10 + ///pill name + var/pill_name = "factory pill" + ///the icon_state number for the pill. + var/pill_number = RANDOM_PILL_STYLE + ///list of id's and icons for the pill selection of the ui + var/list/pill_styles + ///list of pills stored in the machine, so we dont have 610 pills on one tile + var/list/stored_pills = list() + ///max amount of pills allowed on our tile before we start storing them instead + var/max_floor_pills = 10 + +/obj/machinery/plumbing/pill_press/examine(mob/user) + . = ..() + . += "The [name] currently has [stored_pills.len] stored. There needs to be less than [max_floor_pills] on the floor to continue dispensing." + +/obj/machinery/plumbing/pill_press/Initialize(mapload, bolt) + . = ..() + AddComponent(/datum/component/plumbing/simple_demand, bolt) + + //expertly copypasted from chemmasters + var/datum/asset/spritesheet/simple/assets = get_asset_datum(/datum/asset/spritesheet/simple/pills) + pill_styles = list() + for (var/x in 1 to PILL_STYLE_COUNT) + var/list/SL = list() + SL["id"] = x + SL["htmltag"] = assets.icon_tag("pill[x]") + pill_styles += list(SL) + +/obj/machinery/plumbing/pill_press/process() + if(stat & NOPOWER) + return + if(reagents.total_volume >= pill_size) + var/obj/item/reagent_containers/pill/P = new(src) + reagents.trans_to(P, pill_size) + P.name = pill_name + stored_pills += P + if(pill_number == RANDOM_PILL_STYLE) + P.icon_state = "pill[rand(1,21)]" + else + P.icon_state = "pill[pill_number]" + if(P.icon_state == "pill4") //mirrored from chem masters + P.desc = "A tablet or capsule, but not just any, a red one, one taken by the ones not scared of knowledge, freedom, uncertainty and the brutal truths of reality." + if(stored_pills.len) + var/pill_amount = 0 + for(var/obj/item/reagent_containers/pill/P in loc) + pill_amount++ + if(pill_amount >= max_floor_pills) //too much so just stop + break + if(pill_amount < max_floor_pills) + var/atom/movable/AM = stored_pills[1] //AM because forceMove is all we need + stored_pills -= AM + AM.forceMove(drop_location()) + + +/obj/machinery/plumbing/pill_press/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/simple/pills), + ) + +/obj/machinery/plumbing/pill_press/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ChemPress", name) + ui.open() + +/obj/machinery/plumbing/pill_press/ui_data(mob/user) + var/list/data = list() + data["pill_style"] = pill_number + data["current_volume"] = current_volume + data["product_name"] = product_name + data["pill_styles"] = pill_styles + data["product"] = product + data["min_volume"] = min_volume + data["max_volume"] = max_volume + return data + +/obj/machinery/plumbing/pill_press/ui_act(action, params) + if(..()) + return + . = TRUE + switch(action) + if("change_pill_style") + pill_number = clamp(text2num(params["id"]), 1 , PILL_STYLE_COUNT) + if("change_current_volume") + current_volume = clamp(text2num(params["volume"]), min_volume, max_volume) + if("change_product_name") + product_name = html_encode(params["name"]) + if("change_product") + product = params["product"] + if (product == "pill") + max_volume = max_pill_volume + else if (product == "patch") + max_volume = max_patch_volume + else if (product == "bottle") + max_volume = max_bottle_volume + current_volume = clamp(current_volume, min_volume, max_volume) diff --git a/code/modules/plumbing/plumbers/pumps.dm b/code/modules/plumbing/plumbers/pumps.dm new file mode 100644 index 0000000000..aeec8c9994 --- /dev/null +++ b/code/modules/plumbing/plumbers/pumps.dm @@ -0,0 +1,83 @@ +///We pump liquids from activated(plungerated) geysers to a plumbing outlet. We need to be wired. +/obj/machinery/power/liquid_pump + name = "liquid pump" + desc = "Pump up those sweet liquids from under the surface." + icon = 'icons/obj/plumbing/plumbers.dmi' + icon_state = "pump" + anchored = FALSE + density = TRUE + idle_power_usage = 10 + active_power_usage = 1000 + ///Are we powered? + var/powered = FALSE + ///units we pump per process (2 seconds) + var/pump_power = 2 + ///set to true if the loop couldnt find a geyser in process, so it remembers and stops checking every loop until moved. more accurate name would be absolutely_no_geyser_under_me_so_dont_try + var/geyserless = FALSE + ///The geyser object + var/obj/structure/geyser/geyser + ///volume of our internal buffer + var/volume = 200 + +/obj/machinery/power/liquid_pump/Initialize() + . = ..() + create_reagents(volume) + AddComponent(/datum/component/plumbing/simple_supply, TRUE) + +/obj/machinery/power/liquid_pump/attackby(obj/item/W, mob/user, params) + if(!powered) + if(!anchored) + if(default_deconstruction_screwdriver(user, "[initial(icon_state)]_open", "[initial(icon_state)]",W)) + return + if(default_deconstruction_crowbar(W)) + return + return ..() + +/obj/machinery/power/liquid_pump/wrench_act(mob/living/user, obj/item/I) + ..() + default_unfasten_wrench(user, I) + return TRUE +///please note that the component has a hook in the parent call, wich handles activating and deactivating +/obj/machinery/power/liquid_pump/default_unfasten_wrench(mob/user, obj/item/I, time = 20) + . = ..() + if(. == SUCCESSFUL_UNFASTEN) + geyser = null + update_icon() + powered = FALSE + geyserless = FALSE //we switched state, so lets just set this back aswell + +/obj/machinery/power/liquid_pump/process() + if(!anchored || panel_open) + return + if(!geyser && !geyserless) + for(var/obj/structure/geyser/G in loc.contents) + geyser = G + if(!geyser) //we didnt find one, abort + anchored = FALSE + geyserless = TRUE + visible_message("The [name] makes a sad beep!") + playsound(src, 'sound/machines/buzz-sigh.ogg', 50) + return + + if(avail(active_power_usage)) + if(!powered) //we werent powered before this tick so update our sprite + powered = TRUE + update_icon() + add_load(active_power_usage) + pump() + else if(powered) //we were powered, but now we arent + powered = FALSE + update_icon() +///pump up that sweet geyser nectar +/obj/machinery/power/liquid_pump/proc/pump() + if(!geyser || !geyser.reagents) + return + geyser.reagents.trans_to(src, pump_power) + +/obj/machinery/power/liquid_pump/update_icon() + if(powered) + icon_state = initial(icon_state) + "-on" + else if(panel_open) + icon_state = initial(icon_state) + "-open" + else + icon_state = initial(icon_state) diff --git a/code/modules/plumbing/plumbers/reaction_chamber.dm b/code/modules/plumbing/plumbers/reaction_chamber.dm new file mode 100644 index 0000000000..33fc5c8f0c --- /dev/null +++ b/code/modules/plumbing/plumbers/reaction_chamber.dm @@ -0,0 +1,63 @@ +///a reaction chamber for plumbing. pretty much everything can react, but this one keeps the reagents seperated and only reacts under your given terms +/obj/machinery/plumbing/reaction_chamber + name = "reaction chamber" + desc = "Keeps chemicals seperated until given conditions are met." + icon_state = "reaction_chamber" + + 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() + ///our reagent goal has been reached, so now we lock our inputs and start emptying + var/emptying = FALSE + +/obj/machinery/plumbing/reaction_chamber/Initialize(mapload, bolt) + . = ..() + AddComponent(/datum/component/plumbing/reaction_chamber, bolt) + +/obj/machinery/plumbing/reaction_chamber/on_reagent_change() + if(reagents.total_volume == 0 && emptying) //we were emptying, but now we aren't + emptying = FALSE + reagent_flags |= NO_REACT + +/obj/machinery/plumbing/reaction_chamber/power_change() + . = ..() + if(use_power != NO_POWER_USE) + icon_state = initial(icon_state) + "_on" + else + icon_state = initial(icon_state) + +/obj/machinery/plumbing/reaction_chamber/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ChemReactionChamber", name) + ui.open() + +/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 + return data + +/obj/machinery/plumbing/reaction_chamber/ui_act(action, params) + if(..()) + 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 diff --git a/code/modules/plumbing/plumbers/splitters.dm b/code/modules/plumbing/plumbers/splitters.dm new file mode 100644 index 0000000000..a26813486c --- /dev/null +++ b/code/modules/plumbing/plumbers/splitters.dm @@ -0,0 +1,50 @@ +///it splits the reagents however you want. So you can "every 60 units, 45 goes left and 15 goes straight". The side direction is EAST, you can change this in the component +/obj/machinery/plumbing/splitter + name = "Chemical Splitter" + desc = "A chemical splitter for smart chemical factorization. Waits till a set of conditions is met and then stops all input and splits the buffer evenly or other in two ducts." + icon_state = "splitter" + buffer = 100 + density = FALSE + + ///constantly switches between TRUE and FALSE. TRUE means the batch tick goes straight, FALSE means the next batch goes in the side duct. + var/turn_straight = TRUE + ///how much we must transfer straight. note input can be as high as 10 reagents per process, usually + var/transfer_straight = 5 + ///how much we must transfer to the side + var/transfer_side = 5 + //the maximum you can set the transfer to + var/max_transfer = 9 + + +/obj/machinery/plumbing/splitter/Initialize(mapload, bolt) + . = ..() + AddComponent(/datum/component/plumbing/splitter, bolt) + +/obj/machinery/plumbing/splitter/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ChemSplitter", name) + ui.open() + +/obj/machinery/plumbing/splitter/ui_data(mob/user) + var/list/data = list() + data["straight"] = transfer_straight + data["side"] = transfer_side + data["max_transfer"] = max_transfer + return data + +/obj/machinery/plumbing/splitter/ui_act(action, params) + if(..()) + return + . = TRUE + switch(action) + if("set_amount") + var/direction = params["target"] + var/value = clamp(text2num(params["amount"]), 1, max_transfer) + switch(direction) + if("straight") + transfer_straight = value + if("side") + transfer_side = value + else + return FALSE diff --git a/code/modules/plumbing/plumbers/synthesizer.dm b/code/modules/plumbing/plumbers/synthesizer.dm new file mode 100644 index 0000000000..2d782e9d3e --- /dev/null +++ b/code/modules/plumbing/plumbers/synthesizer.dm @@ -0,0 +1,111 @@ +///A single machine that produces a single chem. Can be placed in unison with others through plumbing to create chemical factories +/obj/machinery/plumbing/synthesizer + name = "chemical synthesizer" + desc = "Produces a single chemical at a given volume. Must be plumbed. Most effective when working in unison with other chemical synthesizers, heaters and filters." + + icon_state = "synthesizer" + icon = 'icons/obj/plumbing/plumbers.dmi' + rcd_cost = 25 + rcd_delay = 15 + + ///Amount we produce for every process. Ideally keep under 5 since thats currently the standard duct capacity + var/amount = 1 + ///The maximum we can produce for every process + buffer = 5 + ///I track them here because I have no idea how I'd make tgui loop like that + var/static/list/possible_amounts = list(0,1,2,3,4,5) + ///The reagent we are producing. We are a typepath, but are also typecast because there's several occations where we need to use initial. + var/datum/reagent/reagent_id = null + ///straight up copied from chem dispenser. Being a subtype would be extremely tedious and making it global would restrict potential subtypes using different dispensable_reagents + var/list/dispensable_reagents = list( + /datum/reagent/aluminium, + /datum/reagent/bromine, + /datum/reagent/carbon, + /datum/reagent/chlorine, + /datum/reagent/copper, + /datum/reagent/consumable/ethanol, + /datum/reagent/fluorine, + /datum/reagent/hydrogen, + /datum/reagent/iodine, + /datum/reagent/iron, + /datum/reagent/lithium, + /datum/reagent/mercury, + /datum/reagent/nitrogen, + /datum/reagent/oxygen, + /datum/reagent/phosphorus, + /datum/reagent/potassium, + /datum/reagent/uranium/radium, + /datum/reagent/silicon, + /datum/reagent/silver, + /datum/reagent/sodium, + /datum/reagent/stable_plasma, + /datum/reagent/consumable/sugar, + /datum/reagent/sulfur, + /datum/reagent/toxin/acid, + /datum/reagent/water, + /datum/reagent/fuel, + ) + +/obj/machinery/plumbing/synthesizer/Initialize(mapload, bolt) + . = ..() + AddComponent(/datum/component/plumbing/simple_supply, bolt) + +/obj/machinery/plumbing/synthesizer/process() + if(machine_stat & NOPOWER || !reagent_id || !amount) + return + if(reagents.total_volume >= amount) //otherwise we get leftovers, and we need this to be precise + return + reagents.add_reagent(reagent_id, amount) + +/obj/machinery/plumbing/synthesizer/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ChemSynthesizer", name) + ui.open() + +/obj/machinery/plumbing/synthesizer/ui_data(mob/user) + var/list/data = list() + + var/is_hallucinating = user.hallucinating() + var/list/chemicals = list() + + for(var/A in dispensable_reagents) + var/datum/reagent/R = GLOB.chemical_reagents_list[A] + if(R) + var/chemname = R.name + if(is_hallucinating && prob(5)) + chemname = "[pick_list_replacements("hallucination.json", "chemicals")]" + chemicals.Add(list(list("title" = chemname, "id" = ckey(R.name)))) + data["chemicals"] = chemicals + data["amount"] = amount + data["possible_amounts"] = possible_amounts + + data["current_reagent"] = ckey(initial(reagent_id.name)) + return data + +/obj/machinery/plumbing/synthesizer/ui_act(action, params) + if(..()) + return + . = TRUE + switch(action) + if("amount") + var/new_amount = text2num(params["target"]) + if(new_amount in possible_amounts) + amount = new_amount + . = TRUE + if("select") + var/new_reagent = GLOB.name2reagent[params["reagent"]] + if(new_reagent in dispensable_reagents) + reagent_id = new_reagent + . = TRUE + update_icon() + reagents.clear_reagents() + +/obj/machinery/plumbing/synthesizer/update_overlays() + . = ..() + var/mutable_appearance/r_overlay = mutable_appearance(icon, "[icon_state]_overlay") + if(reagent_id) + r_overlay.color = initial(reagent_id.color) + else + r_overlay.color = "#FFFFFF" + . += r_overlay diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index c2662a8342..c489edf88e 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -1165,3 +1165,9 @@ random_reagents += R var/picked_reagent = pick(random_reagents) return picked_reagent + +/proc/get_chem_id(chem_name) + for(var/X in GLOB.chemical_reagents_list) + var/datum/reagent/R = GLOB.chemical_reagents_list[X] + if(ckey(chem_name) == ckey(lowertext(R.name))) + return X diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index a85ac8b085..cdda9d0469 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -8,6 +8,7 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) if (length(initial(R.name))) .[ckey(initial(R.name))] = t + //Various reagents //Toxin & acid reagents //Hydroponics stuff @@ -220,4 +221,3 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) bloodsuckerdatum.handle_eat_human_food(disgust, blood_puke, force) if(blood_change) bloodsuckerdatum.AddBloodVolume(blood_change) - diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 0131e6d028..077c8331ae 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -24,7 +24,8 @@ /obj/structure/reagent_dispensers/Initialize() create_reagents(tank_volume, DRAINABLE | AMOUNT_VISIBLE) - reagents.add_reagent(reagent_id, tank_volume) + if(reagent_id) + reagents.add_reagent(reagent_id, tank_volume) . = ..() /obj/structure/reagent_dispensers/proc/boom() @@ -91,6 +92,38 @@ user.put_in_hands(S) paper_cups-- +/obj/structure/reagent_dispensers/plumbed + name = "stationairy water tank" + anchored = TRUE + icon_state = "water_stationairy" + desc = "A stationairy, plumbed, water tank." + +/obj/structure/reagent_dispensers/plumbed/wrench_act(mob/living/user, obj/item/I) + default_unfasten_wrench(user, I) + return TRUE + +/obj/structure/reagent_dispensers/plumbed/default_unfasten_wrench(mob/user, obj/item/I, time = 20) + . = ..() + if(. == SUCCESSFUL_UNFASTEN) + user.visible_message("[user.name] [anchored ? "fasten" : "unfasten"] [src]", \ + "You [anchored ? "fasten" : "unfasten"] [src]") + var/datum/component/plumbing/CP = GetComponent(/datum/component/plumbing) + if(anchored) + CP.enable() + else + CP.disable() + +/obj/structure/reagent_dispensers/plumbed/ComponentInitialize() + AddComponent(/datum/component/plumbing/simple_supply) + +/obj/structure/reagent_dispensers/plumbed/storage + name = "stationairy storage tank" + icon_state = "tank_stationairy" + reagent_id = null //start empty + +/obj/structure/reagent_dispensers/plumbed/storage/ComponentInitialize() + AddComponent(/datum/component/plumbing/tank) + ////////////// //Fuel Tanks// ////////////// @@ -284,5 +317,3 @@ icon_state = "bluekeg" reagent_id = /datum/reagent/consumable/ethanol/neurotoxin tank_volume = 100 //2.5x less than the other kegs because it's harder to get - - diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index e1e55f3476..0598ebf9ab 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -961,3 +961,158 @@ build_path = /obj/item/bodypart/r_arm/robot/surplus_upgraded category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + + +/datum/design/acclimator + name = "Plumbing Acclimator" + desc = "A heating and cooling device for pipes!" + id = "acclimator" + build_type = PROTOLATHE + materials = list(MAT_METAL = 1000, MAT_GLASS = 500) + construction_time = 75 + build_path = /obj/machinery/plumbing/acclimator + category = list("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/disposer + name = "Plumbing Disposer" + desc = "Using the power of SCIENCE, dissolves reagents into nothing (almost)." + id = "disposer" + build_type = PROTOLATHE + materials = list(MAT_METAL = 500, MAT_GLASS = 100) + construction_time = 75 + build_path = /obj/machinery/plumbing/disposer + category = list("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/plumb_filter + name = "Plumbing Filter" + desc = "Filters out chemicals by their NTDB ID." + id = "plumb_filter" + build_type = PROTOLATHE + materials = list(MAT_METAL = 1000, MAT_GLASS = 500) + construction_time = 75 + build_path = /obj/machinery/plumbing/filter + category = list("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/plumb_synth + name = "Plumbing Synthesizer" + desc = "Using standard mass-energy dynamic autoconverters, generates reagents from power and puts them in a pipe." + id = "plumb_synth" + build_type = PROTOLATHE + materials = list(MAT_METAL = 5000, MAT_GLASS = 1000, MAT_PLASTIC = 1000) + construction_time = 75 + build_path = /obj/machinery/plumbing/synthesizer + category = list("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/plumb_grinder + name = "Plumbing-Linked Autogrinder" + desc = "Automatically extracts reagents from an item by grinding it. Think of the possibilities! Note: does not grind people." + id = "plumb_grinder" + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000, MAT_GLASS = 1500) + construction_time = 75 + build_path = /obj/machinery/plumbing/grinder_chemical + category = list("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/reaction_chamber + name = "Plumbing Reaction Chamber" + desc = "You can set a list of allowed reagents and amounts. Once the chamber has these reagents, will let the products through." + id = "reaction_chamber" + build_type = PROTOLATHE + materials = list(MAT_METAL = 1000, MAT_GLASS = 500) + construction_time = 75 + build_path = /obj/machinery/plumbing/reaction_chamber + category = list("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/duct_print + name = "Plumbing Ducts" + desc = "Ducts for plumbing! Now lathed for efficiency." + id = "duct_print" + build_type = PROTOLATHE + materials = list(MAT_PLASTIC = 400) + construction_time = 5 + build_path = /obj/item/stack/ducts + category = list("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/plumb_splitter + name = "Plumbing Chemical Splitter" + desc = "A splitter. Has 2 outputs. Can be configured to allow a certain amount through each side." + id = "plumb_splitter" + build_type = PROTOLATHE + materials = list(MAT_METAL = 750, MAT_GLASS = 250) + construction_time = 75 + build_path = /obj/machinery/plumbing/splitter + category = list("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/pill_press + name = "Plumbing Automatic Pill Former" + desc = "Automatically forms pills to the required parameters with piped reagents! A good replacement for those lazy, useless chemists." + id = "pill_press" + build_type = PROTOLATHE + materials = list(MAT_METAL = 1000, MAT_GLASS = 500) + construction_time = 75 + build_path = /obj/machinery/plumbing/pill_press + category = list("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/plumb_pump + name = "Liquid Extraction Pump" + desc = "Use it for extracting liquids from lavaland's geysers!" + id = "plumb_pump" + build_type = PROTOLATHE + materials = list(MAT_METAL = 1000, MAT_GLASS = 500) + construction_time = 75 + build_path = /obj/machinery/power/liquid_pump + category = list("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/plumb_in + name = "Plumbing Input Device" + desc = "A big piped funnel for putting stuff in the pipe network." + id = "plumb_in" + build_type = PROTOLATHE + materials = list(MAT_METAL = 400, MAT_GLASS = 400) + construction_time = 75 + build_path = /obj/machinery/plumbing/input + category = list("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/plumb_out + name = "Plumbing Output Device" + desc = "A big piped funnel for taking stuff out of the pipe network." + id = "plumb_out" + build_type = PROTOLATHE + materials = list(MAT_METAL = 400, MAT_GLASS = 400) + construction_time = 75 + build_path = /obj/machinery/plumbing/output + category = list("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/plumb_tank + name = "Plumbed Storage Tank" + desc = "A tank for storing plumbed reagents." + id = "plumb_tank" + build_type = PROTOLATHE + materials = list(MAT_METAL = 10000, MAT_GLASS = 10000, MAT_PLASTIC = 4000) + construction_time = 75 + build_path = /obj/machinery/plumbing/tank + category = list("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/plumb_rcd + name = "Plumbed Autoconstruction Device" + desc = "A RCD for plumbing machines! Cannot make ducts." + id = "plumb_rcd" + build_type = PROTOLATHE + materials = list(MAT_METAL = 20000, MAT_GLASS = 10000, MAT_PLASTIC = 20000, MAT_PLASMA = 6000, MAT_DIAMOND = 5000, MAT_BLUESPACE = 5000, MAT_GOLD = 5000, MAT_SILVER = 5000) + construction_time = 150 + build_path = /obj/item/construction/plumbing + category = list("Misc","Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCEs diff --git a/code/modules/research/techweb/nodes/medical_nodes.dm b/code/modules/research/techweb/nodes/medical_nodes.dm index 71dd7c943c..43fe941ab2 100644 --- a/code/modules/research/techweb/nodes/medical_nodes.dm +++ b/code/modules/research/techweb/nodes/medical_nodes.dm @@ -24,6 +24,23 @@ design_ids = list("defib_decay", "defib_shock", "defib_heal", "defib_speed") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) +/datum/techweb_node/plumbing + id = "plumbing" + display_name = "Reagent Plumbing Technology" + description = "Plastic tubes, and machinery used for manipulating things in them." + prereq_ids = list("base") + design_ids = list("acclimator", "disposer", "plumb_filter", "plumb_synth", "plumb_grinder", "reaction_chamber", "duct_print", "plumb_splitter", "pill_press", "plumb_pump", "plumb_in", "plumb_out", "plumb_tank") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + + +/datum/techweb_node/advplumbing + id = "advplumbing" + display_name = "Advanced Plumbing Technology" + description = "Plumbing RCD." + prereq_ids = list("plumbing", "adv_engi", "adv_biotech") + design_ids = list("plumb_rcd") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10000) + //////////////////////Cybernetics///////////////////// /datum/techweb_node/surplus_limbs diff --git a/icons/obj/watercloset.dmi b/icons/obj/watercloset.dmi index 4a299f29ddb16c596e251c00890f4f016c630ece..94ea415ed7cf406fb49befddd17426bccf1fd3c2 100644 GIT binary patch literal 16012 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 literal 23715 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 From 661b9920fc1a9a06c894d539c687c454cdcca781 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Wed, 22 Jul 2020 10:59:21 +1000 Subject: [PATCH 02/42] luh mayo --- code/game/objects/items/RPD.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/RPD.dm b/code/game/objects/items/RPD.dm index 2c8acf5a28..85a594d7d1 100644 --- a/code/game/objects/items/RPD.dm +++ b/code/game/objects/items/RPD.dm @@ -282,7 +282,7 @@ GLOBAL_LIST_INIT(fluid_duct_recipes, list( "categories" = list(), "selected_color" = paint_color, "paint_colors" = GLOB.pipe_paint_colors, - "mode" = mode + "mode" = mode, "locked" = locked ) From c07e5d544ba31479fb7a38a29fd0112fb3ec78bb Mon Sep 17 00:00:00 2001 From: lolman360 Date: Wed, 22 Jul 2020 11:27:26 +1000 Subject: [PATCH 03/42] wweeee --- .../circuitboards/machine_circuitboards.dm | 4 ++ code/modules/hydroponics/hydroponics.dm | 15 +++--- code/modules/plumbing/plumbers/autohydro.dm | 54 +++++++++++++++++++ .../machine_designs_service.dm | 8 +++ .../research/techweb/nodes/medical_nodes.dm | 2 +- 5 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 code/modules/plumbing/plumbers/autohydro.dm diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm index 56eb25f953..8a8aba50a3 100644 --- a/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -512,6 +512,10 @@ /obj/item/stack/sheet/glass = 1) needs_anchored = FALSE +/obj/item/circuitboard/machine/hydroponics/automagic + name = "Automatic Hydroponics Tray (Machine Board)" + build_path = /obj/machinery/hydroponics/constructable/automagic + /obj/item/circuitboard/machine/seed_extractor name = "Seed Extractor (Machine Board)" build_path = /obj/machinery/seed_extractor diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index b673937c9c..14c510a2d2 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -30,7 +30,7 @@ var/self_sufficiency_req = 20 //Required total dose to make a self-sufficient hydro tray. 1:1 with earthsblood. var/self_sufficiency_progress = 0 var/self_sustaining = FALSE //If the tray generates nutrients and water on its own - + var/canirrigate = TRUE //tin /obj/machinery/hydroponics/constructable name = "hydroponics tray" @@ -847,12 +847,13 @@ if (!anchored) to_chat(user, "Anchor the tray first!") return - using_irrigation = !using_irrigation - O.play_tool_sound(src) - user.visible_message("[user] [using_irrigation ? "" : "dis"]connects [src]'s irrigation hoses.", \ - "You [using_irrigation ? "" : "dis"]connect [src]'s irrigation hoses.") - for(var/obj/machinery/hydroponics/h in range(1,src)) - h.update_icon() + if(canirrigate) + using_irrigation = !using_irrigation + O.play_tool_sound(src) + user.visible_message("[user] [using_irrigation ? "" : "dis"]connects [src]'s irrigation hoses.", \ + "You [using_irrigation ? "" : "dis"]connect [src]'s irrigation hoses.") + for(var/obj/machinery/hydroponics/h in range(1,src)) + h.update_icon() else if(istype(O, /obj/item/shovel/spade)) if(!myseed && !weedlevel) diff --git a/code/modules/plumbing/plumbers/autohydro.dm b/code/modules/plumbing/plumbers/autohydro.dm new file mode 100644 index 0000000000..4ea1d70aaf --- /dev/null +++ b/code/modules/plumbing/plumbers/autohydro.dm @@ -0,0 +1,54 @@ +/obj/machinery/hydroponics/constructable/automagic + name = "automated hydroponics system" + desc = "The bane of botanists everywhere. Accepts chemical reagents via plumbing, automatically harvests and removes dead plants." + obj_flags = CAN_BE_HIT | UNIQUE_RENAME + circuit = /obj/item/circuitboard/machine/hydroponics/automagic + self_sufficiency_req = 400 //automating hydroponics makes gaia sad so she needs more drugs to turn they tray godly. + canirrigate = FALSE + + +/obj/machinery/hydroponics/attackby(obj/item/O, mob/user, params) + if(istype(O, /obj/item/reagent_containers) ) + return FALSE //avoid fucky wuckies + ..() + +/obj/machinery/hydroponics/constructable/automagic/default_unfasten_wrench(mob/user, obj/item/I, time = 20) + . = ..() + if(. == SUCCESSFUL_UNFASTEN) + user.visible_message("[user.name] [anchored ? "fasten" : "unfasten"] [src]", \ + "You [anchored ? "fasten" : "unfasten"] [src]") + var/datum/component/plumbing/CP = GetComponent(/datum/component/plumbing) + if(anchored) + CP.enable() + else + CP.disable() + +/obj/machinery/hydroponics/constructable/automagic/Initialize(mapload, bolt) + . = ..() + AddComponent(/datum/component/plumbing/simple_demand, bolt) + create_reagents(100 , AMOUNT_VISIBLE) + + +/obj/machinery/hydroponics/constructable/automagic/process() + if(reagents) + applyChemicals(reagents) + reagents.clear_reagents() + if(dead) + dead = 0 + qdel(myseed) + myseed = null + update_icon() + name = initial(name) + desc = initial(desc) + if(harvest) + myseed.harvest() + harvest = 0 + lastproduce = age + if(!myseed.get_gene(/datum/plant_gene/trait/repeated_harvest)) + qdel(myseed) + myseed = null + dead = 0 + name = initial(name) + desc = initial(desc) + update_icon() + ..() diff --git a/code/modules/research/designs/machine_desings/machine_designs_service.dm b/code/modules/research/designs/machine_desings/machine_designs_service.dm index 5cbff1c66a..af4f650793 100644 --- a/code/modules/research/designs/machine_desings/machine_designs_service.dm +++ b/code/modules/research/designs/machine_desings/machine_designs_service.dm @@ -81,6 +81,14 @@ category = list ("Hydroponics Machinery") departmental_flags = DEPARTMENTAL_FLAG_SERVICE +/datum/design/board/hydroponics/auto + name = "Machine Design (Automatic Hydroponics Tray Board)" + desc = "The circuit board for an automatic hydroponics tray. GIVE ME THE PLANT, CAPTAIN." + id = "autohydrotray" + build_path = /obj/machinery/hydroponics/constructable/automagic + category = list ("Hydroponics Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SERVICE | DEPARTMENTAL_FLAG_MEDICAL + /datum/design/board/monkey_recycler name = "Machine Design (Monkey Recycler Board)" desc = "The circuit board for a monkey recycler." diff --git a/code/modules/research/techweb/nodes/medical_nodes.dm b/code/modules/research/techweb/nodes/medical_nodes.dm index 43fe941ab2..a2fba1741a 100644 --- a/code/modules/research/techweb/nodes/medical_nodes.dm +++ b/code/modules/research/techweb/nodes/medical_nodes.dm @@ -38,7 +38,7 @@ display_name = "Advanced Plumbing Technology" description = "Plumbing RCD." prereq_ids = list("plumbing", "adv_engi", "adv_biotech") - design_ids = list("plumb_rcd") + design_ids = list("plumb_rcd", "autohydrotray") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10000) //////////////////////Cybernetics///////////////////// From f33487e972fc85cf1724588efc63ce39e57c1609 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Wed, 22 Jul 2020 11:36:50 +1000 Subject: [PATCH 04/42] complie --- .../components/plumbing/reaction_chamber.dm | 1 - icons/obj/plumbing/fluid_ducts.dmi | Bin 0 -> 979 bytes icons/obj/plumbing/plumbers.dmi | Bin 0 -> 16528 bytes tgstation.dme | 23 ++++++++++++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 icons/obj/plumbing/fluid_ducts.dmi create mode 100644 icons/obj/plumbing/plumbers.dmi diff --git a/code/datums/components/plumbing/reaction_chamber.dm b/code/datums/components/plumbing/reaction_chamber.dm index d0a9416e34..6435d11ae6 100644 --- a/code/datums/components/plumbing/reaction_chamber.dm +++ b/code/datums/components/plumbing/reaction_chamber.dm @@ -32,7 +32,6 @@ RC.reagent_flags &= ~NO_REACT reagents.handle_reactions() - Add when everything works: if(reagents.fermiIsReacting) return RC.emptying = TRUE diff --git a/icons/obj/plumbing/fluid_ducts.dmi b/icons/obj/plumbing/fluid_ducts.dmi new file mode 100644 index 0000000000000000000000000000000000000000..87d9d2233b43ec1fe08f2f0d529166c3c74131d0 GIT binary patch literal 979 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 HZ`5_WK4d- zgK3xKPT`?liAP0eGyala%(Vl)^gC!MxV0f%je;+c762X2E8xC_Xon_M+^DSHlseh@ z1FJ8$olJH7sd&%o{5)^kcGTvT!6Pc%&X~BEn6(tMf3mzBV+-yVEmcH<7_`6uf)okd zR*|SH(2qt(5{b$RJGqUPwXg0bOFvdIw5Q^IWvp#XN_IL8`j~)9Pc-7{J!%>b)f(IUIUbHeV30hmIpJx}`Nh zll~{&@yiF(@wh*7>IdCo-92ez?e|lC4Wn;Sw^f+3@J`(l`KTN~;rUhstkt{zIq+6x zml8F|K1%1}^Kdt7-AbLnkjBT0&5X1s2z)rBU*#cR`s1_$^7@3^SX1d0}V)prRQ%tEv;Wz?G|}& zQZf_|x_R0;JpE+uXJ8Y%zbcvc12Zoa{?*V4Vq0hgE6%k(Ur0!t#D0qbYYI~oJIz|( z|5B7?$$Waq{+`k}8p+3u>=L95iGr(XAKQuo!jBsi?Bi)u9j#~9Ui*R~R5V4{9->~$ zT&pw!Lj_{Q)j#z`g1!V6CulrbM4$t=Yyaf7ZPB0+=OlgOO`^a$B3PG&4@19X_}AMj z->}mmHj=pb$(B}b{1VeIXENb0dIXIa$2(cucFAQYLxY)KF_J!wT+ABtcAGMJl-#+T rX$TS(C~FEO&j^hd5RUC2(OfYe(THM}i3WdZ0MNN@sPR$F_WAz<->$Gj literal 0 HcmV?d00001 diff --git a/tgstation.dme b/tgstation.dme index 2c5d30e2b7..f6e46312d0 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -79,6 +79,7 @@ #include "code\__DEFINES\networks.dm" #include "code\__DEFINES\pinpointers.dm" #include "code\__DEFINES\pipe_construction.dm" +#include "code\__DEFINES\plumbing.dm" #include "code\__DEFINES\pool.dm" #include "code\__DEFINES\power.dm" #include "code\__DEFINES\preferences.dm" @@ -289,6 +290,7 @@ #include "code\controllers\subsystem\events.dm" #include "code\controllers\subsystem\fail2topic.dm" #include "code\controllers\subsystem\fire_burning.dm" +#include "code\controllers\subsystem\fluid.dm" #include "code\controllers\subsystem\garbage.dm" #include "code\controllers\subsystem\holodeck.dm" #include "code\controllers\subsystem\icon_smooth.dm" @@ -362,6 +364,7 @@ #include "code\datums\datumvars.dm" #include "code\datums\dna.dm" #include "code\datums\dog_fashion.dm" +#include "code\datums\ductnet.dm" #include "code\datums\emotes.dm" #include "code\datums\ert.dm" #include "code\datums\explosion.dm" @@ -472,6 +475,11 @@ #include "code\datums\components\fantasy\affix.dm" #include "code\datums\components\fantasy\prefixes.dm" #include "code\datums\components\fantasy\suffixes.dm" +#include "code\datums\components\plumbing\_plumbing.dm" +#include "code\datums\components\plumbing\chemical_acclimator.dm" +#include "code\datums\components\plumbing\filter.dm" +#include "code\datums\components\plumbing\reaction_chamber.dm" +#include "code\datums\components\plumbing\splitter.dm" #include "code\datums\components\storage\storage.dm" #include "code\datums\components\storage\ui.dm" #include "code\datums\components\storage\concrete\_concrete.dm" @@ -1181,6 +1189,7 @@ #include "code\game\objects\structures\flora.dm" #include "code\game\objects\structures\fluff.dm" #include "code\game\objects\structures\fugitive_role_spawners.dm" +#include "code\game\objects\structures\geyser.dm" #include "code\game\objects\structures\ghost_role_spawners.dm" #include "code\game\objects\structures\girders.dm" #include "code\game\objects\structures\grille.dm" @@ -2797,6 +2806,20 @@ #include "code\modules\photography\photos\album.dm" #include "code\modules\photography\photos\frame.dm" #include "code\modules\photography\photos\photo.dm" +#include "code\modules\plumbing\ducts.dm" +#include "code\modules\plumbing\plumbers\_plumb_machinery.dm" +#include "code\modules\plumbing\plumbers\acclimator.dm" +#include "code\modules\plumbing\plumbers\autohydro.dm" +#include "code\modules\plumbing\plumbers\bottler.dm" +#include "code\modules\plumbing\plumbers\destroyer.dm" +#include "code\modules\plumbing\plumbers\fermenter.dm" +#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\pumps.dm" +#include "code\modules\plumbing\plumbers\reaction_chamber.dm" +#include "code\modules\plumbing\plumbers\splitters.dm" +#include "code\modules\plumbing\plumbers\synthesizer.dm" #include "code\modules\pool\pool_controller.dm" #include "code\modules\pool\pool_drain.dm" #include "code\modules\pool\pool_effects.dm" From 17d1fd0692a568984c90e7df2641c3facb7c4150 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Wed, 22 Jul 2020 12:42:46 +1000 Subject: [PATCH 05/42] fixes --- code/datums/components/plumbing/_plumbing.dm | 4 +- .../plumbing/chemical_acclimator.dm | 2 +- code/datums/components/plumbing/filter.dm | 4 +- .../components/plumbing/reaction_chamber.dm | 10 +-- code/game/objects/items/RCD.dm | 19 +++-- code/game/objects/structures/geyser.dm | 74 ---------------- .../turfs/simulated/floor/plating/asteroid.dm | 11 +++ code/modules/asset_cache/asset_list_items.dm | 2 +- .../plumbing/plumbers/_plumb_machinery.dm | 3 +- code/modules/plumbing/plumbers/acclimator.dm | 4 +- code/modules/plumbing/plumbers/bottler.dm | 2 +- code/modules/plumbing/plumbers/fermenter.dm | 25 ++++-- code/modules/plumbing/plumbers/filter.dm | 4 +- .../plumbing/plumbers/grinder_chemical.dm | 28 +++--- code/modules/plumbing/plumbers/pill_press.dm | 80 +++++++++++------- code/modules/plumbing/plumbers/pumps.dm | 59 +++++-------- code/modules/plumbing/plumbers/synthesizer.dm | 4 +- code/modules/reagents/chemistry/reagents.dm | 8 ++ .../chemistry/reagents/medicine_reagents.dm | 16 ++-- .../chemistry/reagents/other_reagents.dm | 16 +++- .../chemistry/reagents/toxin_reagents.dm | 1 + .../reagents/chemistry/recipes/medicine.dm | 8 +- .../reagents/chemistry/recipes/others.dm | 35 +++++++- .../research/designs/medical_designs.dm | 2 +- code/modules/vending/medical.dm | 3 +- icons/obj/lavaland/terrain.dmi | Bin 0 -> 771 bytes tgstation.dme | 1 - 27 files changed, 222 insertions(+), 203 deletions(-) delete mode 100644 code/game/objects/structures/geyser.dm create mode 100644 icons/obj/lavaland/terrain.dmi diff --git a/code/datums/components/plumbing/_plumbing.dm b/code/datums/components/plumbing/_plumbing.dm index 22b8bafde5..6592e41103 100644 --- a/code/datums/components/plumbing/_plumbing.dm +++ b/code/datums/components/plumbing/_plumbing.dm @@ -17,7 +17,7 @@ var/turn_connects = TRUE /datum/component/plumbing/Initialize(start=TRUE, _turn_connects=TRUE) //turn_connects for wheter or not we spin with the object to change our pipes - if(parent && !ismovableatom(parent)) + if(parent && !istype(parent, /atom/movable)) return COMPONENT_INCOMPATIBLE var/atom/movable/AM = parent if(!AM.reagents) @@ -77,7 +77,7 @@ if(reagent) //only asked for one type of reagent for(var/A in reagents.reagent_list) var/datum/reagent/R = A - if(R.id == reagent) + if(R.type == reagent) return TRUE else if(reagents.total_volume > 0) //take whatever return TRUE diff --git a/code/datums/components/plumbing/chemical_acclimator.dm b/code/datums/components/plumbing/chemical_acclimator.dm index 44a1396c6d..1cbe6ff017 100644 --- a/code/datums/components/plumbing/chemical_acclimator.dm +++ b/code/datums/components/plumbing/chemical_acclimator.dm @@ -1,4 +1,4 @@ -v/datum/component/plumbing/acclimator +/datum/component/plumbing/acclimator demand_connects = WEST supply_connects = EAST var/obj/machinery/plumbing/acclimator/AC diff --git a/code/datums/components/plumbing/filter.dm b/code/datums/components/plumbing/filter.dm index cfbfb641da..55647a353b 100644 --- a/code/datums/components/plumbing/filter.dm +++ b/code/datums/components/plumbing/filter.dm @@ -35,12 +35,12 @@ else for(var/A in reagents.reagent_list) var/datum/reagent/R = A - if(!can_give_in_direction(direction, R.id)) + if(!can_give_in_direction(direction, R)) continue var/new_amount if(R.volume < amount) new_amount = amount - R.volume - reagents.trans_id_to(target.parent, R.id, amount) + reagents.trans_id_to(target.parent, R, amount) amount = new_amount if(amount <= 0) break diff --git a/code/datums/components/plumbing/reaction_chamber.dm b/code/datums/components/plumbing/reaction_chamber.dm index 6435d11ae6..705611ed1a 100644 --- a/code/datums/components/plumbing/reaction_chamber.dm +++ b/code/datums/components/plumbing/reaction_chamber.dm @@ -17,17 +17,17 @@ var/obj/machinery/plumbing/reaction_chamber/RC = parent if(RC.emptying || !LAZYLEN(RC.required_reagents)) return - for(var/RTid in RC.required_reagents) + for(var/RT in RC.required_reagents) var/has_reagent = FALSE for(var/A in reagents.reagent_list) var/datum/reagent/RD = A - if(RTid == RD.id) + if(RT == RD) has_reagent = TRUE - if(RD.volume < RC.required_reagents[RTid]) - process_request(min(RC.required_reagents[RTid] - RD.volume, MACHINE_REAGENT_TRANSFER) , RTid, dir) + 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[RTid], MACHINE_REAGENT_TRANSFER), RTid, dir) + process_request(min(RC.required_reagents[RT], MACHINE_REAGENT_TRANSFER), RT, dir) return RC.reagent_flags &= ~NO_REACT diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm index 29a04237ea..3ec0da70c5 100644 --- a/code/game/objects/items/RCD.dm +++ b/code/game/objects/items/RCD.dm @@ -135,6 +135,14 @@ RLD flick("[icon_state]_empty", src) //somewhat hacky thing to make RCDs with ammo counters actually have a blinking yellow light return . + +/obj/item/construction/proc/check_menu(mob/living/user) + if(!istype(user)) + return FALSE + if(user.incapacitated() || !user.Adjacent(src)) + return FALSE + return TRUE + /obj/item/construction/proc/range_check(atom/A, mob/user) if(!(A in range(custom_range, get_turf(user)))) to_chat(user, "The \'Out of Range\' light on [src] blinks red.") @@ -276,13 +284,6 @@ RLD //Not scaling these down to button size because they look horrible then, instead just bumping up radius. return MA -/obj/item/construction/rcd/proc/check_menu(mob/living/user) - if(!istype(user)) - return FALSE - if(user.incapacitated() || !user.Adjacent(src)) - return FALSE - return TRUE - /obj/item/construction/rcd/proc/change_computer_dir(mob/user) if(!user) return @@ -915,9 +916,9 @@ RLD if(O.density) //let's not built ontop of dense stuff, like big machines and other obstacles, it kills my immershion return FALSE -/obj/item/construction/plumbing/afterattack(atom/A, mob/user, proximity) +/obj/item/construction/plumbing/afterattack(atom/A, mob/user) . = ..() - if(!prox_check(proximity)) + if(!range_check(A, user)) return if(istype(A, /obj/machinery/plumbing)) var/obj/machinery/plumbing/P = A diff --git a/code/game/objects/structures/geyser.dm b/code/game/objects/structures/geyser.dm deleted file mode 100644 index 1d77a3c4f8..0000000000 --- a/code/game/objects/structures/geyser.dm +++ /dev/null @@ -1,74 +0,0 @@ -//If you look at the "geyser_soup" overlay icon_state, you'll see that the first frame has 25 ticks. -//That's because the first 18~ ticks are completely skipped for some ungodly weird fucking byond reason - -/obj/structure/geyser - name = "geyser" - icon = 'icons/obj/lavaland/terrain.dmi' - icon_state = "geyser" - anchored = TRUE - var/decay = 0 //reagents/tick removed - var/erupting_state = null //set to null to get it greyscaled from "[icon_state]_soup". Not very usable with the whole random thing, but more types can be added if you change the spawn prob - var/activated = FALSE //whether we are active and generating chems - var/reagent_id = "oil" - var/potency = 2 //how much reagents we add every process (2 seconds) - var/max_volume = 500 - var/start_volume = 50 - -/obj/structure/geyser/proc/start_chemming() - activated = TRUE - create_reagents(max_volume, DRAINABLE) - reagents.add_reagent(reagent_id, start_volume) - START_PROCESSING(SSfluids, src) //It's main function is to be plumbed, so use SSfluids - if(erupting_state) - icon_state = erupting_state - else - var/mutable_appearance/I = mutable_appearance('icons/obj/lavaland/terrain.dmi', "[icon_state]_soup") - I.color = mix_color_from_reagents(reagents.reagent_list) - add_overlay(I) - -/obj/structure/geyser/process() - if(activated && reagents.total_volume <= reagents.maximum_volume) //this is also evaluated in add_reagent, but from my understanding proc calls are expensive and should be avoided in continous - reagents.add_reagent(reagent_id, potency) //processes - if(potency > 0) - potency =- decay //decaying! - -/obj/structure/geyser/plunger_act(obj/item/plunger/P, mob/living/user, _reinforced) - if(!_reinforced) - to_chat(user, "The [P.name] isn't strong enough!") - return - if(activated) - to_chat(user, "The [name] is already active!") - return - - to_chat(user, "You start vigorously plunging [src]!") - if(do_after(user, 50*P.plunge_mod, target = src) && !activated) - start_chemming() - -/obj/structure/geyser/random - erupting_state = null - var/list/options = list("oil" = 2, "clf3" = 1) //formerly crudeoil, 2,1 -/obj/structure/geyser/random/Initialize() - . = ..() - reagent_id = pickweight(options) - -/obj/item/plunger - name = "plunger" - desc = "It's a plunger for plunging." - icon = 'icons/obj/watercloset.dmi' - icon_state = "plunger" - slot_flags = ITEM_SLOT_MASK - - var/plunge_mod = 1 //time*plunge_mod = total time we take to plunge an object - var/reinforced = FALSE //whether we do heavy duty stuff like geysers - -/obj/item/plunger/attack_obj(obj/O, mob/living/user) - if(!O.plunger_act(src, user, reinforced)) - return ..() - -/obj/item/plunger/reinforced - name = "reinforced plunger" - desc = " It's an M. 7 Reinforced Plunger for heavy duty plunging." - icon_state = "reinforced_plunger" - - reinforced = TRUE - plunge_mod = 0.8 diff --git a/code/game/turfs/simulated/floor/plating/asteroid.dm b/code/game/turfs/simulated/floor/plating/asteroid.dm index 1fbeee7523..f862a314a7 100644 --- a/code/game/turfs/simulated/floor/plating/asteroid.dm +++ b/code/game/turfs/simulated/floor/plating/asteroid.dm @@ -334,8 +334,19 @@ spawned_flora = SpawnFlora(T) if(!spawned_flora) // no rocks beneath mob spawners / mobs. SpawnMonster(T) + SpawnTerrain(T) T.ChangeTurf(turf_type, null, CHANGETURF_IGNORE_AIR) +/turf/open/floor/plating/asteroid/airless/cave/proc/SpawnTerrain(turf/T) + if(prob(1)) + if(istype(loc, /area/mine/explored) || istype(loc, /area/lavaland/surface/outdoors/explored)) + return + var/randumb = pickweight(terrain_spawn_list) + for(var/obj/structure/geyser/F in range(7, T)) + if(istype(F, randumb)) + return + new randumb(T) + /// Spawns a random mob or megafauna in the tunnel /turf/open/floor/plating/asteroid/airless/cave/proc/SpawnMonster(turf/T) if(!isarea(loc)) diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index e44c5e2a3f..febf3a84e5 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -266,7 +266,7 @@ name = "pipes" /datum/asset/spritesheet/pipes/register() - for (var/each in list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi', 'icons/obj/atmospherics/pipes/transit_tube.dmi', 'icons/obj/plumbing/fluid_ducts.dmi')) + for (var/each in list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi', 'icons/obj/atmospherics/pipes/transit_tube.dmi', 'icons/obj/plumbing/fluid_ducts.dmi')) InsertAll("", each, GLOB.alldirs) ..() diff --git a/code/modules/plumbing/plumbers/_plumb_machinery.dm b/code/modules/plumbing/plumbers/_plumb_machinery.dm index 1ea7ab56b1..0566945e3b 100644 --- a/code/modules/plumbing/plumbers/_plumb_machinery.dm +++ b/code/modules/plumbing/plumbers/_plumb_machinery.dm @@ -26,11 +26,12 @@ . = ..() anchored = bolt create_reagents(buffer, reagent_flags) - AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS ) + AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated)) /obj/machinery/plumbing/proc/can_be_rotated(mob/user,rotation_type) return TRUE + /obj/machinery/plumbing/examine(mob/user) . = ..() . += "The maximum volume display reads: [reagents.maximum_volume] units." diff --git a/code/modules/plumbing/plumbers/acclimator.dm b/code/modules/plumbing/plumbers/acclimator.dm index 475614c421..1b4ebe8808 100644 --- a/code/modules/plumbing/plumbers/acclimator.dm +++ b/code/modules/plumbing/plumbers/acclimator.dm @@ -25,8 +25,8 @@ and you get nasty leftovers */ var/emptying = FALSE - var/ui_x = 320 - var/ui_y = 310 + ui_x = 320 + ui_y = 310 /obj/machinery/plumbing/acclimator/Initialize(mapload, bolt) . = ..() diff --git a/code/modules/plumbing/plumbers/bottler.dm b/code/modules/plumbing/plumbers/bottler.dm index c6595fc397..57233a5873 100644 --- a/code/modules/plumbing/plumbers/bottler.dm +++ b/code/modules/plumbing/plumbers/bottler.dm @@ -56,7 +56,7 @@ to_chat(user, " The [src] will now fill for [wanted_amount]u.") /obj/machinery/plumbing/bottler/process() - if(machine_stat & NOPOWER) + if(stat & NOPOWER) return ///see if machine has enough to fill if(reagents.total_volume >= wanted_amount && anchored) diff --git a/code/modules/plumbing/plumbers/fermenter.dm b/code/modules/plumbing/plumbers/fermenter.dm index e13ae3feff..b1e1e4b676 100644 --- a/code/modules/plumbing/plumbers/fermenter.dm +++ b/code/modules/plumbing/plumbers/fermenter.dm @@ -14,17 +14,25 @@ . = ..() AddComponent(/datum/component/plumbing/simple_supply, bolt) -/obj/machinery/plumbing/grinder_chemical/can_be_rotated(mob/user, rotation_type) +/obj/machinery/plumbing/fermenter/can_be_rotated(mob/user,rotation_type) if(anchored) to_chat(user, "It is fastened to the floor!") return FALSE - return TRUE + switch(eat_dir) + if(WEST) + eat_dir = NORTH + return TRUE + if(EAST) + eat_dir = SOUTH + return TRUE + if(NORTH) + eat_dir = EAST + return TRUE + if(SOUTH) + eat_dir = WEST + return TRUE -/obj/machinery/plumbing/fermenter/setDir(newdir) - . = ..() - eat_dir = newdir - -/obj/machinery/plumbing/fermenter/CanAllowThrough(atom/movable/AM) +/obj/machinery/plumbing/fermenter/CanPass(atom/movable/AM) . = ..() if(!anchored) return @@ -36,9 +44,8 @@ . = ..() ferment(AM) -/// uses fermentation proc similar to fermentation barrels /obj/machinery/plumbing/fermenter/proc/ferment(atom/AM) - if(machine_stat & NOPOWER) + if(stat & NOPOWER) return if(reagents.holder_full()) return diff --git a/code/modules/plumbing/plumbers/filter.dm b/code/modules/plumbing/plumbers/filter.dm index 3058120b9d..b748c576e7 100644 --- a/code/modules/plumbing/plumbers/filter.dm +++ b/code/modules/plumbing/plumbers/filter.dm @@ -12,8 +12,8 @@ var/list/english_left = list() ///whitelist of chems but their name instead of path var/list/english_right = list() - var/ui_x = 320 - var/ui_y = 310 + ui_x = 320 + ui_y = 310 /obj/machinery/plumbing/filter/Initialize(mapload, bolt) diff --git a/code/modules/plumbing/plumbers/grinder_chemical.dm b/code/modules/plumbing/plumbers/grinder_chemical.dm index 4fa0d9e2cc..d080bd7c8f 100644 --- a/code/modules/plumbing/plumbers/grinder_chemical.dm +++ b/code/modules/plumbing/plumbers/grinder_chemical.dm @@ -7,23 +7,31 @@ rcd_cost = 30 rcd_delay = 30 buffer = 400 - var/eat_dir = SOUTH + var/eat_dir = NORTH /obj/machinery/plumbing/grinder_chemical/Initialize(mapload, bolt) . = ..() AddComponent(/datum/component/plumbing/simple_supply, bolt) -/obj/machinery/plumbing/grinder_chemical/can_be_rotated(mob/user, rotation_type) +/obj/machinery/plumbing/grinder_chemical/can_be_rotated(mob/user,rotation_type) if(anchored) to_chat(user, "It is fastened to the floor!") return FALSE - return TRUE + switch(eat_dir) + if(WEST) + eat_dir = NORTH + return TRUE + if(EAST) + eat_dir = SOUTH + return TRUE + if(NORTH) + eat_dir = EAST + return TRUE + if(SOUTH) + eat_dir = WEST + return TRUE -/obj/machinery/plumbing/grinder_chemical/setDir(newdir) - . = ..() - eat_dir = newdir - -/obj/machinery/plumbing/grinder_chemical/CanAllowThrough(atom/movable/AM) +/obj/machinery/plumbing/grinder_chemical/CanPass(atom/movable/AM) . = ..() if(!anchored) return @@ -36,7 +44,7 @@ grind(AM) /obj/machinery/plumbing/grinder_chemical/proc/grind(atom/AM) - if(machine_stat & NOPOWER) + if(stat & NOPOWER) return if(reagents.holder_full()) return @@ -53,6 +61,4 @@ return I.on_grind() reagents.add_reagent_list(I.grind_results) - if(I.reagents) - I.reagents.trans_to(src, I.reagents.total_volume, transfered_by = src) qdel(I) diff --git a/code/modules/plumbing/plumbers/pill_press.dm b/code/modules/plumbing/plumbers/pill_press.dm index 9c2505661c..56510fac87 100644 --- a/code/modules/plumbing/plumbers/pill_press.dm +++ b/code/modules/plumbing/plumbers/pill_press.dm @@ -1,28 +1,36 @@ ///We take a constant input of reagents, and produce a pill once a set volume is reached /obj/machinery/plumbing/pill_press - name = "pill press" - desc = "A press that presses pills." + name = "chemical press" + desc = "A press that makes pills, patches and bottles." icon_state = "pill_press" - ///the minimum size a pill can be - var/minimum_pill = 5 - ///the maximum size a pill can be - var/maximum_pill = 50 - ///the size of the pill - var/pill_size = 10 - ///pill name - var/pill_name = "factory pill" + ///maximum size of a pill + var/max_pill_volume = 50 + ///maximum size of a patch + var/max_patch_volume = 40 + ///maximum size of a bottle + var/max_bottle_volume = 30 + ///current operating product (pills or patches) + var/product = "pill" + ///the minimum size a pill or patch can be + var/min_volume = 5 + ///the maximum size a pill or patch can be + var/max_volume = 50 + ///selected size of the product + var/current_volume = 10 + ///prefix for the product name + var/product_name = "factory" ///the icon_state number for the pill. var/pill_number = RANDOM_PILL_STYLE ///list of id's and icons for the pill selection of the ui var/list/pill_styles - ///list of pills stored in the machine, so we dont have 610 pills on one tile - var/list/stored_pills = list() + ///list of products stored in the machine, so we dont have 610 pills on one tile + var/list/stored_products = list() ///max amount of pills allowed on our tile before we start storing them instead - var/max_floor_pills = 10 + var/max_floor_products = 50 //haha massive pill piles /obj/machinery/plumbing/pill_press/examine(mob/user) . = ..() - . += "The [name] currently has [stored_pills.len] stored. There needs to be less than [max_floor_pills] on the floor to continue dispensing." + . += "The [name] currently has [stored_products.len] stored. There needs to be less than [max_floor_products ] on the floor to continue dispensing." /obj/machinery/plumbing/pill_press/Initialize(mapload, bolt) . = ..() @@ -37,29 +45,41 @@ SL["htmltag"] = assets.icon_tag("pill[x]") pill_styles += list(SL) + /obj/machinery/plumbing/pill_press/process() if(stat & NOPOWER) return - if(reagents.total_volume >= pill_size) - var/obj/item/reagent_containers/pill/P = new(src) - reagents.trans_to(P, pill_size) - P.name = pill_name - stored_pills += P - if(pill_number == RANDOM_PILL_STYLE) - P.icon_state = "pill[rand(1,21)]" - else - P.icon_state = "pill[pill_number]" - if(P.icon_state == "pill4") //mirrored from chem masters - P.desc = "A tablet or capsule, but not just any, a red one, one taken by the ones not scared of knowledge, freedom, uncertainty and the brutal truths of reality." - if(stored_pills.len) + if(reagents.total_volume >= current_volume) + if (product == "pill") + var/obj/item/reagent_containers/pill/P = new(src) + reagents.trans_to(P, current_volume) + P.name = trim("[product_name] pill") + stored_products += P + if(pill_number == RANDOM_PILL_STYLE) + P.icon_state = "pill[rand(1,21)]" + else + P.icon_state = "pill[pill_number]" + if(P.icon_state == "pill4") //mirrored from chem masters + P.desc = "A tablet or capsule, but not just any, a red one, one taken by the ones not scared of knowledge, freedom, uncertainty and the brutal truths of reality." + else if (product == "patch") + var/obj/item/reagent_containers/pill/patch/P = new(src) + reagents.trans_to(P, current_volume) + P.name = trim("[product_name] patch") + stored_products += P + else if (product == "bottle") + var/obj/item/reagent_containers/glass/bottle/P = new(src) + reagents.trans_to(P, current_volume) + P.name = trim("[product_name] bottle") + stored_products += P + if(stored_products.len) var/pill_amount = 0 for(var/obj/item/reagent_containers/pill/P in loc) pill_amount++ - if(pill_amount >= max_floor_pills) //too much so just stop + if(pill_amount >= max_floor_products) //too much so just stop break - if(pill_amount < max_floor_pills) - var/atom/movable/AM = stored_pills[1] //AM because forceMove is all we need - stored_pills -= AM + if(pill_amount < max_floor_products) + var/atom/movable/AM = stored_products[1] //AM because forceMove is all we need + stored_products -= AM AM.forceMove(drop_location()) diff --git a/code/modules/plumbing/plumbers/pumps.dm b/code/modules/plumbing/plumbers/pumps.dm index aeec8c9994..c24e48098d 100644 --- a/code/modules/plumbing/plumbers/pumps.dm +++ b/code/modules/plumbing/plumbers/pumps.dm @@ -1,15 +1,17 @@ -///We pump liquids from activated(plungerated) geysers to a plumbing outlet. We need to be wired. -/obj/machinery/power/liquid_pump +///We pump liquids from activated(plungerated) geysers to a plumbing outlet. We don't need to be wired. +/obj/machinery/plumbing/liquid_pump name = "liquid pump" - desc = "Pump up those sweet liquids from under the surface." + desc = "Pump up those sweet liquids from under the surface. Uses thermal energy from geysers to power itself." //better than placing 200 cables, because it wasnt fun icon = 'icons/obj/plumbing/plumbers.dmi' icon_state = "pump" anchored = FALSE density = TRUE idle_power_usage = 10 active_power_usage = 1000 - ///Are we powered? - var/powered = FALSE + + rcd_cost = 30 + rcd_delay = 40 + ///units we pump per process (2 seconds) var/pump_power = 2 ///set to true if the loop couldnt find a geyser in process, so it remembers and stops checking every loop until moved. more accurate name would be absolutely_no_geyser_under_me_so_dont_try @@ -19,63 +21,42 @@ ///volume of our internal buffer var/volume = 200 -/obj/machinery/power/liquid_pump/Initialize() +/obj/machinery/plumbing/liquid_pump/Initialize(mapload, bolt) . = ..() - create_reagents(volume) - AddComponent(/datum/component/plumbing/simple_supply, TRUE) + AddComponent(/datum/component/plumbing/simple_supply, bolt) -/obj/machinery/power/liquid_pump/attackby(obj/item/W, mob/user, params) - if(!powered) - if(!anchored) - if(default_deconstruction_screwdriver(user, "[initial(icon_state)]_open", "[initial(icon_state)]",W)) - return - if(default_deconstruction_crowbar(W)) - return - return ..() - -/obj/machinery/power/liquid_pump/wrench_act(mob/living/user, obj/item/I) - ..() - default_unfasten_wrench(user, I) - return TRUE ///please note that the component has a hook in the parent call, wich handles activating and deactivating -/obj/machinery/power/liquid_pump/default_unfasten_wrench(mob/user, obj/item/I, time = 20) +/obj/machinery/plumbing/liquid_pump/default_unfasten_wrench(mob/user, obj/item/I, time = 20) . = ..() if(. == SUCCESSFUL_UNFASTEN) geyser = null update_icon() - powered = FALSE geyserless = FALSE //we switched state, so lets just set this back aswell -/obj/machinery/power/liquid_pump/process() - if(!anchored || panel_open) +/obj/machinery/plumbing/liquid_pump/process() + if(!anchored || panel_open || geyserless) return - if(!geyser && !geyserless) + + if(!geyser) for(var/obj/structure/geyser/G in loc.contents) geyser = G + update_icon() if(!geyser) //we didnt find one, abort - anchored = FALSE geyserless = TRUE visible_message("The [name] makes a sad beep!") playsound(src, 'sound/machines/buzz-sigh.ogg', 50) return - if(avail(active_power_usage)) - if(!powered) //we werent powered before this tick so update our sprite - powered = TRUE - update_icon() - add_load(active_power_usage) - pump() - else if(powered) //we were powered, but now we arent - powered = FALSE - update_icon() + pump() + ///pump up that sweet geyser nectar -/obj/machinery/power/liquid_pump/proc/pump() +/obj/machinery/plumbing/liquid_pump/proc/pump() if(!geyser || !geyser.reagents) return geyser.reagents.trans_to(src, pump_power) -/obj/machinery/power/liquid_pump/update_icon() - if(powered) +/obj/machinery/plumbing/liquid_pump/update_icon_state() + if(geyser) icon_state = initial(icon_state) + "-on" else if(panel_open) icon_state = initial(icon_state) + "-open" diff --git a/code/modules/plumbing/plumbers/synthesizer.dm b/code/modules/plumbing/plumbers/synthesizer.dm index 2d782e9d3e..c2bc3439ff 100644 --- a/code/modules/plumbing/plumbers/synthesizer.dm +++ b/code/modules/plumbing/plumbers/synthesizer.dm @@ -34,7 +34,7 @@ /datum/reagent/oxygen, /datum/reagent/phosphorus, /datum/reagent/potassium, - /datum/reagent/uranium/radium, + /datum/reagent/radium, /datum/reagent/silicon, /datum/reagent/silver, /datum/reagent/sodium, @@ -51,7 +51,7 @@ AddComponent(/datum/component/plumbing/simple_supply, bolt) /obj/machinery/plumbing/synthesizer/process() - if(machine_stat & NOPOWER || !reagent_id || !amount) + if(stat & NOPOWER || !reagent_id || !amount) return if(reagents.total_volume >= amount) //otherwise we get leftovers, and we need this to be precise return diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index cdda9d0469..f2add0bd4f 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -53,6 +53,14 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) var/metabolizing = FALSE var/chemical_flags // See fermi/readme.dm REAGENT_DEAD_PROCESS, REAGENT_DONOTSPLIT, REAGENT_ONLYINVERSE, REAGENT_ONMOBMERGE, REAGENT_INVISIBLE, REAGENT_FORCEONNEW, REAGENT_SNEAKYNAME var/value = REAGENT_VALUE_NONE //How much does it sell for in cargo? + var/datum/material/material //are we made of material? + +/datum/reagent/New() + . = ..() + + if(material) + material = getmaterialref(material) + /datum/reagent/Destroy() // This should only be called by the holder, so it's already handled clearing its references . = ..() diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index b9da8271ab..b173df3282 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -505,12 +505,13 @@ overdose_threshold = 30 pH = 2 value = REAGENT_VALUE_UNCOMMON + var/healing = 0.5 /datum/reagent/medicine/omnizine/on_mob_life(mob/living/carbon/M) - M.adjustToxLoss(-0.5*REM, 0) - M.adjustOxyLoss(-0.5*REM, 0) - M.adjustBruteLoss(-0.5*REM, 0) - M.adjustFireLoss(-0.5*REM, 0) + M.adjustToxLoss(-healing*REM, 0) + M.adjustOxyLoss(-healing*REM, 0) + M.adjustBruteLoss(-healing*REM, 0) + M.adjustFireLoss(-healing*REM, 0) ..() . = 1 @@ -522,6 +523,12 @@ ..() . = 1 +/datum/reagent/medicine/omnizine/protozine + name = "Protozine" + description = "A less environmentally friendly and somewhat weaker variant of omnizine." + color = "#d8c7b7" + healing = 0.2 + /datum/reagent/medicine/calomel name = "Calomel" description = "Quickly purges the body of all chemicals. Toxin damage is dealt if the patient is in good condition." @@ -1587,4 +1594,3 @@ if(prob(3)) to_chat(C, "[pick(GLOB.wisdoms)]") //give them a random wisdom ..() - diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 4c66b0e51e..b249310d7e 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -308,6 +308,13 @@ metabolization_rate = 45 * REAGENTS_METABOLISM . = 1 +/datum/reagent/water/hollowwater + name = "Hollow Water" + description = "An ubiquitous chemical substance that is composed of hydrogen and oxygen, but it looks kinda hollow." + color = "#88878777" + taste_description = "emptyiness" + + /datum/reagent/water/holywater name = "Holy Water" description = "Water blessed by some deity." @@ -943,6 +950,7 @@ color = "#1C1300" // rgb: 30, 20, 0 taste_description = "sour chalk" pH = 5 + material = /datum/material/diamond /datum/reagent/carbon/reaction_turf(turf/T, reac_volume) if(!isspaceturf(T)) @@ -1065,6 +1073,7 @@ pH = 6 overdose_threshold = 30 color = "#c2391d" + material = /datum/material/iron /datum/reagent/iron/on_mob_life(mob/living/carbon/C) if((HAS_TRAIT(C, TRAIT_NOMARROW))) @@ -1096,6 +1105,7 @@ reagent_state = SOLID color = "#F7C430" // rgb: 247, 196, 48 taste_description = "expensive metal" + material = /datum/material/gold /datum/reagent/silver name = "Silver" @@ -1103,6 +1113,7 @@ reagent_state = SOLID color = "#D0D0D0" // rgb: 208, 208, 208 taste_description = "expensive yet reasonable metal" + material = /datum/material/silver /datum/reagent/silver/reaction_mob(mob/living/M, method=TOUCH, reac_volume) if(M.has_bane(BANE_SILVER)) @@ -1116,6 +1127,7 @@ color = "#B8B8C0" // rgb: 184, 184, 192 taste_description = "the inside of a reactor" pH = 4 + material = /datum/material/uranium /datum/reagent/uranium/on_mob_life(mob/living/carbon/M) M.apply_effect(1/M.metabolism_efficiency,EFFECT_IRRADIATE,0) @@ -1137,6 +1149,7 @@ taste_description = "fizzling blue" pH = 12 value = REAGENT_VALUE_RARE + material = /datum/material/bluespace /datum/reagent/bluespace/reaction_mob(mob/living/M, method=TOUCH, reac_volume) if(method == TOUCH || method == VAPOR) @@ -1175,6 +1188,7 @@ color = "#A8A8A8" // rgb: 168, 168, 168 taste_mult = 0 pH = 10 + material = /datum/material/glass /datum/reagent/fuel name = "Welding fuel" @@ -2301,4 +2315,4 @@ description = "A crystaline polydextrose polymer, plants swear by this stuff." reagent_state = SOLID color = "#E6E6DA" - taste_mult = 0 \ No newline at end of file + taste_mult = 0 diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index c7add16fcf..704895b8d4 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -64,6 +64,7 @@ toxpwr = 3 pH = 4 value = REAGENT_VALUE_RARE //sheets are worth more + material = /datum/material/plasma /datum/reagent/toxin/plasma/on_mob_life(mob/living/carbon/C) if(holder.has_reagent(/datum/reagent/medicine/epinephrine)) diff --git a/code/modules/reagents/chemistry/recipes/medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm index e591daeb8e..663d232bb8 100644 --- a/code/modules/reagents/chemistry/recipes/medicine.dm +++ b/code/modules/reagents/chemistry/recipes/medicine.dm @@ -207,6 +207,12 @@ results = list(/datum/reagent/medicine/strange_reagent = 3) required_reagents = list(/datum/reagent/medicine/omnizine = 1, /datum/reagent/water/holywater = 1, /datum/reagent/toxin/mutagen = 1) +/datum/chemical_reaction/strange_reagent/alt + name = "Strange Reagent" + id = /datum/reagent/medicine/strange_reagent + results = list(/datum/reagent/medicine/strange_reagent = 2) + required_reagents = list(/datum/reagent/medicine/omnizine/protozine = 1, /datum/reagent/water/holywater = 1, /datum/reagent/toxin/mutagen = 1) + /datum/chemical_reaction/mannitol name = "Mannitol" id = /datum/reagent/medicine/mannitol @@ -333,4 +339,4 @@ /datum/chemical_reaction/medmesh/on_reaction(datum/reagents/holder, created_volume) var/location = get_turf(holder.my_atom) for(var/i = 1, i <= created_volume, i++) - new /obj/item/stack/medical/mesh/advanced(location) \ No newline at end of file + new /obj/item/stack/medical/mesh/advanced(location) diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index f32bc708d0..ddf38749c9 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -1,3 +1,34 @@ +/datum/chemical_reaction/metalgen + name = "metalgen" + id = /datum/reagent/metalgen + required_reagents = list(/datum/reagent/wittel = 1, /datum/reagent/bluespace = 1, /datum/reagent/toxin/mutagen = 1) + results = list(/datum/reagent/metalgen = 1) + +/datum/chemical_reaction/metalgen_imprint + name = "metalgen imprint" + id = /datum/reagent/metalgen + required_reagents = list(/datum/reagent/metalgen = 1, /datum/reagent/liquid_dark_matter = 1) + results = list(/datum/reagent/metalgen = 1) + +/datum/chemical_reaction/holywater + name = "Holy Water" + id = /datum/reagent/water/holywater + results = list(/datum/reagent/water/holywater = 1) + required_reagents = list(/datum/reagent/water/hollowwater = 1) + required_catalysts = list(/datum/reagent/water/holywater = 1) + +/datum/chemical_reaction/metalgen_imprint/on_reaction(datum/reagents/holder, created_volume) + var/datum/reagent/metalgen/MM = holder.get_reagent(/datum/reagent/metalgen) + for(var/datum/reagent/R in holder.reagent_list) + if(R.material && R.volume >= 40) + MM.data["material"] = R.material + holder.remove_reagent(R.type, 40) + +/datum/chemical_reaction/gravitum + name = "gravitum" + id = /datum/reagent/gravitum + required_reagents = list(/datum/reagent/wittel = 1, /datum/reagent/sorium = 10) + results = list(/datum/reagent/gravitum = 10) /datum/chemical_reaction/sterilizine name = "Sterilizine" @@ -711,7 +742,7 @@ /datum/chemical_reaction/slime_extractification/on_reaction(datum/reagents/holder, created_volume) var/location = get_turf(holder.my_atom) new /obj/item/slime_extract/grey(location) - + // Liquid Carpets /datum/chemical_reaction/carpet @@ -837,4 +868,4 @@ /datum/chemical_reaction/cellulose_carbonization results = list(/datum/reagent/carbon = 1) required_reagents = list(/datum/reagent/cellulose = 1) - required_temp = 512 \ No newline at end of file + required_temp = 512 diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 0598ebf9ab..7d4950cee5 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -1115,4 +1115,4 @@ construction_time = 150 build_path = /obj/item/construction/plumbing category = list("Misc","Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCEs + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE diff --git a/code/modules/vending/medical.dm b/code/modules/vending/medical.dm index fbd9a10bf2..2cdc7891c7 100644 --- a/code/modules/vending/medical.dm +++ b/code/modules/vending/medical.dm @@ -41,7 +41,8 @@ /obj/item/wrench/medical = 1, /obj/item/storage/belt/medolier/full = 2, /obj/item/gun/syringe/dart = 2, - /obj/item/storage/briefcase/medical = 2) + /obj/item/storage/briefcase/medical = 2, + /obj/item/plunger/reinforced = 2) armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) resistance_flags = FIRE_PROOF diff --git a/icons/obj/lavaland/terrain.dmi b/icons/obj/lavaland/terrain.dmi new file mode 100644 index 0000000000000000000000000000000000000000..4db51145eeef144f90f9f37f1065da9be3d46aa6 GIT binary patch literal 771 zcmV+e1N{7nP)QvN*L!iHkEOv#1y-V93RpR+N~V3Spxui%(3> zOu??!6eyaSlUNC2>Oi0c(EgnK`~rx|qSW%tycDda7U!21U^mqaVX6_#SRh0)S6RW; z&jsvr07@rD&a+2(_y7O_;Ymb6RA_=|4*pM5!Shep-&-+suPf$?DsA%qY@2qE;Ao4Ky*O!Doi zJtE@HcrO-9)yxZo5bNQ}neWpyP0XHcMdbRT;DbMvKUAlm%b%&y%wO;1JH`|^z00!H z{j59r<5^6b-ELQ86pi=y$KK2@C<=oxSX1td_g!x{-ei0o5GUG3S9m6hEb~KJMU;iP zJK5|VeF@3`mQn6;ZzY_mW=!}YkBL5z7et|Wm~451JTTZ>ZEU(1bCJ3_p$jO%9 zK^%Ix+6W!zxzz48Fv9`M8sZtvKu2UIW3 zuWF$GvE2jGejm_$xTC7n16b2E!-@JHApO)jiC%y6{sFW-z%Q1alYG>7JnjJtWgqw| zop5^Gj(G&rKJYAkci0DPnf8H&^!s)nuyuJKSStH~^@L0Nz^%FuSWmF-1GcaaAcPP? z2qA?2@9MvEUC%APdDZ$JFxmPZ(0M=J-5xMpn76J*yM3sa)0dhzs_NFY(cLv>^t^Lz z^2IJ3(3m``T02y?wf*s*O}3@~cFMhNwZ-V4_8UJGANA)W?9KoH002ovPDHLkV1kyO Bc`*P0 literal 0 HcmV?d00001 diff --git a/tgstation.dme b/tgstation.dme index f6e46312d0..2fea4eef9c 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1189,7 +1189,6 @@ #include "code\game\objects\structures\flora.dm" #include "code\game\objects\structures\fluff.dm" #include "code\game\objects\structures\fugitive_role_spawners.dm" -#include "code\game\objects\structures\geyser.dm" #include "code\game\objects\structures\ghost_role_spawners.dm" #include "code\game\objects\structures\girders.dm" #include "code\game\objects\structures\grille.dm" From 08547027621707de1b6792c3c1935eafa7b11b48 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Wed, 22 Jul 2020 12:45:42 +1000 Subject: [PATCH 06/42] whoops --- .../chemistry/reagents/other_reagents.dm | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index b249310d7e..e0632a9199 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -2220,6 +2220,66 @@ color = "#f7685e" metabolization_rate = REAGENTS_METABOLISM * 0.25 +/datum/reagent/wittel + name = "Wittel" + description = "An extremely rare metallic-white substance only found on demon-class planets." + color = "#FFFFFF" // rgb: 255, 255, 255 + taste_mult = 0 // oderless and tasteless + +/datum/reagent/metalgen + name = "Metalgen" + data = list("material"=null) + description = "A purple metal morphic liquid, said to impose it's metallic properties on whatever it touches." + color = "#b000aa" + taste_mult = 0 // oderless and tasteless + var/applied_material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR + var/minumum_material_amount = 100 + +/datum/reagent/metalgen/reaction_obj(obj/O, volume) + metal_morph(O) + return + +/datum/reagent/metalgen/reaction_turf(turf/T, volume) + metal_morph(T) + return + +///turn an object into a special material +/datum/reagent/metalgen/proc/metal_morph(atom/A) + var/metal_ref = data["material"] + if(!metal_ref) + return + var/metal_amount = 0 + + for(var/B in A.custom_materials) //list with what they're made of + metal_amount += A.custom_materials[B] + + if(!metal_amount) + metal_amount = minumum_material_amount //some stuff doesn't have materials at all. To still give them properties, we give them a material. Basically doesnt exist + + var/list/metal_dat = list() + metal_dat[metal_ref] = metal_amount //if we pass the list directly, byond turns metal_ref into "metal_ref" kjewrg8fwcyvf + + A.material_flags = applied_material_flags + A.set_custom_materials(metal_dat) + +/datum/reagent/gravitum + name = "Gravitum" + description = "A rare kind of null fluid, capable of temporalily removing all weight of whatever it touches." //i dont even + color = "#050096" // rgb: 5, 0, 150 + taste_mult = 0 // oderless and tasteless + metabolization_rate = 0.1 * REAGENTS_METABOLISM //20 times as long, so it's actually viable to use + var/time_multiplier = 1 MINUTES //1 minute per unit of gravitum on objects. Seems overpowered, but the whole thing is very niche + +/datum/reagent/gravitum/reaction_obj(obj/O, volume) + O.AddElement(/datum/element/forced_gravity, 0) + + addtimer(CALLBACK(O, .proc/RemoveElement, /datum/element/forced_gravity, 0), volume * time_multiplier) + +/datum/reagent/gravitum/on_mob_add(mob/living/L) + L.AddElement(/datum/element/forced_gravity, 0) //0 is the gravity, and in this case weightless + +/datum/reagent/gravitum/on_mob_end_metabolize(mob/living/L) + L.RemoveElement(/datum/element/forced_gravity, 0) //body bluids /datum/reagent/consumable/semen From 4affcfcdd718fb8772a4aa230c3b5fed99bf2b27 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Wed, 22 Jul 2020 22:14:49 +1000 Subject: [PATCH 07/42] Update tgstation.dme --- tgstation.dme | 1 + 1 file changed, 1 insertion(+) diff --git a/tgstation.dme b/tgstation.dme index 2fea4eef9c..017ee5bec8 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1262,6 +1262,7 @@ #include "code\game\objects\structures\crates_lockers\crates\secure.dm" #include "code\game\objects\structures\crates_lockers\crates\wooden.dm" #include "code\game\objects\structures\icemoon\cave_entrance.dm" +#include "code\game\objects\structures\lavaland\geyser.dm" #include "code\game\objects\structures\lavaland\necropolis_tendril.dm" #include "code\game\objects\structures\signs\_signs.dm" #include "code\game\objects\structures\signs\signs_departments.dm" From 6b23138b3171a4aa81465f67fb6a940ef625c180 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Wed, 22 Jul 2020 22:20:42 +1000 Subject: [PATCH 08/42] l --- code/modules/hydroponics/hydroponics.dm | 9 +++++--- code/modules/hydroponics/seeds.dm | 24 +++++++++++++++++++++ code/modules/plumbing/plumbers/autohydro.dm | 2 +- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index 14c510a2d2..efe965e8ea 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -914,11 +914,14 @@ harvest = 0 lastproduce = age if(istype(myseed, /obj/item/seeds/replicapod)) - to_chat(user, "You harvest from the [myseed.plantname].") + if(user)//runtimes + to_chat(user, "You harvest from the [myseed.plantname].") else if(myseed.getYield() <= 0) - to_chat(user, "You fail to harvest anything useful!") + if(user) + to_chat(user, "You fail to harvest anything useful!") else - to_chat(user, "You harvest [myseed.getYield()] items from the [myseed.plantname].") + if(user) + to_chat(user, "You harvest [myseed.getYield()] items from the [myseed.plantname].") if(!myseed.get_gene(/datum/plant_gene/trait/repeated_harvest)) qdel(myseed) myseed = null diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm index 5e49a32a23..69b36c58ce 100644 --- a/code/modules/hydroponics/seeds.dm +++ b/code/modules/hydroponics/seeds.dm @@ -190,6 +190,30 @@ obj/item/seeds/proc/is_gene_forbidden(typepath) parent.update_tray(user) return result +/obj/item/seeds/proc/harvest_userless() + var/obj/machinery/hydroponics/parent = loc //for ease of access + var/t_amount = 0 + var/list/result = list() + var/output_loc = parent.loc + while(t_amount < getYield()) + var/obj/item/reagent_containers/food/snacks/grown/t_prod = new product(output_loc, src) + if(parent.myseed.plantname != initial(parent.myseed.plantname)) + t_prod.name = lowertext(parent.myseed.plantname) + if(productdesc) + t_prod.desc = productdesc + t_prod.seed.name = parent.myseed.name + t_prod.seed.desc = parent.myseed.desc + t_prod.seed.plantname = parent.myseed.plantname + result.Add(t_prod) // User gets a consumable + if(!t_prod) + return + t_amount++ + product_name = parent.myseed.plantname + if(getYield() >= 1) + SSblackbox.record_feedback("tally", "food_harvested", getYield(), product_name) + parent.investigate_log("autmoatic harvest of [getYield()] of [src], with seed traits [english_list(genes)] and reagents_add [english_list(reagents_add)] and potency [potency].", INVESTIGATE_BOTANY) + parent.update_tray() + return result /obj/item/seeds/proc/prepare_result(var/obj/item/reagent_containers/food/snacks/grown/T) if(!T.reagents) diff --git a/code/modules/plumbing/plumbers/autohydro.dm b/code/modules/plumbing/plumbers/autohydro.dm index 4ea1d70aaf..7085548885 100644 --- a/code/modules/plumbing/plumbers/autohydro.dm +++ b/code/modules/plumbing/plumbers/autohydro.dm @@ -41,7 +41,7 @@ name = initial(name) desc = initial(desc) if(harvest) - myseed.harvest() + myseed.harvest_userless() harvest = 0 lastproduce = age if(!myseed.get_gene(/datum/plant_gene/trait/repeated_harvest)) From fee304fb19c94e295de7450cdcfa5080da6833a1 Mon Sep 17 00:00:00 2001 From: lolman360 <22850904+lolman360@users.noreply.github.com> Date: Sun, 9 Aug 2020 22:55:10 +1000 Subject: [PATCH 09/42] Update medical_designs.dm --- code/modules/research/designs/medical_designs.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 7d4950cee5..fbe805af41 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -1069,7 +1069,7 @@ build_type = PROTOLATHE materials = list(MAT_METAL = 1000, MAT_GLASS = 500) construction_time = 75 - build_path = /obj/machinery/power/liquid_pump + build_path = /obj/machinery/liquid_pump category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE From 6c57dce54eb409848324394ca33e2e75a61d24a5 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Sun, 9 Aug 2020 23:52:21 +1000 Subject: [PATCH 10/42] WX --- code/game/objects/structures/lavaland/geyser.dm | 2 +- code/game/turfs/simulated/floor/plating/asteroid.dm | 5 ++++- code/modules/hydroponics/seeds.dm | 1 + code/modules/reagents/chemistry/reagents.dm | 2 +- code/modules/reagents/chemistry/reagents/other_reagents.dm | 2 +- code/modules/research/designs/medical_designs.dm | 2 +- 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/code/game/objects/structures/lavaland/geyser.dm b/code/game/objects/structures/lavaland/geyser.dm index 4f6256e9c4..2379513d03 100644 --- a/code/game/objects/structures/lavaland/geyser.dm +++ b/code/game/objects/structures/lavaland/geyser.dm @@ -9,7 +9,7 @@ var/erupting_state = null //set to null to get it greyscaled from "[icon_state]_soup". Not very usable with the whole random thing, but more types can be added if you change the spawn prob var/activated = FALSE //whether we are active and generating chems - var/reagent_id = /datum/reagent/fuel/oil + var/reagent_id = /datum/reagent/oil var/potency = 2 //how much reagents we add every process (2 seconds) var/max_volume = 500 var/start_volume = 50 diff --git a/code/game/turfs/simulated/floor/plating/asteroid.dm b/code/game/turfs/simulated/floor/plating/asteroid.dm index f862a314a7..6e7dbaf904 100644 --- a/code/game/turfs/simulated/floor/plating/asteroid.dm +++ b/code/game/turfs/simulated/floor/plating/asteroid.dm @@ -149,6 +149,8 @@ var/list/megafauna_spawn_list /// Flora that can spawn in the tunnel, weighted list var/list/flora_spawn_list + //terrain to spawn weighted list + var/list/terrain_spawn_list /// Turf type to choose when spawning in tunnel at 1% chance, weighted list var/list/choose_turf_type /// if the tunnel should keep being created @@ -230,7 +232,8 @@ megafauna_spawn_list = list(/mob/living/simple_animal/hostile/megafauna/dragon = 4, /mob/living/simple_animal/hostile/megafauna/colossus = 2, /mob/living/simple_animal/hostile/megafauna/bubblegum = SPAWN_BUBBLEGUM) if (!flora_spawn_list) flora_spawn_list = list(/obj/structure/flora/ash/leaf_shroom = 2 , /obj/structure/flora/ash/cap_shroom = 2 , /obj/structure/flora/ash/stem_shroom = 2 , /obj/structure/flora/ash/cacti = 1, /obj/structure/flora/ash/tall_shroom = 2) - + if(!terrain_spawn_list) + terrain_spawn_list = list(/obj/structure/geyser/random = 1) . = ..() if(!has_data) produce_tunnel_from_data() diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm index 69b36c58ce..c7314bf180 100644 --- a/code/modules/hydroponics/seeds.dm +++ b/code/modules/hydroponics/seeds.dm @@ -195,6 +195,7 @@ obj/item/seeds/proc/is_gene_forbidden(typepath) var/t_amount = 0 var/list/result = list() var/output_loc = parent.loc + var/product_name while(t_amount < getYield()) var/obj/item/reagent_containers/food/snacks/grown/t_prod = new product(output_loc, src) if(parent.myseed.plantname != initial(parent.myseed.plantname)) diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index f2add0bd4f..672127cb11 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -59,7 +59,7 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) . = ..() if(material) - material = getmaterialref(material) + material = SSmaterials.GetMaterialRef(material) /datum/reagent/Destroy() // This should only be called by the holder, so it's already handled clearing its references diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index ea9dbd5a9d..75e65a14d3 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -2273,7 +2273,7 @@ /datum/reagent/gravitum/reaction_obj(obj/O, volume) O.AddElement(/datum/element/forced_gravity, 0) - addtimer(CALLBACK(O, .proc/RemoveElement, /datum/element/forced_gravity, 0), volume * time_multiplier) + addtimer(CALLBACK(O, .proc/_RemoveElement, /datum/element/forced_gravity, 0), volume * time_multiplier) /datum/reagent/gravitum/on_mob_add(mob/living/L) L.AddElement(/datum/element/forced_gravity, 0) //0 is the gravity, and in this case weightless diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index fbe805af41..04ef917b00 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -1069,7 +1069,7 @@ build_type = PROTOLATHE materials = list(MAT_METAL = 1000, MAT_GLASS = 500) construction_time = 75 - build_path = /obj/machinery/liquid_pump + build_path = /obj/machinery/plumbing/liquid_pump category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE From cbe783023d54d345fb3916f8d0814cbbef35945a Mon Sep 17 00:00:00 2001 From: lolman360 <22850904+lolman360@users.noreply.github.com> Date: Sun, 9 Aug 2020 23:55:06 +1000 Subject: [PATCH 11/42] Update autohydro.dm --- code/modules/plumbing/plumbers/autohydro.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/plumbing/plumbers/autohydro.dm b/code/modules/plumbing/plumbers/autohydro.dm index 7085548885..0f0e3008f0 100644 --- a/code/modules/plumbing/plumbers/autohydro.dm +++ b/code/modules/plumbing/plumbers/autohydro.dm @@ -33,7 +33,7 @@ if(reagents) applyChemicals(reagents) reagents.clear_reagents() - if(dead) + if(dead) dead = 0 qdel(myseed) myseed = null From a44afad8497fc69af95a11a2779618cccf96e5de Mon Sep 17 00:00:00 2001 From: lolman360 <22850904+lolman360@users.noreply.github.com> Date: Mon, 10 Aug 2020 08:26:06 +1000 Subject: [PATCH 12/42] Update autohydro.dm --- code/modules/plumbing/plumbers/autohydro.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/plumbing/plumbers/autohydro.dm b/code/modules/plumbing/plumbers/autohydro.dm index 0f0e3008f0..5157000754 100644 --- a/code/modules/plumbing/plumbers/autohydro.dm +++ b/code/modules/plumbing/plumbers/autohydro.dm @@ -33,7 +33,7 @@ if(reagents) applyChemicals(reagents) reagents.clear_reagents() - if(dead) + if(dead) dead = 0 qdel(myseed) myseed = null From 6c4c3e42553152a8c92c3ef44645f2dd83a7d6bd Mon Sep 17 00:00:00 2001 From: lolman360 Date: Mon, 10 Aug 2020 09:47:55 +1000 Subject: [PATCH 13/42] lint! --- code/modules/reagents/chemistry/machinery/chem_master.dm | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index 207325e1b3..32ac7cecba 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -1,5 +1,3 @@ -#define PILL_STYLE_COUNT 22 //Update this if you add more pill icons or you die -#define RANDOM_PILL_STYLE 22 //Dont change this one though /obj/machinery/chem_master name = "ChemMaster 3000" From d9c198825b7b0b0bec07a59ddbb6df607c6815be Mon Sep 17 00:00:00 2001 From: lolman360 Date: Mon, 10 Aug 2020 10:29:17 +1000 Subject: [PATCH 14/42] linter 2: --- code/datums/ductnet.dm | 4 ++-- code/modules/plumbing/plumbers/bottler.dm | 6 +++--- code/modules/plumbing/plumbers/grinder_chemical.dm | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/code/datums/ductnet.dm b/code/datums/ductnet.dm index f81e9e608c..3ab936f6f4 100644 --- a/code/datums/ductnet.dm +++ b/code/datums/ductnet.dm @@ -6,7 +6,7 @@ var/capacity /datum/ductnet/proc/add_duct(obj/machinery/duct/D) - if(!D || D in ducts) + if(!D || (D in ducts)) return ducts += D D.duct = src @@ -57,4 +57,4 @@ var/obj/machinery/duct/D = A D.duct = null if(delete) //I don't want code to run with qdeleted objects because that can never be good, so keep this in-case the ductnet has some business left to attend to before commiting suicide - qdel(src) + qdel(src) diff --git a/code/modules/plumbing/plumbers/bottler.dm b/code/modules/plumbing/plumbers/bottler.dm index 57233a5873..396c7cac22 100644 --- a/code/modules/plumbing/plumbers/bottler.dm +++ b/code/modules/plumbing/plumbers/bottler.dm @@ -65,15 +65,15 @@ var/obj/item/reagent_containers/B = AM ///see if it would overflow else inject if((B.reagents.total_volume + wanted_amount) <= B.reagents.maximum_volume) - reagents.trans_to(B, wanted_amount, transfered_by = src) + reagents.trans_to(B, wanted_amount) B.forceMove(goodspot) return ///glass was full so we move it away AM.forceMove(badspot) if(istype(AM, /obj/item/slime_extract)) ///slime extracts need inject AM.forceMove(goodspot) - reagents.trans_to(AM, wanted_amount, transfered_by = src, method = INJECT) + reagents.trans_to(AM, wanted_amount) return if(istype(AM, /obj/item/slimecross/industrial)) ///no need to move slimecross industrial things - reagents.trans_to(AM, wanted_amount, transfered_by = src, method = INJECT) + reagents.trans_to(AM, wanted_amount) return diff --git a/code/modules/plumbing/plumbers/grinder_chemical.dm b/code/modules/plumbing/plumbers/grinder_chemical.dm index d080bd7c8f..f39c79f906 100644 --- a/code/modules/plumbing/plumbers/grinder_chemical.dm +++ b/code/modules/plumbing/plumbers/grinder_chemical.dm @@ -56,7 +56,7 @@ I.on_juice() reagents.add_reagent_list(I.juice_results) if(I.reagents) - I.reagents.trans_to(src, I.reagents.total_volume, transfered_by = src) + I.reagents.trans_to(src, I.reagents.total_volume) qdel(I) return I.on_grind() From 900b0b068aa97bf7e95dac7615147802d38b2316 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Mon, 10 Aug 2020 10:38:53 +1000 Subject: [PATCH 15/42] linter take 3 love u putnam --- code/modules/plumbing/ducts.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/plumbing/ducts.dm b/code/modules/plumbing/ducts.dm index 735526511f..c05f3f93f0 100644 --- a/code/modules/plumbing/ducts.dm +++ b/code/modules/plumbing/ducts.dm @@ -42,8 +42,8 @@ All the important duct code: active = FALSE anchored = FALSE else if(!can_anchor()) - CRASH("Overlapping ducts detected") qdel(src) + CRASH("Overlapping ducts detected") if(force_connects) connects = force_connects //skip change_connects() because we're still initializing and we need to set our connects at one point if(!lock_layers) From c160d73265508debe38af90c6b8fb840f45c3248 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Mon, 10 Aug 2020 10:40:30 +1000 Subject: [PATCH 16/42] ee --- code/modules/plumbing/ducts.dm | 1 - 1 file changed, 1 deletion(-) diff --git a/code/modules/plumbing/ducts.dm b/code/modules/plumbing/ducts.dm index c05f3f93f0..6cc09c7d15 100644 --- a/code/modules/plumbing/ducts.dm +++ b/code/modules/plumbing/ducts.dm @@ -43,7 +43,6 @@ All the important duct code: anchored = FALSE else if(!can_anchor()) qdel(src) - CRASH("Overlapping ducts detected") if(force_connects) connects = force_connects //skip change_connects() because we're still initializing and we need to set our connects at one point if(!lock_layers) From 1b35e02554688072dfb8049d58318b7ca71d2c5c Mon Sep 17 00:00:00 2001 From: lolman360 Date: Mon, 10 Aug 2020 11:21:24 +1000 Subject: [PATCH 17/42] eee --- code/game/objects/items/RCD.dm | 1 + code/game/objects/items/RPD.dm | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm index 3ec0da70c5..c89e318714 100644 --- a/code/game/objects/items/RCD.dm +++ b/code/game/objects/items/RCD.dm @@ -860,6 +860,7 @@ RLD /obj/item/construction/plumbing name = "Plumbing Constructor" desc = "An expertly modified RCD outfitted to construct plumbing machinery. Reload with compressed matter cartridges." + icon = 'icons/obj/tools.dmi' icon_state = "arcd" item_state = "oldrcd" has_ammobar = FALSE diff --git a/code/game/objects/items/RPD.dm b/code/game/objects/items/RPD.dm index 79f03f0005..70e4441c66 100644 --- a/code/game/objects/items/RPD.dm +++ b/code/game/objects/items/RPD.dm @@ -522,7 +522,7 @@ GLOBAL_LIST_INIT(fluid_duct_recipes, list( /obj/item/pipe_dispenser/proc/activate() playsound(get_turf(src), 'sound/items/deconstruct.ogg', 50, 1) - +/* unneeded, you can craft ducts from plastic /obj/item/pipe_dispenser/plumbing name = "Plumberinator" desc = "A crude device to rapidly plumb things." @@ -540,7 +540,7 @@ GLOBAL_LIST_INIT(fluid_duct_recipes, list( recipe = first_plumbing - +*/ #undef ATMOS_CATEGORY #undef DISPOSALS_CATEGORY From 957b3948bd3d53fff6fe9c4e2dfdb8e1de6863fe Mon Sep 17 00:00:00 2001 From: lolman360 Date: Mon, 10 Aug 2020 20:26:32 +1000 Subject: [PATCH 18/42] aare --- icons/obj/watercloset.dmi | Bin 16012 -> 24009 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/icons/obj/watercloset.dmi b/icons/obj/watercloset.dmi index 94ea415ed7cf406fb49befddd17426bccf1fd3c2..2b641768783821717ab7969abdc117e27e7b7bf9 100644 GIT binary patch literal 24009 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 literal 16012 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 From c18f0defd8ae7138e4966ccbc514b431223d193a Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 11 Aug 2020 16:44:08 +1000 Subject: [PATCH 19/42] medi8el --- .../plumbing/plumbers/medipenrefill.dm | 96 ++++++++++++++++++ icons/obj/machines/medipen_refiller.dmi | Bin 0 -> 640 bytes 2 files changed, 96 insertions(+) create mode 100644 code/modules/plumbing/plumbers/medipenrefill.dm create mode 100644 icons/obj/machines/medipen_refiller.dmi diff --git a/code/modules/plumbing/plumbers/medipenrefill.dm b/code/modules/plumbing/plumbers/medipenrefill.dm new file mode 100644 index 0000000000..4b11076e04 --- /dev/null +++ b/code/modules/plumbing/plumbers/medipenrefill.dm @@ -0,0 +1,96 @@ +/obj/machinery/medipen_refiller + name = "Medipen Refiller" + desc = "A machine that refills used medipens with chemicals." + icon = 'icons/obj/machines/medipen_refiller.dmi' + icon_state = "medipen_refiller" + density = TRUE + circuit = /obj/item/circuitboard/machine/medipen_refiller + idle_power_usage = 100 + /// list of medipen subtypes it can refill + var/list/allowed = list(/obj/item/reagent_containers/hypospray/medipen = /datum/reagent/medicine/epinephrine, + /obj/item/reagent_containers/hypospray/medipen/atropine = /datum/reagent/medicine/atropine, + /obj/item/reagent_containers/hypospray/medipen/salbutamol = /datum/reagent/medicine/salbutamol, + /obj/item/reagent_containers/hypospray/medipen/oxandrolone = /datum/reagent/medicine/oxandrolone, + /obj/item/reagent_containers/hypospray/medipen/salacid = /datum/reagent/medicine/sal_acid, + /obj/item/reagent_containers/hypospray/medipen/penacid = /datum/reagent/medicine/pen_acid) + /// var to prevent glitches in the animation + var/busy = FALSE + +/obj/machinery/medipen_refiller/Initialize() + . = ..() + create_reagents(100, TRANSPARENT) + for(var/obj/item/stock_parts/matter_bin/B in component_parts) + reagents.maximum_volume += 100 * B.rating + AddComponent(/datum/component/plumbing/simple_demand) + + +/obj/machinery/medipen_refiller/RefreshParts() + var/new_volume = 100 + for(var/obj/item/stock_parts/matter_bin/B in component_parts) + new_volume += 100 * B.rating + if(!reagents) + create_reagents(new_volume, TRANSPARENT) + reagents.maximum_volume = new_volume + return TRUE + +/// handles the messages and animation, calls refill to end the animation +/obj/machinery/medipen_refiller/attackby(obj/item/I, mob/user, params) + if(busy) + to_chat(user, "The machine is busy.") + return + if(istype(I, /obj/item/reagent_containers) && I.is_open_container()) + var/obj/item/reagent_containers/RC = I + var/units = RC.reagents.trans_to(src, RC.amount_per_transfer_from_this, transfered_by = user) + if(units) + to_chat(user, "You transfer [units] units of the solution to the [name].") + return + else + to_chat(user, "The [name] is full.") + return + if(istype(I, /obj/item/reagent_containers/hypospray/medipen)) + var/obj/item/reagent_containers/hypospray/medipen/P = I + if(!(LAZYFIND(allowed, P.type))) + to_chat(user, "Error! Unknown schematics.") + return + if(P.reagents?.reagent_list.len) + to_chat(user, "The medipen is already filled.") + return + if(reagents.has_reagent(allowed[P.type], 10)) + busy = TRUE + add_overlay("active") + addtimer(CALLBACK(src, .proc/refill, P, user), 20) + qdel(P) + return + to_chat(user, "There aren't enough reagents to finish this operation.") + return + ..() + +/obj/machinery/medipen_refiller/plunger_act(obj/item/plunger/P, mob/living/user, reinforced) + to_chat(user, "You start furiously plunging [name].") + if(do_after(user, 30, target = src)) + to_chat(user, "You finish plunging the [name].") + reagents.expose(get_turf(src), TOUCH) + reagents.clear_reagents() + +/obj/machinery/medipen_refiller/wrench_act(mob/living/user, obj/item/I) + ..() + default_unfasten_wrench(user, I) + return TRUE + +/obj/machinery/medipen_refiller/crowbar_act(mob/user, obj/item/I) + ..() + default_deconstruction_crowbar(I) + return TRUE + +/obj/machinery/medipen_refiller/screwdriver_act(mob/living/user, obj/item/I) + . = ..() + if(!.) + return default_deconstruction_screwdriver(user, "medipen_refiller_open", "medipen_refiller", I) + +/// refills the medipen +/obj/machinery/medipen_refiller/proc/refill(obj/item/reagent_containers/hypospray/medipen/P, mob/user) + new P.type(loc) + reagents.remove_reagent(allowed[P.type], 10) + cut_overlays() + busy = FALSE + to_chat(user, "Medipen refilled.") diff --git a/icons/obj/machines/medipen_refiller.dmi b/icons/obj/machines/medipen_refiller.dmi new file mode 100644 index 0000000000000000000000000000000000000000..300d218d2d75da8958ce0d57dda1dc588fa30d6b GIT binary patch literal 640 zcmV-`0)PF9P)Mv0GynfHGfL+AX#fBK0d!JMQvg8b*k%9#0HAtQSad{Xb7OL8aCB*JZU6vy zoKseCa&`CgQ*iP1H7zqIC$&h4i!&v&s2C_^$ixjKRt@3mscg1`_| zNrITRKM-Kij Date: Tue, 11 Aug 2020 16:44:45 +1000 Subject: [PATCH 20/42] THESUN --- tgstation.dme | 1 + 1 file changed, 1 insertion(+) diff --git a/tgstation.dme b/tgstation.dme index 77c4dd3c7e..5b17db08f4 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2856,6 +2856,7 @@ #include "code\modules\plumbing\plumbers\fermenter.dm" #include "code\modules\plumbing\plumbers\filter.dm" #include "code\modules\plumbing\plumbers\grinder_chemical.dm" +#include "code\modules\plumbing\plumbers\medipenrefill.dm" #include "code\modules\plumbing\plumbers\pill_press.dm" #include "code\modules\plumbing\plumbers\pumps.dm" #include "code\modules\plumbing\plumbers\reaction_chamber.dm" From f977ac7180a435c27c2359e874ea725bc6a37fce Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 11 Aug 2020 17:08:21 +1000 Subject: [PATCH 21/42] e e e ee ee --- .../objects/items/circuitboards/machine_circuitboards.dm | 7 +++++++ code/modules/plumbing/plumbers/medipenrefill.dm | 9 ++++----- .../designs/machine_desings/machine_designs_medical.dm | 8 ++++++++ code/modules/research/techweb/nodes/medical_nodes.dm | 8 ++++---- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm index 8a8aba50a3..37d19783b5 100644 --- a/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -48,6 +48,13 @@ /obj/item/stack/cable_coil = 5, /obj/item/stack/sheet/glass = 1) +/obj/item/circuitboard/machine/medipen_refiller + name = "Medipen Refiller (Machine Board)" + icon_state = "medical" + build_path = /obj/machinery/medipen_refiller + req_components = list( + /obj/item/stock_parts/matter_bin = 1) + /obj/item/circuitboard/machine/clonepod name = "Clone Pod (Machine Board)" build_path = /obj/machinery/clonepod diff --git a/code/modules/plumbing/plumbers/medipenrefill.dm b/code/modules/plumbing/plumbers/medipenrefill.dm index 4b11076e04..79522fabf6 100644 --- a/code/modules/plumbing/plumbers/medipenrefill.dm +++ b/code/modules/plumbing/plumbers/medipenrefill.dm @@ -8,11 +8,10 @@ idle_power_usage = 100 /// list of medipen subtypes it can refill var/list/allowed = list(/obj/item/reagent_containers/hypospray/medipen = /datum/reagent/medicine/epinephrine, - /obj/item/reagent_containers/hypospray/medipen/atropine = /datum/reagent/medicine/atropine, - /obj/item/reagent_containers/hypospray/medipen/salbutamol = /datum/reagent/medicine/salbutamol, - /obj/item/reagent_containers/hypospray/medipen/oxandrolone = /datum/reagent/medicine/oxandrolone, - /obj/item/reagent_containers/hypospray/medipen/salacid = /datum/reagent/medicine/sal_acid, - /obj/item/reagent_containers/hypospray/medipen/penacid = /datum/reagent/medicine/pen_acid) + /obj/item/reagent_containers/hypospray/medipen/ekit = /datum/reagent/medicine/epinephrine, + /obj/item/reagent_containers/hypospray/medipen/firelocker = /datum/reagent/medicine/oxandrolone, + /obj/item/reagent_containers/hypospray/medipen/stimpack = /datum/reagent/medicine/ephedrine, + /obj/item/reagent_containers/hypospray/medipen/blood_loss = /datum/reagent/medicine/coagulant/weak) /// var to prevent glitches in the animation var/busy = FALSE diff --git a/code/modules/research/designs/machine_desings/machine_designs_medical.dm b/code/modules/research/designs/machine_desings/machine_designs_medical.dm index 329fb7bf6e..84a3ed10d5 100644 --- a/code/modules/research/designs/machine_desings/machine_designs_medical.dm +++ b/code/modules/research/designs/machine_desings/machine_designs_medical.dm @@ -105,3 +105,11 @@ build_path = /obj/item/circuitboard/machine/bloodbankgen departmental_flags = DEPARTMENTAL_FLAG_MEDICAL category = list ("Medical Machinery") + +/datum/design/board/medipen_refiller + name = "Machine Design (Medipen Refiller)" + desc = "The circuit board for a Medipen Refiller." + id = "medipen_refiller" + build_path = /obj/item/circuitboard/machine/medipen_refiller + category = list ("Medical Machinery") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL diff --git a/code/modules/research/techweb/nodes/medical_nodes.dm b/code/modules/research/techweb/nodes/medical_nodes.dm index 02562f941e..150e420c09 100644 --- a/code/modules/research/techweb/nodes/medical_nodes.dm +++ b/code/modules/research/techweb/nodes/medical_nodes.dm @@ -29,17 +29,17 @@ display_name = "Reagent Plumbing Technology" description = "Plastic tubes, and machinery used for manipulating things in them." prereq_ids = list("base") - design_ids = list("acclimator", "disposer", "plumb_filter", "plumb_synth", "plumb_grinder", "reaction_chamber", "duct_print", "plumb_splitter", "pill_press", "plumb_pump", "plumb_in", "plumb_out", "plumb_tank") - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + design_ids = list("acclimator", "disposer", "plumb_filter", "plumb_synth", "plumb_grinder", "reaction_chamber", "duct_print", "plumb_splitter", "pill_press", "plumb_pump", "plumb_in", "plumb_out", "plumb_tank", "medipen_refiller") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000) /datum/techweb_node/advplumbing id = "advplumbing" display_name = "Advanced Plumbing Technology" description = "Plumbing RCD." - prereq_ids = list("plumbing", "adv_engi", "adv_biotech") + prereq_ids = list("plumbing", "adv_engi") design_ids = list("plumb_rcd", "autohydrotray") - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10000) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) //////////////////////Cybernetics///////////////////// From 6e2bb798342d0f387fbba2f80dcd90dfb5e4c85f Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 11 Aug 2020 17:32:44 +1000 Subject: [PATCH 22/42] Update medipenrefill.dm --- code/modules/plumbing/plumbers/medipenrefill.dm | 1 - 1 file changed, 1 deletion(-) diff --git a/code/modules/plumbing/plumbers/medipenrefill.dm b/code/modules/plumbing/plumbers/medipenrefill.dm index 79522fabf6..195d866a38 100644 --- a/code/modules/plumbing/plumbers/medipenrefill.dm +++ b/code/modules/plumbing/plumbers/medipenrefill.dm @@ -68,7 +68,6 @@ to_chat(user, "You start furiously plunging [name].") if(do_after(user, 30, target = src)) to_chat(user, "You finish plunging the [name].") - reagents.expose(get_turf(src), TOUCH) reagents.clear_reagents() /obj/machinery/medipen_refiller/wrench_act(mob/living/user, obj/item/I) From 6ec9a3c4bddc091720463c5b31b9b5f679fbb6f0 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 11 Aug 2020 17:38:16 +1000 Subject: [PATCH 23/42] craftable plunger --- .../components/crafting/recipes/recipes_misc.dm | 11 ++++++++++- code/game/objects/structures/lavaland/geyser.dm | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/code/datums/components/crafting/recipes/recipes_misc.dm b/code/datums/components/crafting/recipes/recipes_misc.dm index 214c1b1427..a1111dfb8b 100644 --- a/code/datums/components/crafting/recipes/recipes_misc.dm +++ b/code/datums/components/crafting/recipes/recipes_misc.dm @@ -2,6 +2,15 @@ //Large Objects// ///////////////// +/datum/crafting_recipe/plunger + name = "Plunger" + result = /obj/item/plunger + time = 1 + reqs = list(/obj/item/stack/sheet/mineral/plastic = 1, + /obj/item/stack/sheet/mineral/wood = 1) + category = CAT_MISC + subcategory = CAT_TOOL + /datum/crafting_recipe/showercurtain name = "Shower Curtains" reqs = list(/obj/item/stack/sheet/cloth = 2, @@ -324,7 +333,7 @@ result = /obj/item/toy/sword/cx subcategory = CAT_MISCELLANEOUS category = CAT_MISC - + /datum/crafting_recipe/catgirlplushie name = "Catgirl Plushie" reqs = list(/obj/item/toy/plush/hairball = 3) diff --git a/code/game/objects/structures/lavaland/geyser.dm b/code/game/objects/structures/lavaland/geyser.dm index 2379513d03..263b47aab2 100644 --- a/code/game/objects/structures/lavaland/geyser.dm +++ b/code/game/objects/structures/lavaland/geyser.dm @@ -77,10 +77,10 @@ /obj/item/plunger/reinforced name = "reinforced plunger" - desc = "It's an M. 7 Reinforced Plunger© for heavy duty plunging." + desc = "It's an M. 7 Reinforced Plunger� for heavy duty plunging." icon_state = "reinforced_plunger" reinforced = TRUE plunge_mod = 0.8 - custom_premium_price = 1200 + custom_premium_price = 600 From 740b4f3ecf2cbba62c7b54e758b457af6db98bd4 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 11 Aug 2020 17:50:30 +1000 Subject: [PATCH 24/42] WHOOPS --- code/datums/components/crafting/recipes/recipes_misc.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/datums/components/crafting/recipes/recipes_misc.dm b/code/datums/components/crafting/recipes/recipes_misc.dm index a1111dfb8b..bab03fb55e 100644 --- a/code/datums/components/crafting/recipes/recipes_misc.dm +++ b/code/datums/components/crafting/recipes/recipes_misc.dm @@ -6,7 +6,7 @@ name = "Plunger" result = /obj/item/plunger time = 1 - reqs = list(/obj/item/stack/sheet/mineral/plastic = 1, + reqs = list(/obj/item/stack/sheet/plastic = 1, /obj/item/stack/sheet/mineral/wood = 1) category = CAT_MISC subcategory = CAT_TOOL From c69972925004356d6e7af4e5be9386e7a3f7d22c Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 11 Aug 2020 17:53:51 +1000 Subject: [PATCH 25/42] lint --- code/modules/plumbing/plumbers/medipenrefill.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/plumbing/plumbers/medipenrefill.dm b/code/modules/plumbing/plumbers/medipenrefill.dm index 195d866a38..fb7553a4d5 100644 --- a/code/modules/plumbing/plumbers/medipenrefill.dm +++ b/code/modules/plumbing/plumbers/medipenrefill.dm @@ -39,7 +39,7 @@ return if(istype(I, /obj/item/reagent_containers) && I.is_open_container()) var/obj/item/reagent_containers/RC = I - var/units = RC.reagents.trans_to(src, RC.amount_per_transfer_from_this, transfered_by = user) + var/units = RC.reagents.trans_to(src, RC.amount_per_transfer_from_this) if(units) to_chat(user, "You transfer [units] units of the solution to the [name].") return From f10053fe963669af99f98f05e406698ceb8ccbfb Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 11 Aug 2020 18:03:59 +1000 Subject: [PATCH 26/42] suture --- .../reagents/chemistry/recipes/medicine.dm | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/code/modules/reagents/chemistry/recipes/medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm index 9b303cd5c0..9e0c78d2e6 100644 --- a/code/modules/reagents/chemistry/recipes/medicine.dm +++ b/code/modules/reagents/chemistry/recipes/medicine.dm @@ -352,3 +352,19 @@ var/location = get_turf(holder.my_atom) for(var/i = 1, i <= created_volume, i++) new /obj/item/stack/medical/mesh/advanced(location) + +/datum/chemical_reaction/suture + required_reagents = list(/datum/reagent/cellulose = 2, /datum/reagent/medicine/styptic_powder = 2) + +/datum/chemical_reaction/suture/on_reaction(datum/reagents/holder, created_volume) + var/location = get_turf(holder.my_atom) + for(var/i = 1, i <= created_volume, i++) + new /obj/item/stack/medical/suture/(location) + +/datum/chemical_reaction/mesh + required_reagents = list(/datum/reagent/cellulose = 2, /datum/reagent/medicine/silver_sulfadiazine = 2) + +/datum/chemical_reaction/mesh/on_reaction(datum/reagents/holder, created_volume) + var/location = get_turf(holder.my_atom) + for(var/i = 1, i <= created_volume, i++) + new /obj/item/stack/medical/mesh/(location) From 49948e77a41180fa9de7291fc7eaf3e0f0e193e3 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 11 Aug 2020 18:08:40 +1000 Subject: [PATCH 27/42] SELLYOULOSE --- code/game/objects/items/stacks/medical.dm | 1 + code/game/objects/items/stacks/sheets/sheet_types.dm | 3 +++ 2 files changed, 4 insertions(+) diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 11df83230c..1840921be4 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -128,6 +128,7 @@ absorption_capacity = 5 splint_factor = 0.35 custom_price = PRICE_REALLY_CHEAP + grind_results = list(/datum/reagent/cellulose = 2) // gauze is only relevant for wounds, which are handled in the wounds themselves /obj/item/stack/medical/gauze/try_heal(mob/living/M, mob/user, silent) diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 44906a7179..9cbd8b3c92 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -412,6 +412,7 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \ force = 0 throwforce = 0 merge_type = /obj/item/stack/sheet/cloth + grind_results = list(/datum/reagent/cellulose = 2) /obj/item/stack/sheet/cloth/get_main_recipes() . = ..() @@ -843,6 +844,7 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra merge_type = /obj/item/stack/sheet/cotton var/pull_effort = 30 var/loom_result = /obj/item/stack/sheet/cloth + grind_results = list(/datum/reagent/cellulose = 1) /obj/item/stack/sheet/cotton/ten amount = 10 @@ -858,6 +860,7 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra merge_type = /obj/item/stack/sheet/cotton/durathread pull_effort = 70 loom_result = /obj/item/stack/sheet/durathread + grind_results = list(/datum/reagent/cellulose = 4) /obj/item/stack/sheet/meat name = "meat sheets" From 2f14d8998bb7aa2edb9da4b7d837db63625678ea Mon Sep 17 00:00:00 2001 From: lolman360 <22850904+lolman360@users.noreply.github.com> Date: Mon, 17 Aug 2020 22:42:41 +1000 Subject: [PATCH 28/42] Update autohydro.dm --- code/modules/plumbing/plumbers/autohydro.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/plumbing/plumbers/autohydro.dm b/code/modules/plumbing/plumbers/autohydro.dm index 5157000754..5e4c618284 100644 --- a/code/modules/plumbing/plumbers/autohydro.dm +++ b/code/modules/plumbing/plumbers/autohydro.dm @@ -7,7 +7,7 @@ canirrigate = FALSE -/obj/machinery/hydroponics/attackby(obj/item/O, mob/user, params) +/obj/machinery/hydroponics/constructable/automagic/attackby(obj/item/O, mob/user, params) if(istype(O, /obj/item/reagent_containers) ) return FALSE //avoid fucky wuckies ..() From 32c0335a17dcaff6c2c625ecec8aca640907f78b Mon Sep 17 00:00:00 2001 From: lolman360 <22850904+lolman360@users.noreply.github.com> Date: Tue, 18 Aug 2020 10:35:56 +1000 Subject: [PATCH 29/42] Update medical_designs.dm --- .../research/designs/medical_designs.dm | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 04ef917b00..8b322cea0d 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -968,7 +968,7 @@ desc = "A heating and cooling device for pipes!" id = "acclimator" build_type = PROTOLATHE - materials = list(MAT_METAL = 1000, MAT_GLASS = 500) + materials = list(/datum/material/iron = 1000, /datum/material/glass = 500) construction_time = 75 build_path = /obj/machinery/plumbing/acclimator category = list("Misc","Medical Designs") @@ -976,10 +976,10 @@ /datum/design/disposer name = "Plumbing Disposer" - desc = "Using the power of SCIENCE, dissolves reagents into nothing (almost)." + desc = "Using the power of Science, dissolves reagents into nothing (almost)." id = "disposer" build_type = PROTOLATHE - materials = list(MAT_METAL = 500, MAT_GLASS = 100) + materials = list(/datum/material/iron = 500, /datum/material/glass = 100) construction_time = 75 build_path = /obj/machinery/plumbing/disposer category = list("Misc","Medical Designs") @@ -990,7 +990,7 @@ desc = "Filters out chemicals by their NTDB ID." id = "plumb_filter" build_type = PROTOLATHE - materials = list(MAT_METAL = 1000, MAT_GLASS = 500) + materials = list(/datum/material/iron = 1000, /datum/material/glass = 500) construction_time = 75 build_path = /obj/machinery/plumbing/filter category = list("Misc","Medical Designs") @@ -1001,7 +1001,7 @@ desc = "Using standard mass-energy dynamic autoconverters, generates reagents from power and puts them in a pipe." id = "plumb_synth" build_type = PROTOLATHE - materials = list(MAT_METAL = 5000, MAT_GLASS = 1000, MAT_PLASTIC = 1000) + materials = list(/datum/material/iron = 5000, /datum/material/glass = 1000, MAT_PLASTIC = 1000) construction_time = 75 build_path = /obj/machinery/plumbing/synthesizer category = list("Misc","Medical Designs") @@ -1012,7 +1012,7 @@ desc = "Automatically extracts reagents from an item by grinding it. Think of the possibilities! Note: does not grind people." id = "plumb_grinder" build_type = PROTOLATHE - materials = list(MAT_METAL = 2000, MAT_GLASS = 1500) + materials = list(/datum/material/iron = 2000, /datum/material/glass = 1500) construction_time = 75 build_path = /obj/machinery/plumbing/grinder_chemical category = list("Misc","Medical Designs") @@ -1023,7 +1023,7 @@ desc = "You can set a list of allowed reagents and amounts. Once the chamber has these reagents, will let the products through." id = "reaction_chamber" build_type = PROTOLATHE - materials = list(MAT_METAL = 1000, MAT_GLASS = 500) + materials = list(/datum/material/iron = 1000, /datum/material/glass = 500) construction_time = 75 build_path = /obj/machinery/plumbing/reaction_chamber category = list("Misc","Medical Designs") @@ -1034,8 +1034,8 @@ desc = "Ducts for plumbing! Now lathed for efficiency." id = "duct_print" build_type = PROTOLATHE - materials = list(MAT_PLASTIC = 400) - construction_time = 5 + materials = list(/datum/material/plastic = 400) + construction_time = 1 build_path = /obj/item/stack/ducts category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE @@ -1045,7 +1045,7 @@ desc = "A splitter. Has 2 outputs. Can be configured to allow a certain amount through each side." id = "plumb_splitter" build_type = PROTOLATHE - materials = list(MAT_METAL = 750, MAT_GLASS = 250) + materials = list(/datum/material/iron = 750, /datum/material/glass = 250) construction_time = 75 build_path = /obj/machinery/plumbing/splitter category = list("Misc","Medical Designs") @@ -1056,7 +1056,7 @@ desc = "Automatically forms pills to the required parameters with piped reagents! A good replacement for those lazy, useless chemists." id = "pill_press" build_type = PROTOLATHE - materials = list(MAT_METAL = 1000, MAT_GLASS = 500) + materials = list(/datum/material/iron = 1000, /datum/material/glass = 500) construction_time = 75 build_path = /obj/machinery/plumbing/pill_press category = list("Misc","Medical Designs") @@ -1067,7 +1067,7 @@ desc = "Use it for extracting liquids from lavaland's geysers!" id = "plumb_pump" build_type = PROTOLATHE - materials = list(MAT_METAL = 1000, MAT_GLASS = 500) + materials = list(/datum/material/iron = 1000, /datum/material/glass = 500) construction_time = 75 build_path = /obj/machinery/plumbing/liquid_pump category = list("Misc","Medical Designs") @@ -1078,7 +1078,7 @@ desc = "A big piped funnel for putting stuff in the pipe network." id = "plumb_in" build_type = PROTOLATHE - materials = list(MAT_METAL = 400, MAT_GLASS = 400) + materials = list(/datum/material/iron = 400, /datum/material/glass = 400) construction_time = 75 build_path = /obj/machinery/plumbing/input category = list("Misc","Medical Designs") @@ -1089,7 +1089,7 @@ desc = "A big piped funnel for taking stuff out of the pipe network." id = "plumb_out" build_type = PROTOLATHE - materials = list(MAT_METAL = 400, MAT_GLASS = 400) + materials = list(/datum/material/iron = 400, /datum/material/glass = 400) construction_time = 75 build_path = /obj/machinery/plumbing/output category = list("Misc","Medical Designs") @@ -1100,7 +1100,7 @@ desc = "A tank for storing plumbed reagents." id = "plumb_tank" build_type = PROTOLATHE - materials = list(MAT_METAL = 10000, MAT_GLASS = 10000, MAT_PLASTIC = 4000) + materials = list(/datum/material/iron = 10000, /datum/material/glass = 10000, MAT_PLASTIC = 4000) construction_time = 75 build_path = /obj/machinery/plumbing/tank category = list("Misc","Medical Designs") @@ -1111,7 +1111,7 @@ desc = "A RCD for plumbing machines! Cannot make ducts." id = "plumb_rcd" build_type = PROTOLATHE - materials = list(MAT_METAL = 20000, MAT_GLASS = 10000, MAT_PLASTIC = 20000, MAT_PLASMA = 6000, MAT_DIAMOND = 5000, MAT_BLUESPACE = 5000, MAT_GOLD = 5000, MAT_SILVER = 5000) + materials = list(/datum/material/iron = 20000, /datum/material/glass = 10000, /datum/material/plastic = 20000, /datum/material/titanium = 2000, /datum/material/diamond = 800, /datum/material/gold = 2000, /datum/material/silver = 2000) construction_time = 150 build_path = /obj/item/construction/plumbing category = list("Misc","Medical Designs") From 8356dbf20a0defb1f3b9456f50812eeb75b38716 Mon Sep 17 00:00:00 2001 From: lolman360 <22850904+lolman360@users.noreply.github.com> Date: Tue, 18 Aug 2020 13:26:32 +1000 Subject: [PATCH 30/42] Update medical_designs.dm --- code/modules/research/designs/medical_designs.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 8b322cea0d..e177fd0aec 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -1001,7 +1001,7 @@ desc = "Using standard mass-energy dynamic autoconverters, generates reagents from power and puts them in a pipe." id = "plumb_synth" build_type = PROTOLATHE - materials = list(/datum/material/iron = 5000, /datum/material/glass = 1000, MAT_PLASTIC = 1000) + materials = list(/datum/material/iron = 5000, /datum/material/glass = 1000, /datum/material/plastic = 1000) construction_time = 75 build_path = /obj/machinery/plumbing/synthesizer category = list("Misc","Medical Designs") @@ -1100,7 +1100,7 @@ desc = "A tank for storing plumbed reagents." id = "plumb_tank" build_type = PROTOLATHE - materials = list(/datum/material/iron = 10000, /datum/material/glass = 10000, MAT_PLASTIC = 4000) + materials = list(/datum/material/iron = 10000, /datum/material/glass = 10000, /datum/material/plastic = 4000) construction_time = 75 build_path = /obj/machinery/plumbing/tank category = list("Misc","Medical Designs") From f2a86f2c428f087f5ba1a2c143dc7e265dae8b1f Mon Sep 17 00:00:00 2001 From: lolman360 <22850904+lolman360@users.noreply.github.com> Date: Tue, 18 Aug 2020 14:23:09 +1000 Subject: [PATCH 31/42] Update medical_designs.dm --- .../research/designs/medical_designs.dm | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index e177fd0aec..0a9fce2e67 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -969,7 +969,7 @@ id = "acclimator" build_type = PROTOLATHE materials = list(/datum/material/iron = 1000, /datum/material/glass = 500) - construction_time = 75 + construction_time = 15 build_path = /obj/machinery/plumbing/acclimator category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE @@ -980,7 +980,7 @@ id = "disposer" build_type = PROTOLATHE materials = list(/datum/material/iron = 500, /datum/material/glass = 100) - construction_time = 75 + construction_time = 15 build_path = /obj/machinery/plumbing/disposer category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE @@ -991,7 +991,7 @@ id = "plumb_filter" build_type = PROTOLATHE materials = list(/datum/material/iron = 1000, /datum/material/glass = 500) - construction_time = 75 + construction_time = 15 build_path = /obj/machinery/plumbing/filter category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE @@ -1002,7 +1002,7 @@ id = "plumb_synth" build_type = PROTOLATHE materials = list(/datum/material/iron = 5000, /datum/material/glass = 1000, /datum/material/plastic = 1000) - construction_time = 75 + construction_time = 15 build_path = /obj/machinery/plumbing/synthesizer category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE @@ -1013,7 +1013,7 @@ id = "plumb_grinder" build_type = PROTOLATHE materials = list(/datum/material/iron = 2000, /datum/material/glass = 1500) - construction_time = 75 + construction_time = 15 build_path = /obj/machinery/plumbing/grinder_chemical category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE @@ -1024,7 +1024,7 @@ id = "reaction_chamber" build_type = PROTOLATHE materials = list(/datum/material/iron = 1000, /datum/material/glass = 500) - construction_time = 75 + construction_time = 15 build_path = /obj/machinery/plumbing/reaction_chamber category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE @@ -1046,7 +1046,7 @@ id = "plumb_splitter" build_type = PROTOLATHE materials = list(/datum/material/iron = 750, /datum/material/glass = 250) - construction_time = 75 + construction_time = 15 build_path = /obj/machinery/plumbing/splitter category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE @@ -1057,7 +1057,7 @@ id = "pill_press" build_type = PROTOLATHE materials = list(/datum/material/iron = 1000, /datum/material/glass = 500) - construction_time = 75 + construction_time = 15 build_path = /obj/machinery/plumbing/pill_press category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE @@ -1068,7 +1068,7 @@ id = "plumb_pump" build_type = PROTOLATHE materials = list(/datum/material/iron = 1000, /datum/material/glass = 500) - construction_time = 75 + construction_time = 15 build_path = /obj/machinery/plumbing/liquid_pump category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE @@ -1079,7 +1079,7 @@ id = "plumb_in" build_type = PROTOLATHE materials = list(/datum/material/iron = 400, /datum/material/glass = 400) - construction_time = 75 + construction_time = 15 build_path = /obj/machinery/plumbing/input category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE @@ -1090,7 +1090,7 @@ id = "plumb_out" build_type = PROTOLATHE materials = list(/datum/material/iron = 400, /datum/material/glass = 400) - construction_time = 75 + construction_time = 15 build_path = /obj/machinery/plumbing/output category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE @@ -1101,7 +1101,7 @@ id = "plumb_tank" build_type = PROTOLATHE materials = list(/datum/material/iron = 10000, /datum/material/glass = 10000, /datum/material/plastic = 4000) - construction_time = 75 + construction_time = 15 build_path = /obj/machinery/plumbing/tank category = list("Misc","Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE From 3040032409ab7c71118bb65fb654a934d4d3edf0 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 18 Aug 2020 15:42:19 +1000 Subject: [PATCH 32/42] documentation updates --- code/datums/ductnet.dm | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/code/datums/ductnet.dm b/code/datums/ductnet.dm index 3ab936f6f4..14a74a67c4 100644 --- a/code/datums/ductnet.dm +++ b/code/datums/ductnet.dm @@ -1,32 +1,34 @@ +///We handle the unity part of plumbing. We track who is connected to who. /datum/ductnet var/list/suppliers = list() var/list/demanders = list() var/list/obj/machinery/duct/ducts = list() var/capacity - +///Add a duct to our network /datum/ductnet/proc/add_duct(obj/machinery/duct/D) if(!D || (D in ducts)) return ducts += D D.duct = src - +///Remove a duct from our network and commit suicide, because this is probably easier than to check who that duct was connected to and what part of us was lost /datum/ductnet/proc/remove_duct(obj/machinery/duct/ducting) destroy_network(FALSE) - for(var/A in ducting.neighbours) - var/obj/machinery/duct/D = A - D.attempt_connect() //we destroyed the network, so now we tell the disconnected ducts neighbours they can start making a new ductnet + for(var/obj/machinery/duct/D in ducting.neighbours) + addtimer(CALLBACK(D, /obj/machinery/duct/proc/reconnect), 0) //all needs to happen after the original duct that was destroyed finishes destroying itself + addtimer(CALLBACK(D, /obj/machinery/duct/proc/generate_connects), 0) qdel(src) - +///add a plumbing object to either demanders or suppliers /datum/ductnet/proc/add_plumber(datum/component/plumbing/P, dir) if(!P.can_add(src, dir)) - return + return FALSE P.ducts[num2text(dir)] = src if(dir & P.supply_connects) suppliers += P else if(dir & P.demand_connects) demanders += P - + return TRUE +///remove a plumber. we dont delete ourselves because ductnets dont persist through plumbing objects /datum/ductnet/proc/remove_plumber(datum/component/plumbing/P) suppliers.Remove(P) //we're probably only in one of these, but Remove() is inherently sane so this is fine demanders.Remove(P) @@ -34,7 +36,9 @@ for(var/dir in P.ducts) if(P.ducts[dir] == src) P.ducts -= dir - + if(!ducts.len) //there were no ducts, so it was a direct connection. we destroy ourselves since a ductnet with only one plumber and no ducts is worthless + destroy_network() +///we combine ductnets. this occurs when someone connects to seperate sets of fluid ducts /datum/ductnet/proc/assimilate(datum/ductnet/D) ducts.Add(D.ducts) suppliers.Add(D.suppliers) @@ -48,8 +52,9 @@ for(var/A in D.ducts) var/obj/machinery/duct/M = A M.duct = src //forget your old master - qdel(D) + destroy_network() +///destroy the network and tell all our ducts and plumbers we are gone /datum/ductnet/proc/destroy_network(delete=TRUE) for(var/A in suppliers + demanders) remove_plumber(A) From 325f4eb72e6aa5e20d1b3ae42715ef5996bf8a77 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 18 Aug 2020 15:43:44 +1000 Subject: [PATCH 33/42] Updates 2 --- code/modules/plumbing/ducts.dm | 138 ++++++++++++++++++++++++--------- 1 file changed, 101 insertions(+), 37 deletions(-) diff --git a/code/modules/plumbing/ducts.dm b/code/modules/plumbing/ducts.dm index 6cc09c7d15..cb94f281db 100644 --- a/code/modules/plumbing/ducts.dm +++ b/code/modules/plumbing/ducts.dm @@ -7,10 +7,10 @@ All the important duct code: name = "fluid duct" icon = 'icons/obj/plumbing/fluid_ducts.dmi' icon_state = "nduct" - level = 1 + ///bitfield with the directions we're connected in var/connects - ///set to TRUE to disable smart cable behaviour + ///set to TRUE to disable smart duct behaviour var/dumb = FALSE ///wheter we allow our connects to be changed after initialization or not var/lock_connects = FALSE @@ -36,13 +36,16 @@ All the important duct code: ///wheter we just unanchored or drop whatever is in the variable. either is safe var/drop_on_wrench = /obj/item/stack/ducts -/obj/machinery/duct/Initialize(mapload, no_anchor, color_of_duct, layer_of_duct = DUCT_LAYER_DEFAULT, force_connects) +/obj/machinery/duct/Initialize(mapload, no_anchor, color_of_duct = "#ffffff", layer_of_duct = DUCT_LAYER_DEFAULT, force_connects) . = ..() + if(no_anchor) active = FALSE - anchored = FALSE + set_anchored(FALSE) else if(!can_anchor()) qdel(src) + CRASH("Overlapping ducts detected") + if(force_connects) connects = force_connects //skip change_connects() because we're still initializing and we need to set our connects at one point if(!lock_layers) @@ -51,17 +54,22 @@ All the important duct code: duct_color = color_of_duct if(duct_color) add_atom_colour(duct_color, FIXED_COLOUR_PRIORITY) + handle_layer() + for(var/obj/machinery/duct/D in loc) if(D == src) continue if(D.duct_layer & duct_layer) disconnect_duct() + if(active) attempt_connect() + + AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) + ///start looking around us for stuff to connect to /obj/machinery/duct/proc/attempt_connect() - reset_connects() //All connects are gathered here again eitherway, we might aswell reset it so they properly update when reconnecting for(var/atom/movable/AM in loc) var/datum/component/plumbing/P = AM.GetComponent(/datum/component/plumbing) @@ -75,6 +83,7 @@ All the important duct code: if(connect_network(AM, D)) add_connects(D) update_icon() + ///see if whatever we found can be connected to /obj/machinery/duct/proc/connect_network(atom/movable/AM, direction, ignore_color) if(istype(AM, /obj/machinery/duct)) @@ -84,6 +93,7 @@ All the important duct code: if(!plumber) return return connect_plumber(plumber, direction) + ///connect to a duct /obj/machinery/duct/proc/connect_duct(obj/machinery/duct/D, direction, ignore_color) var/opposite_dir = turn(direction, 180) @@ -92,11 +102,11 @@ All the important duct code: if(!dumb && D.dumb && !(opposite_dir & D.connects)) return - if(dumb && D.dumb && !(connects & D.connects)) //we eliminated a few more scenario in attempt connect + if(dumb && D.dumb && !(connects & D.connects)) //we eliminated a few more scenarios in attempt connect return if((duct == D.duct) && duct)//check if we're not just comparing two null values - add_neighbour(D) + add_neighbour(D, direction) D.add_connects(opposite_dir) D.update_icon() @@ -118,9 +128,12 @@ All the important duct code: else create_duct() duct.add_duct(D) - add_neighbour(D) - D.attempt_connect()//tell our buddy its time to pass on the torch of connecting to pipes. This shouldn't ever infinitely loop since it only works on pipes that havent been inductrinated + add_neighbour(D, direction) + //tell our buddy its time to pass on the torch of connecting to pipes. This shouldn't ever infinitely loop since it only works on pipes that havent been inductrinated + D.attempt_connect() + return TRUE + ///connect to a plumbing object /obj/machinery/duct/proc/connect_plumber(datum/component/plumbing/P, direction) var/opposite_dir = turn(direction, 180) @@ -134,11 +147,14 @@ All the important duct code: if(opposite_dir & comp_directions) if(!duct) create_duct() - duct.add_plumber(P, opposite_dir) - return TRUE + if(duct.add_plumber(P, opposite_dir)) + neighbours[P.parent] = direction + return TRUE + ///we disconnect ourself from our neighbours. we also destroy our ductnet and tell our neighbours to make a new one -/obj/machinery/duct/proc/disconnect_duct() - anchored = FALSE +/obj/machinery/duct/proc/disconnect_duct(skipanchor) + if(!skipanchor) //since set_anchored calls us too. + set_anchored(FALSE) active = FALSE if(duct) duct.remove_duct(src) @@ -149,30 +165,72 @@ All the important duct code: new drop_on_wrench(drop_location()) qdel(src) +///''''''''''''''''optimized''''''''''''''''' proc for quickly reconnecting after a duct net was destroyed +/obj/machinery/duct/proc/reconnect() + if(neighbours.len && !duct) + create_duct() + for(var/atom/movable/AM in neighbours) + if(istype(AM, /obj/machinery/duct)) + var/obj/machinery/duct/D = AM + if(D.duct) + if(D.duct == duct) //we're already connected + continue + else + duct.assimilate(D.duct) + continue + else + duct.add_duct(D) + D.reconnect() + else + var/datum/component/plumbing/P = AM.GetComponent(/datum/component/plumbing) + if(AM in get_step(src, neighbours[AM])) //did we move? + if(P) + connect_plumber(P, neighbours[AM]) + else + neighbours -= AM //we moved + +///Special proc to draw a new connect frame based on neighbours. not the norm so we can support multiple duct kinds +/obj/machinery/duct/proc/generate_connects() + if(lock_connects) + return + connects = 0 + for(var/A in neighbours) + connects |= neighbours[A] + update_icon() + ///create a new duct datum /obj/machinery/duct/proc/create_duct() duct = new() duct.add_duct(src) + ///add a duct as neighbour. this means we're connected and will connect again if we ever regenerate -/obj/machinery/duct/proc/add_neighbour(obj/machinery/duct/D) +/obj/machinery/duct/proc/add_neighbour(obj/machinery/duct/D, direction) if(!(D in neighbours)) - neighbours += D + neighbours[D] = direction if(!(src in D.neighbours)) - D.neighbours += src + D.neighbours[src] = turn(direction, 180) + ///remove all our neighbours, and remove us from our neighbours aswell /obj/machinery/duct/proc/lose_neighbours() - for(var/A in neighbours) - var/obj/machinery/duct/D = A + for(var/obj/machinery/duct/D in neighbours) D.neighbours.Remove(src) neighbours = list() + ///add a connect direction /obj/machinery/duct/proc/add_connects(new_connects) //make this a define to cut proc calls? if(!lock_connects) connects |= new_connects + +///remove a connect direction +/obj/machinery/duct/proc/remove_connects(dead_connects) + if(!lock_connects) + connects &= ~dead_connects + ///remove our connects /obj/machinery/duct/proc/reset_connects() if(!lock_connects) connects = 0 + ///get a list of the ducts we can connect to if we are dumb /obj/machinery/duct/proc/get_adjacent_ducts() var/list/adjacents = list() @@ -183,7 +241,7 @@ All the important duct code: adjacents += D return adjacents -/obj/machinery/duct/update_icon() //setting connects isnt a parameter because sometimes we make more than one change, overwrite it completely or just add it to the bitfield +/obj/machinery/duct/update_icon_state() var/temp_icon = initial(icon_state) for(var/D in GLOB.cardinals) if(D & connects) @@ -196,6 +254,7 @@ All the important duct code: if(D == WEST) temp_icon += "_w" icon_state = temp_icon + ///update the layer we are on /obj/machinery/duct/proc/handle_layer() var/offset @@ -214,31 +273,33 @@ All the important duct code: pixel_y = offset +/obj/machinery/duct/set_anchored(anchorvalue) + . = ..() + if(isnull(.)) + return + if(anchorvalue) + active = TRUE + attempt_connect() + else + disconnect_duct(TRUE) + /obj/machinery/duct/wrench_act(mob/living/user, obj/item/I) //I can also be the RPD ..() add_fingerprint(user) I.play_tool_sound(src) - if(anchored) + if(anchored || can_anchor()) + set_anchored(!anchored) user.visible_message( \ - "[user] unfastens \the [src].", \ - "You unfasten \the [src].", \ + "[user] [anchored ? null : "un"]fastens \the [src].", \ + "You [anchored ? null : "un"]fasten \the [src].", \ "You hear ratcheting.") - disconnect_duct() - else if(can_anchor()) - anchored = TRUE - active = TRUE - user.visible_message( \ - "[user] fastens \the [src].", \ - "You fasten \the [src].", \ - "You hear ratcheting.") - attempt_connect() return TRUE -///collection of all the sanity checks to prevent us from stacking ducts that shouldnt be stacked +///collection of all the sanity checks to prevent us from stacking ducts that shouldn't be stacked /obj/machinery/duct/proc/can_anchor(turf/T) if(!T) T = get_turf(src) for(var/obj/machinery/duct/D in T) - if(!anchored) + if(!anchored || D == src) continue for(var/A in GLOB.cardinals) if(A & connects && A & D.connects) @@ -271,9 +332,10 @@ All the important duct code: return add_connects(direction) //the connect of the other duct is handled in connect_network, but do this here for the parent duct because it's not necessary in normal cases - add_neighbour(D) + add_neighbour(D, direction) connect_network(D, direction, TRUE) update_icon() + ///has a total of 5 layers and doesnt give a shit about color. its also dumb so doesnt autoconnect. /obj/machinery/duct/multilayered name = "duct layer-manifold" @@ -298,8 +360,9 @@ All the important duct code: . = ..() update_connects() -/obj/machinery/duct/multilayered/update_icon() - return +/obj/machinery/duct/multilayered/ComponentInitialize() + . = ..() + AddElement(/datum/element/update_icon_blocker) /obj/machinery/duct/multilayered/wrench_act(mob/living/user, obj/item/I) . = ..() @@ -311,7 +374,7 @@ All the important duct code: else connects = EAST | WEST -///don't connect to other multilayered stuff because honestly it shouldnt be done and I dont wanna deal with it +///don't connect to other multilayered stuff because honestly it shouldn't be done and I dont wanna deal with it /obj/machinery/duct/multilayered/connect_duct(obj/machinery/duct/D, direction, ignore_color) if(istype(D, /obj/machinery/duct/multilayered)) return @@ -326,6 +389,7 @@ All the important duct code: singular_name = "duct" icon = 'icons/obj/plumbing/fluid_ducts.dmi' icon_state = "ducts" + custom_materials = list(/datum/material/iron=500) w_class = WEIGHT_CLASS_TINY novariants = FALSE max_amount = 50 From d8e286d7bef5b7da6a6c5d9b81836fb461703cbe Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 18 Aug 2020 16:17:00 +1000 Subject: [PATCH 34/42] ee --- code/game/atoms_movable.dm | 7 +++++++ code/modules/plumbing/ducts.dm | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 0238529195..0a6c2b9eca 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -646,3 +646,10 @@ animate(I, alpha = 175, pixel_x = to_x, pixel_y = to_y, time = 3, transform = M, easing = CUBIC_EASING) sleep(1) animate(I, alpha = 0, transform = matrix(), time = 1) + +/atom/movable/proc/set_anchored(anchorvalue) //literally only for plumbing ran + SHOULD_CALL_PARENT(TRUE) + if(anchored == anchorvalue) + return + . = anchored + anchored = anchorvalue diff --git a/code/modules/plumbing/ducts.dm b/code/modules/plumbing/ducts.dm index cb94f281db..8a27f2669c 100644 --- a/code/modules/plumbing/ducts.dm +++ b/code/modules/plumbing/ducts.dm @@ -66,7 +66,6 @@ All the important duct code: if(active) attempt_connect() - AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) ///start looking around us for stuff to connect to /obj/machinery/duct/proc/attempt_connect() From 9931cb006c0815b679141db223498d28ddd122b5 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 18 Aug 2020 17:33:20 +1000 Subject: [PATCH 35/42] eee --- code/datums/components/plumbing/filter.dm | 4 ++-- .../components/plumbing/reaction_chamber.dm | 17 ++++++----------- code/modules/plumbing/plumbers/filter.dm | 11 +---------- .../plumbing/plumbers/reaction_chamber.dm | 2 +- 4 files changed, 10 insertions(+), 24 deletions(-) diff --git a/code/datums/components/plumbing/filter.dm b/code/datums/components/plumbing/filter.dm index 55647a353b..76b76323c5 100644 --- a/code/datums/components/plumbing/filter.dm +++ b/code/datums/components/plumbing/filter.dm @@ -35,12 +35,12 @@ else for(var/A in reagents.reagent_list) var/datum/reagent/R = A - if(!can_give_in_direction(direction, R)) + if(!can_give_in_direction(direction, R.type)) continue var/new_amount if(R.volume < amount) new_amount = amount - R.volume - reagents.trans_id_to(target.parent, R, amount) + reagents.trans_id_to(target.parent, R.type, amount) amount = new_amount if(amount <= 0) break diff --git a/code/datums/components/plumbing/reaction_chamber.dm b/code/datums/components/plumbing/reaction_chamber.dm index 705611ed1a..3345eb0e76 100644 --- a/code/datums/components/plumbing/reaction_chamber.dm +++ b/code/datums/components/plumbing/reaction_chamber.dm @@ -7,7 +7,7 @@ if(!istype(parent, /obj/machinery/plumbing/reaction_chamber)) return COMPONENT_INCOMPATIBLE -/datum/component/plumbing/reaction_chamber/can_give(amount, reagent) +/datum/component/plumbing/reaction_chamber/can_give(amount, reagent, datum/ductnet/net) . = ..() var/obj/machinery/plumbing/reaction_chamber/RC = parent if(!. || !RC.emptying) @@ -21,7 +21,7 @@ var/has_reagent = FALSE for(var/A in reagents.reagent_list) var/datum/reagent/RD = A - if(RT == RD) + 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) @@ -30,14 +30,9 @@ process_request(min(RC.required_reagents[RT], MACHINE_REAGENT_TRANSFER), RT, dir) return - RC.reagent_flags &= ~NO_REACT + reagents.flags &= ~NO_REACT reagents.handle_reactions() - if(reagents.fermiIsReacting) - return - RC.emptying = TRUE -/datum/component/plumbing/reaction_chamber/can_give(amount, reagent, datum/ductnet/net) - . = ..() - var/obj/machinery/plumbing/reaction_chamber/RC = parent - if(!. || !RC.emptying) - return FALSE + RC.emptying = TRUE //If we move this up, it'll instantly get turned off since any reaction always sets the reagent_total to zero. Other option is make the reaction update + //everything for every chemical removed, wich isn't a good option either. + RC.on_reagent_change() //We need to check it now, because some reactions leave nothing left. diff --git a/code/modules/plumbing/plumbers/filter.dm b/code/modules/plumbing/plumbers/filter.dm index b748c576e7..1ffd170507 100644 --- a/code/modules/plumbing/plumbers/filter.dm +++ b/code/modules/plumbing/plumbers/filter.dm @@ -4,6 +4,7 @@ desc = "A chemical filter for filtering chemicals. The left and right outputs appear to be from the perspective of the input port." icon_state = "filter" density = FALSE + ///whitelist of chems id's that go to the left side. Empty to disable port var/list/left = list() ///whitelist of chem id's that go to the right side. Empty to disable port @@ -12,21 +13,11 @@ var/list/english_left = list() ///whitelist of chems but their name instead of path var/list/english_right = list() - ui_x = 320 - ui_y = 310 - /obj/machinery/plumbing/filter/Initialize(mapload, bolt) . = ..() AddComponent(/datum/component/plumbing/filter, bolt) -/obj/machinery/plumbing/filter/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - - if(!ui) - ui = new(user, src, ui_key, "chemical_filter", name, ui_x, ui_y, master_ui, state) - ui.open() - /obj/machinery/plumbing/filter/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) diff --git a/code/modules/plumbing/plumbers/reaction_chamber.dm b/code/modules/plumbing/plumbers/reaction_chamber.dm index 33fc5c8f0c..949543c300 100644 --- a/code/modules/plumbing/plumbers/reaction_chamber.dm +++ b/code/modules/plumbing/plumbers/reaction_chamber.dm @@ -3,9 +3,9 @@ name = "reaction chamber" desc = "Keeps chemicals seperated until given conditions are met." icon_state = "reaction_chamber" - 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) */ From 4e235590659c1908c5da8b63828a69b59d1556b4 Mon Sep 17 00:00:00 2001 From: lolman360 <22850904+lolman360@users.noreply.github.com> Date: Tue, 18 Aug 2020 18:15:24 +1000 Subject: [PATCH 36/42] Update sheet_types.dm --- code/game/objects/items/stacks/sheets/sheet_types.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 9cbd8b3c92..eac57eb767 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -844,7 +844,7 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra merge_type = /obj/item/stack/sheet/cotton var/pull_effort = 30 var/loom_result = /obj/item/stack/sheet/cloth - grind_results = list(/datum/reagent/cellulose = 1) + grind_results = list(/datum/reagent/cellulose = 5) /obj/item/stack/sheet/cotton/ten amount = 10 @@ -860,7 +860,7 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra merge_type = /obj/item/stack/sheet/cotton/durathread pull_effort = 70 loom_result = /obj/item/stack/sheet/durathread - grind_results = list(/datum/reagent/cellulose = 4) + grind_results = list(/datum/reagent/cellulose = 10) /obj/item/stack/sheet/meat name = "meat sheets" From b466a5aae94c70ae8689d43ad22379141d28b460 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 18 Aug 2020 18:57:23 +1000 Subject: [PATCH 37/42] e --- code/datums/components/plumbing/reaction_chamber.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/datums/components/plumbing/reaction_chamber.dm b/code/datums/components/plumbing/reaction_chamber.dm index 3345eb0e76..029f1fde86 100644 --- a/code/datums/components/plumbing/reaction_chamber.dm +++ b/code/datums/components/plumbing/reaction_chamber.dm @@ -30,7 +30,7 @@ process_request(min(RC.required_reagents[RT], MACHINE_REAGENT_TRANSFER), RT, dir) return - reagents.flags &= ~NO_REACT + reagent_flags &= ~NO_REACT reagents.handle_reactions() RC.emptying = TRUE //If we move this up, it'll instantly get turned off since any reaction always sets the reagent_total to zero. Other option is make the reaction update From 7390effcb96e89049f6c19e4307129ac5f7c86c0 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 18 Aug 2020 19:09:01 +1000 Subject: [PATCH 38/42] e --- code/datums/components/plumbing/reaction_chamber.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/datums/components/plumbing/reaction_chamber.dm b/code/datums/components/plumbing/reaction_chamber.dm index 029f1fde86..90f4e621da 100644 --- a/code/datums/components/plumbing/reaction_chamber.dm +++ b/code/datums/components/plumbing/reaction_chamber.dm @@ -30,7 +30,7 @@ process_request(min(RC.required_reagents[RT], MACHINE_REAGENT_TRANSFER), RT, dir) return - reagent_flags &= ~NO_REACT + RC.reagent_flags &= ~NO_REACT reagents.handle_reactions() RC.emptying = TRUE //If we move this up, it'll instantly get turned off since any reaction always sets the reagent_total to zero. Other option is make the reaction update From e00c383b1a6bd46af34698020831559d1e3ff2b8 Mon Sep 17 00:00:00 2001 From: lolman360 Date: Tue, 18 Aug 2020 20:19:54 +1000 Subject: [PATCH 39/42] Update belt.dm --- code/game/objects/items/storage/belt.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index 3c554aa58e..0001494fdd 100755 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -478,7 +478,7 @@ /obj/item/assembly/signaler, /obj/item/lightreplacer, /obj/item/rcd_ammo, - /obj/item/construction/rcd, + /obj/item/construction, /obj/item/pipe_dispenser, /obj/item/stack/rods, /obj/item/stack/tile/plasteel, @@ -492,7 +492,7 @@ icon_state = "grenadebeltnew" item_state = "security" rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE - + /obj/item/storage/belt/grenade/ComponentInitialize() . = ..() var/datum/component/storage/STR = GetComponent(/datum/component/storage) From e82772dc44005af2b6ab24d82af839a03e8ff85f Mon Sep 17 00:00:00 2001 From: lolman360 Date: Wed, 19 Aug 2020 17:22:52 +1000 Subject: [PATCH 40/42] kekw --- code/modules/plumbing/plumbers/autohydro.dm | 17 ++++++++++++++--- .../chemistry/machinery/smoke_machine.dm | 5 +++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/code/modules/plumbing/plumbers/autohydro.dm b/code/modules/plumbing/plumbers/autohydro.dm index 5e4c618284..dbc70dfcf5 100644 --- a/code/modules/plumbing/plumbers/autohydro.dm +++ b/code/modules/plumbing/plumbers/autohydro.dm @@ -8,7 +8,7 @@ /obj/machinery/hydroponics/constructable/automagic/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/reagent_containers) ) + if(istype(O, /obj/item/reagent_containers)) return FALSE //avoid fucky wuckies ..() @@ -23,11 +23,22 @@ else CP.disable() -/obj/machinery/hydroponics/constructable/automagic/Initialize(mapload, bolt) +/obj/machinery/hydroponics/constructable/automagic/Destroy() . = ..() - AddComponent(/datum/component/plumbing/simple_demand, bolt) + STOP_PROCESSING(SSobj, src) + +/obj/machinery/hydroponics/constructable/automagic/Initialize(mapload) + . = ..() + START_PROCESSING(SSobj, src) create_reagents(100 , AMOUNT_VISIBLE) +/obj/machinery/hydroponics/constructable/automagic/ComponentInitialize() + . = ..() + AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated)) + AddComponent(/datum/component/plumbing/simple_demand) + +/obj/machinery/hydroponics/constructable/proc/can_be_rotated(mob/user, rotation_type) + return !anchored /obj/machinery/hydroponics/constructable/automagic/process() if(reagents) diff --git a/code/modules/reagents/chemistry/machinery/smoke_machine.dm b/code/modules/reagents/chemistry/machinery/smoke_machine.dm index a539897c9d..fd9f9116b7 100644 --- a/code/modules/reagents/chemistry/machinery/smoke_machine.dm +++ b/code/modules/reagents/chemistry/machinery/smoke_machine.dm @@ -36,6 +36,11 @@ for(var/obj/item/stock_parts/matter_bin/B in component_parts) reagents.maximum_volume += REAGENTS_BASE_VOLUME * B.rating +/obj/machinery/smoke_machine/ComponentInitialize() + . = ..() + AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated)) + AddComponent(/datum/component/plumbing/simple_demand) //this SURELY CANT' LEAD TO BAD THINGS HAPPENING. + /obj/machinery/smoke_machine/update_icon_state() if((!is_operational()) || (!on) || (reagents.total_volume == 0)) if (panel_open) From 1654a925b69e421daf76589bb601cf549bee14bd Mon Sep 17 00:00:00 2001 From: lolman360 Date: Wed, 19 Aug 2020 18:26:53 +1000 Subject: [PATCH 41/42] Update smoke_machine.dm --- code/modules/reagents/chemistry/machinery/smoke_machine.dm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/modules/reagents/chemistry/machinery/smoke_machine.dm b/code/modules/reagents/chemistry/machinery/smoke_machine.dm index fd9f9116b7..d22523c4b8 100644 --- a/code/modules/reagents/chemistry/machinery/smoke_machine.dm +++ b/code/modules/reagents/chemistry/machinery/smoke_machine.dm @@ -41,6 +41,9 @@ AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated)) AddComponent(/datum/component/plumbing/simple_demand) //this SURELY CANT' LEAD TO BAD THINGS HAPPENING. +/obj/machinery/smoke_machine/proc/can_be_rotated(mob/user, rotation_type) + return !anchored + /obj/machinery/smoke_machine/update_icon_state() if((!is_operational()) || (!on) || (reagents.total_volume == 0)) if (panel_open) From a9ce548755c118d21c94cb15749d0b5053bbc294 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 20 Aug 2020 23:32:29 -0500 Subject: [PATCH 42/42] Automatic changelog generation for PR #13178 [ci skip] --- html/changelogs/AutoChangeLog-pr-13178.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-13178.yml diff --git a/html/changelogs/AutoChangeLog-pr-13178.yml b/html/changelogs/AutoChangeLog-pr-13178.yml new file mode 100644 index 0000000000..d6765a7694 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13178.yml @@ -0,0 +1,5 @@ +author: "Time-Green (copypasta'd by lolman360)" +delete-after: True +changes: + - rscadd: "plumbing" + - rscadd: "automatic hydro trays"