mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-30 08:37:43 +01:00
[MIRROR] Componentizes Food Storage (#561)
* Componentizes Food Storage (#53116) Moves the food storage behavior on (formerly) bread, cheese wheels, and cakes from /food/snacks/store/ to a component. This lets just about any food/edible thing be used for storing fun things like glass shards, which is pretty neat. Also helps work towards the food refactor. * Componentizes Food Storage Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
This commit is contained in:
@@ -657,7 +657,7 @@
|
||||
|
||||
//Food
|
||||
|
||||
///from base of obj/item/reagent_containers/food/snacks/attack() & Edible component: (mob/living/eater, mob/feeder)
|
||||
///from base of obj/item/reagent_containers/food/snacks/attack() & Edible component: (mob/living/eater, mob/feeder, bitecount, bitesize)
|
||||
#define COMSIG_FOOD_EATEN "food_eaten"
|
||||
|
||||
#define COMSIG_ITEM_FRIED "item_fried"
|
||||
|
||||
@@ -214,7 +214,7 @@ Behavior that's still missing from this component that original food items had t
|
||||
eater.satiety -= junkiness
|
||||
playsound(eater.loc,'sound/items/eatfood.ogg', rand(10,50), TRUE)
|
||||
if(owner.reagents.total_volume)
|
||||
SEND_SIGNAL(parent, COMSIG_FOOD_EATEN, eater, feeder)
|
||||
SEND_SIGNAL(parent, COMSIG_FOOD_EATEN, eater, feeder, bitecount, bite_consumption)
|
||||
var/fraction = min(bite_consumption / owner.reagents.total_volume, 1)
|
||||
owner.reagents.trans_to(eater, bite_consumption, transfered_by = feeder, methods = INGEST)
|
||||
bitecount++
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/// --Food storage component--
|
||||
/// This component lets you slide one item into large foods, such as bread, cheese wheels, or cakes.
|
||||
/// Consuming food storages with an item inside can cause unique interactions, such as eating glass shards.
|
||||
|
||||
/datum/component/food_storage
|
||||
/// Reference to what we have in our food.
|
||||
var/obj/item/stored_item
|
||||
/// The amount of volume the food has on creation - Used for probabilities
|
||||
var/initial_volume = 10
|
||||
/// Minimum size items that can be inserted
|
||||
var/minimum_weight_class = WEIGHT_CLASS_SMALL
|
||||
/// What are the odds we bite into the stored item?
|
||||
var/bad_chance_of_discovery = 0
|
||||
/// What are the odds we see the stored item before we bite it?
|
||||
var/good_chance_of_discovery = 100
|
||||
/// The stored item was found out somehow.
|
||||
var/discovered = FALSE
|
||||
|
||||
/datum/component/food_storage/Initialize(_minimum_weight_class = WEIGHT_CLASS_SMALL, _bad_chance = 0, _good_chance = 100)
|
||||
|
||||
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/try_inserting_item)
|
||||
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, .proc/try_removing_item)
|
||||
RegisterSignal(parent, COMSIG_FOOD_EATEN, .proc/consume_food_storage)
|
||||
|
||||
var/atom/food = parent
|
||||
initial_volume = food.reagents.total_volume
|
||||
|
||||
minimum_weight_class = _minimum_weight_class
|
||||
bad_chance_of_discovery = _bad_chance
|
||||
good_chance_of_discovery = _good_chance
|
||||
|
||||
/datum/component/food_storage/Destroy(force, silent)
|
||||
if(stored_item)
|
||||
stored_item.forceMove(stored_item.drop_location())
|
||||
stored_item = null
|
||||
. = ..()
|
||||
|
||||
/** Begins the process of inserted an item.
|
||||
*
|
||||
* Clicking on the food storage with an item on disarm intent will begin a do_after, which if successful inserts the item.
|
||||
*
|
||||
* Arguments
|
||||
* inserted_item - the item being placed into the food
|
||||
* user - the person inserting the item
|
||||
*/
|
||||
/datum/component/food_storage/proc/try_inserting_item(datum/source, obj/item/inserted_item, mob/user, params)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
// No matryoshka-ing food storage
|
||||
if(istype(inserted_item, /obj/item/storage) || IS_EDIBLE(inserted_item))
|
||||
return
|
||||
|
||||
if(inserted_item.w_class > minimum_weight_class)
|
||||
to_chat(user, "<span class='warning'>\The [inserted_item.name] won't fit in \the [parent].</span>")
|
||||
return
|
||||
|
||||
if(!QDELETED(stored_item))
|
||||
to_chat(user, "<span class='warning'>There's something in \the [parent].</span>")
|
||||
return
|
||||
|
||||
user.visible_message("<span class='notice'>[user.name] begins inserting [inserted_item.name] into \the [parent].</span>", \
|
||||
"<span class='notice'>You start to insert the [inserted_item.name] into \the [parent].</span>")
|
||||
|
||||
INVOKE_ASYNC(src, .proc/insert_item, inserted_item, user)
|
||||
return COMPONENT_ITEM_NO_ATTACK
|
||||
|
||||
/** Begins the process of attempting to remove the stored item.
|
||||
*
|
||||
* Clicking on food storage on grab intent will begin a do_after, which if successful removes the stored_item.
|
||||
*
|
||||
* Arguments
|
||||
* user - the person removing the item.
|
||||
*/
|
||||
/datum/component/food_storage/proc/try_removing_item(datum/source, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(user.a_intent != INTENT_GRAB)
|
||||
return
|
||||
|
||||
if(QDELETED(stored_item))
|
||||
return
|
||||
|
||||
user.visible_message("<span class='notice'>[user.name] begins tearing at \the [parent].</span>", \
|
||||
"<span class='notice'>You start to rip into \the [parent].</span>")
|
||||
|
||||
INVOKE_ASYNC(src, .proc/begin_remove_item, user)
|
||||
return COMPONENT_ITEM_NO_ATTACK
|
||||
|
||||
/** Inserts the item into the food, after a do_after.
|
||||
*
|
||||
* Arguments
|
||||
* inserted_item - The item being inserted.
|
||||
* user - the person inserting the item.
|
||||
*/
|
||||
/datum/component/food_storage/proc/insert_item(obj/item/inserted_item, mob/user)
|
||||
if(do_after(user, 1.5 SECONDS, target = parent))
|
||||
var/atom/food = parent
|
||||
to_chat(user, "<span class='notice'>You slip [inserted_item.name] inside \the [parent].</span>")
|
||||
inserted_item.forceMove(food)
|
||||
user.log_message("[key_name(user)] inserted [inserted_item] into [parent] at [AREACOORD(user)]", LOG_ATTACK)
|
||||
food.add_fingerprint(user)
|
||||
inserted_item.add_fingerprint(user)
|
||||
|
||||
stored_item = inserted_item
|
||||
|
||||
/** Removes the item from the food, after a do_after.
|
||||
*
|
||||
* Arguments
|
||||
* user - person removing the item.
|
||||
*/
|
||||
/datum/component/food_storage/proc/begin_remove_item(mob/user)
|
||||
if(do_after(user, 10 SECONDS, target = parent))
|
||||
remove_item(user)
|
||||
|
||||
/**
|
||||
* Removes the stored item, putting it in user's hands or on the ground, then updates the reference.
|
||||
*/
|
||||
/datum/component/food_storage/proc/remove_item(mob/user)
|
||||
if(user.put_in_hands(stored_item))
|
||||
user.visible_message("<span class='warning'>[user.name] slowly pulls [stored_item.name] out of \the [parent].</span>", \
|
||||
"<span class='warning'>You slowly pull [stored_item.name] out of \the [parent].</span>")
|
||||
else
|
||||
stored_item.visible_message("<span class='warning'>[stored_item.name] falls out of \the [parent].</span>")
|
||||
|
||||
update_stored_item()
|
||||
|
||||
/** Checks for stored items when the food is eaten.
|
||||
*
|
||||
* If the food is eaten while an item is stored in it, calculates the odds that the item will be found.
|
||||
* Then, if the item is found before being bitten, the item is removed.
|
||||
* If the item is found by biting into it, calls on_accidental_consumption on the stored item.
|
||||
* Afterwards, removes the item from the food if it was discovered.
|
||||
*
|
||||
* Arguments
|
||||
* target - person doing the eating (can be the same as user)
|
||||
* user - person causing the eating to happen
|
||||
* bitecount - how many times the current food has been bitten
|
||||
* bitesize - how large bties are for this food
|
||||
*/
|
||||
/datum/component/food_storage/proc/consume_food_storage(datum/source, mob/living/target, mob/living/user, bitecount, bitesize)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(QDELETED(stored_item)) //if the stored item was deleted/null...
|
||||
if(!update_stored_item()) //check if there's a replacement item
|
||||
return
|
||||
|
||||
/// Chance of biting the held item = amount of bites / (intitial reagents / reagents per bite) * 100
|
||||
bad_chance_of_discovery = (bitecount / (initial_volume / bitesize))*100
|
||||
/// Chance of finding the held item = bad chance - 50
|
||||
good_chance_of_discovery = bad_chance_of_discovery - 50
|
||||
|
||||
if(prob(good_chance_of_discovery)) //finding the item, without biting it
|
||||
discovered = TRUE
|
||||
to_chat(target, "<span class='warning'>It feels like there's something in \the [parent]...!</span>")
|
||||
|
||||
else if(prob(bad_chance_of_discovery)) //finding the item, BY biting it
|
||||
user.log_message("[key_name(user)] just fed [key_name(target)] a/an [stored_item] which was hidden in [parent] at [AREACOORD(target)]", LOG_ATTACK)
|
||||
discovered = stored_item.on_accidental_consumption(target, user, parent)
|
||||
update_stored_item() //make sure if the item was changed, the reference changes as well
|
||||
|
||||
if(!QDELETED(stored_item) && discovered)
|
||||
INVOKE_ASYNC(src, .proc/remove_item, user)
|
||||
|
||||
/** Updates the reference of the stored item.
|
||||
*
|
||||
* Checks the food's contents for if an alternate item was placed into the food.
|
||||
* If there is an alternate item, updates the reference to the new item.
|
||||
* If there isn't, updates the reference to null.
|
||||
*
|
||||
* Returns FALSE if the ref is nulled, or TRUE is another item replaced it.
|
||||
*/
|
||||
/datum/component/food_storage/proc/update_stored_item()
|
||||
var/atom/food = parent
|
||||
if(!food?.contents.len) //if there's no items in the food or food is deleted somehow
|
||||
stored_item = null
|
||||
return FALSE
|
||||
|
||||
for(var/obj/item/i in food.contents) //search the food's contents for a replacement item
|
||||
if(istype(i, /obj/item/reagent_containers/food/snacks))
|
||||
continue
|
||||
if(QDELETED(i))
|
||||
continue
|
||||
|
||||
stored_item = i //we found something to replace it
|
||||
return TRUE
|
||||
|
||||
//if there's nothing else in the food, or we found nothing valid
|
||||
stored_item = null
|
||||
return FALSE
|
||||
@@ -1079,18 +1079,16 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
if(custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)] >= total_material_amount * 0.60)
|
||||
if(prob(66)) //66% chance to break it
|
||||
/// The glass shard that is spawned into the source item
|
||||
var/obj/item/shard/broken_glass = new /obj/item/shard(src)
|
||||
var/obj/item/shard/broken_glass = new /obj/item/shard(loc)
|
||||
broken_glass.name = "broken [name]"
|
||||
broken_glass.desc = "This used to be \a [name], but it sure isn't anymore."
|
||||
playsound(M, "shatter", 25, TRUE)
|
||||
qdel(src)
|
||||
if(S)
|
||||
S.contents += broken_glass //puts the glass back into the source item
|
||||
else
|
||||
if(QDELETED(S))
|
||||
broken_glass.on_accidental_consumption(M, user)
|
||||
else //33% chance to just "crack" it (play a sound) and leave it in the bread
|
||||
playsound(M, "shatter", 15, TRUE)
|
||||
discover_after = FALSE
|
||||
discover_after = FALSE
|
||||
|
||||
M.adjust_disgust(33)
|
||||
M.visible_message("<span class='warning'>[M] looks like [M.p_theyve()] just bitten into something hard.</span>", \
|
||||
@@ -1113,10 +1111,6 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
else
|
||||
to_chat(M, "<span class='warning'>[source_item? "Something strange was in the \the [source_item]..." : "I just bit something strange..."] </span>")
|
||||
|
||||
//Just in case - can't discover something that doesn't exist
|
||||
if(QDELETED(src))
|
||||
return FALSE
|
||||
|
||||
return discover_after
|
||||
|
||||
#undef MAX_BONUS_MATS_PER_BITE
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
/obj/item/food/bread/Initialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/dunkable, 10)
|
||||
AddComponent(/datum/component/food_storage)
|
||||
|
||||
/obj/item/food/breadslice
|
||||
icon = 'icons/obj/food/burgerbread.dmi'
|
||||
|
||||
@@ -155,7 +155,7 @@ All foods are distributed among various categories. Use common sense.
|
||||
M.satiety -= junkiness
|
||||
playsound(M.loc,'sound/items/eatfood.ogg', rand(10,50), TRUE)
|
||||
if(reagents.total_volume)
|
||||
SEND_SIGNAL(src, COMSIG_FOOD_EATEN, M, user)
|
||||
SEND_SIGNAL(src, COMSIG_FOOD_EATEN, M, user, bitecount, bitesize)
|
||||
var/fraction = min(bitesize / reagents.total_volume, 1)
|
||||
reagents.trans_to(M, bitesize, transfered_by = user, methods = INGEST)
|
||||
bitecount++
|
||||
@@ -340,78 +340,10 @@ All foods are distributed among various categories. Use common sense.
|
||||
/// All the food items that can store an item inside itself, like bread or cake.
|
||||
/obj/item/reagent_containers/food/snacks/store
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
/// If an item has been stored in the food
|
||||
var/stored_item = FALSE
|
||||
/// The amount of volume the food has on creation
|
||||
var/volume_on_creation = 0
|
||||
/// Allows someone to bypass the small weight class requirement, so they can put whatever they want into a slice of bread
|
||||
var/bypass_weight_limit = FALSE
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/store/Initialize()
|
||||
. = ..()
|
||||
if(reagents?.total_volume)
|
||||
volume_on_creation = reagents.total_volume
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/store/attackby(obj/item/W, mob/user, params)
|
||||
..()
|
||||
if(istype(W, /obj/item/reagent_containers/food/snacks)) //can't slip snacks inside, they're used for custom foods.
|
||||
return FALSE
|
||||
|
||||
if((bypass_weight_limit || W.w_class <= WEIGHT_CLASS_SMALL))
|
||||
if(W.get_sharpness() && user.a_intent != INTENT_DISARM)
|
||||
return FALSE
|
||||
if(istype(W, /obj/item/storage))
|
||||
return FALSE
|
||||
if(stored_item)
|
||||
return FALSE
|
||||
if(!iscarbon(user))
|
||||
return FALSE
|
||||
if(contents.len >= 20)
|
||||
to_chat(user, "<span class='warning'>[src] is full.</span>")
|
||||
return FALSE
|
||||
user.visible_message("<span class='notice'>[user.name] begins inserting [W] into [src].</span>", \
|
||||
"<span class='notice'>You start to insert the [W] into \the [src].</span>")
|
||||
if(!do_after(user, 1.5 SECONDS, target = src))
|
||||
return FALSE
|
||||
to_chat(user, "<span class='notice'>You slip [W] inside [src].</span>")
|
||||
user.transferItemToLoc(W, src)
|
||||
log_message("[key_name(user)] inserted [W.name] into [src.name] at [AREACOORD(src)]", LOG_ATTACK)
|
||||
add_fingerprint(user)
|
||||
contents += W
|
||||
stored_item = TRUE
|
||||
return TRUE // no afterattack here
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/store/attack(mob/living/carbon/M, mob/living/carbon/user, def_zone)
|
||||
if(!..())
|
||||
return
|
||||
/// What are the odds we eat glass? - [Bitecount / Max number of bites] * 100
|
||||
var/bad_chance_of_discovery = (bitecount / (volume_on_creation / bitesize))*100 //the closer you get to finishing it, the higher the chance you bite into it
|
||||
/// What are the odds we see the glass but don't bite it? - ([Bitecount / Max number of bites] * 100) - 50
|
||||
var/good_chance_of_discovery = bad_chance_of_discovery - 50 //the closer you get to finishing it, the more likely you can see what is in it
|
||||
/// We've found the item, and plan on remove it
|
||||
var/discovered = FALSE
|
||||
|
||||
if(stored_item)
|
||||
for(var/obj/item/I in contents)
|
||||
if(istype(I, /obj/item/reagent_containers/food/snacks))
|
||||
return FALSE
|
||||
if(prob(good_chance_of_discovery))
|
||||
discovered = TRUE
|
||||
to_chat(M, "<span class='warning'>It feels like there's something in this [src.name]...!</span>")
|
||||
|
||||
else if(prob(bad_chance_of_discovery))
|
||||
log_message("[key_name(user)] just fed [key_name(M)] a/an [I.name] which was hidden in [src.name] at [AREACOORD(src)]", LOG_ATTACK)
|
||||
discovered = I.on_accidental_consumption(M, user, src)
|
||||
|
||||
if(!QDELETED(I) && discovered)
|
||||
contents -= I
|
||||
stored_item = FALSE
|
||||
if(M.put_in_hands(I)) //the moment when you slowly pull out whatever you just bit into in your food
|
||||
to_chat(M, "<span class='warning'>You slowly pull [I] out of \the [src].</span>")
|
||||
else
|
||||
to_chat(M, "<span class='warning'>[I] falls out of \the [src].</span>")
|
||||
|
||||
return FALSE
|
||||
AddComponent(/datum/component/food_storage)
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/MouseDrop(atom/over)
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
@@ -497,6 +497,7 @@
|
||||
#include "code\datums\components\plumbing\filter.dm"
|
||||
#include "code\datums\components\plumbing\reaction_chamber.dm"
|
||||
#include "code\datums\components\plumbing\splitter.dm"
|
||||
#include "code\datums\components\storage\food_storage.dm"
|
||||
#include "code\datums\components\storage\storage.dm"
|
||||
#include "code\datums\components\storage\concrete\_concrete.dm"
|
||||
#include "code\datums\components\storage\concrete\bag_of_holding.dm"
|
||||
|
||||
Reference in New Issue
Block a user