From 7ee5ab7041f175f606ad7440e7c7c27ff658226b Mon Sep 17 00:00:00 2001
From: Kashargul <144968721+Kashargul@users.noreply.github.com>
Date: Sat, 27 Jul 2024 02:22:43 +0200
Subject: [PATCH] Allow to give control to stored souls in the soulcatcher &
ghost join (#8687)
---
code/__defines/mob_ch.dm | 1 +
code/__defines/nif_ch.dm | 2 +
.../station/station_special_abilities_vr.dm | 4 +
.../nifsoft/software/13_soulcatcher.dm | 28 +-
code/modules/vore/eating/vorepanel_vr.dm | 73 ++--
.../code/modules/vore/eating/soulcatcher.dm | 368 ++++++++++++------
.../modules/vore/eating/soulcatcher_mob.dm | 35 +-
.../vore/eating/soulcatcher_observer.dm | 52 +++
.../chompstation/VorePanel/VoreAbilities.tsx | 56 +++
.../VorePanel/VoreSoulcatcher.tsx | 235 ++---------
.../VoreSoulcatcherSettings/CatchSettings.tsx | 70 ++++
.../VoreSoulcatcherSettings/GlobalOptions.tsx | 51 +++
.../GlobalSettings.tsx | 88 +++++
.../VoreSoulcatcherSettings/SoulOptions.tsx | 59 +++
.../VorePanel/VoreUserPreferences.tsx | 15 +
.../VoreUserPreferencesSoulcatcher.tsx | 8 +-
.../chompstation/VorePanel/index.tsx | 7 +-
.../chompstation/VorePanel/types.ts | 21 +-
vorestation.dme | 1 +
19 files changed, 807 insertions(+), 367 deletions(-)
create mode 100644 modular_chomp/code/modules/vore/eating/soulcatcher_observer.dm
create mode 100644 tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreAbilities.tsx
create mode 100644 tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/CatchSettings.tsx
create mode 100644 tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/GlobalOptions.tsx
create mode 100644 tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/GlobalSettings.tsx
create mode 100644 tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/SoulOptions.tsx
diff --git a/code/__defines/mob_ch.dm b/code/__defines/mob_ch.dm
index b7ee81ea6f..871a0d77f2 100644
--- a/code/__defines/mob_ch.dm
+++ b/code/__defines/mob_ch.dm
@@ -2,3 +2,4 @@
#define SOULCATCHER_ALLOW_TRANSFER 0x2
#define SOULCATCHER_ALLOW_DELETION 0x4
#define SOULCATCHER_ALLOW_DELETION_INSTANT 0x8
+#define SOULCATCHER_ALLOW_TAKEOVER 0x10
diff --git a/code/__defines/nif_ch.dm b/code/__defines/nif_ch.dm
index 84a08b9bdc..ea78284f43 100644
--- a/code/__defines/nif_ch.dm
+++ b/code/__defines/nif_ch.dm
@@ -6,5 +6,7 @@
#define NIF_SC_ALLOW_EYES 0x8
#define NIF_SC_BACKUPS 0x10
#define NIF_SC_PROJECTING 0x20
+#define SOULGEM_CATCHING_GHOSTS 0x200
#define SOULGEM_ACTIVE 0x400
#define SOULGEM_SHOW_VORE_SFX 0x800
+#define SOULGEM_CATCHING_DRAIN 0x1000
diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm
index 88dcbfe078..5293724edc 100644
--- a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm
+++ b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm
@@ -635,6 +635,8 @@
to_chat(T, "An odd sensation flows through your body as you as [src] begins to drain you to dangerous levels!")
if(51 to 98)
if(T.stat == DEAD)
+ if(soulgem?.flag_check(SOULGEM_ACTIVE | SOULGEM_CATCHING_DRAIN, TRUE))
+ soulgem.catch_mob(T)
T.apply_damage(500, OXY) //Bit of fluff.
absorbing_prey = 0
to_chat(src, "You have completely drained [T], killing them.")
@@ -649,6 +651,8 @@
if(drain_finalized != 1)
stage = 51
if(100) //They shouldn't survive long enough to get here, but just in case.
+ if(soulgem?.flag_check(SOULGEM_ACTIVE | SOULGEM_CATCHING_DRAIN, TRUE))
+ soulgem.catch_mob(T)
T.apply_damage(500, OXY) //Kill them.
absorbing_prey = 0
to_chat(src, "You have completely drained [T], killing them in the process.")
diff --git a/code/modules/nifsoft/software/13_soulcatcher.dm b/code/modules/nifsoft/software/13_soulcatcher.dm
index b0babaf818..9c84f23bde 100644
--- a/code/modules/nifsoft/software/13_soulcatcher.dm
+++ b/code/modules/nifsoft/software/13_soulcatcher.dm
@@ -329,14 +329,16 @@
if(parent_mob) return
//If they're blinded
- if(ext_blind)
- eye_blind = 5
- client.screen.Remove(global_hud.whitense)
- overlay_fullscreen("blind", /obj/screen/fullscreen/blind)
- else
- eye_blind = 0
- clear_fullscreens()
- client.screen.Add(global_hud.whitense)
+ if(soulcatcher) // CHOMPEdit Start, needs it's own handling to allow vore_fx
+ if(ext_blind)
+ eye_blind = 5
+ client.screen.Remove(global_hud.whitense)
+ overlay_fullscreen("blind", /obj/screen/fullscreen/blind)
+ else
+ eye_blind = 0
+ clear_fullscreens()
+ client.screen.Add(global_hud.whitense)
+ //CHOMPEdit End
//If they're deaf
if(ext_deaf)
@@ -484,7 +486,10 @@
var/mob/living/owner = B.owner
var/obj/soulgem/gem = owner.soulgem
if(owner && gem.flag_check(SOULGEM_ACTIVE | NIF_SC_CATCHING_OTHERS, TRUE))
- gem.catch_mob(L)
+ var/to_use_custom_name = null
+ if(isanimal(L))
+ to_use_custom_name = L.name
+ gem.catch_mob(L, to_use_custom_name)
return TRUE
var/mob/living/carbon/human/HP = B.owner
var/mob/living/carbon/human/H = L
@@ -495,7 +500,10 @@
else
var/obj/soulgem/gem = L.soulgem
if(gem && gem.flag_check(SOULGEM_ACTIVE | NIF_SC_CATCHING_ME, TRUE))
- gem.catch_mob(L)
+ var/to_use_custom_name = null
+ if(isanimal(L))
+ to_use_custom_name = L.name
+ gem.catch_mob(L, to_use_custom_name)
return TRUE
var/mob/living/carbon/human/H = L
if(!istype(H)) return TRUE
diff --git a/code/modules/vore/eating/vorepanel_vr.dm b/code/modules/vore/eating/vorepanel_vr.dm
index c615229c33..22336287d3 100644
--- a/code/modules/vore/eating/vorepanel_vr.dm
+++ b/code/modules/vore/eating/vorepanel_vr.dm
@@ -555,6 +555,7 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono",
//Soulcatcher
"soulcatcher_allow_capture" = host.soulcatcher_pref_flags & SOULCATCHER_ALLOW_CAPTURE,
"soulcatcher_allow_transfer" = host.soulcatcher_pref_flags & SOULCATCHER_ALLOW_TRANSFER,
+ "soulcatcher_allow_takeover" = host.soulcatcher_pref_flags & SOULCATCHER_ALLOW_TAKEOVER,
"soulcatcher_allow_deletion" = (global_flag_check(host.soulcatcher_pref_flags, SOULCATCHER_ALLOW_DELETION) + global_flag_check(host.soulcatcher_pref_flags, SOULCATCHER_ALLOW_DELETION_INSTANT))
//CHOMPEdit end
)
@@ -572,8 +573,11 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono",
data["soulcatcher"]["selected_soul"] = host.soulgem.selected_soul
data["soulcatcher"]["selected_sfx"] = host.soulgem.linked_belly
data["soulcatcher"]["interior_design"] = host.soulgem.inside_flavor
+ data["soulcatcher"]["taken_over"] = host.soulgem.is_taken_over()
data["soulcatcher"]["catch_self"] = host.soulgem.flag_check(NIF_SC_CATCHING_ME)
data["soulcatcher"]["catch_prey"] = host.soulgem.flag_check(NIF_SC_CATCHING_OTHERS)
+ data["soulcatcher"]["catch_drain"] = host.soulgem.flag_check(SOULGEM_CATCHING_DRAIN)
+ data["soulcatcher"]["catch_ghost"] = host.soulgem.flag_check(SOULGEM_CATCHING_GHOSTS)
data["soulcatcher"]["ext_hearing"] = host.soulgem.flag_check(NIF_SC_ALLOW_EARS)
data["soulcatcher"]["ext_vision"] = host.soulgem.flag_check(NIF_SC_ALLOW_EYES)
data["soulcatcher"]["mind_backups"] = host.soulgem.flag_check(NIF_SC_BACKUPS)
@@ -2170,10 +2174,20 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono",
//Soulcatcher prefs
if("toggle_soulcatcher_allow_capture")
host.soulcatcher_pref_flags ^= SOULCATCHER_ALLOW_CAPTURE
+ if(host.client.prefs_vr)
+ host.client.prefs_vr.soulcatcher_pref_flags = host.soulcatcher_pref_flags
unsaved_changes = TRUE
return TRUE
if("toggle_soulcatcher_allow_transfer")
host.soulcatcher_pref_flags ^= SOULCATCHER_ALLOW_TRANSFER
+ if(host.client.prefs_vr)
+ host.client.prefs_vr.soulcatcher_pref_flags = host.soulcatcher_pref_flags
+ unsaved_changes = TRUE
+ return TRUE
+ if("toggle_soulcatcher_allow_takeover")
+ host.soulcatcher_pref_flags ^= SOULCATCHER_ALLOW_TAKEOVER
+ if(host.client.prefs_vr)
+ host.client.prefs_vr.soulcatcher_pref_flags = host.soulcatcher_pref_flags
unsaved_changes = TRUE
return TRUE
if("toggle_soulcatcher_allow_deletion")
@@ -2188,6 +2202,8 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono",
host.soulcatcher_pref_flags ^= SOULCATCHER_ALLOW_DELETION
if(host.soulcatcher_pref_flags & SOULCATCHER_ALLOW_DELETION_INSTANT)
host.soulcatcher_pref_flags ^= SOULCATCHER_ALLOW_DELETION_INSTANT
+ if(host.client.prefs_vr)
+ host.client.prefs_vr.soulcatcher_pref_flags = host.soulcatcher_pref_flags
unsaved_changes = TRUE
return TRUE
if("adjust_own_size")
@@ -2199,13 +2215,34 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono",
H.adjust_nutrition(-VORE_RESIZE_COST)
H.resize(new_size, uncapped = host.has_large_resize_bounds(), ignore_prefs = TRUE)
return TRUE
- //Soulcatcher settings
- if("soulcatcher_toggle")
- host.soulgem.toggle_setting(SOULGEM_ACTIVE)
- unsaved_changes = TRUE
+ //Soulcatcher functions
+ if("soulcatcher_release_all")
+ host.soulgem.release_mobs()
+ return TRUE
+ if("soulcatcher_erase_all")
+ host.soulgem.erase_mobs()
+ return TRUE
+ if("soulcatcher_release")
+ host.soulgem.release_selected()
+ return TRUE
+ if("soulcatcher_transfer")
+ host.soulgem.transfer_selected()
+ return TRUE
+ if("soulcatcher_delete")
+ host.soulgem.delete_selected()
+ return TRUE
+ if("soulcatcher_transfer_control")
+ host.soulgem.take_control_selected()
+ return TRUE
+ if("soulcatcher_release_control")
+ host.soulgem.take_control_owner()
return TRUE
if("soulcatcher_select")
host.soulgem.selected_soul = locate(params["selected_soul"])
+ return TRUE
+ //Soulcatcher settings
+ if("soulcatcher_toggle")
+ host.soulgem.toggle_setting(SOULGEM_ACTIVE)
unsaved_changes = TRUE
return TRUE
if("soulcatcher_sfx")
@@ -2214,18 +2251,6 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono",
host.soulgem.update_linked_belly(belly)
unsaved_changes = TRUE
return TRUE
- if("soulcatcher_release")
- host.soulgem.release_selected()
- unsaved_changes = TRUE
- return TRUE
- if("soulcatcher_transfer")
- host.soulgem.transfer_selected()
- unsaved_changes = TRUE
- return TRUE
- if("soulcatcher_delete")
- host.soulgem.delete_selected()
- unsaved_changes = TRUE
- return TRUE
if("toggle_self_catching")
host.soulgem.toggle_setting(NIF_SC_CATCHING_ME)
unsaved_changes = TRUE
@@ -2234,6 +2259,14 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono",
host.soulgem.toggle_setting(NIF_SC_CATCHING_OTHERS)
unsaved_changes = TRUE
return TRUE
+ if("toggle_drain_catching")
+ host.soulgem.toggle_setting(SOULGEM_CATCHING_DRAIN)
+ unsaved_changes = TRUE
+ return TRUE
+ if("toggle_ghost_catching")
+ host.soulgem.toggle_setting(SOULGEM_CATCHING_GHOSTS)
+ unsaved_changes = TRUE
+ return TRUE
if("toggle_ext_hearing")
host.soulgem.toggle_setting(NIF_SC_ALLOW_EARS)
unsaved_changes = TRUE
@@ -2254,14 +2287,6 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono",
host.soulgem.toggle_setting(SOULGEM_SHOW_VORE_SFX)
unsaved_changes = TRUE
return TRUE
- if("soulcatcher_release_all")
- unsaved_changes = TRUE
- host.soulgem.release_mobs()
- return TRUE
- if("soulcatcher_erase_all")
- unsaved_changes = TRUE
- host.soulgem.erase_mobs()
- return TRUE
if("soulcatcher_rename")
var/new_name = tgui_input_text(host, "Adjust the name of your soulcatcher. Limit 60 chars.", \
"New Name", html_decode(host.soulgem.name), 60, prevent_enter = TRUE)
diff --git a/modular_chomp/code/modules/vore/eating/soulcatcher.dm b/modular_chomp/code/modules/vore/eating/soulcatcher.dm
index 9ef5a65e64..a08e4cba84 100644
--- a/modular_chomp/code/modules/vore/eating/soulcatcher.dm
+++ b/modular_chomp/code/modules/vore/eating/soulcatcher.dm
@@ -4,6 +4,7 @@
var/mob/living/owner
var/datum/own_mind
var/obj/belly/linked_belly
+ var/taken_over_name
var/setting_flags = (NIF_SC_ALLOW_EARS|NIF_SC_ALLOW_EYES|NIF_SC_BACKUPS|NIF_SC_PROJECTING)
var/mob/selected_soul = null
@@ -15,6 +16,7 @@
var/transfer_message = "Transfer Message"
var/delete_message = "Delete Message"
+// Append the vars to save to our savefile list
/obj/soulgem/vars_to_save()
var/list/saving = list (
"setting_flags",
@@ -28,11 +30,13 @@
)
return ..() + saving
+// Load the vars_to_save from the savefile
/obj/soulgem/Initialize()
. = ..()
if(ismob(loc))
owner = loc
+// Store the vars_to_save into the save file
/obj/soulgem/deserialize(list/data)
. = ..()
for(var/obj/belly in owner.vore_organs)
@@ -41,6 +45,7 @@
return
linked_belly = null
+// Allows to transfer the soulgem to the given mob
/obj/soulgem/proc/transfer_self(var/mob/target)
QDEL_NULL(target.soulgem)
owner.soulgem = null
@@ -48,6 +53,7 @@
owner = target
target.soulgem = src
+// Cleaning up our refs before deletion
/obj/soulgem/Destroy()
owner = null
selected_soul = null
@@ -58,12 +64,14 @@
linked_belly = null
..()
+// Sends messages to the owner of the soulcatcher
/obj/soulgem/proc/notify_holder(var/message)
to_chat(owner, span_nif("[name] displays, \"[message]\""))
for(var/mob/living/carbon/brain/caught_soul/CS as anything in brainmobs)
to_chat(CS, span_nif("[name] displays, \"[message]\""))
+// Forwards the speech of captured souls
/obj/soulgem/proc/use_speech(var/message, var/mob/living/sender, var/mob/eyeobj)
var/sender_name = eyeobj ? eyeobj.name : sender.name
@@ -79,6 +87,7 @@
log_nsay(message, owner.real_name, sender)
+// Forwards the emotes of captured souls
/obj/soulgem/proc/use_emote(var/message, var/mob/living/sender, var/mob/eyeobj)
var/sender_name = eyeobj ? eyeobj.name : sender.name
@@ -94,43 +103,9 @@
log_nme(message, owner.real_name,sender)
-/obj/soulgem/proc/update_linked_belly(var/obj/belly, var/skip_unreg = FALSE)
- if(!belly && linked_belly)
- UnregisterSignal(linked_belly, COMSIG_BELLY_UPDATE_VORE_FX)
- linked_belly = null
- return
- if(!isbelly(belly))
- return
- if(!linked_belly)
- linked_belly = belly
- RegisterSignal(linked_belly, COMSIG_BELLY_UPDATE_VORE_FX, PROC_REF(soulgem_vfx))
- return
- if(belly != linked_belly)
- if(!skip_unreg)
- UnregisterSignal(linked_belly, COMSIG_BELLY_UPDATE_VORE_FX)
- linked_belly = belly
- RegisterSignal(linked_belly, COMSIG_BELLY_UPDATE_VORE_FX, PROC_REF(soulgem_vfx))
-
-/obj/soulgem/proc/soulgem_vfx(var/update, var/severity = 0)
- if(linked_belly)
- for(var/mob/living/L in brainmobs)
- if(flag_check(SOULGEM_SHOW_VORE_SFX))
- show_vore_fx(L, update, severity)
- else
- clear_vore_fx()
-
-/obj/soulgem/proc/show_vore_fx(var/mob/living/L, var/update, var/severity = 0)
- if(!linked_belly || !flag_check(SOULGEM_SHOW_VORE_SFX))
- return
- if(!istype(L) || L.eyeobj)
- return
- linked_belly.vore_fx(L, update, severity)
-
-/obj/soulgem/proc/clear_vore_fx(var/mob/M)
- M.clear_fullscreen("belly")
-
+// The capture function which transfers the given mob's mind into the soulcatcher
/obj/soulgem/proc/catch_mob(var/mob/M, var/custom_name)
- if(!(M.soulcatcher_pref_flags & SOULCATCHER_ALLOW_CAPTURE)) return
+ if(!(M.soulcatcher_pref_flags & SOULCATCHER_ALLOW_CAPTURE) && !isobserver(M)) return // Bypass pref check for observer join
if(!M.mind) return
if(isbrain(owner)) return
//Create a new brain mob
@@ -140,6 +115,8 @@
brainmob.stat = 0
brainmob.silent = FALSE
dead_mob_list -= brainmob
+ brainmob.ext_deaf = !flag_check(NIF_SC_ALLOW_EARS)
+ brainmob.ext_blind = !flag_check(NIF_SC_ALLOW_EYES)
brainmob.add_language(LANGUAGE_GALCOM)
brainmobs |= brainmob
@@ -149,7 +126,7 @@
brainmob.real_name = custom_name ? custom_name : brainmob.mind.name
//If we caught our owner, special settings.
- if(M == owner)
+ if(M == owner && !own_mind) // Need some more sanity if we allow takeover
brainmob.ext_deaf = FALSE
brainmob.ext_blind = FALSE
brainmob.parent_mob = TRUE
@@ -194,25 +171,206 @@
show_vore_fx(brainmob, TRUE)
return TRUE
-/obj/soulgem/proc/release_selected()
- release_mob(selected_soul)
+// Allows to adjust the interior of the soulcatcher
+/obj/soulgem/proc/adjust_interior(var/new_flavor)
+ new_flavor = sanitize(new_flavor, MAX_MESSAGE_LEN * 2)
+ inside_flavor = new_flavor
+ notify_holder("Updating environment...")
+ for(var/mob/living/carbon/brain/caught_soul/vore/CS as anything in brainmobs)
+ to_chat(CS, span_notice("[transit_message]") + "\n[inside_flavor]")
+
+// Allows to return to the body after being captured by one's own soulcatcher
+/obj/soulgem/proc/return_to_body(var/datum/mind)
+ if(own_mind != mind)
+ to_chat(src, span_warning("You aren't in your own soulcatcher!"))
+ return
+ var/mob/self = null
+ for(var/mob/mob in brainmobs)
+ if(mob.mind == mind)
+ self = mob
+ break
+ if(!self)
+ return
+ if(owner.mind)
+ catch_mob(owner, taken_over_name)
+ self.mind.transfer_to(owner)
+ own_mind = null
+ taken_over_name = null
+ qdel(self)
+
+// Sets the custom messages depending on the input
+/obj/soulgem/proc/set_custom_message(var/message, var/target)
+ message = sanitize(message, MAX_MESSAGE_LEN / 4)
+ switch(target)
+ if("capture")
+ capture_message = message
+ if("transit")
+ transit_message = message
+ if("release")
+ release_message = message
+ if("transfer")
+ transfer_message = message
+ if("delete")
+ delete_message = message
+
+// Allows to rename the soulgem
+/obj/soulgem/proc/rename(var/new_name)
+ if(length(new_name) < 3 || length(new_name) > 60)
+ to_chat(owner, span_warning("Your soulcatcher's name needs to be between 3 and 60 characters long!"))
+ return
+ new_name = sanitize(new_name, 60)
+ name = new_name
+
+// Toggles the given flag
+/obj/soulgem/proc/toggle_setting(var/flag)
+ setting_flags ^= flag
+ if(flag & SOULGEM_SHOW_VORE_SFX)
+ soulgem_vfx(TRUE)
+ if(flag & NIF_SC_BACKUPS)
+ soulgem_backup()
+ if(flag & NIF_SC_ALLOW_EARS)
+ soulgem_hear()
+ if(flag & NIF_SC_ALLOW_EYES)
+ soulgem_sight()
+ if(flag & NIF_SC_PROJECTING)
+ soulgem_projecting()
+
+// Checks a single flag, or if all combined flags are true
+/obj/soulgem/proc/flag_check(var/flag, var/match_all = FALSE)
+ if(match_all)
+ return (setting_flags & flag) == flag
+ return setting_flags & flag
+
+// Updates the selected soul after an interaction which rleased, deleted or transferred the previous one
+/obj/soulgem/proc/update_selected_soul()
if(brainmobs.len > 1)
selected_soul = brainmobs[1]
else
selected_soul = null
-/obj/soulgem/proc/release_mobs()
- selected_soul = null
- if(!brainmobs.len) return
- for(var/mob/M in brainmobs)
- release_mob(M)
+// Backup toggling
+/obj/soulgem/proc/soulgem_backup()
+ if(flag_check(NIF_SC_BACKUPS))
+ notify_holder("External backups enabled.")
+ else
+ notify_holder("External backups disabled.")
-/obj/soulgem/proc/release_mob(var/mob/M)
- to_chat(M, span_notice("[release_message]"))
+// Deaf toggling
+/obj/soulgem/proc/soulgem_hear()
+ for(var/mob/living/carbon/brain/caught_soul/L in brainmobs)
+ L.ext_deaf = !flag_check(NIF_SC_ALLOW_EARS)
+ if(flag_check(NIF_SC_ALLOW_EARS))
+ notify_holder("External sounds enabled.")
+ else
+ notify_holder("External sounds disabled.")
+
+// Sight toggling
+/obj/soulgem/proc/soulgem_sight()
+ for(var/mob/living/carbon/brain/caught_soul/L in brainmobs)
+ L.ext_blind = !flag_check(NIF_SC_ALLOW_EYES)
+ if(flag_check(NIF_SC_ALLOW_EYES))
+ notify_holder("External vision enabled.")
+ else
+ notify_holder("External vision disabled.")
+
+// Projecting toggling
+/obj/soulgem/proc/soulgem_projecting()
+ if(flag_check(NIF_SC_PROJECTING))
+ notify_holder("AR projecting enabled.")
+ else
+ notify_holder("AR projecting disabled.")
+
+// VORE FX Section
+
+// Updates the vore FX signal links to the new given belly
+/obj/soulgem/proc/update_linked_belly(var/obj/belly, var/skip_unreg = FALSE)
+ if(!belly && linked_belly)
+ UnregisterSignal(linked_belly, COMSIG_BELLY_UPDATE_VORE_FX)
+ linked_belly = null
+ return
+ if(!isbelly(belly))
+ return
+ if(!linked_belly)
+ linked_belly = belly
+ RegisterSignal(linked_belly, COMSIG_BELLY_UPDATE_VORE_FX, PROC_REF(soulgem_vfx))
+ return
+ if(belly != linked_belly)
+ if(!skip_unreg)
+ UnregisterSignal(linked_belly, COMSIG_BELLY_UPDATE_VORE_FX)
+ linked_belly = belly
+ RegisterSignal(linked_belly, COMSIG_BELLY_UPDATE_VORE_FX, PROC_REF(soulgem_vfx))
+
+// Handles the vore fx updates for the captured souls
+/obj/soulgem/proc/soulgem_vfx(var/update, var/severity = 0)
+ if(linked_belly)
+ for(var/mob/living/L in brainmobs)
+ if(flag_check(SOULGEM_SHOW_VORE_SFX))
+ show_vore_fx(L, update, severity)
+ else
+ clear_vore_fx(L)
+ if(flag_check(SOULGEM_SHOW_VORE_SFX))
+ notify_holder("Interior simulation enabled.")
+ else
+ notify_holder("Interior simulation disabled.")
+
+// Function to show the vore fx overlay
+/obj/soulgem/proc/show_vore_fx(var/mob/living/L, var/update, var/severity = 0)
+ if(!linked_belly || !flag_check(SOULGEM_SHOW_VORE_SFX))
+ return
+ if(!istype(L) || L.eyeobj)
+ return
+ linked_belly.vore_fx(L, update, severity)
+
+// Function to clear the vore fx overlay
+/obj/soulgem/proc/clear_vore_fx(var/mob/M)
+ M.clear_fullscreen("belly")
+ if(M.hud_used && !M.hud_used.hud_shown)
+ M.toggle_hud_vis(TRUE)
+
+// Takeover section
+
+// Give control of the body to the current selected soul
+/obj/soulgem/proc/take_control_selected()
+ if(!selected_soul) return
+ take_control(selected_soul)
+ if(owner.mind == own_mind)
+ own_mind = null
+ taken_over_name = null
+
+// Give back control of the body to the owner
+/obj/soulgem/proc/take_control_owner()
+ var/mob/self = null
+ for(var/mob/mob in brainmobs)
+ if(mob.mind == own_mind)
+ self = mob
+ break
+ if(!self)
+ return
+ take_control(self)
+ own_mind = null
+ taken_over_name = null
+
+/obj/soulgem/proc/take_control(var/mob/M)
+ if(!(owner.soulcatcher_pref_flags & SOULCATCHER_ALLOW_CAPTURE) || !(owner.soulcatcher_pref_flags & SOULCATCHER_ALLOW_TAKEOVER)) return
+ if(!(M.soulcatcher_pref_flags & SOULCATCHER_ALLOW_CAPTURE) || !(M.soulcatcher_pref_flags & SOULCATCHER_ALLOW_TAKEOVER)) return
+ if(!own_mind)
+ if(issilicon(owner) || isanimal(owner))
+ taken_over_name = owner.name
+ catch_mob(owner, taken_over_name)
+ taken_over_name = M.name
+ M.mind.transfer_to(owner)
brainmobs -= M
- M.ghostize(FALSE)
+ if(M == selected_soul)
+ update_selected_soul()
qdel(M)
+// Funtion to test if the owner's body has been taken over
+/obj/soulgem/proc/is_taken_over()
+ return (own_mind && owner.mind != own_mind)
+
+// Transfer section to transfer captured souls
+
+// Returns nearby,valid transfer locations as a list
/obj/soulgem/proc/find_transfer_objects()
var/list/valid_trasfer_objects = list(
/obj/item/device/sleevemate,
@@ -244,6 +402,7 @@
return
return valid_objects
+// Transfer the selected soul to either a valid object or another soulcatcher
/obj/soulgem/proc/transfer_selected()
if(!selected_soul) return
if(!(selected_soul.soulcatcher_pref_flags & SOULCATCHER_ALLOW_TRANSFER)) return
@@ -264,7 +423,9 @@
return
transfer_mob(selected_soul, target)
+// Transfers a captured soul to a valid object (sleevemate, mmi)
/obj/soulgem/proc/transfer_mob(var/mob/M, var/obj/target)
+ if(is_taken_over()) return
if(!M || !target) return
if(istype(target, /obj/item/device/sleevemate))
var/obj/item/device/sleevemate/mate = target
@@ -272,7 +433,7 @@
to_chat(owner, span_notice("You scan yourself to transfer the soul into the [target]!"))
to_chat(M, span_notice("[transfer_message]"))
mate.get_mind(M)
- else if(istype(M, /obj/item/device/mmi))
+ else if(istype(target, /obj/item/device/mmi))
var/obj/item/device/mmi/mm = target
if(!mm.brainmob)
to_chat(owner, span_notice("You transfer the soul into the [target]!"))
@@ -284,96 +445,71 @@
own_mind = null
brainmobs -= M
if(M == selected_soul)
- if(brainmobs.len > 1)
- selected_soul = brainmobs[1]
- else
- selected_soul = null
+ update_selected_soul()
qdel(M)
+// Transfers a captured soul to another soulcatcher
/obj/soulgem/proc/transfer_mob_soulcatcher(var/mob/living/carbon/brain/caught_soul/vore/M, var/obj/soulgem/gem)
+ if(is_taken_over()) return
if(!istype(M) || !gem) return
+ if(M.mind == own_mind)
+ own_mind = null
brainmobs -= M
M.gem = gem
M.container = gem
gem.brainmobs += M
+ if(M == selected_soul)
+ update_selected_soul()
+// Release section
+
+// Release the selected soul as a ghost
+/obj/soulgem/proc/release_selected()
+ if(!selected_soul) return
+ if(release_mob(selected_soul))
+ update_selected_soul()
+
+// Release all captured souls as ghosts
+/obj/soulgem/proc/release_mobs()
+ selected_soul = null
+ if(!brainmobs.len) return
+ for(var/mob/M in brainmobs)
+ release_mob(M)
+
+// Proc to release the soul as ghost, returns TRUE on success
+/obj/soulgem/proc/release_mob(var/mob/M)
+ if(is_taken_over()) return FALSE
+ to_chat(M, span_notice("[release_message]"))
+ brainmobs -= M
+ M.ghostize(FALSE)
+ qdel(M)
+ return TRUE
+
+// Delete section to delete captured souls from the soulcatcher
+
+// Delete the selected mob
/obj/soulgem/proc/delete_selected()
if(!selected_soul) return
- delete_mob(selected_soul)
- if(brainmobs.len > 1)
- selected_soul = brainmobs[1]
- else
- selected_soul = null
+ if(delete_mob(selected_soul))
+ update_selected_soul()
+// Delete all captured mobs
/obj/soulgem/proc/erase_mobs()
if(!brainmobs.len) return
for(var/mob/M in brainmobs)
delete_mob(M)
+// The function handling the actual delete, returns TRUE on success
/obj/soulgem/proc/delete_mob(var/mob/M)
+ if(is_taken_over()) return FALSE
if(!(M.soulcatcher_pref_flags & SOULCATCHER_ALLOW_DELETION))
- release_mob(M)
- return
+ return release_mob(M)
if(!(M.soulcatcher_pref_flags & SOULCATCHER_ALLOW_DELETION_INSTANT))
- if(!(tgui_alert(M, "Do you really want to allow [owner] to delete you? On decline, you'll be ghosted.", "Allow Deletion", list("No", "Yes")) == "Yes"))
- release_mob(M)
- return
+ if(tgui_alert(M, "Do you really want to allow [owner] to delete you? On decline, you'll be ghosted.", "Allow Deletion", list("No", "Yes"), timeout=1 MINUTES) != "Yes")
+ return release_mob(M)
to_chat(M, span_danger("[delete_message]"))
brainmobs -= M
var/mob/observer/dead/ghost = M.ghostize(FALSE)
ghost.abandon_mob()
qdel(M)
-
-/obj/soulgem/proc/adjust_interior(var/new_flavor)
- new_flavor = sanitize(new_flavor, MAX_MESSAGE_LEN * 2)
- inside_flavor = new_flavor
- notify_holder("Updating environment...")
- for(var/mob/living/carbon/brain/caught_soul/vore/CS as anything in brainmobs)
- to_chat(CS, span_notice("[transit_message]") + "\n[inside_flavor]")
-
-/obj/soulgem/proc/return_to_body(var/datum/mind)
- if(own_mind != mind)
- to_chat(src, span_warning("You aren't in your own soulcatcher!"))
- return
- var/mob/self = null
- for(var/mob/mob in brainmobs)
- if(mob.mind == mind)
- self = mob
- break
- if(!self)
- return
- self.mind.transfer_to(owner)
- own_mind = null
- qdel(self)
-
-/obj/soulgem/proc/set_custom_message(var/message, var/target)
- message = sanitize(message, MAX_MESSAGE_LEN / 4)
- switch(target)
- if("capture")
- capture_message = message
- if("transit")
- transit_message = message
- if("release")
- release_message = message
- if("transfer")
- transfer_message = message
- if("delete")
- delete_message = message
-
-/obj/soulgem/proc/rename(var/new_name)
- if(length(new_name) < 3 || length(new_name) > 60)
- to_chat(owner, span_warning("Your soulcatcher's name needs to be between 3 and 60 characters long!"))
- return
- new_name = sanitize(new_name, 60)
- name = new_name
-
-/obj/soulgem/proc/toggle_setting(var/flag)
- setting_flags ^= flag
- if(flag & SOULGEM_SHOW_VORE_SFX)
- soulgem_vfx(TRUE)
-
-// Checks a single flag, or an entire list if all elements in it are true
-/obj/soulgem/proc/flag_check(var/flag, var/match_all = FALSE)
- if(match_all)
- return (setting_flags & flag) == flag
- return setting_flags & flag
+ return TRUE
diff --git a/modular_chomp/code/modules/vore/eating/soulcatcher_mob.dm b/modular_chomp/code/modules/vore/eating/soulcatcher_mob.dm
index 25fa178e8e..0a107dd216 100644
--- a/modular_chomp/code/modules/vore/eating/soulcatcher_mob.dm
+++ b/modular_chomp/code/modules/vore/eating/soulcatcher_mob.dm
@@ -4,6 +4,7 @@
var/obj/soulgem/gem
+// Cleaning up the refs during deletion
/mob/living/carbon/brain/caught_soul/vore/Destroy()
if(gem)
gem.notify_holder("Mind unloaded: [name]")
@@ -15,26 +16,48 @@
container = null
return ..()
+// Handling the automatic transcore backups in a set interval
/mob/living/carbon/brain/caught_soul/vore/Life()
. = ..()
+ if(QDELETED(src))
+ return
if(!parent_mob && !transient &&(life_tick % 150 == 0) && gem.setting_flags & NIF_SC_BACKUPS)
SStranscore.m_backup(mind,0) //Passed 0 means "Don't touch the nif fields on the mind record"
+ if(!client)
+ return
+
+ if(ext_blind)
+ eye_blind = 5
+ client.screen.Remove(global_hud.whitense)
+ overlay_fullscreen("blind", /obj/screen/fullscreen/blind)
+ else
+ eye_blind = 0
+ clear_fullscreen("blind")
+ if(!gem.flag_check(SOULGEM_SHOW_VORE_SFX))
+ client.screen.Add(global_hud.whitense)
+ if(gem.flag_check(SOULGEM_SHOW_VORE_SFX))
+ client.screen.Remove(global_hud.whitense)
+
+// Say proc for captures souls
/mob/living/carbon/brain/caught_soul/vore/say(var/message, var/datum/language/speaking = null, var/whispering = 0)
if(silent) return FALSE
gem.use_speech(message, src, eyeobj)
+// Emote proc for captured souls
/mob/living/carbon/brain/caught_soul/vore/custom_emote(var/m_type, var/message)
if(silent) return FALSE
gem.use_emote(message,src,eyeobj)
+// Resist override, only returning a message that one is stuck for now
/mob/living/carbon/brain/caught_soul/vore/resist()
set name = "Resist"
set category = "IC.Game" //CHOMPEdit
to_chat(src, span_warning("There's no way out! You're stuck inside your predator."))
+// Allows the predator to enter their own soulcatcher
/mob/proc/enter_soulcatcher()
set name = "Enter Soulcatcher"
set desc = "Enter your own Soulcatcher."
@@ -43,8 +66,12 @@
if(!soulgem) // Only sanity...
return
if(soulgem && soulgem.flag_check(SOULGEM_ACTIVE))
- soulgem.catch_mob(src)
+ var/to_use_custom_name = null
+ if(issilicon(src) || isanimal(src))
+ to_use_custom_name = src.name
+ soulgem.catch_mob(src, to_use_custom_name)
+// Speak to the captured souls within the own soulcatcher
/mob/proc/nsay_vore()
set name = "NSay Vore"
set desc = "Speak into your Soulcatcher."
@@ -67,6 +94,7 @@
var/sane_message = sanitize(message)
gem.use_speech(sane_message, src)
+// Emote to the captured souls within the soulcatcher
/mob/proc/nme_vore()
set name = "NMe Vore"
set desc = "Emote into your Soulcatcher."
@@ -90,6 +118,7 @@
var/sane_message = sanitize(message)
gem.use_emote(sane_message, src)
+// AR project as captured soul
/mob/living/carbon/brain/caught_soul/vore/ar_project()
set name = "AR Project"
set desc = "Project your form into Augmented Reality for those around your predator with the appearance of your loaded character."
@@ -110,6 +139,7 @@
gem.notify_holder("[src] now AR projecting.")
gem.clear_vore_fx(src)
+// Jump to the owner as AR projection
/mob/living/carbon/brain/caught_soul/vore/jump_to_owner()
set name = "Jump to Owner"
set desc = "Jump your projection back to the owner of the soulcatcher you're inside."
@@ -121,6 +151,7 @@
eyeobj.forceMove(get_turf(gem))
+// End AR projecting and return to the soulcatcher containing the soul
/mob/living/carbon/brain/caught_soul/vore/reenter_soulcatcher()
set name = "Re-enter Soulcatcher"
set desc = "Leave AR projection and drop back into the soulcatcher."
@@ -154,6 +185,7 @@
var/sane_message = sanitize(message)
gem.use_emote(sane_message, src)
+// Allows the captured owner to transfer themselves to valid nearby objects
/mob/living/carbon/brain/caught_soul/vore/proc/transfer_self()
set name = "Transfer Self"
set desc = "Transfer youself while being in your own soulcatcher into a nearby Sleevemate or MMI."
@@ -172,6 +204,7 @@
var/obj/item/target = tgui_input_list(src, "Select where you want to store your own mind into.", "Mind Transfer Target", valid_objects)
gem.transfer_mob(src, target)
+// Allows the owner to reenter the body after being caught or having given away control
/mob/living/carbon/brain/caught_soul/vore/proc/reenter_body()
set name = "Re-enter Body"
set desc = "Return to your body after self capturing."
diff --git a/modular_chomp/code/modules/vore/eating/soulcatcher_observer.dm b/modular_chomp/code/modules/vore/eating/soulcatcher_observer.dm
new file mode 100644
index 0000000000..6b0580ab40
--- /dev/null
+++ b/modular_chomp/code/modules/vore/eating/soulcatcher_observer.dm
@@ -0,0 +1,52 @@
+/mob/observer/dead/verb/soulcatcherjoin()
+ set category = "Ghost.Join"
+ set name = "Join Into Soulcatcher(Vore)"
+ set desc = "Select a player with enabled Soulcatcher to join."
+
+ var/list/valid_players = list()
+ for(var/mob/player in player_list)
+ if(player.soulgem?.flag_check(SOULGEM_ACTIVE | SOULGEM_CATCHING_GHOSTS, TRUE))
+ valid_players += player
+
+ if(!valid_players.len)
+ to_chat(src, span_warning("No valid players found."))
+ return
+
+ var/picked = tgui_input_list(usr, "Pick a friend with enabled Soulcatcher to join into. Harrass strangers, get banned.","Select a player", valid_players)
+
+ if(!picked)
+ return
+
+ if(!ismob(picked))
+ to_chat(src, span_warning("Target is no mob."))
+ return
+
+ var/mob/M = picked
+
+ var/obj/soulgem/gem = M.soulgem
+
+ if(!gem?.flag_check(SOULGEM_ACTIVE))
+ to_chat(src, span_warning("[M] has no enabled Soulcatcher."))
+ return
+
+ var/req_time = world.time
+ gem.notify_holder("Transient mindstate detected, analyzing...")
+ sleep(15) //So if they are typing they get interrupted by sound and message, and don't type over the box
+ if(tgui_alert(M,"[src.name] wants to join into your Soulcatcher.","Soulcatcher Request",list("Deny","Allow"), timeout=1 MINUTES) != "Allow")
+ to_chat(src, span_warning("[M] has denied your request."))
+ return
+
+ if((world.time - req_time) > 1 MINUTES)
+ to_chat(M, span_warning("The request had already expired. (1 minute waiting max)"))
+ return
+
+ //Final check since we waited for input a couple times.
+ if(M && src && src.key && !M.stat && gem?.flag_check(SOULGEM_ACTIVE | SOULGEM_CATCHING_GHOSTS, TRUE))
+ if(!mind) //No mind yet, aka haven't played in this round.
+ mind = new(key)
+
+ mind.name = name
+ mind.current = src
+ mind.active = TRUE
+
+ gem.catch_mob(src) //This will result in us being deleted so...
diff --git a/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreAbilities.tsx b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreAbilities.tsx
new file mode 100644
index 0000000000..ce56c8a631
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreAbilities.tsx
@@ -0,0 +1,56 @@
+import { toFixed } from 'common/math';
+
+import { useBackend } from '../../../backend';
+import { Flex, LabeledList, Section, Slider } from '../../../components';
+import { abilities } from './types';
+
+export const VoreAbilities = (props: { abilities: abilities }) => {
+ const { act } = useBackend();
+
+ const { abilities } = props;
+
+ const { nutrition, current_size, minimum_size, maximum_size, resize_cost } =
+ abilities;
+
+ function is_enabled(nutri: number, cost: number): boolean {
+ return nutri >= cost;
+ }
+
+ return (
+
+
+
+
+
+ toFixed(value, 2) + '%'}
+ value={current_size * 100}
+ minValue={minimum_size * 100}
+ maxValue={maximum_size * 100}
+ onChange={(e, value: number) =>
+ act('adjust_own_size', {
+ new_mob_size: value / 100,
+ })
+ }
+ />
+
+ Cost:
+ {resize_cost}
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcher.tsx b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcher.tsx
index 57241950c6..797f1e5868 100644
--- a/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcher.tsx
+++ b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcher.tsx
@@ -1,25 +1,27 @@
-import { toFixed } from 'common/math';
-
import { useBackend } from '../../../backend';
import {
Box,
Button,
Dropdown,
- Flex,
LabeledList,
Section,
- Slider,
Stack,
} from '../../../components';
-import { bellyData, Data, DropdownEntry, soulcatcherData } from './types';
+import { abilities, bellyData, DropdownEntry, soulcatcherData } from './types';
+import { VoreAbilities } from './VoreAbilities';
+import { CatchSettings } from './VoreSoulcatcherSettings/CatchSettings';
+import { GlobalOptions } from './VoreSoulcatcherSettings/GlobalOptions';
+import { GlobalSettings } from './VoreSoulcatcherSettings/GlobalSettings';
+import { SoulOptions } from './VoreSoulcatcherSettings/SoulOptions';
export const VoreSoulcatcher = (props: {
soulcatcher: soulcatcherData | null;
our_bellies: Required & {
map(arg0: (belly: bellyData) => DropdownEntry): DropdownEntry[];
};
+ abilities: abilities;
}) => {
- const { soulcatcher, our_bellies } = props;
+ const { soulcatcher, our_bellies, abilities } = props;
const getBellies = our_bellies.map((belly) => {
return { ...belly, displayText: belly.name, value: belly.ref };
@@ -33,7 +35,7 @@ export const VoreSoulcatcher = (props: {
overlayBellies={getBellies}
/>
)}
-
+
>
);
};
@@ -42,7 +44,7 @@ const VoreSoulcatcherSection = (props: {
soulcatcher: soulcatcherData;
overlayBellies: DropdownEntry[];
}) => {
- const { act } = useBackend();
+ const { act } = useBackend();
const { soulcatcher, overlayBellies } = props;
@@ -54,12 +56,15 @@ const VoreSoulcatcherSection = (props: {
interior_design,
catch_self,
catch_prey,
+ catch_drain,
+ catch_ghost,
ext_hearing,
ext_vision,
mind_backups,
ar_projecting,
selected_sfx,
show_vore_sfx,
+ taken_over,
} = soulcatcher;
return (
@@ -108,157 +113,21 @@ const VoreSoulcatcherSection = (props: {
- {selected_soul ? (
-
-
-
-
- act('soulcatcher_delete')}
- >
- Delete
-
-
-
- ) : (
- ''
- )}
- {caught_souls.length ? (
-
-
- act('soulcatcher_release_all')}
- >
- Release Souls
-
- act('soulcatcher_erase_all')}
- >
- Erase Souls
-
-
-
- ) : (
- ''
- )}
-
-
-
-
-
-
-
-
-
-
-
+ {selected_soul ? : ''}
+ {caught_souls.length ? : ''}
+
+
);
};
-
-const VoreAbilities = (props) => {
- const { act, data } = useBackend();
- const { nutrition, current_size, minimum_size, maximum_size, resize_cost } =
- data.abilities;
-
- function is_enabled(nutri: number, cost: number): boolean {
- return nutri >= cost;
- }
-
- return (
-
-
-
-
-
- toFixed(value, 2) + '%'}
- value={current_size * 100}
- minValue={minimum_size * 100}
- maxValue={maximum_size * 100}
- onChange={(e, value: number) =>
- act('adjust_own_size', {
- new_mob_size: value / 100,
- })
- }
- />
-
- Cost:
- {resize_cost}
-
-
-
-
- );
-};
diff --git a/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/CatchSettings.tsx b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/CatchSettings.tsx
new file mode 100644
index 0000000000..8cd73ab825
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/CatchSettings.tsx
@@ -0,0 +1,70 @@
+import { BooleanLike } from 'common/react';
+
+import { useBackend } from '../../../../backend';
+import { Box, Button, LabeledList } from '../../../../components';
+
+export const CatchSettings = (props: {
+ catch_self: BooleanLike;
+ catch_prey: BooleanLike;
+ catch_drain: BooleanLike;
+ catch_ghost: BooleanLike;
+}) => {
+ const { act } = useBackend();
+
+ const { catch_self, catch_prey, catch_drain, catch_ghost } = props;
+
+ return (
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/GlobalOptions.tsx b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/GlobalOptions.tsx
new file mode 100644
index 0000000000..1fbe70d4a7
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/GlobalOptions.tsx
@@ -0,0 +1,51 @@
+import { BooleanLike } from 'common/react';
+
+import { useBackend } from '../../../../backend';
+import { Box, Button, LabeledList } from '../../../../components';
+
+export const GlobalOptions = (props: { taken_over: BooleanLike }) => {
+ const { act } = useBackend();
+
+ const { taken_over } = props;
+
+ return (
+
+
+ {!taken_over ? (
+ <>
+ act('soulcatcher_release_all')}
+ >
+ Release Souls
+
+ act('soulcatcher_erase_all')}
+ >
+ Erase Souls
+
+ >
+ ) : (
+
+ )}
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/GlobalSettings.tsx b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/GlobalSettings.tsx
new file mode 100644
index 0000000000..65e3baf6f6
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/GlobalSettings.tsx
@@ -0,0 +1,88 @@
+import { BooleanLike } from 'common/react';
+
+import { useBackend } from '../../../../backend';
+import { Box, Button, LabeledList } from '../../../../components';
+
+export const GlobalSettings = (props: {
+ ext_hearing: BooleanLike;
+ ext_vision: BooleanLike;
+ mind_backups: BooleanLike;
+ ar_projecting: BooleanLike;
+ show_vore_sfx: BooleanLike;
+}) => {
+ const { act } = useBackend();
+
+ const {
+ ext_hearing,
+ ext_vision,
+ mind_backups,
+ ar_projecting,
+ show_vore_sfx,
+ } = props;
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/SoulOptions.tsx b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/SoulOptions.tsx
new file mode 100644
index 0000000000..9131ac3696
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreSoulcatcherSettings/SoulOptions.tsx
@@ -0,0 +1,59 @@
+import { BooleanLike } from 'common/react';
+
+import { useBackend } from '../../../../backend';
+import { Box, Button, LabeledList } from '../../../../components';
+
+export const SoulOptions = (props: { taken_over: BooleanLike }) => {
+ const { act } = useBackend();
+
+ const { taken_over } = props;
+
+ return (
+
+
+ {!taken_over ? (
+ <>
+
+
+ act('soulcatcher_delete')}
+ >
+ Delete
+
+ >
+ ) : (
+ ''
+ )}
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreUserPreferences.tsx b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreUserPreferences.tsx
index d04ce9d009..a9e4e57443 100644
--- a/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreUserPreferences.tsx
+++ b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreUserPreferences.tsx
@@ -66,6 +66,7 @@ export const VoreUserPreferences = (props: {
soulcatcher_allow_capture,
soulcatcher_allow_transfer,
soulcatcher_allow_deletion,
+ soulcatcher_allow_takeover,
} = prefs;
const preferences: localPrefs = {
@@ -628,6 +629,20 @@ export const VoreUserPreferences = (props: {
disabled: 'Do Not Allow Soul Capturing',
},
},
+ soulcatcher_takeover: {
+ action: 'toggle_soulcatcher_allow_takeover',
+ test: soulcatcher_allow_takeover,
+ tooltip: {
+ main: 'This button is for allowing or preventing to give body control to captured souls.',
+ enable:
+ 'Click here to allow body takeovers. (Both parties need it enabled to function)',
+ disable: 'Click here to prevent body takeovers.',
+ },
+ content: {
+ enabled: 'Body Takeover Allowed',
+ disabled: 'Do Not Allow Body Takeover',
+ },
+ },
soulcatcher_transfer: {
action: 'toggle_soulcatcher_allow_transfer',
test: soulcatcher_allow_transfer,
diff --git a/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreUserPreferencesTabs/VoreUserPreferencesSoulcatcher.tsx b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreUserPreferencesTabs/VoreUserPreferencesSoulcatcher.tsx
index f4a5affb8e..f764e8ebf1 100644
--- a/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreUserPreferencesTabs/VoreUserPreferencesSoulcatcher.tsx
+++ b/tgui/packages/tgui/interfaces/chompstation/VorePanel/VoreUserPreferencesTabs/VoreUserPreferencesSoulcatcher.tsx
@@ -32,10 +32,16 @@ export const VoreUserPreferencesSoulcatcher = (props: {
+
+
+
) : (
''
diff --git a/tgui/packages/tgui/interfaces/chompstation/VorePanel/index.tsx b/tgui/packages/tgui/interfaces/chompstation/VorePanel/index.tsx
index dea76770ea..8a6c5b0a7a 100644
--- a/tgui/packages/tgui/interfaces/chompstation/VorePanel/index.tsx
+++ b/tgui/packages/tgui/interfaces/chompstation/VorePanel/index.tsx
@@ -157,6 +157,7 @@ export const VorePanel = () => {
our_bellies,
selected,
soulcatcher,
+ abilities,
prefs,
show_pictures,
icon_overflow,
@@ -177,7 +178,11 @@ export const VorePanel = () => {
/>
);
tabs[1] = (
-
+
);
tabs[2] = (