Skip to content

Commit

Permalink
plumbing enabled showers & sinks, water recyclers, stationary tanks a…
Browse files Browse the repository at this point in the history
  • Loading branch information
goober3 committed Jul 27, 2023
1 parent dabad38 commit 4769be0
Show file tree
Hide file tree
Showing 15 changed files with 268 additions and 38 deletions.
2 changes: 1 addition & 1 deletion code/controllers/subsystem/materials.dm
Expand Up @@ -18,7 +18,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("Material floor tile", /obj/item/stack/tile/material, 1, 4, 20, applies_mats = TRUE),
)

Expand Down
4 changes: 2 additions & 2 deletions code/datums/components/plumbing/_plumbing.dm
Expand Up @@ -250,11 +250,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
Expand Down
4 changes: 2 additions & 2 deletions code/game/objects/items/stacks/sheets/sheet_types.dm
Expand Up @@ -110,8 +110,8 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
new/datum/stack_recipe("voting box", /obj/structure/votebox, 15, time = 50), \
new/datum/stack_recipe("mortar", /obj/item/reagent_containers/glass/mortar/metal, 3), \
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("shower", /obj/machinery/shower, 3, time = 25)
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
Expand Down
12 changes: 12 additions & 0 deletions code/game/objects/items/storage/boxes.dm
Expand Up @@ -1556,3 +1556,15 @@
/obj/item/stack/wrapping_paper/small=1
)
generate_items_inside(items_inside,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)
105 changes: 92 additions & 13 deletions code/game/objects/structures/shower.dm
@@ -1,38 +1,61 @@
#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
#define SHOWER_REACTION_MULTIPLIER 0.025

/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." //this'll probably get a more lore compliant description later
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
var/reaction_volume = 200
//How much reagent capacity should the shower begin with when built.
var/reagent_capacity = 200
///How many units the shower refills every second.
var/refill_rate = 0.5
/// Whether or not the shower's water reclaimer is operating.
var/can_refill = TRUE
/// Whether to allow players to toggle the water reclaimer.
var/can_toggle_refill = TRUE

/obj/machinery/shower/Initialize()
. = ..()
create_reagents(reaction_volume)
reagents.add_reagent(reagent_id, reaction_volume)

create_reagents(reagent_capacity)
reagents.add_reagent(reagent_id, reagent_capacity)
soundloop = new(list(src), FALSE)

AddComponent(/datum/component/plumbing/simple_demand)

var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = .proc/on_entered,
)
AddElement(/datum/element/connect_loc, loc_connections)

/obj/machinery/shower/examine(mob/user)
. = ..()
. += "<span class='notice'>[reagents.total_volume]/[reagents.maximum_volume] liquids remaining.</span>"

/obj/machinery/shower/Destroy()
QDEL_NULL(soundloop)
QDEL_NULL(reagents)
return ..()

/obj/machinery/shower/interact(mob/M)
if(reagents.total_volume < 5)
to_chat(M,"<span class='notice'>\The [src] is dry.</span>")
return FALSE
on = !on
update_icon()
handle_mist()
Expand All @@ -53,6 +76,16 @@
else
return ..()

/obj/machinery/shower/multitool_act(mob/living/user, obj/item/I)
. = ..()
if(. || !can_toggle_refill)
return

can_refill = !can_refill
to_chat(user, "<span class=notice>You [can_refill ? "en" : "dis"]able the shower's water recycler.</span>")
playsound(src, 'sound/machines/click.ogg', 20, TRUE)
return TRUE

/obj/machinery/shower/wrench_act(mob/living/user, obj/item/I)
..()
to_chat(user, "<span class='notice'>You begin to adjust the temperature valve with \the [I]...</span>")
Expand All @@ -74,7 +107,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
Expand All @@ -89,7 +124,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
Expand All @@ -99,30 +135,40 @@

/obj/machinery/shower/proc/on_entered(datum/source, atom/movable/AM)
SIGNAL_HANDLER
if(on)
if(on && reagents.total_volume)
wash_atom(AM)

/obj/machinery/shower/proc/wash_atom(atom/A)
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)

if(isliving(A))
check_heat(A)

if(iscarbon(A)) //WS edit - moth dust from hugging
if(iscarbon(A))
var/mob/living/carbon/C = A
C.mothdust -= 10;

/obj/machinery/shower/process()
if(on)
/obj/machinery/shower/process(delta_time)
if(on && reagents.total_volume)
reagents.remove_any(2.5 * delta_time)
wash_atom(loc)
for(var/am in loc)
var/atom/movable/movable_content = am
reagents.expose(movable_content, TOUCH, SHOWER_REACTION_MULTIPLIER * delta_time) //There's not many reagents leaving the shower at once! This should make for a 10 unit reaction
if(!ismopable(movable_content)) // Mopables will be cleaned anyways by the turf wash above
wash_atom(movable_content)
else
return

on = FALSE
soundloop.stop()
handle_mist()
if(can_refill)
reagents.add_reagent(reagent_id, refill_rate * delta_time)
update_icon()
if(reagents.total_volume == reagents.maximum_volume)
return PROCESS_KILL

/obj/machinery/shower/deconstruct(disassembled = TRUE)
Expand Down Expand Up @@ -150,3 +196,36 @@
layer = FLY_LAYER
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT

#undef SHOWER_REACTION_MULTIPLIER
#undef SHOWER_BOILING_TEMP
#undef SHOWER_BOILING
#undef SHOWER_NORMAL_TEMP
#undef SHOWER_NORMAL
#undef SHOWER_FREEZING_TEMP
#undef SHOWER_FREEZING

/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, "<span class='warning'>It is fastened to the floor!</span>")
return !anchored
87 changes: 78 additions & 9 deletions code/game/objects/structures/watercloset.dm
Expand Up @@ -244,12 +244,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?
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)
. = ..()
. += "<span class='notice'>[reagents.total_volume]/[reagents.maximum_volume] liquids remaining.</span>"

/obj/structure/sink/attack_hand(mob/living/user)
. = ..()
Expand All @@ -261,10 +280,13 @@
return
if(!Adjacent(user))
return

if(reagents.total_volume < 5)
to_chat(user, "<span class='warning'>The sink has no more contents left!</span>")
return
if(busy)
to_chat(user, "<span class='warning'>Someone's already washing here!</span>")
return
return //coders scare me
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))
Expand All @@ -278,6 +300,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)
Expand All @@ -300,9 +324,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, "<span class='notice'>\The [src] is dry.</span>")
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, "<span class='notice'>You fill [RG] from [src].</span>")
return TRUE
to_chat(user, "<span class='notice'>\The [RG] is full.</span>")
Expand All @@ -321,7 +349,10 @@
return

if(istype(O, /obj/item/mop))
O.reagents.add_reagent(dispensedreagent, 5)
if(reagents.total_volume <= 0)
to_chat(user, "<span class='notice'>\The [src] is dry.</span>")
return FALSE
reagents.trans_to(O, 5, transfered_by = user)
to_chat(user, "<span class='notice'>You wet [O] in [src].</span>")
playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE)
return
Expand Down Expand Up @@ -358,8 +389,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("<span class='notice'>[user] washes [O] using [src].</span>", \
"<span class='notice'>You wash [O] using [src].</span>")
Expand All @@ -372,6 +401,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)
Expand All @@ -380,6 +415,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"
Expand Down Expand Up @@ -415,6 +456,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, "<span class='warning'>It is fastened to the floor!</span>")
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//

Expand Down

0 comments on commit 4769be0

Please sign in to comment.