Refactors vore code to allow for instant-vore that does not sleep (#18219)

Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
Cameron Lennox
2025-08-15 20:54:27 -04:00
committed by GitHub
parent 99e71ef15e
commit a0e66075a6
13 changed files with 237 additions and 131 deletions
@@ -343,7 +343,7 @@
m.drop_from_inventory(S.OurRig)
if(wearer && devour) //We're being worn. Engulf em', if prefs align.. otherwise just drop off.
if(P.can_be_drop_pred && wearer.devourable && wearer.can_be_drop_prey && P.vore_selected)
perform_the_nom(P,wearer,P,P.vore_selected,-1)
begin_instant_nom(P,wearer,P,P.vore_selected)
else
to_chat(P, span_vwarning("You can't assimilate your current host."))
P.forceMove(get_turf(S.OurRig))
@@ -84,6 +84,10 @@
. = ..()
movement_cooldown = 10
/mob/living/simple_mob/vore/alienanimals/dustjumper/begin_instant_nom(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly)
. = ..()
movement_cooldown = 10
/datum/ai_holder/simple_mob/melee/evasive/dustjumper
hostile = FALSE
retaliate = TRUE
@@ -152,6 +152,10 @@
return //If it can do normal vore mechanics, it can carry players to the OM,
//and release them there. I think that's probably a bad idea.
/mob/living/simple_mob/vore/overmap/stardog/begin_instant_nom(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly)
to_chat(src, span_warning("You can't do that."))
return
/mob/living/simple_mob/vore/overmap/stardog/Initialize(mapload)
. = ..()
child_om_marker.set_light(5, 1, "#ff8df5")
@@ -813,24 +813,38 @@ GLOBAL_VAR_INIT(teppi_count, 0) // How mant teppi DO we have?
/mob/living/simple_mob/vore/alienanimals/teppi/perform_the_nom(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly, delay_time)
if(!client)
var/teppi_checks = teppi_checks(user, prey, pred, belly)
if(teppi_checks)
belly = teppi_checks
ai_holder.set_busy(TRUE)
prey.stop_pulling()
..()
if(!client)
ai_holder.set_busy(FALSE)
/mob/living/simple_mob/vore/alienanimals/teppi/begin_instant_nom(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly)
if(!client)
var/teppi_checks = teppi_checks(user, prey, pred, belly)
if(teppi_checks)
belly = teppi_checks
ai_holder.set_busy(TRUE)
prey.stop_pulling()
..()
if(!client)
ai_holder.set_busy(FALSE)
///Retrns the belly we'll be using if we are friends.
/mob/living/simple_mob/vore/alienanimals/teppi/proc/teppi_checks(mob/living/user, mob/living/prey, mob/living/pred)
if(!pred)
pred = user
if(client)
return ..()
var/current_affinity = affinity[prey.real_name]
ai_holder.set_busy(TRUE)
prey.stop_pulling()
if(current_affinity >= 50)
belly = friend_zone
..()
ai_holder.set_busy(FALSE)
return
return friend_zone
if(current_affinity <= -50)
vore_selected.digest_mode = DM_DIGEST
else
vore_selected.digest_mode = DM_DRAIN
..()
ai_holder.set_busy(FALSE)
//Instead of copying this everywhere let's just make a proc
/mob/living/simple_mob/vore/alienanimals/teppi/proc/lets_eat(person)
@@ -77,7 +77,7 @@
if(L in eaten_mobs)
return
if(L.devourable && L.allowmobvore && (src.vore_fullness < src.vore_capacity))
perform_the_nom(src,L,src,src.vore_selected,-1)
begin_instant_nom(src,L,src,src.vore_selected)
eaten_mobs += L
+1 -113
View File
@@ -678,118 +678,6 @@
var/belly = tgui_input_list(user, "Choose Belly", "Belly Choice", pred.feedable_bellies())
return perform_the_nom(user, prey, pred, belly)
//
// Master vore proc that actually does vore procedures
//
/mob/living/proc/perform_the_nom(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly, delay_time)
//Sanity
if(!user || !prey || !pred || !istype(belly) || !(belly in pred.vore_organs))
log_debug("[user] attempted to feed [prey] to [pred], via [belly ? lowertext(belly.name) : "*null*"] but it went wrong.")
return FALSE
if(pred == prey)
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)"
//Final distance check. Time has passed, menus have come and gone. Can't use do_after adjacent because doesn't behave for held micros
var/user_to_pred = get_dist(get_turf(user),get_turf(pred))
var/user_to_prey = get_dist(get_turf(user),get_turf(prey))
if(user == pred && isAI(user))
var/mob/living/silicon/ai/AI = user
if(AI.holo && AI.holo.masters[AI])
user_to_prey = get_dist(get_turf(AI.holo.masters[AI]), get_turf(prey))
if(user_to_pred > 1 || user_to_prey > 1)
return FALSE
if(!prey.devourable)
to_chat(user, span_vnotice("They aren't able to be devoured."))
log_and_message_admins("attempted to devour [key_name_admin(prey)] against their prefs ([prey ? ADMIN_JMP(prey) : "null"])", src)
return FALSE
if(prey.absorbed || pred.absorbed)
to_chat(user, span_vwarning("They aren't aren't in a state to be devoured."))
return FALSE
//Determining vore attempt privacy
var/message_range = world.view
if(!pred.is_slipping && !prey.is_slipping) //We only care about privacy preference if it's NOT a spontaneous vore.
switch(belly.eating_privacy_local) //if("loud") case not added, as it would not modify message_range
if("default")
if(pred.eating_privacy_global)
message_range = 1
if("subtle")
message_range = 1
// Prepare messages
if(prey.is_slipping)
attempt_msg = span_vwarning("It seems like [prey] is about to slide into [pred]'s [lowertext(belly.name)]!")
success_msg = span_vwarning("[prey] suddenly slides into [pred]'s [lowertext(belly.name)]!")
else if(pred.is_slipping)
attempt_msg = span_vwarning("It seems like [prey] is gonna end up inside [pred]'s [lowertext(belly.name)] as [pred] comes sliding over!")
success_msg = span_vwarning("[prey] suddenly slips inside of [pred]'s [lowertext(belly.name)] as [pred] slides into them!")
else if(user == pred) //Feeding someone to yourself
attempt_msg = span_vwarning("[pred] is attempting to [lowertext(belly.vore_verb)] [prey] into their [lowertext(belly.name)]!")
success_msg = span_vwarning("[pred] manages to [lowertext(belly.vore_verb)] [prey] into their [lowertext(belly.name)]!")
else //Feeding someone to another person
attempt_msg = span_vwarning("[user] is attempting to make [pred] [lowertext(belly.vore_verb)] [prey] into their [lowertext(belly.name)]!")
success_msg = span_vwarning("[user] manages to make [pred] [lowertext(belly.vore_verb)] [prey] into their [lowertext(belly.name)]!")
// Announce that we start the attempt!
user.visible_message(attempt_msg, range = message_range)
// Now give the prey time to escape... return if they did
var/swallow_time
if(delay_time < 0)
swallow_time = 0 //No delay!
else if(delay_time)
swallow_time = delay_time
else
swallow_time = ishuman(prey) ? belly.human_prey_swallow_time : belly.nonhuman_prey_swallow_time
// Their AI should get notified so they can stab us
prey.ai_holder?.react_to_attack(user)
//Timer and progress bar
if(!user.client && prey.weakened > 0) // stop crwaling instantly break swallow attempt for mobvore
prey.Stun(min(prey.weakened, 2)) // stop crwaling instantly break swallow attempt for mobvore
if(!do_after(user, swallow_time, prey, exclusive = TASK_USER_EXCLUSIVE))
return FALSE // Prey escaped (or user disabled) before timer expired.
// If we got this far, nom successful! Announce it!
user.visible_message(success_msg, range = message_range)
// Actually shove prey into the belly.
if(istype(prey.loc, /obj/item/holder))
var/obj/item/holder/H = prey.loc
for(var/mob/living/M in H.contents)
belly.nom_mob(M, user)
if(M.loc == H) // In case nom_mob failed somehow.
M.forceMove(get_turf(src))
H.held_mob = null
qdel(H)
else
belly.nom_mob(prey, user)
user.update_icon()
var/mob/living/carbon/victim = prey // Check for afk vore
if(istype(victim) && !victim.client && !victim.ai_holder && victim.ckey)
log_and_message_admins("ate [key_name_admin(prey)] whilst the prey was AFK ([pred ? ADMIN_JMP(pred) : "null"])", pred)
var/mob/living/carbon/victim_pred = pred // Check for afk vore
if(istype(victim_pred) && !victim_pred.client && !victim_pred.ai_holder && victim_pred.ckey)
log_and_message_admins("ate [key_name_admin(prey)] whilst the pred was AFK ([pred ? ADMIN_JMP(pred) : "null"])", pred)
// Inform Admins
if(pred == user)
add_attack_logs(pred, prey, "Eaten via [belly.name]")
else
add_attack_logs(user, pred, "Forced to eat [key_name(prey)]")
return TRUE
//
// Magical pred-air breathing for inside preds
// overrides a proc defined on atom called by breathe.dm
@@ -869,7 +757,7 @@
if(user.is_incorporeal())
return FALSE
var/belly = user.vore_selected
return perform_the_nom(user, prey, user, belly, -1)
return begin_instant_nom(user, prey, user, belly)
/mob/living/proc/glow_toggle()
set name = "Glow (Toggle)"
@@ -37,6 +37,11 @@
belly = vore_selected
return ..()
/mob/living/simple_mob/begin_instant_nom(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly)
if(vore_active && !voremob_loaded && pred == src) //Only init your own bellies.
init_vore(TRUE)
belly = vore_selected
return ..()
//
// Simple proc for animals to have their digestion toggled on/off externally
// Added as a verb in /mob/living/simple_mob/init_vore() if vore is enabled for this mob.
+2 -2
View File
@@ -48,12 +48,12 @@
var/mob/living/target = AM
if(istype(target) && !target.is_incorporeal() && !src.is_incorporeal()) //The slip vore begins
if(can_slip_vore(target) && !src.slip_vore_in_progress && !target.slip_vore_in_progress) //If we can vore them go for it
perform_the_nom(src,target,src,src.vore_selected,-1)
begin_instant_nom(src,target,src,src.vore_selected)
target.slip_vore_in_progress = FALSE
target.is_slipping = FALSE
return
else if(can_be_slip_vored_by(target) && !src.slip_vore_in_progress && !target.slip_vore_in_progress) //Otherwise, if they can vore us, make it happen.
perform_the_nom(target,src,target,target.vore_selected,-1)
begin_instant_nom(target,src,target,target.vore_selected)
slip_vore_in_progress = FALSE
is_slipping = FALSE
return
+2 -2
View File
@@ -38,13 +38,13 @@
if(CanStumbleVore(M)) //This is if the person stumbling into us is able to eat us!
visible_message(span_vwarning("[M] flops carelessly into [src]!"))
M.forceMove(get_turf(src))
perform_the_nom(src,M,src,src.vore_selected,-1)
begin_instant_nom(src,M,src,src.vore_selected)
return
if(M.CanStumbleVore(src)) //This is if the person stumbling into us is able to be eaten by us! BROKEN!
visible_message(span_vwarning("[M] flops carelessly into [src]!"))
M.forceMove(get_turf(src))
perform_the_nom(M,src,M,M.vore_selected,-1)
begin_instant_nom(M,src,M,M.vore_selected)
return
if(istype(S) && S.species.lightweight == 1)
+189
View File
@@ -0,0 +1,189 @@
//Vore procs! Can be delayed or instant.
//If delayed, the chain is: perform_the_nom -> begin_delayed_nom -> vore_sanity_checks -> attempt_to_devour_prey -> devour_and_move_prey
//If instant, the chain is: perform_the_nom -> begin_instant_nom -> vore_sanity_checks -> devour_and_move_prey
/**
* Vore proc that actually does the main part of eating
* Written to be compatible with instant or delayed noms. Alternatively, call begin_instant_nom if the vore is going to be instant.
* Params: user, prey, pred, belly, delay_time
*/
/mob/living/proc/perform_the_nom(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly, delay_time)
if(!delay_time || delay_time > 0)
return begin_delayed_nom(user, prey, pred, belly, delay_time)
else //This is here to catch when delay_time is set to a negative number, aka, 'we want this to be instant.' Any uses of this should be replaced by begin_instant_nom
return begin_instant_nom(user, prey, pred, belly)
/**
* Vore proc that actually does the main part of eating
* Params: user, prey, pred, belly, delay_time
*/
/proc/begin_delayed_nom(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly, delay_time)
if(vore_sanity_checks(user, prey, pred, belly))
return attempt_to_devour_prey(user, prey, pred, belly, delay_time)
else
return FALSE
/**
* Vore proc that actually tries to instantly eat the preey
* Params: user, prey, pred, belly
* NOTE: THIS MUST NOT SLEEP, THIS IS CALLED BY THINGS THAT USE SIGNAL_HANDLER
*/
/mob/living/proc/begin_instant_nom(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly)
SHOULD_NOT_SLEEP(TRUE)
if(vore_sanity_checks(user, prey, pred, belly))
return devour_and_move_prey(user, prey, pred, belly)
else
return FALSE //We failed
/proc/attempt_to_devour_prey(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly, delay_time)
var/message_range = get_vore_message_range(user, pred, prey, belly)
var/attempt_msg = generate_vore_attempt_message(user, prey, pred, belly)
// Announce that we start the attempt!
user.visible_message(attempt_msg, range = message_range)
// Now give the prey time to escape... return if they did
var/swallow_time
if(delay_time < 0) //Shouldn't happen, but let's just be sane..
swallow_time = 0
else if(delay_time)
swallow_time = delay_time
else
swallow_time = ishuman(prey) ? belly.human_prey_swallow_time : belly.nonhuman_prey_swallow_time
// Their AI should get notified so they can stab us
prey.ai_holder?.react_to_attack(user)
//Timer and progress bar
if(!user.client && prey.weakened > 0) // stop crwaling instantly break swallow attempt for mobvore
prey.Stun(min(prey.weakened, 2)) // stop crawling instantly break swallow attempt for mobvore
if(!do_after(user, swallow_time, prey, exclusive = TASK_USER_EXCLUSIVE))
return FALSE // Prey escaped (or user disabled) before timer expired.
// If we got this far, nom successful! Announce it and move the prey!
return devour_and_move_prey(user, prey, pred, belly, message_range)
/proc/devour_and_move_prey(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly, message_range)
var/success_msg = generate_vore_success_message(user, prey, pred, belly)
if(!message_range)
message_range = get_vore_message_range(user, pred, prey, belly)
user.visible_message(success_msg, range = message_range)
// Actually shove prey into the belly.
if(istype(prey.loc, /obj/item/holder))
var/obj/item/holder/H = prey.loc
for(var/mob/living/M in H.contents)
belly.nom_mob(M, user)
if(M.loc == H) // In case nom_mob failed somehow.
M.forceMove(get_turf(user))
H.held_mob = null
qdel(H)
else
belly.nom_mob(prey, user)
user.update_icon()
var/mob/living/carbon/victim = prey // Check for afk vore
if(istype(victim) && !victim.client && !victim.ai_holder && victim.ckey)
log_and_message_admins("ate [key_name_admin(prey)] whilst the prey was AFK ([pred ? ADMIN_JMP(pred) : "null"])", pred)
var/mob/living/carbon/victim_pred = pred // Check for afk vore
if(istype(victim_pred) && !victim_pred.client && !victim_pred.ai_holder && victim_pred.ckey)
log_and_message_admins("ate [key_name_admin(prey)] whilst the pred was AFK ([pred ? ADMIN_JMP(pred) : "null"])", pred)
// Inform Admins
if(pred == user)
add_attack_logs(pred, prey, "Eaten via [belly.name]")
else
add_attack_logs(user, pred, "Forced to eat [key_name(prey)]")
return TRUE
///VORE HELPER PROCS UNDER HERE
/**
* Vore helper proc that creates the vore success message we'll be using
* Params: user, prey, pred, belly, delay_time
* Returns: success_msg
*/
/proc/generate_vore_success_message(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly)
//Determining vore attempt privacy
var/success_msg = "ERROR: Vore message couldn't be created. Notify a dev. (sc)"
if(prey.is_slipping)
success_msg = span_vwarning("[prey] suddenly slides into [pred]'s [lowertext(belly.name)]!")
else if(pred.is_slipping)
success_msg = span_vwarning("[prey] suddenly slips inside of [pred]'s [lowertext(belly.name)] as [pred] slides into them!")
else if(user == pred) //Feeding someone to yourself
success_msg = span_vwarning("[pred] manages to [lowertext(belly.vore_verb)] [prey] into their [lowertext(belly.name)]!")
else //Feeding someone to another person
success_msg = span_vwarning("[user] manages to make [pred] [lowertext(belly.vore_verb)] [prey] into their [lowertext(belly.name)]!")
return success_msg
/**
* Vore proc helper that creates the vore attempt message we'll be using
* Params: user, prey, pred, belly, delay_time
* Returns: attempt_msg
*/
/proc/generate_vore_attempt_message(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly)
//Determining vore attempt privacy
var/attempt_msg = "ERROR: Vore message couldn't be created. Notify a dev. (sc)"
if(prey.is_slipping)
attempt_msg = span_vwarning("It seems like [prey] is about to slide into [pred]'s [lowertext(belly.name)]!")
else if(pred.is_slipping)
attempt_msg = span_vwarning("It seems like [prey] is gonna end up inside [pred]'s [lowertext(belly.name)] as [pred] comes sliding over!")
else if(user == pred) //Feeding someone to yourself
attempt_msg = span_vwarning("[pred] is attempting to [lowertext(belly.vore_verb)] [prey] into their [lowertext(belly.name)]!")
else //Feeding someone to another person
attempt_msg = span_vwarning("[user] is attempting to make [pred] [lowertext(belly.vore_verb)] [prey] into their [lowertext(belly.name)]!")
return attempt_msg
/**
* Vore proc helper that finds what range our message should be
* Params: user, prey, pred, belly, delay_time
* Returns: attempt_msg
*/
/proc/get_vore_message_range(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly)
var/message_range = world.view
if(!pred.is_slipping && !prey.is_slipping) //We only care about privacy preference if it's NOT a spontaneous vore.
switch(belly.eating_privacy_local) //if("loud") case not added, as it would not modify message_range
if("default")
if(pred.eating_privacy_global)
message_range = 1
if("subtle")
message_range = 1
return message_range
/**
* Vore proc helper that sees if we can actually do the vore attempt
* Params: user, prey, pred, belly
* Returns: TRUE/FALSE
* FALSE: Can not eat.
* TRUE: Can eat.
*/
/proc/vore_sanity_checks(mob/living/user, mob/living/prey, mob/living/pred, obj/belly/belly)
//Sanity
if(!user || !prey || !pred || !istype(belly) || !(belly in pred.vore_organs))
log_debug("[user] attempted to feed [prey] to [pred], via [belly ? lowertext(belly.name) : "*null*"] but it went wrong.")
return FALSE
if(pred == prey)
return FALSE
//Final distance check. Time has passed, menus have come and gone. Can't use do_after adjacent because doesn't behave for held micros
var/user_to_pred = get_dist(get_turf(user),get_turf(pred))
var/user_to_prey = get_dist(get_turf(user),get_turf(prey))
if(user == pred && isAI(user))
var/mob/living/silicon/ai/AI = user
if(AI.holo && AI.holo.masters[AI])
user_to_prey = get_dist(get_turf(AI.holo.masters[AI]), get_turf(prey))
if(user_to_pred > 1 || user_to_prey > 1)
return FALSE
if(!prey.devourable)
to_chat(user, span_vnotice("They aren't able to be devoured."))
log_and_message_admins("attempted to devour [key_name_admin(prey)] against their prefs ([prey ? ADMIN_JMP(prey) : "null"])", user)
return FALSE
if(prey.absorbed || pred.absorbed)
to_chat(user, span_vwarning("They aren't aren't in a state to be devoured."))
return FALSE
return TRUE