diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 54d5e473b70..0fd95993ade 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -154,6 +154,21 @@ #define MOB_TINY 5 #define MOB_MINISCULE 1 +// Gluttony levels. Used for eating items and mobs. +#define GLUT_NONE 0 // Cannot eat any mob or item. +#define GLUT_TINY 1 // Eat anything tiny and smaller +#define GLUT_SMALLER 2 // Eat anything smaller than we are +#define GLUT_ANYTHING 4 // Eat anything, ever + +#define GLUT_ITEM_TINY 8 // Eat items with a w_class of small or smaller +#define GLUT_ITEM_NORMAL 16 // Eat items with a w_class of normal or smaller +#define GLUT_ITEM_ANYTHING 32 // Eat any item +#define GLUT_PROJECTILE_VOMIT 64 // When vomitting, does it fly out? + +// Devour speeds, returned by can_devour() +#define DEVOUR_SLOW 1 +#define DEVOUR_FAST 2 + #define TINT_NONE 0 #define TINT_MODERATE 1 #define TINT_HEAVY 2 diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 322db95d5d7..40e2c74f108 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -561,3 +561,83 @@ return ingest(holder, ingested, amount, multiplier, copy) if(chem_type == CHEM_TOUCH) return holder.trans_to_holder(touching, amount, multiplier, copy) + + +/mob/living/carbon/devour(atom/movable/victim) + var/can_eat = can_devour(victim) + if(!can_eat) + return FALSE + var/eat_speed = 100 + if(can_eat == DEVOUR_FAST) + eat_speed = 30 + visible_message(SPAN_DANGER("\The [src] is attempting to devour \the [victim] whole!")) + var/action_target = victim + if(isobj(victim)) + action_target = src + if(!do_mob(src, action_target, eat_speed)) + return FALSE + visible_message(SPAN_DANGER("\The [src] devours \the [victim] whole!")) + if(ismob(victim)) + add_attack_logs(src, victim, "devoured") + else + drop_from_inventory(victim) + move_to_stomach(victim) + return TRUE + +/mob/living/carbon/proc/can_devour(atom/movable/victim, var/silent = FALSE) + + if(!should_have_organ(O_STOMACH)) + return FALSE + + var/obj/item/organ/internal/stomach/stomach = get_internal_organ(O_STOMACH) + if(!istype(stomach) || stomach.is_broken()) + if(!silent) + to_chat(src, SPAN_WARNING("Your stomach is not functional!")) + return FALSE + + if(!stomach.can_eat_atom(victim)) + if(!silent) + to_chat(src, SPAN_WARNING("You are not capable of devouring \the [victim] whole!")) + return FALSE + + if(stomach.is_full(victim)) + if(!silent) + to_chat(src, SPAN_WARNING("Your [stomach.name] is full!")) + return FALSE + + return stomach.get_devour_time(victim) + +/mob/living/carbon/proc/move_to_stomach(atom/movable/victim) + SHOULD_CALL_PARENT(FALSE) + var/mob/mob_victim = victim + if(istype(mob_victim, /obj/item/holder)) + mob_victim = locate(/mob) in mob_victim + if(mob_victim && mob_victim != victim) + stomach_contents.Add(mob_victim) + qdel(victim) + else + stomach_contents.Add(victim) + +/mob/living/carbon/empty_stomach(var/blood_vomit) + + for(var/atom/movable/thing in stomach_contents) + thing.dropInto(get_turf(src)) + if(species.gluttonous & GLUT_PROJECTILE_VOMIT) + thing.throw_at(get_edge_target_turf(src,dir),7,7,src) + + visible_message(SPAN_DANGER("\The [src] throws up!"),SPAN_DANGER("You throw up!")) + playsound(loc, 'sound/effects/splat.ogg', 50, 1) + Stun(5) + + var/turf/simulated/T = get_turf(src) //TODO: Make add_blood_floor remove blood from human mobs + if(istype(T)) + if(blood_vomit) + T.add_blood_floor(src) + else + T.add_vomit_floor(src, 1) + if(blood_vomit) + if(getBruteLoss() < 50) + adjustBruteLoss(3) + else + adjust_nutrition(-40) + adjustToxLoss(-3) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 0d69192ef0c..8981ae46c4c 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1109,7 +1109,7 @@ if(species.icon_scale_x != 1 || species.icon_scale_y != 1) update_transform() - if(!default_colour || !species.apply_default_colours(src)) + if(!default_colour) r_skin = 0 g_skin = 0 b_skin = 0 @@ -1126,6 +1126,8 @@ species.create_organs(src) + species.apply_default_colours(src) + species.handle_post_spawn(src) maxHealth = species.total_health @@ -1687,3 +1689,38 @@ for(var/obj/item/accessory in uniform.accessories) if(accessory.is_mob_movement_sensitive()) LAZYADD(., accessory) + +/mob/living/carbon/human/empty_stomach(var/blood_vomit) + if(should_have_organ(O_STOMACH)) + var/turf/dumping_loc = get_turf(src) + for(var/atom/movable/thing as anything in stomach_contents) + thing.dropInto(dumping_loc) + if(species.gluttonous & GLUT_PROJECTILE_VOMIT) + thing.throw_at(get_edge_target_turf(src,dir),7,7,src) + if(!blood_vomit && should_have_organ(O_LIVER)) + var/obj/item/organ/internal/L = get_internal_organ(O_LIVER) + blood_vomit = !istype(L) || L.is_broken() + ..(blood_vomit) + +/mob/living/carbon/human/move_to_stomach(atom/movable/victim) + var/obj/item/organ/internal/stomach = get_internal_organ(O_STOMACH) + if(istype(stomach)) + var/mob/mob_victim = victim + if(istype(mob_victim, /obj/item/holder)) + mob_victim = locate(/mob) in mob_victim + if(mob_victim && victim != mob_victim) + mob_victim.forceMove(stomach) + qdel(victim) + else + victim.forceMove(stomach) + +/mob/living/carbon/human/attackby(obj/item/I, mob/user) + var/user_zone_sel = user.zone_sel?.selecting + if(user == src && user_zone_sel == O_MOUTH && can_devour(I, silent = TRUE)) + var/obj/item/blocked = check_mouth_coverage() + if(blocked) + to_chat(user, SPAN_WARNING("\The [blocked] is in the way!")) + return TRUE + if(devour(I)) + return TRUE + return ..() diff --git a/code/modules/mob/living/carbon/human/species/outsider/vox.dm b/code/modules/mob/living/carbon/human/species/outsider/vox.dm index 15cc100be58..88ef397c399 100644 --- a/code/modules/mob/living/carbon/human/species/outsider/vox.dm +++ b/code/modules/mob/living/carbon/human/species/outsider/vox.dm @@ -51,7 +51,7 @@ cold_level_2 = 150 //Default 200 cold_level_3 = 90 //Default 120 - gluttonous = 1 + gluttonous = GLUT_TINY|GLUT_ITEM_NORMAL breath_type = "nitrogen" poison_type = "oxygen" @@ -93,6 +93,7 @@ O_KIDNEYS = /obj/item/organ/internal/kidneys/vox, O_BRAIN = /obj/item/organ/internal/brain/vox, O_EYES = /obj/item/organ/internal/eyes, + O_STOMACH = /obj/item/organ/internal/stomach/vox ) genders = list(NEUTER) @@ -184,12 +185,13 @@ H.b_skin = hex2num(copytext(skin_color,6,8)) var/scutes_color = "#BC7D3E" var/obj/item/organ/external/head = H.get_organ(BP_HEAD) - head.markings = list( - "Vox Beak" = list( - "color" = scutes_color, - "datum" = body_marking_styles_list["Vox Beak"] + if(head) + head.markings = list( + "Vox Beak" = list( + "color" = scutes_color, + "datum" = body_marking_styles_list["Vox Beak"] + ) ) - ) for(var/bp in list(BP_L_ARM, BP_L_HAND, BP_R_ARM, BP_R_HAND, BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) var/obj/item/organ/external/limb = H.get_organ(bp) if(limb) diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index a95e1475277..d06aa893009 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -214,7 +214,8 @@ var/primitive_form // Lesser form, if any (ie. monkey for humans) var/greater_form // Greater form, if any, ie. human for monkeys. var/holder_type - var/gluttonous // Can eat some mobs. 1 for mice, 2 for monkeys, 3 for people. + var/gluttonous = GLUT_NONE // Can eat some mobs. Values can be GLUT_TINY, GLUT_SMALLER, GLUT_ANYTHING, GLUT_ITEM_TINY, GLUT_ITEM_NORMAL, GLUT_ITEM_ANYTHING, GLUT_PROJECTILE_VOMIT + var/stomach_capacity = 5 // How much stuff they can stick in their stomach var/rarity_value = 1 // Relative rarity/collector value for this species. var/economic_modifier = 2 // How much money this species makes diff --git a/code/modules/mob/living/carbon/human/species/station/station.dm b/code/modules/mob/living/carbon/human/species/station/station.dm index 617bd92994d..4704949531d 100644 --- a/code/modules/mob/living/carbon/human/species/station/station.dm +++ b/code/modules/mob/living/carbon/human/species/station/station.dm @@ -56,7 +56,7 @@ primitive_form = SPECIES_MONKEY_UNATHI darksight = 3 ambiguous_genders = TRUE - gluttonous = 1 + gluttonous = GLUT_TINY slowdown = 0.5 total_health = 125 brute_mod = 0.85 @@ -190,7 +190,7 @@ burn_mod = 1.15 flash_mod = 1.1 metabolic_rate = 1.1 - gluttonous = 1 + gluttonous = GLUT_TINY num_alternate_languages = 3 secondary_langs = list(LANGUAGE_SIIK, LANGUAGE_AKHANI, LANGUAGE_ALAI) name_language = LANGUAGE_SIIK @@ -389,7 +389,7 @@ flash_mod = 2 flash_burn = 15 //flashing a zaddat probably counts as police brutality metabolic_rate = 0.7 //did u know if your ancestors starved ur body will actually start in starvation mode? - gluttonous = 1 + gluttonous = GLUT_TINY taste_sensitivity = TASTE_SENSITIVE num_alternate_languages = 3 secondary_langs = list(LANGUAGE_ZADDAT, LANGUAGE_UNATHI) diff --git a/code/modules/mob/living/carbon/human/species/station/teshari.dm b/code/modules/mob/living/carbon/human/species/station/teshari.dm index 8a16bdeb29a..8f44c90933a 100644 --- a/code/modules/mob/living/carbon/human/species/station/teshari.dm +++ b/code/modules/mob/living/carbon/human/species/station/teshari.dm @@ -52,7 +52,7 @@ pass_flags = PASSTABLE holder_type = /obj/item/holder/human // short_sighted = 1 - gluttonous = 1 + gluttonous = GLUT_TINY blood_volume = 400 hunger_factor = 0.2 hearboost = 1 //Big heckin chonker ultragigamega-ears. diff --git a/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm b/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm index e20ac6bc22d..0e156890db2 100644 --- a/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm +++ b/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm @@ -12,7 +12,7 @@ has_fine_manipulation = 0 siemens_coefficient = 0 - gluttonous = 2 + gluttonous = GLUT_SMALLER brute_mod = 0.5 // Hardened carapace. burn_mod = 2 // Weak to fire. diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 74604711bb4..b8987a74843 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -760,33 +760,7 @@ sleep(150) //15 seconds until second warning to_chat(src, "You feel like you are about to throw up!") sleep(100) //and you have 10 more for mad dash to the bucket - - //Damaged livers cause you to vomit blood. - if(!blood_vomit) - if(ishuman(src)) - var/mob/living/carbon/human/H = src - if(!H.isSynthetic()) - var/obj/item/organ/internal/liver/L = H.internal_organs_by_name["liver"] - if(!L || L.is_broken()) - blood_vomit = 1 - - Stun(5) - src.visible_message("[src] throws up!","You throw up!") - playsound(src, 'sound/effects/splat.ogg', 50, 1) - - var/turf/simulated/T = get_turf(src) //TODO: Make add_blood_floor remove blood from human mobs - if(istype(T)) - if(blood_vomit) - T.add_blood_floor(src) - else - T.add_vomit_floor(src, 1) - - if(blood_vomit) - if(getBruteLoss() < 50) - adjustBruteLoss(3) - else - adjust_nutrition(-40) - adjustToxLoss(-3) + empty_stomach(blood_vomit) spawn(350) lastpuke = 0 @@ -1173,3 +1147,6 @@ /mob/living/proc/IWasAttackedBy(var/mob/living/attacker) return + +/mob/living/proc/empty_stomach() + return diff --git a/code/modules/mob/living/organs.dm b/code/modules/mob/living/organs.dm index 76694553f6c..b9089956522 100644 --- a/code/modules/mob/living/organs.dm +++ b/code/modules/mob/living/organs.dm @@ -16,6 +16,9 @@ zone = BP_HEAD return organs_by_name[zone] +/mob/living/proc/get_internal_organ(var/zone) + return zone && internal_organs_by_name[zone] + /mob/living/gib() for(var/path in internal_organs) if(ispath(path)) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 9a3265b6445..5721976fd75 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1265,4 +1265,7 @@ return TRUE /mob/proc/hearing_boost_range() - return hearing_boost_range \ No newline at end of file + return hearing_boost_range + +/mob/proc/devour(atom/movable/victim) + return diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index 66c8cd4db6e..edfa588a131 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -333,8 +333,8 @@ pin_down(affecting, assailant) //clicking on yourself while grabbing them - if(M == assailant && state >= GRAB_AGGRESSIVE) - devour(affecting, assailant) + if(M == assailant && state >= GRAB_AGGRESSIVE && assailant.devour(affecting)) + qdel(src) /obj/item/grab/dropped() loc = null diff --git a/code/modules/mob/mob_grab_specials.dm b/code/modules/mob/mob_grab_specials.dm index 7bbc72844d2..0c62b9402f6 100644 --- a/code/modules/mob/mob_grab_specials.dm +++ b/code/modules/mob/mob_grab_specials.dm @@ -148,27 +148,3 @@ step_to(attacker, target) attacker.set_dir(EAST) //face the victim target.set_dir(SOUTH) //face up - -/obj/item/grab/proc/devour(mob/target, mob/user) - var/can_eat - if((FAT in user.mutations) && ismini(target)) - can_eat = 1 - else - var/mob/living/carbon/human/H = user - if(istype(H) && H.species.gluttonous) - if(H.species.gluttonous == 2) - can_eat = 2 - else if((H.mob_size > target.mob_size) && !ishuman(target) && ismini(target)) - can_eat = 1 - - if(can_eat) - var/mob/living/carbon/attacker = user - user.visible_message("[user] is attempting to devour [target]!") - if(can_eat == 2) - if(!do_mob(user, target)||!do_after(user, 30)) return - else - if(!do_mob(user, target)||!do_after(user, 70)) return - user.visible_message("[user] devours [target]!") - target.loc = user - attacker.stomach_contents.Add(target) - qdel(src) \ No newline at end of file diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 2d467bc99f2..885666e65a8 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -26,13 +26,6 @@ return L.mob_size <= MOB_TINY return 0 - -/proc/ismini(A) - if(A && istype(A, /mob/living)) - var/mob/living/L = A - return L.mob_size <= MOB_MINISCULE - return 0 - /mob/living/silicon/isSynthetic() return 1 @@ -683,4 +676,4 @@ var/global/image/backplane item.screen_loc = screen_place /mob/proc/can_feed() - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/organs/internal/heart.dm b/code/modules/organs/internal/heart.dm index 68ba76605da..7d9f47cb4e1 100644 --- a/code/modules/organs/internal/heart.dm +++ b/code/modules/organs/internal/heart.dm @@ -42,12 +42,9 @@ organ_tag = O_PUMP dead_icon = "pump-off" robotic = ORGAN_ROBOT - standard_pulse_level = PULSE_NONE -/obj/item/organ/internal/stomach/machine/handle_organ_proc_special() +/obj/item/organ/internal/heart/machine/handle_organ_proc_special() ..() if(owner && owner.stat != DEAD) owner.bodytemperature += round(owner.robobody_count * 0.25, 0.1) - - return diff --git a/code/modules/organs/internal/stomach.dm b/code/modules/organs/internal/stomach.dm index 2ebfefea615..7c65bfaf859 100644 --- a/code/modules/organs/internal/stomach.dm +++ b/code/modules/organs/internal/stomach.dm @@ -3,29 +3,35 @@ icon_state = "stomach" organ_tag = O_STOMACH parent_organ = BP_GROIN - unacidable = TRUE // Don't melt when holding your acid, dangit. var/acidtype = "stomacid" // Incase you want some stomach organ with, say, polyacid instead, or sulphuric. var/max_acid_volume = 30 - + var/stomach_capacity var/deadly_hold = TRUE // Does the stomach do damage to mobs eaten by its owner? Xenos should probably have this FALSE. /obj/item/organ/internal/stomach/Initialize() . = ..() - if(reagents) - reagents.maximum_volume = 30 - else + if(!reagents) create_reagents(30) + reagents.maximum_volume = 30 + return INITIALIZE_HINT_LATELOAD + +/obj/item/organ/internal/stomach/LateInitialize() + . = ..() + if(species && !stomach_capacity) + stomach_capacity = species.stomach_capacity /obj/item/organ/internal/stomach/handle_organ_proc_special() - if(owner && istype(owner, /mob/living/carbon/human)) + if(istype(owner, /mob/living/carbon/human)) if(reagents) if(reagents.total_volume + 2 < max_acid_volume && prob(20)) reagents.add_reagent(acidtype, rand(1,2)) - - for(var/mob/living/L in owner.stomach_contents) // Splashes mobs inside with acid. Twice as effective as being splashed with the same acid outside the body. - reagents.trans_to(L, 2, 2, 0) + if(reagents.total_volume) + for(var/mob/living/L in owner.stomach_contents) // Splashes mobs inside with acid. Twice as effective as being splashed with the same acid outside the body. + reagents.trans_to(L, 2, 2, 0) + for(var/atom/movable/AM in contents) + reagents.touch(AM) if(is_broken() && prob(1)) owner.custom_pain("There's a twisting pain in your abdomen!",1) @@ -45,6 +51,46 @@ owner.adjustToxLoss(3) owner.vomit(FALSE, TRUE) +/obj/item/organ/internal/stomach/proc/can_eat_atom(var/atom/movable/food) + return !isnull(get_devour_time(food)) + +/obj/item/organ/internal/stomach/proc/is_full(var/atom/movable/food) + var/total = round(reagents?.total_volume / 10) + for(var/a in contents + food) + if(ismob(a)) + var/mob/M = a + total += M.mob_size + else if(isobj(a)) + var/obj/item/I = a + total += I.get_storage_cost() + else + continue + if(total > stomach_capacity) + return TRUE + return FALSE + +/obj/item/organ/internal/stomach/proc/get_devour_time(var/atom/movable/food) + if(istype(food, /obj/item/holder)) // Don't eat holders. They are special. + food = locate(/mob) in food + if(iscarbon(food) || isanimal(food)) + var/mob/living/L = food + if((species.gluttonous & GLUT_TINY) && (L.mob_size <= MOB_TINY) && !ishuman(food)) // Anything MOB_TINY or smaller + return DEVOUR_SLOW + else if((species.gluttonous & GLUT_SMALLER) && owner.mob_size > L.mob_size) // Anything we're larger than + return DEVOUR_SLOW + else if(species.gluttonous & GLUT_ANYTHING) // Eat anything ever + return DEVOUR_FAST + else if(istype(food, /obj/item)) + var/obj/item/I = food + var/cost = I.get_storage_cost() + if(cost < ITEMSIZE_COST_NO_CONTAINER) + if((species.gluttonous & GLUT_ITEM_TINY) && cost < ITEMSIZE_COST_NORMAL) + return DEVOUR_SLOW + else if((species.gluttonous & GLUT_ITEM_NORMAL) && cost <= ITEMSIZE_COST_NORMAL) + return DEVOUR_SLOW + else if(species.gluttonous & GLUT_ITEM_ANYTHING) + return DEVOUR_FAST + /obj/item/organ/internal/stomach/xeno color = "#555555" acidtype = "pacid" @@ -53,20 +99,14 @@ name = "reagent cycler" icon_state = "cycler" organ_tag = O_CYCLER - robotic = ORGAN_ROBOT - acidtype = "sacid" /obj/item/organ/internal/stomach/machine/handle_organ_proc_special() ..() if(owner && owner.stat != DEAD) owner.bodytemperature += round(owner.robobody_count * 0.25, 0.1) - if(ishuman(owner)) var/mob/living/carbon/human/H = owner - if(H.ingested?.total_volume && H.bloodstr) H.ingested.trans_to_holder(H.bloodstr, rand(2,5)) - - return diff --git a/code/modules/organs/subtypes/vox.dm b/code/modules/organs/subtypes/vox.dm index 52b62005581..42bc9de63e3 100644 --- a/code/modules/organs/subtypes/vox.dm +++ b/code/modules/organs/subtypes/vox.dm @@ -29,3 +29,32 @@ /obj/item/organ/external/groin/vox //vox have an extended ribcage for extra protection. encased = "lower ribcage" + +/obj/item/organ/internal/stomach/vox + name = "processing tuble" + parent_organ = BP_TORSO + color = "#0033cc" + acidtype = "voxenzyme" + unacidable = TRUE + stomach_capacity = 20 + // Items to convert into nutriment during processing. + var/static/list/convert_into_nutriment = list( + /obj/item/trash = 10, + /obj/item/flame/candle = 5, + /obj/item/ore = 5, + /obj/item/soap = 5, + /obj/item/material/shard/shrapnel = 3 + ) + +/obj/item/organ/internal/stomach/vox/handle_organ_proc_special() + if(istype(owner, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = owner + for(var/obj/item/thing in contents) + if(thing.unacidable) + continue + for(var/check_type in convert_into_nutriment) + if(istype(thing, check_type)) + H.ingested.add_reagent("nutriment", convert_into_nutriment[check_type]) + qdel(thing) + break + . = ..()