Ashdrake noms (#2868)

* Initial commit

* probably a better way to check this

* It compiled boys. Let's see if it works.

* IT WAS ALL NULLS, BOYS

* Hooray, it's not broken anymore boys!

* Well it works, but we need transfer IDs to work

locating them post-initialize would help, probably. Then assigning their
links soon after.

* Bellies linked, autotransfer, LoseTarget

* half-thought sanity check needs to go away

* A few last minute tweaks.
This commit is contained in:
Poojawa
2017-09-21 06:29:08 -05:00
committed by GitHub
parent 5b38d23bfd
commit aec48daa0a
11 changed files with 239 additions and 39 deletions

View File

@@ -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

View File

@@ -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()

View File

@@ -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()
. = ..()

View File

@@ -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

View File

@@ -89,11 +89,15 @@
. = ..()
if(. && isliving(target))
var/mob/living/L = target
if(L.stat >= SOFT_CRIT)
if(vore_active == TRUE && L.devourable == TRUE)
dragon_feeding(src,L)
else if(L.stat == DEAD)
devour(L)
else
if(L.stat != DEAD)
if(!client && ranged && ranged_cooldown <= world.time)
OpenFire()
else
devour(L)
/mob/living/simple_animal/hostile/megafauna/proc/devour(mob/living/L)
if(!L)

View File

@@ -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

View File

@@ -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/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(
@@ -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, "<span class='warning'><B>[src.inside_flavor]</B></span>")
if(isliving(prey))
var/mob/living/M = prey
if(can_taste && M.get_taste_message(0))
to_chat(owner, "<span class='notice'>[M] tastes of [M.get_taste_message(0)].</span>")
// Setup the autotransfer checks if needed
if(transferlocation && autotransferchance > 0)
addtimer(CALLBACK(src, /datum/belly/.proc/check_autotransfer, prey), autotransferwait)
/datum/belly/proc/check_autotransfer(var/mob/prey)
// Some sanity checks
if(transferlocation && (autotransferchance > 0) && (prey in internal_contents))
if(prob(autotransferchance))
// Double check transferlocation isn't insane
if(verify_transferlocation())
transfer_contents(prey, transferlocation)
else
// Didn't transfer, so wait before retrying
addtimer(CALLBACK(src, /datum/belly/.proc/check_autotransfer, prey), autotransferwait)
/datum/belly/proc/verify_transferlocation()
for(var/I in owner.vore_organs)
var/datum/belly/B = owner.vore_organs[I]
if(B == transferlocation)
return TRUE
for(var/I in owner.vore_organs)
var/datum/belly/B = owner.vore_organs[I]
if(B.name == transferlocation.name)
transferlocation = B
return TRUE
return FALSE
// Get the line that should show up in Examine message if the owner of this belly
// is examined. By making this a proc, we not only take advantage of polymorphism,
@@ -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, "<span class='warning'>Something went wrong with your belly transfer settings.</span>")
transferlocation = null
return
@@ -358,18 +383,14 @@
to_chat(R, "<span class='warning'>But make no progress in escaping [owner]'s [name].</span>")
to_chat(owner, "<span class='warning'>But appears to be unable to make any progress in escaping your [name].</span>")
return
//Transfers contents from one belly to another
/datum/belly/proc/transfer_contents(var/atom/movable/content, var/datum/belly/target, silent = 0)
if(!(content in internal_contents))
return
internal_contents.Remove(content)
target.internal_contents.Add(content)
if(isliving(content))
var/mob/living/M = content
if(target.inside_flavor)
to_chat(M, "<span class='notice'><B>[target.inside_flavor]</B></span>")
if(target.can_taste && M.get_taste_message(0))
to_chat(owner, "<span class='notice'>[M] tastes of [M.get_taste_message(0)].</span>")
// 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

View File

@@ -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, "<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>")
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

View File

@@ -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("<span class='warning'>[] starts to [] [] into their []!</span>",pred,lowertext(belly_target.vore_verb),prey,lowertext(belly_target.name))
success_msg = text("<span class='warning'>[] manages to [] [] into their []!</span>",pred,lowertext(belly_target.vore_verb),prey,lowertext(belly_target.name))
// 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()

View File

@@ -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.

View File

@@ -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"