Merge pull request #9504 from Poojawa/vore-UI-improvements
Vore fixes and optimizations
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
status_flags = GODMODE|CANPUSH
|
||||
mouse_drag_pointer = MOUSE_INACTIVE_POINTER
|
||||
var/in_use = FALSE
|
||||
no_vore = TRUE
|
||||
|
||||
INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
autotransferwait = 200
|
||||
|
||||
/obj/belly/megafauna/dragon/gut
|
||||
name = "stomach"
|
||||
name = "gut"
|
||||
vore_capacity = 5 //I doubt this many people will actually last in the gut, but...
|
||||
vore_sound = "Tauric Swallow"
|
||||
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."
|
||||
|
||||
@@ -29,21 +29,22 @@
|
||||
/mob/living/simple_animal/Destroy()
|
||||
release_vore_contents(include_absorbed = TRUE, silent = TRUE)
|
||||
prey_excludes.Cut()
|
||||
QDEL_NULL_LIST(vore_organs)
|
||||
. = ..()
|
||||
|
||||
// Update fullness based on size & quantity of belly contents
|
||||
/mob/living/simple_animal/proc/update_fullness(var/atom/movable/M)
|
||||
var/new_fullness = 0
|
||||
for(var/I in vore_organs)
|
||||
var/datum/belly/B = vore_organs[I]
|
||||
if (!(M in B.internal_contents))
|
||||
for(var/belly in vore_organs)
|
||||
var/obj/belly/B = vore_organs[belly]
|
||||
if (!(M in B.contents))
|
||||
return FALSE // Nothing's inside
|
||||
new_fullness += M
|
||||
|
||||
vore_fullness = new_fullness
|
||||
|
||||
/mob/living/simple_animal/death()
|
||||
release_vore_contents(silent = TRUE)
|
||||
release_vore_contents()
|
||||
. = ..()
|
||||
|
||||
// Simple animals have only one belly. This creates it (if it isn't already set up)
|
||||
@@ -51,16 +52,18 @@
|
||||
vore_init = TRUE
|
||||
if(CHECK_BITFIELD(flags_1, HOLOGRAM_1))
|
||||
return
|
||||
if(vore_organs.len)
|
||||
return
|
||||
if(no_vore) //If it can't vore, let's not give it a stomach.
|
||||
if(!vore_active || no_vore) //If it can't vore, let's not give it a stomach.
|
||||
return
|
||||
if(vore_active && !IsAdvancedToolUser()) //vore active, but doesn't have thumbs to grab people with.
|
||||
verbs |= /mob/living/simple_animal/proc/animal_nom
|
||||
|
||||
var/obj/belly/B = new /obj/belly(src)
|
||||
if(LAZYLEN(vore_organs))
|
||||
return
|
||||
|
||||
LAZYINITLIST(vore_organs)
|
||||
var/obj/belly/B = new (src)
|
||||
vore_selected = B
|
||||
B.immutable = 1
|
||||
B.immutable = TRUE
|
||||
B.name = vore_stomach_name ? vore_stomach_name : "stomach"
|
||||
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
|
||||
@@ -125,13 +128,12 @@
|
||||
// Simple nom proc for if you get ckey'd into a simple_animal mob! Avoids grabs.
|
||||
//
|
||||
/mob/living/simple_animal/proc/animal_nom(var/mob/living/T in oview(1))
|
||||
set name = "Animal Nom"
|
||||
set name = "Animal Nom (pull target)"
|
||||
set category = "Vore"
|
||||
set desc = "Since you can't grab, you get a verb!"
|
||||
|
||||
if (stat != CONSCIOUS)
|
||||
return
|
||||
if (T.devourable == FALSE)
|
||||
to_chat(usr, "<span class='warning'>You can't eat this!</span>")
|
||||
if(!T.devourable)
|
||||
return
|
||||
return vore_attack(usr,T,usr)
|
||||
return vore_attack(src,T,src)
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
//CARBON MOBS
|
||||
/mob/living/carbon/alien
|
||||
devourable = TRUE
|
||||
digestable = TRUE
|
||||
feeding = TRUE
|
||||
|
||||
/mob/living/carbon/monkey
|
||||
devourable = TRUE
|
||||
digestable = TRUE
|
||||
feeding = TRUE
|
||||
|
||||
|
||||
/*
|
||||
REFER TO code/modules/mob/living/simple_animal/simple_animal_vr.dm for Var information!
|
||||
*/
|
||||
|
||||
|
||||
//NUETRAL MOBS
|
||||
/mob/living/simple_animal/crab
|
||||
devourable = TRUE
|
||||
digestable = TRUE
|
||||
feeding = TRUE
|
||||
|
||||
/mob/living/simple_animal/cow
|
||||
devourable = TRUE
|
||||
digestable = TRUE
|
||||
feeding = TRUE
|
||||
vore_active = TRUE
|
||||
isPredator = TRUE
|
||||
vore_default_mode = DM_HOLD
|
||||
|
||||
/mob/living/simple_animal/chick
|
||||
devourable = TRUE
|
||||
digestable = TRUE
|
||||
|
||||
/mob/living/simple_animal/chicken
|
||||
devourable = TRUE
|
||||
digestable = TRUE
|
||||
|
||||
/mob/living/simple_animal/mouse
|
||||
devourable = TRUE
|
||||
digestable = TRUE
|
||||
|
||||
/mob/living/simple_animal/kiwi
|
||||
devourable = TRUE
|
||||
digestable = TRUE
|
||||
|
||||
//STATION PETS
|
||||
/mob/living/simple_animal/pet
|
||||
devourable = TRUE
|
||||
digestable = TRUE
|
||||
feeding = TRUE
|
||||
|
||||
/mob/living/simple_animal/pet/fox
|
||||
vore_active = TRUE
|
||||
isPredator = TRUE
|
||||
vore_default_mode = DM_HOLD
|
||||
|
||||
/mob/living/simple_animal/pet/cat
|
||||
vore_active = TRUE
|
||||
isPredator = TRUE
|
||||
vore_default_mode = DM_HOLD
|
||||
|
||||
/mob/living/simple_animal/pet/dog
|
||||
vore_active = TRUE
|
||||
isPredator = TRUE
|
||||
vore_default_mode = DM_HOLD
|
||||
|
||||
/mob/living/simple_animal/sloth
|
||||
devourable = TRUE
|
||||
digestable = TRUE
|
||||
|
||||
/mob/living/simple_animal/parrot
|
||||
devourable = TRUE
|
||||
digestable = TRUE
|
||||
|
||||
//HOSTILE MOBS
|
||||
/mob/living/simple_animal/hostile/retaliate/goat
|
||||
devourable = TRUE
|
||||
digestable = TRUE
|
||||
feeding = TRUE
|
||||
vore_active = TRUE
|
||||
isPredator = TRUE
|
||||
vore_stomach_flavor = "You find yourself squeezed into the hollow of the goat, the smell of oats and hay thick in the tight space, all of which grinds in on you. Harmless for now..."
|
||||
vore_default_mode = DM_HOLD
|
||||
|
||||
/mob/living/simple_animal/hostile/lizard
|
||||
devourable = TRUE
|
||||
digestable = TRUE
|
||||
feeding = TRUE
|
||||
vore_active = TRUE
|
||||
isPredator = TRUE
|
||||
vore_default_mode = DM_DIGEST
|
||||
|
||||
/mob/living/simple_animal/hostile/alien
|
||||
feeding = TRUE
|
||||
vore_active = TRUE
|
||||
isPredator = TRUE
|
||||
vore_default_mode = DM_DIGEST
|
||||
|
||||
/mob/living/simple_animal/hostile/bear
|
||||
feeding = TRUE
|
||||
vore_active = TRUE
|
||||
isPredator = TRUE
|
||||
vore_default_mode = DM_DIGEST
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider
|
||||
feeding = TRUE
|
||||
vore_active = TRUE
|
||||
isPredator = TRUE
|
||||
vore_default_mode = DM_DIGEST
|
||||
|
||||
/mob/living/simple_animal/hostile/retaliate/poison/snake
|
||||
feeding = TRUE
|
||||
vore_active = TRUE
|
||||
isPredator = TRUE
|
||||
vore_default_mode = DM_DIGEST
|
||||
|
||||
/mob/living/simple_animal/hostile/gorilla
|
||||
feeding = TRUE
|
||||
vore_active = TRUE
|
||||
isPredator = TRUE
|
||||
vore_default_mode = DM_DIGEST
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath
|
||||
feeding = TRUE //for pet Goliaths I guess or something.
|
||||
vore_active = TRUE
|
||||
isPredator = TRUE
|
||||
vore_default_mode = DM_DIGEST
|
||||
|
||||
/mob/living/simple_animal/hostile/carp
|
||||
devourable = TRUE
|
||||
digestable = TRUE
|
||||
feeding = TRUE
|
||||
vore_active = TRUE
|
||||
isPredator = TRUE
|
||||
vore_default_mode = DM_DIGEST
|
||||
@@ -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! //
|
||||
// // // // // // // // // // // //
|
||||
@@ -0,0 +1,733 @@
|
||||
//#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 = "Gulp" // Sound when ingesting someone
|
||||
var/vore_verb = "ingest" // Verb for eating with this in messages
|
||||
var/release_sound = "Splatter" // Sound for letting someone out.
|
||||
var/human_prey_swallow_time = 100 // Time in deciseconds to swallow /mob/living/carbon/human
|
||||
var/nonhuman_prey_swallow_time = 30 // Time in deciseconds to swallow anything else
|
||||
var/emote_time = 60 SECONDS // How long between stomach emotes at prey
|
||||
var/digest_brute = 2 // Brute damage per tick in digestion mode
|
||||
var/digest_burn = 2 // Burn damage per tick in digestion mode
|
||||
var/immutable = FALSE // Prevents this belly from being deleted
|
||||
var/escapable = TRUE // Belly can be resisted out of at any time
|
||||
var/escapetime = 20 SECONDS // Deciseconds, how long to escape this belly
|
||||
var/digestchance = 0 // % Chance of stomach beginning to digest if prey struggles
|
||||
var/absorbchance = 0 // % Chance of stomach beginning to absorb if prey struggles
|
||||
var/escapechance = 0 // % Chance of prey beginning to escape if prey struggles.
|
||||
var/can_taste = FALSE // If this belly prints the flavor of prey when it eats someone.
|
||||
var/bulge_size = 0.25 // The minimum size the prey has to be in order to show up on examine.
|
||||
// var/shrink_grow_size = 1 // This horribly named variable determines the minimum/maximum size it will shrink/grow prey to.
|
||||
|
||||
var/transferlocation // Location that the prey is released if they struggle and get dropped off.
|
||||
var/transferchance = 0 // % Chance of prey being transferred to transfer location when resisting
|
||||
var/autotransferchance = 0 // % Chance of prey being autotransferred to transfer location
|
||||
var/autotransferwait = 10 // Time between trying to transfer.
|
||||
var/swallow_time = 10 SECONDS // for mob transfering automation
|
||||
var/vore_capacity = 1 // simple animal nom capacity
|
||||
var/is_wet = TRUE // Is this belly inside slimy parts?
|
||||
var/wet_loop = TRUE // Does this belly have a slimy internal loop?
|
||||
|
||||
//I don't think we've ever altered these lists. making them static until someone actually overrides them somewhere.
|
||||
var/tmp/static/list/digest_modes = list(DM_HOLD,DM_DIGEST,DM_HEAL,DM_NOISY,DM_ABSORB,DM_UNABSORB) // 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 // Prevent audio spam
|
||||
var/tmp/last_hearcheck = 0
|
||||
var/tmp/list/hearing_mobs
|
||||
|
||||
// Don't forget to watch your commas at the end of each line if you change these.
|
||||
var/list/struggle_messages_outside = list(
|
||||
"%pred's %belly wobbles with a squirming meal.",
|
||||
"%pred's %belly jostles with movement.",
|
||||
"%pred's %belly briefly swells outward as someone pushes from inside.",
|
||||
"%pred's %belly fidgets with a trapped victim.",
|
||||
"%pred's %belly jiggles with motion from inside.",
|
||||
"%pred's %belly sloshes around.",
|
||||
"%pred's %belly gushes softly.",
|
||||
"%pred's %belly lets out a wet squelch.")
|
||||
|
||||
var/list/struggle_messages_inside = list(
|
||||
"Your useless squirming only causes %pred's slimy %belly to squelch over your body.",
|
||||
"Your struggles only cause %pred's %belly to gush softly around you.",
|
||||
"Your movement only causes %pred's %belly to slosh around you.",
|
||||
"Your motion causes %pred's %belly to jiggle.",
|
||||
"You fidget around inside of %pred's %belly.",
|
||||
"You shove against the walls of %pred's %belly, making it briefly swell outward.",
|
||||
"You jostle %pred's %belly with movement.",
|
||||
"You squirm inside of %pred's %belly, making it wobble around.")
|
||||
|
||||
var/list/digest_messages_owner = list(
|
||||
"You feel %prey's body succumb to your digestive system, which breaks it apart into soft slurry.",
|
||||
"You hear a lewd glorp as your %belly muscles grind %prey into a warm pulp.",
|
||||
"Your %belly lets out a rumble as it melts %prey into sludge.",
|
||||
"You feel a soft gurgle as %prey's body loses form in your %belly. They're nothing but a soft mass of churning slop now.",
|
||||
"Your %belly begins gushing %prey's remains through your system, adding some extra weight to your thighs.",
|
||||
"Your %belly begins gushing %prey's remains through your system, adding some extra weight to your rump.",
|
||||
"Your %belly begins gushing %prey's remains through your system, adding some extra weight to your belly.",
|
||||
"Your %belly groans as %prey falls apart into a thick soup. You can feel their remains soon flowing deeper into your body to be absorbed.",
|
||||
"Your %belly kneads on every fiber of %prey, softening them down into mush to fuel your next hunt.",
|
||||
"Your %belly churns %prey down into a hot slush. You can feel the nutrients coursing through your digestive track with a series of long, wet glorps.")
|
||||
|
||||
var/list/digest_messages_prey = list(
|
||||
"Your body succumbs to %pred's digestive system, which breaks you apart into soft slurry.",
|
||||
"%pred's %belly lets out a lewd glorp as their muscles grind you into a warm pulp.",
|
||||
"%pred's %belly lets out a rumble as it melts you into sludge.",
|
||||
"%pred feels a soft gurgle as your body loses form in their %belly. You're nothing but a soft mass of churning slop now.",
|
||||
"%pred's %belly begins gushing your remains through their system, adding some extra weight to %pred's thighs.",
|
||||
"%pred's %belly begins gushing your remains through their system, adding some extra weight to %pred's rump.",
|
||||
"%pred's %belly begins gushing your remains through their system, adding some extra weight to %pred's belly.",
|
||||
"%pred's %belly groans as you fall apart into a thick soup. Your remains soon flow deeper into %pred's body to be absorbed.",
|
||||
"%pred's %belly kneads on every fiber of your body, softening you down into mush to fuel their next hunt.",
|
||||
"%pred's %belly churns you down into a hot slush. Your nutrient-rich remains course through their digestive track with a series of long, wet glorps.")
|
||||
|
||||
var/list/examine_messages = list(
|
||||
"They have something solid in their %belly!",
|
||||
"It looks like they have something in their %belly!")
|
||||
|
||||
//Mostly for being overridden on precreated bellies on mobs. Could be VV'd into
|
||||
//a carbon's belly if someone really wanted. No UI for carbons to adjust this.
|
||||
//List has indexes that are the digestion mode strings, and keys that are lists of strings.
|
||||
var/tmp/list/emote_lists = list()
|
||||
|
||||
//For serialization, keep this updated AND IN ORDER OF VARS LISTED ABOVE AND IN DUPE AT THE BOTTOM!!, required for bellies to save correctly.
|
||||
/obj/belly/vars_to_save()
|
||||
return ..() + list(
|
||||
"name",
|
||||
"desc",
|
||||
"vore_sound",
|
||||
"vore_verb",
|
||||
"release_sound",
|
||||
"human_prey_swallow_time",
|
||||
"nonhuman_prey_swallow_time",
|
||||
"emote_time",
|
||||
"digest_brute",
|
||||
"digest_burn",
|
||||
"immutable",
|
||||
"escapable",
|
||||
"escapetime",
|
||||
"digestchance",
|
||||
"absorbchance",
|
||||
"escapechance",
|
||||
"can_taste",
|
||||
"bulge_size",
|
||||
"transferlocation",
|
||||
"transferchance",
|
||||
"autotransferchance",
|
||||
"autotransferwait",
|
||||
"swallow_time",
|
||||
"vore_capacity",
|
||||
"struggle_messages_outside",
|
||||
"struggle_messages_inside",
|
||||
"digest_messages_owner",
|
||||
"digest_messages_prey",
|
||||
"examine_messages",
|
||||
"emote_lists",
|
||||
"is_wet",
|
||||
"wet_loop"
|
||||
)
|
||||
|
||||
//ommitted list
|
||||
// "shrink_grow_size",
|
||||
/obj/belly/Initialize()
|
||||
. = ..()
|
||||
take_ownership(src.loc)
|
||||
|
||||
/obj/belly/proc/take_ownership(var/newloc)
|
||||
//If not, we're probably just in a prefs list or something.
|
||||
if(isliving(newloc))
|
||||
owner = loc
|
||||
owner.vore_organs |= src
|
||||
SSbellies.belly_list += src
|
||||
|
||||
/obj/belly/Destroy()
|
||||
SSbellies.belly_list -= src
|
||||
if(owner)
|
||||
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(vore_sound && !recent_sound)
|
||||
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/living/H in get_hearers_in_view(3, owner))
|
||||
if(!H.client || !(H.client.prefs.cit_toggles & EATING_NOISES))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, H)
|
||||
last_hearcheck = world.time
|
||||
for(var/mob/living/H in hearing_mobs)
|
||||
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
|
||||
var/sound/eating = GLOB.pred_vore_sounds[vore_sound]
|
||||
SEND_SOUND(H,eating)
|
||||
else if(H && H in contents && H.client)
|
||||
var/sound/eating = GLOB.prey_vore_sounds[vore_sound]
|
||||
SEND_SOUND(H,eating)
|
||||
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>")
|
||||
|
||||
// Release all contents of this belly into the owning mob's location.
|
||||
// If that location is another mob, contents are transferred into whichever of its bellies the owning mob is in.
|
||||
// Returns the number of mobs so released.
|
||||
/obj/belly/proc/release_all_contents(var/include_absorbed = FALSE, var/silent = FALSE)
|
||||
// var/atom/destination = drop_location()
|
||||
//Don't bother if we don't have contents
|
||||
if(!contents.len)
|
||||
return FALSE
|
||||
|
||||
var/count = 0
|
||||
for(var/thing in contents)
|
||||
var/atom/movable/AM = thing
|
||||
if(isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
var/mob/living/OW = owner
|
||||
if(L.absorbed && !include_absorbed)
|
||||
continue
|
||||
L.absorbed = FALSE
|
||||
L.stop_sound_channel(CHANNEL_PREYLOOP)
|
||||
L.cure_blind("belly_[REF(src)]")
|
||||
SEND_SIGNAL(OW, COMSIG_CLEAR_MOOD_EVENT, "fedpred", /datum/mood_event/fedpred)
|
||||
SEND_SIGNAL(L, COMSIG_CLEAR_MOOD_EVENT, "fedprey", /datum/mood_event/fedprey)
|
||||
SEND_SIGNAL(OW, COMSIG_ADD_MOOD_EVENT, "emptypred", /datum/mood_event/emptypred)
|
||||
SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "emptyprey", /datum/mood_event/emptyprey)
|
||||
count += release_specific_contents(AM, silent = TRUE)
|
||||
|
||||
//Clean up our own business
|
||||
items_preserved.Cut()
|
||||
owner.update_icons()
|
||||
|
||||
if(!silent)
|
||||
if(release_sound && !recent_sound)
|
||||
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/living/H in get_hearers_in_view(3, owner))
|
||||
if(!H.client || !(H.client.prefs.cit_toggles & EATING_NOISES))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, H)
|
||||
last_hearcheck = world.time
|
||||
for(var/mob/living/H in hearing_mobs)
|
||||
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
|
||||
var/sound/releasement = GLOB.pred_release_sounds[release_sound]
|
||||
SEND_SOUND(H,releasement)
|
||||
else if(H && H in contents && H.client)
|
||||
var/sound/releasement = GLOB.prey_release_sounds[release_sound]
|
||||
SEND_SOUND(H,releasement)
|
||||
recent_sound = TRUE
|
||||
owner.visible_message("<font color='green'><b>[owner] expels everything from their [lowertext(name)]!</b></font>")
|
||||
|
||||
return count
|
||||
|
||||
// Release a specific atom from the contents of this belly into the owning mob's location.
|
||||
// If that location is another mob, the atom is transferred into whichever of its bellies the owning mob is in.
|
||||
// Returns the number of atoms so released.
|
||||
/obj/belly/proc/release_specific_contents(var/atom/movable/M, var/silent = FALSE)
|
||||
if (!(M in contents))
|
||||
return FALSE // They weren't in this belly anyway
|
||||
|
||||
M.forceMove(drop_location()) // Move the belly contents into the same location as belly's owner.
|
||||
items_preserved -= M
|
||||
|
||||
|
||||
if(istype(M,/mob/living))
|
||||
var/mob/living/ML = M
|
||||
var/mob/living/OW = owner
|
||||
if(ML.client)
|
||||
ML.stop_sound_channel(CHANNEL_PREYLOOP) //Stop the internal loop, it'll restart if the isbelly check on next tick anyway
|
||||
ML.cure_blind("belly_[REF(src)]")
|
||||
SEND_SIGNAL(OW, COMSIG_CLEAR_MOOD_EVENT, "fedpred", /datum/mood_event/fedpred)
|
||||
SEND_SIGNAL(ML, COMSIG_CLEAR_MOOD_EVENT, "fedprey", /datum/mood_event/fedprey)
|
||||
SEND_SIGNAL(OW, COMSIG_ADD_MOOD_EVENT, "emptypred", /datum/mood_event/emptypred)
|
||||
SEND_SIGNAL(ML, COMSIG_ADD_MOOD_EVENT, "emptyprey", /datum/mood_event/emptyprey)
|
||||
|
||||
if(ML.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)
|
||||
|
||||
//Clean up our own business
|
||||
owner.update_icons()
|
||||
|
||||
if(!silent)
|
||||
if(release_sound && !recent_sound)
|
||||
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/living/H in get_hearers_in_view(3, owner))
|
||||
if(!H.client || !(H.client.prefs.cit_toggles & EATING_NOISES))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, H)
|
||||
last_hearcheck = world.time
|
||||
for(var/mob/living/H in hearing_mobs)
|
||||
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
|
||||
var/sound/releasement = GLOB.pred_release_sounds[release_sound]
|
||||
SEND_SOUND(H,releasement)
|
||||
else if(H && H in contents && H.client)
|
||||
var/sound/releasement = GLOB.prey_release_sounds[release_sound]
|
||||
SEND_SOUND(H,releasement)
|
||||
recent_sound = TRUE
|
||||
owner.visible_message("<font color='green'><b>[owner] expels [M] from their [lowertext(name)]!</b></font>")
|
||||
|
||||
return TRUE
|
||||
|
||||
// Actually perform the mechanics of devouring the tasty prey.
|
||||
// The purpose of this method is to avoid duplicate code, and ensure that all necessary
|
||||
// steps are taken.
|
||||
/obj/belly/proc/nom_mob(var/mob/prey, var/mob/user)
|
||||
if(owner.stat == DEAD)
|
||||
return
|
||||
if (prey.buckled)
|
||||
prey.buckled.unbuckle_mob(prey,TRUE)
|
||||
|
||||
if(!isbelly(prey.loc))
|
||||
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "fedpred", /datum/mood_event/fedpred)
|
||||
SEND_SIGNAL(prey, COMSIG_ADD_MOOD_EVENT, "fedprey", /datum/mood_event/fedprey)
|
||||
else
|
||||
SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "emptypred", /datum/mood_event/emptypred)
|
||||
SEND_SIGNAL(prey, COMSIG_CLEAR_MOOD_EVENT, "emptyprey", /datum/mood_event/emptyprey)
|
||||
|
||||
prey.forceMove(src)
|
||||
|
||||
owner.updateVRPanel()
|
||||
|
||||
for(var/mob/living/M in contents)
|
||||
M.updateVRPanel()
|
||||
M.become_blind("belly_[REF(src)]")
|
||||
|
||||
// Setup the autotransfer checks if needed
|
||||
if(transferlocation != null && autotransferchance > 0)
|
||||
addtimer(CALLBACK(src, /obj/belly/.proc/check_autotransfer, prey), autotransferwait)
|
||||
|
||||
/obj/belly/proc/check_autotransfer(var/mob/prey, var/obj/belly/target)
|
||||
// Some sanity checks
|
||||
if(transferlocation && (autotransferchance > 0) && (prey in contents))
|
||||
if(prob(autotransferchance))
|
||||
transfer_contents(prey, transferlocation)
|
||||
else
|
||||
// Didn't transfer, so wait before retrying
|
||||
addtimer(CALLBACK(src, /obj/belly/.proc/check_autotransfer, prey), autotransferwait)
|
||||
|
||||
//Transfers contents from one belly to another
|
||||
/obj/belly/proc/transfer_contents(var/atom/movable/content, var/obj/belly/target, silent = FALSE)
|
||||
if(!(content in src) || !istype(target))
|
||||
return
|
||||
content.forceMove(target)
|
||||
for(var/mob/living/M in contents)
|
||||
M.cure_blind("belly_[REF(src)]")
|
||||
// target.nom_mob(content, target.owner)
|
||||
if(vore_sound && !recent_sound && !silent)
|
||||
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/living/H in get_hearers_in_view(3, owner))
|
||||
if(!H.client || !(H.client.prefs.cit_toggles & EATING_NOISES))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, H)
|
||||
last_hearcheck = world.time
|
||||
for(var/mob/living/H in hearing_mobs)
|
||||
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
|
||||
var/sound/eating = GLOB.pred_vore_sounds[vore_sound]
|
||||
SEND_SOUND(H,eating)
|
||||
else if(H && H in contents && H.client)
|
||||
var/sound/eating = GLOB.prey_vore_sounds[vore_sound]
|
||||
SEND_SOUND(H,eating)
|
||||
recent_sound = TRUE
|
||||
|
||||
owner.updateVRPanel()
|
||||
for(var/mob/living/M in contents)
|
||||
M.updateVRPanel()
|
||||
|
||||
// Get the line that should show up in Examine message if the owner of this belly
|
||||
// is examined. By making this a proc, we not only take advantage of polymorphism,
|
||||
// but can easily make the message vary based on how many people are inside, etc.
|
||||
// Returns a string which shoul be appended to the Examine output.
|
||||
/obj/belly/proc/get_examine_msg()
|
||||
if(contents.len && examine_messages.len)
|
||||
var/formatted_message
|
||||
var/raw_message = pick(examine_messages)
|
||||
var/total_bulge = 0
|
||||
|
||||
formatted_message = replacetext(raw_message,"%belly",lowertext(name))
|
||||
formatted_message = replacetext(formatted_message,"%pred",owner)
|
||||
formatted_message = replacetext(formatted_message,"%prey",english_list(contents))
|
||||
for(var/mob/living/P in contents)
|
||||
if(!P.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],"%","%")
|
||||
|
||||
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))
|
||||
M.release_vore_contents(include_absorbed = TRUE, silent = TRUE)
|
||||
|
||||
//Drop all items into the belly
|
||||
for(var/obj/item/W in M)
|
||||
if(!M.dropItemToGround(W))
|
||||
qdel(W)
|
||||
|
||||
//Incase they have the loop going, let's double check to stop it.
|
||||
M.stop_sound_channel(CHANNEL_PREYLOOP)
|
||||
|
||||
// Delete the digested mob
|
||||
qdel(M)
|
||||
|
||||
//Update owner
|
||||
owner.updateVRPanel()
|
||||
|
||||
// Handle a mob being absorbed
|
||||
/obj/belly/proc/absorb_living(var/mob/living/M)
|
||||
M.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>")
|
||||
|
||||
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.drop_location()
|
||||
//Sketchy fallback for safety, put them somewhere safe.
|
||||
else if(ismob(src))
|
||||
testing("[src] (\ref[src]) doesn't have an owner, and dropped someone at a latespawn point!")
|
||||
SSjob.SendToLateJoin(src)
|
||||
// wew lad. let's see if this never gets used, hopefully
|
||||
else
|
||||
qdel(src) //final option, I guess.
|
||||
testing("[src] (\ref[src]) was QDEL'd for not having a drop_location!")
|
||||
|
||||
//Yes, it's ""safe"" to drop items here
|
||||
/obj/belly/AllowDrop()
|
||||
return TRUE
|
||||
/*
|
||||
/obj/belly/onDropInto(var/atom/movable/AM)
|
||||
return null */
|
||||
|
||||
//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) //If owner is stat (dead, KO) we can actually escape
|
||||
to_chat(R,"<span class='warning'>You attempt to climb out of \the [lowertext(name)]. (This will take around [escapetime/10] seconds.)</span>")
|
||||
to_chat(owner,"<span class='warning'>Someone is attempting to climb out of your [lowertext(name)]!</span>")
|
||||
|
||||
if(do_after(R, owner, escapetime))
|
||||
if((owner.stat || escapable) && (R.loc == src)) //Can still escape?
|
||||
release_specific_contents(R)
|
||||
return
|
||||
else if(R.loc != src) //Aren't even in the belly. Quietly fail.
|
||||
return
|
||||
else //Belly became inescapable or mob revived
|
||||
to_chat(R,"<span class='warning'>Your attempt to escape [lowertext(name)] has failed!</span>")
|
||||
to_chat(owner,"<span class='notice'>The attempt to escape from your [lowertext(name)] has failed!</span>")
|
||||
return
|
||||
return
|
||||
|
||||
var/struggle_outer_message = pick(struggle_messages_outside)
|
||||
var/struggle_user_message = pick(struggle_messages_inside)
|
||||
|
||||
struggle_outer_message = replacetext(struggle_outer_message,"%pred",owner)
|
||||
struggle_outer_message = replacetext(struggle_outer_message,"%prey",R)
|
||||
struggle_outer_message = replacetext(struggle_outer_message,"%belly",lowertext(name))
|
||||
|
||||
struggle_user_message = replacetext(struggle_user_message,"%pred",owner)
|
||||
struggle_user_message = replacetext(struggle_user_message,"%prey",R)
|
||||
struggle_user_message = replacetext(struggle_user_message,"%belly",lowertext(name))
|
||||
|
||||
struggle_outer_message = "<span class='alert'>" + struggle_outer_message + "</span>"
|
||||
struggle_user_message = "<span class='alert'>" + struggle_user_message + "</span>"
|
||||
|
||||
var/sound/pred_struggle_snuggle = sound(get_sfx("struggle_sound"))
|
||||
var/sound/prey_struggle_snuggle = sound(get_sfx("prey_struggle"))
|
||||
var/sound/struggle_rustle = sound(get_sfx("rustle"))
|
||||
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/living/H in get_hearers_in_view(3, owner))
|
||||
if(!H.client || !(H.client.prefs.cit_toggles & EATING_NOISES))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, H)
|
||||
|
||||
if(is_wet)
|
||||
for(var/mob/living/H in hearing_mobs)
|
||||
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
|
||||
SEND_SOUND(H,pred_struggle_snuggle)
|
||||
else if(H && H in contents && H.client)
|
||||
SEND_SOUND(H,prey_struggle_snuggle)
|
||||
|
||||
else
|
||||
for(var/mob/living/H in hearing_mobs)
|
||||
if(H && H.client)
|
||||
SEND_SOUND(H, struggle_rustle)
|
||||
|
||||
for(var/mob/living/H in hearing_mobs)
|
||||
if(H && H.client && (isturf(H.loc)))
|
||||
H.show_message(struggle_outer_message, 1) // visible
|
||||
|
||||
to_chat(R,struggle_user_message)
|
||||
|
||||
if(escapable) //If the stomach has escapable enabled.
|
||||
if(prob(escapechance)) //Let's have it check to see if the prey escapes first.
|
||||
to_chat(R,"<span class='warning'>You start to climb out of \the [lowertext(name)].</span>")
|
||||
to_chat(owner,"<span class='warning'>Someone is attempting to climb out of your [lowertext(name)]!</span>")
|
||||
if(do_after(R, escapetime))
|
||||
if((escapable) && (R.loc == src)) //Can still escape?
|
||||
release_specific_contents(R)
|
||||
to_chat(R,"<span class='warning'>You climb out of \the [lowertext(name)].</span>")
|
||||
to_chat(owner,"<span class='warning'>[R] climbs out of your [lowertext(name)]!</span>")
|
||||
return
|
||||
else if(R.loc != src) //Aren't even in the belly. Quietly fail.
|
||||
return
|
||||
else //Belly became inescapable or mob revived
|
||||
to_chat(R,"<span class='warning'>Your attempt to escape [lowertext(name)] has failed!</span>")
|
||||
to_chat(owner,"<span class='notice'>The attempt to escape from your [lowertext(name)] has failed!</span>")
|
||||
return
|
||||
else if(prob(transferchance) && transferlocation) //Next, let's have it see if they end up getting into an even bigger mess then when they started.
|
||||
var/obj/belly/dest_belly
|
||||
for(var/belly in owner.vore_organs)
|
||||
var/obj/belly/B = belly
|
||||
if(B.name == transferlocation)
|
||||
dest_belly = B
|
||||
break
|
||||
|
||||
if(!dest_belly)
|
||||
to_chat(owner, "<span class='warning'>Something went wrong with your belly transfer settings. Your <b>[lowertext(name)]</b> has had it's transfer chance and transfer location cleared as a precaution.</span>")
|
||||
transferchance = 0
|
||||
transferlocation = null
|
||||
return
|
||||
|
||||
to_chat(R,"<span class='warning'>Your attempt to escape [lowertext(name)] has failed and your struggles only results in you sliding into [owner]'s [transferlocation]!</span>")
|
||||
to_chat(owner,"<span class='warning'>Someone slid into your [transferlocation] due to their struggling inside your [lowertext(name)]!</span>")
|
||||
transfer_contents(R, dest_belly)
|
||||
return
|
||||
|
||||
else if(prob(absorbchance) && digest_mode != DM_ABSORB) //After that, let's have it run the absorb chance.
|
||||
to_chat(R,"<span class='warning'>In response to your struggling, \the [lowertext(name)] begins to cling more tightly...</span>")
|
||||
to_chat(owner,"<span class='warning'>You feel your [lowertext(name)] start to cling onto its contents...</span>")
|
||||
digest_mode = DM_ABSORB
|
||||
return
|
||||
|
||||
else if(prob(digestchance) && digest_mode != DM_DIGEST) //Finally, let's see if it should run the digest chance.
|
||||
to_chat(R,"<span class='warning'>In response to your struggling, \the [lowertext(name)] begins to get more active...</span>")
|
||||
to_chat(owner,"<span class='warning'>You feel your [lowertext(name)] beginning to become active!</span>")
|
||||
digest_mode = DM_DIGEST
|
||||
return
|
||||
|
||||
else //Nothing interesting happened.
|
||||
to_chat(R,"<span class='warning'>You make no progress in escaping [owner]'s [lowertext(name)].</span>")
|
||||
to_chat(owner,"<span class='warning'>Your prey appears to be unable to make any progress in escaping your [lowertext(name)].</span>")
|
||||
return
|
||||
|
||||
/obj/belly/proc/get_mobs_and_objs_in_belly()
|
||||
var/list/see = list()
|
||||
var/list/belly_mobs = list()
|
||||
see["mobs"] = belly_mobs
|
||||
var/list/belly_objs = list()
|
||||
see["objs"] = belly_objs
|
||||
for(var/mob/living/L in loc.contents)
|
||||
belly_mobs |= L
|
||||
for(var/obj/O in loc.contents)
|
||||
belly_objs |= O
|
||||
|
||||
return see
|
||||
|
||||
|
||||
// Belly copies and then returns the copy
|
||||
// Needs to be updated for any var changes AND KEPT IN ORDER OF THE VARS ABOVE AS WELL!
|
||||
/obj/belly/proc/copy(mob/new_owner)
|
||||
var/obj/belly/dupe = new /obj/belly(new_owner)
|
||||
|
||||
//// Non-object variables
|
||||
dupe.name = name
|
||||
dupe.desc = desc
|
||||
dupe.vore_sound = vore_sound
|
||||
dupe.vore_verb = vore_verb
|
||||
dupe.release_sound = release_sound
|
||||
dupe.human_prey_swallow_time = human_prey_swallow_time
|
||||
dupe.nonhuman_prey_swallow_time = nonhuman_prey_swallow_time
|
||||
dupe.emote_time = emote_time
|
||||
dupe.digest_brute = digest_brute
|
||||
dupe.digest_burn = digest_burn
|
||||
dupe.immutable = immutable
|
||||
dupe.escapable = escapable
|
||||
dupe.escapetime = escapetime
|
||||
dupe.digestchance = digestchance
|
||||
dupe.absorbchance = absorbchance
|
||||
dupe.escapechance = escapechance
|
||||
dupe.can_taste = can_taste
|
||||
dupe.bulge_size = bulge_size
|
||||
dupe.transferlocation = transferlocation
|
||||
dupe.transferchance = transferchance
|
||||
dupe.autotransferchance = autotransferchance
|
||||
dupe.autotransferwait = autotransferwait
|
||||
dupe.swallow_time = swallow_time
|
||||
dupe.vore_capacity = vore_capacity
|
||||
dupe.is_wet = is_wet
|
||||
dupe.wet_loop = wet_loop
|
||||
|
||||
//// Object-holding variables
|
||||
//struggle_messages_outside - strings
|
||||
dupe.struggle_messages_outside.Cut()
|
||||
for(var/I in struggle_messages_outside)
|
||||
dupe.struggle_messages_outside += I
|
||||
|
||||
//struggle_messages_inside - strings
|
||||
dupe.struggle_messages_inside.Cut()
|
||||
for(var/I in struggle_messages_inside)
|
||||
dupe.struggle_messages_inside += I
|
||||
|
||||
//digest_messages_owner - strings
|
||||
dupe.digest_messages_owner.Cut()
|
||||
for(var/I in digest_messages_owner)
|
||||
dupe.digest_messages_owner += I
|
||||
|
||||
//digest_messages_prey - strings
|
||||
dupe.digest_messages_prey.Cut()
|
||||
for(var/I in digest_messages_prey)
|
||||
dupe.digest_messages_prey += I
|
||||
|
||||
//examine_messages - strings
|
||||
dupe.examine_messages.Cut()
|
||||
for(var/I in examine_messages)
|
||||
dupe.examine_messages += I
|
||||
|
||||
//emote_lists - index: digest mode, key: list of strings
|
||||
dupe.emote_lists.Cut()
|
||||
for(var/K in emote_lists)
|
||||
dupe.emote_lists[K] = list()
|
||||
for(var/I in emote_lists[K])
|
||||
dupe.emote_lists[K] += I
|
||||
return dupe
|
||||
@@ -0,0 +1,303 @@
|
||||
// Process the predator's effects upon the contents of its belly (i.e digestion/transformation etc)
|
||||
/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(!owner)
|
||||
qdel(src)
|
||||
SSbellies.belly_list -= src
|
||||
return SSBELLIES_PROCESSED
|
||||
|
||||
if(loc != owner)
|
||||
if(isliving(owner)) //we don't have machine based bellies. (yet :honk:)
|
||||
forceMove(owner)
|
||||
else
|
||||
SSbellies.belly_list -= src
|
||||
qdel(src)
|
||||
return SSBELLIES_PROCESSED
|
||||
|
||||
next_process = times_fired + (6 SECONDS/wait) //Set up our next process time.
|
||||
var/to_update = FALSE
|
||||
|
||||
/////////////////////////// Auto-Emotes ///////////////////////////
|
||||
if(contents.len && next_emote <= times_fired)
|
||||
next_emote = times_fired + round(emote_time/wait,1)
|
||||
var/list/EL = emote_lists[digest_mode]
|
||||
if(LAZYLEN(EL))
|
||||
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>")
|
||||
|
||||
///////////////////// Prey Loop Refresh/hack //////////////////////
|
||||
for(var/mob/living/M in contents)
|
||||
if(M && isbelly(M.loc))
|
||||
if(world.time > M.next_preyloop)
|
||||
if(is_wet && wet_loop)
|
||||
if(!M.client)
|
||||
continue
|
||||
M.stop_sound_channel(CHANNEL_PREYLOOP) // sanity just in case
|
||||
if(M.client.prefs.cit_toggles & DIGESTION_NOISES)
|
||||
var/sound/preyloop = sound('sound/vore/prey/loop.ogg')
|
||||
M.playsound_local(get_turf(src),preyloop, 80,0, channel = CHANNEL_PREYLOOP)
|
||||
M.next_preyloop = (world.time + 51 SECONDS)
|
||||
|
||||
|
||||
/////////////////////////// Exit Early ////////////////////////////
|
||||
var/list/touchable_items = contents - items_preserved
|
||||
if(!length(touchable_items))
|
||||
return SSBELLIES_PROCESSED
|
||||
|
||||
//////////////////////// Absorbed Handling ////////////////////////
|
||||
for(var/mob/living/M in contents)
|
||||
if(M.absorbed)
|
||||
M.Stun(5)
|
||||
|
||||
////////////////////////// Sound vars /////////////////////////////
|
||||
var/sound/prey_digest = sound(get_sfx("digest_prey"))
|
||||
var/sound/prey_death = sound(get_sfx("death_prey"))
|
||||
var/sound/pred_digest = sound(get_sfx("digest_pred"))
|
||||
var/sound/pred_death = sound(get_sfx("death_pred"))
|
||||
|
||||
///////////////////////////// DM_HOLD /////////////////////////////
|
||||
if(digest_mode == DM_HOLD)
|
||||
return SSBELLIES_PROCESSED
|
||||
|
||||
//////////////////////////// DM_DIGEST ////////////////////////////
|
||||
else if(digest_mode == DM_DIGEST)
|
||||
if(HAS_TRAIT(owner, TRAIT_PACIFISM)) //obvious.
|
||||
digest_mode = DM_NOISY
|
||||
return
|
||||
|
||||
for (var/mob/living/M in contents)
|
||||
if(prob(25))
|
||||
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/living/H in get_hearers_in_view(3, owner))
|
||||
if(!H.client || !(H.client.prefs.cit_toggles & DIGESTION_NOISES))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, H)
|
||||
last_hearcheck = world.time
|
||||
for(var/mob/living/H in hearing_mobs)
|
||||
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
|
||||
SEND_SOUND(H,pred_digest)
|
||||
else if(H && H in contents && H.client)
|
||||
SEND_SOUND(H,prey_digest)
|
||||
|
||||
//Pref protection!
|
||||
if (!M.digestable || M.absorbed)
|
||||
continue
|
||||
|
||||
//Person just died in guts!
|
||||
if(M.stat == DEAD)
|
||||
var/digest_alert_owner = pick(digest_messages_owner)
|
||||
var/digest_alert_prey = pick(digest_messages_prey)
|
||||
|
||||
//Replace placeholder vars
|
||||
digest_alert_owner = replacetext(digest_alert_owner,"%pred",owner)
|
||||
digest_alert_owner = replacetext(digest_alert_owner,"%prey",M)
|
||||
digest_alert_owner = replacetext(digest_alert_owner,"%belly",lowertext(name))
|
||||
|
||||
digest_alert_prey = replacetext(digest_alert_prey,"%pred",owner)
|
||||
digest_alert_prey = replacetext(digest_alert_prey,"%prey",M)
|
||||
digest_alert_prey = replacetext(digest_alert_prey,"%belly",lowertext(name))
|
||||
|
||||
//Send messages
|
||||
to_chat(owner, "<span class='warning'>[digest_alert_owner]</span>")
|
||||
to_chat(M, "<span class='warning'>[digest_alert_prey]</span>")
|
||||
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*.
|
||||
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/living/H in get_hearers_in_view(3, owner))
|
||||
if(!H.client || !(H.client.prefs.cit_toggles & DIGESTION_NOISES))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, H)
|
||||
last_hearcheck = world.time
|
||||
for(var/mob/living/H in hearing_mobs)
|
||||
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
|
||||
SEND_SOUND(H,pred_death)
|
||||
else if(H && H in contents && H.client)
|
||||
SEND_SOUND(H,prey_death)
|
||||
M.stop_sound_channel(CHANNEL_PREYLOOP)
|
||||
digestion_death(M)
|
||||
owner.update_icons()
|
||||
to_update = TRUE
|
||||
continue
|
||||
|
||||
|
||||
// Deal digestion damage (and feed the pred)
|
||||
if(!(M.status_flags & GODMODE))
|
||||
M.adjustFireLoss(digest_burn)
|
||||
owner.nutrition += 1
|
||||
|
||||
//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)
|
||||
|
||||
///////////////////////////// DM_HEAL /////////////////////////////
|
||||
if(digest_mode == DM_HEAL)
|
||||
for (var/mob/living/M in contents)
|
||||
if(prob(25))
|
||||
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/living/H in get_hearers_in_view(3, owner))
|
||||
if(!H.client || !(H.client.prefs.cit_toggles & DIGESTION_NOISES))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, H)
|
||||
last_hearcheck = world.time
|
||||
for(var/mob/living/H in hearing_mobs)
|
||||
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
|
||||
SEND_SOUND(H,pred_digest)
|
||||
else if(H && H in contents && H.client)
|
||||
SEND_SOUND(H,prey_digest)
|
||||
|
||||
if(M.stat != DEAD)
|
||||
if(owner.nutrition >= NUTRITION_LEVEL_STARVING && (M.health < M.maxHealth))
|
||||
M.adjustBruteLoss(-3)
|
||||
M.adjustFireLoss(-3)
|
||||
owner.nutrition -= 5
|
||||
|
||||
////////////////////////// DM_NOISY /////////////////////////////////
|
||||
//for when you just want people to squelch around
|
||||
if(digest_mode == DM_NOISY)
|
||||
if(prob(35))
|
||||
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/living/H in get_hearers_in_view(3, owner))
|
||||
if(!H.client || !(H.client.prefs.cit_toggles & DIGESTION_NOISES))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, H)
|
||||
last_hearcheck = world.time
|
||||
for(var/mob/living/H in hearing_mobs)
|
||||
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
|
||||
SEND_SOUND(H,pred_digest)
|
||||
else if(H && H in contents && H.client)
|
||||
SEND_SOUND(H,prey_digest)
|
||||
|
||||
|
||||
//////////////////////////// DM_ABSORB ////////////////////////////
|
||||
else if(digest_mode == DM_ABSORB)
|
||||
|
||||
for (var/mob/living/M in contents)
|
||||
|
||||
if(prob(10))//Less often than gurgles. People might leave this on forever.
|
||||
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/living/H in get_hearers_in_view(3, owner))
|
||||
if(!H.client || !(H.client.prefs.cit_toggles & DIGESTION_NOISES))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, H)
|
||||
last_hearcheck = world.time
|
||||
for(var/mob/living/H in hearing_mobs)
|
||||
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
|
||||
SEND_SOUND(H,pred_digest)
|
||||
else if(H && H in contents && H.client)
|
||||
SEND_SOUND(H,prey_digest)
|
||||
|
||||
if(M.absorbed)
|
||||
continue
|
||||
|
||||
if(M.nutrition >= 100) //Drain them until there's no nutrients left. Slowly "absorb" them.
|
||||
var/oldnutrition = (M.nutrition * 0.05)
|
||||
M.nutrition = (M.nutrition * 0.95)
|
||||
owner.nutrition += oldnutrition
|
||||
else if(M.nutrition < 100) //When they're finally drained.
|
||||
absorb_living(M)
|
||||
to_update = TRUE
|
||||
|
||||
//////////////////////////// DM_UNABSORB ////////////////////////////
|
||||
else if(digest_mode == DM_UNABSORB)
|
||||
|
||||
for (var/mob/living/M in contents)
|
||||
if(M.absorbed && owner.nutrition >= 100)
|
||||
M.absorbed = FALSE
|
||||
to_chat(M,"<span class='notice'>You suddenly feel solid again </span>")
|
||||
to_chat(owner,"<span class='notice'>You feel like a part of you is missing.</span>")
|
||||
owner.nutrition -= 100
|
||||
to_update = TRUE
|
||||
|
||||
//////////////////////////DM_DRAGON /////////////////////////////////////
|
||||
//because dragons need snowflake guts
|
||||
if(digest_mode == DM_DRAGON)
|
||||
if(HAS_TRAIT(owner, TRAIT_PACIFISM)) //imagine var editing this when you're a pacifist. smh
|
||||
digest_mode = DM_NOISY
|
||||
return
|
||||
|
||||
for (var/mob/living/M in contents)
|
||||
if(prob(55)) //if you're hearing this, you're a vore ho anyway.
|
||||
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/living/H in get_hearers_in_view(3, owner))
|
||||
if(!H.client || !(H.client.prefs.cit_toggles & DIGESTION_NOISES))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, H)
|
||||
last_hearcheck = world.time
|
||||
for(var/mob/living/H in hearing_mobs)
|
||||
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
|
||||
SEND_SOUND(H,pred_digest)
|
||||
else if(H && H in contents && H.client)
|
||||
SEND_SOUND(H,prey_digest)
|
||||
|
||||
//No digestion protection for megafauna.
|
||||
|
||||
//Person just died in guts!
|
||||
if(M.stat == DEAD)
|
||||
var/digest_alert_owner = pick(digest_messages_owner)
|
||||
var/digest_alert_prey = pick(digest_messages_prey)
|
||||
|
||||
//Replace placeholder vars
|
||||
digest_alert_owner = replacetext(digest_alert_owner,"%pred",owner)
|
||||
digest_alert_owner = replacetext(digest_alert_owner,"%prey",M)
|
||||
digest_alert_owner = replacetext(digest_alert_owner,"%belly",lowertext(name))
|
||||
|
||||
digest_alert_prey = replacetext(digest_alert_prey,"%pred",owner)
|
||||
digest_alert_prey = replacetext(digest_alert_prey,"%prey",M)
|
||||
digest_alert_prey = replacetext(digest_alert_prey,"%belly",lowertext(name))
|
||||
|
||||
//Send messages
|
||||
to_chat(owner, "<span class='warning'>[digest_alert_owner]</span>")
|
||||
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>")
|
||||
if((world.time + NORMIE_HEARCHECK) > last_hearcheck)
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/living/H in get_hearers_in_view(3, owner))
|
||||
if(!H.client || !(H.client.prefs.cit_toggles & DIGESTION_NOISES))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, H)
|
||||
last_hearcheck = world.time
|
||||
for(var/mob/living/H in hearing_mobs)
|
||||
if(H && H.client && (isturf(H.loc) || (H.loc != src.contents)))
|
||||
SEND_SOUND(H,pred_death)
|
||||
else if(H && H in contents && H.client)
|
||||
SEND_SOUND(H,prey_death)
|
||||
M.spill_organs(FALSE,TRUE,TRUE)
|
||||
M.stop_sound_channel(CHANNEL_PREYLOOP)
|
||||
digestion_death(M)
|
||||
owner.update_icons()
|
||||
to_update = TRUE
|
||||
continue
|
||||
|
||||
|
||||
// Deal digestion damage (and feed the pred)
|
||||
if(!(M.status_flags & GODMODE))
|
||||
M.adjustFireLoss(digest_burn)
|
||||
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)
|
||||
|
||||
if(to_update)
|
||||
for(var/mob/living/M in contents)
|
||||
if(M.client)
|
||||
M.updateVRPanel()
|
||||
if(owner.client)
|
||||
owner.updateVRPanel()
|
||||
|
||||
return SSBELLIES_PROCESSED
|
||||
@@ -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)) //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/perfect_tele_beacon/digest_act(...)
|
||||
return FALSE //Sorta important to not digest your own beacons.
|
||||
/obj/item/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/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/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
|
||||
@@ -0,0 +1,409 @@
|
||||
///////////////////// Mob Living /////////////////////
|
||||
/mob/living
|
||||
var/digestable = FALSE // Can the mob be digested inside a belly?
|
||||
var/showvoreprefs = TRUE // Determines if the mechanical vore preferences button will be displayed on the mob or not.
|
||||
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 = FALSE // Is the vore panel open?
|
||||
var/absorbed = FALSE //are we absorbed?
|
||||
var/next_preyloop
|
||||
var/vore_init = FALSE //Has this mob's vore been initialized yet?
|
||||
var/vorepref_init = FALSE //Has this mob's voreprefs been initialized?
|
||||
|
||||
//
|
||||
// Hook for generic creation of stuff on new creatures
|
||||
//
|
||||
/hook/living_new/proc/vore_setup(mob/living/M)
|
||||
M.verbs += /mob/living/proc/preyloop_refresh
|
||||
M.verbs += /mob/living/proc/lick
|
||||
M.verbs += /mob/living/proc/escapeOOC
|
||||
|
||||
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 TRUE
|
||||
M.verbs += /mob/living/proc/insidePanel
|
||||
|
||||
//Tries to load prefs if a client is present otherwise gives freebie stomach
|
||||
spawn(2 SECONDS) // long delay because the server delays in its startup. just on the safe side.
|
||||
if(M)
|
||||
M.init_vore()
|
||||
|
||||
//return TRUE to hook-caller
|
||||
return TRUE
|
||||
|
||||
/mob/living/proc/init_vore()
|
||||
vore_init = TRUE
|
||||
//Something else made organs, meanwhile.
|
||||
if(LAZYLEN(vore_organs))
|
||||
return TRUE
|
||||
|
||||
//We'll load our client's organs if we have one
|
||||
if(client && client.prefs_vr)
|
||||
if(!copy_from_prefs_vr())
|
||||
to_chat(src,"<span class='warning'>ERROR: You seem to have saved vore prefs, but they couldn't be loaded.</span>")
|
||||
return FALSE
|
||||
if(LAZYLEN(vore_organs))
|
||||
vore_selected = vore_organs[1]
|
||||
return TRUE
|
||||
|
||||
//Or, we can create a basic one for them
|
||||
if(!LAZYLEN(vore_organs))
|
||||
LAZYINITLIST(vore_organs)
|
||||
var/obj/belly/B = new /obj/belly(src)
|
||||
vore_selected = B
|
||||
B.immutable = TRUE
|
||||
B.name = "Stomach"
|
||||
B.desc = "It appears to be rather warm and wet. Makes sense, considering it's inside [name]."
|
||||
B.can_taste = TRUE
|
||||
return TRUE
|
||||
|
||||
// Handle being clicked, perhaps with something to devour
|
||||
//
|
||||
|
||||
// Refactored to use centralized vore code system - Leshana
|
||||
|
||||
// Critical adjustments due to TG grab changes - Poojawa
|
||||
|
||||
/mob/living/proc/vore_attack(var/mob/living/user, var/mob/living/prey, var/mob/living/pred)
|
||||
if(!user || !prey || !pred)
|
||||
return
|
||||
|
||||
if(!isliving(pred)) //no badmin, you can't feed people to ghosts or objects.
|
||||
return
|
||||
|
||||
if(pred == prey) //you click your target
|
||||
if(!pred.feeding)
|
||||
to_chat(user, "<span class='notice'>They aren't able to be fed.</span>")
|
||||
to_chat(pred, "<span class='notice'>[user] tried to feed you themselves, but you aren't voracious enough to be fed.</span>")
|
||||
return
|
||||
if(!is_vore_predator(pred))
|
||||
to_chat(user, "<span class='notice'>They aren't voracious enough.</span>")
|
||||
return
|
||||
feed_self_to_grabbed(user, pred)
|
||||
|
||||
else if(pred == user) //you click yourself
|
||||
if(!is_vore_predator(src))
|
||||
to_chat(user, "<span class='notice'>You aren't voracious enough.</span>")
|
||||
return
|
||||
feed_grabbed_to_self(user, prey)
|
||||
|
||||
else // click someone other than you/prey
|
||||
if(!pred.feeding)
|
||||
to_chat(user, "<span class='notice'>They aren't voracious enough to be fed.</span>")
|
||||
to_chat(pred, "<span class='notice'>[user] tried to feed you [prey], but you aren't voracious enough to be fed.</span>")
|
||||
return
|
||||
if(!prey.feeding)
|
||||
to_chat(user, "<span class='notice'>They aren't able to be fed to someone.</span>")
|
||||
to_chat(prey, "<span class='notice'>[user] tried to feed you to [pred], but you aren't able to be fed to them.</span>")
|
||||
return
|
||||
if(!is_vore_predator(pred))
|
||||
to_chat(user, "<span class='notice'>They aren't voracious enough.</span>")
|
||||
return
|
||||
feed_grabbed_to_other(user, prey, pred)
|
||||
//
|
||||
// Eating procs depending on who clicked what
|
||||
//
|
||||
/mob/living/proc/feed_grabbed_to_self(var/mob/living/user, var/mob/living/prey)
|
||||
var/belly = user.vore_selected
|
||||
return perform_the_nom(user, prey, user, belly)
|
||||
|
||||
/mob/living/proc/feed_self_to_grabbed(var/mob/living/user, var/mob/living/pred)
|
||||
var/belly = input("Choose Belly") in pred.vore_organs
|
||||
return perform_the_nom(user, user, pred, belly)
|
||||
|
||||
/mob/living/proc/feed_grabbed_to_other(var/mob/living/user, var/mob/living/prey, var/mob/living/pred)
|
||||
var/belly = input("Choose Belly") in pred.vore_organs
|
||||
return perform_the_nom(user, prey, pred, belly)
|
||||
|
||||
//
|
||||
// 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/obj/belly/belly, var/delay)
|
||||
//Sanity
|
||||
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 FALSE
|
||||
|
||||
// The belly selected at the time of noms
|
||||
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)"
|
||||
|
||||
// Prepare messages
|
||||
if(user == pred) //Feeding someone to yourself
|
||||
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.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))
|
||||
|
||||
if(!prey.Adjacent(user)) // let's not even bother attempting it yet if they aren't next to us.
|
||||
return FALSE
|
||||
|
||||
// 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
|
||||
if(delay)
|
||||
swallow_time = delay
|
||||
else
|
||||
swallow_time = istype(prey, /mob/living/carbon/human) ? belly.human_prey_swallow_time : belly.nonhuman_prey_swallow_time
|
||||
|
||||
//Timer and progress bar
|
||||
if(!do_after(user, swallow_time, prey))
|
||||
return FALSE // Prey escaped (or user disabled) before timer expired.
|
||||
|
||||
if(!prey.Adjacent(user)) //double check'd just in case they moved during the timer and the do_mob didn't fail for whatever reason
|
||||
return FALSE
|
||||
|
||||
// If we got this far, nom successful! Announce it!
|
||||
user.visible_message(success_msg)
|
||||
|
||||
// Actually shove prey into the belly.
|
||||
belly.nom_mob(prey, user)
|
||||
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
|
||||
if(prey.ckey)
|
||||
prey_stat = prey.stat//only return this if they're not an unmonkey or whatever
|
||||
if(!prey.client)//if they disconnected, tell us
|
||||
prey_braindead = TRUE
|
||||
if (pred == user)
|
||||
message_admins("[ADMIN_LOOKUPFLW(pred)] ate [ADMIN_LOOKUPFLW(prey)][!prey_braindead ? "" : " (BRAINDEAD)"][prey_stat ? " (DEAD/UNCONSCIOUS)" : ""].")
|
||||
pred.log_message("[key_name(pred)] ate [key_name(prey)].", LOG_ATTACK)
|
||||
prey.log_message("[key_name(prey)] was eaten by [key_name(pred)].", LOG_ATTACK)
|
||||
else
|
||||
message_admins("[ADMIN_LOOKUPFLW(user)] forced [ADMIN_LOOKUPFLW(pred)] to eat [ADMIN_LOOKUPFLW(prey)].")
|
||||
user.log_message("[key_name(user)] forced [key_name(pred)] to eat [key_name(prey)].", LOG_ATTACK)
|
||||
pred.log_message("[key_name(user)] forced [key_name(pred)] to eat [key_name(prey)].", LOG_ATTACK)
|
||||
prey.log_message("[key_name(user)] forced [key_name(pred)] to eat [key_name(prey)].", LOG_ATTACK)
|
||||
return TRUE
|
||||
|
||||
//
|
||||
//End vore code.
|
||||
|
||||
|
||||
//
|
||||
// Our custom resist catches for /mob/living
|
||||
//
|
||||
/mob/living/proc/vore_process_resist()
|
||||
|
||||
//Are we resisting from inside a belly?
|
||||
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
|
||||
|
||||
// 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"
|
||||
src.stop_sound_channel(CHANNEL_PREYLOOP) // sanity just in case
|
||||
if(isbelly(loc))
|
||||
var/sound/preyloop = sound('sound/vore/prey/loop.ogg')
|
||||
SEND_SOUND(src, preyloop)
|
||||
else
|
||||
to_chat(src, "<span class='alert'>You aren't inside anything, you clod.</span>")
|
||||
|
||||
// OOC Escape code for pref-breaking or AFK preds
|
||||
//
|
||||
/mob/living/proc/escapeOOC()
|
||||
set name = "OOC Escape"
|
||||
set category = "Vore"
|
||||
|
||||
//You're in a belly!
|
||||
if(isbelly(loc))
|
||||
var/obj/belly/B = loc
|
||||
var/confirm = alert(src, "You're in a mob. If you're otherwise unable to escape from a pred AFK for a long time, use this.", "Confirmation", "Okay", "Cancel")
|
||||
if(!confirm == "Okay" || loc != B)
|
||||
return
|
||||
//Actual escaping
|
||||
B.release_specific_contents(src,TRUE) //we might as well take advantage of that specific belly's handling. Else we stay blinded forever.
|
||||
src.stop_sound_channel(CHANNEL_PREYLOOP)
|
||||
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "fedprey", /datum/mood_event/fedprey)
|
||||
for(var/mob/living/simple_animal/SA in range(10))
|
||||
SA.prey_excludes[src] = world.time
|
||||
|
||||
if(isanimal(B.owner))
|
||||
var/mob/living/simple_animal/SA = B.owner
|
||||
SA.update_icons()
|
||||
|
||||
//You're in a dogborg!
|
||||
else if(istype(loc, /obj/item/dogborg/sleeper))
|
||||
var/obj/item/dogborg/sleeper/belly = loc //The belly!
|
||||
|
||||
var/confirm = alert(src, "You're in a dogborg sleeper. This is for escaping from preference-breaking or if your predator disconnects/AFKs. You can also resist out naturally too.", "Confirmation", "Okay", "Cancel")
|
||||
if(!confirm == "Okay" || loc != belly)
|
||||
return
|
||||
//Actual escaping
|
||||
belly.go_out(src) //Just force-ejects from the borg as if they'd clicked the eject button.
|
||||
else
|
||||
to_chat(src,"<span class='alert'>You aren't inside anyone, though, is the thing.</span>")
|
||||
|
||||
//
|
||||
// Verb for saving vore preferences to save file
|
||||
//
|
||||
/mob/living/proc/save_vore_prefs()
|
||||
if(!client || !client.prefs_vr)
|
||||
return FALSE
|
||||
if(!copy_to_prefs_vr())
|
||||
return FALSE
|
||||
if(!client.prefs_vr.save_vore())
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/mob/living/proc/apply_vore_prefs()
|
||||
if(!client || !client.prefs_vr)
|
||||
return FALSE
|
||||
if(!client.prefs_vr.load_vore())
|
||||
return FALSE
|
||||
if(!copy_from_prefs_vr())
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/mob/living/proc/copy_to_prefs_vr()
|
||||
if(!client || !client.prefs_vr)
|
||||
to_chat(src,"<span class='warning'>You attempted to save your vore prefs but somehow you're in this character without a client.prefs_vr variable. Tell a dev.</span>")
|
||||
return FALSE
|
||||
|
||||
var/datum/vore_preferences/P = client.prefs_vr
|
||||
|
||||
P.digestable = src.digestable
|
||||
P.devourable = src.devourable
|
||||
P.feeding = src.feeding
|
||||
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
|
||||
|
||||
//
|
||||
// Proc for applying vore preferences, given bellies
|
||||
//
|
||||
/mob/living/proc/copy_from_prefs_vr()
|
||||
if(!client || !client.prefs_vr)
|
||||
to_chat(src,"<span class='warning'>You attempted to apply your vore prefs but somehow you're in this character without a client.prefs_vr variable. Tell a dev.</span>")
|
||||
return FALSE
|
||||
vorepref_init = TRUE
|
||||
|
||||
var/datum/vore_preferences/P = client.prefs_vr
|
||||
|
||||
digestable = P.digestable
|
||||
devourable = P.devourable
|
||||
feeding = P.feeding
|
||||
vore_taste = P.vore_taste
|
||||
|
||||
release_vore_contents(silent = TRUE)
|
||||
vore_organs.Cut()
|
||||
for(var/entry in P.belly_prefs)
|
||||
list_to_object(entry,src)
|
||||
|
||||
return TRUE
|
||||
|
||||
//
|
||||
// Release everything in every vore organ
|
||||
//
|
||||
/mob/living/proc/release_vore_contents(var/include_absorbed = TRUE, var/silent = FALSE)
|
||||
for(var/belly in vore_organs)
|
||||
var/obj/belly/B = belly
|
||||
B.release_all_contents(include_absorbed, silent)
|
||||
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
/mob/living/proc/lick(var/mob/living/tasted in oview(1))
|
||||
set name = "Lick Someone"
|
||||
set category = "Vore"
|
||||
set desc = "Lick someone nearby!"
|
||||
|
||||
if(!istype(tasted))
|
||||
return
|
||||
|
||||
if(src == stat)
|
||||
return
|
||||
|
||||
src.setClickCooldown(100)
|
||||
|
||||
src.visible_message("<span class='warning'>[src] licks [tasted]!</span>","<span class='notice'>You lick [tasted]. They taste rather like [tasted.get_taste_message()].</span>","<b>Slurp!</b>")
|
||||
|
||||
|
||||
/mob/living/proc/get_taste_message(allow_generic = TRUE, datum/species/mrace)
|
||||
if(!vore_taste && !allow_generic)
|
||||
return FALSE
|
||||
|
||||
var/taste_message = ""
|
||||
if(vore_taste && (vore_taste != ""))
|
||||
taste_message += "[vore_taste]"
|
||||
else
|
||||
if(ishuman(src))
|
||||
taste_message += "they haven't bothered to set their flavor text"
|
||||
else
|
||||
taste_message += "a plain old normal [src]"
|
||||
|
||||
/* if(ishuman(src))
|
||||
var/mob/living/carbon/human/H = src
|
||||
if(H.touching.reagent_list.len) //Just the first one otherwise I'll go insane.
|
||||
var/datum/reagent/R = H.touching.reagent_list[1]
|
||||
taste_message += " You also get the flavor of [R.taste_description] from something on them"*/
|
||||
return taste_message
|
||||
@@ -0,0 +1,169 @@
|
||||
|
||||
/*
|
||||
VVVVVVVV VVVVVVVV OOOOOOOOO RRRRRRRRRRRRRRRRR EEEEEEEEEEEEEEEEEEEEEE
|
||||
V::::::V V::::::V OO:::::::::OO R::::::::::::::::R E::::::::::::::::::::E
|
||||
V::::::V V::::::V OO:::::::::::::OO R::::::RRRRRR:::::R E::::::::::::::::::::E
|
||||
V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEEEEE::::E
|
||||
V:::::V V:::::V O::::::O O::::::O R::::R R:::::R E:::::E EEEEEE
|
||||
V:::::V V:::::V O:::::O O:::::O R::::R R:::::R E:::::E
|
||||
V:::::V V:::::V O:::::O O:::::O R::::RRRRRR:::::R E::::::EEEEEEEEEE
|
||||
V:::::V V:::::V O:::::O O:::::O R:::::::::::::RR E:::::::::::::::E
|
||||
V:::::V V:::::V O:::::O O:::::O R::::RRRRRR:::::R E:::::::::::::::E
|
||||
V:::::V V:::::V O:::::O O:::::O R::::R R:::::R E::::::EEEEEEEEEE
|
||||
V:::::V:::::V O:::::O O:::::O R::::R R:::::R E:::::E
|
||||
V:::::::::V O::::::O O::::::O R::::R R:::::R E:::::E EEEEEE
|
||||
V:::::::V O:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEEEE:::::E
|
||||
V:::::V OO:::::::::::::OO R::::::R R:::::RE::::::::::::::::::::E
|
||||
V:::V OO:::::::::OO R::::::R R:::::RE::::::::::::::::::::E
|
||||
VVV OOOOOOOOO RRRRRRRR RRRRRRREEEEEEEEEEEEEEEEEEEEEE
|
||||
|
||||
-Aro <3 */
|
||||
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
#define VORE_VERSION 4
|
||||
|
||||
GLOBAL_LIST_EMPTY(vore_preferences_datums)
|
||||
|
||||
/client
|
||||
var/datum/vore_preferences/prefs_vr
|
||||
|
||||
/datum/vore_preferences
|
||||
//Actual preferences
|
||||
var/digestable = FALSE
|
||||
var/devourable = FALSE
|
||||
var/feeding = FALSE
|
||||
// var/allowmobvore = TRUE
|
||||
var/list/belly_prefs = list()
|
||||
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
|
||||
|
||||
/datum/vore_preferences/New(client/C)
|
||||
if(istype(C))
|
||||
client = C
|
||||
client_ckey = C.ckey
|
||||
load_vore()
|
||||
|
||||
//
|
||||
// Check if an object is capable of eating things, based on vore_organs
|
||||
//
|
||||
/proc/is_vore_predator(var/mob/living/O)
|
||||
if(istype(O,/mob/living))
|
||||
if(O.vore_organs.len > 0)
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
|
||||
//
|
||||
// Belly searching for simplifying other procs
|
||||
// Mostly redundant now with belly-objects and isbelly(loc)
|
||||
//
|
||||
/proc/check_belly(atom/movable/A)
|
||||
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.prefs || !client.prefs.default_slot)
|
||||
return FALSE //Need to know what character to load!
|
||||
|
||||
slot = client.prefs.default_slot
|
||||
|
||||
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/list/json_from_file = json_decode(file2text(path))
|
||||
if(!json_from_file)
|
||||
return FALSE //My concern grows
|
||||
|
||||
var/version = json_from_file["version"]
|
||||
json_from_file = patch_version(json_from_file,version)
|
||||
|
||||
digestable = json_from_file["digestable"]
|
||||
devourable = json_from_file["devourable"]
|
||||
feeding = json_from_file["feeding"]
|
||||
vore_taste = json_from_file["vore_taste"]
|
||||
belly_prefs = json_from_file["belly_prefs"]
|
||||
|
||||
//Quick sanitize
|
||||
if(isnull(digestable))
|
||||
digestable = FALSE
|
||||
if(isnull(devourable))
|
||||
devourable = FALSE
|
||||
if(isnull(feeding))
|
||||
feeding = FALSE
|
||||
if(isnull(belly_prefs))
|
||||
belly_prefs = list()
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/vore_preferences/proc/save_vore()
|
||||
if(!path)
|
||||
return FALSE
|
||||
|
||||
var/version = VORE_VERSION //For "good times" use in the future
|
||||
var/list/settings_list = list(
|
||||
"version" = version,
|
||||
"digestable" = digestable,
|
||||
"devourable" = devourable,
|
||||
"feeding" = feeding,
|
||||
"vore_taste" = vore_taste,
|
||||
"belly_prefs" = belly_prefs,
|
||||
)
|
||||
|
||||
//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
|
||||
//#ifdef RUST_G
|
||||
// call(RUST_G, "file_write")(json_to_file, path)
|
||||
//#else
|
||||
// Fall back to using old format if we are not using rust-g
|
||||
if(fexists(path))
|
||||
fdel(path) //Byond only supports APPENDING to files, not replacing.
|
||||
text2file(json_to_file, path)
|
||||
//#endif
|
||||
if(!fexists(path))
|
||||
testing("Saving: [path] failed file write")
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/* commented out list things
|
||||
"allowmobvore" = allowmobvore,
|
||||
"can_be_drop_prey" = can_be_drop_prey,
|
||||
"can_be_drop_pred" = can_be_drop_pred, */
|
||||
|
||||
//Can do conversions here
|
||||
datum/vore_preferences/proc/patch_version(var/list/json_from_file,var/version)
|
||||
return json_from_file
|
||||
|
||||
#undef VORE_VERSION
|
||||
@@ -0,0 +1,62 @@
|
||||
// -------------- Sickshot -------------
|
||||
/obj/item/gun/energy/sickshot
|
||||
name = "\improper MPA6 \'Sickshot\'"
|
||||
desc = "A device that can trigger convusions in specific areas to eject foreign material from a host. Must be used very close to a target. Not for Combat usage."
|
||||
|
||||
icon_state = "dragnet"
|
||||
item_state = "dragnet"
|
||||
ammo_x_offset = 1
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/sickshot)
|
||||
|
||||
/obj/item/ammo_casing/energy/sickshot
|
||||
projectile_type = /obj/item/projectile/sickshot
|
||||
e_cost = 100
|
||||
|
||||
//Projectile
|
||||
/obj/item/projectile/sickshot
|
||||
name = "sickshot pulse"
|
||||
icon_state = "e_netting"
|
||||
damage = 0
|
||||
damage_type = STAMINA
|
||||
range = 2
|
||||
|
||||
/obj/item/projectile/sickshot/on_hit(var/atom/movable/target, var/blocked = 0)
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/H = target
|
||||
if(prob(5))
|
||||
for(var/X in H.vore_organs)
|
||||
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
|
||||
|
||||
|
||||
////////////////////////// Anti-Noms Drugs //////////////////////////
|
||||
/*
|
||||
/datum/reagent/medicine/ickypak
|
||||
name = "Ickypak"
|
||||
id = "ickypak"
|
||||
description = "A foul-smelling green liquid, for inducing muscle contractions to expel accidentally ingested things."
|
||||
reagent_state = LIQUID
|
||||
color = "#0E900E"
|
||||
metabolization_rate = 0.25 * REAGENTS_METABOLISM
|
||||
|
||||
/datum/reagent/medicine/ickypak/on_mob_life(var/mob/living/M, method=INGEST)
|
||||
if(prob(10))
|
||||
M.visible_message("<span class='danger'>[M] retches!</span>", \
|
||||
"<span class='userdanger'>You don't feel good...</span>")
|
||||
for(var/I in M.vore_organs)
|
||||
var/datum/belly/B = M.vore_organs[I]
|
||||
for(var/atom/movable/A in B.internal_contents)
|
||||
if(prob(55))
|
||||
playsound(M, 'sound/effects/splat.ogg', 50, 1)
|
||||
B.release_specific_contents(A)
|
||||
M.visible_message("<span class='danger'>[M] contracts strangely, spewing out something!</span>", \
|
||||
"<span class='userdanger'>You spew out something from inside you!</span>")
|
||||
return ..()
|
||||
|
||||
/datum/chemical_reaction/ickypak
|
||||
name = "Ickypak"
|
||||
id = "ickypak"
|
||||
results = list("ickypak" = 2)
|
||||
required_reagents = list("chlorine" = 2 , "oil" = 1) */
|
||||
@@ -0,0 +1,738 @@
|
||||
//
|
||||
// Vore management panel for players
|
||||
//
|
||||
|
||||
#define BELLIES_MAX 20
|
||||
#define BELLIES_NAME_MIN 2
|
||||
#define BELLIES_NAME_MAX 12
|
||||
#define BELLIES_DESC_MAX 1024
|
||||
#define FLAVOR_MAX 40
|
||||
|
||||
/mob/living/proc/insidePanel()
|
||||
set name = "Vore Panel"
|
||||
set category = "Vore"
|
||||
|
||||
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", 450, 700, picker_holder)
|
||||
picker_holder.popup.set_content(dat)
|
||||
picker_holder.popup.open()
|
||||
src.openpanel = TRUE
|
||||
|
||||
/mob/living/proc/updateVRPanel() //Panel popup update call from belly events.
|
||||
if(src.openpanel == TRUE)
|
||||
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", 450, 700, picker_holder)
|
||||
picker_holder.popup.set_content(dat)
|
||||
picker_holder.popup.open()
|
||||
|
||||
//
|
||||
// Callback Handler for the Inside form
|
||||
//
|
||||
/datum/vore_look
|
||||
var/obj/belly/selected
|
||||
var/show_interacts = FALSE
|
||||
var/datum/browser/popup
|
||||
var/loop = null; // Magic self-reference to stop the handler from being GC'd before user takes action.
|
||||
|
||||
/datum/vore_look/Destroy()
|
||||
loop = null
|
||||
selected = null
|
||||
return QDEL_HINT_HARDDEL
|
||||
|
||||
/datum/vore_look/Topic(href,href_list[])
|
||||
if (vp_interact(href, href_list))
|
||||
popup.set_content(gen_vui(usr))
|
||||
usr << output(popup.get_content(), "insidePanel.browser")
|
||||
|
||||
/datum/vore_look/proc/gen_vui(var/mob/living/user)
|
||||
var/dat
|
||||
dat += "Remember to toggle the vore mode, it's to the left of your combat toggle. Open mouth means you're voracious!<br>"
|
||||
dat += "Remember that the prey is blind, use audible mode subtle messages to communicate to them with posts!<br>"
|
||||
dat += "<HR>"
|
||||
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 [user.absorbed ? "absorbed into " : "inside "]</font> <font color = 'yellow'>[eater]'s</font> <font color = 'red'>[inside_belly]</font>!<br><br>"
|
||||
|
||||
if(inside_belly.desc)
|
||||
dat += "[inside_belly.desc]<br><br>"
|
||||
|
||||
if (inside_belly.contents.len > 1)
|
||||
dat += "You can see the following around you:<br>"
|
||||
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>"
|
||||
|
||||
//Zero-width space, for wrapping
|
||||
dat += "​"
|
||||
else
|
||||
dat += "You aren't inside anyone."
|
||||
|
||||
dat += "<HR>"
|
||||
|
||||
dat += "<ol style='list-style: none; padding: 0; overflow: auto;'>"
|
||||
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
|
||||
dat += "<li style='float: left'><a href='?src=\ref[src];bellypick=\ref[B]'>[B.name]"
|
||||
var/spanstyle
|
||||
switch(B.digest_mode)
|
||||
if(DM_HOLD)
|
||||
spanstyle = ""
|
||||
if(DM_DIGEST)
|
||||
spanstyle = "color:red;"
|
||||
if(DM_HEAL)
|
||||
spanstyle = "color:darkgreen;"
|
||||
if(DM_NOISY)
|
||||
spanstyle = "color:purple;"
|
||||
if(DM_ABSORB)
|
||||
spanstyle = "color:purple;"
|
||||
if(DM_DRAGON)
|
||||
spanstyle = "color:blue;"
|
||||
|
||||
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>"
|
||||
dat += "</ol>"
|
||||
dat += "<HR>"
|
||||
|
||||
// Selected Belly (contents, configuration)
|
||||
if(!selected)
|
||||
dat += "No belly selected. Click one to select it."
|
||||
else
|
||||
if(selected.contents.len)
|
||||
dat += "<b>Contents:</b> "
|
||||
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 += "​"
|
||||
|
||||
//If there's more than one thing, add an [All] button
|
||||
if(selected.contents.len > 1)
|
||||
dat += "<a href='?src=\ref[src];insidepick=1;pickall=1'>\[All\]</a>"
|
||||
|
||||
dat += "<HR>"
|
||||
|
||||
//Belly Name Button
|
||||
dat += "<a href='?src=\ref[src];b_name=\ref[selected]'>Name:</a>"
|
||||
dat += " '[selected.name]'"
|
||||
|
||||
//Belly Type button
|
||||
dat += "<br><a href='?src=\ref[src];b_wetness=\ref[selected]'>Is Fleshy:</a>"
|
||||
dat += "[selected.is_wet ? "Yes" : "No"]"
|
||||
if(selected.is_wet)
|
||||
dat += "<br><a href='?src=\ref[src];b_wetloop=\ref[selected]'>Internal loop for prey?:</a>"
|
||||
dat += "[selected.wet_loop ? "Yes" : "No"]"
|
||||
|
||||
//Digest Mode Button
|
||||
dat += "<br><a href='?src=\ref[src];b_mode=\ref[selected]'>Belly Mode:</a>"
|
||||
dat += " [selected.digest_mode]"
|
||||
|
||||
//Belly verb
|
||||
dat += "<br><a href='?src=\ref[src];b_verb=\ref[selected]'>Vore Verb:</a>"
|
||||
dat += " '[selected.vore_verb]'"
|
||||
|
||||
//Inside flavortext
|
||||
dat += "<br><a href='?src=\ref[src];b_desc=\ref[selected]'>Flavor Text:</a>"
|
||||
dat += " '[selected.desc]'"
|
||||
|
||||
//Belly sound
|
||||
dat += "<br><a href='?src=\ref[src];b_sound=\ref[selected]'>Vore Sound: [selected.vore_sound]</a>"
|
||||
dat += "<a href='?src=\ref[src];b_soundtest=\ref[selected]'>Test</a>"
|
||||
|
||||
//Release sound
|
||||
dat += "<br><a href='?src=\ref[src];b_release=\ref[selected]'>Release Sound: [selected.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>"
|
||||
|
||||
//Can belly taste?
|
||||
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)
|
||||
dat += "<a href='?src=\ref[src];show_int=\ref[selected]'>[show_interacts ? "Hide" : "Show"]</a>"
|
||||
|
||||
if(show_interacts && selected.escapable)
|
||||
dat += "<HR>"
|
||||
dat += "Interaction Settings <a href='?src=\ref[src];int_help=\ref[selected]'>?</a>"
|
||||
dat += "<br><a href='?src=\ref[src];b_escapechance=\ref[selected]'>Set Belly Escape Chance</a>"
|
||||
dat += " [selected.escapechance]%"
|
||||
|
||||
dat += "<br><a href='?src=\ref[src];b_escapetime=\ref[selected]'>Set Belly Escape Time</a>"
|
||||
dat += " [selected.escapetime/10]s"
|
||||
|
||||
//Special <br> here to add a gap
|
||||
dat += "<br style='line-height:5px;'>"
|
||||
dat += "<br><a href='?src=\ref[src];b_transferchance=\ref[selected]'>Set Belly Transfer Chance</a>"
|
||||
dat += " [selected.transferchance]%"
|
||||
|
||||
dat += "<br><a href='?src=\ref[src];b_transferlocation=\ref[selected]'>Set Belly Transfer Location</a>"
|
||||
dat += " [selected.transferlocation ? selected.transferlocation : "Disabled"]"
|
||||
|
||||
//Special <br> here to add a gap
|
||||
dat += "<br style='line-height:5px;'>"
|
||||
dat += "<br><a href='?src=\ref[src];b_absorbchance=\ref[selected]'>Set Belly Absorb Chance</a>"
|
||||
dat += " [selected.absorbchance]%"
|
||||
|
||||
dat += "<br><a href='?src=\ref[src];b_digestchance=\ref[selected]'>Set Belly Digest Chance</a>"
|
||||
dat += " [selected.digestchance]%"
|
||||
dat += "<HR>"
|
||||
|
||||
//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 += "<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];applyprefs=1'>Reload Slot Prefs</a>"
|
||||
|
||||
dat += "<HR>"
|
||||
switch(user.digestable)
|
||||
if(TRUE)
|
||||
dat += "<br><a style='background:#173d15;' href='?src=\ref[src];toggledg=1'>Toggle Digestable (Currently: ON)</a>"
|
||||
if(FALSE)
|
||||
dat += "<br><a style='background:#990000;' href='?src=\ref[src];toggledg=1'>Toggle Digestable (Currently: OFF)</a>"
|
||||
switch(user.devourable)
|
||||
if(TRUE)
|
||||
dat += "<br><a style='background:#173d15;' href='?src=\ref[src];toggledvor=1'>Toggle Devourable (Currently: ON)</a>"
|
||||
if(FALSE)
|
||||
dat += "<br><a style='background:#990000;' href='?src=\ref[src];toggledvor=1'>Toggle Devourable (Currently: OFF)</a>"
|
||||
switch(user.feeding)
|
||||
if(TRUE)
|
||||
dat += "<br><a style='background:#173d15;' href='?src=\ref[src];toggledfeed=1'>Toggle Feeding (Currently: ON)</a>"
|
||||
if(FALSE)
|
||||
dat += "<br><a style='background:#990000;' href='?src=\ref[src];toggledfeed=1'>Toggle Feeding (Currently: OFF)</a>"
|
||||
|
||||
//Returns the dat html to the vore_look
|
||||
return dat
|
||||
|
||||
/datum/vore_look/proc/vp_interact(href, href_list)
|
||||
var/mob/living/user = usr
|
||||
for(var/H in href_list)
|
||||
|
||||
if(href_list["close"])
|
||||
qdel(src) // Cleanup
|
||||
user.openpanel = FALSE
|
||||
return
|
||||
|
||||
if(href_list["show_int"])
|
||||
show_interacts = !show_interacts
|
||||
return TRUE //Force update
|
||||
|
||||
if(href_list["int_help"])
|
||||
alert("These control how your belly responds to someone using 'resist' while inside you. The percent chance to trigger each is listed below, \
|
||||
and you can change them to whatever you see fit. Setting them to 0% will disable the possibility of that interaction. \
|
||||
These only function as long as interactions are turned on in general. Keep in mind, the 'belly mode' interactions (digest/absorb) \
|
||||
will affect all prey in that belly, if one resists and triggers digestion/absorption. If multiple trigger at the same time, \
|
||||
only the first in the order of 'Escape > Transfer > Absorb > Digest' will occur.","Interactions Help")
|
||||
return FALSE //Force update
|
||||
|
||||
if(href_list["outsidepick"])
|
||||
var/atom/movable/tgt = locate(href_list["outsidepick"])
|
||||
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"
|
||||
|
||||
if(istype(tgt,/mob/living))
|
||||
var/mob/living/M = tgt
|
||||
intent = alert("What do you want to do to them?","Query","Examine","Help Out","Devour")
|
||||
switch(intent)
|
||||
if("Examine") //Examine a mob inside another mob
|
||||
M.examine(user)
|
||||
|
||||
if("Help Out") //Help the inside-mob out
|
||||
if(user.stat || user.absorbed || M.absorbed)
|
||||
to_chat(user,"<span class='warning'>You can't do that in your state!</span>")
|
||||
return TRUE
|
||||
|
||||
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>")
|
||||
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>")
|
||||
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 TRUE
|
||||
|
||||
if(!user.vore_selected)
|
||||
to_chat(user,"<span class='warning'>Pick a belly on yourself first!</span>")
|
||||
return TRUE
|
||||
|
||||
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) //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)
|
||||
|
||||
else if(istype(tgt,/obj/item))
|
||||
var/obj/item/T = tgt
|
||||
if(!(tgt in OB))
|
||||
//Doesn't exist anymore, update.
|
||||
return TRUE
|
||||
intent = alert("What do you want to do to that?","Query","Examine","Use Hand")
|
||||
switch(intent)
|
||||
if("Examine")
|
||||
T.examine(user)
|
||||
|
||||
if("Use Hand")
|
||||
if(user.stat)
|
||||
to_chat(user,"<span class='warning'>You can't do that in your state!</span>")
|
||||
return TRUE
|
||||
|
||||
user.ClickOn(T)
|
||||
sleep(5) //Seems to exit too fast for the panel to update
|
||||
|
||||
if(href_list["insidepick"])
|
||||
var/intent
|
||||
|
||||
//Handle the [All] choice. Ugh inelegant. Someone make this pretty.
|
||||
if(href_list["pickall"])
|
||||
intent = alert("Eject all, Move all?","Query","Eject all","Cancel","Move all")
|
||||
switch(intent)
|
||||
if("Cancel")
|
||||
return FALSE
|
||||
|
||||
if("Eject all")
|
||||
if(user.stat)
|
||||
to_chat(user,"<span class='warning'>You can't do that in your state!</span>")
|
||||
return FALSE
|
||||
|
||||
selected.release_all_contents()
|
||||
|
||||
if("Move all")
|
||||
if(user.stat)
|
||||
to_chat(user,"<span class='warning'>You can't do that in your state!</span>")
|
||||
return FALSE
|
||||
|
||||
var/obj/belly/choice = input("Move all where?","Select Belly") as null|anything in user.vore_organs
|
||||
if(!choice)
|
||||
return FALSE
|
||||
|
||||
for(var/atom/movable/tgt in selected)
|
||||
to_chat(tgt,"<span class='warning'>You're squished from [user]'s [lowertext(selected)] to their [lowertext(choice.name)]!</span>")
|
||||
selected.transfer_contents(tgt, choice, 1)
|
||||
|
||||
var/atom/movable/tgt = locate(href_list["insidepick"])
|
||||
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")
|
||||
switch(intent)
|
||||
if("Examine")
|
||||
tgt.examine(user)
|
||||
|
||||
if("Eject")
|
||||
if(user.stat)
|
||||
to_chat(user,"<span class='warning'>You can't do that in your state!</span>")
|
||||
return FALSE
|
||||
|
||||
selected.release_specific_contents(tgt)
|
||||
|
||||
if("Move")
|
||||
if(user.stat)
|
||||
to_chat(user,"<span class='warning'>You can't do that in your state!</span>")
|
||||
return FALSE
|
||||
|
||||
var/obj/belly/choice = input("Move [tgt] where?","Select Belly") as null|anything in user.vore_organs
|
||||
if(!choice || !(tgt in selected))
|
||||
return FALSE
|
||||
|
||||
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)
|
||||
|
||||
if(href_list["newbelly"])
|
||||
if(user.vore_organs.len >= BELLIES_MAX)
|
||||
return FALSE
|
||||
|
||||
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)
|
||||
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 FALSE
|
||||
|
||||
var/obj/belly/NB = new(user)
|
||||
NB.name = new_name
|
||||
selected = NB
|
||||
|
||||
if(href_list["bellypick"])
|
||||
selected = locate(href_list["bellypick"])
|
||||
user.vore_selected = selected
|
||||
|
||||
////
|
||||
//Please keep these the same order they are on the panel UI for ease of coding
|
||||
////
|
||||
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)
|
||||
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 FALSE
|
||||
|
||||
selected.name = new_name
|
||||
|
||||
if(href_list["b_wetness"])
|
||||
selected.is_wet = !selected.is_wet
|
||||
|
||||
if(href_list["b_wetloop"])
|
||||
selected.wet_loop = !selected.wet_loop
|
||||
|
||||
if(href_list["b_mode"])
|
||||
var/list/menu_list = selected.digest_modes
|
||||
|
||||
var/new_mode = input("Choose Mode (currently [selected.digest_mode])") as null|anything in menu_list
|
||||
if(!new_mode)
|
||||
return FALSE
|
||||
selected.digest_mode = new_mode
|
||||
|
||||
if(href_list["b_desc"])
|
||||
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)
|
||||
alert("Entered belly desc too long. [BELLIES_DESC_MAX] character limit.","Error")
|
||||
return FALSE
|
||||
selected.desc = new_desc
|
||||
else //Returned null
|
||||
return FALSE
|
||||
|
||||
if(href_list["b_msgs"])
|
||||
var/list/messages = list(
|
||||
"Digest Message (to prey)",
|
||||
"Digest Message (to you)",
|
||||
"Struggle Message (outside)",
|
||||
"Struggle Message (inside)",
|
||||
"Examine Message (when full)",
|
||||
"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") 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)
|
||||
if("Digest Message (to prey)")
|
||||
var/new_message = input(user,"These are sent to prey when they expire. Write them in 2nd person ('you feel X'). Avoid using %prey in this type."+help,"Digest Message (to prey)",selected.get_messages("dmp")) as message
|
||||
if(new_message)
|
||||
selected.set_messages(new_message,"dmp")
|
||||
|
||||
if("Digest Message (to you)")
|
||||
var/new_message = input(user,"These are sent to you when prey expires in you. Write them in 2nd person ('you feel X'). Avoid using %pred in this type."+help,"Digest Message (to you)",selected.get_messages("dmo")) as message
|
||||
if(new_message)
|
||||
selected.set_messages(new_message,"dmo")
|
||||
|
||||
if("Struggle Message (outside)")
|
||||
var/new_message = input(user,"These are sent to those nearby when prey struggles. Write them in 3rd person ('X's Y bulges')."+help,"Struggle Message (outside)",selected.get_messages("smo")) as message
|
||||
if(new_message)
|
||||
selected.set_messages(new_message,"smo")
|
||||
|
||||
if("Struggle Message (inside)")
|
||||
var/new_message = input(user,"These are sent to prey when they struggle. Write them in 2nd person ('you feel X'). Avoid using %prey in this type."+help,"Struggle Message (inside)",selected.get_messages("smi")) as message
|
||||
if(new_message)
|
||||
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
|
||||
if(new_message)
|
||||
selected.set_messages(new_message,"em")
|
||||
|
||||
if("Reset All To Default")
|
||||
var/confirm = alert(user,"This will delete any custom messages. Are you sure?","Confirmation","DELETE","Cancel")
|
||||
if(confirm == "DELETE")
|
||||
selected.digest_messages_prey = initial(selected.digest_messages_prey)
|
||||
selected.digest_messages_owner = initial(selected.digest_messages_owner)
|
||||
selected.struggle_messages_outside = initial(selected.struggle_messages_outside)
|
||||
selected.struggle_messages_inside = initial(selected.struggle_messages_inside)
|
||||
|
||||
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)
|
||||
alert("Entered verb length invalid (must be longer than [BELLIES_NAME_MIN], no longer than [BELLIES_NAME_MAX]).","Error")
|
||||
return FALSE
|
||||
|
||||
selected.vore_verb = new_verb
|
||||
|
||||
if(href_list["b_release"])
|
||||
var/choice = input(user,"Currently set to [selected.release_sound]","Select Sound") as null|anything in GLOB.pred_release_sounds
|
||||
|
||||
if(!choice)
|
||||
return
|
||||
|
||||
selected.release_sound = choice
|
||||
|
||||
if(href_list["b_releasesoundtest"])
|
||||
var/sound/releasetest = GLOB.prey_release_sounds[selected.release_sound]
|
||||
if(releasetest)
|
||||
SEND_SOUND(user, releasetest)
|
||||
|
||||
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 = choice
|
||||
|
||||
if(href_list["b_soundtest"])
|
||||
var/sound/voretest = GLOB.prey_vore_sounds[selected.vore_sound]
|
||||
if(voretest)
|
||||
SEND_SOUND(user, voretest)
|
||||
|
||||
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
|
||||
to_chat(usr,"<span class='warning'>Prey now have special interactions with your [lowertext(selected.name)] depending on your settings.</span>")
|
||||
else if(selected.escapable == TRUE) //Never escapable.
|
||||
selected.escapable = FALSE
|
||||
to_chat(usr,"<span class='warning'>Prey will not be able to have special interactions with your [lowertext(selected.name)].</span>")
|
||||
show_interacts = FALSE //Force the hiding of the panel
|
||||
else
|
||||
alert("Something went wrong. Your stomach will now not have special interactions. Press the button enable them again and tell a dev.","Error") //If they somehow have a varable that's not 0 or 1
|
||||
selected.escapable = FALSE
|
||||
show_interacts = FALSE //Force the hiding of the panel
|
||||
|
||||
if(href_list["b_escapechance"])
|
||||
var/escape_chance_input = input(user, "Set prey escape chance on resist (as %)", "Prey Escape Chance") as num|null
|
||||
if(!isnull(escape_chance_input)) //These have to be 'null' because both cancel and 0 are valid, separate options
|
||||
selected.escapechance = sanitize_integer(escape_chance_input, 0, 100, initial(selected.escapechance))
|
||||
|
||||
if(href_list["b_escapetime"])
|
||||
var/escape_time_input = input(user, "Set number of seconds for prey to escape on resist (1-60)", "Prey Escape Time") as num|null
|
||||
if(!isnull(escape_time_input))
|
||||
selected.escapetime = sanitize_integer(escape_time_input*10, 10, 600, initial(selected.escapetime))
|
||||
|
||||
if(href_list["b_transferchance"])
|
||||
var/transfer_chance_input = input(user, "Set belly transfer chance on resist (as %). You must also set the location for this to have any effect.", "Prey Escape Time") as num|null
|
||||
if(!isnull(transfer_chance_input))
|
||||
selected.transferchance = sanitize_integer(transfer_chance_input, 0, 100, initial(selected.transferchance))
|
||||
|
||||
if(href_list["b_transferlocation"])
|
||||
var/obj/belly/choice = input("Where do you want your [lowertext(selected.name)] to lead if prey resists?","Select Belly") as null|anything in (user.vore_organs + "None - Remove" - selected)
|
||||
|
||||
if(!choice) //They cancelled, no changes
|
||||
return FALSE
|
||||
else if(choice == "None - Remove")
|
||||
selected.transferlocation = null
|
||||
else
|
||||
selected.transferlocation = choice.name
|
||||
|
||||
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_del"])
|
||||
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(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='warning'>Belly Preferences not saved!</span>")
|
||||
|
||||
else
|
||||
to_chat(user, "<span class='notice'>Belly Preferences were saved!</span>")
|
||||
log_admin("Could not save vore prefs on USER: [user].")
|
||||
|
||||
if(href_list["applyprefs"])
|
||||
var/alert = alert("Are you sure you want to reload character slot preferences? This will remove your current vore organs and eject their contents.","Confirmation","Reload","Cancel")
|
||||
if(!alert == "Reload")
|
||||
return FALSE
|
||||
if(!user.apply_vore_prefs())
|
||||
alert("ERROR: Vore preferences failed to apply!","Error")
|
||||
else
|
||||
to_chat(user,"<span class='notice'>Vore preferences applied from active slot!</span>")
|
||||
|
||||
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 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 FALSE
|
||||
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")
|
||||
switch(choice)
|
||||
if("Cancel")
|
||||
return FALSE
|
||||
if("Allow Digestion")
|
||||
user.digestable = TRUE
|
||||
if("Prevent Digestion")
|
||||
user.digestable = FALSE
|
||||
|
||||
if(user.client.prefs_vr)
|
||||
user.client.prefs_vr.digestable = user.digestable
|
||||
|
||||
if(href_list["toggledvor"])
|
||||
var/choice = alert(user, "This button is for those who don't like vore at all. Devouring you is currently: [user.devourable ? "Allowed" : "Prevented"]", "", "Allow Devourment", "Cancel", "Prevent Devourment")
|
||||
switch(choice)
|
||||
if("Cancel")
|
||||
return FALSE
|
||||
if("Allow Devourment")
|
||||
user.devourable = TRUE
|
||||
if("Prevent Devourment")
|
||||
user.devourable = FALSE
|
||||
|
||||
if(user.client.prefs_vr)
|
||||
user.client.prefs_vr.devourable = user.devourable
|
||||
|
||||
if(href_list["toggledfeed"])
|
||||
var/choice = alert(user, "This button is to toggle your ability to be fed to others. Feeding predators is currently: [user.feeding ? "Allowed" : "Prevented"]", "", "Allow Feeding", "Cancel", "Prevent Feeding")
|
||||
switch(choice)
|
||||
if("Cancel")
|
||||
return FALSE
|
||||
if("Allow Feeding")
|
||||
user.feeding = TRUE
|
||||
if("Prevent Feeding")
|
||||
user.feeding = FALSE
|
||||
|
||||
if(user.client.prefs_vr)
|
||||
user.client.prefs_vr.feeding = user.feeding
|
||||
|
||||
//Refresh when interacted with, returning 1 makes vore_look.Topic update
|
||||
return TRUE
|
||||
@@ -0,0 +1,37 @@
|
||||
//The base hooks themselves
|
||||
|
||||
//New() hooks
|
||||
/hook/client_new
|
||||
|
||||
/hook/mob_new
|
||||
|
||||
/hook/living_new
|
||||
|
||||
/hook/carbon_new
|
||||
|
||||
/hook/human_new
|
||||
|
||||
/hook/simple_animal_new
|
||||
|
||||
//Hooks for interactions
|
||||
/hook/living_attackby
|
||||
|
||||
//
|
||||
//Hook helpers to expand hooks to others
|
||||
//
|
||||
/hook/mob_new/proc/chain_hooks(mob/M)
|
||||
var/result = 1
|
||||
if(isliving(M))
|
||||
if(!hook_vr("living_new",args))
|
||||
result = 0
|
||||
|
||||
if(iscarbon(M))
|
||||
if(!hook_vr("carbon_new",args))
|
||||
result = 0
|
||||
|
||||
if(ishuman(M))
|
||||
if(!hook_vr("human_new",args))
|
||||
result = 0
|
||||
|
||||
//Return 1 to superhook
|
||||
return result
|
||||
@@ -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
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// Gravity Pull effect which draws in movable objects to its center.
|
||||
// In this case, "number" refers to the range. directions is ignored.
|
||||
//
|
||||
/datum/effect/effect/system/grav_pull
|
||||
var/pull_radius = 3
|
||||
var/pull_anchored = 0
|
||||
var/break_windows = 0
|
||||
|
||||
/datum/effect/effect/system/grav_pull/set_up(range, num, loca)
|
||||
pull_radius = range
|
||||
number = num
|
||||
if(istype(loca, /turf/))
|
||||
location = loca
|
||||
else
|
||||
location = get_turf(loca)
|
||||
|
||||
/datum/effect/effect/system/grav_pull/start()
|
||||
spawn(0)
|
||||
if(holder)
|
||||
src.location = get_turf(holder)
|
||||
for(var/i=0, i < number, i++)
|
||||
do_pull()
|
||||
sleep(25)
|
||||
|
||||
/datum/effect/effect/system/grav_pull/proc/do_pull()
|
||||
//following is adapted from supermatter and singulo code
|
||||
if(defer_powernet_rebuild != 2)
|
||||
defer_powernet_rebuild = 1
|
||||
|
||||
// Let's just make this one loop.
|
||||
for(var/atom/X in orange(pull_radius, location))
|
||||
// Movable atoms only
|
||||
if(istype(X, /atom/movable))
|
||||
if(istype(X, /obj/effect/overlay)) continue
|
||||
if(X && !istype(X, /mob/living/carbon/human))
|
||||
if(break_windows && istype(X, /obj/structure/window)) //shatter windows
|
||||
var/obj/structure/window/W = X
|
||||
W.ex_act(2.0)
|
||||
|
||||
if(istype(X, /obj))
|
||||
var/obj/O = X
|
||||
if(O.anchored)
|
||||
if (!pull_anchored) continue // Don't pull anchored stuff unless configured
|
||||
step_towards(X, location) // step just once if anchored
|
||||
continue
|
||||
|
||||
step_towards(X, location) // Step twice
|
||||
step_towards(X, location)
|
||||
|
||||
else if(istype(X,/mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/H = X
|
||||
if(istype(H.shoes,/obj/item/clothing/shoes/magboots))
|
||||
var/obj/item/clothing/shoes/magboots/M = H.shoes
|
||||
if(M.magpulse)
|
||||
step_towards(H, location) //step just once with magboots
|
||||
continue
|
||||
step_towards(H, location) //step twice
|
||||
step_towards(H, location)
|
||||
|
||||
if(defer_powernet_rebuild != 2)
|
||||
defer_powernet_rebuild = 0
|
||||
return
|
||||
@@ -0,0 +1,33 @@
|
||||
// Micro Holders - Extends /obj/item/holder
|
||||
|
||||
/obj/item/holder/micro
|
||||
name = "micro"
|
||||
desc = "Another crewmember, small enough to fit in your hand."
|
||||
icon_state = "micro"
|
||||
slot_flags = SLOT_FEET | SLOT_HEAD | SLOT_ID
|
||||
w_class = 2
|
||||
item_icons = null // Override value from parent. We don't have magic sprites.
|
||||
pixel_y = 0 // Override value from parent.
|
||||
|
||||
/obj/item/holder/micro/examine(var/mob/user)
|
||||
for(var/mob/living/M in contents)
|
||||
M.examine(user)
|
||||
|
||||
/obj/item/holder/MouseDrop(mob/M as mob)
|
||||
..()
|
||||
if(M != usr) return
|
||||
if(usr == src) return
|
||||
if(!Adjacent(usr)) return
|
||||
if(istype(M,/mob/living/silicon/ai)) return
|
||||
for(var/mob/living/carbon/human/O in contents)
|
||||
O.show_inv(usr)
|
||||
|
||||
/obj/item/holder/micro/attack_self(var/mob/living/user)
|
||||
for(var/mob/living/carbon/human/M in contents)
|
||||
M.help_shake_act(user)
|
||||
|
||||
/obj/item/holder/micro/update_state()
|
||||
// If any items have been dropped by contained mob, drop them to floor.
|
||||
for(var/obj/O in contents)
|
||||
O.forceMove(get_turf(src))
|
||||
..()
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
//these aren't defines so they can stay in this file
|
||||
GLOBAL_VAR_CONST(SIZESCALE_HUGE, 2)
|
||||
GLOBAL_VAR_CONST(SIZESCALE_BIG, 1.5)
|
||||
GLOBAL_VAR_CONST(SIZESCALE_NORMAL, 1)
|
||||
GLOBAL_VAR_CONST(SIZESCALE_SMALL, 0.85)
|
||||
GLOBAL_VAR_CONST(SIZESCALE_TINY, 0.60)
|
||||
|
||||
GLOBAL_VAR_CONST(SIZESCALE_A_HUGEBIG, (GLOB.SIZESCALE_HUGE + GLOB.SIZESCALE_BIG) / 2)
|
||||
GLOBAL_VAR_CONST(SIZESCALE_A_BIGNORMAL, (GLOB.SIZESCALE_BIG + GLOB.SIZESCALE_NORMAL) / 2)
|
||||
GLOBAL_VAR_CONST(SIZESCALE_A_NORMALSMALL,(GLOB.SIZESCALE_NORMAL + GLOB.SIZESCALE_SMALL) / 2)
|
||||
GLOBAL_VAR_CONST(SIZESCALE_A_SMALLTINY,(GLOB.SIZESCALE_SMALL + GLOB.SIZESCALE_TINY) / 2)
|
||||
*/
|
||||
// Adding needed defines to /mob/living
|
||||
// Note: Polaris had this on /mob/living/carbon/human We need it higher up for animals and stuff.
|
||||
/mob/living
|
||||
var/size_multiplier = 1 //multiplier for the mob's icon size
|
||||
|
||||
// Define holder_type on types we want to be scoop-able
|
||||
//mob/living/carbon/human
|
||||
// holder_type = /obj/item/holder/micro
|
||||
|
||||
/**
|
||||
* Scale up the size of a mob's icon by the size_multiplier.
|
||||
* NOTE: mob/living/carbon/human/update_transform() has a more complicated system and
|
||||
* is already applying this transform. BUT, it does not call ..()
|
||||
* as long as that is true, we should be fine. If that changes we need to
|
||||
* re-evaluate.
|
||||
*/
|
||||
/mob/living/update_transform()
|
||||
ASSERT(!iscarbon(src))
|
||||
var/matrix/M = matrix()
|
||||
M.Scale(size_multiplier)
|
||||
M.Translate(0, 16*(size_multiplier-1))
|
||||
src.transform = M
|
||||
|
||||
/**
|
||||
* Get the effective size of a mob.
|
||||
* Currently this is based only on size_multiplier for micro/macro stuff,
|
||||
* but in the future we may also incorporate the "mob_size", so that
|
||||
* a macro mouse is still only effectively "normal" or a micro dragon is still large etc.
|
||||
*/
|
||||
/mob/living/proc/get_effective_size()
|
||||
return src.size_multiplier
|
||||
|
||||
/**
|
||||
* Resizes the mob immediately to the desired mod, animating it growing/shrinking.
|
||||
* It can be used by anything that calls it.
|
||||
*/
|
||||
/mob/living/proc/sizescale(var/new_size)
|
||||
var/matrix/sizescale = matrix() // Defines the matrix to change the player's size
|
||||
sizescale.Scale(new_size) //Change the size of the matrix
|
||||
|
||||
if(new_size >= SIZESCALE_NORMAL)
|
||||
sizescale.Translate(0, -1 * (1 - new_size) * 16) //Move the player up in the tile so their feet align with the bottom
|
||||
|
||||
animate(src, transform = sizescale, time = 5) //Animate the player resizing
|
||||
size_multiplier = new_size //Change size_multiplier so that other items can interact with them
|
||||
|
||||
/*
|
||||
* Verb proc for a command that lets players change their size OOCly.
|
||||
* Ace was here! Redid this a little so we'd use math for shrinking characters. This is the old code.
|
||||
|
||||
/mob/living/proc/set_size()
|
||||
set name = "Set Character Size"
|
||||
set category = "Vore"
|
||||
var/nagmessage = "DO NOT ABUSE THESE COMMANDS. They are not here for you to play with. \
|
||||
We were originally going to remove them but kept them for popular demand. \
|
||||
Do not abuse their existence outside of ERP scenes where they apply, \
|
||||
or reverting OOCly unwanted changes like someone lolshooting the crew with a shrink ray. -Ace"
|
||||
|
||||
var/size_name = input(nagmessage, "Pick a Size") in player_sizes_list
|
||||
if (size_name && player_sizes_list[size_name])
|
||||
src.sizescale(player_sizes_list[size_name])
|
||||
message_admins("[key_name(src)] used the sizescale command in-game to be [size_name]. \
|
||||
([src ? "<a href='?_src_=holder;adminplayerobservecoodjump=1;X=[src.x];Y=[src.y];Z=[src.z]'>JMP</a>" : "null"])")
|
||||
|
||||
/** Add the set_size() proc to usable verbs. */
|
||||
/hook/living_new/proc/sizescale_setup(mob/living/M)
|
||||
M.verbs += /mob/living/proc/set_size
|
||||
return 1
|
||||
|
||||
|
||||
* Attempt to scoop up this mob up into M's hands, if the size difference is large enough.
|
||||
* @return false if normal code should continue, 1 to prevent normal code.
|
||||
|
||||
/mob/living/proc/attempt_to_scoop(var/mob/living/carbon/human/M)
|
||||
if(!istype(M))
|
||||
return 0;
|
||||
if(M.buckled)
|
||||
usr << "<span class='notice'>You have to unbuckle \the [M] before you pick them up.</span>"
|
||||
return 0
|
||||
if(M.get_effective_size() - src.get_effective_size() >= 0.75)
|
||||
var/obj/item/holder/m_holder = get_scooped(M)
|
||||
if (m_holder)
|
||||
return 1
|
||||
else
|
||||
return 0; // Unable to scoop, let other code run
|
||||
*/
|
||||
/*
|
||||
* Handle bumping into someone with helping intent.
|
||||
* Called from /mob/living/Bump() in the 'brohugs all around' section.
|
||||
* @return false if normal code should continue, 1 to prevent normal code.
|
||||
* // TODO - can the now_pushing = 0 be moved up? What does it do anyway?
|
||||
*/
|
||||
/mob/living/proc/handle_micro_bump_helping(var/mob/living/tmob)
|
||||
if(src.get_effective_size() <= SIZESCALE_A_SMALLTINY && tmob.get_effective_size() <= SIZESCALE_A_SMALLTINY)
|
||||
// Both small! Go ahead and
|
||||
now_pushing = 0
|
||||
src.forceMove(tmob.loc)
|
||||
return 1
|
||||
if(abs(src.get_effective_size() - tmob.get_effective_size()) >= 0.20)
|
||||
now_pushing = 0
|
||||
src.forceMove(tmob.loc)
|
||||
|
||||
if(src.get_effective_size() > tmob.get_effective_size())
|
||||
/* var/mob/living/carbon/human/tmob = src
|
||||
if(istype(tmob) && istype(tmob.tail_style, /datum/sprite_accessory/tail/taur/naga))
|
||||
src << "You carefully slither around [tmob]."
|
||||
M << "[src]'s huge tail slithers past beside you!"
|
||||
else
|
||||
*/
|
||||
src.forceMove(tmob.loc)
|
||||
src << "You carefully step over [tmob]."
|
||||
tmob << "[src] steps over you carefully!"
|
||||
if(tmob.get_effective_size() > src.get_effective_size())
|
||||
/* var/mob/living/carbon/human/M = M
|
||||
if(istype(M) && istype(M.tail_style, /datum/sprite_accessory/tail/taur/naga))
|
||||
src << "You jump over [M]'s thick tail."
|
||||
M << "[src] bounds over your tail."
|
||||
else
|
||||
*/
|
||||
src.forceMove(tmob.loc)
|
||||
src << "You run between [tmob]'s legs."
|
||||
tmob << "[src] runs between your legs."
|
||||
return 1
|
||||
|
||||
/**
|
||||
* Handle bumping into someone without mutual help intent.
|
||||
* Called from /mob/living/Bump()
|
||||
* NW was here, adding even more options for stomping!
|
||||
*
|
||||
* @return false if normal code should continue, 1 to prevent normal code.
|
||||
*/
|
||||
/mob/living/proc/handle_micro_bump_other(var/mob/living/tmob)
|
||||
ASSERT(isliving(tmob)) // Baby don't hurt me
|
||||
|
||||
if(src.a_intent == "disarm" && src.canmove && !src.buckled)
|
||||
// If bigger than them by at least 0.75, move onto them and print message.
|
||||
if((src.get_effective_size() - tmob.get_effective_size()) >= 0.20)
|
||||
now_pushing = 0
|
||||
src.forceMove(tmob.loc)
|
||||
tmob.Stun(4)
|
||||
/*
|
||||
var/mob/living/carbon/human/H = src
|
||||
if(istype(H) && istype(H.tail_style, /datum/sprite_accessory/tail/taur/naga))
|
||||
src << "You carefully squish [tmob] under your tail!"
|
||||
tmob << "[src] pins you under their tail!"
|
||||
else
|
||||
*/
|
||||
src << "You pin [tmob] beneath your foot!"
|
||||
tmob << "[src] pins you beneath their foot!"
|
||||
return 1
|
||||
|
||||
if(src.a_intent == "harm" && src.canmove && !src.buckled)
|
||||
if((src.get_effective_size() - tmob.get_effective_size()) >= 0.20)
|
||||
now_pushing = 0
|
||||
src.forceMove(tmob.loc)
|
||||
tmob.adjustStaminaLoss(35)
|
||||
tmob.adjustBruteLoss(5)
|
||||
/* var/mob/living/carbon/human/M = src
|
||||
if(istype(M) && istype(M.tail_style, /datum/sprite_accessory/tail/taur/naga))
|
||||
src << "You steamroller over [tmob] with your heavy tail!"
|
||||
tmob << "[src] ploughs you down mercilessly with their heavy tail!"
|
||||
else
|
||||
*/
|
||||
src << "You bring your foot down heavily upon [tmob]!"
|
||||
tmob << "[src] steps carelessly on your body!"
|
||||
return 1
|
||||
|
||||
// until I figure out grabbing micros with the godawful pull code...
|
||||
if(src.a_intent == "grab" && src.canmove && !src.buckled)
|
||||
if((src.get_effective_size() - tmob.get_effective_size()) >= 0.20)
|
||||
now_pushing = 0
|
||||
tmob.adjustStaminaLoss(15)
|
||||
src.forceMove(tmob.loc)
|
||||
src << "You press [tmob] beneath your foot!"
|
||||
tmob << "[src] presses you beneath their foot!"
|
||||
/*
|
||||
var/mob/living/carbon/human/M = src
|
||||
if(istype(M) && !M.shoes)
|
||||
// User is a human (capable of scooping) and not wearing shoes! Scoop into foot slot!
|
||||
equip_to_slot_if_possible(tmob.get_scooped(M), slot_shoes, 0, 1)
|
||||
if(istype(M.tail_style, /datum/sprite_accessory/tail/taur/naga))
|
||||
src << "You wrap up [tmob] with your powerful tail!"
|
||||
tmob << "[src] binds you with their powerful tail!"
|
||||
else
|
||||
src << "You clench your toes around [tmob]'s body!"
|
||||
tmob << "[src] grabs your body with their toes!"
|
||||
else if(istype(M) && istype(M.tail_style, /datum/sprite_accessory/tail/taur/naga))
|
||||
src << "You carefully squish [tmob] under your tail!"
|
||||
tmob << "[src] pins you under their tail!"
|
||||
else
|
||||
src << "You pin [tmob] beneath your foot!"
|
||||
tmob << "[src] pins you beneath their foot!"
|
||||
return 1
|
||||
*/
|
||||
@@ -0,0 +1,115 @@
|
||||
|
||||
////////////////////////////
|
||||
/// shrinking serum ///
|
||||
////////////////////////////
|
||||
|
||||
/datum/reagent/medicine/macrocillin
|
||||
name = "Macrocillin"
|
||||
id = "macrocillin"
|
||||
description = "Glowing yellow liquid."
|
||||
reagent_state = LIQUID
|
||||
color = "#FFFF00" // rgb: 255, 255, 0
|
||||
overdose_threshold = 20
|
||||
|
||||
/datum/reagent/medicine/macrocillin/on_mob_life(mob/living/M, method=INGEST)
|
||||
for(var/size in list(SIZESCALE_SMALL, SIZESCALE_NORMAL, SIZESCALE_BIG, SIZESCALE_HUGE))
|
||||
if(M.size_multiplier < size)
|
||||
M.sizescale(size)
|
||||
M << "<font color='green'>You grow!</font>"
|
||||
break
|
||||
if(M.reagents.has_reagent("macrocillin"))
|
||||
M.reagents.remove_reagent("macrocillin", 20)
|
||||
..()
|
||||
|
||||
/datum/reagent/medicine/microcillin
|
||||
name = "Microcillin"
|
||||
id = "microcillin"
|
||||
description = "Murky purple liquid."
|
||||
reagent_state = LIQUID
|
||||
color = "#800080"
|
||||
overdose_threshold = 20
|
||||
|
||||
/datum/reagent/microcillin/on_mob_life(mob/living/M, method=INGEST)
|
||||
for(var/size in list(SIZESCALE_BIG, SIZESCALE_NORMAL, SIZESCALE_SMALL, SIZESCALE_TINY))
|
||||
if(M.size_multiplier > size)
|
||||
M.sizescale(size)
|
||||
M << "<span class='alert'>You shrink!</span>"
|
||||
break;
|
||||
if(M.reagents.has_reagent("microcillin"))
|
||||
M.reagents.remove_reagent("microcillin", 20)
|
||||
|
||||
..()
|
||||
|
||||
/datum/reagent/medicine/normalcillin
|
||||
name = "Normalcillin"
|
||||
id = "normalcillin"
|
||||
description = "Translucent cyan liquid."
|
||||
reagent_state = LIQUID
|
||||
color = "#00FFFF"
|
||||
overdose_threshold = 20
|
||||
|
||||
/datum/reagent/medicine/normalcillin/on_mob_life(mob/living/M, method=INGEST)
|
||||
if(M.size_multiplier > SIZESCALE_BIG)
|
||||
M.sizescale(SIZESCALE_BIG)
|
||||
M << "<span class='alert'>You shrink!</span>"
|
||||
else if(M.size_multiplier > SIZESCALE_NORMAL)
|
||||
M.sizescale(SIZESCALE_NORMAL)
|
||||
M << "<span class='alert'>You shrink!</span>"
|
||||
else if(M.size_multiplier < SIZESCALE_NORMAL)
|
||||
M.sizescale(SIZESCALE_NORMAL)
|
||||
M << "<font color='green'>You grow!</font>"
|
||||
else if(M.size_multiplier < SIZESCALE_SMALL)
|
||||
M.sizescale(SIZESCALE_SMALL)
|
||||
M << "<font color='green'>You grow!</font>"
|
||||
|
||||
if(M.reagents.has_reagent("normalcillin"))
|
||||
M.reagents.remove_reagent("normalcillin", 20)
|
||||
..()
|
||||
|
||||
|
||||
/datum/reagent/medicine/sizeoxadone
|
||||
name = "Sizeoxadone"
|
||||
id = "sizeoxadone"
|
||||
description = "A volatile liquid used as a precursor to size-altering chemicals. Causes dizziness if taken unprocessed."
|
||||
reagent_state = LIQUID
|
||||
color = "#1E90FF"
|
||||
overdose_threshold = 30
|
||||
metabolization_rate = 0.8 * REAGENTS_METABOLISM
|
||||
|
||||
/datum/reagent/sizeoxadone/on_mob_life(var/mob/living/carbon/M, var/removed)
|
||||
if(M.hallucination < volume && prob(20))
|
||||
M.hallucination += 5
|
||||
if(!M.confused) M.confused = 1
|
||||
M.confused = max(M.confused, 20)
|
||||
return
|
||||
|
||||
/datum/reagent/medicine/sizeoxadone/overdose_process(mob/living/M)
|
||||
M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1)
|
||||
M.adjustToxLoss(1)
|
||||
..()
|
||||
. = 1
|
||||
|
||||
////////////////////////// Anti-Noms Drugs //////////////////////////
|
||||
|
||||
/datum/reagent/medicine/ickypak
|
||||
name = "Ickypak"
|
||||
id = "ickypak"
|
||||
description = "A foul-smelling green liquid, for inducing muscle contractions to expel accidentally ingested things."
|
||||
reagent_state = LIQUID
|
||||
color = "#0E900E"
|
||||
metabolization_rate = 0.25 * REAGENTS_METABOLISM
|
||||
|
||||
/datum/reagent/medicine/ickypak/on_mob_life(var/mob/living/M, method=INGEST)
|
||||
..()
|
||||
if(M.hallucination < volume && prob(20))
|
||||
M.hallucination += 5
|
||||
M.adjustToxLoss(-5)
|
||||
|
||||
for(var/I in M.vore_organs)
|
||||
var/datum/belly/B = M.vore_organs[I]
|
||||
for(var/atom/movable/A in B.internal_contents)
|
||||
if(prob(55))
|
||||
playsound(M, 'sound/effects/splat.ogg', 50, 1)
|
||||
B.release_vore_contents(A)
|
||||
..()
|
||||
. = 1
|
||||
@@ -0,0 +1,169 @@
|
||||
//
|
||||
// Size Gun
|
||||
//
|
||||
/*
|
||||
/obj/item/gun/energy/sizegun
|
||||
name = "shrink ray"
|
||||
desc = "A highly advanced ray gun with two settings: Shrink and Grow. Warning: Do not insert into mouth."
|
||||
icon = 'icons/obj/gun_vr.dmi'
|
||||
icon_state = "sizegun-shrink100" // Someone can probably do better. -Ace
|
||||
item_state = null //so the human update icon uses the icon_state instead
|
||||
fire_sound = 'sound/weapons/wave.ogg'
|
||||
charge_cost = 100
|
||||
projectile_type = /obj/item/projectile/beam/shrinklaser
|
||||
modifystate = "sizegun-shrink"
|
||||
selfcharge = EGUN_SELFCHARGE
|
||||
firemodes = list(
|
||||
list(mode_name = "grow",
|
||||
projectile_type = /obj/item/projectile/beam/growlaser,
|
||||
modifystate = "sizegun-grow",
|
||||
fire_sound = 'sound/weapons/pulse3.ogg'
|
||||
),
|
||||
list(mode_name = "shrink",
|
||||
projectile_type = /obj/item/projectile/beam/shrinklaser,
|
||||
modifystate = "sizegun-shrink",
|
||||
fire_sound = 'sound/weapons/wave.ogg'
|
||||
))
|
||||
|
||||
//
|
||||
// Beams for size gun
|
||||
//
|
||||
// tracers TBD
|
||||
|
||||
/obj/item/projectile/beam/shrinklaser
|
||||
name = "shrink beam"
|
||||
icon_state = "xray"
|
||||
nodamage = 1
|
||||
damage = 0
|
||||
check_armour = "laser"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/xray/muzzle
|
||||
tracer_type = /obj/effect/projectile/xray/tracer
|
||||
impact_type = /obj/effect/projectile/xray/impact
|
||||
|
||||
/obj/item/projectile/beam/shrinklaser/on_hit(var/atom/target, var/blocked = 0)
|
||||
if(istype(target, /mob/living))
|
||||
var/mob/living/M = target
|
||||
switch(M.size_multiplier)
|
||||
if(SIZESCALE_HUGE to INFINITY)
|
||||
M.sizescale(SIZESCALE_BIG)
|
||||
if(SIZESCALE_BIG to SIZESCALE_HUGE)
|
||||
M.sizescale(SIZESCALE_NORMAL)
|
||||
if(SIZESCALE_NORMAL to SIZESCALE_BIG)
|
||||
M.sizescale(SIZESCALE_SMALL)
|
||||
if((0 - INFINITY) to SIZESCALE_NORMAL)
|
||||
M.sizescale(SIZESCALE_TINY)
|
||||
M.update_transform()
|
||||
return 1
|
||||
|
||||
/obj/item/projectile/beam/growlaser
|
||||
name = "growth beam"
|
||||
icon_state = "bluelaser"
|
||||
nodamage = 1
|
||||
damage = 0
|
||||
check_armour = "laser"
|
||||
|
||||
muzzle_type = /obj/effect/projectile/laser_blue/muzzle
|
||||
tracer_type = /obj/effect/projectile/laser_blue/tracer
|
||||
impact_type = /obj/effect/projectile/laser_blue/impact
|
||||
|
||||
/obj/item/projectile/beam/growlaser/on_hit(var/atom/target, var/blocked = 0)
|
||||
if(istype(target, /mob/living))
|
||||
var/mob/living/M = target
|
||||
switch(M.size_multiplier)
|
||||
if(SIZESCALE_BIG to SIZESCALE_HUGE)
|
||||
M.sizescale(SIZESCALE_HUGE)
|
||||
if(SIZESCALE_NORMAL to SIZESCALE_BIG)
|
||||
M.sizescale(SIZESCALE_BIG)
|
||||
if(SIZESCALE_SMALL to SIZESCALE_NORMAL)
|
||||
M.sizescale(SIZESCALE_NORMAL)
|
||||
if((0 - INFINITY) to SIZESCALE_TINY)
|
||||
M.sizescale(SIZESCALE_SMALL)
|
||||
M.update_transform()
|
||||
return 1
|
||||
*/
|
||||
|
||||
datum/design/sizeray
|
||||
name = "Size Ray"
|
||||
desc = "Abuse bluespace tech to alter living matter scale."
|
||||
id = "sizeray"
|
||||
build_type = PROTOLATHE
|
||||
materials = list(MAT_METAL = 1000, MAT_GLASS = 1000, MAT_DIAMOND = 2500, MAT_URANIUM = 2500, MAT_TITANIUM = 1000)
|
||||
build_path = /obj/item/gun/energy/laser/sizeray
|
||||
category = list("Weapons")
|
||||
|
||||
/obj/item/projectile/sizeray
|
||||
name = "sizeray beam"
|
||||
icon_state = "omnilaser"
|
||||
hitsound = null
|
||||
damage = 0
|
||||
damage_type = STAMINA
|
||||
flag = "laser"
|
||||
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
|
||||
|
||||
/obj/item/projectile/sizeray/shrinkray
|
||||
icon_state="bluelaser"
|
||||
|
||||
/obj/item/projectile/sizeray/growthray
|
||||
icon_state="laser"
|
||||
|
||||
/obj/item/projectile/sizeray/shrinkray/on_hit(var/atom/target, var/blocked = 0)
|
||||
if(istype(target, /mob/living))
|
||||
var/mob/living/M = target
|
||||
switch(M.size_multiplier)
|
||||
if(SIZESCALE_HUGE to INFINITY)
|
||||
M.sizescale(SIZESCALE_BIG)
|
||||
if(SIZESCALE_BIG to SIZESCALE_HUGE)
|
||||
M.sizescale(SIZESCALE_NORMAL)
|
||||
if(SIZESCALE_NORMAL to SIZESCALE_BIG)
|
||||
M.sizescale(SIZESCALE_SMALL)
|
||||
if((0 - INFINITY) to SIZESCALE_NORMAL)
|
||||
M.sizescale(SIZESCALE_TINY)
|
||||
M.update_transform()
|
||||
return 1
|
||||
|
||||
/obj/item/projectile/sizeray/growthray/on_hit(var/atom/target, var/blocked = 0)
|
||||
if(istype(target, /mob/living))
|
||||
var/mob/living/M = target
|
||||
switch(M.size_multiplier)
|
||||
if(SIZESCALE_BIG to SIZESCALE_HUGE)
|
||||
M.sizescale(SIZESCALE_HUGE)
|
||||
if(SIZESCALE_NORMAL to SIZESCALE_BIG)
|
||||
M.sizescale(SIZESCALE_BIG)
|
||||
if(SIZESCALE_SMALL to SIZESCALE_NORMAL)
|
||||
M.sizescale(SIZESCALE_NORMAL)
|
||||
if((0 - INFINITY) to SIZESCALE_TINY)
|
||||
M.sizescale(SIZESCALE_SMALL)
|
||||
M.update_transform()
|
||||
return 1
|
||||
|
||||
/obj/item/ammo_casing/energy/laser/growthray
|
||||
projectile_type = /obj/item/projectile/sizeray/growthray
|
||||
select_name = "Growth"
|
||||
|
||||
/obj/item/ammo_casing/energy/laser/shrinkray
|
||||
projectile_type = /obj/item/projectile/sizeray/shrinkray
|
||||
select_name = "Shrink"
|
||||
|
||||
|
||||
//Gun here
|
||||
/obj/item/gun/energy/laser/sizeray
|
||||
name = "size ray"
|
||||
icon_state = "bluetag"
|
||||
desc = "Size manipulator using bluespace breakthroughs."
|
||||
item_state = null //so the human update icon uses the icon_state instead.
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/laser/shrinkray, /obj/item/ammo_casing/energy/laser/growthray)
|
||||
selfcharge = EGUN_SELFCHARGE
|
||||
charge_delay = 5
|
||||
ammo_x_offset = 2
|
||||
clumsy_check = 1
|
||||
|
||||
attackby(obj/item/W, mob/user)
|
||||
if(W==src)
|
||||
if(icon_state=="bluetag")
|
||||
icon_state="redtag"
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/laser/growthray)
|
||||
else
|
||||
icon_state="bluetag"
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/laser/shrinkray)
|
||||
return ..()
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
This file is for jamming single-line procs into Polaris procs.
|
||||
It will prevent runtimes and allow their code to run if these fail.
|
||||
It will also log when we mess up our code rather than making it vague.
|
||||
|
||||
Call it at the top of a stock proc with...
|
||||
|
||||
if(attempt_vr(object,proc to call,args)) return
|
||||
|
||||
...if you are replacing an entire proc.
|
||||
|
||||
The proc you're attemping should return nonzero values on success.
|
||||
*/
|
||||
|
||||
/proc/attempt_vr(callon, procname, list/args=null)
|
||||
try
|
||||
if(!callon || !procname)
|
||||
CRASH("attempt_vr: Invalid obj/proc: [callon]/[procname]")
|
||||
return 0
|
||||
|
||||
var/result = call(callon,procname)(arglist(args))
|
||||
|
||||
return result
|
||||
|
||||
catch(var/exception/e)
|
||||
CRASH("attempt_vr runtimed when calling [procname] on [callon].")
|
||||
CRASH("attempt_vr catch: [e] on [e.file]:[e.line]")
|
||||
return 0
|
||||
|
||||
/*
|
||||
This is the _vr version of calling hooks.
|
||||
It's meant to have different messages, and also the try/catch block.
|
||||
For when you want hooks and want to know when you ruin everything,
|
||||
vs when Polaris ruins everything.
|
||||
|
||||
Call it at the top of a stock proc with...
|
||||
|
||||
if(hook_vr(proc,args)) return
|
||||
|
||||
...if you are replacing an entire proc.
|
||||
|
||||
The hooks you're calling should return nonzero values on success.
|
||||
*/
|
||||
/proc/hook_vr(hook, list/args=null)
|
||||
try
|
||||
var/hook_path = text2path("/hook/[hook]")
|
||||
if(!hook_path)
|
||||
CRASH("hook_vr: Invalid hook '/hook/[hook]' called.")
|
||||
return 0
|
||||
|
||||
var/caller = new hook_path
|
||||
var/status = 1
|
||||
for(var/P in typesof("[hook_path]/proc"))
|
||||
if(!call(caller, P)(arglist(args)))
|
||||
CRASH("hook_vr: Hook '[P]' failed or runtimed.")
|
||||
status = 0
|
||||
|
||||
return status
|
||||
|
||||
catch(var/exception/e)
|
||||
CRASH("hook_vr itself failed or runtimed. Exception below.")
|
||||
CRASH("hook_vr catch: [e] on [e.file]:[e.line]")
|
||||
Reference in New Issue
Block a user