Makes water coolers more mechanically interesting, fills them up with fruit punch sometimes (#92480)

## About The Pull Request

This overhauls Water Coolers (now referred to as "liquid coolers"
internally and in most external cases). They'll feel less like a useless
prop now.

(sprites may look off because of the gif software I use)

**Important Thing # 1:** The reagents are now handled by cooler jugs.
You can pop the jug off of a cooler, fill it with whatever the hell you
want, and put it back on. Fill it with drugs. Fill it with acid. I don't
care, I'm not your mom.

<img width="41" height="56" alt="image"
src="https://github.com/user-attachments/assets/4c618cab-99b4-4a72-ae7b-7b900d7f5eaf"
/>

The sprite now uses an overlay to change colors based on the mix. Water,
when present in the cooler, is magically turned blue to keep that
classic water cooler look.

<img width="280" height="333" alt="image"
src="https://github.com/user-attachments/assets/8a6a905c-bef4-4f5f-9393-5ce5dcca0620"
/>

The jugs are 200 reagents, and are mechanically comparable to the jerry
can. No 200 unit acid splashes or anything.

Y'know, unless you put the jug in a cooler and knock it over...

![JUST COVER ME IN ACID WHY
DONTCHA](https://github.com/user-attachments/assets/7536a13c-1cb4-4af1-bfcc-25842215b393)

**Important Thing # 2:** They can be anchored, tipped, and crafted.

Tipping them requires the cooler be unanchored. If you want to use one
of these tactically, unwrench it beforehand.

![the
tipperrrr](https://github.com/user-attachments/assets/6b2cef28-bfb6-457a-b191-d91ec4fd9028)

**Important Thing # 3:** They can be bought from cargo now!

<img width="501" height="605" alt="image"
src="https://github.com/user-attachments/assets/6f261aab-f11a-4126-98ce-d9278d01d0a0"
/>

**Important Thing # 4**

Fruit punch.

<img width="175" height="127" alt="image"
src="https://github.com/user-attachments/assets/ac81e48c-47ff-461f-85f8-3e77a6a6ca55"
/>

Fruit punch is a drink with impressive (slightly faster than omnizine)
healing qualities. Unfortunately, it's so unbearably sweet that it can
only be safely consumed near a liquid cooler! Drinking this stuff is a
commitment, since if you stray from the cooler while digesting it, you
will be hurt and disoriented. You should avoid drinking too much at
once, in case an emergency comes up!

For this reason, we encourage drinkers to _take small sips from your
paper cooler cup, hang around the cooler, and maybe chat with other
people who are healing too._

(Mapload) water coolers have a 1% chance to start full of punch instead
of water. Maybe you'll get lucky and start with a healing station in
your department, or maybe a hopeless space explorer will find one in a
ruin and be saved.

You can also buy one from cargo but it's priceyyyyy. It's also the only
source of punch.
## Why It's Good For The Game

Realizing how little functionality water coolers have was super
disappointing. They do hardly anything but take up space and look
pretty. And now, they're getting mapped into all of the new stations! We
can't have a bunch of inert, sandbox-shunning furniture just sitting
around, the other codebases would laugh at us!

Now, coolers are more congruent with how most people would expect one to
behave in the game environment. Even minor changes like making them
unanchorable or refillable helps them fit in with most players'
intuitive understanding of how atoms typically behave.

The reagent storage change gives coolers their own unique niche in the
sandbox. Tipping them over can also give you an edge in escaping
someone, so even the untouched roundstart coolers can serve a purpose to
savvy players.

For the punch cooler, I figured "why not make a reagent that rewards
players for treating the water cooler like a water cooler?" The punch
encourages players to stand around it, slowly sip from the cooler, and
chat with their co-workers. Just like people do in offices! It's
thematically sound!
## Changelog
🆑 Rhials
add: You may now remove water cooler jugs and put whatever you want in
them. Go nuts.
add: Water coolers are now craftable (with plastic), orderable from
cargo, and tippable when unanchored.
add: Rarely, water coolers will spawn full of healing fruit punch
instead of water! Be careful not to stray from the cooler while
digesting this stuff.
add: You can also just buy the punch cooler from cargo if you feel like
it.
/🆑

---------

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
# Conflicts:
#	code/modules/reagents/reagent_dispenser.dm
This commit is contained in:
Rhials
2025-08-19 22:42:52 -04:00
committed by nevimer
parent 619fee0fc5
commit 906ac46477
10 changed files with 314 additions and 8 deletions
@@ -901,6 +901,8 @@ GLOBAL_LIST_INIT(plastic_recipes, list(
new /datum/stack_recipe("wet floor sign", /obj/item/clothing/suit/caution, 2, crafting_flags = NONE, category = CAT_EQUIPMENT), \
new /datum/stack_recipe("warning cone", /obj/item/clothing/head/cone, 2, crafting_flags = NONE, category = CAT_EQUIPMENT), \
new /datum/stack_recipe("blank wall sign", /obj/item/sign, 1, crafting_flags = NONE, category = CAT_FURNITURE), \
new /datum/stack_recipe("liquid cooler jug", /obj/item/reagent_containers/cooler_jug, 4, time = 5 SECONDS, crafting_flags = NONE, category = CAT_CONTAINERS), \
new /datum/stack_recipe("liquid cooler", /obj/structure/reagent_dispensers/water_cooler/jugless, 25, time = 10 SECONDS, crafting_flags = NONE, category = CAT_STRUCTURE), \
new /datum/stack_recipe("rebellion mask", /obj/item/clothing/mask/rebellion, 1, crafting_flags = NONE, category = CAT_CLOTHING)))
/obj/item/stack/sheet/plastic
@@ -909,7 +911,7 @@ GLOBAL_LIST_INIT(plastic_recipes, list(
singular_name = "plastic sheet"
icon_state = "sheet-plastic"
inhand_icon_state = "sheet-plastic"
mats_per_unit = list(/datum/material/plastic=SHEET_MATERIAL_AMOUNT)
mats_per_unit = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT)
throwforce = 7
material_type = /datum/material/plastic
merge_type = /obj/item/stack/sheet/plastic
+14
View File
@@ -257,3 +257,17 @@
/obj/item/hatchet/cutterblade = 1,
)
crate_name = "paper cutters crate"
/datum/supply_pack/misc/water_cooler
name = "Liquid Cooler - Water"
desc = "A liquid cooler, stocked with the freshest water in all the Periphery. Perfect for wasting time at the office!"
cost = CARGO_CRATE_VALUE * 2
contains = list(/obj/structure/reagent_dispensers/water_cooler/jugless = 1, /obj/item/reagent_containers/cooler_jug/water = 1)
crate_name = "water cooler crate"
/datum/supply_pack/misc/punch_cooler
name = "Liquid Cooler - Fruit Punch"
desc = "A liquid cooler, stocked with exotic fruit punch that can heal injuries! Be sure to remain near the liquid cooler while drinking, for your own health and safety. Seriously, do NOT drink this stuff away from the cooler."
cost = CARGO_CRATE_VALUE * 6
contains = list(/obj/structure/reagent_dispensers/water_cooler/jugless = 1, /obj/item/reagent_containers/cooler_jug/punch = 1)
crate_name = "punch cooler crate"
@@ -1309,3 +1309,60 @@
var/obj/item/organ/stomach/ethereal/stomach = exposed_carbon.get_organ_slot(ORGAN_SLOT_STOMACH)
if(istype(stomach))
stomach.adjust_charge(reac_volume * 20 * ETHEREAL_DISCHARGE_RATE)
/datum/reagent/consumable/fruit_punch
name = "fruit punch"
description = "Impossibly sweet fruit punch. Nobody knows what fruits were used to make it, not even it's creators... \
It's unique recipe heals and rejuvinates the drinker, but is unsafe to consume without the support of a nearby watercooler."
color = "#f7b2e3"
taste_description = "dangerously sweet fruit"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
quality = DRINK_VERYGOOD
/datum/reagent/consumable/fruit_punch/on_mob_life(mob/living/affected_mob, seconds_per_tick)
. = ..()
var/need_mob_update
var/found_valid_cooler = FALSE
for(var/obj/structure/reagent_dispensers/water_cooler/found_cooler in range(4, affected_mob))
if(found_cooler.anchored)
found_valid_cooler = TRUE
var/obj/effect/temp_visual/heal/heal_effect = new /obj/effect/temp_visual/heal(get_turf(found_cooler))
heal_effect.color = "#f7b2e3"
break
if(found_valid_cooler)
affected_mob.clear_alert("punch_bad")
affected_mob.throw_alert("punch_good", /atom/movable/screen/alert/fruit_punch_good)
need_mob_update = affected_mob.adjustToxLoss(-0.6 * REM * seconds_per_tick, updating_health = FALSE)
need_mob_update = affected_mob.adjustBruteLoss(-0.6 * REM * seconds_per_tick, updating_health = FALSE)
need_mob_update = affected_mob.adjustFireLoss(-0.6 * REM * seconds_per_tick, updating_health = FALSE)
affected_mob.remove_movespeed_modifier(/datum/movespeed_modifier/punch_punishment)
else
affected_mob.clear_alert("punch_good")
affected_mob.throw_alert("punch_bad", /atom/movable/screen/alert/fruit_punch_bad)
need_mob_update = affected_mob.apply_damage(1.5 * REM * seconds_per_tick, TOX)
affected_mob.add_movespeed_modifier(/datum/movespeed_modifier/punch_punishment)
if(SPT_PROB(10, seconds_per_tick))
affected_mob.Knockdown(3 SECONDS, 6 SECONDS) //Gives daze effect. Using the cooler is a commitment and if you get jumped during it or have to run away to fight something, you should be vulnerable.
to_chat(affected_mob, span_warning("The overwhelming sweetness of the fruit punch disorients and confounds you!"))
if(need_mob_update)
return UPDATE_MOB_HEALTH
/datum/movespeed_modifier/punch_punishment
multiplicative_slowdown = 0.30
/datum/reagent/consumable/fruit_punch/on_mob_end_metabolize(mob/living/affected_mob)
. = ..()
affected_mob.clear_alert("punch_bad")
affected_mob.clear_alert("punch_good")
affected_mob.remove_movespeed_modifier(/datum/movespeed_modifier/punch_punishment)
/atom/movable/screen/alert/fruit_punch_good
name = "Fruit Punch Blessing"
desc = "The sweetness of the fruit punch and the friendly company of the liquid cooler are slowly restoring your health..."
icon_state = "punch_blessing"
/atom/movable/screen/alert/fruit_punch_bad
name = "Fruit Punishment"
desc = "The unbearable sweetness of the fruit punch is too much to bear without the soothing aura of a liquid cooler! Your body is going into shock!"
icon_state = "punch_punishment"
@@ -0,0 +1,25 @@
/obj/item/reagent_containers/cooler_jug
name = "cooler jug"
desc = "A huge, unwieldy jug. Serves as the life force for liquid coolers. It smells like freshly cooled plastic."
icon = 'icons/obj/medical/chemical_tanks.dmi'
icon_state = "cooler_jug"
volume = 200
custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT * 4)
reagent_flags = REFILLABLE | DRAINABLE | INJECTABLE | DRAWABLE | TRANSPARENT | SMART_CAP
spillable = TRUE
has_variable_transfer_amount = FALSE
interaction_flags_click = NEED_DEXTERITY
fill_icon_state = "cooler_jug_overlay"
fill_icon_thresholds = list(25, 50, 75, 100)
obj_flags = UNIQUE_RENAME
w_class = WEIGHT_CLASS_BULKY
/obj/item/reagent_containers/cooler_jug/water
name = "water jug"
desc = "An elegant-looking water cooler jug. There's a water cooler out there, somewhere, waiting to be reunited with this. The jug's mouth smells intoxicatingly stale and metallic."
list_reagents = list(/datum/reagent/water = 200)
/obj/item/reagent_containers/cooler_jug/punch
name = "punch jug"
desc = "A jug meant for storing fruit punch. It's covered in dozens of warning labels and scary-looking symbols you don't recognize. The smell of sweet punch sticks to the mouth of the jug."
list_reagents = list(/datum/reagent/consumable/fruit_punch = 200)
+214 -7
View File
@@ -1,4 +1,5 @@
#define REAGENT_SPILL_DIVISOR 200
#define COOLER_JUG_EJECT_TIME (8 SECONDS)
/obj/structure/reagent_dispensers
name = "Dispenser"
@@ -370,13 +371,30 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/reagent_dispensers/wall/peppertank, 3
find_and_hang_on_wall()
/obj/structure/reagent_dispensers/water_cooler
name = "liquid cooler"
desc = "A machine that dispenses liquid to drink."
icon = 'icons/obj/machines/vending.dmi' //SKYRAT EDIT - ICON OVERRIDDEN BY AESTHETICS - SEE MODULE
name = "water cooler"
desc = "A machine that cools and dispenses liquids to drink. The 'hot' handle doesn't seem to do anything."
icon_state = "water_cooler"
anchored = TRUE
tank_volume = 500
var/paper_cups = 25 //Paper cups left from the cooler
tank_volume = 200
can_be_tanked = FALSE
max_integrity = 150
///Paper cups left from the cooler.
var/paper_cups = 25
///Reference to our jug.
var/obj/item/reagent_containers/cooler_jug/our_jug
///Have we been tipped?
var/tipped = FALSE
/obj/structure/reagent_dispensers/water_cooler/Initialize(mapload)
. = ..()
if(prob(2) && mapload)
reagents.convert_reagent(/datum/reagent/water, /datum/reagent/consumable/fruit_punch)
create_jug()
refresh_appearance()
/obj/structure/reagent_dispensers/water_cooler/Destroy()
. = ..()
our_jug = null
/obj/structure/reagent_dispensers/water_cooler/examine(mob/user)
. = ..()
@@ -391,14 +409,202 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/reagent_dispensers/wall/peppertank, 3
. = ..()
if(.)
return
if(tipped)
balloon_alert(user, "un-tipping...")
if(!do_after(user, 5 SECONDS, src))
return
tipped = FALSE
refresh_appearance()
return
if(user.combat_mode && our_jug)
balloon_alert(user, "removing jug...")
if(!do_after(user, COOLER_JUG_EJECT_TIME, src))
return
eject_jug(user)
return
if(!paper_cups)
to_chat(user, span_warning("There aren't any cups left!"))
return
user.visible_message(span_notice("[user] takes a cup from [src]."), span_notice("You take a paper cup from [src]."))
var/obj/item/reagent_containers/cup/glass/sillycup/S = new(get_turf(src))
user.put_in_hands(S)
var/obj/item/reagent_containers/cup/glass/sillycup/new_cup = new(get_turf(src))
user.put_in_hands(new_cup)
paper_cups--
/obj/structure/reagent_dispensers/water_cooler/update_overlays()
. = ..()
if(!reagents)
return
if(!reagents.total_volume)
return
var/mixcolor
var/vol_counter = 0
var/vol_temp
for(var/datum/reagent/stored_reagent as anything in reagents.reagent_list)
vol_temp = stored_reagent.volume
vol_counter += vol_temp
var/chosen_color = stored_reagent.color
if(istype(stored_reagent, /datum/reagent/water))
if(!mixcolor)
mixcolor = "#2694D6" //Override the water to be a nice blue
continue
else
chosen_color = "#2694D6"
else
if(!mixcolor)
mixcolor = stored_reagent.color
continue
else
chosen_color = stored_reagent.color
if (length(mixcolor) >= length(chosen_color))
mixcolor = BlendRGB(mixcolor, chosen_color, vol_temp/vol_counter)
else
mixcolor = BlendRGB(chosen_color, mixcolor, vol_temp/vol_counter)
if(mixcolor)
var/overlay_tag
if(vol_counter > 100)
overlay_tag = "100"
else
overlay_tag = "50" //Can't be 0, because there'd be no mixcolor
var/mutable_appearance/tank_overlay = mutable_appearance('icons/obj/medical/chemical_tanks.dmi', "water_cooler_overlay[overlay_tag]")
tank_overlay.color = mixcolor
. += tank_overlay
/obj/structure/reagent_dispensers/water_cooler/wrench_act(mob/living/user, obj/item/tool)
if(user.combat_mode)
return NONE
. = ITEM_INTERACT_BLOCKING
if(default_unfasten_wrench(user, tool, time = 6 SECONDS) == SUCCESSFUL_UNFASTEN)
return ITEM_INTERACT_SUCCESS
/obj/structure/reagent_dispensers/water_cooler/attack_hand_secondary(mob/user, modifiers)
if(tipped)
balloon_alert(user, "it's already tipped!")
return
if(anchored)
balloon_alert(user, "it's anchored!")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if(!do_after(user, 1.5 SECONDS, src))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
INVOKE_ASYNC(src, PROC_REF(boom), user)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/structure/reagent_dispensers/water_cooler/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/reagent_containers/cooler_jug))
return
if(tipped)
balloon_alert(user, "it's tipped!")
return
var/obj/item/reagent_containers/cooler_jug/new_jug = tool
balloon_alert(user, "replacing jug...")
if(!do_after(user, COOLER_JUG_EJECT_TIME, src))
return
if(!user.transferItemToLoc(new_jug, src))
return ITEM_INTERACT_BLOCKING
eject_jug(user, our_jug)
our_jug = new_jug
our_jug.reagents.trans_to(reagents, tank_volume)
balloon_alert(user, "attached")
user.log_message("attached a [new_jug] to [src] at [AREACOORD(src)] containing ([new_jug.reagents.get_reagent_log_string()])", LOG_ATTACK)
add_fingerprint(user)
refresh_appearance()
return ITEM_INTERACT_SUCCESS
/obj/structure/reagent_dispensers/water_cooler/boom()
if(QDELETED(src))
return
var/liquid_amount = 0
if(reagents.total_volume)
visible_message(span_danger("\The [src] flips on it's side and spills everywhere!"))
chem_splash(get_turf(src), null, 2 + (reagents.total_volume + liquid_amount) / 1000, list(reagents), extra_heat=(liquid_amount / 50), adminlog=(liquid_amount<25))
eject_jug(throw_away = TRUE)
playsound(src, 'sound/effects/glass/glassbash.ogg', 100)
tip_over()
/obj/structure/reagent_dispensers/water_cooler/proc/refresh_appearance()
if(tipped)
icon_state = "water_cooler_disgraced"
else
if(!our_jug)
icon_state = "water_cooler_forlorn"
else
icon_state = "water_cooler"
update_overlays()
update_appearance()
///Creates an empty jug inside of the cooler. Doesn't need to be filled bc it absorbs the cooler's reagent on eject.
/obj/structure/reagent_dispensers/water_cooler/proc/create_jug()
our_jug = new /obj/item/reagent_containers/cooler_jug(src)
///Eject the jug in a variety of ways. If there is a user, the jug goes into their hands. throw_away is passed on tip, to empty and throw the jug away. We delete the reagents since we create a splash before this is called.
/obj/structure/reagent_dispensers/water_cooler/proc/eject_jug(mob/living/user, throw_away = FALSE)
if(!our_jug)
return
if(user)
user.put_in_hands(our_jug)
else
our_jug.forceMove(drop_location())
if(throw_away)
var/turf/turf_to_throw_at = get_ranged_target_turf(src, pick(GLOB.alldirs), 2)
our_jug.throw_at(turf_to_throw_at, 2, 3)
reagents.remove_all(tank_volume) //Gets spilled on floor during boom()
else
reagents.trans_to(our_jug.reagents, tank_volume)
our_jug = null
refresh_appearance()
///Handles the visual stuff related to the cooler itself tipping.
/obj/structure/reagent_dispensers/water_cooler/proc/tip_over()
tipped = TRUE
refresh_appearance()
///Pre-tipped version for mapping.
/obj/structure/reagent_dispensers/water_cooler/fallen
tipped = TRUE
reagent_id = null
anchored = FALSE
/obj/structure/reagent_dispensers/water_cooler/fallen/Initialize(mapload)
. = ..()
tip_over()
/obj/structure/reagent_dispensers/water_cooler/fallen/create_jug()
return
/obj/structure/reagent_dispensers/water_cooler/jugless
reagent_id = null
anchored = FALSE
/obj/structure/reagent_dispensers/water_cooler/jugless/create_jug()
return
///Punch cooler. Starts full of healing juice. In case anyone wants to map one somewhere as a healing station.
/obj/structure/reagent_dispensers/water_cooler/punch_cooler
name = "punch cooler"
desc = "A machine that dispenses fruit punch to drink. This juice is unbearably sweet, and can only be safely consumed in the presence of a liquid cooler. Engage with caution."
reagent_id = /datum/reagent/consumable/fruit_punch
/obj/structure/reagent_dispensers/beerkeg
name = "beer keg"
desc = "Beer is liquid bread, it's good for you..."
@@ -485,3 +691,4 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/reagent_dispensers/wall/virusfood, 30
accepts_rig = TRUE
#undef REAGENT_SPILL_DIVISOR
#undef COOLER_JUG_EJECT_TIME
Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

+1
View File
@@ -6106,6 +6106,7 @@
#include "code\modules\reagents\reagent_containers\blood_pack.dm"
#include "code\modules\reagents\reagent_containers\chem_pack.dm"
#include "code\modules\reagents\reagent_containers\condiment.dm"
#include "code\modules\reagents\reagent_containers\cooler_jug.dm"
#include "code\modules\reagents\reagent_containers\dropper.dm"
#include "code\modules\reagents\reagent_containers\hypospray.dm"
#include "code\modules\reagents\reagent_containers\jerrycan.dm"