Vore code to json and more (#5789)

* Vore 2.0

Initial commit

* double checked porting

* Fixes compile issues

* converts Ash Drake bellies to new system

digs out lingering datum/belly stuff too

* Let's just work on this later

* System operational

* Update preferences.dm
This commit is contained in:
Poojawa
2018-03-06 03:59:31 -06:00
committed by GitHub
parent 9d8286ee53
commit 6e5131cfc5
33 changed files with 1976 additions and 871 deletions

View File

@@ -14,9 +14,10 @@
#define SOUND_ANNOUNCEMENTS 2048
#define DISABLE_DEATHRATTLE 4096
#define DISABLE_ARRIVALRATTLE 8192
#define MEDIHOUND_SLEEPER 16384
#define TOGGLES_DEFAULT (SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|MEMBER_PUBLIC|INTENT_STYLE|MIDROUND_ANTAG|SOUND_INSTRUMENTS|SOUND_SHIP_AMBIENCE|SOUND_PRAYERS|SOUND_ANNOUNCEMENTS|MEDIHOUND_SLEEPER)
#define MEDIHOUND_SLEEPER 16384 //CITADEL EDITS, vore prefs.
#define EATING_NOISES 32768
#define DIGESTION_NOISES 65536
#define TOGGLES_DEFAULT (SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|MEMBER_PUBLIC|INTENT_STYLE|MIDROUND_ANTAG|SOUND_INSTRUMENTS|SOUND_SHIP_AMBIENCE|SOUND_PRAYERS|SOUND_ANNOUNCEMENTS|MEDIHOUND_SLEEPER|EATING_NOISES|DIGESTION_NOISES)
//Chat toggles
#define CHAT_OOC 1
@@ -66,4 +67,4 @@
#define EXP_TYPE_GHOST "Ghost"
//Flags in the players table in the db
#define DB_FLAG_EXEMPT 1
#define DB_FLAG_EXEMPT 1

View File

@@ -11,12 +11,13 @@
//CIT CHANNELS - TRY NOT TO REGRESS
#define CHANNEL_PRED 1015
#define CHANNEL_PREYLOOP 1014
#define CHANNEL_DIGEST 1014
#define CHANNEL_PREYLOOP 1013
//THIS SHOULD ALWAYS BE THE LOWEST ONE!
//KEEP IT UPDATED
#define CHANNEL_HIGHEST_AVAILABLE 1013 //CIT CHANGE - COMPENSATES FOR VORESOUND CHANNELS
#define CHANNEL_HIGHEST_AVAILABLE 1012 //CIT CHANGE - COMPENSATES FOR VORESOUND CHANNELS
#define SOUND_MINIMUM_PRESSURE 10

View File

@@ -5,6 +5,9 @@
#define DM_NOISY "Noisy"
#define DM_DRAGON "Dragon"
#define isbelly(A) istype(A, /obj/belly)
#define QDEL_NULL_LIST(x) if(x) { for(var/y in x) { qdel(y) } ; x = null }
#define VORE_STRUGGLE_EMOTE_CHANCE 40
// Stance for hostile mobs to be in while devouring someone.
@@ -60,6 +63,10 @@ GLOBAL_LIST_INIT(pred_vore_sounds, list(
"Squish3" = 'sound/vore/pred/squish_03.ogg',
"Squish4" = 'sound/vore/pred/squish_04.ogg',
"Rustle (cloth)" = 'sound/effects/rustle5.ogg',
"rustle2(cloth)" = 'sound/effects/rustle2.ogg',
"rustle3(cloth)" = 'sound/effects/rustle3.ogg',
"rustle4(cloth)" = 'sound/effects/rustle4.ogg',
"rustle5(cloth)" = 'sound/effects/rustle5.ogg',
"None" = null))
/*
GLOBAL_LIST_INIT(pred_struggle_sounds, list(
@@ -121,3 +128,14 @@ GLOBAL_LIST_INIT(death_prey, list(
"death9" = 'sound/vore/prey/death_09.ogg',
"death10" = 'sound/vore/prey/death_10.ogg'))
*/
GLOBAL_LIST_INIT(release_sound, list(
"rustle (cloth)" = 'sound/effects/rustle1.ogg',
"rustle2 (cloth)" = 'sound/effects/rustle2.ogg',
"rustle3 (cloth)" = 'sound/effects/rustle3.ogg',
"rustle4 (cloth)" = 'sound/effects/rustle4.ogg',
"rustle5 (cloth)" = 'sound/effects/rustle5.ogg',
"Stomach Move" = 'sound/vore/pred/stomachmove.ogg',
"Pred Escape" = 'sound/vore/pred/escape.ogg',
"Splatter" = 'sound/effects/splat.ogg',
"None" = null))

View File

@@ -105,3 +105,7 @@
. += copytext(text, last_found, found)
last_found = found + delim_len
while (found)
// Returns true if val is from min to max, inclusive.
/proc/IsInRange(val, min, max)
return (val >= min) && (val <= max)

View File

@@ -608,6 +608,12 @@
playsound(get_turf(hound),"death_pred",50,0,-6,0,channel=CHANNEL_PRED,ignore_walls = FALSE)
T.stop_sound_channel(CHANNEL_PRED)
T.playsound_local("death_prey",60)
for(var/belly in T.vore_organs)
var/obj/belly/B = belly
for(var/atom/movable/thing in B)
thing.forceMove(src)
if(ismob(thing))
to_chat(thing, "As [T] melts away around you, you find yourself in [hound]'s [name]")
for(var/obj/item/W in T)
if(!T.dropItemToGround(W))
qdel(W)

View File

@@ -0,0 +1,41 @@
#define SSBELLIES_PROCESSED 1
#define SSBELLIES_IGNORED 2
//
// Bellies subsystem - Process vore bellies
//
SUBSYSTEM_DEF(bellies)
name = "Bellies"
priority = 5
wait = 1 SECONDS
flags = SS_KEEP_TIMING|SS_NO_INIT
runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME
var/static/list/belly_list = list()
var/list/currentrun = list()
var/ignored_bellies = 0
/datum/controller/subsystem/bellies/stat_entry()
..("#: [belly_list.len] | P: [ignored_bellies]")
/datum/controller/subsystem/bellies/fire(resumed = 0)
if (!resumed)
ignored_bellies = 0
src.currentrun = belly_list.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
var/times_fired = src.times_fired
while(currentrun.len)
var/obj/belly/B = currentrun[currentrun.len]
currentrun.len--
if(QDELETED(B))
belly_list -= B
else
if(B.process_belly(times_fired,wait) == SSBELLIES_IGNORED)
ignored_bellies++
if (MC_TICK_CHECK)
return

View File

@@ -324,6 +324,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<b>Ghost pda:</b> <a href='?_src_=prefs;preference=ghost_pda'>[(chat_toggles & CHAT_GHOSTPDA) ? "All Messages" : "Nearest Creatures"]</a><br>"
dat += "<b>Pull requests:</b> <a href='?_src_=prefs;preference=pull_requests'>[(chat_toggles & CHAT_PULLR) ? "Yes" : "No"]</a><br>"
dat += "<b>Midround Antagonist:</b> <a href='?_src_=prefs;preference=allow_midround_antag'>[(toggles & MIDROUND_ANTAG) ? "Yes" : "No"]</a><br>"
//VORE SOUNDS
dat += "<b>Hear Vore Sounds:</b> <a href='?_src_=prefs;preference=toggleeatingnoise'>[(toggles & EATING_NOISES) ? "Yes" : "No"]</a><br>"
dat += "<b>Hear Vore Digestion Sounds:</b> <a href='?_src_=prefs;preference=toggledigestionnoise'>[(toggles & DIGESTION_NOISES) ? "Yes" : "No"]</a><br>"
if(CONFIG_GET(flag/allow_metadata))
dat += "<b>OOC Notes:</b> <a href='?_src_=prefs;preference=metadata;task=input'>Edit </a><br>"
@@ -1765,6 +1768,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
user.client.playtitlemusic()
else
user.stop_sound_channel(CHANNEL_LOBBYMUSIC)
// VORE SOUND TOGGLES
if("toggleeatingnoise")
toggles ^= EATING_NOISES
if("toggledigestionnoise")
toggles ^= DIGESTION_NOISES
if("ghost_ears")
chat_toggles ^= CHAT_GHOSTEARS

View File

@@ -5,4 +5,7 @@
/datum/preferences/proc/set_biological_gender(var/gender)
biological_gender = gender
identifying_gender = gender
identifying_gender = gender
/obj/item/clothing/var/hides_bulges = FALSE // OwO wats this?

View File

@@ -252,10 +252,6 @@
if(91.01 to INFINITY)
msg += "[t_He] [t_is] a shitfaced, slobbering wreck.\n"
for (var/I in src.vore_organs)
var/datum/belly/B = vore_organs[I]
msg += B.get_examine_msg()
msg += "</span>"
if(!appears_dead)
@@ -329,7 +325,7 @@
if(print_flavor_text() && get_visible_name() != "Unknown")//Are we sure we know who this is? Don't show flavor text unless we can recognize them. Prevents certain metagaming with impersonation.
msg += "[print_flavor_text()]\n"
msg += "*---------*</span>"
to_chat(user, msg)

View File

@@ -42,13 +42,4 @@
message = "<span class='warning'>[t_His] stomach is firmly packed with digesting slop. [t_He] must have eaten at least a few times worth their body weight! It looks hard for them to stand, and [t_his] gut jiggles when they move.</span>\n"
if(4075 to 10000) // Four or more people.
message = "<span class='warning'>[t_He] [t_is] so absolutely stuffed that you aren't sure how it's possible to move. [t_He] can't seem to swell any bigger. The surface of [t_his] belly looks sorely strained!</span>\n"
return message
/mob/living/carbon/human/proc/examine_bellies()
var/message = ""
for (var/I in src.vore_organs)
var/datum/belly/B = vore_organs[I]
message += B.get_examine_msg()
return message

View File

@@ -32,6 +32,7 @@
/mob/living/carbon/human/Destroy()
QDEL_NULL(physiology)
QDEL_NULL_LIST(vore_organs) // CITADEL EDIT belly stuff
return ..()
/mob/living/carbon/human/OpenCraftingMenu()

View File

@@ -64,6 +64,10 @@
/mob/living/carbon/human/calculate_affecting_pressure(pressure)
if((wear_suit && (wear_suit.flags_1 & STOPSPRESSUREDMAGE_1)) && (head && (head.flags_1 & STOPSPRESSUREDMAGE_1)))
return ONE_ATMOSPHERE
if(istype(loc, /obj/belly))
return ONE_ATMOSPHERE
if(istype(loc, /obj/item/device/dogborg/sleeper))
return ONE_ATMOSPHERE
else
return pressure
@@ -138,6 +142,8 @@
return FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
if(ismob(loc))
return FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
if(istype(loc, /obj/belly))
return FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
//END EDIT
if(wear_suit)
if(wear_suit.max_heat_protection_temperature >= FIRE_SUIT_MAX_TEMP_PROTECT)
@@ -246,10 +252,12 @@
/mob/living/carbon/human/proc/get_cold_protection(temperature)
if(has_trait(TRAIT_RESISTCOLD))
return TRUE
//CITADEL EDIT Mandatory for vore code.
if(istype(loc, /obj/item/device/dogborg/sleeper))
return 1 //freezing to death in sleepers ruins fun.
if(istype(loc, /obj/belly))
return 1
if(ismob(loc))
return 1 //because lazy and being inside somemone insulates you from space
//END EDIT

View File

@@ -351,3 +351,10 @@
H.update_body()
else
return
//misc
/mob/living/carbon/human/dummy
no_vore = TRUE
/mob/living/carbon/human/vore
devourable = TRUE

View File

@@ -52,6 +52,8 @@
return
if(ismob(loc))
return
if(istype(loc, /obj/belly))
return
var/datum/gas_mixture/environment
if(loc)

View File

@@ -54,9 +54,6 @@
handle_fire()
// Citadel Vore code for belly processes
handle_internal_contents()
//stuff in the stomach
handle_stomach()

View File

@@ -1,26 +1,27 @@
/mob/living/simple_animal/hostile/megafauna/dragon
vore_active = TRUE
no_vore = FALSE
/mob/living/simple_animal/hostile/megafauna/dragon/Initialize()
// Create and register 'stomachs'
var/datum/belly/megafauna/dragon/maw/maw = new(src)
var/datum/belly/megafauna/dragon/gullet/gullet = new(src)
var/datum/belly/megafauna/dragon/gut/gut = new(src)
for(var/datum/belly/X in list(maw, gullet, gut))
vore_organs[X.name] = X
var/obj/belly/megafauna/dragon/maw/maw = new(src)
var/obj/belly/megafauna/dragon/gullet/gullet = new(src)
var/obj/belly/megafauna/dragon/gut/gut = new(src)
// for(var/obj/belly/X in list(maw, gullet, gut))
// vore_organs[X.name] = X
// Connect 'stomachs' together
maw.transferlocation = gullet
gullet.transferlocation = gut
vore_selected = maw.name // NPC eats into maw
vore_selected = maw // NPC eats into maw
return ..()
/datum/belly/megafauna/dragon
/obj/belly/megafauna/dragon
human_prey_swallow_time = 50 // maybe enough to switch targets if distracted
nonhuman_prey_swallow_time = 50
/datum/belly/megafauna/dragon/maw
/obj/belly/megafauna/dragon/maw
name = "maw"
inside_flavor = "The maw of the dreaded Ash drake closes around you, engulfing you into a swelteringly hot, disgusting enviroment. The acidic saliva tingles over your form while that tongue pushes you further back...towards the dark gullet beyond."
desc = "The maw of the dreaded Ash drake closes around you, engulfing you into a swelteringly hot, disgusting enviroment. The acidic saliva tingles over your form while that tongue pushes you further back...towards the dark gullet beyond."
vore_verb = "scoop"
vore_sound = 'sound/vore/pred/taurswallow.ogg'
swallow_time = 20
@@ -30,9 +31,9 @@
autotransferchance = 66
autotransferwait = 200
/datum/belly/megafauna/dragon/gullet
/obj/belly/megafauna/dragon/gullet
name = "gullet"
inside_flavor = "A ripple of muscle and arching of the tongue pushes you down like any other food. No choice in the matter, you're simply consumed. The dark ambiance of the outside world is replaced with working, wet flesh. Your only light being what you brought with you."
desc = "A ripple of muscle and arching of the tongue pushes you down like any other food. No choice in the matter, you're simply consumed. The dark ambiance of the outside world is replaced with working, wet flesh. Your only light being what you brought with you."
swallow_time = 60 // costs extra time to eat directly to here
escapechance = 5
// From above, will transfer into gut
@@ -40,10 +41,10 @@
autotransferchance = 50
autotransferwait = 200
/datum/belly/megafauna/dragon/gut
/obj/belly/megafauna/dragon/gut
name = "stomach"
vore_capacity = 5 //I doubt this many people will actually last in the gut, but...
inside_flavor = "With a rush of burning ichor greeting you, you're introduced to the Drake's stomach. Wrinkled walls greedily grind against you, acidic slimes working into your body as you become fuel and nutriton for a superior predator. All that's left is your body's willingness to resist your destiny."
desc = "With a rush of burning ichor greeting you, you're introduced to the Drake's stomach. Wrinkled walls greedily grind against you, acidic slimes working into your body as you become fuel and nutriton for a superior predator. All that's left is your body's willingness to resist your destiny."
digest_mode = DM_DRAGON
digest_burn = 5
swallow_time = 100 // costs extra time to eat directly to here

View File

@@ -103,7 +103,8 @@
stack_trace("Simple animal being instantiated in nullspace")
if(vore_active)
init_belly()
verbs |= /mob/living/proc/animal_nom
if(!IsAdvancedToolUser())
verbs |= /mob/living/simple_animal/proc/animal_nom
/mob/living/simple_animal/Destroy()
GLOB.simple_animals[AIStatus] -= src

View File

@@ -7,18 +7,18 @@
var/vore_default_mode = DM_DIGEST // Default bellymode (DM_DIGEST, DM_HOLD, DM_ABSORB)
var/vore_digest_chance = 25 // Chance to switch to digest mode if resisted
var/vore_escape_chance = 25 // Chance of resisting out of mob
var/vore_absorb_chance = 0 // chance of absorbtion by mob
var/vore_stomach_name // The name for the first belly if not "stomach"
var/vore_stomach_flavor // The flavortext for the first belly if not the default
var/vore_fullness = 0 // How "full" the belly is (controls icons)
var/list/living_mobs = list()
// Release belly contents beforey being gc'd!
/mob/living/simple_animal/Destroy()
for(var/I in vore_organs)
var/datum/belly/B = vore_organs[I]
B.release_all_contents() // When your stomach is empty
release_vore_contents()
prey_excludes.Cut()
. = ..()
@@ -34,10 +34,10 @@
vore_fullness = new_fullness
/*
/mob/living/simple_animal/proc/swallow_check()
for(var/I in vore_organs)
var/datum/belly/B = vore_organs[I]
var/obj/belly/B = vore_organs[I]
if(vore_active)
update_fullness()
if(!vore_fullness)
@@ -48,16 +48,14 @@
/mob/living/simple_animal/proc/swallow_mob()
for(var/I in vore_organs)
var/datum/belly/B = vore_organs[I]
for(var/mob/living/M in B.internal_contents)
B.transfer_contents(M, B.transferlocation)
var/obj/belly/B = vore_organs[I]
for(var/mob/living/M in B.contents)
B.transfer_contents(M, transferlocation)
*/
/mob/living/simple_animal/death()
for(var/I in vore_organs)
var/datum/belly/B = vore_organs[I]
B.release_all_contents() // When your stomach is empty
..() // then you have my permission to die.
release_vore_contents()
. = ..()
// Simple animals have only one belly. This creates it (if it isn't already set up)
/mob/living/simple_animal/proc/init_belly()
@@ -66,18 +64,19 @@
if(no_vore) //If it can't vore, let's not give it a stomach.
return
var/datum/belly/B = new /datum/belly(src)
B.immutable = TRUE
var/obj/belly/B = new /obj/belly(src)
vore_selected = B
B.immutable = 1
B.name = vore_stomach_name ? vore_stomach_name : "stomach"
B.inside_flavor = vore_stomach_flavor ? vore_stomach_flavor : "Your surroundings are warm, soft, and slimy. Makes sense, considering you're inside \the [name]."
B.desc = vore_stomach_flavor ? vore_stomach_flavor : "Your surroundings are warm, soft, and slimy. Makes sense, considering you're inside \the [name]."
B.digest_mode = vore_default_mode
B.escapable = vore_escape_chance > 0
B.escapechance = vore_escape_chance
B.digestchance = vore_digest_chance
B.absorbchance = vore_absorb_chance
B.human_prey_swallow_time = swallowTime
B.nonhuman_prey_swallow_time = swallowTime
B.vore_verb = "swallow"
// TODO - Customizable per mob
B.emote_lists[DM_HOLD] = list( // We need more that aren't repetitive. I suck at endo. -Ace
"The insides knead at you gently for a moment.",
"The guts glorp wetly around you as some air shifts.",
@@ -98,5 +97,21 @@
"The juices pooling beneath you sizzle against your sore skin.",
"The churning walls slowly pulverize you into meaty nutrients.",
"The stomach glorps and gurgles as it tries to work you into slop.")
src.vore_organs[B.name] = B
src.vore_selected = B.name
/* B.emote_lists[DM_ITEMWEAK] = list(
"The burning acids eat away at your form.",
"The muscular stomach flesh grinds harshly against you.",
"The caustic air stings your chest when you try to breathe.",
"The slimy guts squeeze inward to help the digestive juices soften you up.",
"The onslaught against your body doesn't seem to be letting up; you're food now.",
"The predator's body ripples and crushes against you as digestive enzymes pull you apart.",
"The juices pooling beneath you sizzle against your sore skin.",
"The churning walls slowly pulverize you into meaty nutrients.",
"The stomach glorps and gurgles as it tries to work you into slop.")*/
//Grab = Nomf
/*
/mob/living/simple_animal/UnarmedAttack(var/atom/A, var/proximity)
. = ..()
if(a_intent == I_GRAB && isliving(A) && !has_hands)
animal_nom(A)*/

View File

@@ -1,7 +1,8 @@
//include unit test files in this module in this ifdef
// CITADEL EDIT add vore_tests.dm
#ifdef UNIT_TESTS
#include "unit_test.dm"
#include "reagent_recipe_collisions.dm"
#include "reagent_id_typos.dm"
//#include "vore_tests.dm"
#endif

View File

@@ -0,0 +1,218 @@
/datum/unit_test
var/static/default_mobloc = null
/datum/unit_test/proc/create_test_mob(var/turf/mobloc = null, var/mobtype = /mob/living/carbon/human, var/with_mind = FALSE)
if(isnull(mobloc))
if(!default_mobloc)
for(var/turf/simulated/floor/tiled/T in world)
var/pressure = T.zone.air.return_pressure()
if(90 < pressure && pressure < 120) // Find a turf between 90 and 120
default_mobloc = T
break
mobloc = default_mobloc
if(!mobloc)
Fail("Unable to find a location to create test mob")
return FALSE
var/mob/living/carbon/human/H = new mobtype(mobloc)
if(with_mind)
H.mind_initialize("TestKey[rand(0,10000)]")
return H
/datum/unit_test/space_suffocation
name = "MOB: human mob suffocates in space"
var/startOxyloss
var/endOxyloss
var/mob/living/carbon/human/H
async = 1
/datum/unit_test/space_suffocation/Run()
var/turf/open/space/T = locate()
H = new(T)
startOxyloss = H.getOxyLoss()
return 1
/datum/unit_test/space_suffocation/check_result()
if(H.life_tick < 10)
return 0
endOxyloss = H.getOxyLoss()
if(!startOxyloss < endOxyloss)
Fail("Human mob is not taking oxygen damage in space. (Before: [startOxyloss]; after: [endOxyloss])")
qdel(H)
return 1
/datum/unit_test/belly_nonsuffocation
name = "MOB: human mob does not suffocate in a belly"
var/startLifeTick
var/startOxyloss
var/endOxyloss
var/mob/living/carbon/human/pred
var/mob/living/carbon/human/prey
/datum/unit_test/belly_nonsuffocation/Run()
pred = create_test_mob()
if(!istype(pred))
return FALSE
prey = create_test_mob(pred.loc)
if(!istype(prey))
return FALSE
return TRUE
/datum/unit_test/belly_nonsuffocation/check_result()
// Unfortuantely we need to wait for the pred's belly to initialize. (Currently after a spawn())
if(!pred.vore_organs || !pred.vore_organs.len)
return FALSE
// Now that pred belly exists, we can eat the prey.
if(!pred.vore_selected)
Fail("[pred] has no vore_selected.")
return TRUE
// Attempt to eat the prey
if(prey.loc != pred.vore_selected)
pred.vore_selected.nom_mob(prey)
if(prey.loc != pred.vore_selected)
Fail("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]")
return TRUE
// Okay, we succeeded in eating them, now lets wait a bit
startLifeTick = pred.life_tick
startOxyloss = prey.getOxyLoss()
return FALSE
if(pred.life_tick < (startLifeTick + 10))
return FALSE // Wait for them to breathe a few times
// Alright lets check it!
endOxyloss = prey.getOxyLoss()
if(startOxyloss < endOxyloss)
Fail("Prey takes oxygen damage in a pred's belly! (Before: [startOxyloss]; after: [endOxyloss])")
qdel(prey)
qdel(pred)
return TRUE
////////////////////////////////////////////////////////////////
/datum/unit_test/belly_spacesafe
name = "MOB: human mob protected from space in a belly"
var/startLifeTick
var/startOxyloss
var/startBruteloss
var/endOxyloss
var/endBruteloss
var/mob/living/carbon/human/pred
var/mob/living/carbon/human/prey
/datum/unit_test/belly_spacesafe/Run()
pred = create_test_mob()
if(!istype(pred))
return FALSE
prey = create_test_mob(pred.loc)
if(!istype(prey))
return FALSE
return TRUE
/datum/unit_test/belly_spacesafe/check_result()
// Unfortuantely we need to wait for the pred's belly to initialize. (Currently after a spawn())
if(!pred.vore_organs || !pred.vore_organs.len)
return FALSE
// Now that pred belly exists, we can eat the prey.
if(!pred.vore_selected)
Fail("[pred] has no vore_selected.")
return TRUE
// Attempt to eat the prey
if(prey.loc != pred.vore_selected)
pred.vore_selected.nom_mob(prey)
if(prey.loc != pred.vore_selected)
Fail("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]")
return TRUE
else
var/turf/T = locate(/turf/open/space)
if(!T)
Fail("could not find a space turf for testing")
return TRUE
else
pred.forceMove(T)
// Okay, we succeeded in eating them, now lets wait a bit
startLifeTick = pred.life_tick
startOxyloss = prey.getOxyLoss()
startBruteloss = prey.getBruteloss()
return FALSE
if(pred.life_tick < (startLifeTick + 10))
return FALSE // Wait for them to breathe a few times
// Alright lets check it!
endOxyloss = prey.getOxyLoss()
endBruteloss = prey.getBruteLoss()
if(startBruteloss < endBruteloss)
Fail("Prey takes brute damage in space! (Before: [startBruteloss]; after: [endBruteloss])")
qdel(prey)
qdel(pred)
return TRUE
////////////////////////////////////////////////////////////////
/datum/unit_test/belly_damage
name = "MOB: human mob takes damage from digestion"
var/startLifeTick
var/startBruteBurn
var/endBruteBurn
var/mob/living/carbon/human/pred
var/mob/living/carbon/human/prey
/datum/unit_test/belly_damage/Run()
pred = create_test_mob()
if(!istype(pred))
return FALSE
prey = create_test_mob(pred.loc)
if(!istype(prey))
return FALSE
return TRUE
/datum/unit_test/belly_damage/check_result()
// Unfortuantely we need to wait for the pred's belly to initialize. (Currently after a spawn())
if(!pred.vore_organs || !pred.vore_organs.len)
return FALSE
// Now that pred belly exists, we can eat the prey.
if(!pred.vore_selected)
Fail("[pred] has no vore_selected.")
return TRUE
// Attempt to eat the prey
if(prey.loc != pred.vore_selected)
pred.vore_selected.nom_mob(prey)
if(prey.loc != pred.vore_selected)
Fail("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]")
return TRUE
// Okay, we succeeded in eating them, now lets wait a bit
pred.vore_selected.digest_mode = DM_DIGEST
startLifeTick = pred.life_tick
startBruteBurn = prey.getBruteLoss() + prey.getFireLoss()
return FALSE
if(pred.life_tick < (startLifeTick + 10))
return FALSE // Wait a few ticks for damage to happen
// Alright lets check it!
endBruteBurn = prey.getBruteLoss() + prey.getFireLoss()
if(startBruteBurn >= endBruteBurn)
Fail("Prey doesn't take damage in digesting belly! (Before: [startBruteBurn]; after: [endBruteBurn])")
qdel(prey)
qdel(pred)
return TRUE

View File

@@ -0,0 +1,162 @@
// THIS IS NOW MERELY LEGACY, because memes. hopefully it won't be dumb.
//
// 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 = 10 SECONDS // Time in deciseconds to swallow /mob/living/carbon/human
var/nonhuman_prey_swallow_time = 5 SECONDS // Time in deciseconds to swallow anything else
var/emoteTime = 30 SECONDS // 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 = FALSE // Belly can be resisted out of at any time
var/escapetime = 60 SECONDS // Deciseconds, how long to escape this belly
var/digestchance = 0 // % Chance of stomach beginning to digest if prey struggles
// var/silenced = FALSE // Will the heartbeat/fleshy internal loop play?
var/escapechance = 0 // % Chance of prey beginning to escape if prey struggles.
var/datum/belly/transferlocation = null // 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/can_taste = FALSE // If this belly prints the flavor of prey when it eats someone.
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,DM_NOISY) // 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
var/swallow_time = 10 SECONDS // for mob transfering automation
var/vore_capacity = 1 // The capacity (in people) this person can hold
// 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()
//OLD: This only exists for legacy conversion purposes
//It's called whenever an old datum-style belly is loaded
/datum/belly/proc/copy(obj/belly/new_belly)
//// Non-object variables
new_belly.name = name
new_belly.desc = inside_flavor
new_belly.vore_sound = vore_sound
new_belly.vore_verb = vore_verb
new_belly.human_prey_swallow_time = human_prey_swallow_time
new_belly.nonhuman_prey_swallow_time = nonhuman_prey_swallow_time
new_belly.emote_time = emoteTime
new_belly.digest_brute = digest_brute
new_belly.digest_burn = digest_burn
new_belly.immutable = immutable
new_belly.can_taste = can_taste
new_belly.escapable = escapable
new_belly.escapetime = escapetime
new_belly.digestchance = digestchance
new_belly.escapechance = escapechance
new_belly.transferchance = transferchance
new_belly.transferlocation = transferlocation
//// Object-holding variables
//struggle_messages_outside - strings
new_belly.struggle_messages_outside.Cut()
for(var/I in struggle_messages_outside)
new_belly.struggle_messages_outside += I
//struggle_messages_inside - strings
new_belly.struggle_messages_inside.Cut()
for(var/I in struggle_messages_inside)
new_belly.struggle_messages_inside += I
//digest_messages_owner - strings
new_belly.digest_messages_owner.Cut()
for(var/I in digest_messages_owner)
new_belly.digest_messages_owner += I
//digest_messages_prey - strings
new_belly.digest_messages_prey.Cut()
for(var/I in digest_messages_prey)
new_belly.digest_messages_prey += I
//examine_messages - strings
new_belly.examine_messages.Cut()
for(var/I in examine_messages)
new_belly.examine_messages += I
//emote_lists - index: digest mode, key: list of strings
new_belly.emote_lists.Cut()
for(var/K in emote_lists)
new_belly.emote_lists[K] = list()
for(var/I in emote_lists[K])
new_belly.emote_lists[K] += I
return new_belly
// // // // // // // // // // // //
// // // LEGACY USE ONLY!! // // //
// // // // // // // // // // // //
// See top of file! //
// // // // // // // // // // // //

