diff --git a/code/datums/components/crafting/recipes/tools.dm b/code/datums/components/crafting/recipes/tools.dm
index 71f3e57b6aa9..1aea51b0d5c5 100644
--- a/code/datums/components/crafting/recipes/tools.dm
+++ b/code/datums/components/crafting/recipes/tools.dm
@@ -55,6 +55,16 @@
////CONTAINERS/////
+/datum/crafting_recipe/jar
+ name = "Glass Jar"
+ time = 60
+ reqs = list(
+ /obj/item/stack/sheet/mineral/wood = 3,
+ /obj/item/stack/sheet/leather = 2,
+ /obj/item/stack/sheet/glass = 5)
+ result = /obj/item/reagent_containers/glass/beaker/jar
+ category = CAT_TOOL
+
/datum/crafting_recipe/woodbucket
name = "Wooden Bucket"
time = 30
diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm
index 686a77a69966..59fe82d19366 100755
--- a/code/modules/reagents/reagent_containers/glass.dm
+++ b/code/modules/reagents/reagent_containers/glass.dm
@@ -17,14 +17,13 @@
return
if(!reagents || !reagents.total_volume)
- to_chat(user, "[src] is empty!")
+ to_chat(user, span_warning("[src] is empty!"))
return
if(istype(M))
if(user.a_intent == INTENT_HARM)
var/R
- M.visible_message("[user] splashes the contents of [src] onto [M]!", \
- "[user] splashes the contents of [src] onto you!")
+ M.visible_message(span_danger("[user] splashes the contents of [src] onto [M]!"), span_userdanger("[user] splashes the contents of [src] onto you!"))
if(reagents)
for(var/datum/reagent/A in reagents.reagent_list)
R += "[A] ([num2text(A.volume)]),"
@@ -37,17 +36,15 @@
reagents.clear_reagents()
else
if(M != user)
- M.visible_message("[user] attempts to feed [M] something from [src].", \
- "[user] attempts to feed you something from [src].")
+ M.visible_message(span_danger("[user] attempts to feed [M] something from [src]."), span_userdanger("[user] attempts to feed you something from [src]."))
if(!do_mob(user, M))
return
if(!reagents || !reagents.total_volume)
return // The drink might be empty after the delay, such as by spam-feeding
- M.visible_message("[user] feeds [M] something from [src].", \
- "[user] feeds you something from [src].")
+ M.visible_message(span_danger("[user] feeds [M] something from [src]."), span_userdanger("[user] feeds you something from [src]."))
log_combat(user, M, "fed", reagents.log_list())
else
- to_chat(user, "You swallow a gulp of [src].")
+ to_chat(user, span_notice("You swallow a gulp of [src]."))
SEND_SIGNAL(src, COMSIG_GLASS_DRANK, M, user)
addtimer(CALLBACK(reagents, TYPE_PROC_REF(/datum/reagents, trans_to), M, 5, TRUE, TRUE, FALSE, user, FALSE, INGEST), 5)
playsound(M.loc,'sound/items/drink.ogg', rand(10,50), TRUE)
@@ -73,32 +70,31 @@
if(target.is_refillable()) //Something like a glass. Player probably wants to transfer TO it.
if(!reagents.total_volume)
- to_chat(user, "[src] is empty!")
+ to_chat(user, span_warning("[src] is empty!"))
return
if(target.reagents.holder_full())
- to_chat(user, "[target] is full.")
+ to_chat(user, span_warning("[target] is full."))
return
var/trans = reagents.trans_to(target, amount_per_transfer_from_this, transfered_by = user)
- to_chat(user, "You transfer [trans] unit\s of the solution to [target].")
+ to_chat(user, span_notice("You transfer [trans] unit\s of the solution to [target]."))
else if(target.is_drainable()) //A dispenser. Transfer FROM it TO us.
if(!target.reagents.total_volume)
- to_chat(user, "[target] is empty and can't be refilled!")
+ to_chat(user, span_warning("[target] is empty and can't be refilled!"))
return
if(reagents.holder_full())
- to_chat(user, "[src] is full.")
+ to_chat(user, span_warning("[src] is full."))
return
var/trans = target.reagents.trans_to(src, amount_per_transfer_from_this, transfered_by = user)
- to_chat(user, "You fill [src] with [trans] unit\s of the contents of [target].")
+ to_chat(user, span_notice("You fill [src] with [trans] unit\s of the contents of [target]."))
else if(reagents.total_volume)
if(user.a_intent == INTENT_HARM)
- user.visible_message("[user] splashes the contents of [src] onto [target]!", \
- "You splash the contents of [src] onto [target].")
+ user.visible_message(span_danger("[user] splashes the contents of [src] onto [target]!"), span_notice("You splash the contents of [src] onto [target]."))
reagents.expose(target, TOUCH)
reagents.clear_reagents()
@@ -106,15 +102,15 @@
var/hotness = I.get_temperature()
if(hotness && reagents)
reagents.expose_temperature(hotness)
- to_chat(user, "You heat [name] with [I]!")
+ to_chat(user, span_notice("You heat [name] with [I]!"))
if(istype(I, /obj/item/food/egg)) //breaking eggs
var/obj/item/food/egg/E = I
if(reagents)
if(reagents.total_volume >= reagents.maximum_volume)
- to_chat(user, "[src] is full.")
+ to_chat(user, span_notice("[src] is full."))
else
- to_chat(user, "You break [E] in [src].")
+ to_chat(user, span_notice("You break [E] in [src]."))
E.reagents.trans_to(src, E.reagents.total_volume, transfered_by = user)
qdel(E)
return
@@ -145,15 +141,104 @@
/obj/item/reagent_containers/glass/beaker/get_part_rating()
return reagents.maximum_volume
+/*
+* Jars
+* Apparently water bottles in drinks.dm have all this code for sealing
+* and unsealing themselves with a visible cap. But they also have this
+* junk code for flipping bottles. Im unsure how to feel about making
+* a duplicate of the code but without flipping.
+* -IP
+*/
/obj/item/reagent_containers/glass/beaker/jar
name = "jar"
- desc = "A jar for goops and powders. It can hold up to 40 units."
+ desc = "A jar for goops and powders. It can hold up to 50 units."
icon = 'icons/obj/chemical.dmi'
icon_state = "jar"
fill_icon_state = "jar"
volume = 50
- spillable = FALSE
- fill_icon_thresholds = list(0, 10, 30, 50)
+ var/cap_icon_state = "jar_top"
+ var/cap_on = TRUE
+ var/cap_lost = FALSE
+ var/mutable_appearance/cap_overlay
+ fill_icon_thresholds = list(0, 10, 25, 35, 50)
+
+/obj/item/reagent_containers/glass/beaker/jar/Initialize()
+ . = ..()
+ cap_overlay = mutable_appearance(icon, cap_icon_state)
+ if(cap_on)
+ spillable = FALSE
+ update_icon()
+
+/obj/item/reagent_containers/glass/beaker/jar/update_overlays()
+ . = ..()
+ if(cap_on)
+ . += cap_overlay
+
+/obj/item/reagent_containers/glass/beaker/jar/examine(mob/user)
+ . = ..()
+ if(cap_lost)
+ . += span_notice("The cap seems to be missing.")
+ else if(cap_on)
+ . += span_notice("The cap is firmly on to prevent spilling. Alt-click to remove the cap.")
+ else
+ . += span_notice("The cap has been taken off. Alt-click to put a cap on.")
+
+/obj/item/reagent_containers/glass/beaker/jar/AltClick(mob/user)
+ . = ..()
+ if(cap_lost)
+ to_chat(user, span_warning("The cap seems to be missing! Where did it go?"))
+ return
+
+ var/fumbled = HAS_TRAIT(user, TRAIT_CLUMSY) && prob(5)
+ if(cap_on || fumbled)
+ cap_on = FALSE
+ spillable = TRUE
+ animate(src, transform = null, time = 2, loop = 0)
+ if(fumbled)
+ to_chat(user, span_warning("You fumble with [src]'s cap! The cap falls onto the ground and simply vanishes. Where the hell did it go?"))
+ cap_lost = TRUE
+ else
+ to_chat(user, span_notice("You remove the cap from [src]."))
+ else
+ cap_on = TRUE
+ spillable = FALSE
+ to_chat(user, span_notice("You put the cap on [src]."))
+ update_icon()
+
+/obj/item/reagent_containers/glass/beaker/jar/is_refillable()
+ if(cap_on)
+ return FALSE
+ return ..()
+
+/obj/item/reagent_containers/glass/beaker/jar/is_drainable()
+ if(cap_on)
+ return FALSE
+ return ..()
+
+/obj/item/reagent_containers/glass/beaker/jar/attack(mob/target, mob/user, def_zone)
+ if(!target)
+ return
+
+ if(user.a_intent != INTENT_HARM)
+ if(cap_on && reagents.total_volume && istype(target))
+ to_chat(user, span_warning("You must remove the cap before you can do that!"))
+ return
+
+ return ..()
+
+ if(!cap_on)
+ SplashReagents(target)
+
+/obj/item/reagent_containers/glass/beaker/jar/afterattack(obj/target, mob/user, proximity)
+ if(cap_on && (target.is_refillable() || target.is_drainable() || (reagents.total_volume && user.a_intent == INTENT_HARM)))
+ to_chat(user, span_warning("You must remove the cap before you can do that!"))
+ return
+
+ else if(istype(target, /obj/item/reagent_containers/glass/beaker/jar))
+ var/obj/item/reagent_containers/glass/beaker/jar/WB = target
+ if(WB.cap_on)
+ to_chat(user, span_warning("[WB] has a cap firmly twisted on!"))
+ return ..()
/obj/item/reagent_containers/glass/beaker/jar/pudding
name = "jar of pudding"
@@ -305,13 +390,13 @@
/obj/item/reagent_containers/glass/bucket/attackby(obj/O, mob/user, params)
if(istype(O, /obj/item/mop))
if(reagents.total_volume < 1)
- to_chat(user, "[src] is out of water!")
+ to_chat(user, span_warning("[src] is out of water!"))
else
reagents.trans_to(O, 5, transfered_by = user)
- to_chat(user, "You wet [O] in [src].")
+ to_chat(user, span_notice("You wet [O] in [src]."))
playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE)
else if(isprox(O)) //This works with wooden buckets for now. Somewhat unintended, but maybe someone will add sprites for it soon(TM)
- to_chat(user, "You add [O] to [src].")
+ to_chat(user, span_notice("You add [O] to [src]."))
qdel(O)
qdel(src)
user.put_in_hands(new /obj/item/bot_assembly/cleanbot)
@@ -322,7 +407,7 @@
..()
if (slot == ITEM_SLOT_HEAD)
if(reagents.total_volume)
- to_chat(user, "[src]'s contents spill all over you!")
+ to_chat(user, span_userdanger("[src]'s contents spill all over you!"))
reagents.expose(user, TOUCH)
reagents.clear_reagents()
reagents.flags = NONE
@@ -364,43 +449,43 @@
if(grinded)
grinded.forceMove(drop_location())
grinded = null
- to_chat(user, "You eject the item inside.")
+ to_chat(user, span_notice("You eject the item inside."))
/obj/item/reagent_containers/glass/mortar/attackby(obj/item/I, mob/living/carbon/human/user)
..()
if(istype(I,/obj/item/pestle))
if(grinded)
if(user.getStaminaLoss() > 50)
- to_chat(user, "You are too tired to work!")
+ to_chat(user, span_warning("You are too tired to work!"))
return
- to_chat(user, "You start grinding...")
+ to_chat(user, span_notice("You start grinding..."))
if((do_after(user, 25, target = src)) && grinded)
user.adjustStaminaLoss(40)
if(grinded.juice_results) //prioritize juicing
grinded.on_juice()
reagents.add_reagent_list(grinded.juice_results)
- to_chat(user, "You juice [grinded] into a fine liquid.")
+ to_chat(user, span_notice("You juice [grinded] into a fine liquid."))
QDEL_NULL(grinded)
return
grinded.on_grind()
reagents.add_reagent_list(grinded.grind_results)
if(grinded.reagents) //food and pills
grinded.reagents.trans_to(src, grinded.reagents.total_volume, transfered_by = user)
- to_chat(user, "You break [grinded] into powder.")
+ to_chat(user, span_notice("You break [grinded] into powder."))
QDEL_NULL(grinded)
return
return
else
- to_chat(user, "There is nothing to grind!")
+ to_chat(user, span_warning("There is nothing to grind!"))
return
if(grinded)
- to_chat(user, "There is something inside already!")
+ to_chat(user, span_warning("There is something inside already!"))
return
if(I.juice_results || I.grind_results)
I.forceMove(src)
grinded = I
return
- to_chat(user, "You can't grind this!")
+ to_chat(user, span_warning("You can't grind this!"))
/obj/item/reagent_containers/glass/saline
name = "saline canister"
diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi
index 266dcf63e68e..9c62b88a007c 100644
Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ
diff --git a/icons/obj/reagentfillings.dmi b/icons/obj/reagentfillings.dmi
index 51bc7977be0f..e52059175237 100644
Binary files a/icons/obj/reagentfillings.dmi and b/icons/obj/reagentfillings.dmi differ