Merge branch 'master' of https://github.com/tgstation/tgstation into upstream-sync

This commit is contained in:
xPokee
2025-10-03 07:05:54 -04:00
517 changed files with 24339 additions and 21253 deletions
@@ -4,8 +4,7 @@
amount_per_transfer_from_this = 10
possible_transfer_amounts = list(5, 10, 15, 20, 25, 30, 50)
volume = 50
reagent_flags = OPENCONTAINER | DUNKABLE
spillable = TRUE
initial_reagent_flags = OPENCONTAINER | DUNKABLE
resistance_flags = ACID_PROOF
icon_state = "bottle"
lefthand_file = 'icons/mob/inhands/items/drinks_lefthand.dmi'
@@ -66,33 +65,28 @@
gourmand.adjust_disgust(-5 + -2.5 * fraction)
gourmand.add_mood_event("fav_food", /datum/mood_event/favorite_food)
/obj/item/reagent_containers/cup/attack(mob/living/target_mob, mob/living/user, obj/target)
/obj/item/reagent_containers/cup/proc/try_drink(mob/living/target_mob, mob/living/user)
if(!canconsume(target_mob, user))
return
if(!spillable)
return
if(!reagents || !reagents.total_volume)
to_chat(user, span_warning("[src] is empty!"))
return
if(!istype(target_mob))
return
return ITEM_INTERACT_BLOCKING
if(target_mob != user)
target_mob.visible_message(span_danger("[user] attempts to feed [target_mob] something from [src]."), \
span_userdanger("[user] attempts to feed you something from [src]."))
target_mob.visible_message(
span_danger("[user] attempts to feed [target_mob] something from [src]."),
span_userdanger("[user] attempts to feed you something from [src]."),
)
if(!do_after(user, 3 SECONDS, target_mob))
return
return ITEM_INTERACT_BLOCKING
if(!reagents || !reagents.total_volume)
return // The drink might be empty after the delay, such as by spam-feeding
target_mob.visible_message(span_danger("[user] feeds [target_mob] something from [src]."), \
span_userdanger("[user] feeds you something from [src]."))
return ITEM_INTERACT_BLOCKING // The drink might be empty after the delay, such as by spam-feeding
target_mob.visible_message(
span_danger("[user] feeds [target_mob] something from [src]."),
span_userdanger("[user] feeds you something from [src]."),
)
log_combat(user, target_mob, "fed", reagents.get_reagent_log_string())
else
to_chat(user, span_notice("You swallow a gulp of [src]."))
. = ITEM_INTERACT_SUCCESS
SEND_SIGNAL(src, COMSIG_GLASS_DRANK, target_mob, user)
SEND_SIGNAL(target_mob, COMSIG_GLASS_DRANK, src, user) // SKYRAT EDIT ADDITION - Hemophages can't casually drink what's not going to regenerate their blood
var/fraction = min(gulp_size/reagents.total_volume, 1)
@@ -100,119 +94,62 @@
checkLiked(fraction, target_mob)
playsound(target_mob.loc, consumption_sound, rand(10,50), TRUE)
if(!iscarbon(target_mob))
return
return .
var/mob/living/carbon/carbon_drinker = target_mob
var/list/diseases = carbon_drinker.get_static_viruses()
if(!LAZYLEN(diseases))
return
return .
var/list/datum/disease/diseases_to_add = list()
for(var/datum/disease/malady as anything in diseases)
if(malady.spread_flags & DISEASE_SPREAD_CONTACT_FLUIDS)
diseases_to_add += malady
if(LAZYLEN(diseases_to_add))
AddComponent(/datum/component/infective, diseases_to_add)
return .
/obj/item/reagent_containers/cup/interact_with_atom(atom/target, mob/living/user, list/modifiers)
if(!check_allowed_items(target, target_self = TRUE))
return NONE
if(!spillable)
. = ..()
if(. & ITEM_INTERACT_ANY_BLOCKER)
return .
if(!is_open_container())
return NONE
if(target.is_refillable()) //Something like a glass. Player probably wants to transfer TO it.
if(!reagents.total_volume)
to_chat(user, span_warning("[src] is empty!"))
return ITEM_INTERACT_BLOCKING
if(target.reagents.holder_full())
to_chat(user, span_warning("[target] is full."))
return ITEM_INTERACT_BLOCKING
var/trans = round(reagents.trans_to(target, amount_per_transfer_from_this, transferred_by = user), CHEMICAL_VOLUME_ROUNDING)
playsound(target.loc, SFX_LIQUID_POUR, 50, TRUE)
to_chat(user, span_notice("You transfer [trans] unit\s of the solution to [target]."))
SEND_SIGNAL(src, COMSIG_REAGENTS_CUP_TRANSFER_TO, target)
target.update_appearance()
return ITEM_INTERACT_SUCCESS
return try_refill(target, user)
if(target.is_drainable()) //A dispenser. Transfer FROM it TO us.
if(!target.reagents.total_volume)
to_chat(user, span_warning("[target] is empty and can't be refilled!"))
return ITEM_INTERACT_BLOCKING
return try_drain(target, user)
if(reagents.holder_full())
to_chat(user, span_warning("[src] is full."))
return ITEM_INTERACT_BLOCKING
var/trans = round(target.reagents.trans_to(src, amount_per_transfer_from_this, transferred_by = user), CHEMICAL_VOLUME_ROUNDING)
playsound(target.loc, SFX_LIQUID_POUR, 50, TRUE)
to_chat(user, span_notice("You fill [src] with [trans] unit\s of the contents of [target]."))
SEND_SIGNAL(src, COMSIG_REAGENTS_CUP_TRANSFER_FROM, target)
target.update_appearance()
return ITEM_INTERACT_SUCCESS
if(isliving(target))
return try_drink(target, user)
return NONE
/obj/item/reagent_containers/cup/interact_with_atom_secondary(atom/target, mob/living/user, list/modifiers)
if(user.combat_mode)
return NONE
if(!check_allowed_items(target, target_self = TRUE))
return NONE
if(!spillable)
. = ..()
if(. & ITEM_INTERACT_ANY_BLOCKER)
return .
if(!is_open_container())
return NONE
if(target.is_drainable()) //A dispenser. Transfer FROM it TO us.
if(!target.reagents.total_volume)
to_chat(user, span_warning("[target] is empty!"))
return ITEM_INTERACT_BLOCKING
if(reagents.holder_full())
to_chat(user, span_warning("[src] is full."))
return ITEM_INTERACT_BLOCKING
var/trans = round(target.reagents.trans_to(src, amount_per_transfer_from_this, transferred_by = user), CHEMICAL_VOLUME_ROUNDING)
playsound(target.loc, SFX_LIQUID_POUR, 50, TRUE)
to_chat(user, span_notice("You fill [src] with [trans] unit\s of the contents of [target]."))
SEND_SIGNAL(src, COMSIG_REAGENTS_CUP_TRANSFER_FROM, target)
target.update_appearance()
return ITEM_INTERACT_SUCCESS
return try_drain(target, user)
return NONE
/obj/item/reagent_containers/cup/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
var/hotness = attacking_item.get_temperature()
if(hotness && reagents)
reagents.expose_temperature(hotness)
to_chat(user, span_notice("You heat [name] with [attacking_item]!"))
return TRUE
//Cooling method
if(istype(attacking_item, /obj/item/extinguisher))
var/obj/item/extinguisher/extinguisher = attacking_item
if(extinguisher.safety)
return TRUE
if (extinguisher.reagents.total_volume < 1)
to_chat(user, span_warning("\The [extinguisher] is empty!"))
return TRUE
var/cooling = (0 - reagents.chem_temp) * extinguisher.cooling_power * 2
reagents.expose_temperature(cooling)
to_chat(user, span_notice("You cool \the [src] with the [attacking_item]!"))
playsound(loc, 'sound/effects/extinguish.ogg', 75, TRUE, -3)
extinguisher.reagents.remove_all(1)
return TRUE
if(istype(attacking_item, /obj/item/food/egg)) //breaking eggs
var/obj/item/food/egg/attacking_egg = attacking_item
if(!reagents)
return TRUE
/obj/item/reagent_containers/cup/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!is_open_container())
return NONE
if(istype(tool, /obj/item/food/egg)) //breaking eggs
if(reagents.holder_full())
to_chat(user, span_notice("[src] is full."))
else
to_chat(user, span_notice("You break [attacking_egg] in [src]."))
attacking_egg.reagents.trans_to(src, attacking_egg.reagents.total_volume, transferred_by = user)
qdel(attacking_egg)
return TRUE
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You break [tool] in [src]."))
tool.reagents.trans_to(src, tool.reagents.total_volume, transferred_by = user)
qdel(tool)
return ITEM_INTERACT_SUCCESS
return ..()
return NONE
/*
* On accidental consumption, make sure the container is partially glass, and continue to the reagent_container proc
@@ -296,7 +233,7 @@
reactions. Can hold up to 50 units."
icon_state = "beakernoreact"
custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT * 1.5)
reagent_flags = OPENCONTAINER | NO_REACT
initial_reagent_flags = OPENCONTAINER | NO_REACT
volume = 50
amount_per_transfer_from_this = 10
@@ -411,21 +348,21 @@
melee = 10
acid = 50
/obj/item/reagent_containers/cup/bucket/attackby(obj/O, mob/user, list/modifiers, list/attack_modifiers)
if(istype(O, /obj/item/mop))
/obj/item/reagent_containers/cup/bucket/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(istype(tool, /obj/item/mop))
if(reagents.total_volume < 1)
user.balloon_alert(user, "empty!")
else
reagents.trans_to(O, 5, transferred_by = user)
user.balloon_alert(user, "doused [O]")
playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE)
return
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, span_notice("You add [O] to [src]."))
qdel(O)
return ITEM_INTERACT_BLOCKING
reagents.trans_to(tool, 5, transferred_by = user)
user.balloon_alert(user, "doused [tool]")
playsound(src, 'sound/effects/slosh.ogg', 25, TRUE)
return ITEM_INTERACT_SUCCESS
if(isprox(tool)) //This works with wooden buckets for now. Somewhat unintended, but maybe someone will add sprites for it soon(TM)
to_chat(user, span_notice("You add [tool] to [src]."))
qdel(tool)
var/obj/item/bot_assembly/cleanbot/new_cleanbot_ass = new(null, src)
user.put_in_hands(new_cleanbot_ass)
return
return ITEM_INTERACT_SUCCESS
return ..()
@@ -453,11 +390,11 @@
to_chat(user, span_userdanger("[src]'s contents spill all over you!"))
reagents.expose(user, TOUCH)
reagents.clear_reagents()
reagents.flags = NONE
update_container_flags(NONE)
/obj/item/reagent_containers/cup/bucket/dropped(mob/user)
. = ..()
reagents.flags = initial(reagent_flags)
reset_container_flags()
/obj/item/reagent_containers/cup/bucket/equip_to_best_slot(mob/M)
if(reagents.total_volume) //If there is water in a bucket, don't quick equip it to the head
@@ -486,8 +423,7 @@
volume = 100
custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT)
resistance_flags = FLAMMABLE
reagent_flags = OPENCONTAINER
spillable = TRUE
initial_reagent_flags = OPENCONTAINER
var/obj/item/grinded
/obj/item/reagent_containers/cup/mortar/click_alt(mob/user)
@@ -498,43 +434,45 @@
balloon_alert(user, "ejected")
return CLICK_ACTION_SUCCESS
/obj/item/reagent_containers/cup/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, span_warning("You are too tired to work!"))
return
var/list/choose_options = list(
"Grind" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_grind"),
"Juice" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_juice")
)
var/picked_option = show_radial_menu(user, src, choose_options, radius = 38, require_near = TRUE)
if(grinded && in_range(src, user) && user.is_holding(I) && picked_option)
to_chat(user, span_notice("You start grinding..."))
if(do_after(user, 2.5 SECONDS, target = src))
user.adjustStaminaLoss(40)
switch(picked_option)
if("Juice")
return juice_item(grinded, user)
if("Grind")
return grind_item(grinded, user)
else
to_chat(user, span_notice("You try to grind the mortar itself instead of [grinded]. You failed."))
return
return
else
/obj/item/reagent_containers/cup/mortar/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
. = ..()
if(. & ITEM_INTERACT_ANY_BLOCKER)
return .
if(istype(tool, /obj/item/pestle))
if(!grinded)
to_chat(user, span_warning("There is nothing to grind!"))
return
return ITEM_INTERACT_BLOCKING
if(user.getStaminaLoss() > 50)
to_chat(user, span_warning("You are too tired to work!"))
return ITEM_INTERACT_BLOCKING
var/list/choose_options = list(
"Grind" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_grind"),
"Juice" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_juice")
)
var/picked_option = show_radial_menu(user, src, choose_options, radius = 38, require_near = TRUE)
if(!grinded || !in_range(src, user) || !user.is_holding(tool) || !picked_option)
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You start grinding..."))
if(!do_after(user, 2.5 SECONDS, target = src))
return ITEM_INTERACT_BLOCKING
user.adjustStaminaLoss(40)
switch(picked_option)
if("Juice")
return juice_item(grinded, user) ? ITEM_INTERACT_BLOCKING : ITEM_INTERACT_SUCCESS
if("Grind")
return grind_item(grinded, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You try to grind the mortar itself instead of [grinded]. You failed."))
return ITEM_INTERACT_BLOCKING
if(grinded)
to_chat(user, span_warning("There is something inside already!"))
return
if(!I.blend_requirements(src))
return ITEM_INTERACT_BLOCKING
if(!tool.blend_requirements(src))
to_chat(user, span_warning("Cannot grind this!"))
return
if(length(I.grind_results) || I.reagents?.total_volume)
I.forceMove(src)
grinded = I
return ITEM_INTERACT_BLOCKING
if((length(tool.grind_results) || tool.reagents?.total_volume) && user.transferItemToLoc(tool, src))
grinded = tool
return ITEM_INTERACT_SUCCESS
return NONE
/obj/item/reagent_containers/cup/mortar/blended(obj/item/blended_item, grinded)
src.grinded = null
@@ -499,13 +499,12 @@
fill_icon_thresholds = list(0, 20, 40, 60, 80, 100)
possible_transfer_amounts = list(5, 10)
amount_per_transfer_from_this = 5
spillable = FALSE
/// Do we currently have our pump cap on?
var/cap_on = TRUE
/obj/item/reagent_containers/cup/bottle/syrup_bottle/Initialize(mapload)
. = ..()
register_context()
// this is not done via initial_reagent_flags because it represents state
update_container_flags(SEALED_CONTAINER | TRANSPARENT)
/obj/item/reagent_containers/cup/bottle/syrup_bottle/examine(mob/user)
. = ..()
@@ -515,10 +514,10 @@
/obj/item/reagent_containers/cup/bottle/syrup_bottle/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
. = ..()
context[SCREENTIP_CONTEXT_ALT_LMB] = (cap_on ? "Remove Pump Cap" : "Add Pump Cap")
context[SCREENTIP_CONTEXT_ALT_LMB] = (is_open_container() ? "Add Pump Cap" : "Remove Pump Cap")
if(IS_WRITING_UTENSIL(held_item))
context[SCREENTIP_CONTEXT_LMB] = "Write Label"
else if(cap_on && held_item?.is_refillable())
else if(is_open_container() && held_item?.is_refillable())
context[SCREENTIP_CONTEXT_LMB] = "Use Pump"
return CONTEXTUAL_SCREENTIP_SET
@@ -527,7 +526,7 @@
/obj/item/reagent_containers/cup/bottle/syrup_bottle/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(IS_WRITING_UTENSIL(tool))
return writing_utensil_act(user, tool)
if(cap_on && tool.is_refillable())
if(is_open_container() && tool.is_refillable())
return refillable_act(user, tool)
return ..()
@@ -561,18 +560,22 @@
update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/item/reagent_containers/cup/bottle/syrup_bottle/click_alt(mob/user)
cap_on = !cap_on
if(cap_on)
icon_state = "syrup"
spillable = FALSE
balloon_alert(user, "put pump cap on")
else
/obj/item/reagent_containers/cup/bottle/syrup_bottle/update_icon_state()
. = ..()
if(is_open_container())
icon_state = "syrup_open"
spillable = TRUE
balloon_alert(user, "removed pump cap")
else
icon_state = "syrup"
update_icon_state()
/obj/item/reagent_containers/cup/bottle/syrup_bottle/click_alt(mob/user)
if(is_open_container())
balloon_alert(user, "put pump cap on")
update_container_flags(SEALED_CONTAINER | TRANSPARENT)
else
balloon_alert(user, "removed pump cap")
reset_container_flags()
update_appearance()
return CLICK_ACTION_SUCCESS
//types of syrups
@@ -9,7 +9,6 @@
volume = 50
custom_materials = list(/datum/material/glass=SMALL_MATERIAL_AMOUNT*5)
max_integrity = 20
spillable = TRUE
resistance_flags = ACID_PROOF
obj_flags = UNIQUE_RENAME
drop_sound = 'sound/items/handling/drinkglass_drop.ogg'
@@ -11,7 +11,8 @@
resistance_flags = NONE
isGlass = TRUE
attack_verb_continuous = list("smashes", "bashes")
attack_verb_simple = list("smash", "bash")
/obj/item/reagent_containers/cup/glass/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum, do_splash = TRUE)
. = ..()
@@ -26,7 +27,7 @@
return
if(bartender_check(target, thrower) && throwingdatum)
return
SplashReagents(target, throwingdatum, override_spillable = TRUE)
splash_reagents(target, thrower || throwingdatum?.get_thrower(), allow_closed_splash = TRUE)
var/obj/item/broken_bottle/B = new (loc)
B.mimic_broken(src, target, break_top)
qdel(src)
@@ -55,7 +56,6 @@
has_variable_transfer_amount = FALSE
volume = 5
obj_flags = CONDUCTS_ELECTRICITY
spillable = TRUE
resistance_flags = FIRE_PROOF
isGlass = FALSE
@@ -109,7 +109,6 @@
icon_state = "coffee"
base_icon_state = "coffee"
list_reagents = list(/datum/reagent/consumable/coffee = 30)
spillable = TRUE
resistance_flags = FREEZE_PROOF
isGlass = FALSE
drink_type = BREAKFAST
@@ -154,7 +153,6 @@
custom_price = PAYCHECK_LOWER * 0.6
icon_state = "icecup"
list_reagents = list(/datum/reagent/consumable/ice = 30)
spillable = TRUE
isGlass = FALSE
/obj/item/reagent_containers/cup/glass/ice/prison
@@ -169,7 +167,6 @@
icon_state = "tea_empty"
base_icon_state = "tea"
inhand_icon_state = "coffee"
spillable = TRUE
/obj/item/reagent_containers/cup/glass/mug/update_icon_state()
icon_state = "[base_icon_state][reagents.total_volume ? null : "_empty"]"
@@ -204,7 +201,6 @@
base_icon_state = "coffee_cup"
possible_transfer_amounts = list(10)
volume = 30
spillable = TRUE
isGlass = FALSE
/obj/item/reagent_containers/cup/glass/coffee_cup/update_icon_state()
@@ -235,7 +231,7 @@
// The 2 bottles have separate cap overlay icons because if the bottle falls over while bottle flipping the cap stays fucked on the moved overlay
var/cap_icon = 'icons/obj/drinks/drink_effects.dmi'
var/cap_icon_state = "bottle_cap_small"
var/cap_on = TRUE
var/start_capped = TRUE
var/cap_lost = FALSE
var/mutable_appearance/cap_overlay
var/flip_chance = 10
@@ -245,20 +241,21 @@
/obj/item/reagent_containers/cup/glass/waterbottle/Initialize(mapload)
cap_overlay = mutable_appearance(cap_icon, cap_icon_state)
. = ..()
if(cap_on)
spillable = FALSE
if(start_capped)
// this is not done via initial_reagent_flags because it represents state
update_container_flags(SEALED_CONTAINER | TRANSPARENT)
update_appearance()
/obj/item/reagent_containers/cup/glass/waterbottle/update_overlays()
. = ..()
if(cap_on)
if(!is_open_container())
. += cap_overlay
/obj/item/reagent_containers/cup/glass/waterbottle/examine(mob/user)
. = ..()
if(cap_lost)
. += span_notice("The cap seems to be missing.")
else if(cap_on)
else if(!is_open_container())
. += 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.")
@@ -269,9 +266,8 @@
return CLICK_ACTION_BLOCKING
var/fumbled = HAS_TRAIT(user, TRAIT_CLUMSY) && prob(5)
if(cap_on || fumbled)
cap_on = FALSE
spillable = TRUE
if(!is_open_container() || fumbled)
reset_container_flags()
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?"))
@@ -280,52 +276,18 @@
to_chat(user, span_notice("You remove the cap from [src]."))
playsound(loc, 'sound/items/handling/reagent_containers/plastic_bottle/bottle_cap_open.ogg', 50, TRUE)
else
cap_on = TRUE
spillable = FALSE
update_container_flags(SEALED_CONTAINER | TRANSPARENT)
to_chat(user, span_notice("You put the cap on [src]."))
playsound(loc, 'sound/items/handling/reagent_containers/plastic_bottle/bottle_cap_close.ogg', 50, TRUE)
update_appearance()
return CLICK_ACTION_SUCCESS
/obj/item/reagent_containers/cup/glass/waterbottle/is_refillable()
if(cap_on)
return FALSE
return ..()
/obj/item/reagent_containers/cup/glass/waterbottle/is_drainable()
if(cap_on)
return FALSE
return ..()
/obj/item/reagent_containers/cup/glass/waterbottle/attack(mob/target, mob/living/user, def_zone)
if(!target)
return
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 ..()
/obj/item/reagent_containers/cup/glass/waterbottle/interact_with_atom(atom/target, mob/living/user, list/modifiers)
if(cap_on && (target.is_refillable() || target.is_drainable() || (reagents.total_volume && !user.combat_mode)))
to_chat(user, span_warning("You must remove the cap before you can do that!"))
return ITEM_INTERACT_BLOCKING
if(istype(target, /obj/item/reagent_containers/cup/glass/waterbottle))
var/obj/item/reagent_containers/cup/glass/waterbottle/other_bottle = target
if(other_bottle.cap_on)
to_chat(user, span_warning("[other_bottle] has a cap firmly twisted on!"))
return ITEM_INTERACT_BLOCKING
return ..()
// heehoo bottle flipping
/obj/item/reagent_containers/cup/glass/waterbottle/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
if(QDELETED(src))
return
if(!cap_on || !reagents.total_volume)
if(is_open_container() || !reagents.total_volume)
return
if(prob(flip_chance)) // landed upright
src.visible_message(span_notice("[src] lands upright!"))
@@ -341,7 +303,7 @@
/obj/item/reagent_containers/cup/glass/waterbottle/empty
list_reagents = list()
cap_on = FALSE
start_capped = FALSE
/obj/item/reagent_containers/cup/glass/waterbottle/large
desc = "A fresh commercial-sized bottle of water."
@@ -355,7 +317,7 @@
/obj/item/reagent_containers/cup/glass/waterbottle/large/empty
list_reagents = list()
cap_on = FALSE
start_capped = FALSE
// Admin spawn
/obj/item/reagent_containers/cup/glass/waterbottle/relic
@@ -378,7 +340,6 @@
icon_state = "water_cup_e"
possible_transfer_amounts = list(10)
volume = 10
spillable = TRUE
isGlass = FALSE
/obj/item/reagent_containers/cup/glass/sillycup/update_icon_state()
@@ -405,8 +366,8 @@
/obj/item/reagent_containers/cup/glass/bottle/juice/smallcarton/smash(atom/target, mob/thrower, datum/thrownthing/throwingdatum, break_top)
if(bartender_check(target, thrower) && throwingdatum)
return
SplashReagents(target, throwingdatum, override_spillable = TRUE)
var/obj/item/broken_bottle/bottle_shard = new (loc)
splash_reagents(target, thrower || throwingdatum?.get_thrower(), allow_closed_splash = TRUE)
var/obj/item/broken_bottle/bottle_shard = new(drop_location())
bottle_shard.mimic_broken(src, target)
qdel(src)
target.Bumped(bottle_shard)
@@ -560,4 +521,3 @@
icon_state = "britcup_empty"
base_icon_state = "britcup"
volume = 30
spillable = TRUE
@@ -128,8 +128,8 @@
/obj/item/reagent_containers/cup/glass/bottle/smash(mob/living/target, mob/thrower, datum/thrownthing/throwingdatum, break_top)
if(bartender_check(target, thrower) && throwingdatum)
return
SplashReagents(target, throwingdatum, override_spillable = TRUE)
return FALSE
splash_reagents(target, thrower || throwingdatum?.get_thrower(), allow_closed_splash = TRUE)
var/obj/item/broken_bottle/broken = new(drop_location())
if(!throwingdatum && thrower)
thrower.put_in_hands(broken)
@@ -140,70 +140,44 @@
qdel(src)
target.Bumped(broken)
return TRUE
/obj/item/reagent_containers/cup/glass/bottle/try_splash(mob/living/user, atom/target)
if(!target || !isliving(target))
return ..()
/obj/item/reagent_containers/cup/glass/bottle/try_splash(mob/user, atom/target)
if(!isGlass)
return ..()
return FALSE // instead of splashing, hit them with the bottle!
if(HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, span_warning("You don't want to harm [target]!"))
return FALSE
/obj/item/reagent_containers/cup/glass/bottle/afterattack(atom/target, mob/user, list/modifiers)
if(!isGlass)
return
var/mob/living/living_target = target
var/obj/item/bodypart/affecting = user.zone_selected //Find what the player is aiming at
var/head_hitter = user.zone_selected == BODY_ZONE_HEAD && isliving(target)
var/armor_block = 0 //Get the target's armor values for normal attack damage.
var/knockdown_effectiveness = 0 //The more force the bottle has, the longer the duration.
// An attack that targets the head of a living mob will attempt to knock them down
if(head_hitter)
var/mob/living/living_target = target
var/knockdown_effectiveness = 0
if(!HAS_TRAIT(target, TRAIT_HEAD_INJURY_BLOCKED))
knockdown_effectiveness = bottle_knockdown_duration + ((force / 10) * 1 SECONDS) - living_target.getarmor(BODY_ZONE_HEAD, MELEE)
if(prob(knockdown_effectiveness))
living_target.Knockdown(min(knockdown_effectiveness, 20 SECONDS))
//Calculating duration and calculating damage.
if(ishuman(target))
var/mob/living/carbon/human/H = target
var/headarmor = 0 // Target's head armor
armor_block = H.run_armor_check(affecting, MELEE, "", "", armour_penetration) // For normal attack damage
//If they have a hat/helmet and the user is targeting their head.
if(istype(H.head, /obj/item/clothing/head) && affecting == BODY_ZONE_HEAD)
headarmor = H.head.get_armor_rating(MELEE)
//Calculate the knockdown duration for the target.
knockdown_effectiveness = (bottle_knockdown_duration - headarmor) + force
// Displays a custom message which follows the attack
if(target == user)
target.visible_message(
span_warning("[user] smashes [src] [head_hitter ? "over [user.p_their()] head" : "against [user.p_them()]selves"]!"),
span_warning("You smash [src] [head_hitter ? "over your head" : "against yourself"]!"),
)
else
//Only humans can have armor, right?
armor_block = living_target.run_armor_check(affecting, MELEE)
if(affecting == BODY_ZONE_HEAD)
knockdown_effectiveness = bottle_knockdown_duration + force
//Apply the damage!
armor_block = min(90,armor_block)
living_target.apply_damage(force, BRUTE, affecting, armor_block)
target.visible_message(
span_warning("[user] smashes [src] [head_hitter ? "over [target]'s head" : "against [target]"]!"),
span_warning("[user] smashes [src] [head_hitter ? "over your head" : "against you"]!"),
)
// You are going to knock someone down for longer if they are not wearing a helmet.
var/head_attack_message = ""
if(affecting == BODY_ZONE_HEAD && iscarbon(target) && !HAS_TRAIT(target, TRAIT_HEAD_INJURY_BLOCKED))
head_attack_message = " on the head"
if(knockdown_effectiveness && prob(knockdown_effectiveness))
living_target.apply_effect(min(knockdown_effectiveness, 200) , EFFECT_KNOCKDOWN)
//Display an attack message.
if(target != user)
target.visible_message(span_danger("[user] hits [target][head_attack_message] with a bottle of [src.name]!"), \
span_userdanger("[user] hits you [head_attack_message] with a bottle of [src.name]!"))
else
target.visible_message(span_danger("[target] hits [target.p_them()]self with a bottle of [src.name][head_attack_message]!"), \
span_userdanger("You hit yourself with a bottle of [src.name][head_attack_message]!"))
//Attack logs
log_combat(user, target, "attacked", src)
//Finally, smash the bottle. This kills (del) the bottle.
// Finally, smash the bottle. This kills (del) the bottle and also does all the logging for us
smash(target, user)
return TRUE
/*
* Proc to make the bottle spill some of its contents out in a froth geyser of varying intensity/height
* Arguments:
@@ -679,8 +653,7 @@
desc = "Finely sourced from only the most pretentious French vineyards."
icon_state = "champagne_bottle"
base_icon_state = "champagne_bottle"
reagent_flags = TRANSPARENT
spillable = FALSE
initial_reagent_flags = TRANSPARENT
list_reagents = list(/datum/reagent/consumable/ethanol/champagne = 100)
drink_type = ALCOHOL
///Used for sabrage; increases the chance of success per 1 force of the attacking sharp item
@@ -695,33 +668,34 @@
sabrage_success_percentile = 0 //force of the sharp item used to sabrage will not increase success chance
/obj/item/reagent_containers/cup/glass/bottle/champagne/attack_self(mob/user)
if(spillable)
if(is_open_container())
return ..()
balloon_alert(user, "fiddling with cork...")
if(do_after(user, 1 SECONDS, src))
return pop_cork(user, sabrage = FALSE, froth_severity = pick(0, 1))
/obj/item/reagent_containers/cup/glass/bottle/champagne/attackby(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers)
/obj/item/reagent_containers/cup/glass/bottle/champagne/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
. = ..()
if(. & ITEM_INTERACT_ANY_BLOCKER)
return .
if(is_open_container())
return NONE
if(spillable)
return
if(tool.get_sharpness() != SHARP_EDGED)
return NONE
if(attacking_item.get_sharpness() != SHARP_EDGED)
return
if(attacking_item != user.get_active_held_item()) //no TK allowed
if(tool != user.get_active_held_item()) //no TK allowed
to_chat(user, span_userdanger("Such a feat is beyond your skills of telekinesis!"))
return
return ITEM_INTERACT_BLOCKING
if(attacking_item.force < 5)
if(tool.force < 5)
balloon_alert(user, "not strong enough!")
return
return ITEM_INTERACT_BLOCKING
playsound(user, 'sound/items/unsheath.ogg', 25, TRUE)
balloon_alert(user, "preparing to swing...")
if(!do_after(user, 2 SECONDS, src)) //takes longer because you are supposed to take the foil off the bottle first
return
return ITEM_INTERACT_BLOCKING
//The bonus to success chance that the user gets for being a command role
var/obj/item/organ/liver/liver = user.get_organ_slot(ORGAN_SLOT_LIVER)
@@ -730,7 +704,7 @@
//The bonus to success chance that the user gets for having a sabrage skillchip installed/otherwise having the trait through other means
var/skillchip_bonus = HAS_TRAIT(user, TRAIT_SABRAGE_PRO) ? 35 : 0
//calculate success chance. example: captain's sabre - 15 force = 75% chance
var/sabrage_chance = (attacking_item.force * sabrage_success_percentile) + command_bonus + skillchip_bonus
var/sabrage_chance = (tool.force * sabrage_success_percentile) + command_bonus + skillchip_bonus
if(prob(sabrage_chance))
///Severity of the resulting froth to pass to make_froth()
@@ -745,18 +719,18 @@
severity_to_pass = 2
if(67 to 99)
severity_to_pass = 1
return pop_cork(user, sabrage = TRUE, froth_severity = severity_to_pass)
else //you dun goofed
user.visible_message(
span_danger("[user] fumbles the sabrage and cuts [src] in half, spilling it over themselves!"),
span_danger("You fail your stunt and cut [src] in half, spilling it over you!"),
)
user.add_mood_event("sabrage_fail", /datum/mood_event/sabrage_fail)
return smash(target = user, break_top = TRUE)
return pop_cork(user, sabrage = TRUE, froth_severity = severity_to_pass) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
user.visible_message(
span_danger("[user] fumbles the sabrage and cuts [src] in half, spilling it over themselves!"),
span_danger("You fail your stunt and cut [src] in half, spilling it over you!"),
)
user.add_mood_event("sabrage_fail", /datum/mood_event/sabrage_fail)
return smash(target = user, break_top = TRUE) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
/obj/item/reagent_containers/cup/glass/bottle/champagne/update_icon_state()
. = ..()
if(spillable)
if(is_open_container())
if(sabraged)
icon_state = "[base_icon_state]_sabrage"
else
@@ -783,9 +757,8 @@
continue
stunt_witness.add_mood_event("sabrage_witness", /datum/mood_event/sabrage_witness)
reagents.flags |= OPENCONTAINER
add_container_flags(OPENCONTAINER)
playsound(src, 'sound/items/champagne_pop.ogg', 70, TRUE)
spillable = TRUE
update_appearance()
make_froth(offset_x = 0, offset_y = sabraged ? 13 : 15, intensity = froth_severity) //the y offset for sabraged is lower because the bottle's lip is smashed
///Type of cork to fire away
@@ -795,6 +768,7 @@
popped_cork.firer = user
popped_cork.fired_from = src
popped_cork.fire(dir2angle(user.dir) + rand(-30, 30))
return TRUE
/obj/projectile/bullet/champagne_cork
name = "champagne cork"
@@ -946,15 +920,17 @@
target.fire_act()
new /obj/effect/hotspot(get_turf(target))
/obj/item/reagent_containers/cup/glass/bottle/molotov/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
if(I.get_temperature() && !active)
active = TRUE
log_bomber(user, "has primed a", src, "for detonation")
/obj/item/reagent_containers/cup/glass/bottle/molotov/item_interaction(mob/living/user, obj/item/item, list/modifiers)
if(!item.get_temperature() || active)
return NONE
active = TRUE
log_bomber(user, "has primed a", src, "for detonation")
to_chat(user, span_info("You light [src] on fire."))
add_overlay(custom_fire_overlay() || GLOB.fire_overlay)
if(!isGlass)
addtimer(CALLBACK(src, PROC_REF(explode)), 5 SECONDS)
to_chat(user, span_info("You light [src] on fire."))
add_overlay(custom_fire_overlay() || GLOB.fire_overlay)
if(!isGlass)
addtimer(CALLBACK(src, PROC_REF(explode)), 5 SECONDS)
return ITEM_INTERACT_SUCCESS
/obj/item/reagent_containers/cup/glass/bottle/molotov/proc/explode()
if(!active)
@@ -964,7 +940,7 @@
for(var/i in 1 to 2)
if(istype(target, /obj/item/storage))
target = target.loc
SplashReagents(target, override_spillable = TRUE)
splash_reagents(target, allow_closed_splash = TRUE)
target.fire_act()
qdel(src)
@@ -4,8 +4,7 @@
icon = 'icons/obj/devices/mauna_mug.dmi'
icon_state = "maunamug"
base_icon_state = "maunamug"
spillable = TRUE
reagent_flags = OPENCONTAINER
initial_reagent_flags = OPENCONTAINER
fill_icon_state = "maunafilling"
fill_icon_thresholds = list(25)
var/obj/item/stock_parts/power_store/cell
@@ -73,21 +72,21 @@
to_chat(user, span_notice("You screw the battery case on [src] [open ? "open" : "closed"] ."))
update_appearance()
/obj/item/reagent_containers/cup/maunamug/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
add_fingerprint(user)
if(!istype(I, /obj/item/stock_parts/power_store/cell))
/obj/item/reagent_containers/cup/maunamug/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/stock_parts/power_store/cell))
return ..()
if(!open)
to_chat(user, span_warning("The battery case must be open to insert a power cell!"))
return FALSE
return ITEM_INTERACT_BLOCKING
if(cell)
to_chat(user, span_warning("There is already a power cell inside!"))
return FALSE
else if(!user.transferItemToLoc(I, src))
return
cell = I
return ITEM_INTERACT_BLOCKING
else if(!user.transferItemToLoc(tool, src))
return ITEM_INTERACT_BLOCKING
cell = tool
user.visible_message(span_notice("[user] inserts a power cell into [src]."), span_notice("You insert the power cell into [src]."))
update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/item/reagent_containers/cup/maunamug/attack_hand(mob/living/user, list/modifiers)
if(cell && open)
@@ -43,12 +43,11 @@
// Clicking on the jar with an organ lets you put the organ inside, if there isn't one already
// Otherwise it should act like a normal bottle
/obj/item/reagent_containers/cup/beaker/organ_jar/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
. = ..()
if(!istype(tool, /obj/item/organ))
return
return ..()
if(held_organ)
balloon_alert(user, "the jar already contains [held_organ]")
return ITEM_INTERACT_BLOCKING
return ITEM_INTERACT_BLOCKING
if(!user.transferItemToLoc(tool, src))
return ITEM_INTERACT_BLOCKING
@@ -12,8 +12,7 @@
icon_state = "cola"
icon_state_preview = "cola"
abstract_type = /obj/item/reagent_containers/cup/soda_cans
reagent_flags = NONE
spillable = FALSE
initial_reagent_flags = NONE
custom_price = PAYCHECK_CREW * 0.9
obj_flags = CAN_BE_HIT
possible_transfer_amounts = list(5, 10, 15, 25, 30)
@@ -64,17 +63,24 @@
sleep(2 SECONDS) //dramatic pause
return TOXLOSS
/obj/item/reagent_containers/cup/soda_cans/attack(mob/M, mob/living/user)
if(iscarbon(M) && !reagents.total_volume && user.combat_mode && user.zone_selected == BODY_ZONE_HEAD)
if(M == user)
user.visible_message(span_warning("[user] crushes the can of [src] on [user.p_their()] forehead!"), span_notice("You crush the can of [src] on your forehead."))
/obj/item/reagent_containers/cup/soda_cans/interact_with_atom(atom/target, mob/living/user, list/modifiers)
if(iscarbon(target) && !reagents.total_volume && user.combat_mode && user.zone_selected == BODY_ZONE_HEAD)
if(target == user)
user.visible_message(
span_warning("[user] crushes the can of [src] on [user.p_their()] forehead!"),
span_notice("You crush the can of [src] on your forehead."),
)
else
user.visible_message(span_warning("[user] crushes the can of [src] on [M]'s forehead!"), span_notice("You crush the can of [src] on [M]'s forehead."))
playsound(M,'sound/items/weapons/pierce.ogg', rand(10,50), TRUE)
var/obj/item/trash/can/crushed_can = new /obj/item/trash/can(M.loc)
user.visible_message(
span_warning("[user] crushes the can of [src] on [target]'s forehead!"),
span_notice("You crush the can of [src] on [target]'s forehead."),
)
playsound(src, 'sound/items/weapons/pierce.ogg', rand(10, 50), TRUE)
var/obj/item/trash/can/crushed_can = new /obj/item/trash/can(target.drop_location())
crushed_can.icon_state = icon_state
qdel(src)
return TRUE
return ITEM_INTERACT_SUCCESS
return ..()
/obj/item/reagent_containers/cup/soda_cans/bullet_act(obj/projectile/proj)
@@ -96,9 +102,8 @@
return
to_chat(user, "You pull back the tab of [src] with a satisfying pop.") //Ahhhhhhhh
reagents.flags |= OPENCONTAINER
add_container_flags(OPENCONTAINER)
playsound(src, SFX_CAN_OPEN, 50, TRUE)
spillable = TRUE
throwforce = 0
/**
@@ -122,15 +127,14 @@
playsound(src, 'sound/items/can/can_pop.ogg', 80, TRUE)
if(!hide_message)
visible_message(span_danger("[src] spills over, fizzing its contents all over [target]!"))
spillable = TRUE
reagents.flags |= OPENCONTAINER
add_container_flags(OPENCONTAINER)
reagents.expose(target, TOUCH)
reagents.clear_reagents()
throwforce = 0
/obj/item/reagent_containers/cup/soda_cans/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
if(. || spillable || !reagents.total_volume) // if it was caught, already opened, or has nothing in it
if(. || is_open_container() || !reagents.total_volume) // if it was caught, already opened, or has nothing in it
return
fizziness += SODA_FIZZINESS_THROWN