diff --git a/code/__DEFINES/voreconstants.dm b/code/__DEFINES/voreconstants.dm
index ea4c26d79e..edcf4f7fd2 100644
--- a/code/__DEFINES/voreconstants.dm
+++ b/code/__DEFINES/voreconstants.dm
@@ -3,6 +3,7 @@
#define DM_DIGEST "Digest"
#define DM_HEAL "Heal"
#define DM_NOISY "Noisy"
+#define DM_DRAGON "Dragon"
#define VORE_STRUGGLE_EMOTE_CHANCE 40
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index 866f9c995a..4b5789f877 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -228,6 +228,9 @@
if(!target || !CanAttack(target))
LoseTarget()
return 0
+ if(isturf(target.loc))
+ LoseTarget()
+ return 0
if(target in possible_targets)
if(target.z != z)
LoseTarget()
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm
index c8272ca65e..d1ddfa9a00 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm
@@ -60,6 +60,7 @@ Difficulty: Medium
score_type = DRAKE_SCORE
deathmessage = "collapses into a pile of bones, its flesh sloughing away."
death_sound = 'sound/magic/demon_dies.ogg'
+ no_vore = FALSE
/mob/living/simple_animal/hostile/megafauna/dragon/Initialize()
. = ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/dragon_vore.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon_vore.dm
new file mode 100644
index 0000000000..898a2ad734
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon_vore.dm
@@ -0,0 +1,50 @@
+/mob/living/simple_animal/hostile/megafauna/dragon
+ vore_active = TRUE
+
+/mob/living/simple_animal/hostile/megafauna/dragon/Initialize()
+ // Create and register 'stomachs'
+ var/datum/belly/megafauna/dragon/maw/maw = new(src)
+ var/datum/belly/megafauna/dragon/gullet/gullet = new(src)
+ var/datum/belly/megafauna/dragon/gut/gut = new(src)
+ for(var/datum/belly/X in list(maw, gullet, gut))
+ vore_organs[X.name] = X
+ // Connect 'stomachs' together
+ maw.transferlocation = gullet
+ gullet.transferlocation = gut
+ vore_selected = maw.name // NPC eats into maw
+ return ..()
+
+/datum/belly/megafauna/dragon
+ human_prey_swallow_time = 50 // maybe enough to switch targets if distracted
+ nonhuman_prey_swallow_time = 50
+
+/datum/belly/megafauna/dragon/maw
+ name = "maw"
+ inside_flavor = "The maw of the dreaded Ash drake closes around you, engulfing you into a swelteringly hot, disgusting enviroment. The acidic saliva tingles over your form while that tongue pushes you further back...towards the dark gullet beyond."
+ vore_verb = "scoop"
+ vore_sound = 'sound/vore/pred/taurswallow.ogg'
+ swallow_time = 20
+ escapechance = 25
+ // From above, will transfer into gullet
+ transferchance = 25
+ autotransferchance = 66
+ autotransferwait = 200
+
+/datum/belly/megafauna/dragon/gullet
+ name = "gullet"
+ inside_flavor = "A ripple of muscle and arching of the tongue pushes you down like any other food. No choice in the matter, you're simply consumed. The dark ambiance of the outside world is replaced with working, wet flesh. Your only light being what you brought with you."
+ swallow_time = 60 // costs extra time to eat directly to here
+ escapechance = 5
+ // From above, will transfer into gut
+ transferchance = 25
+ autotransferchance = 50
+ autotransferwait = 200
+
+/datum/belly/megafauna/dragon/gut
+ name = "stomach"
+ vore_capacity = 5 //I doubt this many people will actually last in the gut, but...
+ inside_flavor = "With a rush of burning ichor greeting you, you're introduced to the Drake's stomach. Wrinkled walls greedily grind against you, acidic slimes working into your body as you become fuel and nutriton for a superior predator. All that's left is your body's willingness to resist your destiny."
+ digest_mode = DM_DRAGON
+ digest_burn = 5
+ swallow_time = 100 // costs extra time to eat directly to here
+ escapechance = 0
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
index dfbdace1f2..5f45d47494 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
@@ -89,11 +89,15 @@
. = ..()
if(. && isliving(target))
var/mob/living/L = target
- if(L.stat != DEAD)
- if(!client && ranged && ranged_cooldown <= world.time)
- OpenFire()
+ if(L.stat >= SOFT_CRIT)
+ if(vore_active == TRUE && L.devourable == TRUE)
+ dragon_feeding(src,L)
+ else if(L.stat == DEAD)
+ devour(L)
else
- devour(L)
+ if(L.stat != DEAD)
+ if(!client && ranged && ranged_cooldown <= world.time)
+ OpenFire()
/mob/living/simple_animal/hostile/megafauna/proc/devour(mob/living/L)
if(!L)
diff --git a/code/modules/mob/living/simple_animal/simple_animal_vr.dm b/code/modules/mob/living/simple_animal/simple_animal_vr.dm
index 1ba77aa67f..2eca841c17 100644
--- a/code/modules/mob/living/simple_animal/simple_animal_vr.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal_vr.dm
@@ -4,15 +4,15 @@
devourable = FALSE //insurance because who knows.
var/vore_active = FALSE // If vore behavior is enabled for this mob
- var/vore_capacity = 1 // The capacity (in people) this person can hold
var/vore_default_mode = DM_DIGEST // Default bellymode (DM_DIGEST, DM_HOLD, DM_ABSORB)
var/vore_digest_chance = 25 // Chance to switch to digest mode if resisted
- var/vore_absorb_chance = 0 // Chance to switch to absorb mode if resisted
var/vore_escape_chance = 25 // Chance of resisting out of mob
var/vore_stomach_name // The name for the first belly if not "stomach"
var/vore_stomach_flavor // The flavortext for the first belly if not the default
+ var/vore_fullness = 0 // How "full" the belly is (controls icons)
+
// Release belly contents beforey being gc'd!
/mob/living/simple_animal/Destroy()
@@ -22,6 +22,37 @@
prey_excludes.Cut()
. = ..()
+
+// 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))
+ return FALSE // Nothing's inside
+ new_fullness += M
+
+ vore_fullness = new_fullness
+
+
+/mob/living/simple_animal/proc/swallow_check()
+ for(var/I in vore_organs)
+ var/datum/belly/B = vore_organs[I]
+ if(vore_active)
+ update_fullness()
+ if(!vore_fullness)
+ // Nothing
+ return
+ else
+ addtimer(CALLBACK(src, .proc/swallow_mob), B.swallow_time)
+
+/mob/living/simple_animal/proc/swallow_mob()
+ for(var/I in vore_organs)
+ var/datum/belly/B = vore_organs[I]
+ for(var/mob/living/M in B.internal_contents)
+ B.transfer_contents(M, B.transferlocation)
+
+
/mob/living/simple_animal/death()
for(var/I in vore_organs)
var/datum/belly/B = vore_organs[I]
@@ -69,4 +100,3 @@
"The stomach glorps and gurgles as it tries to work you into slop.")
src.vore_organs[B.name] = B
src.vore_selected = B.name
-
diff --git a/code/modules/vore/eating/belly_vr.dm b/code/modules/vore/eating/belly_vr.dm
index 1a181efa58..4262d9fecd 100644
--- a/code/modules/vore/eating/belly_vr.dm
+++ b/code/modules/vore/eating/belly_vr.dm
@@ -15,7 +15,7 @@
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 = 300 // How long between stomach emotes at prey
+ 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
@@ -25,15 +25,21 @@
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/transferchance = 0 // % Chance of prey being
- var/can_taste = FALSE // If this belly prints the flavor of prey when it eats someone.
+
var/datum/belly/transferlocation = null // Location that the prey is released if they struggle and get dropped off.
- var/tmp/digest_mode = DM_HOLD // Whether or not to digest. Default to not digest.
+ 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/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(
@@ -153,8 +159,41 @@
// if(B.silenced == FALSE) //this needs more testing later
prey << sound('sound/vore/prey/loop.ogg', repeat = 1, wait = 0, volume = 35, channel = CHANNEL_PREYLOOP)
+ // Handle prey messages
if(inside_flavor)
to_chat(prey, "[src.inside_flavor]")
+ if(isliving(prey))
+ var/mob/living/M = prey
+ if(can_taste && M.get_taste_message(0))
+ to_chat(owner, "[M] tastes of [M.get_taste_message(0)].")
+
+ // Setup the autotransfer checks if needed
+ if(transferlocation && autotransferchance > 0)
+ addtimer(CALLBACK(src, /datum/belly/.proc/check_autotransfer, prey), autotransferwait)
+
+/datum/belly/proc/check_autotransfer(var/mob/prey)
+ // Some sanity checks
+ if(transferlocation && (autotransferchance > 0) && (prey in internal_contents))
+ if(prob(autotransferchance))
+ // Double check transferlocation isn't insane
+ if(verify_transferlocation())
+ transfer_contents(prey, transferlocation)
+ else
+ // Didn't transfer, so wait before retrying
+ addtimer(CALLBACK(src, /datum/belly/.proc/check_autotransfer, prey), autotransferwait)
+
+/datum/belly/proc/verify_transferlocation()
+ for(var/I in owner.vore_organs)
+ var/datum/belly/B = owner.vore_organs[I]
+ if(B == transferlocation)
+ return TRUE
+
+ for(var/I in owner.vore_organs)
+ var/datum/belly/B = owner.vore_organs[I]
+ if(B.name == transferlocation.name)
+ transferlocation = B
+ return TRUE
+ return FALSE
// Get the line that should show up in Examine message if the owner of this belly
// is examined. By making this a proc, we not only take advantage of polymorphism,
@@ -323,23 +362,9 @@
return
else if(prob(transferchance) && istype(transferlocation)) //Next, let's have it see if they end up getting into an even bigger mess then when they started.
- var/location_found = FALSE
- var/name_found = FALSE
- for(var/I in owner.vore_organs)
- var/datum/belly/B = owner.vore_organs[I]
- if(B == transferlocation)
- location_found = TRUE
- break
+ var/location_ok = verify_transferlocation()
- if(!location_found)
- for(var/I in owner.vore_organs)
- var/datum/belly/B = owner.vore_organs[I]
- if(B.name == transferlocation.name)
- name_found = TRUE
- transferlocation = B
- break
-
- if(!location_found && !name_found)
+ if(!location_ok)
to_chat(owner, "Something went wrong with your belly transfer settings.")
transferlocation = null
return
@@ -358,18 +383,14 @@
to_chat(R, "But make no progress in escaping [owner]'s [name].")
to_chat(owner, "But appears to be unable to make any progress in escaping your [name].")
return
+
//Transfers contents from one belly to another
/datum/belly/proc/transfer_contents(var/atom/movable/content, var/datum/belly/target, silent = 0)
if(!(content in internal_contents))
return
internal_contents.Remove(content)
- target.internal_contents.Add(content)
- if(isliving(content))
- var/mob/living/M = content
- if(target.inside_flavor)
- to_chat(M, "[target.inside_flavor]")
- if(target.can_taste && M.get_taste_message(0))
- to_chat(owner, "[M] tastes of [M.get_taste_message(0)].")
+ // Re-use nom_mob
+ target.nom_mob(content, target.owner)
if(!silent)
for(var/mob/hearer in range(1,owner))
hearer << sound(target.vore_sound,volume=80)
@@ -398,6 +419,8 @@
dupe.escapechance = escapechance
dupe.transferchance = transferchance
dupe.transferlocation = transferlocation
+ dupe.autotransferchance = autotransferchance
+ dupe.autotransferwait = autotransferwait
//// Object-holding variables
//struggle_messages_outside - strings
diff --git a/code/modules/vore/eating/bellymodes_vr.dm b/code/modules/vore/eating/bellymodes_vr.dm
index 901c1ca56f..ba5fa1e295 100644
--- a/code/modules/vore/eating/bellymodes_vr.dm
+++ b/code/modules/vore/eating/bellymodes_vr.dm
@@ -91,3 +91,53 @@
playsound(get_turf(owner),"digest_pred",35,0,-6,0,channel=CHANNEL_PRED)
M.stop_sound_channel(CHANNEL_PRED)
M.playsound_local(get_turf(M), null, 45, S = prey_gurgle)
+
+
+//////////////////////////DM_DRAGON /////////////////////////////////////
+//because dragons need snowflake guts
+ if(digest_mode == DM_DRAGON)
+ for (var/mob/living/M in internal_contents)
+ if(prob(25))
+ M.stop_sound_channel(CHANNEL_PRED)
+ playsound(get_turf(owner),"digest_pred",50,0,-6,0,channel=CHANNEL_PRED)
+ M.stop_sound_channel(CHANNEL_PRED)
+ M.playsound_local(get_turf(M), null, 45, S = prey_gurgle)
+
+ //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, "[digest_alert_owner]")
+ to_chat(M, "[digest_alert_prey]")
+ M.visible_message("You watch as [owner]'s guts loudly rumble as it finishes off a meal.")
+
+ M.stop_sound_channel(CHANNEL_PRED)
+ playsound(get_turf(owner),"death_pred",45,0,-6,0,channel=CHANNEL_PRED)
+ M.stop_sound_channel(CHANNEL_PRED)
+ M.playsound_local(get_turf(M), null, 45, S = prey_digest)
+ M.spill_organs(FALSE,TRUE,TRUE)
+ M << sound(null, repeat = 0, wait = 0, volume = 80, channel = CHANNEL_PREYLOOP)
+ digestion_death(M)
+ owner.update_icons()
+ continue
+
+
+ // Deal digestion damage (and feed the pred)
+ if(!(M.status_flags & GODMODE))
+ M.adjustFireLoss(digest_burn)
+ M.adjustToxLoss(4) // something something plasma based acids
+ M.adjustCloneLoss(3) // eventually this'll kill you if you're healing everything else, you nerds.
+ return
\ No newline at end of file
diff --git a/code/modules/vore/eating/living_vr.dm b/code/modules/vore/eating/living_vr.dm
index 60206a43f1..d50cdebf45 100644
--- a/code/modules/vore/eating/living_vr.dm
+++ b/code/modules/vore/eating/living_vr.dm
@@ -121,6 +121,42 @@
var/belly = input("Choose Belly") in pred.vore_organs
return perform_the_nom(user, prey, pred, belly)
+//Dragon noms need to be faster
+/mob/living/proc/dragon_feeding(var/mob/living/user, var/mob/living/prey)
+ var/belly = user.vore_selected
+ return perform_dragon(user, prey, user, belly)
+
+/mob/living/proc/perform_dragon(var/mob/living/user, var/mob/living/prey, var/mob/living/pred, var/belly, swallow_time = 20)
+ //Sanity
+ if(!user || !prey || !pred || !belly || !(belly in pred.vore_organs))
+ return
+
+ // The belly selected at the time of noms
+ var/datum/belly/belly_target = pred.vore_organs[belly]
+ var/attempt_msg = "ERROR: Vore message couldn't be created. Notify a dev. (at)"
+ var/success_msg = "ERROR: Vore message couldn't be created. Notify a dev. (sc)"
+
+ // Prepare messages
+ if(user == pred) //Feeding someone to yourself
+ attempt_msg = text("[] starts to [] [] into their []!",pred,lowertext(belly_target.vore_verb),prey,lowertext(belly_target.name))
+ success_msg = text("[] manages to [] [] into their []!",pred,lowertext(belly_target.vore_verb),prey,lowertext(belly_target.name))
+
+ // Announce that we start the attempt!
+ user.visible_message(attempt_msg)
+
+ if(!do_mob(src, user, swallow_time)) // one second should be good enough, right?
+ return FALSE // Prey escaped (or user disabled) before timer expired.
+
+ // If we got this far, nom successful! Announce it!
+ user.visible_message(success_msg)
+ playsound(user, belly_target.vore_sound, 100, 1)
+
+ // Actually shove prey into the belly.
+ belly_target.nom_mob(prey, user)
+ if (pred == user)
+ message_admins("[key_name(pred)] ate [key_name(prey)].")
+ log_attack("[key_name(pred)] ate [key_name(prey)]")
+ return TRUE
//
// Master vore proc that actually does vore procedures
//
@@ -250,6 +286,7 @@
if(M.loc != src)
B.internal_contents.Remove(M)
+
// OOC Escape code for pref-breaking or AFK preds
//
/mob/living/proc/escapeOOC()
diff --git a/code/modules/vore/eating/vorepanel_vr.dm b/code/modules/vore/eating/vorepanel_vr.dm
index ebbc2f371c..0f01a4c9e3 100644
--- a/code/modules/vore/eating/vorepanel_vr.dm
+++ b/code/modules/vore/eating/vorepanel_vr.dm
@@ -27,7 +27,7 @@
//
/datum/vore_look
var/datum/belly/selected
- var/show_interacts = FALSE
+ var/show_interacts = TRUE
var/datum/browser/popup
var/loop = null; // Magic self-reference to stop the handler from being GC'd before user takes action.
diff --git a/tgstation.dme b/tgstation.dme
index b1fd32e5c7..d852af48c0 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1823,6 +1823,7 @@
#include "code\modules\mob\living\simple_animal\hostile\megafauna\bubblegum.dm"
#include "code\modules\mob\living\simple_animal\hostile\megafauna\colossus.dm"
#include "code\modules\mob\living\simple_animal\hostile\megafauna\dragon.dm"
+#include "code\modules\mob\living\simple_animal\hostile\megafauna\dragon_vore.dm"
#include "code\modules\mob\living\simple_animal\hostile\megafauna\hierophant.dm"
#include "code\modules\mob\living\simple_animal\hostile\megafauna\legion.dm"
#include "code\modules\mob\living\simple_animal\hostile\megafauna\megafauna.dm"