'lolman? did you test your code?'

no i did not ms. kevinz
This commit is contained in:
lolman360
2020-07-22 10:49:56 +10:00
parent 4ac4d94c31
commit 0f1aa3c0ca
36 changed files with 2171 additions and 7 deletions
+1
View File
@@ -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
+9
View File
@@ -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
+4
View File
@@ -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
+5
View File
@@ -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
@@ -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
@@ -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
. = ..()
+59
View File
@@ -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
@@ -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
@@ -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
. = ..()
+60
View File
@@ -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)
+2
View File
@@ -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, "<span class='notice'>You [anchored ? "un" : ""]secure [src].</span>")
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
+75
View File
@@ -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, "<span class='notice'>You change [name]s blueprint to '[choice]'.</span>")
///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, "<span class='warning'>The [P.name] needs to be unanchored!</span>")
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
+68 -1
View File
@@ -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, "<span class='warning'>[src]'s error light flickers; there's something in the way!</span>")
return
to_chat(user, "<span class='notice'>You start building a fluid duct...</span>")
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
@@ -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)))
+3
View File
@@ -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
+74
View File
@@ -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, "<span class='warning'>The [P.name] isn't strong enough!</span>")
return
if(activated)
to_chat(user, "<span class'warning'>The [name] is already active!")
return
to_chat(user, "<span class='notice'>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
+1 -2
View File
@@ -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'
)
+371
View File
@@ -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].", \
"<span class='notice'>You unfasten \the [src].</span>", \
"<span class='hear'>You hear ratcheting.</span>")
disconnect_duct()
else if(can_anchor())
anchored = TRUE
active = TRUE
user.visible_message( \
"[user] fastens \the [src].", \
"<span class='notice'>You fasten \the [src].</span>", \
"<span class='hear'>You hear ratcheting.</span>")
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, "<span class='warning'>You need to be holding a wrench in your active hand to do that!</span>")
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)
. = ..()
. += "<span class='notice'>It's current color and layer are [duct_color] and [duct_layer]. Use in-hand to change.</span>"
/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
@@ -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)
. = ..()
. += "<span class='notice'>The maximum volume display reads: <b>[reagents.maximum_volume] units</b>.</span>"
/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, "<span class='notice'>You start furiously plunging [name].")
if(do_after(user, 30, target = src))
to_chat(user, "<span class='notice'>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, "<span class='warning'>The [name] needs to be unbolted to do that!</span")
if(I.tool_start_check(user, amount=0))
to_chat(user, "<span class='notice'>You start slicing the [name] apart.</span")
if(I.use_tool(src, user, rcd_delay * 2, volume=50))
deconstruct(TRUE)
to_chat(user, "<span class='notice'>You slice the [name] apart.</span")
return TRUE
///We can empty beakers in here and everything
/obj/machinery/plumbing/input
name = "input gate"
desc = "Can be manually filled with reagents from containers."
icon_state = "pipe_input"
reagent_flags = TRANSPARENT | REFILLABLE
rcd_cost = 5
rcd_delay = 5
/obj/machinery/plumbing/input/Initialize(mapload, bolt)
. = ..()
AddComponent(/datum/component/plumbing/simple_supply, bolt)
///We can fill beakers in here and everything. we dont inheret from input because it has nothing that we need
/obj/machinery/plumbing/output
name = "output gate"
desc = "A manual output for plumbing systems, for taking reagents directly into containers."
icon_state = "pipe_output"
reagent_flags = TRANSPARENT | DRAINABLE
rcd_cost = 5
rcd_delay = 5
/obj/machinery/plumbing/output/Initialize(mapload, bolt)
. = ..()
AddComponent(/datum/component/plumbing/simple_demand, bolt)
/obj/machinery/plumbing/tank
name = "chemical tank"
desc = "A massive chemical holding tank."
icon_state = "tank"
buffer = 400
rcd_cost = 25
rcd_delay = 20
/obj/machinery/plumbing/tank/Initialize(mapload, bolt)
. = ..()
AddComponent(/datum/component/plumbing/tank, bolt)
@@ -0,0 +1,105 @@
//we cant use defines in tgui, so use a string instead of magic numbers
#define COOLING "Cooling"
#define HEATING "Heating"
#define NEUTRAL "Neutral"
///this the plumbing version of a heater/freezer.
/obj/machinery/plumbing/acclimator
name = "chemical acclimator"
desc = "An efficient cooler and heater for the perfect showering temperature or illicit chemical factory."
icon_state = "acclimator"
buffer = 200
///towards wich temperature do we build?
var/target_temperature = 300
///I cant find a good name for this. Basically if target is 300, and this is 10, it will still target 300 but will start emptying itself at 290 and 310.
var/allowed_temperature_difference = 1
///cool/heat power
var/heater_coefficient = 0.1
///Are we turned on or off? this is from the on and off button
var/enabled = TRUE
///COOLING, HEATING or NEUTRAL. We track this for change, so we dont needlessly update our icon
var/acclimate_state
/**We can't take anything in, at least till we're emptied. Down side of the round robin chem transfer, otherwise while emptying 5u of an unreacted chem gets added,
and you get nasty leftovers
*/
var/emptying = FALSE
var/ui_x = 320
var/ui_y = 310
/obj/machinery/plumbing/acclimator/Initialize(mapload, bolt)
. = ..()
AddComponent(/datum/component/plumbing/acclimator, bolt)
/obj/machinery/plumbing/acclimator/process()
if(stat & NOPOWER || !enabled || !reagents.total_volume || reagents.chem_temp == target_temperature)
if(acclimate_state != NEUTRAL)
acclimate_state = NEUTRAL
update_icon()
if(!reagents.total_volume)
emptying = FALSE
return
if(reagents.chem_temp < target_temperature && acclimate_state != HEATING) //note that we check if the temperature is the same at the start
acclimate_state = HEATING
update_icon()
else if(reagents.chem_temp > 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
+79
View File
@@ -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, "<span class='warning'>It is fastened to the floor!</span>")
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, "<span class='notice'> The [src] will now fill for [wanted_amount]u.</span>")
/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
@@ -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)
@@ -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, "<span class='warning'>It is fastened to the floor!</span>")
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)
+74
View File
@@ -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, "<span class='warning'>No such known reagent exists!</span>")
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
@@ -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, "<span class='warning'>It is fastened to the floor!</span>")
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)
@@ -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)
. = ..()
. += "<span class='notice'>The [name] currently has [stored_pills.len] stored. There needs to be less than [max_floor_pills] on the floor to continue dispensing.</span>"
/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)
+83
View File
@@ -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("<span class='warning'>The [name] makes a sad beep!</span>")
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)
@@ -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
@@ -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
@@ -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
@@ -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
+1 -1
View File
@@ -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)
+34 -3
View File
@@ -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("<span class='notice'>[user.name] [anchored ? "fasten" : "unfasten"] [src]</span>", \
"<span class='notice'>You [anchored ? "fasten" : "unfasten"] [src]</span>")
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
@@ -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
@@ -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
Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 16 KiB