Files
GS13NG/code/modules/vore/eating/belly_vr.dm
CitadelStationBot 47fa8353eb [MIRROR] Moves playsound_local() to mob and gets rid of a useless proc. (#710)
* Moves playsound_local() to mob and gets rid of a useless proc.

* Fixes compile errors
2017-05-18 01:39:16 -05:00

375 lines
17 KiB
Plaintext

//
// The belly object is what holds onto a mob while they're inside a predator.
// It takes care of altering the pred's decription, digesting the prey, relaying struggles etc.
//
// If you change what variables are on this, then you need to update the copy() proc.
//
// Parent type of all the various "belly" varieties.
//
/datum/belly
var/name // Name of this location
var/inside_flavor // Flavor text description of inside sight/sound/smells/feels.
var/vore_sound = 'sound/vore/pred/swallow_01.ogg' // Sound when ingesting someone
var/vore_verb = "ingest" // Verb for eating with this in messages
var/human_prey_swallow_time = 100 // Time in deciseconds to swallow /mob/living/carbon/human
var/nonhuman_prey_swallow_time = 60 // Time in deciseconds to swallow anything else
var/emoteTime = 300 // How long between stomach emotes at prey
var/digest_brute = 0 // Brute damage per tick in digestion mode
var/digest_burn = 1 // Burn damage per tick in digestion mode
var/digest_tickrate = 9 // Modulus this of air controller tick number to iterate gurgles on
var/immutable = FALSE // Prevents this belly from being deleted
var/escapable = TRUE // Belly can be resisted out of at any time
var/escapetime = 200 // Deciseconds, how long to escape this belly
var/escapechance = 45 // % Chance of prey beginning to escape if prey struggles.
var/tmp/digest_mode = DM_HOLD // Whether or not to digest. Default to not digest.
var/tmp/list/digest_modes = list(DM_HOLD,DM_DIGEST,DM_HEAL) // Possible digest modes
var/tmp/mob/living/owner // The mob whose belly this is.
var/tmp/list/internal_contents = list() // People/Things you've eaten into this belly!
var/tmp/is_full // Flag for if digested remeans are present. (for disposal messages)
var/tmp/emotePend = FALSE // If there's already a spawned thing counting for the next emote
// 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/list/emote_lists = list()
// Constructor that sets the owning mob
/datum/belly/New(var/mob/living/owning_mob)
owner = owning_mob
// Toggle digestion on/off and notify user of the new setting.
// If multiple digestion modes are avaliable (i.e. unbirth) then user should be prompted.
/datum/belly/proc/toggle_digestion()
return
// Checks if any mobs are present inside the belly
// return True if the belly is empty.
/datum/belly/proc/is_empty()
return internal_contents.len == 0
// 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.
/datum/belly/proc/release_all_contents()
if (internal_contents.len == 0)
return 0
for (var/atom/movable/M in internal_contents)
M.forceMove(owner.loc) // Move the belly contents into the same location as belly's owner.
M << sound(null, repeat = 0, wait = 0, volume = 80, channel = 50)
internal_contents.Remove(M) // Remove from the belly contents
var/datum/belly/B = check_belly(owner) // This makes sure that the mob behaves properly if released into another mob
if(B)
B.internal_contents.Add(M)
owner.visible_message("<font color='green'><b>[owner] expels everything from their [lowertext(name)]!</b></font>")
return TRUE
// 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.
/datum/belly/proc/release_specific_contents(var/atom/movable/M)
if (!(M in internal_contents))
return FALSE // They weren't in this belly anyway
M.forceMove(owner.loc) // Move the belly contents into the same location as belly's owner.
M << sound(null, repeat = 0, wait = 0, volume = 80, channel = 50)
src.internal_contents.Add(M) // Remove from the belly contents
var/datum/belly/B = check_belly(owner)
if(B)
B.internal_contents.Add(M)
owner.visible_message("<font color='green'><b>[owner] expels [M] from their [lowertext(name)]!</b></font>")
// owner.regenerate_icons()
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.
/datum/belly/proc/nom_mob(var/mob/prey, var/mob/user)
// if (prey.buckled)
// prey.buckled.unbuckle_mob()
prey.forceMove(owner)
internal_contents.Add(prey)
prey << sound('sound/vore/prey/loop.ogg', repeat = 1, wait = 0, volume = 80, channel = 50)
if(inside_flavor)
prey << "<span class='notice'><B>[inside_flavor]</B></span>"
// 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.
/datum/belly/proc/get_examine_msg()
if(internal_contents.len && examine_messages.len)
var/formatted_message
var/raw_message = pick(examine_messages)
formatted_message = replacetext(raw_message,"%belly",lowertext(name))
return("<span class='warning'>[formatted_message]</span><BR>")
// 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.
/datum/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 = list2text(raw_messages,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.
/datum/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 = text2list(html_encode(raw_text),delim)
if(raw_list.len > 10)
raw_list.Cut(11)
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)
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.
/datum/belly/proc/digestion_death(var/mob/living/M)
is_full = TRUE
internal_contents.Remove(M)
M << sound(null, repeat = 0, wait = 0, volume = 80, channel = 50)
// If digested prey is also a pred... anyone inside their bellies gets moved up.
if (is_vore_predator(M))
for (var/bellytype in M.vore_organs)
var/datum/belly/belly = M.vore_organs[bellytype]
for (var/obj/thing in belly.internal_contents)
thing.loc = owner
internal_contents.Add(thing)
for (var/mob/subprey in belly.internal_contents)
subprey.loc = owner
internal_contents.Add(subprey)
to_chat(subprey, "As [M] melts away around you, you find yourself in [owner]'s [name]")
//Drop all items into the belly.
for(var/obj/item/W in M)
if(!M.dropItemToGround(W))
qdel(W)
message_admins("[key_name(owner)] digested [key_name(M)].")
log_attack("[key_name(owner)] digested [key_name(M)].")
// Delete the digested mob
qdel(M)
//Handle a mob struggling
// Called from /mob/living/carbon/relaymove()
/datum/belly/proc/relay_resist(var/mob/living/R)
if (!(R in internal_contents))
return // User is not in this belly, or struggle too soon.
R.setClickCooldown(50)
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 [name]. (This will take around [escapetime/10] seconds.)</span>")
to_chat(owner, "<span class='warning'>Someone is attempting to climb out of your [name]!</span>")
if(do_after(R, escapetime, owner))
if((owner.stat || escapable) && (R in internal_contents)) //Can still escape?
release_specific_contents(R)
return
else if(!(R in internal_contents)) //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 [name] has failed!</span>")
to_chat(owner, "<span class='notice'>The attempt to escape from your [name] has failed!</span>")
return
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>"
// for(var/mob/M in hearers(4, owner))
// M.visible_message(struggle_outer_message) // hearable
R.visible_message( "<span class='alert'>[struggle_outer_message]</span>", "<span class='alert'>[struggle_user_message]</span>")
playsound(get_turf(owner),"struggle_sound",75,0,-5,1,channel=51)
R.stop_sound_channel(51)
R.playsound_local("prey_struggle_sound",60)
if(escapable && R.a_intent != "help") //If the stomach has escapable enabled and the person is actually trying to kick out
to_chat(R, "<span class='warning'>You attempt to climb out of \the [name].</span>")
to_chat(owner, "<span class='warning'>Someone is attempting to climb out of your [name]!</span>")
if(prob(escapechance)) //Let's have it check to see if the prey escapes first.
if(do_after(R, escapetime))
if((escapable) && (R in internal_contents)) //Does the owner still have escapable enabled?
release_specific_contents(R)
to_chat(R, "<span class='warning'>You climb out of \the [name].</span>")
to_chat(owner, "<span class='warning'>[R] climbs out of your [name]!</span>")
for(var/mob/M in viewers(4, owner))
M.visible_message("<span class='warning'>[R] climbs out of [owner]'s [name]!</span>", 2)
return
else if(!(R in internal_contents)) //Aren't even in the belly. Quietly fail.
return
else //Belly became inescapable.
to_chat(R, "<span class='warning'>Your attempt to escape [name] has failed!</span>")
to_chat(owner, "<span class='notice'>The attempt to escape from your [name] has failed!/span>")
return
else //Nothing interesting happened.
to_chat(R, "<span class='warning'>But make no progress in escaping [owner]'s [name].</span>")
to_chat(owner, "<span class='warning'>But appears to be unable to make any progress in escaping your [name].</span>")
return
else
return
// Belly copies and then returns the copy
// Needs to be updated for any var changes
/datum/belly/proc/copy(mob/new_owner)
var/datum/belly/dupe = new /datum/belly(new_owner)
//// Non-object variables
dupe.name = name
dupe.inside_flavor = inside_flavor
dupe.vore_sound = vore_sound
dupe.vore_verb = vore_verb
dupe.human_prey_swallow_time = human_prey_swallow_time
dupe.nonhuman_prey_swallow_time = nonhuman_prey_swallow_time
dupe.emoteTime = emoteTime
dupe.digest_brute = digest_brute
dupe.digest_burn = digest_burn
dupe.digest_tickrate = digest_tickrate
dupe.immutable = immutable
dupe.escapable = escapable
dupe.escapetime = escapetime
//// 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