Files
GS13NG/code/modules/food_and_drinks/food/snacks.dm
T
kevinz000andGitHub f79148d549 Merge pull request #10947 from Ghommie/Ghommie-cit563
Holopad static fix and a few other mistakes.
2020-02-15 12:16:23 -07:00

382 lines
16 KiB
Plaintext

/** # Snacks
Items in the "Snacks" subcategory are food items that people actually eat. The key points are that they are created
already filled with reagents and are destroyed when empty. Additionally, they make a "munching" noise when eaten.
Notes by Darem: Food in the "snacks" subtype can hold a maximum of 50 units. Generally speaking, you don't want to go over 40
total for the item because you want to leave space for extra condiments. If you want effect besides healing, add a reagent for
it. Try to stick to existing reagents when possible (so if you want a stronger healing effect, just use omnizine). On use
effect (such as the old officer eating a donut code) requires a unique reagent (unless you can figure out a better way).
The nutriment reagent and bitesize variable replace the old heal_amt and amount variables. Each unit of nutriment is equal to
2 of the old heal_amt variable. Bitesize is the rate at which the reagents are consumed. So if you have 6 nutriment and a
bitesize of 2, then it'll take 3 bites to eat. Unlike the old system, the contained reagents are evenly spread among all
the bites. No more contained reagents = no more bites.
Here is an example of the new formatting for anyone who wants to add more food items.
```
/obj/item/reagent_containers/food/snacks/xenoburger //Identification path for the object.
name = "Xenoburger" //Name that displays in the UI.
desc = "Smells caustic. Tastes like heresy." //Duh
icon_state = "xburger" //Refers to an icon in food.dmi
list_reagents = list(/datum/reagent/xenomicrobes = 10,
/datum/reagent/consumable/nutriment = 2) //What's inside the snack.
bitesize = 3 //This is the amount each bite consumes.
```
All foods are distributed among various categories. Use common sense.
*/
/obj/item/reagent_containers/food/snacks
name = "snack"
desc = "Yummy."
icon = 'icons/obj/food/food.dmi'
icon_state = null
lefthand_file = 'icons/mob/inhands/misc/food_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/food_righthand.dmi'
obj_flags = UNIQUE_RENAME
grind_results = list() //To let them be ground up to transfer their reagents
var/bitesize = 2
var/bitecount = 0
var/trash = null
var/slice_path // for sliceable food. path of the item resulting from the slicing
var/slices_num
var/eatverb
var/dried_type = null
var/dry = 0
var/cooked_type = null //for microwave cooking. path of the resulting item after microwaving
var/filling_color = "#FFFFFF" //color to use when added to custom food.
var/custom_food_type = null //for food customizing. path of the custom food to create
var/junkiness = 0 //for junk food. used to lower human satiety.
var/list/bonus_reagents //the amount of reagents (usually nutriment and vitamin) added to crafted/cooked snacks, on top of the ingredients reagents.
var/customfoodfilling = 1 // whether it can be used as filling in custom food
var/list/tastes // for example list("crisps" = 2, "salt" = 1)
var/dunkable = FALSE // for dunkable food, make true
var/dunk_amount = 10 // how much reagent is transferred per dunk
//Placeholder for effect that trigger on eating that aren't tied to reagents.
/obj/item/reagent_containers/food/snacks/add_initial_reagents()
if(tastes && tastes.len)
if(list_reagents)
for(var/rid in list_reagents)
var/amount = list_reagents[rid]
if(rid == /datum/reagent/consumable/nutriment || rid == /datum/reagent/consumable/nutriment/vitamin)
reagents.add_reagent(rid, amount, tastes.Copy())
else
reagents.add_reagent(rid, amount)
else
..()
/obj/item/reagent_containers/food/snacks/proc/On_Consume(mob/living/eater)
if(!eater)
return
if(!reagents.total_volume)
var/mob/living/location = loc
var/obj/item/trash_item = generate_trash(location)
qdel(src)
if(istype(location))
location.put_in_hands(trash_item)
/obj/item/reagent_containers/food/snacks/attack_self(mob/user)
return
/obj/item/reagent_containers/food/snacks/attack(mob/living/M, mob/living/user, def_zone)
if(user.a_intent == INTENT_HARM)
return ..()
if(!eatverb)
eatverb = pick("bite","chew","nibble","gnaw","gobble","chomp")
if(!reagents.total_volume) //Shouldn't be needed but it checks to see if it has anything left in it.
to_chat(user, "<span class='notice'>None of [src] left, oh no!</span>")
qdel(src)
return 0
if(iscarbon(M))
if(!canconsume(M, user))
return 0
var/fullness = M.nutrition + 10
for(var/datum/reagent/consumable/C in M.reagents.reagent_list) //we add the nutrition value of what we're currently digesting
fullness += C.nutriment_factor * C.volume / C.metabolization_rate
if(M == user) //If you're eating it yourself.
if(junkiness && M.satiety < -150 && M.nutrition > NUTRITION_LEVEL_STARVING + 50 )
to_chat(M, "<span class='notice'>You don't feel like eating any more junk food at the moment.</span>")
return 0
else if(fullness <= 50)
user.visible_message("<span class='notice'>[user] hungrily takes a [eatverb] from \the [src], gobbling it down!</span>", "<span class='notice'>You hungrily take a [eatverb] from \the [src], gobbling it down!</span>")
else if(fullness > 50 && fullness < 150)
user.visible_message("<span class='notice'>[user] hungrily takes a [eatverb] from \the [src].</span>", "<span class='notice'>You hungrily take a [eatverb] from \the [src].</span>")
else if(fullness > 150 && fullness < 500)
user.visible_message("<span class='notice'>[user] takes a [eatverb] from \the [src].</span>", "<span class='notice'>You take a [eatverb] from \the [src].</span>")
else if(fullness > 500 && fullness < 600)
user.visible_message("<span class='notice'>[user] unwillingly takes a [eatverb] of a bit of \the [src].</span>", "<span class='warning'>You unwillingly take a [eatverb] of a bit of \the [src].</span>")
else if(fullness > (600 * (1 + M.overeatduration / 2000))) // The more you eat - the more you can eat
user.visible_message("<span class='warning'>[user] cannot force any more of \the [src] to go down [user.p_their()] throat!</span>", "<span class='danger'>You cannot force any more of \the [src] to go down your throat!</span>")
return 0
if(HAS_TRAIT(M, TRAIT_VORACIOUS))
M.changeNext_move(CLICK_CD_MELEE * 0.5) //nom nom nom
else
if(!isbrain(M)) //If you're feeding it to someone else.
if(fullness <= (600 * (1 + M.overeatduration / 1000)))
M.visible_message("<span class='danger'>[user] attempts to feed [M] [src].</span>", \
"<span class='userdanger'>[user] attempts to feed [M] [src].</span>")
else
M.visible_message("<span class='warning'>[user] cannot force any more of [src] down [M]'s throat!</span>", \
"<span class='warning'>[user] cannot force any more of [src] down [M]'s throat!</span>")
return 0
if(!do_mob(user, M))
return
log_combat(user, M, "fed", reagents.log_list())
M.visible_message("<span class='danger'>[user] forces [M] to eat [src].</span>", \
"<span class='userdanger'>[user] forces [M] to eat [src].</span>")
else
to_chat(user, "<span class='warning'>[M] doesn't seem to have a mouth!</span>")
return
if(reagents) //Handle ingestion of the reagent.
if(M.satiety > -200)
M.satiety -= junkiness
playsound(M.loc,'sound/items/eatfood.ogg', rand(10,50), 1)
if(reagents.total_volume)
SEND_SIGNAL(src, COMSIG_FOOD_EATEN, M, user)
var/fraction = min(bitesize / reagents.total_volume, 1)
reagents.reaction(M, INGEST, fraction)
reagents.trans_to(M, bitesize)
bitecount++
On_Consume(M)
checkLiked(fraction, M)
return 1
return 0
/obj/item/reagent_containers/food/snacks/examine(mob/user)
. = ..()
if(bitecount == 0)
return
else if(bitecount == 1)
. += "[src] was bitten by someone!"
else if(bitecount <= 3)
. += "[src] was bitten [bitecount] times!"
else
. += "[src] was bitten multiple times!"
/obj/item/reagent_containers/food/snacks/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/storage))
..() // -> item/attackby()
return 0
if(istype(W, /obj/item/reagent_containers/food/snacks))
var/obj/item/reagent_containers/food/snacks/S = W
if(custom_food_type && ispath(custom_food_type))
if(S.w_class > WEIGHT_CLASS_SMALL)
to_chat(user, "<span class='warning'>[S] is too big for [src]!</span>")
return 0
if(!S.customfoodfilling || istype(W, /obj/item/reagent_containers/food/snacks/customizable) || istype(W, /obj/item/reagent_containers/food/snacks/pizzaslice/custom) || istype(W, /obj/item/reagent_containers/food/snacks/cakeslice/custom))
to_chat(user, "<span class='warning'>[src] can't be filled with [S]!</span>")
return 0
if(contents.len >= 20)
to_chat(user, "<span class='warning'>You can't add more ingredients to [src]!</span>")
return 0
var/obj/item/reagent_containers/food/snacks/customizable/C = new custom_food_type(get_turf(src))
C.initialize_custom_food(src, S, user)
return 0
var/sharp = W.get_sharpness()
if(sharp)
if(slice(sharp, W, user))
return 1
else
..()
//Called when you finish tablecrafting a snack.
/obj/item/reagent_containers/food/snacks/CheckParts(list/parts_list, datum/crafting_recipe/food/R)
..()
reagents.clear_reagents()
for(var/obj/item/reagent_containers/RC in contents)
RC.reagents.trans_to(reagents, RC.reagents.maximum_volume)
if(istype(R))
contents_loop:
for(var/A in contents)
for(var/B in R.real_parts)
if(istype(A, B))
continue contents_loop
qdel(A)
SSblackbox.record_feedback("tally", "food_made", 1, type)
if(bonus_reagents && bonus_reagents.len)
for(var/r_id in bonus_reagents)
var/amount = bonus_reagents[r_id]
if(r_id == /datum/reagent/consumable/nutriment || r_id == /datum/reagent/consumable/nutriment/vitamin)
reagents.add_reagent(r_id, amount, tastes)
else
reagents.add_reagent(r_id, amount)
/obj/item/reagent_containers/food/snacks/proc/slice(accuracy, obj/item/W, mob/user)
if((slices_num <= 0 || !slices_num) || !slice_path) //is the food sliceable?
return FALSE
if ( \
!isturf(src.loc) || \
!(locate(/obj/structure/table) in src.loc) && \
!(locate(/obj/structure/table/optable) in src.loc) && \
!(locate(/obj/item/storage/bag/tray) in src.loc) \
)
to_chat(user, "<span class='warning'>You cannot slice [src] here! You need a table or at least a tray.</span>")
return FALSE
var/slices_lost = 0
if (accuracy >= IS_SHARP_ACCURATE)
user.visible_message( \
"[user] slices [src].", \
"<span class='notice'>You slice [src].</span>" \
)
else
user.visible_message( \
"[user] inaccurately slices [src] with [W]!", \
"<span class='notice'>You inaccurately slice [src] with your [W]!</span>" \
)
slices_lost = rand(1,min(1,round(slices_num/2)))
var/reagents_per_slice = reagents.total_volume/slices_num
for(var/i=1 to (slices_num-slices_lost))
var/obj/item/reagent_containers/food/snacks/slice = new slice_path (loc)
initialize_slice(slice, reagents_per_slice)
qdel(src)
return TRUE
/obj/item/reagent_containers/food/snacks/proc/initialize_slice(obj/item/reagent_containers/food/snacks/slice, reagents_per_slice)
slice.create_reagents(slice.volume, reagent_flags, reagent_value)
reagents.trans_to(slice,reagents_per_slice)
if(name != initial(name))
slice.name = "slice of [name]"
if(desc != initial(desc))
slice.desc = "[desc]"
if(foodtype != initial(foodtype))
slice.foodtype = foodtype //if something happens that overrode our food type, make sure the slice carries that over
/obj/item/reagent_containers/food/snacks/proc/generate_trash(atom/location)
if(trash)
if(ispath(trash, /obj/item))
. = new trash(location)
trash = null
return
else if(isitem(trash))
var/obj/item/trash_item = trash
trash_item.forceMove(location)
. = trash
trash = null
return
/obj/item/reagent_containers/food/snacks/proc/update_snack_overlays(obj/item/reagent_containers/food/snacks/S)
cut_overlays()
var/mutable_appearance/filling = mutable_appearance(icon, "[initial(icon_state)]_filling")
if(S.filling_color == "#FFFFFF")
filling.color = pick("#FF0000","#0000FF","#008000","#FFFF00")
else
filling.color = S.filling_color
add_overlay(filling)
// initialize_cooked_food() is called when microwaving the food
/obj/item/reagent_containers/food/snacks/proc/initialize_cooked_food(obj/item/reagent_containers/food/snacks/S, cooking_efficiency = 1)
S.create_reagents(S.volume, reagent_flags, reagent_value)
if(reagents)
reagents.trans_to(S, reagents.total_volume)
if(S.bonus_reagents && S.bonus_reagents.len)
for(var/r_id in S.bonus_reagents)
var/amount = S.bonus_reagents[r_id] * cooking_efficiency
if(r_id == /datum/reagent/consumable/nutriment || r_id == /datum/reagent/consumable/nutriment/vitamin)
S.reagents.add_reagent(r_id, amount, tastes)
else
S.reagents.add_reagent(r_id, amount)
/obj/item/reagent_containers/food/snacks/microwave_act(obj/machinery/microwave/M)
var/turf/T = get_turf(src)
var/obj/item/result
if(cooked_type)
result = new cooked_type(T)
if(istype(M))
initialize_cooked_food(result, M.efficiency)
else
initialize_cooked_food(result, 1)
SSblackbox.record_feedback("tally", "food_made", 1, result.type)
else
result = new /obj/item/reagent_containers/food/snacks/badrecipe(T)
if(istype(M) && M.dirty < 100)
M.dirty++
qdel(src)
return result
/obj/item/reagent_containers/food/snacks/Destroy()
if(contents)
for(var/atom/movable/something in contents)
something.forceMove(drop_location())
return ..()
/obj/item/reagent_containers/food/snacks/attack_animal(mob/M)
if(isanimal(M))
if(iscorgi(M))
var/mob/living/L = M
if(bitecount == 0 || prob(50))
M.emote("me", EMOTE_VISIBLE, "nibbles away at \the [src]")
bitecount++
L.taste(reagents) // why should carbons get all the fun?
if(bitecount >= 5)
var/sattisfaction_text = pick("burps from enjoyment", "yaps for more", "woofs twice", "looks at the area where \the [src] was")
if(sattisfaction_text)
M.emote("me", EMOTE_VISIBLE, "[sattisfaction_text]")
qdel(src)
//////////////////////////////////////////Dunking///////////////////////////////////////////
/obj/item/reagent_containers/food/snacks/afterattack(obj/item/reagent_containers/M, mob/user, proximity)
. = ..()
if(!dunkable || !proximity)
return
if(istype(M, /obj/item/reagent_containers/glass) || istype(M, /obj/item/reagent_containers/food/drinks)) //you can dunk dunkable snacks into beakers or drinks
if(!M.is_drainable())
to_chat(user, "<span class='warning'>[M] is unable to be dunked in!</span>")
return
if(M.reagents.trans_to(src, dunk_amount)) //if reagents were transfered, show the message
to_chat(user, "<span class='notice'>You dunk the [M].</span>")
return
if(!M.reagents.total_volume)
to_chat(user, "<span class='warning'>[M] is empty!</span>")
else
to_chat(user, "<span class='warning'>[src] is full!</span>")
// //////////////////////////////////////////////Store////////////////////////////////////////
/// 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
var/stored_item = 0
/obj/item/reagent_containers/food/snacks/store/attackby(obj/item/W, mob/user, params)
..()
if(W.w_class <= WEIGHT_CLASS_SMALL & !istype(W, /obj/item/reagent_containers/food/snacks)) //can't slip snacks inside, they're used for custom foods.
if(W.get_sharpness())
return 0
if(stored_item)
return 0
if(!iscarbon(user))
return 0
if(contents.len >= 20)
to_chat(user, "<span class='warning'>[src] is full.</span>")
return 0
to_chat(user, "<span class='notice'>You slip [W] inside [src].</span>")
user.transferItemToLoc(W, src)
add_fingerprint(user)
contents += W
stored_item = 1
return 1 // no afterattack here
/obj/item/reagent_containers/food/snacks/MouseDrop(atom/over)
var/turf/T = get_turf(src)
var/obj/structure/table/TB = locate(/obj/structure/table) in T
if(TB)
TB.MouseDrop(over)
else
return ..()