Files
Bubberstation/code/datums/components/food/decomposition.dm
Andrew 626e5c9dea Eating from floor may cause disease (#76018)
![image](https://github.com/tgstation/tgstation/assets/3625094/a60ac166-5772-4ee1-aa08-4f82828033e7)

## About The Pull Request

There is a 10% chance of getting one of 3 new diseases when you eat
dirty things.

Things become dirty when left on the floor for [more than 5
seconds](https://en.wikipedia.org/wiki/Five-second_rule).

But you can wash (with any method you know from spraying water to
cleaning with soap) or cook them later to avoid this.


![image](https://github.com/tgstation/tgstation/assets/3625094/0f78ef11-1737-4c9c-aecd-072dd95ef013)

Packaged, bowled, canned food (any food that spawns package as trash
afterwards) is protected from this effect.

Makes crafted food spawn on nearby tables when the hands are full.
Except the one behind you.


![8wSPp0hsx1](https://github.com/tgstation/tgstation/assets/3625094/6a51ebce-5626-4aac-9e59-cc4eab46a95a)

#### New diseases:

40% chance:

![image](https://github.com/tgstation/tgstation/assets/3625094/a0b72459-cc10-47e1-99db-b11013eaa61b)

40% chance (Vomiting is of special type that does not stun):

![image](https://github.com/tgstation/tgstation/assets/3625094/ce8e254c-7a65-49ed-bfb2-68652a624aed)

20% chance:

![image](https://github.com/tgstation/tgstation/assets/3625094/b792ae5e-8a99-4271-93d1-bcc172292049)

## Why It's Good For The Game

Things that are left on the floor for too long intentionally are trash
that should be disposed by janitor. If you make a meal or prepare a
medication, it makes sense that you should keep your product sanitized.

Things that are dropped unintentionally are supposed to be picked up
quickly. "Oops I dropped this pie, need to pick it up quickly before the
germs spread". 5 seconds are enough for this. If you didn't manage you
will be like "Oh dammit, now I need to wash this pie in a sink".

Now players will consider to not just throw items meant for eating onto
the floor neglecting the fact that it looks odd. If they still ignore
it, people who consume the items will receive a harmless but annoying
disease.

In general this PR aims to force some IC gameplay onto Medics, Chefs and
Botanists so that they care a bit more about things they make for other
players.

The items have a warning message saying that they are dirty and
dangerous, so the consumers have a way to detect dirty items and an
option to wash them with soap/rag/sink/shower/fire extinguisher to
remove the harmful part from the edible item.

So to avoid this, players just need to examine an item before eating it.

Botanists can spray a pile of fruits from a hose for the same effect,
and washed items that stay on floor dont regain germs until moved to
another tile.

Food that converts into another item during cooking (like meat slab
turning into steak) or crafting, will not retain the infection. This
kinda simulates the sanitizing during cooking.

Medics can use elevated structures (e.g. conveyor belt) to avoid getting
their pills dirty during creation in plumbing. Or they can wash the
pills they want to distribute in the shower before packaging them into
pill bottles or a bag.

## Changelog

🆑
add: Food and pills have a 10% chance to infect with one of three new
diseases on consumption when left for more than 5 seconds on the floor.
You can wash it to avoid disease. ChemMaster and Pill Press are added to
the list of elevated structures (Considered as tables for pills). Made
harvest spawn on top of hydrotrays to stay protected from germs.
add: Added three new advanced diseases: Gastritium, Carpellosis, Nebula
Nausea with static cures obtained by digesting dirty food.
fix: Food no longer decomposes on Hydrotrays, Grilles, Bonfires and all
dense kitchen machinery
code: Decomposition now uses `germ_sensitive` component and follows 5
second rule too.
qol: Crafted food items spawns on nearby tables (except the one behind
you) instead of dropping on floor when hands are full.
/🆑
2023-06-30 01:28:12 -07:00

142 lines
5.0 KiB
Plaintext

//"Don't leave food on the floor, that's how we get ants"
#define DECOMPOSITION_TIME (10 MINUTES)
#define DECOMPOSITION_TIME_RAW (5 MINUTES)
#define DECOMPOSITION_TIME_GROSS (7 MINUTES)
///Makes things decompose when exposed to germs. Requires /datum/component/germ_sensitive to detect exposure.
/datum/component/decomposition
dupe_mode = COMPONENT_DUPE_UNIQUE
/// Makes sure maploaded food only starts decomposing if a player's EVER picked it up before
var/handled = TRUE
/// Used to stop food in someone's hand & in storage slots from decomposing.
var/protected = FALSE
/// The total time that this takes to decompose
var/original_time = DECOMPOSITION_TIME
/// Used so the timer won't reset.
var/time_remaining = DECOMPOSITION_TIME
/// Used to create stink lines when the food is close to going bad
var/stink_timerid
/// Used to stop decomposition & check for the examine proc
var/decomp_timerid
/// Used to give raw/gross food lower timers
var/decomp_flags
/// Use for determining what kind of item the food decomposes into.
var/decomp_result
/// Does our food attract ants?
var/produce_ants = FALSE
/// Stink particle type, if we are supposed to create stink particles
var/stink_particles
/// Stink particle holder
var/obj/effect/abstract/particle_holder/particle_effect
/datum/component/decomposition/Initialize(mapload, decomp_req_handle, decomp_flags = NONE, decomp_result, ant_attracting = FALSE, custom_time = 0, stink_particles = /particles/stink)
if(!ismovable(parent) || !HAS_TRAIT(parent, TRAIT_GERM_SENSITIVE))
return COMPONENT_INCOMPATIBLE
src.decomp_flags = decomp_flags
src.decomp_result = decomp_result
if(mapload || decomp_req_handle)
handled = FALSE
src.produce_ants = ant_attracting
if(custom_time) // We have a custom decomposition time, set it to that
original_time = custom_time
else if(decomp_flags & RAW) // Raw food overrides gross
original_time = DECOMPOSITION_TIME_RAW
else if(decomp_flags & GROSS)
original_time = DECOMPOSITION_TIME_GROSS
time_remaining = original_time
src.stink_particles = stink_particles
/datum/component/decomposition/Destroy()
. = ..()
if(particle_effect)
QDEL_NULL(particle_effect)
/datum/component/decomposition/RegisterWithParent()
RegisterSignal(parent, COMSIG_ATOM_GERM_EXPOSED, PROC_REF(start_timer))
RegisterSignal(parent, COMSIG_ATOM_GERM_UNEXPOSED, PROC_REF(remove_timer))
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(examine))
/datum/component/decomposition/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_ATOM_GERM_EXPOSED,
COMSIG_ATOM_GERM_UNEXPOSED,
COMSIG_ATOM_EXAMINE
))
/datum/component/decomposition/proc/start_timer()
SIGNAL_HANDLER
if(!handled) // If maploaded, has someone touched this previously?
handled = TRUE // First germ exposure is ignored
return
// If all other checks fail, then begin decomposition.
decomp_timerid = addtimer(CALLBACK(src, PROC_REF(decompose)), time_remaining, TIMER_STOPPABLE | TIMER_UNIQUE)
// Also start the stinking timer, if have stink particles and aren't stinking yet
if(!stink_particles || particle_effect)
return
var/stink_time = max(0, time_remaining - (original_time * 0.5))
stink_timerid = addtimer(CALLBACK(src, PROC_REF(stink_up)), stink_time, TIMER_STOPPABLE | TIMER_UNIQUE)
/datum/component/decomposition/Destroy()
remove_timer()
return ..()
/// Returns the time remaining in decomp, either from our potential timer or our own value, whichever is more useful
/datum/component/decomposition/proc/get_time()
if(!decomp_timerid)
return time_remaining
return timeleft(decomp_timerid)
/datum/component/decomposition/proc/remove_timer()
if(!decomp_timerid)
return
time_remaining = timeleft(decomp_timerid)
deltimer(decomp_timerid)
decomp_timerid = null
if(!stink_timerid)
return
deltimer(stink_timerid)
stink_timerid = null
/datum/component/decomposition/proc/stink_up()
stink_timerid = null
// Neither should happen, but to be sure
if(particle_effect || !stink_particles)
return
// we don't want stink lines on mobs (even though it'd be quite funny)
particle_effect = new(parent, stink_particles, isitem(parent) ? NONE : PARTICLE_ATTACH_MOB)
/datum/component/decomposition/proc/decompose()
decomp_timerid = null
var/obj/decomp = parent //Lets us spawn things at decomp
if(produce_ants)
new /obj/effect/decal/cleanable/ants(decomp.loc)
if(decomp_result)
new decomp_result(decomp.loc)
decomp.visible_message(span_warning("[decomp] gets overtaken by mold[produce_ants ? " and ants":""]! Gross!"))
qdel(decomp)
return
/datum/component/decomposition/proc/examine(datum/source, mob/user, list/examine_list)
SIGNAL_HANDLER
var/time_d = get_time()
switch(time_d / original_time)
if(0.5 to 0.75) // 25% rotten
examine_list += span_notice("[parent] looks kinda stale.")
if(0.25 to 0.5) // 50% rotten
examine_list += span_notice("[parent] is starting to look pretty gross.")
if(0 to 0.25) // 75% rotten
examine_list += span_danger("[parent] barely looks edible.")
#undef DECOMPOSITION_TIME
#undef DECOMPOSITION_TIME_GROSS
#undef DECOMPOSITION_TIME_RAW