diff --git a/code/controllers/subsystem/materials.dm b/code/controllers/subsystem/materials.dm
index a1cdd1566c4..9b437705c2f 100644
--- a/code/controllers/subsystem/materials.dm
+++ b/code/controllers/subsystem/materials.dm
@@ -20,7 +20,7 @@ SUBSYSTEM_DEF(materials)
var/list/rigid_stack_recipes = list(
new /datum/stack_recipe("Chair", /obj/structure/chair/greyscale, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
new /datum/stack_recipe("Toilet", /obj/structure/toilet/greyscale, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
- new /datum/stack_recipe("Sink", /obj/structure/sink/greyscale, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
+ new /datum/stack_recipe("Sink Frame", /obj/structure/sinkframe, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
new /datum/stack_recipe("Floor tile", /obj/item/stack/tile/material, 1, 4, 20, applies_mats = TRUE),
)
diff --git a/code/datums/components/plumbing/_plumbing.dm b/code/datums/components/plumbing/_plumbing.dm
index 7ac4659af53..1c443b2f611 100644
--- a/code/datums/components/plumbing/_plumbing.dm
+++ b/code/datums/components/plumbing/_plumbing.dm
@@ -242,11 +242,11 @@
///has one pipe input that only takes, example is manual output pipe
/datum/component/plumbing/simple_demand
- demand_connects = NORTH
+ demand_connects = SOUTH
///has one pipe output that only supplies. example is liquid pump and manual input pipe
/datum/component/plumbing/simple_supply
- supply_connects = NORTH
+ supply_connects = SOUTH
///input and output, like a holding tank
/datum/component/plumbing/tank
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 17e69a1388b..ff36c94453a 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -90,7 +90,8 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
new/datum/stack_recipe("floodlight frame", /obj/structure/floodlight_frame, 5, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("voting box", /obj/structure/votebox, 15, time = 50), \
new/datum/stack_recipe("pestle", /obj/item/pestle, 1, time = 50), \
- new/datum/stack_recipe("hygienebot assembly", /obj/item/bot_assembly/hygienebot, 2, time = 50)
+ new/datum/stack_recipe("hygienebot assembly", /obj/item/bot_assembly/hygienebot, 2, time = 5 SECONDS), \
+ new/datum/stack_recipe("shower frame", /obj/structure/showerframe, 2, time= 2 SECONDS)
))
/obj/item/stack/sheet/metal
diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm
index 43d28beb709..0e1c273fe14 100644
--- a/code/game/objects/items/storage/boxes.dm
+++ b/code/game/objects/items/storage/boxes.dm
@@ -1404,3 +1404,15 @@
/obj/item/storage/box/petridish/PopulateContents()
for(var/i in 1 to 7)
new /obj/item/petri_dish(src)
+
+/obj/item/storage/box/plumbing
+ name = "box of plumbing supplies"
+ desc = "Contains a small supply of pipes, water recyclers, and metal to connect to the rest of the station."
+
+/obj/item/storage/box/plumbing/PopulateContents()
+ var/list/items_inside = list(
+ /obj/item/stock_parts/water_recycler = 2,
+ /obj/item/stack/ducts/fifty = 1,
+ /obj/item/stack/sheet/metal/ten = 1,
+ )
+ generate_items_inside(items_inside, src)
diff --git a/code/game/objects/structures/shower.dm b/code/game/objects/structures/shower.dm
index 998a0d581e2..e80853e34b1 100644
--- a/code/game/objects/structures/shower.dm
+++ b/code/game/objects/structures/shower.dm
@@ -1,26 +1,39 @@
#define SHOWER_FREEZING "freezing"
+#define SHOWER_FREEZING_TEMP 100
#define SHOWER_NORMAL "normal"
+#define SHOWER_NORMAL_TEMP 300
#define SHOWER_BOILING "boiling"
+#define SHOWER_BOILING_TEMP 400
+
/obj/machinery/shower
name = "shower"
- desc = "The HS-451. Installed in the 2550s by the Nanotrasen Hygiene Division."
+ desc = "The HS-452. Installed in the 2550s by the Nanotrasen Hygiene Division, now with 2560 lead compliance! Passively replenishes itself with water when not in use."
icon = 'icons/obj/watercloset.dmi'
icon_state = "shower"
density = FALSE
use_power = NO_POWER_USE
+ ///Is the shower on or off?
var/on = FALSE
+ ///What temperature the shower reagents are set to.
var/current_temperature = SHOWER_NORMAL
+ ///What sound will be played on loop when the shower is on and pouring water.
var/datum/looping_sound/showering/soundloop
+ ///What reagent should the shower be filled with when initially built.
var/reagent_id = /datum/reagent/water
+ ///How much reagent capacity should the shower begin with when built.
var/reaction_volume = 200
/obj/machinery/shower/Initialize()
. = ..()
create_reagents(reaction_volume)
reagents.add_reagent(reagent_id, reaction_volume)
-
soundloop = new(list(src), FALSE)
+ AddComponent(/datum/component/plumbing/simple_demand)
+
+/obj/machinery/shower/examine(mob/user)
+ . = ..()
+ . += "[reagents.total_volume]/[reagents.maximum_volume] liquids remaining."
/obj/machinery/shower/Destroy()
QDEL_NULL(soundloop)
@@ -28,6 +41,9 @@
return ..()
/obj/machinery/shower/interact(mob/M)
+ if(reagents.total_volume < 5)
+ to_chat(M,"\The [src] is dry.")
+ return FALSE
on = !on
update_icon()
handle_mist()
@@ -69,7 +85,9 @@
/obj/machinery/shower/update_overlays()
. = ..()
if(on)
- . += mutable_appearance('icons/obj/watercloset.dmi', "water", ABOVE_MOB_LAYER)
+ var/mutable_appearance/water_falling = mutable_appearance('icons/obj/watercloset.dmi', "water", ABOVE_MOB_LAYER)
+ water_falling.color = mix_color_from_reagents(reagents.reagent_list)
+ . += water_falling
/obj/machinery/shower/proc/handle_mist()
// If there is no mist, and the shower was turned on (on a non-freezing temp): make mist in 5 seconds
@@ -84,7 +102,8 @@
/obj/machinery/shower/proc/make_mist()
var/obj/effect/mist/mist = locate() in loc
if(!mist && on && current_temperature != SHOWER_FREEZING)
- new /obj/effect/mist(loc)
+ var/obj/effect/mist/new_mist = new /obj/effect/mist(loc)
+ new_mist.color = mix_color_from_reagents(reagents.reagent_list)
/obj/machinery/shower/proc/clear_mist()
var/obj/effect/mist/mist = locate() in loc
@@ -101,19 +120,27 @@
A.wash(CLEAN_RAD | CLEAN_TYPE_WEAK) // Clean radiation non-instantly
A.wash(CLEAN_WASH)
SEND_SIGNAL(A, COMSIG_ADD_MOOD_EVENT, "shower", /datum/mood_event/nice_shower)
- reagents.expose(A, TOUCH, reaction_volume)
-
+ reagents.expose(A, TOUCH)
+ reagents.expose(A, VAPOR)
if(isliving(A))
check_heat(A)
/obj/machinery/shower/process()
- if(on)
+ if(on && reagents.total_volume >= 5)
+ reagents.remove_any(5)
wash_atom(loc)
for(var/am in loc)
var/atom/movable/movable_content = am
+ reagents.expose(movable_content, TOUCH, reaction_volume)
if(!ismopable(movable_content)) // Mopables will be cleaned anyways by the turf wash above
wash_atom(movable_content)
else
+ reagents.add_reagent(reagent_id, 1)
+ on = FALSE
+ soundloop.stop()
+ handle_mist()
+ update_icon()
+ if(reagents.total_volume == reagents.maximum_volume)
return PROCESS_KILL
/obj/machinery/shower/deconstruct(disassembled = TRUE)
@@ -134,6 +161,31 @@
to_chat(L, "[src] is searing!")
+/obj/structure/showerframe
+ name = "shower frame"
+ icon = 'icons/obj/watercloset.dmi'
+ icon_state = "shower_frame"
+ desc = "A shower frame, that needs a water recycler to finish construction."
+ anchored = FALSE
+
+/obj/structure/showerframe/attackby(obj/item/I, mob/living/user, params)
+ if(istype(I, /obj/item/stock_parts/water_recycler))
+ qdel(I)
+ var/obj/machinery/shower/new_shower = new /obj/machinery/shower(loc)
+ new_shower.setDir(dir)
+ qdel(src)
+ return
+ return ..()
+
+/obj/structure/showerframe/Initialize()
+ . = ..()
+ AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated))
+
+/obj/structure/showerframe/proc/can_be_rotated(mob/user, rotation_type)
+ if(anchored)
+ to_chat(user, "It is fastened to the floor!")
+ return !anchored
+
/obj/effect/mist
name = "mist"
icon = 'icons/obj/watercloset.dmi'
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index ded293a1168..30bd5c63b16 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -245,12 +245,31 @@
name = "sink"
icon = 'icons/obj/watercloset.dmi'
icon_state = "sink"
- desc = "A sink used for washing one's hands and face."
+ desc = "A sink used for washing one's hands and face. Passively reclaims water over time."
anchored = TRUE
- var/busy = FALSE //Something's being washed at the moment
- var/dispensedreagent = /datum/reagent/water // for whenever plumbing happens
+ ///Something's being washed at the moment
+ var/busy = FALSE
+ ///What kind of reagent is produced by this sink by default? (We now have actual plumbing, Arcane, August 2020)
+ var/dispensedreagent = /datum/reagent/water
+ ///Material to drop when broken or deconstructed.
var/buildstacktype = /obj/item/stack/sheet/metal
+ ///Number of sheets of material to drop when broken or deconstructed.
var/buildstackamount = 1
+ ///Does the sink have a water recycler to recollect it's water supply?
+ var/has_water_reclaimer = TRUE
+ ///Has the water reclamation begun?
+ var/reclaiming = FALSE
+
+/obj/structure/sink/Initialize(mapload, bolt)
+ . = ..()
+ if(has_water_reclaimer)
+ create_reagents(100, NO_REACT)
+ reagents.add_reagent(dispensedreagent, 100)
+ AddComponent(/datum/component/plumbing/simple_demand, bolt)
+
+/obj/structure/sink/examine(mob/user)
+ . = ..()
+ . += "[reagents.total_volume]/[reagents.maximum_volume] liquids remaining."
/obj/structure/sink/attack_hand(mob/living/user)
. = ..()
@@ -262,10 +281,13 @@
return
if(!Adjacent(user))
return
-
+ if(reagents.total_volume < 5)
+ to_chat(user, "The sink has no more contents left!")
+ return
if(busy)
to_chat(user, "Someone's already washing here!")
return
+ process_check()
var/selected_area = parse_zone(user.zone_selected)
var/washing_face = 0
if(selected_area in list(BODY_ZONE_HEAD, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_PRECISE_EYES))
@@ -279,6 +301,8 @@
return
busy = FALSE
+ reagents.remove_any(5)
+ reagents.expose(user, TOUCH)
if(washing_face)
SEND_SIGNAL(user, COMSIG_COMPONENT_CLEAN_FACE_ACT, CLEAN_WASH)
@@ -301,9 +325,13 @@
if(istype(O, /obj/item/reagent_containers))
var/obj/item/reagent_containers/RG = O
+ process_check()
+ if(reagents.total_volume <= 0)
+ to_chat(user, "\The [src] is dry.")
+ return FALSE
if(RG.is_refillable())
if(!RG.reagents.holder_full())
- RG.reagents.add_reagent(dispensedreagent, min(RG.volume - RG.reagents.total_volume, RG.amount_per_transfer_from_this))
+ reagents.trans_to(RG, RG.amount_per_transfer_from_this, transfered_by = user)
to_chat(user, "You fill [RG] from [src].")
return TRUE
to_chat(user, "\The [RG] is full.")
@@ -322,7 +350,10 @@
return
if(istype(O, /obj/item/mop))
- O.reagents.add_reagent(dispensedreagent, 5)
+ if(reagents.total_volume <= 0)
+ to_chat(user, "\The [src] is dry.")
+ return FALSE
+ reagents.trans_to(O, 5, transfered_by = user)
to_chat(user, "You wet [O] in [src].")
playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE)
return
@@ -359,8 +390,6 @@
busy = FALSE
O.wash(CLEAN_WASH)
O.acid_level = 0
- create_reagents(5)
- reagents.add_reagent(dispensedreagent, 5)
reagents.expose(O, TOUCH)
user.visible_message("[user] washes [O] using [src].", \
"You wash [O] using [src].")
@@ -373,6 +402,12 @@
drop_materials()
..()
+/obj/structure/sink/process()
+ if(has_water_reclaimer && reagents.total_volume < reagents.maximum_volume)
+ reagents.add_reagent(dispensedreagent, 1)
+ else
+ return PROCESS_KILL
+
/obj/structure/sink/proc/drop_materials()
if(buildstacktype)
new buildstacktype(loc,buildstackamount)
@@ -381,6 +416,12 @@
var/datum/material/M = i
new M.sheet_type(loc, FLOOR(custom_materials[M] / MINERAL_MATERIAL_AMOUNT, 1))
+/obj/structure/sink/proc/process_check()
+ if(!reclaiming)
+ reclaiming = TRUE
+ START_PROCESSING(SSfluids, src)
+ process()
+
/obj/structure/sink/kitchen
name = "kitchen sink"
icon_state = "sink_alt"
@@ -411,6 +452,34 @@
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
buildstacktype = null
+/obj/structure/sinkframe
+ name = "sink frame"
+ icon = 'icons/obj/watercloset.dmi'
+ icon_state = "sink_frame"
+ desc = "A sink frame, that needs a water recycler to finish construction."
+ anchored = FALSE
+ material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+
+/obj/structure/sinkframe/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated))
+
+/obj/structure/sinkframe/proc/can_be_rotated(mob/user, rotation_type)
+ if(anchored)
+ to_chat(user, "It is fastened to the floor!")
+ return !anchored
+
+/obj/structure/sinkframe/attackby(obj/item/I, mob/living/user, params)
+ if(istype(I, /obj/item/stock_parts/water_recycler))
+ qdel(I)
+ var/obj/structure/sink/greyscale/new_sink = new /obj/structure/sink/greyscale(loc)
+ new_sink.has_water_reclaimer = TRUE
+ new_sink.set_custom_materials(custom_materials)
+ new_sink.setDir(dir)
+ qdel(src)
+ return
+ return ..()
+
//Shower Curtains//
//Defines used are pre-existing in layers.dm//
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index 74de0985fd6..731d86f7505 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -48,8 +48,6 @@
var/unwrenchable = TRUE
///Have we been visited by a bee recently, so bees dont overpollinate one plant
var/recent_bee_visit = FALSE
- ///If the tray is connected to other trays via irrigation hoses
- var/using_irrigation = FALSE
///The last user to add a reagent to the tray, mostly for logging purposes.
var/mob/lastuser
///If the tray generates nutrients and water on its own
@@ -68,6 +66,14 @@
icon = 'icons/obj/hydroponics/equipment.dmi'
icon_state = "hydrotray3"
+/obj/machinery/hydroponics/constructable/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/RefreshParts()
var/tmp_capacity = 0
for (var/obj/item/stock_parts/matter_bin/M in component_parts)
@@ -97,33 +103,11 @@
// handle opening the panel
if(default_deconstruction_screwdriver(user, icon_state, icon_state, I))
return
-
- // handle deconstructing the machine, if permissible
- if(I.tool_behaviour == TOOL_CROWBAR && using_irrigation)
- to_chat(user, "Disconnect the hoses first!")
- return
- else if(default_deconstruction_crowbar(I))
+ if(default_deconstruction_crowbar(I))
return
return ..()
-/obj/machinery/hydroponics/proc/FindConnected()
- var/list/connected = list()
- var/list/processing_atoms = list(src)
-
- while(processing_atoms.len)
- var/atom/a = processing_atoms[1]
- for(var/step_dir in GLOB.cardinals)
- var/obj/machinery/hydroponics/h = locate() in get_step(a, step_dir)
- // Soil plots aren't dense
- if(h && h.using_irrigation && h.density && !(h in connected) && !(h in processing_atoms))
- processing_atoms += h
-
- processing_atoms -= a
- connected += a
-
- return connected
-
/obj/machinery/hydroponics/bullet_act(obj/projectile/Proj) //Works with the Somatoray to modify plant variables.
if(!myseed)
return ..()
@@ -321,8 +305,6 @@
add_overlay(mutable_appearance('icons/obj/hydroponics/equipment.dmi', "gaia_blessing"))
set_light(3)
- update_icon_hoses()
-
if(myseed)
update_icon_plant()
update_icon_lights()
@@ -336,15 +318,6 @@
return
-/obj/machinery/hydroponics/proc/update_icon_hoses()
- var/n = 0
- for(var/Dir in GLOB.cardinals)
- var/obj/machinery/hydroponics/t = locate() in get_step(src,Dir)
- if(t && t.using_irrigation && using_irrigation)
- n += Dir
-
- icon_state = "hoses-[n]"
-
/obj/machinery/hydroponics/proc/update_icon_plant()
var/mutable_appearance/plant_overlay = mutable_appearance(myseed.growing_icon, layer = OBJ_LAYER + 0.01)
if(dead)
@@ -567,7 +540,6 @@
var/list/trays = list(src)//makes the list just this in cases of syringes and compost etc
var/target = myseed ? myseed.plantname : src
var/visi_msg = ""
- var/irrigate = 0 //How am I supposed to irrigate pill contents?
var/transfer_amount
if(istype(reagent_source, /obj/item/reagent_containers/food/snacks) || istype(reagent_source, /obj/item/reagent_containers/pill))
@@ -584,35 +556,21 @@
visi_msg="[user] injects [target] with [syr]"
if(syr.reagents.total_volume <= syr.amount_per_transfer_from_this)
syr.mode = 0
- else if(istype(reagent_source, /obj/item/reagent_containers/spray/))
- visi_msg="[user] sprays [target] with [reagent_source]"
- playsound(loc, 'sound/effects/spray3.ogg', 50, TRUE, -6)
- irrigate = 1
- else if(transfer_amount) // Droppers, cans, beakers, what have you.
- visi_msg="[user] uses [reagent_source] on [target]"
- irrigate = 1
// Beakers, bottles, buckets, etc.
if(reagent_source.is_drainable())
playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE)
- if(irrigate && transfer_amount > 30 && reagent_source.reagents.total_volume >= 30 && using_irrigation)
- trays = FindConnected()
- if (trays.len > 1)
- visi_msg += ", setting off the irrigation system"
-
if(visi_msg)
visible_message("[visi_msg].")
- var/split = round(transfer_amount/trays.len)
-
for(var/obj/machinery/hydroponics/H in trays)
//cause I don't want to feel like im juggling 15 tamagotchis and I can get to my real work of ripping flooring apart in hopes of validating my life choices of becoming a space-gardener
//This was originally in apply_chemicals, but due to apply_chemicals only holding nutrients, we handle it here now.
if(reagent_source.reagents.has_reagent(/datum/reagent/water, 1))
- var/water_amt = reagent_source.reagents.get_reagent_amount(/datum/reagent/water) * split / reagent_source.reagents.total_volume
+ var/water_amt = reagent_source.reagents.get_reagent_amount(/datum/reagent/water) * transfer_amount / reagent_source.reagents.total_volume
H.adjustWater(round(water_amt))
reagent_source.reagents.remove_reagent(/datum/reagent/water, water_amt)
- reagent_source.reagents.trans_to(H.reagents, split, transfered_by = user)
+ reagent_source.reagents.trans_to(H.reagents, transfer_amount, transfered_by = user)
if(istype(reagent_source, /obj/item/reagent_containers/food/snacks) || istype(reagent_source, /obj/item/reagent_containers/pill))
qdel(reagent_source)
lastuser = user
@@ -751,18 +709,6 @@
else if(default_unfasten_wrench(user, O))
return
- else if((O.tool_behaviour == TOOL_WIRECUTTER) && unwrenchable)
- 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()
- return
-
else if(istype(O, /obj/item/shovel/spade))
if(!myseed && !weedlevel)
to_chat(user, "[src] doesn't have any plants or weeds!")
@@ -822,11 +768,6 @@
if (!unwrenchable) // case also covered by NODECONSTRUCT checks in default_unfasten_wrench
return CANT_UNFASTEN
- if (using_irrigation)
- if (!silent)
- to_chat(user, "Disconnect the hoses first!")
- return FAILED_UNFASTEN
-
return ..()
/obj/machinery/hydroponics/attack_hand(mob/user)
@@ -865,6 +806,9 @@
/obj/machinery/hydroponics/AltClick(mob/user)
. = ..()
+ if(!anchored)
+ update_icon()
+ return FALSE
var/warning = alert(user, "Are you sure you wish to empty the tray's nutrient beaker?","Empty Tray Nutrients?", "Yes", "No")
if(warning == "Yes" && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
reagents.clear_reagents()
@@ -953,14 +897,6 @@
var/mob/living/simple_animal/hostile/C = new chosen
C.faction = list("plants")
-//////////////////////////////////////////////////////////////////////////////////
-/obj/machinery/hydroponics/irrigated //Pre-Irrigated trays! Maybe map a couple on each map, just so they get used?
- using_irrigation = TRUE
-
-/obj/machinery/hydroponics/irrigated/Initialize()
- . = ..()
- update_icon() //Visually hooks up all the pipes.
-
///////////////////////////////////////////////////////////////////////////////
/obj/machinery/hydroponics/soil //Not actually hydroponics at all! Honk!
name = "soil"
@@ -974,9 +910,6 @@
flags_1 = NODECONSTRUCT_1
unwrenchable = FALSE
-/obj/machinery/hydroponics/soil/update_icon_hoses()
- return // Has no hoses
-
/obj/machinery/hydroponics/soil/update_icon_lights()
return // Has no lights
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 13840f96384..e4331d4cdbd 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -206,6 +206,13 @@
if(M.blood_volume)
M.blood_volume += 0.1 // water is good for you!
+///For weird backwards situations where water manages to get added to trays nutrients, as opposed to being snowflaked away like usual.
+/datum/reagent/water/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray)
+ if(chems.has_reagent(src.type, 1))
+ mytray.adjustWater(round(chems.get_reagent_amount(src.type) * 1))
+ //You don't belong in this world, monster!
+ chems.remove_reagent(/datum/reagent/water, chems.get_reagent_amount(src.type))
+
/datum/reagent/water/holywater
name = "Holy Water"
description = "Water blessed by some deity."
@@ -217,7 +224,6 @@
// Holy water. Mostly the same as water, it also heals the plant a little with the power of the spirits. Also ALSO increases instability.
/datum/reagent/water/holywater/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray)
- . = ..()
if(chems.has_reagent(src.type, 1))
mytray.adjustWater(round(chems.get_reagent_amount(src.type) * 1))
mytray.adjustHealth(round(chems.get_reagent_amount(src.type) * 0.1))
diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm
index dc91bdf897d..8623b88620d 100644
--- a/code/modules/reagents/reagent_dispenser.dm
+++ b/code/modules/reagents/reagent_dispenser.dm
@@ -1,14 +1,25 @@
/obj/structure/reagent_dispensers
name = "Dispenser"
desc = "..."
- icon = 'icons/obj/objects.dmi'
+ icon = 'icons/obj/chemical_tanks.dmi'
icon_state = "water"
density = TRUE
anchored = FALSE
pressure_resistance = 2*ONE_ATMOSPHERE
max_integrity = 300
- var/tank_volume = 1000 //In units, how much the dispenser can hold
- var/reagent_id = /datum/reagent/water //The ID of the reagent that the dispenser uses
+ ///In units, how much the dispenser can hold
+ var/tank_volume = 1000
+ ///The ID of the reagent that the dispenser uses
+ var/reagent_id = /datum/reagent/water
+ ///Can you turn this into a plumbing tank?
+ var/can_be_tanked = TRUE
+ ///Is this source self-replenishing?
+ var/refilling = FALSE
+
+/obj/structure/reagent_dispensers/examine(mob/user)
+ . = ..()
+ if(can_be_tanked)
+ . += "Use a sheet of metal to convert this into a plumbing-compatible tank."
/obj/structure/reagent_dispensers/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
. = ..()
@@ -18,7 +29,18 @@
/obj/structure/reagent_dispensers/attackby(obj/item/W, mob/user, params)
if(W.is_refillable())
- return 0 //so we can refill them via their afterattack.
+ return FALSE //so we can refill them via their afterattack.
+ if(istype(W, /obj/item/stack/sheet/metal) && can_be_tanked)
+ var/obj/item/stack/sheet/metal/metal_stack = W
+ metal_stack.use(1)
+ var/obj/structure/reagent_dispensers/plumbed/storage/new_tank = new /obj/structure/reagent_dispensers/plumbed/storage(drop_location())
+ new_tank.reagents.maximum_volume = reagents.maximum_volume
+ reagents.trans_to(new_tank, reagents.total_volume)
+ new_tank.name = "stationary [name]"
+ new_tank.update_overlays()
+ new_tank.anchored = anchored
+ qdel(src)
+ return FALSE
else
return ..()
@@ -201,10 +223,11 @@
reagent_id = /datum/reagent/consumable/nutraslop
/obj/structure/reagent_dispensers/plumbed
- name = "stationairy water tank"
+ name = "stationary water tank"
anchored = TRUE
- icon_state = "water_stationairy"
- desc = "A stationairy, plumbed, water tank."
+ icon_state = "water_stationary"
+ desc = "A stationary, plumbed, water tank."
+ can_be_tanked = FALSE
/obj/structure/reagent_dispensers/plumbed/wrench_act(mob/living/user, obj/item/I)
..()
@@ -215,9 +238,31 @@
AddComponent(/datum/component/plumbing/simple_supply)
/obj/structure/reagent_dispensers/plumbed/storage
- name = "stationairy storage tank"
- icon_state = "tank_stationairy"
+ name = "stationary storage tank"
+ icon_state = "tank_stationary"
reagent_id = null //start empty
/obj/structure/reagent_dispensers/plumbed/storage/ComponentInitialize()
- AddComponent(/datum/component/plumbing/tank)
+ . = ..()
+ AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated))
+
+/obj/structure/reagent_dispensers/plumbed/storage/update_overlays()
+ . = ..()
+ if(reagents)
+ if(reagents.total_volume)
+ var/mutable_appearance/tank_color = mutable_appearance('icons/obj/chemical_tanks.dmi', "tank_chem_overlay")
+ tank_color.color = mix_color_from_reagents(reagents.reagent_list)
+ add_overlay(tank_color)
+ else
+ cut_overlays()
+
+/obj/structure/reagent_dispensers/plumbed/storage/proc/can_be_rotated(mob/user, rotation_type)
+ if(anchored)
+ to_chat(user, "It is fastened to the floor!")
+ return !anchored
+
+/obj/structure/reagent_dispensers/plumbed/fuel
+ name = "stationary fuel tank"
+ icon_state = "fuel_stationary"
+ desc = "A stationary, plumbed, fuel tank."
+ reagent_id = /datum/reagent/fuel
diff --git a/code/modules/research/stock_parts.dm b/code/modules/research/stock_parts.dm
index 3006723d902..9cde16cddb9 100644
--- a/code/modules/research/stock_parts.dm
+++ b/code/modules/research/stock_parts.dm
@@ -350,12 +350,20 @@ If you create T5+ please take a pass at mech_fabricator.dm. The parts being good
desc = "A large piece of equipment used to open a window into the subspace dimension."
custom_materials = list(/datum/material/iron=50)
+// Misc. Parts
+
/obj/item/stock_parts/card_reader
name = "card reader"
icon_state = "card_reader"
desc = "A small magnetic card reader, used for devices that take and transmit holocredits."
custom_materials = list(/datum/material/iron=50, /datum/material/glass=10)
+/obj/item/stock_parts/water_recycler
+ name = "water recycler"
+ icon_state = "water_recycler"
+ desc = "A chemical reclaimation component, which serves to re-accumulate and filter water over time."
+ custom_materials = list(/datum/material/plastic=200, /datum/material/iron=50)
+
/obj/item/research//Makes testing much less of a pain -Sieve
name = "research"
icon = 'icons/obj/stock_parts.dmi'
diff --git a/icons/obj/chemical_tanks.dmi b/icons/obj/chemical_tanks.dmi
new file mode 100644
index 00000000000..034bb01f753
Binary files /dev/null and b/icons/obj/chemical_tanks.dmi differ
diff --git a/icons/obj/objects.dmi b/icons/obj/objects.dmi
index 67bc5769c58..eeb04500565 100644
Binary files a/icons/obj/objects.dmi and b/icons/obj/objects.dmi differ
diff --git a/icons/obj/stock_parts.dmi b/icons/obj/stock_parts.dmi
index 75e025185d4..a27f07f3337 100644
Binary files a/icons/obj/stock_parts.dmi and b/icons/obj/stock_parts.dmi differ
diff --git a/icons/obj/watercloset.dmi b/icons/obj/watercloset.dmi
index b61bf2927ab..19c8b83fa8e 100644
Binary files a/icons/obj/watercloset.dmi and b/icons/obj/watercloset.dmi differ