mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-07-20 19:44:46 +01:00
Infection Component & Germs (#19265)
* update these two * Germs! * Update vorestation.dme * Basic signal application * fixed * Update germ_sensitive.dm * Update germ_sensitive.dm * Apply suggestion from @Cameron-The-Raven * fix naming issues --------- Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
// Don't eat off the floor or hold parent object with dirty hands, you'll get sick
|
||||
|
||||
/// Time needed for bacteria to infect the parent object
|
||||
#define GERM_EXPOSURE_DELAY (5 SECONDS) // Five-second rule
|
||||
|
||||
/// Possible diseases
|
||||
GLOBAL_LIST_INIT(floor_diseases, list(
|
||||
/datum/disease/cold = 2,
|
||||
/datum/disease/food_poisoning = 7,
|
||||
/datum/disease/lycan = 1,
|
||||
))
|
||||
|
||||
/// Makes items infective if left on floor, also sending corresponding signals to parent
|
||||
/datum/component/germ_sensitive
|
||||
/// Timer for counting delay before becoming infective
|
||||
var/timer_id
|
||||
/// Whether it is already infective
|
||||
var/infective = FALSE
|
||||
|
||||
/datum/component/germ_sensitive/Initialize(mapload)
|
||||
if(!isobj(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
ADD_TRAIT(parent, TRAIT_GERM_SENSITIVE, REF(src))
|
||||
|
||||
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(examine))
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(handle_movement))
|
||||
RegisterSignals(parent, list(COMSIG_COMPONENT_CLEAN_ACT, COMSIG_ITEM_FRIED, COMSIG_ITEM_BARBEQUE_GRILLED, COMSIG_ATOM_FIRE_ACT), PROC_REF(delete_germs))
|
||||
|
||||
RegisterSignals(parent, list(
|
||||
COMSIG_ITEM_DROPPED, //Dropped into the world
|
||||
COMSIG_ATOM_EXITED, //Object exits a storage object (tables, boxes, etc)
|
||||
),
|
||||
PROC_REF(dropped))
|
||||
|
||||
RegisterSignals(parent, list(
|
||||
COMSIG_ITEM_PICKUP, //Picked up by mob
|
||||
COMSIG_ATOM_ENTERED, //Object enters a storage object (tables, boxes, etc.)
|
||||
),
|
||||
PROC_REF(picked_up))
|
||||
|
||||
// Map spawned items are protected until moved
|
||||
if(!mapload)
|
||||
handle_movement()
|
||||
|
||||
/datum/component/germ_sensitive/UnregisterFromParent()
|
||||
REMOVE_TRAIT(parent, TRAIT_GERM_SENSITIVE, REF(src))
|
||||
UnregisterSignal(parent, list(
|
||||
COMSIG_ATOM_ENTERED,
|
||||
COMSIG_ATOM_EXAMINE,
|
||||
COMSIG_ATOM_EXITED,
|
||||
COMSIG_COMPONENT_CLEAN_ACT,
|
||||
COMSIG_ITEM_FRIED,
|
||||
COMSIG_ITEM_BARBEQUE_GRILLED,
|
||||
COMSIG_ATOM_FIRE_ACT,
|
||||
COMSIG_ITEM_DROPPED,
|
||||
COMSIG_ITEM_PICKUP,
|
||||
COMSIG_MOVABLE_MOVED,
|
||||
))
|
||||
|
||||
/datum/component/germ_sensitive/Destroy()
|
||||
remove_timer()
|
||||
return ..()
|
||||
|
||||
/datum/component/germ_sensitive/proc/remove_timer()
|
||||
if(!timer_id)
|
||||
return
|
||||
deltimer(timer_id)
|
||||
timer_id = null
|
||||
|
||||
/datum/component/germ_sensitive/proc/handle_movement()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/obj/parent_object = parent
|
||||
var/turf/simulated/open_turf = parent_object.loc
|
||||
|
||||
// Is parent on simulated valid turf that is safe? Or held/in a pocket/in a closet/etc?
|
||||
if(!istype(open_turf) || islava(open_turf) || ismineralturf(open_turf) || !parent_object.get_gravity())
|
||||
remove_timer()
|
||||
SEND_SIGNAL(parent, COMSIG_ATOM_GERM_UNEXPOSED, src)
|
||||
return
|
||||
|
||||
// Is parent on an elevated structure?
|
||||
for(var/atom/movable/content as anything in open_turf.contents)
|
||||
if(GLOB.typecache_elevated_structures[content.type])
|
||||
remove_timer()
|
||||
SEND_SIGNAL(parent, COMSIG_ATOM_GERM_UNEXPOSED, src)
|
||||
return
|
||||
|
||||
// Exposed to bacteria, start countdown until becoming infected
|
||||
timer_id = addtimer(CALLBACK(src, PROC_REF(expose_to_germs)), GERM_EXPOSURE_DELAY, TIMER_STOPPABLE | TIMER_UNIQUE)
|
||||
|
||||
/datum/component/germ_sensitive/proc/picked_up()
|
||||
SIGNAL_HANDLER
|
||||
SEND_SIGNAL(parent, COMSIG_ATOM_GERM_UNEXPOSED, src)
|
||||
remove_timer()
|
||||
|
||||
/datum/component/germ_sensitive/proc/dropped()
|
||||
SIGNAL_HANDLER
|
||||
handle_movement()
|
||||
|
||||
/datum/component/germ_sensitive/proc/examine(datum/source, mob/user, list/examine_list)
|
||||
SIGNAL_HANDLER
|
||||
if(infective)
|
||||
examine_list += span_warning("[parent] looks dirty and not safe to consume.")
|
||||
|
||||
/datum/component/germ_sensitive/proc/expose_to_germs()
|
||||
// Admin spawned items are never exposed NYI
|
||||
// var/atom/parent_atom = parent
|
||||
// if(parent_atom.flags_1 & ADMIN_SPAWNED_1)
|
||||
// return
|
||||
|
||||
SEND_SIGNAL(parent, COMSIG_ATOM_GERM_EXPOSED, src)
|
||||
|
||||
if(infective)
|
||||
return
|
||||
infective = TRUE
|
||||
|
||||
var/random_disease = pick_weight(GLOB.floor_diseases)
|
||||
parent.AddComponent(/datum/component/infective, new random_disease, weak = TRUE)
|
||||
|
||||
/datum/component/germ_sensitive/proc/delete_germs()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
. = NONE
|
||||
|
||||
if(infective)
|
||||
infective = FALSE
|
||||
qdel(parent.GetComponent(/datum/component/infective))
|
||||
return COMPONENT_CLEANED
|
||||
|
||||
#undef GERM_EXPOSURE_DELAY
|
||||
@@ -1,43 +1,203 @@
|
||||
/datum/component/infective
|
||||
dupe_mode = COMPONENT_DUPE_ALLOWED
|
||||
var/list/datum/disease/diseases
|
||||
var/list/datum/disease/diseases //make sure these are the static, non-processing versions!
|
||||
var/expire_time
|
||||
var/required_clean_types = CLEAN_TYPE_DISEASE
|
||||
/// The infection is weak and can only infect on consumption with small chance
|
||||
var/is_weak = FALSE
|
||||
/// Chance of weak infection on consumption
|
||||
var/weak_infection_chance = 10
|
||||
|
||||
/datum/component/infective/Initialize(list/datum/disease/_diseases, expire_in)
|
||||
if(islist(_diseases))
|
||||
diseases = _diseases
|
||||
else
|
||||
diseases = list(_diseases)
|
||||
if(!diseases.len || isnull(diseases[1]))
|
||||
stack_trace("Infective component initialized without any diseases!")
|
||||
qdel(src)
|
||||
if(expire_in)
|
||||
expire_time = world.time + expire_in
|
||||
QDEL_IN(src, expire_in)
|
||||
|
||||
/datum/component/infective/Initialize(list/datum/disease/diseases, expire_in, weak = FALSE, weak_infection_chance = 10)
|
||||
if(!ismovable(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
if(!islist(diseases))
|
||||
diseases = list(diseases)
|
||||
|
||||
///Make sure the diseases list is populated with instances of diseases so that it doesn't have to be for each AddComponent call.
|
||||
for(var/datum/disease/disease as anything in diseases)
|
||||
if(!disease) //empty entry, remove.
|
||||
diseases -= disease
|
||||
if(ispath(disease, /datum/disease))
|
||||
var/datum/disease/instance = new disease
|
||||
diseases -= disease
|
||||
diseases += instance
|
||||
else if(!istype(disease))
|
||||
stack_trace("found [isdatum(disease) ? "an instance of [disease.type]" : disease] inside the diseases list argument for [type]")
|
||||
diseases -= disease
|
||||
|
||||
src.diseases = diseases
|
||||
|
||||
if(expire_in)
|
||||
expire_time = world.time + expire_in
|
||||
QDEL_IN(src, expire_in)
|
||||
|
||||
is_weak = weak
|
||||
src.weak_infection_chance = weak_infection_chance
|
||||
|
||||
/datum/component/infective/Destroy()
|
||||
QDEL_LIST(diseases)
|
||||
return ..()
|
||||
|
||||
/datum/component/infective/RegisterWithParent()
|
||||
if(is_weak && isitem(parent))
|
||||
RegisterSignal(parent, COMSIG_FOOD_EATEN, PROC_REF(try_infect_eat))
|
||||
RegisterSignal(parent, COMSIG_PILL_CONSUMED, PROC_REF(try_infect_eat))
|
||||
return
|
||||
var/static/list/disease_connections = list(
|
||||
COMSIG_ATOM_ENTERED = PROC_REF(try_infect_crossed),
|
||||
)
|
||||
AddComponent(/datum/component/connect_loc_behalf, parent, disease_connections)
|
||||
|
||||
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(clean))
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_BUCKLE, PROC_REF(try_infect_buckle))
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_BUMP, PROC_REF(try_infect_collide))
|
||||
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_IMPACT_ZONE, PROC_REF(try_infect_impact_zone))
|
||||
RegisterSignal(parent, COMSIG_ATOM_EXTRAPOLATOR_ACT, PROC_REF(extrapolation))
|
||||
|
||||
if(isitem(parent))
|
||||
RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(try_infect_attack))
|
||||
// RegisterSignal(parent, COMSIG_FOOD_EATEN, PROC_REF(try_infect_eat)) - TODO: Send signal when eating
|
||||
if(isitem(parent))
|
||||
RegisterSignal(parent, COMSIG_ITEM_ATTACK_ZONE, PROC_REF(try_infect_attack_zone))
|
||||
RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(try_infect_attack))
|
||||
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(try_infect_equipped))
|
||||
RegisterSignal(parent, COMSIG_FOOD_EATEN, PROC_REF(try_infect_eat))
|
||||
RegisterSignal(parent, COMSIG_PILL_CONSUMED, PROC_REF(try_infect_eat))
|
||||
if(istype(parent, /obj/item/reagent_containers/glass))
|
||||
RegisterSignal(parent, COMSIG_GLASS_DRANK, PROC_REF(try_infect_drink))
|
||||
if(isorgan(parent))
|
||||
RegisterSignal(parent, COMSIG_ORGAN_IMPLANTED, PROC_REF(on_organ_insertion))
|
||||
|
||||
/datum/component/infective/UnregisterFromParent()
|
||||
. = ..()
|
||||
UnregisterSignal(parent, list(
|
||||
COMSIG_FOOD_EATEN,
|
||||
COMSIG_PILL_CONSUMED,
|
||||
COMSIG_COMPONENT_CLEAN_ACT,
|
||||
COMSIG_MOVABLE_BUMP,
|
||||
COMSIG_MOVABLE_IMPACT_ZONE,
|
||||
COMSIG_ITEM_ATTACK_ZONE,
|
||||
COMSIG_ITEM_ATTACK,
|
||||
COMSIG_ITEM_EQUIPPED,
|
||||
COMSIG_GLASS_DRANK,
|
||||
COMSIG_ORGAN_IMPLANTED,
|
||||
COMSIG_ATOM_EXTRAPOLATOR_ACT
|
||||
))
|
||||
qdel(GetComponent(/datum/component/connect_loc_behalf))
|
||||
|
||||
/datum/component/infective/proc/on_organ_insertion(obj/item/organ/target, mob/living/carbon/receiver)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
for(var/datum/disease/disease in diseases)
|
||||
receiver.ForceContractDisease(disease)
|
||||
|
||||
qdel(src) // once organ is implanted delete the infective component
|
||||
|
||||
/datum/component/infective/proc/try_infect_eat(datum/source, mob/living/eater, mob/living/feeder)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(HAS_TRAIT(eater, TRAIT_STRONG_STOMACH))
|
||||
return
|
||||
|
||||
// eater.add_mood_event("disgust", /datum/mood_event/disgust/dirty_food)
|
||||
|
||||
for(var/datum/disease/disease as anything in diseases)
|
||||
if(is_weak && !prob(weak_infection_chance))
|
||||
continue
|
||||
// if(!disease.has_required_infectious_organ(eater, ORGAN_SLOT_STOMACH))
|
||||
// continue
|
||||
|
||||
eater.ForceContractDisease(disease)
|
||||
|
||||
if(!is_weak)
|
||||
try_infect(feeder, BP_L_ARM)
|
||||
|
||||
/datum/component/infective/proc/try_infect_drink(datum/source, mob/living/drinker, mob/living/feeder)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(HAS_TRAIT(drinker, TRAIT_STRONG_STOMACH))
|
||||
return
|
||||
|
||||
if(!is_weak)
|
||||
var/bodypart_type
|
||||
if(feeder.l_hand == source)
|
||||
bodypart_type = BP_R_ARM
|
||||
else if(feeder.r_hand == source)
|
||||
bodypart_type = BP_L_ARM
|
||||
else
|
||||
bodypart_type = BP_TORSO
|
||||
try_infect(feeder, bodypart_type)
|
||||
|
||||
for(var/datum/disease/disease as anything in diseases)
|
||||
if(is_weak && !prob(weak_infection_chance))
|
||||
continue
|
||||
// if(!disease.has_required_infectious_organ(drinker, ORGAN_SLOT_STOMACH))
|
||||
// continue
|
||||
|
||||
drinker.ForceContractDisease(disease)
|
||||
|
||||
/datum/component/infective/proc/clean(datum/source, clean_types)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
. = NONE
|
||||
if(clean_types & required_clean_types)
|
||||
qdel(src)
|
||||
return TRUE
|
||||
return COMPONENT_CLEANED|COMPONENT_CLEANED_GAIN_XP
|
||||
|
||||
/datum/component/infective/proc/try_infect_buckle(datum/source, mob/M, force)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(isliving(M))
|
||||
try_infect(M)
|
||||
|
||||
/datum/component/infective/proc/try_infect_collide(datum/source, atom/A)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/atom/movable/P = parent
|
||||
if(P.throwing)
|
||||
//this will be handled by try_infect_impact_zone()
|
||||
return
|
||||
if(isliving(A))
|
||||
try_infect(A)
|
||||
|
||||
/datum/component/infective/proc/try_infect_impact_zone(datum/source, mob/living/target, hit_zone)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
try_infect(target, hit_zone)
|
||||
|
||||
/datum/component/infective/proc/try_infect_attack_zone(obj/item/source, mob/living/carbon/target, mob/living/user, hit_zone)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
try_infect(target, hit_zone)
|
||||
|
||||
/datum/component/infective/proc/try_infect_attack(obj/item/source, mob/living/target, mob/living/user)
|
||||
SIGNAL_HANDLER
|
||||
if(source.loc == user)
|
||||
var/obj/item/organ/external/hand = user.get_active_hand()
|
||||
if(!istype(hand)) //Some fuckery is going on here.
|
||||
return
|
||||
|
||||
if(hand.body_part == HAND_LEFT)
|
||||
try_infect(user, BP_L_HAND)
|
||||
else
|
||||
try_infect(user, BP_R_HAND)
|
||||
|
||||
/datum/component/infective/proc/try_infect_equipped(datum/source, mob/living/L, slot)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/old_bio_armor
|
||||
if(isitem(parent))
|
||||
//if you are putting an infective item on, it obviously will not protect you, so set its bio armor low enough that it will never block ContactContractDisease()
|
||||
var/obj/item/equipped_item = parent
|
||||
old_bio_armor = equipped_item.armor["bio"]
|
||||
equipped_item.armor["bio"] = 0
|
||||
|
||||
try_infect(L, slot2body_zone(slot))
|
||||
|
||||
if(isitem(parent))
|
||||
var/obj/item/equipped_item = parent
|
||||
equipped_item.armor["bio"] = old_bio_armor
|
||||
|
||||
/datum/component/infective/proc/try_infect_crossed(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
||||
SIGNAL_HANDLER
|
||||
@@ -45,35 +205,18 @@
|
||||
if(isliving(arrived))
|
||||
try_infect(arrived, BP_L_FOOT)
|
||||
|
||||
/*
|
||||
/datum/component/proc/try_infect_eat(datum/source, mob/living/eater, mob/living/feeder)
|
||||
/datum/component/infective/proc/try_infect_streak(datum/source, list/directions, list/output_diseases)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
for(var/datum/disease/D in diseases)
|
||||
eater.ForceContractDisease(D)
|
||||
try_infect(feeder, BP_L_ARM)
|
||||
*/
|
||||
|
||||
/datum/component/infective/proc/try_infect_collide(datum/source, atom/A)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/atom/movable/P = parent
|
||||
if(P.throwing)
|
||||
// This will be handled by COMSIG_MOVABLE_IMPACT_ZONE, whenever we get that.
|
||||
// This blood is not infectable / does not have a diseases list
|
||||
if(!islist(output_diseases))
|
||||
return
|
||||
if(isliving(A))
|
||||
try_infect(A)
|
||||
|
||||
/datum/component/infective/proc/try_infect_attack(datum/source, mob/living/target, mob/living/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!iscarbon(target))
|
||||
try_infect(target)
|
||||
try_infect(user, BP_L_ARM)
|
||||
output_diseases |= diseases
|
||||
|
||||
/datum/component/infective/proc/try_infect(mob/living/L, target_zone)
|
||||
for(var/datum/disease/D in diseases)
|
||||
L.ContractDisease(D)
|
||||
for(var/V in diseases)
|
||||
L.ContractDisease(V, target_zone)
|
||||
|
||||
/datum/component/infective/proc/extrapolation(datum/source, mob/user, obj/item/extrapolator/extrapolator, dry_run = FALSE, list/result)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
Reference in New Issue
Block a user