mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-01 20:42:08 +00:00
* Golem Rework * SECT 9 * ok --------- Co-authored-by: Jacquerel <hnevard@gmail.com> Co-authored-by: Gandalf <9026500+Gandalf2k15@users.noreply.github.com>
587 lines
22 KiB
Plaintext
587 lines
22 KiB
Plaintext
/*!
|
|
|
|
This component makes it possible to make things edible. What this means is that you can take a bite or force someone to take a bite (in the case of items).
|
|
These items take a specific time to eat, and can do most of the things our original food items could.
|
|
|
|
Behavior that's still missing from this component that original food items had that should either be put into separate components or somewhere else:
|
|
Components:
|
|
Drying component (jerky etc)
|
|
Processable component (Slicing and cooking behavior essentialy, making it go from item A to B when conditions are met.)
|
|
|
|
Misc:
|
|
Something for cakes (You can store things inside)
|
|
|
|
*/
|
|
/datum/component/edible
|
|
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
|
|
///Amount of reagents taken per bite
|
|
var/bite_consumption = 2
|
|
///Amount of bites taken so far
|
|
var/bitecount = 0
|
|
///Flags for food
|
|
var/food_flags = NONE
|
|
///Bitfield of the types of this food
|
|
var/foodtypes = NONE
|
|
///Amount of seconds it takes to eat this food
|
|
var/eat_time = 30
|
|
///Defines how much it lowers someones satiety (Need to eat, essentialy)
|
|
var/junkiness = 0
|
|
///Message to send when eating
|
|
var/list/eatverbs
|
|
///Callback to be ran for when you take a bite of something
|
|
var/datum/callback/after_eat
|
|
///Callback to be ran for when you finish eating something
|
|
var/datum/callback/on_consume
|
|
///Callback to be ran for when the code check if the food is liked, allowing for unique overrides for special foods like donuts with cops.
|
|
var/datum/callback/check_liked
|
|
///Last time we checked for food likes
|
|
var/last_check_time
|
|
///The initial volume of the foods reagents
|
|
var/volume = 50
|
|
///The flavortext for taste (haha get it flavor text)
|
|
var/list/tastes
|
|
|
|
/datum/component/edible/Initialize(
|
|
list/initial_reagents,
|
|
food_flags = NONE,
|
|
foodtypes = NONE,
|
|
volume = 50,
|
|
eat_time = 10,
|
|
list/tastes,
|
|
list/eatverbs = list("bite", "chew", "nibble", "gnaw", "gobble", "chomp"),
|
|
bite_consumption = 2,
|
|
junkiness,
|
|
datum/callback/after_eat,
|
|
datum/callback/on_consume,
|
|
datum/callback/check_liked,
|
|
)
|
|
if(!isatom(parent))
|
|
return COMPONENT_INCOMPATIBLE
|
|
|
|
src.bite_consumption = bite_consumption
|
|
src.food_flags = food_flags
|
|
src.foodtypes = foodtypes
|
|
src.volume = volume
|
|
src.eat_time = eat_time
|
|
src.eatverbs = string_list(eatverbs)
|
|
src.junkiness = junkiness
|
|
src.after_eat = after_eat
|
|
src.on_consume = on_consume
|
|
src.tastes = string_assoc_list(tastes)
|
|
src.check_liked = check_liked
|
|
|
|
setup_initial_reagents(initial_reagents)
|
|
|
|
/datum/component/edible/RegisterWithParent()
|
|
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(examine))
|
|
RegisterSignals(parent, COMSIG_ATOM_ATTACK_ANIMAL, PROC_REF(UseByAnimal))
|
|
RegisterSignal(parent, COMSIG_ATOM_CHECKPARTS, PROC_REF(OnCraft))
|
|
RegisterSignal(parent, COMSIG_ATOM_CREATEDBY_PROCESSING, PROC_REF(OnProcessed))
|
|
RegisterSignal(parent, COMSIG_FOOD_INGREDIENT_ADDED, PROC_REF(edible_ingredient_added))
|
|
RegisterSignal(parent, COMSIG_OOZE_EAT_ATOM, PROC_REF(on_ooze_eat))
|
|
|
|
if(isturf(parent))
|
|
RegisterSignal(parent, COMSIG_ATOM_ENTERED, PROC_REF(on_entered))
|
|
else
|
|
var/static/list/loc_connections = list(COMSIG_ATOM_ENTERED = PROC_REF(on_entered))
|
|
AddComponent(/datum/component/connect_loc_behalf, parent, loc_connections)
|
|
|
|
if(isitem(parent))
|
|
RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(UseFromHand))
|
|
RegisterSignal(parent, COMSIG_ITEM_USED_AS_INGREDIENT, PROC_REF(used_to_customize))
|
|
|
|
var/obj/item/item = parent
|
|
if(!item.grind_results)
|
|
item.grind_results = list() //If this doesn't already exist, add it as an empty list. This is needed for the grinder to accept it.
|
|
|
|
else if(isturf(parent) || isstructure(parent))
|
|
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(TryToEatIt))
|
|
|
|
/datum/component/edible/UnregisterFromParent()
|
|
UnregisterSignal(parent, list(
|
|
COMSIG_ATOM_ATTACK_ANIMAL,
|
|
COMSIG_ATOM_ATTACK_HAND,
|
|
COMSIG_ATOM_CHECKPARTS,
|
|
COMSIG_ATOM_CREATEDBY_PROCESSING,
|
|
COMSIG_ATOM_ENTERED,
|
|
COMSIG_FOOD_INGREDIENT_ADDED,
|
|
COMSIG_ITEM_ATTACK,
|
|
COMSIG_ITEM_USED_AS_INGREDIENT,
|
|
COMSIG_OOZE_EAT_ATOM,
|
|
COMSIG_PARENT_EXAMINE,
|
|
))
|
|
|
|
qdel(GetComponent(/datum/component/connect_loc_behalf))
|
|
|
|
/datum/component/edible/InheritComponent(
|
|
datum/component/edible/old_comp,
|
|
i_am_original,
|
|
list/initial_reagents,
|
|
food_flags = NONE,
|
|
foodtypes = NONE,
|
|
volume,
|
|
eat_time,
|
|
list/tastes,
|
|
list/eatverbs,
|
|
bite_consumption,
|
|
junkiness,
|
|
datum/callback/after_eat,
|
|
datum/callback/on_consume,
|
|
datum/callback/check_liked,
|
|
)
|
|
|
|
// If we got passed an old comp, take only the values that will not override our current ones
|
|
if(old_comp)
|
|
food_flags = old_comp.food_flags
|
|
foodtypes = old_comp.foodtypes
|
|
tastes = old_comp.tastes
|
|
eatverbs = old_comp.eatverbs
|
|
|
|
// only edit if we're OG
|
|
if(!i_am_original)
|
|
return
|
|
|
|
// add food flags and types
|
|
src.food_flags |= food_flags
|
|
src.foodtypes |= foodtypes
|
|
|
|
// add all new eatverbs to the list
|
|
if(islist(eatverbs))
|
|
var/list/cached_verbs = src.eatverbs
|
|
if(islist(cached_verbs))
|
|
// eatverbs becomes a combination of existing verbs and new ones
|
|
src.eatverbs = string_list(cached_verbs | eatverbs)
|
|
else
|
|
src.eatverbs = string_list(eatverbs)
|
|
|
|
// add all new tastes to the tastes
|
|
if(islist(tastes))
|
|
var/list/cached_tastes = src.tastes
|
|
if(islist(cached_tastes))
|
|
// tastes becomes a combination of existing tastes and new ones
|
|
var/list/mixed_tastes = cached_tastes.Copy()
|
|
for(var/new_taste in tastes)
|
|
mixed_tastes[new_taste] += tastes[new_taste]
|
|
|
|
src.tastes = string_assoc_list(mixed_tastes)
|
|
else
|
|
src.tastes = string_assoc_list(tastes)
|
|
|
|
// just set these directly
|
|
if(!isnull(bite_consumption))
|
|
src.bite_consumption = bite_consumption
|
|
if(!isnull(volume))
|
|
src.volume = volume
|
|
if(!isnull(eat_time))
|
|
src.eat_time = eat_time
|
|
if(!isnull(junkiness))
|
|
src.junkiness = junkiness
|
|
if(!isnull(after_eat))
|
|
src.after_eat = after_eat
|
|
if(!isnull(on_consume))
|
|
src.on_consume = on_consume
|
|
if(!isnull(check_liked))
|
|
src.check_liked = check_liked
|
|
|
|
// add newly passed in reagents
|
|
setup_initial_reagents(initial_reagents)
|
|
|
|
/datum/component/edible/Destroy(force, silent)
|
|
QDEL_NULL(after_eat)
|
|
QDEL_NULL(on_consume)
|
|
QDEL_NULL(check_liked)
|
|
return ..()
|
|
|
|
/// Sets up the initial reagents of the food.
|
|
/datum/component/edible/proc/setup_initial_reagents(list/reagents)
|
|
var/atom/owner = parent
|
|
if(owner.reagents)
|
|
owner.reagents.maximum_volume = volume
|
|
else
|
|
owner.create_reagents(volume, INJECTABLE)
|
|
|
|
for(var/rid in reagents)
|
|
var/amount = reagents[rid]
|
|
if(length(tastes) && (rid == /datum/reagent/consumable/nutriment || rid == /datum/reagent/consumable/nutriment/vitamin))
|
|
owner.reagents.add_reagent(rid, amount, tastes.Copy())
|
|
else
|
|
owner.reagents.add_reagent(rid, amount)
|
|
|
|
/datum/component/edible/proc/examine(datum/source, mob/user, list/examine_list)
|
|
SIGNAL_HANDLER
|
|
|
|
if(foodtypes)
|
|
var/list/types = bitfield_to_list(foodtypes, FOOD_FLAGS)
|
|
examine_list += span_notice("It is [lowertext(english_list(types))].")
|
|
|
|
var/datum/mind/mind = user.mind
|
|
if(mind && HAS_TRAIT_FROM(parent, TRAIT_FOOD_CHEF_MADE, REF(mind)))
|
|
examine_list += span_green("[parent] was made by you!")
|
|
|
|
if(!(food_flags & FOOD_IN_CONTAINER))
|
|
switch(bitecount)
|
|
if(0)
|
|
// pass
|
|
if(1)
|
|
examine_list += span_notice("[parent] was bitten by someone!")
|
|
if(2, 3)
|
|
examine_list += span_notice("[parent] was bitten [bitecount] times!")
|
|
else
|
|
examine_list += span_notice("[parent] was bitten multiple times!")
|
|
|
|
/datum/component/edible/proc/UseFromHand(obj/item/source, mob/living/M, mob/living/user)
|
|
SIGNAL_HANDLER
|
|
|
|
return TryToEat(M, user)
|
|
|
|
/datum/component/edible/proc/TryToEatIt(datum/source, mob/user)
|
|
SIGNAL_HANDLER
|
|
|
|
if (!in_range(source, user))
|
|
return
|
|
return TryToEat(user, user)
|
|
|
|
///Called when food is created through processing (Usually this means it was sliced). We use this to pass the OG items reagents.
|
|
/datum/component/edible/proc/OnProcessed(datum/source, atom/original_atom, list/chosen_processing_option)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!original_atom.reagents)
|
|
return
|
|
|
|
var/atom/this_food = parent
|
|
|
|
//Make sure we have a reagent container large enough to fit the original atom's reagents.
|
|
volume = max(volume, ROUND_UP(original_atom.reagents.maximum_volume / chosen_processing_option[TOOL_PROCESSING_AMOUNT]))
|
|
|
|
this_food.create_reagents(volume)
|
|
original_atom.reagents.copy_to(this_food, original_atom.reagents.total_volume / chosen_processing_option[TOOL_PROCESSING_AMOUNT], 1)
|
|
|
|
if(original_atom.name != initial(original_atom.name))
|
|
this_food.name = "slice of [original_atom.name]"
|
|
if(original_atom.desc != initial(original_atom.desc))
|
|
this_food.desc = "[original_atom.desc]"
|
|
|
|
///Called when food is crafted through a crafting recipe datum.
|
|
/datum/component/edible/proc/OnCraft(datum/source, list/parts_list, datum/crafting_recipe/food/recipe)
|
|
SIGNAL_HANDLER
|
|
|
|
var/atom/this_food = parent
|
|
|
|
this_food.reagents.multiply_reagents(CRAFTED_FOOD_BASE_REAGENT_MODIFIER)
|
|
this_food.reagents.maximum_volume *= CRAFTED_FOOD_BASE_REAGENT_MODIFIER
|
|
|
|
for(var/obj/item/food/crafted_part in parts_list)
|
|
if(!crafted_part.reagents)
|
|
continue
|
|
|
|
this_food.reagents.maximum_volume += crafted_part.reagents.maximum_volume * CRAFTED_FOOD_INGREDIENT_REAGENT_MODIFIER
|
|
crafted_part.reagents.trans_to(this_food.reagents, crafted_part.reagents.maximum_volume, CRAFTED_FOOD_INGREDIENT_REAGENT_MODIFIER)
|
|
|
|
this_food.reagents.maximum_volume = ROUND_UP(this_food.reagents.maximum_volume) // Just because I like whole numbers for this.
|
|
|
|
BLACKBOX_LOG_FOOD_MADE(this_food.type)
|
|
|
|
///Makes sure the thing hasn't been destroyed or fully eaten to prevent eating phantom edibles
|
|
/datum/component/edible/proc/IsFoodGone(atom/owner, mob/living/feeder)
|
|
if(QDELETED(owner) || !(IS_EDIBLE(owner)))
|
|
return TRUE
|
|
if(owner.reagents.total_volume)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/// Normal time to forcefeed someone something
|
|
#define EAT_TIME_FORCE_FEED (3 SECONDS)
|
|
/// Multiplier for eat time if the eater has TRAIT_VORACIOUS
|
|
#define EAT_TIME_VORACIOUS_MULT 0.65 // voracious folk eat 35% faster
|
|
/// Multiplier for how much longer it takes a voracious folk to eat while full
|
|
#define EAT_TIME_VORACIOUS_FULL_MULT 4 // Takes at least 4 times as long to eat while full, so dorks cant just clear out the kitchen before they get robusted
|
|
|
|
///All the checks for the act of eating itself and
|
|
/datum/component/edible/proc/TryToEat(mob/living/eater, mob/living/feeder)
|
|
|
|
set waitfor = FALSE
|
|
|
|
var/atom/owner = parent
|
|
|
|
if(feeder.combat_mode)
|
|
return
|
|
|
|
. = COMPONENT_CANCEL_ATTACK_CHAIN //Point of no return I suppose
|
|
|
|
if(IsFoodGone(owner, feeder))
|
|
return
|
|
|
|
if(!CanConsume(eater, feeder))
|
|
return
|
|
var/fullness = eater.get_fullness() + 10 //The theoretical fullness of the person eating if they were to eat this
|
|
|
|
var/time_to_eat = (eater == feeder) ? eat_time : EAT_TIME_FORCE_FEED
|
|
if(HAS_TRAIT(eater, TRAIT_VORACIOUS))
|
|
if(fullness < NUTRITION_LEVEL_FAT || (eater != feeder)) // No extra delay when being forcefed
|
|
time_to_eat *= EAT_TIME_VORACIOUS_MULT
|
|
else
|
|
time_to_eat *= (fullness / NUTRITION_LEVEL_FAT) * EAT_TIME_VORACIOUS_FULL_MULT // takes longer to eat the more well fed you are
|
|
|
|
if(eater == feeder)//If you're eating it yourself.
|
|
if(eat_time && !do_after(feeder, time_to_eat, eater, timed_action_flags = food_flags & FOOD_FINGER_FOOD ? IGNORE_USER_LOC_CHANGE | IGNORE_TARGET_LOC_CHANGE : NONE)) //Gotta pass the minimal eat time
|
|
return
|
|
if(IsFoodGone(owner, feeder))
|
|
return
|
|
var/eatverb = pick(eatverbs)
|
|
|
|
var/message_to_nearby_audience = ""
|
|
var/message_to_consumer = ""
|
|
var/message_to_blind_consumer = ""
|
|
|
|
if(junkiness && eater.satiety < -150 && eater.nutrition > NUTRITION_LEVEL_STARVING + 50 && !HAS_TRAIT(eater, TRAIT_VORACIOUS))
|
|
to_chat(eater, span_warning("You don't feel like eating any more junk food at the moment!"))
|
|
return
|
|
else if(fullness > (600 * (1 + eater.overeatduration / (4000 SECONDS)))) // The more you eat - the more you can eat
|
|
if(HAS_TRAIT(eater, TRAIT_VORACIOUS))
|
|
message_to_nearby_audience = span_notice("[eater] voraciously forces \the [parent] down [eater.p_their()] throat..")
|
|
message_to_consumer = span_notice("You voraciously force \the [parent] down your throat.")
|
|
else
|
|
message_to_nearby_audience = span_warning("[eater] cannot force any more of \the [parent] to go down [eater.p_their()] throat!")
|
|
message_to_consumer = span_warning("You cannot force any more of \the [parent] to go down your throat!")
|
|
message_to_blind_consumer = message_to_consumer
|
|
eater.show_message(message_to_consumer, MSG_VISUAL, message_to_blind_consumer)
|
|
eater.visible_message(message_to_nearby_audience, ignored_mobs = eater)
|
|
//if we're too full, return because we can't eat whatever it is we're trying to eat
|
|
return
|
|
else if(fullness > 500)
|
|
if(HAS_TRAIT(eater, TRAIT_VORACIOUS))
|
|
message_to_nearby_audience = span_notice("[eater] [eatverb]s \the [parent].")
|
|
message_to_consumer = span_notice("You [eatverb] \the [parent].")
|
|
else
|
|
message_to_nearby_audience = span_notice("[eater] unwillingly [eatverb]s a bit of \the [parent].")
|
|
message_to_consumer = span_notice("You unwillingly [eatverb] a bit of \the [parent].")
|
|
else if(fullness > 150)
|
|
message_to_nearby_audience = span_notice("[eater] [eatverb]s \the [parent].")
|
|
message_to_consumer = span_notice("You [eatverb] \the [parent].")
|
|
else if(fullness > 50)
|
|
message_to_nearby_audience = span_notice("[eater] hungrily [eatverb]s \the [parent].")
|
|
message_to_consumer = span_notice("You hungrily [eatverb] \the [parent].")
|
|
else
|
|
message_to_nearby_audience = span_notice("[eater] hungrily [eatverb]s \the [parent], gobbling it down!")
|
|
message_to_consumer = span_notice("You hungrily [eatverb] \the [parent], gobbling it down!")
|
|
|
|
//if we're blind, we want to feel how hungrily we ate that food
|
|
message_to_blind_consumer = message_to_consumer
|
|
eater.show_message(message_to_consumer, MSG_VISUAL, message_to_blind_consumer)
|
|
eater.visible_message(message_to_nearby_audience, ignored_mobs = eater)
|
|
|
|
else //If you're feeding it to someone else.
|
|
if(isbrain(eater))
|
|
to_chat(feeder, span_warning("[eater] doesn't seem to have a mouth!"))
|
|
return
|
|
if(fullness <= (600 * (1 + eater.overeatduration / (2000 SECONDS))) || HAS_TRAIT(eater, TRAIT_VORACIOUS))
|
|
eater.visible_message(
|
|
span_danger("[feeder] attempts to [eater.get_bodypart(BODY_ZONE_HEAD) ? "feed [eater] [parent]." : "stuff [parent] down [eater]'s throat hole! Gross."]"),
|
|
span_userdanger("[feeder] attempts to [eater.get_bodypart(BODY_ZONE_HEAD) ? "feed you [parent]." : "stuff [parent] down your throat hole! Gross."]")
|
|
)
|
|
if(eater.is_blind())
|
|
to_chat(eater, span_userdanger("You feel someone trying to feed you something!"))
|
|
else
|
|
eater.visible_message(
|
|
span_danger("[feeder] cannot force any more of [parent] down [eater]'s [eater.get_bodypart(BODY_ZONE_HEAD) ? "throat!" : "throat hole! Eugh."]"),
|
|
span_userdanger("[feeder] cannot force any more of [parent] down your [eater.get_bodypart(BODY_ZONE_HEAD) ? "throat!" : "throat hole! Eugh."]")
|
|
)
|
|
if(eater.is_blind())
|
|
to_chat(eater, span_userdanger("You're too full to eat what's being fed to you!"))
|
|
return
|
|
if(!do_after(feeder, delay = time_to_eat, target = eater)) //Wait 3-ish seconds before you can feed
|
|
return
|
|
if(IsFoodGone(owner, feeder))
|
|
return
|
|
log_combat(feeder, eater, "fed", owner.reagents.get_reagent_log_string())
|
|
eater.visible_message(
|
|
span_danger("[feeder] forces [eater] to eat [parent]!"),
|
|
span_userdanger("[feeder] forces you to eat [parent]!")
|
|
)
|
|
if(eater.is_blind())
|
|
to_chat(eater, span_userdanger("You're forced to eat something!"))
|
|
|
|
TakeBite(eater, feeder)
|
|
|
|
//If we're not force-feeding and there's an eat delay, try take another bite
|
|
if(eater == feeder && eat_time)
|
|
INVOKE_ASYNC(src, PROC_REF(TryToEat), eater, feeder)
|
|
|
|
#undef EAT_TIME_FORCE_FEED
|
|
#undef EAT_TIME_VORACIOUS_MULT
|
|
#undef EAT_TIME_VORACIOUS_FULL_MULT
|
|
|
|
///This function lets the eater take a bite and transfers the reagents to the eater.
|
|
/datum/component/edible/proc/TakeBite(mob/living/eater, mob/living/feeder)
|
|
|
|
var/atom/owner = parent
|
|
|
|
if(!owner?.reagents)
|
|
stack_trace("[eater] failed to bite [owner], because [owner] had no reagents.")
|
|
return FALSE
|
|
if(eater.satiety > -200)
|
|
eater.satiety -= junkiness
|
|
playsound(eater.loc,'sound/items/eatfood.ogg', rand(10,50), TRUE)
|
|
if(!owner.reagents.total_volume)
|
|
return
|
|
var/sig_return = SEND_SIGNAL(parent, COMSIG_FOOD_EATEN, eater, feeder, bitecount, bite_consumption)
|
|
if(sig_return & DESTROY_FOOD)
|
|
qdel(owner)
|
|
return
|
|
var/fraction = min(bite_consumption / owner.reagents.total_volume, 1)
|
|
owner.reagents.trans_to(eater, bite_consumption, transfered_by = feeder, methods = INGEST)
|
|
bitecount++
|
|
checkLiked(fraction, eater)
|
|
if(!owner.reagents.total_volume)
|
|
On_Consume(eater, feeder)
|
|
|
|
//Invoke our after eat callback if it is valid
|
|
if(after_eat)
|
|
after_eat.Invoke(eater, feeder, bitecount)
|
|
|
|
//Invoke the eater's stomach's after_eat callback if valid
|
|
if(iscarbon(eater))
|
|
var/mob/living/carbon/carbon_eater = eater
|
|
var/obj/item/organ/internal/stomach/stomach = carbon_eater.get_organ_slot(ORGAN_SLOT_STOMACH)
|
|
if(istype(stomach))
|
|
stomach.after_eat(owner)
|
|
|
|
return TRUE
|
|
|
|
///Checks whether or not the eater can actually consume the food
|
|
/datum/component/edible/proc/CanConsume(mob/living/eater, mob/living/feeder)
|
|
if(!iscarbon(eater))
|
|
return FALSE
|
|
var/mob/living/carbon/C = eater
|
|
var/covered = ""
|
|
if(C.is_mouth_covered(ITEM_SLOT_HEAD))
|
|
covered = "headgear"
|
|
else if(C.is_mouth_covered(ITEM_SLOT_MASK))
|
|
covered = "mask"
|
|
if(covered)
|
|
var/who = (isnull(feeder) || eater == feeder) ? "your" : "[eater.p_their()]"
|
|
to_chat(feeder, span_warning("You have to remove [who] [covered] first!"))
|
|
return FALSE
|
|
if(SEND_SIGNAL(eater, COMSIG_CARBON_ATTEMPT_EAT, parent) & COMSIG_CARBON_BLOCK_EAT)
|
|
return
|
|
return TRUE
|
|
|
|
///Check foodtypes to see if we should send a moodlet
|
|
/datum/component/edible/proc/checkLiked(fraction, mob/M)
|
|
if(last_check_time + 50 > world.time)
|
|
return FALSE
|
|
if(!ishuman(M))
|
|
return FALSE
|
|
var/mob/living/carbon/human/H = M
|
|
|
|
//Bruh this breakfast thing is cringe and shouldve been handled separately from food-types, remove this in the future (Actually, just kill foodtypes in general)
|
|
if((foodtypes & BREAKFAST) && world.time - SSticker.round_start_time < STOP_SERVING_BREAKFAST)
|
|
H.add_mood_event("breakfast", /datum/mood_event/breakfast)
|
|
last_check_time = world.time
|
|
|
|
if(HAS_TRAIT(H, TRAIT_AGEUSIA))
|
|
if(foodtypes & H.dna.species.toxic_food)
|
|
to_chat(H, span_warning("You don't feel so good..."))
|
|
H.adjust_disgust(25 + 30 * fraction)
|
|
return // Don't care about the later checks if user has ageusia
|
|
|
|
var/food_taste_reaction
|
|
|
|
if(check_liked) //Callback handling; use this as an override for special food like donuts
|
|
food_taste_reaction = check_liked.Invoke(fraction, H)
|
|
|
|
if(!food_taste_reaction)
|
|
if(foodtypes & H.dna.species.toxic_food)
|
|
food_taste_reaction = FOOD_TOXIC
|
|
else if(foodtypes & H.dna.species.disliked_food)
|
|
food_taste_reaction = FOOD_DISLIKED
|
|
else if(foodtypes & H.dna.species.liked_food)
|
|
food_taste_reaction = FOOD_LIKED
|
|
|
|
if(HAS_TRAIT(parent, TRAIT_FOOD_SILVER)) // it's not real food
|
|
food_taste_reaction = isjellyperson(H) ? FOOD_LIKED : FOOD_TOXIC
|
|
|
|
switch(food_taste_reaction)
|
|
if(FOOD_TOXIC)
|
|
to_chat(H,span_warning("What the hell was that thing?!"))
|
|
H.adjust_disgust(25 + 30 * fraction)
|
|
H.add_mood_event("toxic_food", /datum/mood_event/disgusting_food)
|
|
if(FOOD_DISLIKED)
|
|
to_chat(H,span_notice("That didn't taste very good..."))
|
|
H.adjust_disgust(11 + 15 * fraction)
|
|
H.add_mood_event("gross_food", /datum/mood_event/gross_food)
|
|
if(FOOD_LIKED)
|
|
to_chat(H,span_notice("I love this taste!"))
|
|
H.adjust_disgust(-5 + -2.5 * fraction)
|
|
H.add_mood_event("fav_food", /datum/mood_event/favorite_food)
|
|
if(istype(parent, /obj/item/food))
|
|
var/obj/item/food/memorable_food = parent
|
|
if(memorable_food.venue_value >= FOOD_PRICE_EXOTIC)
|
|
H.add_mob_memory(/datum/memory/good_food, food = parent)
|
|
|
|
///Delete the item when it is fully eaten
|
|
/datum/component/edible/proc/On_Consume(mob/living/eater, mob/living/feeder)
|
|
SEND_SIGNAL(parent, COMSIG_FOOD_CONSUMED, eater, feeder)
|
|
|
|
on_consume?.Invoke(eater, feeder)
|
|
if (QDELETED(parent)) // might be destroyed by the callback
|
|
return
|
|
|
|
to_chat(feeder, span_warning("There is nothing left of [parent], oh no!"))
|
|
if(isturf(parent))
|
|
var/turf/T = parent
|
|
T.ScrapeAway(1, CHANGETURF_INHERIT_AIR)
|
|
else
|
|
qdel(parent)
|
|
|
|
///Ability to feed food to puppers
|
|
/datum/component/edible/proc/UseByAnimal(datum/source, mob/user)
|
|
|
|
SIGNAL_HANDLER
|
|
|
|
|
|
var/atom/owner = parent
|
|
|
|
if(!isdog(user))
|
|
return
|
|
var/mob/living/L = user
|
|
if(bitecount == 0 || prob(50))
|
|
L.manual_emote("nibbles away at \the [parent].")
|
|
bitecount++
|
|
. = COMPONENT_CANCEL_ATTACK_CHAIN
|
|
L.taste(owner.reagents) // why should carbons get all the fun?
|
|
if(bitecount >= 5)
|
|
var/satisfaction_text = pick("burps from enjoyment.", "yaps for more!", "woofs twice.", "looks at the area where \the [parent] was.")
|
|
L.manual_emote(satisfaction_text)
|
|
qdel(parent)
|
|
|
|
|
|
///Ability to feed food to puppers
|
|
/datum/component/edible/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
|
SIGNAL_HANDLER
|
|
SEND_SIGNAL(parent, COMSIG_FOOD_CROSSED, arrived, bitecount)
|
|
|
|
///Response to being used to customize something
|
|
/datum/component/edible/proc/used_to_customize(datum/source, atom/customized)
|
|
SIGNAL_HANDLER
|
|
|
|
SEND_SIGNAL(customized, COMSIG_FOOD_INGREDIENT_ADDED, src)
|
|
|
|
///Response to an edible ingredient being added to parent.
|
|
/datum/component/edible/proc/edible_ingredient_added(datum/source, datum/component/edible/ingredient)
|
|
SIGNAL_HANDLER
|
|
|
|
InheritComponent(ingredient, TRUE)
|
|
|
|
/// Response to oozes trying to eat something edible
|
|
/datum/component/edible/proc/on_ooze_eat(datum/source, mob/eater, edible_flags)
|
|
SIGNAL_HANDLER
|
|
|
|
if(foodtypes & edible_flags)
|
|
var/atom/eaten_food = parent
|
|
eaten_food.reagents.trans_to(eater, eaten_food.reagents.total_volume, transfered_by = eater)
|
|
eater.visible_message(span_warning("[src] eats [eaten_food]!"), span_notice("You eat [eaten_food]."))
|
|
playsound(get_turf(eater),'sound/items/eatfood.ogg', rand(30,50), TRUE)
|
|
qdel(eaten_food)
|
|
return COMPONENT_ATOM_EATEN
|