Files
GS13NG/code/modules/vore/eating/belly_obj.dm
T
Alphas00 8eb38e5244 Fatten mode and vore fatness, Modular jumpsuit options
Ported Fatten vore mode
Ported vore fatness hide function which makes you appear fatter based on how fat the people you've vored are
Readded grey jumpsuits as an option in loadouts
Readded grey jumpsuits to ClothesMate
The helplessness prefs for clothes now ignore modular clothes as did the original modular grey jumpsuit
2024-12-10 11:13:03 +01:00

735 lines
30 KiB
Plaintext

#define VORE_SOUND_FALLOFF 0.1
#define VORE_SOUND_RANGE 3
//
// Belly system 2.0, now using objects instead of datums because EH at datums.
// How many times have I rewritten bellies and vore now? -Aro
//
// If you change what variables are on this, then you need to update the copy() proc.
//
// Parent type of all the various "belly" varieties.
//
/obj/belly
name = "belly" // Name of this location
desc = "It's a belly! You're in it!" // Flavor text description of inside sight/sound/smells/feels.
rad_flags = RAD_NO_CONTAMINATE | RAD_PROTECT_CONTENTS
var/vore_sound = "Gulp" // Sound when ingesting someone
var/vore_verb = "ingest" // Verb for eating with this in messages
var/release_sound = "Splatter" // Sound for letting someone out.
var/human_prey_swallow_time = 100 // Time in deciseconds to swallow /mob/living/carbon/human
var/nonhuman_prey_swallow_time = 30 // Time in deciseconds to swallow anything else
var/emote_time = 60 SECONDS // How long between stomach emotes at prey
var/digest_brute = 2 // Brute damage per tick in digestion mode
var/digest_burn = 2 // Burn damage per tick in digestion mode
var/immutable = FALSE // Prevents this belly from being deleted
var/escapable = TRUE // Belly can be resisted out of at any time
var/escapetime = 20 SECONDS // Deciseconds, how long to escape this belly
var/digestchance = 0 // % Chance of stomach beginning to digest if prey struggles
var/absorbchance = 0 // % Chance of stomach beginning to absorb if prey struggles
var/escapechance = 0 // % Chance of prey beginning to escape if prey struggles.
var/can_taste = FALSE // If this belly prints the flavor of prey when it eats someone.
var/bulge_size = 0.25 // The minimum size the prey has to be in order to show up on examine.
// var/shrink_grow_size = 1 // This horribly named variable determines the minimum/maximum size it will shrink/grow prey to.
var/transferlocation // Location that the prey is released if they struggle and get dropped off.
var/transferchance = 0 // % Chance of prey being transferred to transfer location when resisting
var/autotransferchance = 0 // % Chance of prey being autotransferred to transfer location
var/autotransferwait = 10 // Time between trying to transfer.
var/swallow_time = 10 SECONDS // for mob transfering automation
var/vore_capacity = 1 // simple animal nom capacity
var/is_wet = TRUE // Is this belly inside slimy parts?
var/wet_loop = TRUE // Does this belly have a slimy internal loop?
//I don't think we've ever altered these lists. making them static until someone actually overrides them somewhere.
var/tmp/static/list/digest_modes = list(DM_HOLD,DM_DIGEST,DM_HEAL,DM_NOISY,DM_ABSORB,DM_UNABSORB,DM_FATTEN) // GS13 Edit- Added DM_FATTEN
var/tmp/mob/living/owner // The mob whose belly this is.
var/tmp/digest_mode = DM_HOLD // Current mode the belly is set to from digest_modes (+transform_modes if human)
var/tmp/next_process = 0 // Waiting for this SSbellies times_fired to process again.
var/tmp/list/items_preserved = list() // Stuff that wont digest so we shouldn't process it again.
var/tmp/next_emote = 0 // When we're supposed to print our next emote, as a belly controller tick #
var/tmp/recent_sound // Prevent audio spam
var/tmp/last_hearcheck = 0
var/tmp/list/hearing_mobs
// Don't forget to watch your commas at the end of each line if you change these.
var/list/struggle_messages_outside = list(
"%pred's %belly wobbles with a squirming meal.",
"%pred's %belly jostles with movement.",
"%pred's %belly briefly swells outward as someone pushes from inside.",
"%pred's %belly fidgets with a trapped victim.",
"%pred's %belly jiggles with motion from inside.",
"%pred's %belly sloshes around.",
"%pred's %belly gushes softly.",
"%pred's %belly lets out a wet squelch.")
var/list/struggle_messages_inside = list(
"Your useless squirming only causes %pred's slimy %belly to squelch over your body.",
"Your struggles only cause %pred's %belly to gush softly around you.",
"Your movement only causes %pred's %belly to slosh around you.",
"Your motion causes %pred's %belly to jiggle.",
"You fidget around inside of %pred's %belly.",
"You shove against the walls of %pred's %belly, making it briefly swell outward.",
"You jostle %pred's %belly with movement.",
"You squirm inside of %pred's %belly, making it wobble around.")
var/list/digest_messages_owner = list(
"You feel %prey's body succumb to your digestive system, which breaks it apart into soft slurry.",
"You hear a lewd glorp as your %belly muscles grind %prey into a warm pulp.",
"Your %belly lets out a rumble as it melts %prey into sludge.",
"You feel a soft gurgle as %prey's body loses form in your %belly. They're nothing but a soft mass of churning slop now.",
"Your %belly begins gushing %prey's remains through your system, adding some extra weight to your thighs.",
"Your %belly begins gushing %prey's remains through your system, adding some extra weight to your rump.",
"Your %belly begins gushing %prey's remains through your system, adding some extra weight to your belly.",
"Your %belly groans as %prey falls apart into a thick soup. You can feel their remains soon flowing deeper into your body to be absorbed.",
"Your %belly kneads on every fiber of %prey, softening them down into mush to fuel your next hunt.",
"Your %belly churns %prey down into a hot slush. You can feel the nutrients coursing through your digestive track with a series of long, wet glorps.")
var/list/digest_messages_prey = list(
"Your body succumbs to %pred's digestive system, which breaks you apart into soft slurry.",
"%pred's %belly lets out a lewd glorp as their muscles grind you into a warm pulp.",
"%pred's %belly lets out a rumble as it melts you into sludge.",
"%pred feels a soft gurgle as your body loses form in their %belly. You're nothing but a soft mass of churning slop now.",
"%pred's %belly begins gushing your remains through their system, adding some extra weight to %pred's thighs.",
"%pred's %belly begins gushing your remains through their system, adding some extra weight to %pred's rump.",
"%pred's %belly begins gushing your remains through their system, adding some extra weight to %pred's belly.",
"%pred's %belly groans as you fall apart into a thick soup. Your remains soon flow deeper into %pred's body to be absorbed.",
"%pred's %belly kneads on every fiber of your body, softening you down into mush to fuel their next hunt.",
"%pred's %belly churns you down into a hot slush. Your nutrient-rich remains course through their digestive track with a series of long, wet glorps.")
var/list/examine_messages = list(
"They have something solid in their %belly!",
"It looks like they have something in their %belly!")
//Mostly for being overridden on precreated bellies on mobs. Could be VV'd into
//a carbon's belly if someone really wanted. No UI for carbons to adjust this.
//List has indexes that are the digestion mode strings, and keys that are lists of strings.
var/tmp/list/emote_lists = list()
//For serialization, keep this updated AND IN ORDER OF VARS LISTED ABOVE AND IN DUPE AT THE BOTTOM!!, required for bellies to save correctly.
/obj/belly/vars_to_save()
return ..() + list(
"name",
"desc",
"vore_sound",
"vore_verb",
"release_sound",
"human_prey_swallow_time",
"nonhuman_prey_swallow_time",
"emote_time",
"digest_brute",
"digest_burn",
"immutable",
"escapable",
"escapetime",
"digestchance",
"absorbchance",
"escapechance",
"can_taste",
"bulge_size",
"transferlocation",
"transferchance",
"autotransferchance",
"autotransferwait",
"swallow_time",
"vore_capacity",
"struggle_messages_outside",
"struggle_messages_inside",
"digest_messages_owner",
"digest_messages_prey",
"examine_messages",
"emote_lists",
"is_wet",
"wet_loop"
)
//ommitted list
// "shrink_grow_size",
/obj/belly/Initialize(mapload)
. = ..()
take_ownership(src.loc)
/obj/belly/proc/take_ownership(var/newloc)
//If not, we're probably just in a prefs list or something.
if(isliving(newloc))
owner = loc
owner.vore_organs |= src
SSbellies.belly_list += src
/obj/belly/Destroy()
SSbellies.belly_list -= src
if(owner?.vore_organs)
owner.vore_organs -= src
if(owner.vore_selected == src)
owner.vore_selected = null
owner = null
. = ..()
// Called whenever an atom enters this belly
/obj/belly/Entered(atom/movable/thing, atom/OldLoc)
. = ..()
var/mob/living/L //for chat messages and blindness
if(isliving(thing))
L = thing
L.become_blind("belly_[REF(src)]")
if(OldLoc in contents)
return //Someone dropping something (or being stripdigested)
//Generic entered message
to_chat(owner,"<span class='notice'>[thing] slides into your [lowertext(name)].</span>")
//Sound w/ antispam flag setting
if(vore_sound && !recent_sound)
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
LAZYCLEARLIST(hearing_mobs)
for(var/mob/living/H in get_hearers_in_view(VORE_SOUND_RANGE, owner))
if(!H.client || !(H.client.prefs.cit_toggles & EATING_NOISES))
continue
LAZYADD(hearing_mobs, H)
last_hearcheck = world.time
for(var/mob/living/H in hearing_mobs)
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
var/sound/eating = GLOB.pred_vore_sounds[vore_sound]
SEND_SOUND(H,eating)
else if(H?.client && (H in contents))
var/sound/eating = GLOB.prey_vore_sounds[vore_sound]
SEND_SOUND(H,eating)
recent_sound = TRUE
if(L && desc)
to_chat(L, "<span class='notice'><B>[desc]</B></span>")
/obj/belly/Exited(atom/movable/AM, atom/newloc)
. = ..()
if(isliving(AM))
var/mob/living/L = AM
L.cure_blind("belly_[REF(src)]")
// Release all contents of this belly into the owning mob's location.
// If that location is another mob, contents are transferred into whichever of its bellies the owning mob is in.
// Returns the number of mobs so released.
/obj/belly/proc/release_all_contents(var/include_absorbed = FALSE, var/silent = FALSE)
// var/atom/destination = drop_location()
//Don't bother if we don't have contents
if(!contents.len)
return FALSE
var/count = 0
for(var/thing in contents)
var/atom/movable/AM = thing
if(isliving(AM))
var/mob/living/L = AM
var/mob/living/OW = owner
if(L.vore_flags & ABSORBED && !include_absorbed)
continue
L.vore_flags &= ~ABSORBED
L.stop_sound_channel(CHANNEL_PREYLOOP)
SEND_SIGNAL(OW, COMSIG_CLEAR_MOOD_EVENT, "fedpred", /datum/mood_event/fedpred)
SEND_SIGNAL(L, COMSIG_CLEAR_MOOD_EVENT, "fedprey", /datum/mood_event/fedprey)
SEND_SIGNAL(OW, COMSIG_ADD_MOOD_EVENT, "emptypred", /datum/mood_event/emptypred)
SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "emptyprey", /datum/mood_event/emptyprey)
count += release_specific_contents(AM, silent = TRUE)
//Clean up our own business
items_preserved.Cut()
owner.update_icons()
if(!silent)
if(release_sound && !recent_sound)
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
LAZYCLEARLIST(hearing_mobs)
for(var/mob/living/H in get_hearers_in_view(VORE_SOUND_RANGE, owner))
if(!H.client || !(H.client.prefs.cit_toggles & EATING_NOISES))
continue
LAZYADD(hearing_mobs, H)
last_hearcheck = world.time
for(var/mob/living/H in hearing_mobs)
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
var/sound/releasement = GLOB.pred_release_sounds[release_sound]
H.playsound_local(owner.loc, releasement, 75, TRUE)
else if(H?.client && (H in contents))
var/sound/releasement = GLOB.prey_release_sounds[release_sound]
SEND_SOUND(H,releasement)
recent_sound = TRUE
owner.visible_message("<font color='green'><b>[owner] expels everything from their [lowertext(name)]!</b></font>")
return count
// Release a specific atom from the contents of this belly into the owning mob's location.
// If that location is another mob, the atom is transferred into whichever of its bellies the owning mob is in.
// Returns the number of atoms so released.
/obj/belly/proc/release_specific_contents(var/atom/movable/M, var/silent = FALSE)
if (!(M in contents))
return FALSE // They weren't in this belly anyway
M.forceMove(drop_location()) // Move the belly contents into the same location as belly's owner.
items_preserved -= M
if(istype(M,/mob/living))
var/mob/living/ML = M
var/mob/living/OW = owner
if(ML.client)
ML.stop_sound_channel(CHANNEL_PREYLOOP) //Stop the internal loop, it'll restart if the isbelly check on next tick anyway
SEND_SIGNAL(OW, COMSIG_CLEAR_MOOD_EVENT, "fedpred", /datum/mood_event/fedpred)
SEND_SIGNAL(ML, COMSIG_CLEAR_MOOD_EVENT, "fedprey", /datum/mood_event/fedprey)
SEND_SIGNAL(OW, COMSIG_ADD_MOOD_EVENT, "emptypred", /datum/mood_event/emptypred)
SEND_SIGNAL(ML, COMSIG_ADD_MOOD_EVENT, "emptyprey", /datum/mood_event/emptyprey)
if((ML.vore_flags & ABSORBED))
ML.vore_flags &= ~(ABSORBED)
if(ishuman(M) && ishuman(OW))
var/mob/living/carbon/human/Prey = M
var/mob/living/carbon/human/Pred = OW
var/absorbed_count = 2 //Prey that we were, plus the pred gets a portion
for(var/mob/living/P in contents)
if((P.vore_flags & ABSORBED))
absorbed_count++
Pred.reagents.trans_to(Prey, Pred.reagents.total_volume / absorbed_count)
//Clean up our own business
owner.update_icons()
if(!silent)
if(release_sound && !recent_sound)
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
LAZYCLEARLIST(hearing_mobs)
for(var/mob/living/H in get_hearers_in_view(3, owner))
if(!H.client || !(H.client.prefs.cit_toggles & EATING_NOISES))
continue
LAZYADD(hearing_mobs, H)
last_hearcheck = world.time
for(var/mob/living/H in hearing_mobs)
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
var/sound/releasement = GLOB.pred_release_sounds[release_sound]
H.playsound_local(owner.loc, releasement, 75, TRUE)
else if(H?.client && (H in contents))
var/sound/releasement = GLOB.prey_release_sounds[release_sound]
SEND_SOUND(H,releasement)
recent_sound = TRUE
owner.visible_message("<font color='green'><b>[owner] expels [M] from their [lowertext(name)]!</b></font>")
return TRUE
// Actually perform the mechanics of devouring the tasty prey.
// The purpose of this method is to avoid duplicate code, and ensure that all necessary
// steps are taken.
/obj/belly/proc/nom_mob(var/mob/prey, var/mob/user)
if(owner.stat == DEAD)
return
if (prey.buckled)
prey.buckled.unbuckle_mob(prey,TRUE)
if(!isbelly(prey.loc))
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "fedpred", /datum/mood_event/fedpred)
SEND_SIGNAL(prey, COMSIG_ADD_MOOD_EVENT, "fedprey", /datum/mood_event/fedprey)
else
SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "emptypred", /datum/mood_event/emptypred)
SEND_SIGNAL(prey, COMSIG_CLEAR_MOOD_EVENT, "emptyprey", /datum/mood_event/emptyprey)
prey.forceMove(src)
owner.updateVRPanel()
for(var/mob/living/M in contents)
M.updateVRPanel()
// Setup the autotransfer checks if needed
if(transferlocation != null && autotransferchance > 0)
addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/belly, check_autotransfer), prey), autotransferwait)
/obj/belly/proc/check_autotransfer(var/mob/prey, var/obj/belly/target)
// Some sanity checks
if(transferlocation && (autotransferchance > 0) && (prey in contents))
if(prob(autotransferchance))
transfer_contents(prey, transferlocation)
else
// Didn't transfer, so wait before retrying
addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/belly, check_autotransfer), prey), autotransferwait)
//Transfers contents from one belly to another
/obj/belly/proc/transfer_contents(var/atom/movable/content, var/obj/belly/target, silent = FALSE)
if(!(content in src) || !istype(target))
return
content.forceMove(target)
if(vore_sound && !recent_sound && !silent)
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
LAZYCLEARLIST(hearing_mobs)
for(var/mob/living/H in get_hearers_in_view(VORE_SOUND_RANGE, owner))
if(!H.client || !(H.client.prefs.cit_toggles & EATING_NOISES))
continue
LAZYADD(hearing_mobs, H)
last_hearcheck = world.time
for(var/mob/living/H in hearing_mobs)
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
var/sound/eating = GLOB.pred_vore_sounds[vore_sound]
H.playsound_local(owner.loc, eating, 75, TRUE)
else if(H?.client && (H in contents))
var/sound/eating = GLOB.prey_vore_sounds[vore_sound]
SEND_SOUND(H,eating)
recent_sound = TRUE
owner.updateVRPanel()
for(var/mob/living/M in contents)
M.updateVRPanel()
// Get the line that should show up in Examine message if the owner of this belly
// is examined. By making this a proc, we not only take advantage of polymorphism,
// but can easily make the message vary based on how many people are inside, etc.
// Returns a string which shoul be appended to the Examine output.
/obj/belly/proc/get_examine_msg()
if(contents.len && examine_messages.len)
var/formatted_message
var/raw_message = pick(examine_messages)
var/total_bulge = 0
formatted_message = replacetext(raw_message,"%belly",lowertext(name))
formatted_message = replacetext(formatted_message,"%pred",owner)
formatted_message = replacetext(formatted_message,"%prey",english_list(contents))
for(var/mob/living/P in contents)
if(!(P.vore_flags & ABSORBED)) //This is required first, in case there's a person absorbed and not absorbed in a stomach.
total_bulge += P.mob_size
if(total_bulge >= bulge_size && bulge_size != 0)
return("<span class='warning'>[formatted_message]</span><BR>")
else
return ""
// The next function gets the messages set on the belly, in human-readable format.
// This is useful in customization boxes and such. The delimiter right now is \n\n so
// in message boxes, this looks nice and is easily delimited.
/obj/belly/proc/get_messages(var/type, var/delim = "\n\n")
ASSERT(type == "smo" || type == "smi" || type == "dmo" || type == "dmp" || type == "em")
var/list/raw_messages
switch(type)
if("smo")
raw_messages = struggle_messages_outside
if("smi")
raw_messages = struggle_messages_inside
if("dmo")
raw_messages = digest_messages_owner
if("dmp")
raw_messages = digest_messages_prey
if("em")
raw_messages = examine_messages
var/messages = raw_messages.Join(delim)
return messages
// The next function sets the messages on the belly, from human-readable var
// replacement strings and linebreaks as delimiters (two \n\n by default).
// They also sanitize the messages.
/obj/belly/proc/set_messages(var/raw_text, var/type, var/delim = "\n\n")
ASSERT(type == "smo" || type == "smi" || type == "dmo" || type == "dmp" || type == "em")
var/list/raw_list = splittext(html_encode(raw_text),delim)
if(raw_list.len > 10)
raw_list.Cut(11)
testing("[owner] tried to set [lowertext(name)] with 11+ messages")
for(var/i = 1, i <= raw_list.len, i++)
if(length(raw_list[i]) > 160 || length(raw_list[i]) < 10) //160 is fudged value due to htmlencoding increasing the size
raw_list.Cut(i,i)
testing("[owner] tried to set [lowertext(name)] with >121 or <10 char message")
else
raw_list[i] = readd_quotes(raw_list[i])
//Also fix % sign for var replacement
raw_list[i] = replacetext(raw_list[i],"&#37;","%")
ASSERT(raw_list.len <= 10) //Sanity
switch(type)
if("smo")
struggle_messages_outside = raw_list
if("smi")
struggle_messages_inside = raw_list
if("dmo")
digest_messages_owner = raw_list
if("dmp")
digest_messages_prey = raw_list
if("em")
examine_messages = raw_list
return
// Handle the death of a mob via digestion.
// Called from the process_Life() methods of bellies that digest prey.
// Default implementation calls M.death() and removes from internal contents.
// Indigestable items are removed, and M is deleted.
/obj/belly/proc/digestion_death(var/mob/living/M)
//M.death(1) // "Stop it he's already dead..." Basically redundant and the reason behind screaming mouse carcasses.
if(M.ckey)
message_admins("[key_name(owner)] has digested [key_name(M)] in their [lowertext(name)] ([owner ? "<a href='?_src_=holder;adminplayerobservecoodjump=1;X=[owner.x];Y=[owner.y];Z=[owner.z]'>JMP</a>" : "null"])")
log_attack("[key_name(owner)] digested [key_name(M)].")
// If digested prey is also a pred... anyone inside their bellies gets moved up.
if(has_vore_belly(M))
M.release_vore_contents(include_absorbed = TRUE, silent = TRUE)
//Drop all items into the belly
for(var/obj/item/W in M)
if(!M.dropItemToGround(W))
qdel(W)
//Incase they have the loop going, let's double check to stop it.
M.stop_sound_channel(CHANNEL_PREYLOOP)
// Delete the digested mob
qdel(M)
//Update owner
owner.updateVRPanel()
// Handle a mob being absorbed
/obj/belly/proc/absorb_living(var/mob/living/M)
M.vore_flags |= ABSORBED
to_chat(M,"<span class='notice'>[owner]'s [lowertext(name)] absorbs your body, making you part of them.</span>")
to_chat(owner,"<span class='notice'>Your [lowertext(name)] absorbs [M]'s body, making them part of you.</span>")
if(M.loc != src)
M.forceMove(src)
//Seek out absorbed prey of the prey, absorb them too.
//This in particular will recurse oddly because if there is absorbed prey of prey of prey...
//it will just move them up one belly. This should never happen though since... when they were
//absobred, they should have been absorbed as well!
for(var/belly in M.vore_organs)
var/obj/belly/B = belly
for(var/mob/living/Mm in B)
if((Mm.vore_flags & ABSORBED))
absorb_living(Mm)
//Update owner
owner.updateVRPanel()
//Digest a single item
//Receives a return value from digest_act that's how much nutrition
//the item should be worth
/obj/belly/proc/digest_item(var/obj/item/item)
var/digested = item.digest_act(src, owner)
if(!digested)
items_preserved |= item
else
// owner.adjust_nutrition(5 * digested) // haha no.
if(iscyborg(owner))
var/mob/living/silicon/robot/R = owner
R.cell.charge += (50 * digested)
//Determine where items should fall out of us into.
//Typically just to the owner's location.
/obj/belly/drop_location()
//Should be the case 99.99% of the time
if(owner)
return owner.drop_location()
//Sketchy fallback for safety, put them somewhere safe.
else if(ismob(src))
testing("[src] (\ref[src]) doesn't have an owner, and dropped someone at a latespawn point!")
SSjob.SendToLateJoin(src)
// wew lad. let's see if this never gets used, hopefully
else
qdel(src) //final option, I guess.
testing("[src] (\ref[src]) was QDEL'd for not having a drop_location!")
//Yes, it's ""safe"" to drop items here
/obj/belly/AllowDrop()
return TRUE
//Handle a mob struggling
// Called from /mob/living/carbon/relaymove()
/obj/belly/proc/relay_resist(mob/living/R)
if (!(R in contents))
return // User is not in this belly
if(owner.stat) //If owner is stat (dead, KO) we can actually escape
to_chat(R,"<span class='warning'>You attempt to climb out of \the [lowertext(name)]. (This will take around [escapetime/10] seconds.)</span>")
to_chat(owner,"<span class='warning'>Someone is attempting to climb out of your [lowertext(name)]!</span>")
if(do_after(R, escapetime, owner, (IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM)))
if((owner.stat || escapable) && (R.loc == src)) //Can still escape?
release_specific_contents(R)
return
else if(R.loc != src) //Aren't even in the belly. Quietly fail.
return
else //Belly became inescapable or mob revived
to_chat(R,"<span class='warning'>Your attempt to escape [lowertext(name)] has failed!</span>")
to_chat(owner,"<span class='notice'>The attempt to escape from your [lowertext(name)] has failed!</span>")
return
var/struggle_outer_message = pick(struggle_messages_outside)
var/struggle_user_message = pick(struggle_messages_inside)
struggle_outer_message = replacetext(struggle_outer_message,"%pred",owner)
struggle_outer_message = replacetext(struggle_outer_message,"%prey",R)
struggle_outer_message = replacetext(struggle_outer_message,"%belly",lowertext(name))
struggle_user_message = replacetext(struggle_user_message,"%pred",owner)
struggle_user_message = replacetext(struggle_user_message,"%prey",R)
struggle_user_message = replacetext(struggle_user_message,"%belly",lowertext(name))
struggle_outer_message = "<span class='alert'>" + struggle_outer_message + "</span>"
struggle_user_message = "<span class='alert'>" + struggle_user_message + "</span>"
var/sound/pred_struggle_snuggle = sound(get_sfx("struggle_sound"))
var/sound/prey_struggle_snuggle = sound(get_sfx("prey_struggle"))
var/sound/struggle_rustle = sound(get_sfx("rustle"))
LAZYCLEARLIST(hearing_mobs)
for(var/mob/living/H in get_hearers_in_view(VORE_SOUND_RANGE, owner))
if(!H.client || !(H.client.prefs.cit_toggles & EATING_NOISES))
continue
LAZYADD(hearing_mobs, H)
if(is_wet)
for(var/mob/living/H in hearing_mobs)
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
H.playsound_local(owner.loc, pred_struggle_snuggle, 75, TRUE)
else if(H && H.client && (H in contents))
SEND_SOUND(H,prey_struggle_snuggle)
else
for(var/mob/living/H in hearing_mobs)
if(H && H.client)
H.playsound_local(owner.loc, struggle_rustle, 75, TRUE)
for(var/mob/living/H in hearing_mobs)
if(H && H.client && (isturf(H.loc)))
H.show_message(struggle_outer_message, MSG_VISUAL) // visible
to_chat(R,struggle_user_message)
if(escapable) //If the stomach has escapable enabled.
if(prob(escapechance)) //Let's have it check to see if the prey escapes first.
to_chat(R,"<span class='warning'>You start to climb out of \the [lowertext(name)].</span>")
to_chat(owner,"<span class='warning'>Someone is attempting to climb out of your [lowertext(name)]!</span>")
if(do_after(R, escapetime, owner, (IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM)))
if((escapable) && (R.loc == src)) //Can still escape?
release_specific_contents(R)
to_chat(R,"<span class='warning'>You climb out of \the [lowertext(name)].</span>")
to_chat(owner,"<span class='warning'>[R] climbs out of your [lowertext(name)]!</span>")
return
else if(R.loc != src) //Aren't even in the belly. Quietly fail.
return
else //Belly became inescapable or mob revived
to_chat(R,"<span class='warning'>Your attempt to escape [lowertext(name)] has failed!</span>")
to_chat(owner,"<span class='notice'>The attempt to escape from your [lowertext(name)] has failed!</span>")
return
else if(prob(transferchance) && transferlocation) //Next, let's have it see if they end up getting into an even bigger mess then when they started.
var/obj/belly/dest_belly
for(var/belly in owner.vore_organs)
var/obj/belly/B = belly
if(B.name == transferlocation)
dest_belly = B
break
if(!dest_belly)
to_chat(owner, "<span class='warning'>Something went wrong with your belly transfer settings. Your <b>[lowertext(name)]</b> has had it's transfer chance and transfer location cleared as a precaution.</span>")
transferchance = 0
transferlocation = null
return
to_chat(R,"<span class='warning'>Your attempt to escape [lowertext(name)] has failed and your struggles only results in you sliding into [owner]'s [transferlocation]!</span>")
to_chat(owner,"<span class='warning'>Someone slid into your [transferlocation] due to their struggling inside your [lowertext(name)]!</span>")
transfer_contents(R, dest_belly)
return
else if(prob(absorbchance) && digest_mode != DM_ABSORB) //After that, let's have it run the absorb chance.
to_chat(R,"<span class='warning'>In response to your struggling, \the [lowertext(name)] begins to cling more tightly...</span>")
to_chat(owner,"<span class='warning'>You feel your [lowertext(name)] start to cling onto its contents...</span>")
digest_mode = DM_ABSORB
return
else if(prob(digestchance) && digest_mode != DM_DIGEST) //Finally, let's see if it should run the digest chance.
to_chat(R,"<span class='warning'>In response to your struggling, \the [lowertext(name)] begins to get more active...</span>")
to_chat(owner,"<span class='warning'>You feel your [lowertext(name)] beginning to become active!</span>")
digest_mode = DM_DIGEST
return
else //Nothing interesting happened.
to_chat(R,"<span class='warning'>You make no progress in escaping [owner]'s [lowertext(name)].</span>")
to_chat(owner,"<span class='warning'>Your prey appears to be unable to make any progress in escaping your [lowertext(name)].</span>")
return
/obj/belly/proc/get_mobs_and_objs_in_belly()
var/list/see = list()
var/list/belly_mobs = list()
see["mobs"] = belly_mobs
var/list/belly_objs = list()
see["objs"] = belly_objs
for(var/mob/living/L in loc.contents)
belly_mobs |= L
for(var/obj/O in loc.contents)
belly_objs |= O
return see
// Belly copies and then returns the copy
// Needs to be updated for any var changes AND KEPT IN ORDER OF THE VARS ABOVE AS WELL!
/obj/belly/proc/copy(mob/new_owner)
var/obj/belly/dupe = new /obj/belly(new_owner)
//// Non-object variables
dupe.name = name
dupe.desc = desc
dupe.vore_sound = vore_sound
dupe.vore_verb = vore_verb
dupe.release_sound = release_sound
dupe.human_prey_swallow_time = human_prey_swallow_time
dupe.nonhuman_prey_swallow_time = nonhuman_prey_swallow_time
dupe.emote_time = emote_time
dupe.digest_brute = digest_brute
dupe.digest_burn = digest_burn
dupe.immutable = immutable
dupe.escapable = escapable
dupe.escapetime = escapetime
dupe.digestchance = digestchance
dupe.absorbchance = absorbchance
dupe.escapechance = escapechance
dupe.can_taste = can_taste
dupe.bulge_size = bulge_size
dupe.transferlocation = transferlocation
dupe.transferchance = transferchance
dupe.autotransferchance = autotransferchance
dupe.autotransferwait = autotransferwait
dupe.swallow_time = swallow_time
dupe.vore_capacity = vore_capacity
dupe.is_wet = is_wet
dupe.wet_loop = wet_loop
//// Object-holding variables
//struggle_messages_outside - strings
dupe.struggle_messages_outside.Cut()
for(var/I in struggle_messages_outside)
dupe.struggle_messages_outside += I
//struggle_messages_inside - strings
dupe.struggle_messages_inside.Cut()
for(var/I in struggle_messages_inside)
dupe.struggle_messages_inside += I
//digest_messages_owner - strings
dupe.digest_messages_owner.Cut()
for(var/I in digest_messages_owner)
dupe.digest_messages_owner += I
//digest_messages_prey - strings
dupe.digest_messages_prey.Cut()
for(var/I in digest_messages_prey)
dupe.digest_messages_prey += I
//examine_messages - strings
dupe.examine_messages.Cut()
for(var/I in examine_messages)
dupe.examine_messages += I
//emote_lists - index: digest mode, key: list of strings
dupe.emote_lists.Cut()
for(var/K in emote_lists)
dupe.emote_lists[K] = list()
for(var/I in emote_lists[K])
dupe.emote_lists[K] += I
return dupe