View File

@@ -0,0 +1,655 @@
//#define VORE_SOUND_FALLOFF 0.05
//
// 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.
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/release_sound = 'sound/effects/splat.ogg'
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 = 100 // % 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/silent = FALSE
var/transferlocation = null // 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
//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) // Possible digest modes
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 = FALSE // Prevent audio spam
// 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, 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",
"can_taste",
"escapable",
"escapetime",
"digestchance",
"absorbchance",
"escapechance",
"transferchance",
"transferlocation",
"bulge_size",
"struggle_messages_outside",
"struggle_messages_inside",
"digest_messages_owner",
"digest_messages_prey",
"examine_messages",
"emote_lists",
"silent"
)
//ommitted list
// "shrink_grow_size",
/obj/belly/New(var/newloc)
. = ..(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)
owner.vore_organs -= src
owner = null
. = ..()
// Called whenever an atom enters this belly
/obj/belly/Entered(var/atom/movable/thing,var/atom/OldLoc)
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(!silent && !recent_sound)
for(var/mob/M in get_hearers_in_view(5, get_turf(owner)))
if(M.client && M.client.prefs.toggles & EATING_NOISES)
playsound(get_turf(owner),"[src.vore_sound]",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
recent_sound = TRUE
//Messages if it's a mob
if(isliving(thing))
var/mob/living/M = thing
if(desc)
to_chat(M, "<span class='notice'><B>[desc]</B></span>")
var/taste
if(can_taste && (taste = M.get_taste_message(FALSE)))
to_chat(owner, "<span class='notice'>[M] tastes of [taste].</span>")
// 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/atom/destination = drop_location()
var/count = 0
for(var/thing in contents)
var/atom/movable/AM = thing
if(isliving(AM))
var/mob/living/L = AM
if(L.absorbed && !include_absorbed)
continue
L.absorbed = FALSE
for(var/mob/living/W in AM)
W.stop_sound_channel(CHANNEL_PREYLOOP)
AM.forceMove(destination) // Move the belly contents into the same location as belly's owner.
count++
for(var/mob/M in get_hearers_in_view(5, get_turf(owner)))
if(M.client && M.client.prefs.toggles & EATING_NOISES)
playsound(get_turf(owner),"[src.release_sound]",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
items_preserved.Cut()
owner.visible_message("<font color='green'><b>[owner] expels everything from their [lowertext(name)]!</b></font>")
owner.update_icons()
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)
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
for(var/mob/living/P in M)
P.stop_sound_channel(CHANNEL_PREYLOOP)
if(release_sound)
for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
if(H.client && H.client.prefs.toggles & EATING_NOISES)
playsound(get_turf(owner),"[src.release_sound]",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
if(istype(M,/mob/living))
var/mob/living/ML = M
var/mob/living/OW = owner
if(ML.absorbed)
ML.absorbed = FALSE
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.absorbed)
absorbed_count++
Pred.reagents.trans_to(Prey, Pred.reagents.total_volume / absorbed_count)
owner.visible_message("<font color='green'><b>[owner] expels [M] from their [lowertext(name)]!</b></font>")
owner.update_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.
/obj/belly/proc/nom_mob(var/mob/prey, var/mob/user)
var/sound/preyloop = sound('sound/vore/prey/loop.ogg', repeat = TRUE)
if(owner.stat == DEAD)
return
if (prey.buckled)
prey.buckled.unbuckle_mob(prey,TRUE)
prey.forceMove(src)
prey.playsound_local(loc,preyloop,70,0, channel = CHANNEL_PREYLOOP)
owner.updateVRPanel()
for(var/mob/living/M in contents)
M.updateVRPanel()
// Setup the autotransfer checks if needed
if(transferlocation && autotransferchance > 0)
addtimer(CALLBACK(src, /obj/belly/.proc/check_autotransfer, prey), autotransferwait)
/obj/belly/proc/check_autotransfer(var/mob/prey)
// Some sanity checks
if(transferlocation && (autotransferchance > 0) && (prey in contents))
if(prob(autotransferchance))
// Double check transferlocation isn't insane
if(verify_transferlocation())
transfer_contents(prey, transferlocation)
else
// Didn't transfer, so wait before retrying
addtimer(CALLBACK(src, /obj/belly/.proc/check_autotransfer, prey), autotransferwait)
/obj/belly/proc/verify_transferlocation()
for(var/I in owner.vore_organs)
var/obj/belly/B = owner.vore_organs[I]
if(B == transferlocation)
return TRUE
for(var/I in owner.vore_organs)
var/obj/belly/B = owner.vore_organs[I]
if(B == transferlocation)
transferlocation = B
return TRUE
return FALSE
// 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.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 = 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.
/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 = text2list(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(is_vore_predator(M))
for(var/belly in M.vore_organs)
var/obj/belly/B = belly
for(var/thing in B)
var/atom/movable/AM = thing
AM.forceMove(owner.loc)
if(isliving(AM))
to_chat(AM,"As [M] melts away around you, you find yourself in [owner]'s [lowertext(name)]")
//Drop all items into the belly
for(var/obj/item/W in M)
if(!M.dropItemToGround(W))
qdel(W)
/* //Reagent transfer //maybe someday
if(ishuman(owner))
var/mob/living/carbon/human/Pred = owner
if(ishuman(M))
var/mob/living/carbon/human/Prey = M
Prey.bloodstr.del_reagent("numbenzyme")
Prey.bloodstr.trans_to_holder(Pred.bloodstr, Prey.bloodstr.total_volume, 0.5, TRUE) // Copy=TRUE because we're deleted anyway
Prey.ingested.trans_to_holder(Pred.bloodstr, Prey.ingested.total_volume, 0.5, TRUE) // Therefore don't bother spending cpu
Prey.touching.trans_to_holder(Pred.bloodstr, Prey.touching.total_volume, 0.5, TRUE) // On updating the prey's reagents
else if(M.reagents)
M.reagents.trans_to_holder(Pred.bloodstr, M.reagents.total_volume, 0.5, TRUE) */
// Delete the digested mob
qdel(M)
// Handle a mob being absorbed
/obj/belly/proc/absorb_living(var/mob/living/M)
M.absorbed = TRUE
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>")
// Reagent sharing is neat, but eh. I'll figure it out later
/* if(ishuman(M) && ishuman(owner))
var/mob/living/carbon/human/Prey = M
var/mob/living/carbon/human/Pred = owner
//Reagent sharing for absorbed with pred - Copy so both pred and prey have these reagents.
Prey.bloodstr.trans_to_holder(Pred.bloodstr, Prey.bloodstr.total_volume, copy = TRUE)
Prey.ingested.trans_to_holder(Pred.bloodstr, Prey.ingested.total_volume, copy = TRUE)
Prey.touching.trans_to_holder(Pred.bloodstr, Prey.touching.total_volume, copy = TRUE)
// TODO - Find a way to make the absorbed prey share the effects with the pred.
// Currently this is infeasible because reagent containers are designed to have a single my_atom, and we get
// problems when A absorbs B, and then C absorbs A, resulting in B holding onto an invalid reagent container.
*/
//This is probably already the case, but for sub-prey, it won't be.
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.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.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.loc
//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!")
//Handle a mob struggling
// Called from /mob/living/carbon/relaymove()
/obj/belly/proc/relay_resist(var/mob/living/R)
if (!(R in contents))
return // User is not in this belly
R.setClickCooldown(50)
if(owner.stat || !owner.client && R.a_intent != INTENT_HELP) //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 5 seconds.)</span>")
to_chat(owner,"<span class='warning'>Someone is attempting to climb out of your [lowertext(name)]!</span>")
if(do_after(R, 50, owner))
if(owner.stat && (R in contents) && R.a_intent != INTENT_HELP) //Can still escape and want to?
release_specific_contents(R)
return
else if(!(R in 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 [lowertext(name)] has failed!</span>")
to_chat(owner,"<span class='notice'>The attempt to escape from your [lowertext(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 get_hearers_in_view(3, get_turf(owner)))
M.show_message(struggle_outer_message, 2) // hearable
to_chat(R,struggle_user_message)
if(!silent)
for(var/mob/M in get_hearers_in_view(5, get_turf(owner)))
if(M.client && M.client.prefs.toggles & EATING_NOISES)
playsound(get_turf(owner),"struggle_sound",35,0,-5,1,ignore_walls = FALSE,channel=CHANNEL_PRED)
R.stop_sound_channel(CHANNEL_PRED)
var/sound/prey_struggle = sound(get_sfx("prey_struggle"))
R.playsound_local(get_turf(R),prey_struggle,45,0)
if(R.a_intent != INTENT_HELP) //If on non help intent
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))
if((owner.stat || !owner.client || escapable) && (R in contents))
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>")
for(var/mob/M in hearers(4, owner))
M.show_message("<span class='warning'>[R] climbs out of [owner]'s [lowertext(name)]!</span>", 2)
return
else if(!istype(loc, /obj/belly)) //Aren't even in the belly. Quietly fail.
return
else //Belly became inescapable.
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_ITEMWEAK && 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_ITEMWEAK
return
else if(prob(digestchance) && digest_mode == DM_ITEMWEAK) //Oh god it gets even worse if you fail twice!
to_chat(R,"<span class='warning'>In response to your struggling, \the [lowertext(name)] begins to get even more active!</span>")
to_chat(owner,"<span class='warning'>You feel your [lowertext(name)] beginning to become even more active!</span>")
digest_mode = DM_DIGEST
return */
else if(prob(digestchance)) //Finally, let's see if it should run the digest chance.)
to_chat(R, "<span class='warning'>In response to your struggling, \the [name] begins to get more active...</span>")
to_chat(owner, "<span class='warning'>You feel your [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
//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
target.nom_mob(content, target.owner)
if(!silent)
for(var/mob/M in get_hearers_in_view(5, get_turf(owner)))
if(M.client && M.client.prefs.toggles & EATING_NOISES)
playsound(get_turf(owner),"[src.vore_sound]",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
owner.updateVRPanel()
for(var/mob/living/M in contents)
M.updateVRPanel()
// Belly copies and then returns the copy
// Needs to be updated for any var changes
/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.can_taste = can_taste
dupe.escapable = escapable
dupe.escapetime = escapetime
dupe.digestchance = digestchance
dupe.absorbchance = absorbchance
dupe.escapechance = escapechance
dupe.transferchance = transferchance
dupe.transferlocation = transferlocation
dupe.bulge_size = bulge_size
// dupe.shrink_grow_size = shrink_grow_size
//// 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
dupe.silent = silent
return dupe

View File

@@ -1,467 +0,0 @@
//
// 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 = 10 SECONDS // Time in deciseconds to swallow /mob/living/carbon/human
var/nonhuman_prey_swallow_time = 5 SECONDS // Time in deciseconds to swallow anything else
var/emoteTime = 30 SECONDS // 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 = FALSE // Belly can be resisted out of at any time
var/escapetime = 60 SECONDS // Deciseconds, how long to escape this belly
var/digestchance = 0 // % Chance of stomach beginning to digest if prey struggles
// var/silenced = FALSE // Will the heartbeat/fleshy internal loop play?
var/escapechance = 0 // % Chance of prey beginning to escape if prey struggles.
var/datum/belly/transferlocation = null // 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/can_taste = FALSE // If this belly prints the flavor of prey when it eats someone.
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,DM_NOISY) // 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
var/swallow_time = 10 SECONDS // for mob transfering automation
var/vore_capacity = 1 // The capacity (in people) this person can hold
// 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.
for(var/mob/living/W in M)
W.stop_sound_channel(CHANNEL_PREYLOOP)
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.
for(var/mob/living/W in M)
W.stop_sound_channel(CHANNEL_PREYLOOP)
src.internal_contents.Remove(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)
var/sound/preyloop = sound('sound/vore/prey/loop.ogg', repeat = TRUE)
prey.forceMove(owner)
internal_contents.Add(prey)
prey.playsound_local(get_turf(prey),preyloop,40,0, channel = CHANNEL_PREYLOOP)
// Handle prey messages
if(inside_flavor)
to_chat(prey, "<span class='warning'><B>[src.inside_flavor]</B></span>")
if(isliving(prey))
var/mob/living/M = prey
if(can_taste && M.get_taste_message(0))
to_chat(owner, "<span class='notice'>[M] tastes of [M.get_taste_message(0)].</span>")
// Setup the autotransfer checks if needed
if(transferlocation && autotransferchance > 0)
addtimer(CALLBACK(src, /datum/belly/.proc/check_autotransfer, prey), autotransferwait)
/datum/belly/proc/check_autotransfer(var/mob/prey)
// Some sanity checks
if(transferlocation && (autotransferchance > 0) && (prey in internal_contents))
if(prob(autotransferchance))
// Double check transferlocation isn't insane
if(verify_transferlocation())
transfer_contents(prey, transferlocation)
else
// Didn't transfer, so wait before retrying
addtimer(CALLBACK(src, /datum/belly/.proc/check_autotransfer, prey), autotransferwait)
/datum/belly/proc/verify_transferlocation()
for(var/I in owner.vore_organs)
var/datum/belly/B = owner.vore_organs[I]
if(B == transferlocation)
return TRUE
for(var/I in owner.vore_organs)
var/datum/belly/B = owner.vore_organs[I]
if(B.name == transferlocation.name)
transferlocation = B
return TRUE
return FALSE
// 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))
formatted_message = replacetext(formatted_message,"%pred",owner)
formatted_message = replacetext(formatted_message,"%prey",english_list(internal_contents))
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.stop_sound_channel(CHANNEL_PREYLOOP)
// 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)
var/sound/prey_struggle = sound(get_sfx("prey_struggle"))
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>"
R.visible_message( "<span class='alert'>[struggle_outer_message]</span>", "<span class='alert'>[struggle_user_message]</span>")
playsound(get_turf(owner),"struggle_sound",35,0,-6,1,channel=151,ignore_walls = FALSE)
R.stop_sound_channel(151)
R.playsound_local(get_turf(R),prey_struggle,45,0)
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 if(prob(transferchance) && istype(transferlocation)) //Next, let's have it see if they end up getting into an even bigger mess then when they started.
var/location_ok = verify_transferlocation()
if(!location_ok)
to_chat(owner, "<span class='warning'>Something went wrong with your belly transfer settings.</span>")
transferlocation = null
return
to_chat(R, "<span class='warning'>Your attempt to escape [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 [name]!</span>")
transfer_contents(R, transferlocation)
return
else if(prob(digestchance)) //Finally, let's see if it should run the digest chance.)
to_chat(R, "<span class='warning'>In response to your struggling, \the [name] begins to get more active...</span>")
to_chat(owner, "<span class='warning'>You feel your [name] beginning to become active!</span>")
digest_mode = DM_DIGEST
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
//Transfers contents from one belly to another
/datum/belly/proc/transfer_contents(var/atom/movable/content, var/datum/belly/target, silent = 0)
if(!(content in internal_contents))
return
internal_contents.Remove(content)
// Re-use nom_mob
target.nom_mob(content, target.owner)
if(!silent)
playsound(get_turf(owner),"[target].vore_sound",35,0,-6,1,ignore_walls = FALSE)
/*
//Handles creation of temporary 'vore chest' upon digestion
/datum/belly/proc/slimy_mass(var/obj/item/content, var/mob/living/M)
if(!content in internal_contents)
return
internal_contents += new /obj/structure/closet/crate/vore(src)
internal_contents.Remove(content)
M.transferItemToLoc(content, /obj/structure/closet/crate/vore)
if(!M.transferItemToLoc(W))
qdel(W)
/datum/belly/proc/regurgitate_items(var/obj/structure/closet/crate/vore/C)
*/
// 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.can_taste = can_taste
dupe.escapable = escapable
dupe.escapetime = escapetime
dupe.digestchance = digestchance
dupe.escapechance = escapechance
dupe.transferchance = transferchance
dupe.transferlocation = transferlocation
dupe.autotransferchance = autotransferchance
dupe.autotransferwait = autotransferwait
//// 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

View File

@@ -1,34 +1,53 @@
// Process the predator's effects upon the contents of its belly (i.e digestion/transformation etc)
// Called from /mob/living/Life() proc.
/datum/belly/proc/process_Life()
var/sound/prey_gurgle = sound(get_sfx("digest_prey"))
var/sound/prey_digest = sound(get_sfx("death_prey"))
/obj/belly/proc/process_belly(var/times_fired,var/wait) //Passed by controller
if((times_fired < next_process) || !contents.len)
recent_sound = FALSE
return SSBELLIES_IGNORED
if(loc != owner)
if(istype(owner))
loc = owner
else
qdel(src)
return SSBELLIES_PROCESSED
next_process = times_fired + (6 SECONDS/wait) //Set up our next process time.
/////////////////////////// Auto-Emotes ///////////////////////////
if((digest_mode in emote_lists) && !emotePend)
emotePend = TRUE
if(contents.len && next_emote <= times_fired)
next_emote = times_fired + round(emote_time/wait,1)
var/list/EL = emote_lists[digest_mode]
for(var/mob/living/M in contents)
if(M.digestable || !(digest_mode == DM_DIGEST)) // don't give digesty messages to indigestible people
to_chat(M,"<span class='notice'>[pick(EL)]</span>")
/////////////////////////// Exit Early ////////////////////////////
var/list/touchable_items = contents - items_preserved
if(!length(touchable_items))
return SSBELLIES_PROCESSED
////////////////////////// Sound vars /////////////////////////////
var/sound/prey_digest = sound(get_sfx("digest_prey"))
var/sound/prey_death = sound(get_sfx("death_prey"))
spawn(emoteTime)
var/list/EL = emote_lists[digest_mode]
for(var/mob/living/M in internal_contents)
M << "<span class='notice'>[pick(EL)]</span>"
src.emotePend = FALSE
///////////////////////////// DM_HOLD /////////////////////////////
if(digest_mode == DM_HOLD)
return //Pretty boring, huh
return SSBELLIES_PROCESSED
//////////////////////////// DM_DIGEST ////////////////////////////
if(digest_mode == DM_DIGEST)
for (var/mob/living/M in internal_contents)
else if(digest_mode == DM_DIGEST)
for (var/mob/living/M in contents)
if(prob(25))
M.stop_sound_channel(CHANNEL_PRED)
playsound(get_turf(owner),"digest_pred",50,0,-6,0,channel=CHANNEL_PRED,ignore_walls = FALSE)
M.stop_sound_channel(CHANNEL_PRED)
M.playsound_local(get_turf(M), null, 45, S = prey_gurgle)
M.stop_sound_channel(CHANNEL_DIGEST)
for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
if(H.client && H.client.prefs.toggles & DIGESTION_NOISES)
playsound(get_turf(owner),"digest_pred",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_DIGEST)
M.stop_sound_channel(CHANNEL_DIGEST)
M.playsound_local(get_turf(M), prey_digest, 45)
//Pref protection!
if (!M.digestable)
if (!M.digestable || M.absorbed)
continue
//Person just died in guts!
@@ -51,10 +70,13 @@
M.visible_message("<span class='notice'>You watch as [owner]'s form loses its additions.</span>")
owner.nutrition += 400 // so eating dead mobs gives you *something*.
M.stop_sound_channel(CHANNEL_PRED)
playsound(get_turf(owner),"death_pred",45,0,-6,0,channel=CHANNEL_PRED,ignore_walls = FALSE)
M.stop_sound_channel(CHANNEL_PRED)
M.playsound_local(get_turf(M), null, 45, S = prey_digest)
M.stop_sound_channel(DIGESTION_NOISES)
for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
if(H.client && H.client.prefs.toggles & DIGESTION_NOISES)
playsound(get_turf(owner),"death_pred",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_DIGEST)
M.stop_sound_channel(DIGESTION_NOISES)
M.stop_sound_channel(CHANNEL_PREYLOOP)
M.playsound_local(get_turf(M), prey_death, 65)
digestion_death(M)
owner.update_icons()
continue
@@ -64,44 +86,57 @@
if(!(M.status_flags & GODMODE))
M.adjustFireLoss(digest_burn)
owner.nutrition += 1
return
//Contaminate or gurgle items
var/obj/item/T = pick(touchable_items)
if(istype(T))
if(istype(T,/obj/item/reagent_containers/food) || istype(T,/obj/item/organ))
digest_item(T)
owner.updateVRPanel()
///////////////////////////// DM_HEAL /////////////////////////////
if(digest_mode == DM_HEAL)
for (var/mob/living/M in internal_contents)
for (var/mob/living/M in contents)
if(prob(25))
M.stop_sound_channel(CHANNEL_PRED)
playsound(get_turf(owner),"digest_pred",35,0,-6,0,channel=CHANNEL_PRED,ignore_walls = FALSE)
M.stop_sound_channel(CHANNEL_PRED)
M.playsound_local(get_turf(M), null, 45, S = prey_gurgle)
M.stop_sound_channel(CHANNEL_DIGEST)
for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
if(H.client && H.client.prefs.toggles & DIGESTION_NOISES)
playsound(get_turf(owner),"digest_pred",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_DIGEST)
M.stop_sound_channel(CHANNEL_DIGEST)
M.playsound_local(get_turf(M), prey_digest, 65)
if(M.stat != DEAD)
if(owner.nutrition >= NUTRITION_LEVEL_STARVING && (M.health < M.maxHealth))
M.adjustBruteLoss(-1)
M.adjustFireLoss(-1)
owner.nutrition -= 10
M.adjustBruteLoss(-3)
M.adjustFireLoss(-3)
owner.nutrition -= 5
return
////////////////////////// DM_NOISY /////////////////////////////////
//for when you just want people to squelch around
if(digest_mode == DM_NOISY)
for (var/mob/living/M in internal_contents)
for (var/mob/living/M in contents)
if(prob(35))
M.stop_sound_channel(CHANNEL_DIGEST)
for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
if(H.client && H.client.prefs.toggles & DIGESTION_NOISES)
playsound(get_turf(owner),"digest_pred",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_DIGEST)
M.stop_sound_channel(CHANNEL_PRED)
playsound(get_turf(owner),"digest_pred",35,0,-6,0,channel=CHANNEL_PRED,ignore_walls = FALSE)
M.stop_sound_channel(CHANNEL_PRED)
M.playsound_local(get_turf(M), null, 45, S = prey_gurgle)
M.playsound_local(get_turf(M), prey_digest, 65)
//////////////////////////DM_DRAGON /////////////////////////////////////
//because dragons need snowflake guts
if(digest_mode == DM_DRAGON)
for (var/mob/living/M in internal_contents)
for (var/mob/living/M in contents)
if(prob(25))
M.stop_sound_channel(CHANNEL_PRED)
playsound(get_turf(owner),"digest_pred",50,0,-6,0,channel=CHANNEL_PRED,ignore_walls = FALSE)
M.stop_sound_channel(CHANNEL_PRED)
M.playsound_local(get_turf(M), null, 45, S = prey_gurgle)
M.stop_sound_channel(CHANNEL_DIGEST)
for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
if(H.client && H.client.prefs.toggles & DIGESTION_NOISES)
playsound(get_turf(owner),"digest_pred",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_DIGEST)
M.stop_sound_channel(CHANNEL_DIGEST)
M.playsound_local(get_turf(M), prey_digest, 65)
//No digestion protection for megafauna.
@@ -124,12 +159,14 @@
to_chat(M, "<span class='warning'>[digest_alert_prey]</span>")
M.visible_message("<span class='notice'>You watch as [owner]'s guts loudly rumble as it finishes off a meal.</span>")
M.stop_sound_channel(CHANNEL_PRED)
playsound(get_turf(owner),"death_pred",45,0,-6,0,channel=CHANNEL_PRED)
M.stop_sound_channel(CHANNEL_PRED)
M.playsound_local(get_turf(M), null, 45, S = prey_digest)
M.stop_sound_channel(CHANNEL_DIGEST)
for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
if(H.client && H.client.prefs.toggles & DIGESTION_NOISES)
playsound(get_turf(owner),"death_pred",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_DIGEST)
M.stop_sound_channel(CHANNEL_DIGEST)
M.playsound_local(get_turf(M), prey_death, 65)
M.spill_organs(FALSE,TRUE,TRUE)
M << sound(null, repeat = 0, wait = 0, volume = 80, channel = CHANNEL_PREYLOOP)
M.stop_sound_channel(CHANNEL_PREYLOOP)
digestion_death(M)
owner.update_icons()
continue
@@ -138,6 +175,12 @@
// Deal digestion damage (and feed the pred)
if(!(M.status_flags & GODMODE))
M.adjustFireLoss(digest_burn)
M.adjustToxLoss(4) // something something plasma based acids
M.adjustCloneLoss(3) // eventually this'll kill you if you're healing everything else, you nerds.
return
M.adjustToxLoss(2) // something something plasma based acids
M.adjustCloneLoss(1) // eventually this'll kill you if you're healing everything else, you nerds.
//Contaminate or gurgle items
var/obj/item/T = pick(touchable_items)
if(istype(T))
if(istype(T,/obj/item/reagent_containers/food) || istype(T,/obj/item/organ))
digest_item(T)
owner.updateVRPanel()

View File

@@ -0,0 +1,119 @@
//Please make sure to:
//return FALSE: You are not going away, stop asking me to digest.
//return non-negative integer: Amount of nutrition/charge gained (scaled to nutrition, other end can multiply for charge scale).
// Ye default implementation.
/obj/item/proc/digest_act(var/atom/movable/item_storage = null)
for(var/obj/item/O in contents)
if(istype(O,/obj/item/storage/internal)) //Dump contents from dummy pockets.
for(var/obj/item/SO in O)
if(item_storage)
SO.forceMove(item_storage)
qdel(O)
else if(item_storage)
O.forceMove(item_storage)
qdel(src)
return w_class
/////////////
// Some indigestible stuff
/////////////
/obj/item/hand_tele/digest_act(...)
return FALSE
/obj/item/card/id/digest_act(...)
return FALSE
/obj/item/aicard/digest_act(...)
return FALSE
/obj/item/paicard/digest_act(...)
return FALSE
/obj/item/pinpointer/digest_act(...)
return FALSE
/obj/item/disk/nuclear/digest_act(...)
return FALSE
/obj/item/device/perfect_tele_beacon/digest_act(...)
return FALSE //Sorta important to not digest your own beacons.
/obj/item/device/pda/digest_act(...)
return FALSE
/obj/item/gun/digest_act(...)
return FALSE
/obj/item/clothing/shoes/magboots/digest_act(...)
return FALSE
/obj/item/clothing/head/helmet/space/digest_act(...)
return FALSE
/obj/item/clothing/suit/space/digest_act(...)
return FALSE
/obj/item/reagent_containers/hypospray/CMO/digest_act(...)
return FALSE
/obj/item/tank/jetpack/oxygen/captain/digest_act(...)
return FALSE
/obj/item/clothing/accessory/medal/gold/captain/digest_act(...)
return FALSE
/obj/item/clothing/suit/armor/digest_act(...)
return FALSE
/obj/item/documents/digest_act(...)
return FALSE
/obj/item/nuke_core/digest_act(...)
return FALSE
/obj/item/nuke_core_container/digest_act(...)
return FALSE
/obj/item/areaeditor/blueprints/digest_act(...)
return FALSE
/obj/item/documents/syndicate/digest_act(...)
return FALSE
/obj/item/bombcore/digest_act(...)
return FALSE
/obj/item/grenade/digest_act(...)
return FALSE
/obj/item/storage/digest_act(...)
return FALSE
/////////////
// Some special treatment
/////////////
/*
//PDAs need to lose their ID to not take it with them, so we can get a digested ID
/obj/item/device/pda/digest_act(var/atom/movable/item_storage = null)
if(id)
id = null
. = ..()
*/
/obj/item/reagent_containers/food/digest_act(var/atom/movable/item_storage = null)
if(isbelly(item_storage))
var/obj/belly/B = item_storage
if(ishuman(B.owner))
var/mob/living/carbon/human/H = B.owner
reagents.trans_to(H, (reagents.total_volume * 0.3), 1, 0)
else if(iscyborg(B.owner))
var/mob/living/silicon/robot/R = B.owner
R.cell.charge += 150
. = ..()
/*
/obj/item/holder/digest_act(var/atom/movable/item_storage = null)
for(var/mob/living/M in contents)
if(item_storage)
M.forceMove(item_storage)
held_mob = null
. = ..() */
/obj/item/organ/digest_act(var/atom/movable/item_storage = null)
if((. = ..()))
. += 70 //Organs give a little more
/obj/item/storage/digest_act(var/atom/movable/item_storage = null)
for(var/obj/item/I in contents)
I.screen_loc = null
. = ..()
/////////////
// Some more complicated stuff
/////////////
/obj/item/device/mmi/digital/posibrain/digest_act(var/atom/movable/item_storage = null)
//Replace this with a VORE setting so all types of posibrains can/can't be digested on a whim
return FALSE

View File

@@ -1,23 +1,24 @@
///////////////////// Mob Living /////////////////////
/mob/living
var/digestable = TRUE // Can the mob be digested inside a belly?
var/datum/belly/vore_selected // Default to no vore capability.
var/obj/belly/vore_selected // Default to no vore capability.
var/list/vore_organs = list() // List of vore containers inside a mob
var/devourable = FALSE // Can the mob be vored at all?
// var/feeding = FALSE // Are we going to feed someone else?
var/vore_taste = null // What the character tastes like
var/no_vore = FALSE // If the character/mob can vore.
var/openpanel = 0 // Is the vore panel open?
var/noisy = FALSE // tummies are rumbly?
var/absorbed = FALSE //are we absorbed?
//
// Hook for generic creation of stuff on new creatures
//
/hook/living_new/proc/vore_setup(mob/living/M)
M.verbs += /mob/living/proc/lick
M.verbs += /mob/living/proc/preyloop_refresh
if(M.no_vore) //If the mob isn's supposed to have a stomach, let's not give it an insidepanel so it can make one for itself, or a stomach.
M << "<span class='warning'>The creature that you are can not eat others.</span>"
return TRUE
M.verbs += /mob/living/proc/lick
if(M.no_vore) //If the mob isn't supposed to have a stomach, let's not give it an insidepanel so it can make one for itself, or a stomach.
return 1
M.verbs += /mob/living/proc/insidePanel
//Tries to load prefs if a client is present otherwise gives freebie stomach
@@ -27,44 +28,36 @@
if(M.client && M.client.prefs_vr)
if(!M.copy_from_prefs_vr())
M << "<span class='warning'>ERROR: You seem to have saved vore prefs, but they couldn't be loaded.</span>"
return FALSE
to_chat(M,"<span class='warning'>ERROR: You seem to have saved vore prefs, but they couldn't be loaded.</span>")
return 0
if(M.vore_organs && M.vore_organs.len)
M.vore_selected = M.vore_organs[1]
if(!M.vore_organs || !M.vore_organs.len)
if(!M.vore_organs)
M.vore_organs = list()
var/datum/belly/B = new /datum/belly(M)
var/obj/belly/B = new /obj/belly(M)
M.vore_selected = B
B.immutable = TRUE
B.name = "Stomach"
B.inside_flavor = "It appears to be rather warm and wet. Makes sense, considering it's inside \the [M.name]"
B.can_taste = TRUE
M.vore_organs[B.name] = B
M.vore_selected = B.name
//Simple_animal gets emotes. move this to that hook instead?
if(istype(src,/mob/living/simple_animal))
B.emote_lists[DM_HOLD] = list(
"The insides knead at you gently for a moment.",
"The guts glorp wetly around you as some air shifts.",
"Your predator takes a deep breath and sighs, shifting you somewhat.",
"The stomach squeezes you tight for a moment, then relaxes.",
"During a moment of quiet, breathing becomes the most audible thing.",
"The warm slickness surrounds and kneads on you.")
B.emote_lists[DM_DIGEST] = list(
"The caustic acids eat away at your form.",
"The acrid air burns at your lungs.",
"Without a thought for you, the stomach grinds inwards painfully.",
"The guts treat you like food, squeezing to press more acids against you.",
"The onslaught against your body doesn't seem to be letting up; you're food now.",
"The insides work on you like they would any other food.")
B.desc = "It appears to be rather warm and wet. Makes sense, considering it's inside \the [M.name]."
B.can_taste = FALSE
//Return 1 to hook-caller
return 1
/*
// Hide vore organs in contents
//
/datum/proc/view_variables_filter_contents(list/L)
return 0
/mob/living/view_variables_filter_contents(list/L)
. = ..()
var/len_before = L.len
L -= vore_organs
. += len_before - L.len*/
// Handle being clicked, perhaps with something to devour
//
@@ -126,33 +119,37 @@
var/belly = user.vore_selected
return perform_dragon(user, prey, user, belly)
/mob/living/proc/perform_dragon(var/mob/living/user, var/mob/living/prey, var/mob/living/pred, var/belly, swallow_time = 20)
/mob/living/proc/perform_dragon(var/mob/living/user, var/mob/living/prey, var/mob/living/pred, var/obj/belly/belly, swallow_time = 20)
//Sanity
if(!user || !prey || !pred || !belly || !(belly in pred.vore_organs))
if(!user || !prey || !pred || !istype(belly) || !(belly in pred.vore_organs))
testing("[user] attempted to feed [prey] to [pred], via [lowertext(belly.name)] but it went wrong.")
return
// The belly selected at the time of noms
var/datum/belly/belly_target = pred.vore_organs[belly]
var/attempt_msg = "ERROR: Vore message couldn't be created. Notify a dev. (at)"
var/success_msg = "ERROR: Vore message couldn't be created. Notify a dev. (sc)"
/* //Final distance check. Time has passed, menus have come and gone. Can't use do_after adjacent because doesn't behave for held micros
var/user_to_pred = get_dist(get_turf(user),get_turf(pred))
var/user_to_prey = get_dist(get_turf(user),get_turf(prey)) */
// Prepare messages
if(user == pred) //Feeding someone to yourself
attempt_msg = text("<span class='warning'>[] starts to [] [] into their []!</span>",pred,lowertext(belly_target.vore_verb),prey,lowertext(belly_target.name))
success_msg = text("<span class='warning'>[] manages to [] [] into their []!</span>",pred,lowertext(belly_target.vore_verb),prey,lowertext(belly_target.name))
attempt_msg = text("<span class='warning'>[] starts to [] [] into their []!</span>",pred,lowertext(belly.vore_verb),prey,lowertext(belly.name))
success_msg = text("<span class='warning'>[] manages to [] [] into their []!</span>",pred,lowertext(belly.vore_verb),prey,lowertext(belly.name))
// Announce that we start the attempt!
user.visible_message(attempt_msg)
if(!do_mob(src, user, swallow_time)) // one second should be good enough, right?
if(!do_mob(src, user, swallow_time))
return FALSE // Prey escaped (or user disabled) before timer expired.
// If we got this far, nom successful! Announce it!
user.visible_message(success_msg)
playsound(get_turf(user), belly_target.vore_sound,75,0,-6,0)
playsound(get_turf(user), "[belly.vore_sound]",75,0,-6,0)
// Actually shove prey into the belly.
belly_target.nom_mob(prey, user)
belly.nom_mob(prey, user)
if (pred == user)
message_admins("[key_name(pred)] ate [key_name(prey)].")
log_attack("[key_name(pred)] ate [key_name(prey)]")
@@ -161,43 +158,55 @@
// Master vore proc that actually does vore procedures
//
/mob/living/proc/perform_the_nom(var/mob/living/user, var/mob/living/prey, var/mob/living/pred, var/belly, swallow_time = 100)
/mob/living/proc/perform_the_nom(var/mob/living/user, var/mob/living/prey, var/mob/living/pred, var/obj/belly/belly, var/delay)
//Sanity
if(!user || !prey || !pred || !belly || !(belly in pred.vore_organs))
if(!user || !prey || !pred || !istype(belly) || !(belly in pred.vore_organs))
testing("[user] attempted to feed [prey] to [pred], via [lowertext(belly.name)] but it went wrong.")
return
if (!prey.devourable)
to_chat(user, "This can't be eaten!")
return
// The belly selected at the time of noms
var/datum/belly/belly_target = pred.vore_organs[belly]
var/attempt_msg = "ERROR: Vore message couldn't be created. Notify a dev. (at)"
var/success_msg = "ERROR: Vore message couldn't be created. Notify a dev. (sc)"
/* //Final distance check. Time has passed, menus have come and gone. Can't use do_after adjacent because doesn't behave for held micros
var/user_to_pred = get_dist(get_turf(user),get_turf(pred))
var/user_to_prey = get_dist(get_turf(user),get_turf(prey)) */
// Prepare messages
if(user == pred) //Feeding someone to yourself
attempt_msg = text("<span class='warning'>[] is attemping to [] [] into their []!</span>",pred,lowertext(belly_target.vore_verb),prey,lowertext(belly_target.name))
success_msg = text("<span class='warning'>[] manages to [] [] into their []!</span>",pred,lowertext(belly_target.vore_verb),prey,lowertext(belly_target.name))
attempt_msg = text("<span class='warning'>[] is attemping to [] [] into their []!</span>",pred,lowertext(belly.vore_verb),prey,lowertext(belly.name))
success_msg = text("<span class='warning'>[] manages to [] [] into their []!</span>",pred,lowertext(belly.vore_verb),prey,lowertext(belly.name))
else //Feeding someone to another person
attempt_msg = text("<span class='warning'>[] is attempting to make [] [] [] into their []!</span>",user,pred,lowertext(belly_target.vore_verb),prey,lowertext(belly_target.name))
success_msg = text("<span class='warning'>[] manages to make [] [] [] into their []!</span>",user,pred,lowertext(belly_target.vore_verb),prey,lowertext(belly_target.name))
attempt_msg = text("<span class='warning'>[] is attempting to make [] [] [] into their []!</span>",user,pred,lowertext(belly.vore_verb),prey,lowertext(belly.name))
success_msg = text("<span class='warning'>[] manages to make [] [] [] into their []!</span>",user,pred,lowertext(belly.vore_verb),prey,lowertext(belly.name))
// Announce that we start the attempt!
user.visible_message(attempt_msg)
// Now give the prey time to escape... return if they did
var/swallow_time = delay || ishuman(prey) ? belly.human_prey_swallow_time : belly.nonhuman_prey_swallow_time
if(!do_mob(src, user, swallow_time))
return FALSE // Prey escaped (or user disabled) before timer expired.
// If we got this far, nom successful! Announce it!
user.visible_message(success_msg)
playsound(get_turf(user), belly_target.vore_sound,75,0,-6,0,ignore_walls = FALSE)
for(var/mob/M in get_hearers_in_view(5, get_turf(user)))
if(M.client && M.client.prefs.toggles & EATING_NOISES)
playsound(get_turf(user),"[belly.vore_sound]",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
// Actually shove prey into the belly.
belly_target.nom_mob(prey, user)
belly.nom_mob(prey, user)
// user.update_icons()
stop_pulling()
// Flavor handling
if(belly.can_taste && prey.get_taste_message(FALSE))
to_chat(belly.owner, "<span class='notice'>[prey] tastes of [prey.get_taste_message(FALSE)].</span>")
// Inform Admins
var/prey_braindead
var/prey_stat
@@ -250,50 +259,38 @@
return 0
*/
//
// Release everything in every vore organ
//
/mob/living/proc/release_vore_contents(var/include_absorbed = TRUE)
for(var/belly in vore_organs)
var/obj/belly/B = belly
B.release_all_contents(include_absorbed)
//
// Custom resist catches for /mob/living
//
/mob/living/proc/vore_process_resist()
//Are we resisting from inside a belly?
var/datum/belly/B = check_belly(src)
if(B)
spawn() B.relay_resist(src)
if(isbelly(loc))
var/obj/belly/B = loc
B.relay_resist(src)
return TRUE //resist() on living does this TRUE thing.
//Other overridden resists go here
return FALSE
//
// Proc for updating vore organs and digestion/healing/absorbing
//
/mob/living/proc/handle_internal_contents()
if(SSmobs.times_fired%6==1)
return //The accursed timer
for (var/I in vore_organs)
var/datum/belly/B = vore_organs[I]
if(B.internal_contents.len)
B.process_Life() //AKA 'do bellymodes_vr.dm'
for (var/I in vore_organs)
var/datum/belly/B = vore_organs[I]
if(B.internal_contents.len)
listclearnulls(B.internal_contents)
for(var/atom/movable/M in B.internal_contents)
if(M.loc != src)
B.internal_contents.Remove(M)
// internal slimy button in case the loop stops playing but the player wants to hear it
/mob/living/proc/preyloop_refresh()
set name = "Internal loop refresh"
set category = "Vore"
if(ismob(src.loc))
if(istype(src.loc, /obj/belly))
src.stop_sound_channel(CHANNEL_PREYLOOP) // sanity just in case
var/sound/preyloop = sound('sound/vore/prey/loop.ogg', repeat = TRUE)
src.playsound_local(get_turf(src),preyloop,40,0, channel = CHANNEL_PREYLOOP)
src.playsound_local(get_turf(src),preyloop,80,0, channel = CHANNEL_PREYLOOP)
else
to_chat(src, "<span class='alert'>You aren't inside anything, you clod.</span>")
@@ -309,7 +306,7 @@
var/confirm = alert(src, "You're in a mob. Use this as a trick to get out of hostile animals. If you are in more than one pred, use this more than once.", "Confirmation", "Okay", "Cancel")
if(confirm == "Okay")
for(var/I in pred.vore_organs)
var/datum/belly/B = pred.vore_organs[I]
var/obj/belly/B = pred.vore_organs[I]
B.release_specific_contents(src)
for(var/mob/living/simple_animal/SA in range(10))
@@ -355,9 +352,15 @@
P.digestable = src.digestable
P.devourable = src.devourable
P.belly_prefs = src.vore_organs
P.vore_taste = src.vore_taste
var/list/serialized = list()
for(var/belly in src.vore_organs)
var/obj/belly/B = belly
serialized += list(B.serialize()) //Can't add a list as an object to another list in Byond. Thanks.
P.belly_prefs = serialized
return TRUE
//
@@ -370,16 +373,52 @@
var/datum/vore_preferences/P = client.prefs_vr
src.digestable = P.digestable
src.devourable = P.devourable
src.vore_organs = list()
src.vore_taste = P.vore_taste
digestable = P.digestable
devourable = P.devourable
vore_taste = P.vore_taste
for(var/I in P.belly_prefs)
var/datum/belly/Bp = P.belly_prefs[I]
src.vore_organs[Bp.name] = Bp.copy(src)
vore_organs.Cut()
for(var/entry in P.belly_prefs)
list_to_object(entry,src)
return TRUE
//
// Returns examine messages for bellies
//
/mob/living/proc/examine_bellies()
if(!show_pudge()) //Some clothing or equipment can hide this.
return ""
var/message = ""
for (var/belly in vore_organs)
var/obj/belly/B = belly
message += B.get_examine_msg()
return message
//
// Whether or not people can see our belly messages
//
/mob/living/proc/show_pudge()
return TRUE //Can override if you want.
/mob/living/carbon/human/show_pudge()
//A uniform could hide it.
if(istype(w_uniform,/obj/item/clothing))
var/obj/item/clothing/under = w_uniform
if(under.hides_bulges)
return FALSE
//We return as soon as we find one, no need for 'else' really.
if(istype(wear_suit,/obj/item/clothing))
var/obj/item/clothing/suit = wear_suit
if(suit.hides_bulges)
return FALSE
return ..()
//
// Clearly super important. Obviously.
//

View File

@@ -31,7 +31,7 @@
//
// Simple nom proc for if you get ckey'd into a simple_animal mob! Avoids grabs.
//
/mob/living/proc/animal_nom(var/mob/living/T in oview(1))
/mob/living/simple_animal/proc/animal_nom(var/mob/living/T in oview(1))
set name = "Animal Nom"
set category = "Vore"
set desc = "Since you can't grab, you get a verb!"

View File

@@ -23,6 +23,7 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE
// Overrides/additions to stock defines go here, as well as hooks. Sort them by
// the object they are overriding. So all /mob/living together, etc.
//
//
// The datum type bolted onto normal preferences datums for storing Vore stuff
//
@@ -40,21 +41,23 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE
//Actual preferences
var/digestable = TRUE
var/devourable = FALSE
// var/allowmobvore = TRUE
var/list/belly_prefs = list()
var/vore_taste
var/vore_taste = "nothing in particular"
// var/can_be_drop_prey = FALSE
// var/can_be_drop_pred = FALSE
//Mechanically required
var/path
var/slot
var/client/client
var/client_ckey
var/client/parent
/datum/vore_preferences/New(client/C)
if(istype(C))
client = C
client_ckey = C.ckey
load_vore(C)
load_vore()
//
// Check if an object is capable of eating things, based on vore_organs
@@ -68,46 +71,60 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE
//
// Belly searching for simplifying other procs
// Mostly redundant now with belly-objects and isbelly(loc)
//
/proc/check_belly(atom/movable/A)
if(istype(A.loc,/mob/living))
var/mob/living/M = A.loc
for(var/I in M.vore_organs)
var/datum/belly/B = M.vore_organs[I]
if(A in B.internal_contents)
return(B)
return FALSE
return isbelly(A.loc)
//
// Save/Load Vore Preferences
//
/datum/vore_preferences/proc/load_path(ckey,slot,filename="character",ext="json")
if(!ckey || !slot) return
path = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/vore/[filename][slot].[ext]"
/datum/vore_preferences/proc/load_vore()
if(!client || !client_ckey) return FALSE //No client, how can we save?
if(!client || !client_ckey)
return FALSE //No client, how can we save?
if(!client.prefs || !client.prefs.default_slot)
return FALSE //Need to know what character to load!
slot = client.prefs.default_slot
path = client.prefs.path
load_path(client_ckey,slot)
if(!path) return FALSE //Path couldn't be set?
if(!fexists(path)) //Never saved before
save_vore() //Make the file first
return TRUE
var/savefile/S = new /savefile(path)
if(!S) return FALSE //Savefile object couldn't be created?
var/list/json_from_file = json_decode(file2text(path))
if(!json_from_file)
return FALSE //My concern grows
S.cd = "/character[slot]"
var/version = json_from_file["version"]
json_from_file = patch_version(json_from_file,version)
S["digestable"] >> digestable
S["devourable"] >> devourable
S["belly_prefs"] >> belly_prefs
S["vore_taste"] >> vore_taste
digestable = json_from_file["digestable"]
devourable = json_from_file["devourable"]
// allowmobvore = json_from_file["allowmobvore"]
vore_taste = json_from_file["vore_taste"]
// can_be_drop_prey = json_from_file["can_be_drop_prey"]
// can_be_drop_prey = json_from_file["can_be_drop_pred"]
belly_prefs = json_from_file["belly_prefs"]
//Quick sanitize
if(isnull(digestable))
digestable = TRUE
if(isnull(devourable))
devourable = FALSE
/* if(isnull(allowmobvore))
allowmobvore = TRUE
if(isnull(can_be_drop_prey))
allowmobvore = FALSE
if(isnull(can_be_drop_pred))
allowmobvore = FALSE */
if(isnull(belly_prefs))
belly_prefs = list()
@@ -115,28 +132,37 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE
/datum/vore_preferences/proc/save_vore()
if(!path) return FALSE
if(!slot) return FALSE
var/savefile/S = new /savefile(path)
if(!S) return FALSE
S.cd = "/character[slot]"
WRITE_FILE(S["digestable"], digestable)
WRITE_FILE(S["devourable"], devourable)
WRITE_FILE(S["belly_prefs"], belly_prefs)
WRITE_FILE(S["vore_taste"], vore_taste)
var/version = 1 //For "good times" use in the future
var/list/settings_list = list(
"version" = version,
"digestable" = digestable,
"devourable" = devourable,
"vore_taste" = vore_taste,
"belly_prefs" = belly_prefs,
)
/* commented out list things
"allowmobvore" = allowmobvore,
"can_be_drop_prey" = can_be_drop_prey,
"can_be_drop_pred" = can_be_drop_pred, */
//List to JSON
var/json_to_file = json_encode(settings_list)
if(!json_to_file)
testing("Saving: [path] failed jsonencode")
return FALSE
//Write it out
if(fexists(path))
fdel(path) //Byond only supports APPENDING to files, not replacing.
text2file(json_to_file,path)
if(!fexists(path))
testing("Saving: [path] failed file write")
return FALSE
return TRUE
#ifdef TESTING
//DEBUG
//Some crude tools for testing savefiles
//path is the savefile path
/client/verb/vore_savefile_export(path as text)
var/savefile/S = new /savefile(path)
S.ExportText("/",file("[path].txt"))
//path is the savefile path
/client/verb/vore_savefile_import(path as text)
var/savefile/S = new /savefile(path)
S.ImportText("/",file("[path].txt"))
#endif
//Can do conversions here
/datum/vore_preferences/proc/patch_version(var/list/json_from_file,var/version)
return json_from_file

View File

@@ -16,7 +16,7 @@
/obj/item/projectile/sickshot
name = "sickshot pulse"
icon_state = "e_netting"
damage = 1
damage = 0
damage_type = STAMINA
range = 2
@@ -25,8 +25,7 @@
var/mob/living/carbon/H = target
if(prob(5))
for(var/X in H.vore_organs)
var/datum/belly/B = H.vore_organs[X]
B.release_all_contents()
H.release_vore_contents()
H.visible_message("<span class='danger'>[H] contracts strangely, spewing out contents on the floor!</span>", \
"<span class='userdanger'>You spew out everything inside you on the floor!</span>")
return

View File

@@ -14,7 +14,7 @@
var/datum/vore_look/picker_holder = new()
picker_holder.loop = picker_holder
picker_holder.selected = vore_organs[vore_selected]
picker_holder.selected = vore_selected
var/dat = picker_holder.gen_vui(src)
@@ -22,11 +22,23 @@
picker_holder.popup.set_content(dat)
picker_holder.popup.open()
/mob/living/proc/updateVRPanel() //Panel popup update call from belly events.
if(src.openpanel == 1)
var/datum/vore_look/picker_holder = new()
picker_holder.loop = picker_holder
picker_holder.selected = vore_selected
var/dat = picker_holder.gen_vui(src)
picker_holder.popup = new(src, "insidePanel","Vore Panel", 400, 600, picker_holder)
picker_holder.popup.set_content(dat)
picker_holder.popup.open()
//
// Callback Handler for the Inside form
//
/datum/vore_look
var/datum/belly/selected
var/obj/belly/selected
var/show_interacts = TRUE
var/datum/browser/popup
var/loop = null; // Magic self-reference to stop the handler from being GC'd before user takes action.
@@ -44,29 +56,38 @@
/datum/vore_look/proc/gen_vui(var/mob/living/user)
var/dat
if (is_vore_predator(user.loc))
var/mob/living/eater = user.loc
var/datum/belly/inside_belly
//This big block here figures out where the prey is
inside_belly = check_belly(user)
var/atom/userloc = user.loc
if (isbelly(userloc))
var/obj/belly/inside_belly = userloc
var/mob/living/eater = inside_belly.owner
//Don't display this part if we couldn't find the belly since could be held in hand.
if(inside_belly)
dat += "<font color = 'green'>You are currently inside</font> <font color = 'yellow'>[eater]'s</font> <font color = 'red'>[inside_belly]</font>!<br><br>"
dat += "<font color = 'green'>You are currently [user.absorbed ? "absorbed into " : "inside "]</font> <font color = 'yellow'>[eater]'s</font> <font color = 'red'>[inside_belly]</font>!<br><br>"
if(inside_belly.inside_flavor)
dat += "[inside_belly.inside_flavor]<br><br>"
if(inside_belly.desc)
dat += "[inside_belly.desc]<br><br>"
if (inside_belly.internal_contents.len > 1)
if (inside_belly.contents.len > 1)
dat += "You can see the following around you:<br>"
for (var/atom/movable/O in inside_belly.internal_contents)
for (var/atom/movable/O in inside_belly)
if(istype(O,/mob/living))
var/mob/living/M = O
//That's just you
if(M == user)
continue
//That's an absorbed person you're checking
if(M.absorbed)
if(user.absorbed)
dat += "<a href='?src=\ref[src];outsidepick=\ref[O];outsidebelly=\ref[inside_belly]'><span style='color:purple;'>[O]</span></a>"
continue
else
continue
//Anything else
dat += "<a href='?src=\ref[src];outsidepick=\ref[O];outsidebelly=\ref[inside_belly]'>[O]</a>"
dat += "<a href='?src=\ref[src];outsidepick=\ref[O];outsidebelly=\ref[inside_belly]'>[O]&#8203;</a>"
//Zero-width space, for wrapping
dat += "&#8203;"
else
@@ -75,8 +96,8 @@
dat += "<HR>"
dat += "<ol style='list-style: none; padding: 0; overflow: auto;'>"
for(var/K in user.vore_organs) //Fuggin can't iterate over values
var/datum/belly/B = user.vore_organs[K]
for(var/belly in user.vore_organs)
var/obj/belly/B = belly
if(B == selected)
dat += "<li style='float: left'><a href='?src=\ref[src];bellypick=\ref[B]'><b>[B.name]</b>"
else
@@ -90,8 +111,10 @@
spanstyle = "color:red;"
if(DM_HEAL)
spanstyle = "color:green;"
if(DM_NOISY)
spanstyle = "color:purple;"
dat += "<span style='[spanstyle]'> ([B.internal_contents.len])</span></a></li>"
dat += "<span style='[spanstyle]'> ([B.contents.len])</span></a></li>"
if(user.vore_organs.len < BELLIES_MAX)
dat += "<li style='float: left'><a href='?src=\ref[src];newbelly=1'>New+</a></li>"
@@ -102,15 +125,27 @@
if(!selected)
dat += "No belly selected. Click one to select it."
else
if(selected.internal_contents.len > 0)
if(selected.contents.len)
dat += "<b>Contents:</b> "
for(var/O in selected.internal_contents)
for(var/O in selected)
//Mobs can be absorbed, so treat them separately from everything else
if(istype(O,/mob/living))
var/mob/living/M = O
//Absorbed gets special color OOoOOOOoooo
if(M.absorbed)
dat += "<a href='?src=\ref[src];insidepick=\ref[O]'><span style='color:purple;'>[O]</span></a>"
continue
//Anything else
dat += "<a href='?src=\ref[src];insidepick=\ref[O]'>[O]</a>"
//Zero-width space, for wrapping
dat += "&#8203;"
//If there's more than one thing, add an [All] button
if(selected.internal_contents.len > 1)
if(selected.contents.len > 1)
dat += "<a href='?src=\ref[src];insidepick=1;pickall=1'>\[All\]</a>"
dat += "<HR>"
@@ -129,14 +164,15 @@
//Inside flavortext
dat += "<br><a href='?src=\ref[src];b_desc=\ref[selected]'>Flavor Text:</a>"
dat += " '[selected.inside_flavor]'"
dat += " '[selected.desc]'"
//Belly sound
dat += "<br><a href='?src=\ref[src];b_sound=\ref[selected]'>Set Vore Sound</a>"
dat += "<a href='?src=\ref[src];b_soundtest=\ref[selected]'>Test</a>"
// //Belly silence
// dat += "<br><a href='?src=\ref[src];silenced=\ref[selected]'>Belly Silence ([selected.silenced ? "Silenced" : "Noisy"])</a>"
//Release sound
dat += "<br><a href='?src=\ref[src];b_release=\ref[selected]'>Set Release Sound</a>"
dat += "<a href='?src=\ref[src];b_releasesoundtest=\ref[selected]'>Test</a>"
//Belly messages
dat += "<br><a href='?src=\ref[src];b_msgs=\ref[selected]'>Belly Messages</a>"
@@ -145,6 +181,10 @@
dat += "<br><a href='?src=\ref[src];b_tastes=\ref[selected]'>Can Taste:</a>"
dat += " [selected.can_taste ? "Yes" : "No"]"
//Minimum size prey must be to show up.
dat += "<br><a href='?src=\ref[src];b_bulge_size=\ref[selected]'>Required examine size:</a>"
dat += " [selected.bulge_size*100]%"
//Belly escapability
dat += "<br><a href='?src=\ref[src];b_escapable=\ref[selected]'>Belly Interactions ([selected.escapable ? "On" : "Off"])</a>"
if(selected.escapable)
@@ -173,15 +213,21 @@
dat += " [selected.digestchance]%"
dat += "<HR>"
// Belly Silence
dat += "<br><a href='?src=\ref[src];b_silent=\ref[selected]'>Belly Silence (for not belly bellies):</a>"
dat += " [selected.silent ? "Yes" : "No"]"
//Delete button
dat += "<br><a style='background:#990000;' href='?src=\ref[src];b_del=\ref[selected]'>Delete Belly</a>"
dat += "<a href='?src=\ref[src];setflavor=1'>Set Flavor</a>"
dat += "<a href='?src=\ref[src];togglenoisy=1'>Toggle Hunger Noises</a>"
dat += "<HR>"
//Under the last HR, save and stuff.
dat += "<a href='?src=\ref[src];saveprefs=1'>Save Prefs</a>"
dat += "<a href='?src=\ref[src];refresh=1'>Refresh</a>"
dat += "<a href='?src=\ref[src];setflavor=1'>Set Flavor</a>"
dat += "<HR>"
switch(user.digestable)
@@ -221,8 +267,8 @@
if(href_list["outsidepick"])
var/atom/movable/tgt = locate(href_list["outsidepick"])
var/datum/belly/OB = locate(href_list["outsidebelly"])
if(!(tgt in OB.internal_contents)) //Aren't here anymore, need to update menu.
var/obj/belly/OB = locate(href_list["outsidebelly"])
if(!(tgt in OB)) //Aren't here anymore, need to update menu.
return TRUE
var/intent = "Examine"
@@ -234,42 +280,49 @@
M.examine(user)
if("Help Out") //Help the inside-mob out
to_chat(user, "<font color='green'>You begin to push [M] to freedom!</font>")
to_chat(M, "[usr] begins to push you to freedom!")
M.loc << "<span class='warning'>Someone is trying to escape from inside you!</span>"
if(user.stat || user.absorbed || M.absorbed)
to_chat(user,"<span class='warning'>You can't do that in your state!</span>")
return 1
to_chat(user,"<font color='green'>You begin to push [M] to freedom!</font>")
to_chat(M,"[usr] begins to push you to freedom!")
to_chat(M.loc,"<span class='warning'>Someone is trying to escape from inside you!</span>")
sleep(50)
if(prob(33))
OB.release_specific_contents(M)
to_chat(usr, "<font color='green'>You manage to help [M] to safety!</font>")
to_chat(M, "<font color='green'>[user] pushes you free!</font>")
M.loc << "<span class='alert'>[M] forces free of the confines of your body!</span>"
to_chat(usr,"<font color='green'>You manage to help [M] to safety!</font>")
to_chat(M,"<font color='green'>[user] pushes you free!</font>")
to_chat(OB.owner,"<span class='alert'>[M] forces free of the confines of your body!</span>")
else
to_chat(user, "<span class='alert'>[M] slips back down inside despite your efforts.</span>")
to_chat(M, "<span class='alert'> Even with [user]'s help, you slip back inside again.</span>")
M.loc << "<font color='green'>Your body efficiently shoves [M] back where they belong.</font>"
to_chat(user,"<span class='alert'>[M] slips back down inside despite your efforts.</span>")
to_chat(M,"<span class='alert'> Even with [user]'s help, you slip back inside again.</span>")
to_chat(OB.owner,"<font color='green'>Your body efficiently shoves [M] back where they belong.</font>")
if("Devour") //Eat the inside mob
if(user.absorbed || user.stat)
to_chat(user,"<span class='warning'>You can't do that in your state!</span>")
return 1
if(!user.vore_selected)
to_chat(user, "<span class='warning'>Pick a belly on yourself first!</span>")
return
to_chat(user,"<span class='warning'>Pick a belly on yourself first!</span>")
return 1
var/datum/belly/TB = user.vore_organs[user.vore_selected]
to_chat(user, "<span class='warning'>You begin to [lowertext(TB.vore_verb)] [M] into your [lowertext(TB.name)]!</span>")
to_chat(M, "<span class='warning'>[user] begins to [lowertext(TB.vore_verb)] you into their [lowertext(TB.name)]!</span>")
M.loc << "<span class='warning'>Someone inside you is eating someone else!</span>"
var/obj/belly/TB = user.vore_selected
to_chat(user,"<span class='warning'>You begin to [lowertext(TB.vore_verb)] [M] into your [lowertext(TB.name)]!</span>")
to_chat(M,"<span class='warning'>[user] begins to [lowertext(TB.vore_verb)] you into their [lowertext(TB.name)]!</span>")
to_chat(OB.owner,"<span class='warning'>Someone inside you is eating someone else!</span>")
sleep(TB.nonhuman_prey_swallow_time)
if((user in OB.internal_contents) && (M in OB.internal_contents))
to_chat(user, "<span class='warning'>You manage to [lowertext(TB.vore_verb)] [M] into your [lowertext(TB.name)]!</span>")
to_chat(M, "<span class='warning'>[user] manages to [lowertext(TB.vore_verb)] you into their [lowertext(TB.name)]!</span>")
M.loc << "<span class='warning'>Someone inside you has eaten someone else!</span>"
M.loc = user
sleep(TB.nonhuman_prey_swallow_time) //Can't do after, in a stomach, weird things abound.
if((user in OB) && (M in OB)) //Make sure they're still here.
to_chat(user,"<span class='warning'>You manage to [lowertext(TB.vore_verb)] [M] into your [lowertext(TB.name)]!</span>")
to_chat(M,"<span class='warning'>[user] manages to [lowertext(TB.vore_verb)] you into their [lowertext(TB.name)]!</span>")
to_chat(OB.owner,"<span class='warning'>Someone inside you has eaten someone else!</span>")
TB.nom_mob(M)
OB.internal_contents -= M
else if(istype(tgt,/obj/item))
var/obj/item/T = tgt
if(!(tgt in OB.internal_contents))
if(!(tgt in OB.contents))
//Doesn't exist anymore, update.
return TRUE
intent = alert("What do you want to do to that?","Query","Examine","Use Hand")
@@ -301,27 +354,29 @@
return
selected.release_all_contents()
playsound(get_turf(user),'sound/vore/pred/escape.ogg',50,0,-5,0,ignore_walls = FALSE)
for(var/mob/M in get_hearers_in_view(5, get_turf(user)))
if(M.client && M.client.prefs.toggles & EATING_NOISES)
playsound(get_turf(user),'sound/vore/pred/escape.ogg',50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
to_chat(user.loc,"<span class='danger'>Everything is released from [user]!</span>")
if("Move all")
if(user.stat)
to_chat(user, "<span class='warning'>You can't do that in your state!</span>")
return
return FALSE
var/choice = input("Move all where?","Select Belly") in user.vore_organs + "Cancel - Don't Move"
var/obj/belly/choice = input("Move all where?","Select Belly") as null|anything in user.vore_organs
if(!choice)
return FALSE
if(choice == "Cancel - Don't Move")
return
else
var/datum/belly/B = user.vore_organs[choice]
for(var/atom/movable/tgt in selected.internal_contents)
to_chat(tgt, "<span class='warning'>You're squished from [user]'s [selected] to their [B]!</span>")
selected.transfer_contents(tgt, B, 1)
playsound(get_turf(user),'sound/vore/pred/stomachmove.ogg',50,0,-5,0,ignore_walls = FALSE)
for(var/atom/movable/tgt in selected)
selected.transfer_contents(tgt, choice, 1)
for(var/mob/M in get_hearers_in_view(5, get_turf(user)))
if(M.client && M.client.prefs.toggles & EATING_NOISES)
playsound(get_turf(user),'sound/vore/pred/stomachmove.ogg',50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
to_chat(tgt,"<span class='warning'>You're squished from [user]'s [lowertext(selected)] to their [lowertext(choice.name)]!</span>")
var/atom/movable/tgt = locate(href_list["insidepick"])
if(!(tgt in selected.internal_contents)) //Old menu, needs updating because they aren't really there.
if(!(tgt in selected)) //Old menu, needs updating because they aren't really there.
return TRUE//Forces update
intent = "Examine"
intent = alert("Examine, Eject, Move? Examine if you want to leave this box.","Query","Examine","Eject","Move")
@@ -335,48 +390,54 @@
return FALSE
selected.release_specific_contents(tgt)
playsound(get_turf(user),'sound/effects/splat.ogg',50,0,-5,0,ignore_walls = FALSE)
for(var/mob/M in get_hearers_in_view(5, get_turf(user)))
if(M.client && M.client.prefs.toggles & EATING_NOISES)
playsound(get_turf(user),'sound/vore/pred/escape.ogg',50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
user.loc << "<span class='danger'>[tgt] is released from [user]!</span>"
if("Move")
if(user.stat)
to_chat(user, "<span class='warning'>You can't do that in your state!</span>")
return FALSE
to_chat(user,"<span class='warning'>You can't do that in your state!</span>")
return 0
var/choice = input("Move [tgt] where?","Select Belly") in user.vore_organs + "Cancel - Don't Move"
var/obj/belly/choice = input("Move [tgt] where?","Select Belly") as null|anything in user.vore_organs
if(!choice || !(tgt in selected))
return 0
if(choice == "Cancel - Don't Move")
return
else
var/datum/belly/B = user.vore_organs[choice]
if (!(tgt in selected.internal_contents))
return FALSE
to_chat(tgt, "<span class='warning'>You're moved from [user]'s [lowertext(selected.name)] to their [lowertext(B.name)]!</span>")
playsound(get_turf(user),'sound/vore/pred/stomachmove.ogg',50,0,-5,0,ignore_walls = FALSE)
selected.transfer_contents(tgt, B)
to_chat(tgt,"<span class='warning'>You're squished from [user]'s [lowertext(selected.name)] to their [lowertext(choice.name)]!</span>")
selected.transfer_contents(tgt, choice)
for(var/mob/M in get_hearers_in_view(5, get_turf(user)))
if(M.client && M.client.prefs.toggles & EATING_NOISES)
playsound(get_turf(user),'sound/vore/pred/stomachmove.ogg',50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
if(href_list["newbelly"])
if(user.vore_organs.len >= BELLIES_MAX)
return TRUE
return 0
var/new_name = html_encode(input(usr,"New belly's name:","New Belly") as text|null)
var/failure_msg
if(length(new_name) > BELLIES_NAME_MAX || length(new_name) < BELLIES_NAME_MIN)
to_chat(usr, "<span class='warning'>Entered belly name is too long.</span>")
return FALSE
if(new_name in user.vore_organs)
to_chat(usr, "<span class='warning'>No duplicate belly names, please.</span>")
return FALSE
failure_msg = "Entered belly name length invalid (must be longer than [BELLIES_NAME_MIN], no more than than [BELLIES_NAME_MAX])."
// else if(whatever) //Next test here.
else
for(var/belly in user.vore_organs)
var/obj/belly/B = belly
if(lowertext(new_name) == lowertext(B.name))
failure_msg = "No duplicate belly names, please."
break
var/datum/belly/NB = new(user)
if(failure_msg) //Something went wrong.
alert(user,failure_msg,"Error!")
return 0
var/obj/belly/NB = new(user)
NB.name = new_name
NB.owner = user //might be the thing we all needed.
user.vore_organs[new_name] = NB
selected = NB
if(href_list["bellypick"])
selected = locate(href_list["bellypick"])
user.vore_selected = selected.name
user.vore_selected = selected
////
//Please keep these the same order they are on the panel UI for ease of coding
@@ -384,41 +445,40 @@
if(href_list["b_name"])
var/new_name = html_encode(input(usr,"Belly's new name:","New Name") as text|null)
var/failure_msg
if(length(new_name) > BELLIES_NAME_MAX || length(new_name) < BELLIES_NAME_MIN)
to_chat(usr, "<span class='warning'>Entered belly name length invalid (must be longer than 2, shorter than 12).</span>")
return FALSE
if(new_name in user.vore_organs)
to_chat(usr, "<span class='warning'>No duplicate belly names, please.</span>")
return FALSE
failure_msg = "Entered belly name length invalid (must be longer than [BELLIES_NAME_MIN], no more than than [BELLIES_NAME_MAX])."
// else if(whatever) //Next test here.
else
for(var/belly in user.vore_organs)
var/obj/belly/B = belly
if(lowertext(new_name) == lowertext(B.name))
failure_msg = "No duplicate belly names, please."
break
if(failure_msg) //Something went wrong.
alert(user,failure_msg,"Error!")
return 0
user.vore_organs[new_name] = selected
user.vore_organs -= selected.name
selected.name = new_name
if(href_list["b_mode"])
var/list/menu_list = selected.digest_modes
if(selected.digest_modes.len == 1) // Don't do anything
return 1
if(selected.digest_modes.len == 2) // Just toggle... there's probably a more elegant way to do this...
var/index = selected.digest_modes.Find(selected.digest_mode)
switch(index)
if(1)
selected.digest_mode = selected.digest_modes[2]
if(2)
selected.digest_mode = selected.digest_modes[1]
else
selected.digest_mode = input("Choose Mode (currently [selected.digest_mode])") in menu_list
var/new_mode = input("Choose Mode (currently [selected.digest_mode])") as null|anything in menu_list
if(!new_mode)
return 0
selected.digest_mode = new_mode
if(href_list["b_desc"])
var/new_desc = html_encode(input(usr,"Belly Description (1024 char limit):","New Description",selected.inside_flavor) as message|null)
var/new_desc = html_encode(input(usr,"Belly Description ([BELLIES_DESC_MAX] char limit):","New Description",selected.desc) as message|null)
if(new_desc)
new_desc = readd_quotes(new_desc)
if(length(new_desc) > BELLIES_DESC_MAX)
to_chat(usr, "<span class='warning'>Entered belly desc too long. [BELLIES_DESC_MAX] character limit.</span>")
alert("Entered belly desc too long. [BELLIES_DESC_MAX] character limit.","Error")
return FALSE
selected.inside_flavor = new_desc
selected.desc = new_desc
else //Returned null
return FALSE
@@ -429,12 +489,11 @@
"Struggle Message (outside)",
"Struggle Message (inside)",
"Examine Message (when full)",
"Reset All To Default",
"Cancel - No Changes"
"Reset All To Default"
)
alert(user,"Setting abusive or deceptive messages will result in a ban. Consider this your warning. Max 150 characters per message, max 10 messages per topic.","Really, don't.")
var/choice = input(user,"Select a type to modify. Messages from each topic are pulled at random when needed.","Pick Type") in messages
var/choice = input(user,"Select a type to modify. Messages from each topic are pulled at random when needed.","Pick Type") as null|anything in messages
var/help = " Press enter twice to separate messages. '%pred' will be replaced with your name. '%prey' will be replaced with the prey's name. '%belly' will be replaced with your belly's name."
switch(choice)
@@ -459,7 +518,7 @@
selected.set_messages(new_message,"smi")
if("Examine Message (when full)")
var/new_message = input(user,"These are sent to people who examine you when this belly has contents. Write them in 3rd person ('Their %belly is bulging'). "+help,"Examine Message (when full)",selected.get_messages("em")) as message
var/new_message = input(user,"These are sent to people who examine you when this belly has contents. Write them in 3rd person ('Their %belly is bulging')."+help,"Examine Message (when full)",selected.get_messages("em")) as message
if(new_message)
selected.set_messages(new_message,"em")
@@ -471,40 +530,57 @@
selected.struggle_messages_outside = initial(selected.struggle_messages_outside)
selected.struggle_messages_inside = initial(selected.struggle_messages_inside)
if("Cancel - No Changes")
return
if(href_list["b_verb"])
var/new_verb = html_encode(input(usr,"New verb when eating (infinitive tense, e.g. nom or swallow):","New Verb") as text|null)
if(length(new_verb) > BELLIES_NAME_MAX || length(new_verb) < BELLIES_NAME_MIN)
to_chat(usr, "<span class='warning'>Entered verb length invalid (must be longer than [BELLIES_NAME_MIN], no longer than [BELLIES_NAME_MAX]).</span>")
return FALSE
alert("Entered verb length invalid (must be longer than [BELLIES_NAME_MIN], no longer than [BELLIES_NAME_MAX]).","Error")
return 0
selected.vore_verb = new_verb
if(href_list["b_sound"])
var/choice = input(user,"Currently set to [selected.vore_sound]","Select Sound") in GLOB.pred_vore_sounds + "Cancel - No Changes"
if(href_list["b_release"])
var/choice = input(user,"Currently set to [selected.release_sound]","Select Sound") as null|anything in GLOB.release_sound
if(choice == "Cancel")
if(!choice)
return
selected.release_sound = GLOB.release_sound[choice]
if(href_list["b_releasesoundtest"])
var/soundfile = GLOB.release_sound[selected.release_sound]
if(soundfile)
user << soundfile
if(href_list["b_sound"])
var/choice = input(user,"Currently set to [selected.vore_sound]","Select Sound") as null|anything in GLOB.pred_vore_sounds
if(!choice)
return
selected.vore_sound = GLOB.pred_vore_sounds[choice]
if(href_list["b_soundtest"])
user << selected.vore_sound
/*
if(href_list["silenced"])
if(selected.silenced == FALSE)
selected.silenced = TRUE
to_chat(usr,"<span class='warning'>The [selected.name] is now silenced, it will not play the internal loop to prey within it.</span>")
else if(selected.silenced == TRUE)
selected.silenced = FALSE
to_chat(usr,"<span class='warning'>The [selected.name] will play the internal loop to prey within it.</span>")
*/
var/soundfile = GLOB.pred_vore_sounds[selected.vore_sound]
if(soundfile)
user << soundfile
if(href_list["b_tastes"])
selected.can_taste = !selected.can_taste
if(href_list["b_bulge_size"])
var/new_bulge = input(user, "Choose the required size prey must be to show up on examine, ranging from 25% to 200% Set this to 0 for no text on examine.", "Set Belly Examine Size.") as num|null
if(new_bulge == null)
return
if(new_bulge == 0) //Disable.
selected.bulge_size = 0
to_chat(user,"<span class='notice'>Your stomach will not be seen on examine.</span>")
else if (!IsInRange(new_bulge,25,200))
selected.bulge_size = 0.25 //Set it to the default.
to_chat(user,"<span class='notice'>Invalid size.</span>")
else if(new_bulge)
selected.bulge_size = (new_bulge/100)
if(href_list["b_escapable"])
if(selected.escapable == FALSE) //Possibly escapable and special interactions.
selected.escapable = TRUE
@@ -515,7 +591,7 @@
show_interacts = FALSE //Force the hiding of the panel
else
to_chat(usr,"<span class='warning'>Something went wrong. Your stomach will now not have special interactions. Press the button enable them again and tell a dev.") //If they somehow have a varable that's not 0 or 1
selected.escapable = FALSE
selected.escapable = TRUE
show_interacts = FALSE //Force the hiding of the panel
if(href_list["b_escapechance"])
@@ -537,68 +613,73 @@
var/choice = input("Where do you want your [selected.name] to lead if prey resists?","Select Belly") as null|anything in (user.vore_organs + "None - Remove" - selected.name)
if(!choice) //They cancelled, no changes
return
return FALSE
else if(choice == "None - Remove")
selected.transferlocation = null
else
selected.transferlocation = user.vore_organs[choice]
if(href_list["b_absorbchance"])
var/absorb_chance_input = input(user, "Set belly absorb mode chance on resist (as %)", "Prey Absorb Chance") as num|null
if(!isnull(absorb_chance_input))
selected.absorbchance = sanitize_integer(absorb_chance_input, 0, 100, initial(selected.absorbchance))
if(href_list["b_digestchance"])
var/digest_chance_input = input(user, "Set belly digest mode chance on resist (as %)", "Prey Digest Chance") as num|null
if(!isnull(digest_chance_input))
selected.digestchance = sanitize_integer(digest_chance_input, 0, 100, initial(selected.digestchance))
if(href_list["b_silent"])
selected.silent = !selected.silent
if(href_list["b_del"])
var/dest_for = FALSE //Check to see if it's the destination of another vore organ.
for(var/I in user.vore_organs)
var/datum/belly/B = user.vore_organs[I]
var/alert = alert("Are you sure you want to delete your [lowertext(selected.name)]?","Confirmation","Delete","Cancel")
if(!alert == "Delete")
return FALSE
var/failure_msg = ""
var/dest_for //Check to see if it's the destination of another vore organ.
for(var/belly in user.vore_organs)
var/obj/belly/B = belly
if(B.transferlocation == selected)
dest_for = B.name
failure_msg += "This is the destiantion for at least '[dest_for]' belly transfers. Remove it as the destination from any bellies before deleting it. "
break
if(dest_for)
alert("This is the destiantion for at least '[dest_for]' belly transfers. Remove it as the destination from any bellies before deleting it.","Error")
return TRUE
else if(selected.internal_contents.len)
alert("Can't delete bellies with contents!","Error")
return TRUE
if(selected.internal_contents.len)
to_chat(usr, "<span class='warning'>Can't delete bellies with contents!</span>")
return
else if(selected.immutable)
to_chat(usr, "<span class='warning'>This belly is marked as undeletable.</span>")
return
else if(user.vore_organs.len == 1)
to_chat(usr, "<span class='warning'>You must have at least one belly.</span>")
return
else
var/alert = alert("Are you sure you want to delete [selected]?","Confirmation","Delete","Cancel")
if(alert == "Delete" && !selected.internal_contents.len)
user.vore_organs -= selected.name
user.vore_organs.Remove(selected)
selected = user.vore_organs[1]
user.vore_selected = user.vore_organs[1]
to_chat(usr,"<span class='warning'>Note: If you had this organ selected as a transfer location, please remove the transfer location by selecting Cancel - None - Remove on this stomach.</span>")
if(selected.contents.len)
failure_msg += "You cannot delete bellies with contents! " //These end with spaces, to be nice looking. Make sure you do the same.
if(selected.immutable)
failure_msg += "This belly is marked as undeletable. "
if(user.vore_organs.len == 1)
failure_msg += "You must have at least one belly. "
if(failure_msg)
alert(user,failure_msg,"Error!")
return FALSE
qdel(selected)
selected = user.vore_organs[1]
user.vore_selected = user.vore_organs[1]
if(href_list["saveprefs"])
if(user.save_vore_prefs())
to_chat(user, "<span class='notice'>Belly Preferences saved!</span>")
if(!user.save_vore_prefs())
to_chat(user, "<span class='warning'>Belly Preferences not saved!</span>")
else
to_chat(user, "<span class='warning'>ERROR: Belly Preferences were not saved!</span>")
to_chat(user, "<span class='notice'>Belly Preferences were saved!</span>")
log_admin("Could not save vore prefs on USER: [user].")
if(href_list["setflavor"])
var/new_flavor = html_encode(input(usr,"What your character tastes like (40ch limit). This text will be printed to the pred after 'X tastes of...' so just put something like 'strawberries and cream':","Character Flavor",user.vore_taste) as text|null)
if(!new_flavor)
return 0
if(new_flavor)
new_flavor = readd_quotes(new_flavor)
if(length(new_flavor) > FLAVOR_MAX)
alert("Entered flavor/taste text too long. [FLAVOR_MAX] character limit.","Error")
return FALSE
user.vore_taste = new_flavor
else //Returned null
return FALSE
new_flavor = readd_quotes(new_flavor)
if(length(new_flavor) > FLAVOR_MAX)
alert("Entered flavor/taste text too long. [FLAVOR_MAX] character limit.","Error!")
return 0
user.vore_taste = new_flavor
if(href_list["toggledg"])
var/choice = alert(user, "This button is for those who don't like being digested. It can make you undigestable to all mobs. Digesting you is currently: [user.digestable ? "Allowed" : "Prevented"]", "", "Allow Digestion", "Cancel", "Prevent Digestion")
@@ -626,5 +707,15 @@
if(user.client.prefs_vr)
user.client.prefs_vr.devourable = user.devourable
if(href_list["togglenoisy"])
var/choice = alert(user, "Toggle audible hunger noises. Currently: [user.noisy ? "Enabled" : "Disabled"]", "", "Enable audible hunger", "Cancel", "Disable audible hunger")
switch(choice)
if("Cancel")
return 0
if("Enable audible hunger")
user.noisy = TRUE
if("Disable audible hunger")
user.noisy = FALSE
//Refresh when interacted with, returning 1 makes vore_look.Topic update
return TRUE
return 1

View File

@@ -0,0 +1,90 @@
/*
* Returns a byond list that can be passed to the "deserialize" proc
* to bring a new instance of this atom to its original state
*
* If we want to store this info, we can pass it to `json_encode` or some other
* interface that suits our fancy, to make it into an easily-handled string
*/
/datum/proc/serialize()
var/data = list("type" = "[type]")
return data
/*
* This is given the byond list from above, to bring this atom to the state
* described in the list.
* This will be called after `New` but before `initialize`, so linking and stuff
* would probably be handled in `initialize`
*
* Also, this should only be called by `list_to_object` in persistence.dm - at least
* with current plans - that way it can actually initialize the type from the list
*/
/datum/proc/deserialize(var/list/data)
return
/atom
// This var isn't actually used for anything, but is present so that
// DM's map reader doesn't forfeit on reading a JSON-serialized map
var/map_json_data
// This is so specific atoms can override these, and ignore certain ones
/atom/proc/vars_to_save()
return list("color","dir","icon","icon_state","name","pixel_x","pixel_y")
/atom/proc/map_important_vars()
// A list of important things to save in the map editor
return list("color","dir","icon","icon_state","layer","name","pixel_x","pixel_y")
/area/map_important_vars()
// Keep the area default icons, to keep things nice and legible
return list("name")
// No need to save any state of an area by default
/area/vars_to_save()
return list("name")
/atom/serialize()
var/list/data = ..()
for(var/thing in vars_to_save())
if(vars[thing] != initial(vars[thing]))
data[thing] = vars[thing]
return data
/atom/deserialize(var/list/data)
for(var/thing in vars_to_save())
if(thing in data)
vars[thing] = data[thing]
..()
/*
Whoops, forgot to put documentation here.
What this does, is take a JSON string produced by running
BYOND's native `json_encode` on a list from `serialize` above, and
turns that string into a new instance of that object.
You can also easily get an instance of this string by calling "Serialize Marked Datum"
in the "Debug" tab.
If you're clever, you can do neat things with SDQL and this, though be careful -
some objects, like humans, are dependent that certain extra things are defined
in their list
*/
/proc/object_to_json(var/atom/movable/thing)
return json_encode(thing.serialize())
/proc/json_to_object(var/json_data, var/loc)
return list_to_object(json_decode(json_data), loc)
/proc/list_to_object(var/list/data, var/loc)
if(!islist(data))
throw EXCEPTION("You didn't give me a list, bucko")
if(!("type" in data))
throw EXCEPTION("No 'type' field in the data")
var/path = text2path(data["type"])
if(!path)
throw EXCEPTION("Path not found: [path]")
var/atom/movable/thing = new path(loc)
thing.deserialize(data)
return thing

View File

@@ -0,0 +1,22 @@
TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggleeatingnoise)()
set name = "Toggle Eating Noises"
set category = "Preferences"
set desc = "Hear Eating noises"
usr.client.prefs.toggles ^= EATING_NOISES
usr.client.prefs.save_preferences()
usr.stop_sound_channel(CHANNEL_PRED)
to_chat(usr, "You will [(usr.client.prefs.toggles & EATING_NOISES) ? "now" : "no longer"] hear eating noises.")
/datum/verbs/menu/Settings/Sound/toggleeatingnoise/Get_checked(client/C)
return !(C.prefs.toggles & EATING_NOISES)
TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggledigestionnoise)()
set name = "Toggle Digestion Noises"
set category = "Preferences"
set desc = "Hear digestive noises"
usr.client.prefs.toggles ^= DIGESTION_NOISES
usr.client.prefs.save_preferences()
usr.stop_sound_channel(CHANNEL_DIGEST)
to_chat(usr, "You will [(usr.client.prefs.toggles & DIGESTION_NOISES) ? "now" : "no longer"] hear digestion noises.")
/datum/verbs/menu/Settings/Sound/toggledigestionnoise/Get_checked(client/C)
return !(C.prefs.toggles & DIGESTION_NOISES)

View File

@@ -284,6 +284,7 @@
#include "code\controllers\subsystem\timer.dm"
#include "code\controllers\subsystem\title.dm"
#include "code\controllers\subsystem\traumas.dm"
#include "code\controllers\subsystem\vore.dm"
#include "code\controllers\subsystem\vote.dm"
#include "code\controllers\subsystem\weather.dm"
#include "code\controllers\subsystem\processing\circuit.dm"
@@ -2587,9 +2588,12 @@
#include "code\modules\vehicles\vehicle_actions.dm"
#include "code\modules\vehicles\vehicle_key.dm"
#include "code\modules\vore\hook-defs_vr.dm"
#include "code\modules\vore\persistence.dm"
#include "code\modules\vore\trycatch_vr.dm"
#include "code\modules\vore\eating\belly_vr.dm"
#include "code\modules\vore\eating\belly_dat_vr.dm"
#include "code\modules\vore\eating\belly_obj_vr.dm"
#include "code\modules\vore\eating\bellymodes_vr.dm"
#include "code\modules\vore\eating\digest_act_vr.dm"
#include "code\modules\vore\eating\living_vr.dm"
#include "code\modules\vore\eating\simple_animal_vr.dm"
#include "code\modules\vore\eating\vore_vr.dm"
@@ -2659,6 +2663,7 @@
#include "modular_citadel\code\modules\client\client_procs.dm"
#include "modular_citadel\code\modules\client\preferences.dm"
#include "modular_citadel\code\modules\client\preferences_savefile.dm"
#include "modular_citadel\code\modules\client\preferences_toggles.dm"
#include "modular_citadel\code\modules\client\loadout\__donator.dm"
#include "modular_citadel\code\modules\client\loadout\_medical.dm"
#include "modular_citadel\code\modules\client\loadout\_security.dm"