diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index 51579b4d88..91dafd3cf4 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -71,7 +71,8 @@ #define COMSIG_MOVABLE_MOVED "movable_moved" //from base of atom/movable/Moved(): (/atom, dir) #define COMSIG_MOVABLE_CROSSED "movable_crossed" //from base of atom/movable/Crossed(): (/atom/movable) #define COMSIG_MOVABLE_COLLIDE "movable_collide" //from base of atom/movable/Collide(): (/atom) -#define COMSIG_MOVABLE_IMPACT "movable_impact" //from base of atom/movable/throw_impact(): (/atom, throwingdatum) +#define COMSIG_MOVABLE_IMPACT "movable_impact" //from base of atom/movable/throw_impact(): (/atom/hit_atom, /datum/thrownthing/throwingdatum) +#define COMSIG_MOVABLE_IMPACT_ZONE "item_impact_zone" //from base of mob/living/hitby(): (mob/living/target, hit_zone) #define COMSIG_MOVABLE_BUCKLE "buckle" //from base of atom/movable/buckle_mob(): (mob, force) #define COMSIG_MOVABLE_UNBUCKLE "unbuckle" //from base of atom/movable/unbuckle_mob(): (mob, force) @@ -80,7 +81,9 @@ #define COMSIG_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (/mob) #define COMSIG_ITEM_ATTACK_OBJ "item_attack_obj" //from base of obj/item/attack_obj(): (/obj, /mob) #define COMSIG_ITEM_EQUIPPED "item_equip" //from base of obj/item/equipped(): (/mob/equipper, slot) -#define COMSIG_ITEM_DROPPED "item_drop" //from base of obj/item/dropped(): (/mob/dropper) +#define COMSIG_ITEM_DROPPED "item_drop" +#define COMSIG_ITEM_PICKUP "item_pickup" //from base of obj/item/pickup(): (/mob/taker) +#define COMSIG_ITEM_ATTACK_ZONE "item_attack_zone" //from base of mob/living/carbon/attacked_by(): (mob/living/carbon/target, mob/living/user, hit_zone) // /obj/item/clothing signals #define COMSIG_SHOES_STEP_ACTION "shoes_step_action" //from base of obj/item/clothing/shoes/proc/step_action(): () @@ -101,3 +104,6 @@ //Wet floors #define COMSIG_TURF_IS_WET "check_turf_wet" //(): Returns bitflags of wet values. #define COMSIG_TURF_MAKE_DRY "make_turf_try" //(max_strength, immediate, duration_decrease = INFINITY): Returns bool. + +//Food +#define COMSIG_FOOD_EATEN "food_eaten" //from base of obj/item/reagent_containers/food/snacks/attack(): (mob/living/eater, mob/feeder) diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index d4d69df017..79f6a2c007 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -398,7 +398,28 @@ return covered_parts +/proc/slot2body_zone(slot) + switch(slot) + if(slot_back, slot_wear_suit, slot_w_uniform, slot_belt, slot_wear_id) + return BODY_ZONE_CHEST + if(slot_gloves, slot_hands, slot_handcuffed) + return pick(BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND) + + if(slot_head, slot_neck, slot_neck, slot_ears) + return BODY_ZONE_HEAD + + if(slot_wear_mask) + return BODY_ZONE_PRECISE_MOUTH + + if(slot_glasses) + return BODY_ZONE_PRECISE_EYES + + if(slot_shoes) + return pick(BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT) + + if(slot_legcuffed) + return pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) //adapted from http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ /proc/heat2colour(temp) diff --git a/code/datums/components/infective.dm b/code/datums/components/infective.dm index 521d9ece2c..f434ed6f52 100644 --- a/code/datums/components/infective.dm +++ b/code/datums/components/infective.dm @@ -1,12 +1,78 @@ /datum/component/infective + dupe_mode = COMPONENT_DUPE_ALLOWED var/list/datum/disease/diseases //make sure these are the static, non-processing versions! + var/expire_time + var/min_clean_strength = CLEAN_WEAK -/datum/component/infective/Initialize(list/datum/disease/_diseases) - RegisterSignal(COMSIG_MOVABLE_CROSSED, .proc/Infect) - diseases = _diseases +/datum/component/infective/Initialize(list/datum/disease/_diseases, expire_in) + if(islist(_diseases)) + diseases = diseases + else + diseases = list(_diseases) + if(expire_in) + expire_time = world.time + expire_in + QDEL_IN(src, expire_in) + RegisterSignal(COMSIG_MOVABLE_BUCKLE, .proc/try_infect_buckle) + RegisterSignal(COMSIG_MOVABLE_COLLIDE, .proc/try_infect_collide) + RegisterSignal(COMSIG_MOVABLE_CROSSED, .proc/try_infect_crossed) + RegisterSignal(COMSIG_ITEM_ATTACK_ZONE, .proc/try_infect_attack_zone) + RegisterSignal(COMSIG_ITEM_ATTACK, .proc/try_infect_attack) + RegisterSignal(COMSIG_ITEM_EQUIPPED, .proc/try_infect_equipped) + RegisterSignal(COMSIG_MOVABLE_IMPACT_ZONE, .proc/try_infect_impact_zone) + RegisterSignal(COMSIG_FOOD_EATEN, .proc/try_infect_eat) + RegisterSignal(COMSIG_COMPONENT_CLEAN_ACT, .proc/clean) -/datum/component/infective/proc/Infect(atom/movable/AM) - var/mob/living/carbon/victim = AM - if(istype(victim)) - for(var/datum/disease/D in diseases) - victim.ContactContractDisease(D, "feet") \ No newline at end of file +/datum/component/infective/proc/try_infect_eat(mob/living/eater, mob/living/feeder) + for(var/V in diseases) + eater.ForceContractDisease(V) + try_infect(feeder, BODY_ZONE_L_ARM) + +/datum/component/infective/proc/clean(clean_strength) + if(clean_strength >= min_clean_strength) + qdel(src) + +/datum/component/infective/proc/try_infect_buckle(mob/M, force) + if(isliving(M)) + try_infect(M) + +/datum/component/infective/proc/try_infect_collide(atom/A) + 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(mob/living/target, hit_zone) + try_infect(target, hit_zone) + +/datum/component/infective/proc/try_infect_attack_zone(mob/living/carbon/target, mob/living/user, hit_zone) + try_infect(user, BODY_ZONE_L_ARM) + try_infect(target, hit_zone) + +/datum/component/infective/proc/try_infect_attack(mob/living/target, mob/living/user) + if(!iscarbon(target)) //this case will be handled by try_infect_attack_zone + try_infect(target) + try_infect(user, BODY_ZONE_L_ARM) + +/datum/component/infective/proc/try_infect_equipped(mob/living/L, slot) + var/old_permeability + if(isitem(parent)) + //if you are putting an infective item on, it obviously will not protect you, so set its permeability high enough that it will never block ContactContractDisease() + var/obj/item/I = parent + old_permeability = I.permeability_coefficient + I.permeability_coefficient = 1.01 + + try_infect(L, slot2body_zone(slot)) + + if(isitem(parent)) + var/obj/item/I = parent + I.permeability_coefficient = old_permeability + +/datum/component/infective/proc/try_infect_crossed(atom/movable/M) + if(isliving(M)) + try_infect(M, BODY_ZONE_PRECISE_L_FOOT) + +/datum/component/infective/proc/try_infect(mob/living/L, target_zone) + for(var/V in diseases) + L.ContactContractDisease(V, target_zone) diff --git a/code/datums/diseases/_MobProcs.dm b/code/datums/diseases/_MobProcs.dm index 1a28c3eadf..7955f81e66 100644 --- a/code/datums/diseases/_MobProcs.dm +++ b/code/datums/diseases/_MobProcs.dm @@ -46,8 +46,12 @@ if(satiety>0 && prob(satiety/10)) // positive satiety makes it harder to contract the disease. return + + //Lefts and rights do not matter for arms and legs, they both run the same checks if(!target_zone) - target_zone = pick(head_ch;BODY_ZONE_HEAD,body_ch;"body",hands_ch;"hands",feet_ch;"feet") + target_zone = pick(head_ch;BODY_ZONE_HEAD,body_ch;BODY_ZONE_CHEST,hands_ch;BODY_ZONE_L_ARM,feet_ch;BODY_ZONE_L_LEG) + else + target_zone = check_zone(target_zone) if(ishuman(src)) var/mob/living/carbon/human/H = src @@ -63,14 +67,14 @@ if(passed && isobj(H.wear_neck)) Cl = H.wear_neck passed = prob((Cl.permeability_coefficient*100) - 1) - if("body") + if(BODY_ZONE_CHEST) if(isobj(H.wear_suit)) Cl = H.wear_suit passed = prob((Cl.permeability_coefficient*100) - 1) if(passed && isobj(slot_w_uniform)) Cl = slot_w_uniform passed = prob((Cl.permeability_coefficient*100) - 1) - if("hands") + if(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM) if(isobj(H.wear_suit) && H.wear_suit.body_parts_covered&HANDS) Cl = H.wear_suit passed = prob((Cl.permeability_coefficient*100) - 1) @@ -78,7 +82,7 @@ if(passed && isobj(H.gloves)) Cl = H.gloves passed = prob((Cl.permeability_coefficient*100) - 1) - if("feet") + if(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) if(isobj(H.wear_suit) && H.wear_suit.body_parts_covered&FEET) Cl = H.wear_suit passed = prob((Cl.permeability_coefficient*100) - 1) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 49407327f5..3ecdbb61a1 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -400,7 +400,7 @@ SSspacedrift.processing[src] = src return 1 -/atom/movable/proc/throw_impact(atom/hit_atom, throwingdatum) +/atom/movable/proc/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) set waitfor = 0 SendSignal(COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum) return hit_atom.hitby(src) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 92a4a93243..8c4c9a587e 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -440,7 +440,6 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) var/datum/action/A = X if(item_action_slot_check(slot, user)) //some items only give their actions buttons when in a specific slot. A.Grant(user) - SendSignal(COMSIG_ITEM_EQUIPPED,user,slot) item_flags |= IN_INVENTORY //sometimes we only want to grant the item's action if it's equipped in a specific slot. @@ -571,8 +570,9 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) else return -/obj/item/throw_impact(atom/A) +/obj/item/throw_impact(atom/A, datum/thrownthing/throwingdatum) if(A && !QDELETED(A)) + SendSignal(COMSIG_MOVABLE_IMPACT, A, throwingdatum) if(is_hot() && isliving(A)) var/mob/living/L = A L.IgniteMob() diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm index 54b10a6203..93b8635dc5 100644 --- a/code/game/objects/structures/beds_chairs/chair.dm +++ b/code/game/objects/structures/beds_chairs/chair.dm @@ -228,6 +228,7 @@ return usr.visible_message("[usr] grabs \the [src.name].", "You grab \the [src.name].") var/C = new item_chair(loc) + TransferComponents(C) usr.put_in_hands(C) qdel(src) @@ -279,6 +280,7 @@ user.visible_message("[user] rights \the [src.name].", "You right \the [name].") var/obj/structure/chair/C = new origin_type(get_turf(loc)) + TransferComponents(C) C.setDir(dir) qdel(src) diff --git a/code/modules/antagonists/disease/disease_abilities.dm b/code/modules/antagonists/disease/disease_abilities.dm index 8172d58d8e..9a792749a0 100644 --- a/code/modules/antagonists/disease/disease_abilities.dm +++ b/code/modules/antagonists/disease/disease_abilities.dm @@ -7,6 +7,7 @@ is currently following. GLOBAL_LIST_INIT(disease_ability_singletons, list( new /datum/disease_ability/action/cough(), new /datum/disease_ability/action/sneeze(), + new /datum/disease_ability/action/infect(), new /datum/disease_ability/symptom/cough(), new /datum/disease_ability/symptom/sneeze(),\ new /datum/disease_ability/symptom/hallucigen(), @@ -165,7 +166,6 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list( short_desc = "Force the host you are following to sneeze, spreading your infection to those in front of them." long_desc = "Force the host you are following to sneeze with extra force, spreading your infection to any victims in a 4 meter cone in front of your host.
Cooldown: 20 seconds" - /datum/action/cooldown/disease_sneeze name = "Sneeze" icon_icon = 'icons/mob/actions/actions_minor_antag.dmi' @@ -194,6 +194,47 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list( StartCooldown() return TRUE + +/datum/disease_ability/action/infect + name = "Secrete Infection" + actions = list(/datum/action/cooldown/disease_infect) + cost = 2 + required_total_points = 3 + short_desc = "Cause all objects your host is touching to become infectious for a limited time, spreading your infection to anyone who touches them." + long_desc = "Cause the host you are following to excrete an infective substance from their pores, causing all objects touching their skin to transmit your infection to anyone who touches them for the next 30 seconds. This includes the floor, if they are not wearing shoes, and any items they are holding, if they are not wearing gloves.
Cooldown: 40 seconds" + +/datum/action/cooldown/disease_infect + name = "Secrete Infection" + icon_icon = 'icons/mob/actions/actions_minor_antag.dmi' + button_icon_state = "infect" + desc = "Cause the host you are following to excrete an infective substance from their pores, causing all objects touching their skin to transmit your infection to anyone who touches them for the next 30 seconds.
Cooldown: 40 seconds" + cooldown_time = 400 + +/datum/action/cooldown/disease_infect/Trigger() + if(!..()) + return FALSE + var/mob/camera/disease/D = owner + var/mob/living/carbon/human/H = D.following_host + if(!H) + return FALSE + for(var/V in H.get_equipped_items(FALSE)) + var/obj/O = V + O.AddComponent(/datum/component/infective, D.disease_template, 300) + //no shoes? infect the floor. + if(!H.shoes) + var/turf/T = get_turf(H) + if(T && !isspaceturf(T)) + T.AddComponent(/datum/component/infective, D.disease_template, 300) + //no gloves? infect whatever we are holding. + if(!H.gloves) + for(var/V in H.held_items) + if(!V) + continue + var/obj/O = V + O.AddComponent(/datum/component/infective, D.disease_template, 300) + StartCooldown() + return TRUE + //passive symptom abilities /datum/disease_ability/symptom/cough diff --git a/code/modules/food_and_drinks/food/snacks.dm b/code/modules/food_and_drinks/food/snacks.dm index c8b9a2bd17..c6a6a11c1a 100644 --- a/code/modules/food_and_drinks/food/snacks.dm +++ b/code/modules/food_and_drinks/food/snacks.dm @@ -37,20 +37,20 @@ else ..() -/obj/item/reagent_containers/food/snacks/proc/On_Consume() - if(!usr) +/obj/item/reagent_containers/food/snacks/proc/On_Consume(mob/living/eater) + if(!eater) return if(!reagents.total_volume) - var/obj/item/trash_item = generate_trash(usr) + var/obj/item/trash_item = generate_trash(eater) qdel(src) - usr.put_in_hands(trash_item) + eater.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/user, def_zone) +/obj/item/reagent_containers/food/snacks/attack(mob/living/M, mob/living/user, def_zone) if(user.a_intent == INTENT_HARM) return ..() if(!eatverb) @@ -109,11 +109,12 @@ M.satiety -= junkiness playsound(M.loc,'sound/items/eatfood.ogg', rand(10,50), 1) if(reagents.total_volume) + SendSignal(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() + On_Consume(M) checkLiked(fraction, M) return 1 diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 75233a70c1..54ad7da184 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -75,6 +75,7 @@ affecting = get_bodypart(ran_zone(user.zone_selected)) if(!affecting) //missing limb? we select the first bodypart (you can never have zero, because of chest) affecting = bodyparts[1] + I.SendSignal(COMSIG_ITEM_ATTACK_ZONE, src, user, affecting) send_item_attack_message(I, user, affecting.name) if(I.force) //CIT CHANGES START HERE - combatmode and resting checks diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index eddfa997b8..253a6ed641 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -173,6 +173,8 @@ affecting = get_bodypart(ran_zone(user.zone_selected)) var/target_area = parse_zone(check_zone(user.zone_selected)) //our intended target + I.SendSignal(COMSIG_ITEM_ATTACK_ZONE, src, user, affecting) + SSblackbox.record_feedback("nested tally", "item_used_for_combat", 1, list("[I.force]", "[I.type]")) SSblackbox.record_feedback("tally", "zone_targeted", 1, target_area) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 085f43075c..37c2af605e 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -67,6 +67,7 @@ var/zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest var/dtype = BRUTE var/volume = I.get_volume_by_throwforce_and_or_w_class() + I.SendSignal(COMSIG_MOVABLE_IMPACT_ZONE, src, zone) dtype = I.damtype if (I.throwforce > 0) //If the weapon's throwforce is greater than zero... diff --git a/icons/mob/actions/actions_minor_antag.dmi b/icons/mob/actions/actions_minor_antag.dmi index 20daacd32c..66a3e06f31 100644 Binary files a/icons/mob/actions/actions_minor_antag.dmi and b/icons/mob/actions/actions_minor_antag.dmi differ