diff --git a/code/controllers/subsystems/mobs.dm b/code/controllers/subsystems/mobs.dm index c124b5573e..66352c9c9e 100644 --- a/code/controllers/subsystems/mobs.dm +++ b/code/controllers/subsystems/mobs.dm @@ -43,6 +43,11 @@ SUBSYSTEM_DEF(mobs) else if(M.low_priority && !(M.loc && get_z(M) && process_z[get_z(M)])) slept_mobs++ continue + //CHOMPEdit Start - Enable pausing mobs (For transformation, holding until reformation, etc.) + else if(!M.enabled) + slept_mobs++ + continue + //CHOMPEdit End M.Life(times_fired) @@ -90,4 +95,4 @@ SUBSYSTEM_DEF(mobs) /datum/controller/subsystem/mobs/critfail() ..() - log_recent() \ No newline at end of file + log_recent() diff --git a/code/game/objects/items/devices/defib.dm b/code/game/objects/items/devices/defib.dm index 75fddde40e..4fa7e7d983 100644 --- a/code/game/objects/items/devices/defib.dm +++ b/code/game/objects/items/devices/defib.dm @@ -291,7 +291,7 @@ /obj/item/weapon/shockpaddles/proc/can_revive(mob/living/carbon/human/H) //This is checked right before attempting to revive var/obj/item/organ/internal/brain/brain = H.internal_organs_by_name[O_BRAIN] - if(H.should_have_organ(O_BRAIN) && (!brain || brain.defib_timer <= 0 ) ) + if(H.should_have_organ(O_BRAIN) && (!brain || (istype(brain) && brain.defib_timer <= 0 ) ) ) //CHOMPEdit - Fix a runtime when brain is an MMI return "buzzes, \"Resuscitation failed - Excessive neural degeneration. Further attempts futile.\"" H.updatehealth() diff --git a/code/modules/vore/eating/belly_obj_vr.dm b/code/modules/vore/eating/belly_obj_vr.dm index 43ce4a33cc..a0f977d789 100644 --- a/code/modules/vore/eating/belly_obj_vr.dm +++ b/code/modules/vore/eating/belly_obj_vr.dm @@ -321,6 +321,10 @@ return if(OldLoc in contents) return //Someone dropping something (or being stripdigested) + //CHOMPEdit Start - Prevent reforming causing a lot of log spam/sounds + if(istype(OldLoc, /mob/observer) || istype(OldLoc, /obj/item/device/mmi)) + return //Someone getting reformed most likely (And if not, uh... shouldn't happen anyways?) + //CHOMPEdit end //Generic entered message if(!owner.mute_entry) //CHOMPEdit @@ -801,10 +805,31 @@ /obj/belly/proc/digestion_death(mob/living/M) add_attack_logs(owner, M, "Digested in [lowertext(name)]") + //CHOMPEdit Start - Reverts TF on death. This fixes a bug with posibrains or similar, and also makes reforming easier. + if(M.tf_mob_holder && M.tf_mob_holder.loc == M) + M.tf_mob_holder.ckey = M.ckey + M.tf_mob_holder.enabled = TRUE + M.tf_mob_holder.loc = M.loc + M.tf_mob_holder.forceMove(M.loc) + QDEL_LIST_NULL(M.tf_mob_holder.vore_organs) + M.tf_mob_holder.vore_organs = list() + for(var/obj/belly/B as anything in M.vore_organs) + B.loc = M.tf_mob_holder + B.forceMove(M.tf_mob_holder) + B.owner = M.tf_mob_holder + M.tf_mob_holder.vore_organs |= B + M.vore_organs -= B + + if(M.tf_mob_holder) + M.tf_mob_holder = null + //CHOMPEdit End + // 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) + var/obj/item/device/mmi/hasMMI // CHOMPEdit - Adjust how MMI's are handled + //Drop all items into the belly. if(config.items_survive_digestion) for(var/obj/item/W in M) @@ -813,6 +838,7 @@ var/obj/item/device/mmi/brainbox = MMI.removed() if(brainbox) items_preserved += brainbox + hasMMI = brainbox // CHOMPEdit - Adjust how MMI's are handled for(var/slot in slots) var/obj/item/I = M.get_equipped_item(slot = slot) if(I) @@ -840,10 +866,42 @@ //Incase they have the loop going, let's double check to stop it. M.stop_sound_channel(CHANNEL_PREYLOOP) // Delete the digested mob - var/mob/observer/G = M.ghostize() //CHOMPEdit start. Make sure they're out, so we can copy attack logs and such. - if(G) - G.forceMove(src) //CHOMPEdit end. - qdel(M) + //CHOMPEdit start - Changed qdel to a forceMove to allow reforming, and... handled robots special. + if(isrobot(M)) + var/mob/living/silicon/robot/R = M + if(R.mmi && R.mind && R.mmi.brainmob) + R.mmi.loc = src + items_preserved += R.mmi + var/obj/item/weapon/robot_module/MB = locate() in R.contents + if(MB) + R.mmi.brainmob.languages = MB.original_languages + else + R.mmi.brainmob.languages = R.languages + R.mmi.brainmob.remove_language("Robot Talk") + hasMMI = R.mmi + M.mind.transfer_to(hasMMI.brainmob) + R.mmi = null + else if(!R.shell) // Shells don't have brainmobs in their MMIs. + to_chat(R, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") + if(R.shell) // Let the standard procedure for shells handle this. + qdel(R) + return + + if(istype(hasMMI)) + hasMMI.body_backup = M + M.enabled = FALSE + M.forceMove(hasMMI) + else + //Another CHOMPEdit started here. I left the comment here, though obviously we're doing a lot more now as well. + var/mob/observer/G = M.ghostize(FALSE) //CHOMPEdit start. Make sure they're out, so we can copy attack logs and such. + if(G) + G.forceMove(src) + G.body_backup = M + M.enabled = FALSE + M.forceMove(G) + else + qdel(M) + //CHOMPEdit End // Handle a mob being absorbed /obj/belly/proc/absorb_living(mob/living/M) diff --git a/code/modules/vore/eating/vorepanel_vr.dm b/code/modules/vore/eating/vorepanel_vr.dm index 8ab067efca..01610d895e 100644 --- a/code/modules/vore/eating/vorepanel_vr.dm +++ b/code/modules/vore/eating/vorepanel_vr.dm @@ -814,6 +814,10 @@ var/list/available_options = list("Examine", "Eject", "Move", "Transfer") if(ishuman(target)) available_options += "Transform" + //CHOMPEdit Begin - Add Reforming + if(isobserver(target) || istype(target,/obj/item/device/mmi)) + available_options += "Reform" + //CHOMPEdit End if(isliving(target)) var/mob/living/datarget = target if(datarget.client) @@ -899,6 +903,154 @@ V.tgui_interact(user) return TRUE + //CHOMPEdit Begin - Add Reforming + if("Reform") + if(host.stat) + to_chat(user,"You can't do that in your state!") + return TRUE + + if(isobserver(target)) + var/mob/observer/T = target + if(!ismob(T.body_backup) || prevent_respawns.Find(T.mind.name) || ispAI(T.body_backup)) + to_chat(user,"They don't seem to be reformable!") + return TRUE + + var/accepted = tgui_alert(T, "[host] is trying to reform your body! Would you like to get reformed inside [host]'s [lowertext(host.vore_selected.name)]?", "Reforming Attempt", list("Yes", "No")) + if(accepted != "Yes") + to_chat(user,"[T] refused to be reformed!") + return TRUE + + if(isliving(T.body_backup)) + var/mob/living/body_backup = T.body_backup + if(ishuman(body_backup)) + var/mob/living/carbon/human/H = body_backup + body_backup.adjustBruteLoss(-6) + body_backup.adjustFireLoss(-6) + body_backup.setOxyLoss(0) + if(H.isSynthetic()) + H.adjustToxLoss(-H.getToxLoss()) + else + H.adjustToxLoss(-6) + body_backup.adjustCloneLoss(-6) + body_backup.updatehealth() + // Now we do the check to see if we should revive... + var/should_proceed_with_revive = TRUE + var/obj/item/organ/internal/brain/brain = H.internal_organs_by_name[O_BRAIN] + should_proceed_with_revive &&= !H.should_have_organ(O_BRAIN) || (brain && (!istype(brain) || brain.defib_timer > 0)) + if(!H.isSynthetic()) + should_proceed_with_revive &&= !(HUSK in H.mutations) && H.can_defib + if(should_proceed_with_revive) + for(var/organ_tag in H.species.has_organ) + var/obj/item/organ/O = H.species.has_organ[organ_tag] + var/vital = initial(O.vital) //check for vital organs + if(vital) + O = H.internal_organs_by_name[organ_tag] + if(!O || O.damage > O.max_damage) + should_proceed_with_revive = FALSE + break + if(should_proceed_with_revive) + dead_mob_list.Remove(H) + if((H in living_mob_list) || (H in dead_mob_list)) + WARNING("Mob [H] was defibbed but already in the living or dead list still!") + living_mob_list += H + + H.timeofdeath = 0 + H.set_stat(UNCONSCIOUS) //Life() can bring them back to consciousness if it needs to. + H.failed_last_breath = 0 //So mobs that died of oxyloss don't revive and have perpetual out of breath. + H.reload_fullscreen() + else + body_backup.revive() + body_backup.forceMove(T.loc) + body_backup.enabled = TRUE + body_backup.ajourn = 0 + body_backup.key = T.key + body_backup.teleop = null + T.body_backup = null + host.vore_selected.release_specific_contents(T, TRUE) + if(istype(body_backup, /mob/living/simple_mob)) + var/mob/living/simple_mob/sm = body_backup + if(sm.icon_rest && sm.resting) + sm.icon_state = sm.icon_rest + else + sm.icon_state = sm.icon_living + T.update_icon() + announce_ghost_joinleave(T.mind, 0, "They now occupy their body again.") + else if(istype(target,/obj/item/device/mmi)) // A good bit of repeated code, sure, but... cleanest way to do this. + var/obj/item/device/mmi/MMI = target + if(!ismob(MMI.body_backup) || !MMI.brainmob.mind || prevent_respawns.Find(MMI.brainmob.mind.name)) + to_chat(user,"They don't seem to be reformable!") + return TRUE + var/accepted = tgui_alert(MMI.brainmob, "[host] is trying to reform your body! Would you like to get reformed inside [host]'s [lowertext(host.vore_selected.name)]?", "Reforming Attempt", list("Yes", "No")) + if(accepted != "Yes") + to_chat(user,"[MMI] refused to be reformed!") + return TRUE + + if(isliving(MMI.body_backup)) + var/mob/living/body_backup = MMI.body_backup + body_backup.enabled = TRUE + body_backup.forceMove(MMI.loc) + body_backup.ajourn = 0 + body_backup.teleop = null + //And now installing the MMI into the body... + if(isrobot(body_backup)) //Just do the reverse of getting the MMI pulled out in /obj/belly/proc/digestion_death + var/mob/living/silicon/robot/R = body_backup + R.revive() + MMI.brainmob.mind.transfer_to(R) + MMI.loc = R + R.mmi = MMI + R.mmi.brainmob.add_language("Robot Talk") + else //reference /datum/surgery_step/robotics/install_mmi/end_step + var/obj/item/organ/internal/mmi_holder/holder + if(istype(MMI, /obj/item/device/mmi/digital/posibrain)) + var/obj/item/organ/internal/mmi_holder/posibrain/holdertmp = new(body_backup, 1) + holder = holdertmp + else if(istype(MMI, /obj/item/device/mmi/digital/robot)) + var/obj/item/organ/internal/mmi_holder/robot/holdertmp = new(body_backup, 1) + holder = holdertmp + else + holder = new(body_backup, 1) + body_backup.internal_organs_by_name["brain"] = holder + MMI.loc = holder + holder.stored_mmi = MMI + holder.update_from_mmi() + + if(MMI.brainmob && MMI.brainmob.mind) + MMI.brainmob.mind.transfer_to(body_backup) + body_backup.languages = MMI.brainmob.languages + //You've hopefully already named yourself, so... not implementing that bit. + var/mob/living/carbon/human/H = body_backup + body_backup.adjustBruteLoss(-6, TRUE) + body_backup.adjustFireLoss(-6, TRUE) + body_backup.setOxyLoss(0) + H.adjustToxLoss(-H.getToxLoss()) + body_backup.adjustCloneLoss(-6) + body_backup.updatehealth() + // Now we do the check to see if we should revive... + var/should_proceed_with_revive = TRUE + var/obj/item/organ/internal/brain/brain = H.internal_organs_by_name[O_BRAIN] + should_proceed_with_revive &&= !H.should_have_organ(O_BRAIN) || (brain && brain.defib_timer > 0 ) + if(should_proceed_with_revive) + for(var/organ_tag in H.species.has_organ) + var/obj/item/organ/O = H.species.has_organ[organ_tag] + var/vital = initial(O.vital) //check for vital organs + if(vital) + O = H.internal_organs_by_name[organ_tag] + if(!O || O.damage > O.max_damage) + should_proceed_with_revive = FALSE + break + if(should_proceed_with_revive) + dead_mob_list.Remove(H) + if((H in living_mob_list) || (H in dead_mob_list)) + WARNING("Mob [H] was defibbed but already in the living or dead list still!") + living_mob_list += H + + H.timeofdeath = 0 + H.set_stat(UNCONSCIOUS) //Life() can bring them back to consciousness if it needs to. + H.failed_last_breath = 0 //So mobs that died of oxyloss don't revive and have perpetual out of breath. + H.reload_fullscreen() + MMI.body_backup = null + return TRUE + //CHOMPEdit End if("Process") var/mob/living/ourtarget = target var/list/process_options = list() diff --git a/modular_chomp/code/modules/mob/dead/observer/observer.dm b/modular_chomp/code/modules/mob/dead/observer/observer.dm new file mode 100644 index 0000000000..7928b41556 --- /dev/null +++ b/modular_chomp/code/modules/mob/dead/observer/observer.dm @@ -0,0 +1,7 @@ +/mob/observer + var/mob/living/body_backup = null //add reforming + +/mob/observer/Destroy() + if(body_backup) + qdel(body_backup) + ..() diff --git a/modular_chomp/code/modules/mob/living/carbon/brain/MMI.dm b/modular_chomp/code/modules/mob/living/carbon/brain/MMI.dm new file mode 100644 index 0000000000..ae2da9c9d3 --- /dev/null +++ b/modular_chomp/code/modules/mob/living/carbon/brain/MMI.dm @@ -0,0 +1,7 @@ +/obj/item/device/mmi + var/mob/living/body_backup = null //add reforming + +/obj/item/device/mmi/Destroy() + if(body_backup) + qdel(body_backup) + ..() diff --git a/modular_chomp/code/modules/mob/mob.dm b/modular_chomp/code/modules/mob/mob.dm index 56eb7ea782..be6e3daee9 100644 --- a/modular_chomp/code/modules/mob/mob.dm +++ b/modular_chomp/code/modules/mob/mob.dm @@ -1,9 +1,10 @@ /mob var/voice_freq = 42500 // Preference for character voice frequency var/list/voice_sounds_list = list() // The sound list containing our voice sounds! + var/enabled = TRUE //Pauses a mob if disabled (Prevents life ticks from happening) var/died_in_vr = FALSE //For virtual reality sleepers /mob/is_incorporeal() if(incorporeal_move) return 1 - ..() \ No newline at end of file + ..() diff --git a/vorestation.dme b/vorestation.dme index 9341f6948d..49af4423c3 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -4534,8 +4534,10 @@ #include "modular_chomp\code\modules\mining\shelters_ch.dm" #include "modular_chomp\code\modules\mob\holder.dm" #include "modular_chomp\code\modules\mob\mob.dm" +#include "modular_chomp\code\modules\mob\dead\observer\observer.dm" #include "modular_chomp\code\modules\mob\living\emote.dm" #include "modular_chomp\code\modules\mob\living\living.dm" +#include "modular_chomp\code\modules\mob\living\carbon\brain\MMI.dm" #include "modular_chomp\code\modules\mob\living\carbon\human\human.dm" #include "modular_chomp\code\modules\mob\living\carbon\human\human_defines.dm" #include "modular_chomp\code\modules\mob\living\carbon\human\update_icons.dm"