From ebd00b43bd844ae3c468587089c930da0eaca592 Mon Sep 17 00:00:00 2001
From: Kashargul <144968721+Kashargul@users.noreply.github.com>
Date: Sun, 2 Nov 2025 16:50:02 +0100
Subject: [PATCH] modernizes the belly content interactions (#18659)
* modernizes the belly content interactions
* should use stack items
* .
* .
* .
* .
* .
* search
* .
* .
* .
---
.../panel_databackend/vorepanel_vore_data.dm | 23 ++
code/modules/vore/eating/vorepanel_vr.dm | 22 +-
.../VorePanelMainTabs/VoreSelectedBelly.tsx | 2 +-
.../VorePanelMainTabs/VoreUserPreferences.tsx | 15 +-
.../VoreContentsPanel.tsx | 376 +++++++++++++-----
.../VoreUserPreferencesMechanical.tsx | 32 +-
.../tgui/interfaces/VorePanel/constants.ts | 3 +-
.../tgui/interfaces/VorePanel/functions.ts | 112 ++++++
.../tgui/interfaces/VorePanel/index.tsx | 8 +-
.../tgui/interfaces/VorePanel/types.ts | 9 +
10 files changed, 447 insertions(+), 155 deletions(-)
diff --git a/code/modules/vore/eating/panel_databackend/vorepanel_vore_data.dm b/code/modules/vore/eating/panel_databackend/vorepanel_vore_data.dm
index e05875b8465..611de1bd7cd 100644
--- a/code/modules/vore/eating/panel_databackend/vorepanel_vore_data.dm
+++ b/code/modules/vore/eating/panel_databackend/vorepanel_vore_data.dm
@@ -63,6 +63,11 @@
for(var/atom/movable/O in inside_belly)
if(O == owner)
continue
+ var/our_type
+ if(isliving(O))
+ our_type = "Living"
+ else if(isitem(O))
+ our_type = "Item"
var/list/info = list(
"name" = "[O]",
@@ -70,6 +75,7 @@
"stat" = 0,
"ref" = "\ref[O]",
"outside" = FALSE,
+ "our_type" = our_type
)
if(show_pictures) //disables icon mode
if(inside_belly.contents.len <= max_icon_content)
@@ -292,6 +298,22 @@
var/list/selected_contents
var/total_content_count = 0
for(var/O in selected)
+
+ // Please don't pass the options as a list... let TGUI handle that
+ var/our_type
+ if(ishuman(O))
+ our_type = "Human"
+
+ else if(isobserver(O) || istype(O,/obj/item/mmi))
+ our_type = "Obeserver"
+
+ else if(isliving(O))
+ var/mob/living/datarget = O
+ if(datarget.client)
+ our_type = "LivingC"
+ else
+ our_type = "Living"
+
total_content_count++
if(active_vore_tab == CONTENTS_TAB)
var/list/info = list(
@@ -300,6 +322,7 @@
"stat" = 0,
"ref" = "\ref[O]",
"outside" = TRUE,
+ "our_type" = our_type
)
if(show_pictures) //disables icon mode
if(selected.contents.len <= max_icon_content)
diff --git a/code/modules/vore/eating/vorepanel_vr.dm b/code/modules/vore/eating/vorepanel_vr.dm
index c5526e30f1a..f5548a4b91b 100644
--- a/code/modules/vore/eating/vorepanel_vr.dm
+++ b/code/modules/vore/eating/vorepanel_vr.dm
@@ -867,10 +867,16 @@
// Only allow indirect belly viewers to examine
if(user in OB)
if(isliving(target))
- intent = tgui_alert(user, "What do you want to do to them?","Query",list("Examine","Help Out","Devour"))
+ if(params["option"] in list("Examine","Help Out","Devour"))
+ intent = params["option"]
+ else
+ intent = tgui_alert(user, "What do you want to do to them?","Query",list("Examine","Help Out","Devour"))
- else if(istype(target, /obj/item))
- intent = tgui_alert(user, "What do you want to do to that?","Query",list("Examine","Use Hand"))
+ else if(isitem(target))
+ if(params["option"] in list("Examine","Use Hand"))
+ intent = params["option"]
+ else
+ intent = tgui_alert(user, "What do you want to do to that?","Query",list("Examine","Use Hand"))
//End of indirect vorefx changes
switch(intent)
@@ -989,7 +995,10 @@
if(datarget.client)
available_options += "Process"
available_options += "Health"
- intent = tgui_input_list(user, "What would you like to do with [target]?", "Vore Pick", available_options)
+ if((params["option"] in available_options))
+ intent = params["option"]
+ else
+ intent = tgui_input_list(user, "What would you like to do with [target]?", "Vore Pick", available_options)
switch(intent)
if("Examine")
var/list/results = target.examine(host)
@@ -1023,7 +1032,9 @@
if(host.stat)
to_chat(user,span_warning("You can't do that in your state!"))
return TRUE
- var/obj/belly/choice = tgui_input_list(user, "Move [target] where?","Select Belly", host.vore_organs)
+ var/obj/belly/choice = locate(params["targetBelly"])
+ if(!(choice in host.vore_organs))
+ choice = tgui_input_list(user, "Move [target] where?","Select Belly", host.vore_organs)
if(!choice || !(target in host.vore_selected))
return TRUE
to_chat(target,span_vwarning("You're squished from [host]'s [lowertext(host.vore_selected.name)] to their [lowertext(choice.name)]!"))
@@ -1082,6 +1093,7 @@
return FALSE
if(!H.allow_spontaneous_tf)
+ to_chat(user,span_warning("Your target can't be transformed!"))
return FALSE
var/datum/tgui_module/appearance_changer/vore/V = new(host, H)
diff --git a/tgui/packages/tgui/interfaces/VorePanel/VorePanelMainTabs/VoreSelectedBelly.tsx b/tgui/packages/tgui/interfaces/VorePanel/VorePanelMainTabs/VoreSelectedBelly.tsx
index 6e8795fa88a..9592767c68c 100644
--- a/tgui/packages/tgui/interfaces/VorePanel/VorePanelMainTabs/VoreSelectedBelly.tsx
+++ b/tgui/packages/tgui/interfaces/VorePanel/VorePanelMainTabs/VoreSelectedBelly.tsx
@@ -132,7 +132,7 @@ export const VoreSelectedBelly = (props: {
-
+
{tabs[activeVoreTab] || 'Error'}
diff --git a/tgui/packages/tgui/interfaces/VorePanel/VorePanelMainTabs/VoreUserPreferences.tsx b/tgui/packages/tgui/interfaces/VorePanel/VorePanelMainTabs/VoreUserPreferences.tsx
index 81a40df27fc..679df1b4954 100644
--- a/tgui/packages/tgui/interfaces/VorePanel/VorePanelMainTabs/VoreUserPreferences.tsx
+++ b/tgui/packages/tgui/interfaces/VorePanel/VorePanelMainTabs/VoreUserPreferences.tsx
@@ -1,5 +1,4 @@
import { Section } from 'tgui-core/components';
-import type { BooleanLike } from 'tgui-core/react';
import { digestModeToColor } from '../constants';
import type { localPrefs, prefData } from '../types';
@@ -10,12 +9,8 @@ import { VoreUserPreferencesSoulcatcher } from '../VoreUserPreferencesTabs/VoreU
import { VoreUserPreferencesSpawn } from '../VoreUserPreferencesTabs/VoreUserPreferencesSpawn';
import { VoreUserPreferencesSpontaneous } from '../VoreUserPreferencesTabs/VoreUserPreferencesSpontaneous';
-export const VoreUserPreferences = (props: {
- prefs: prefData;
- show_pictures: BooleanLike;
- icon_overflow: BooleanLike;
-}) => {
- const { prefs, show_pictures, icon_overflow } = props;
+export const VoreUserPreferences = (props: { prefs: prefData }) => {
+ const { prefs } = props;
const {
digestable,
absorbable,
@@ -715,11 +710,7 @@ export const VoreUserPreferences = (props: {
return (
-
+
{
const { act } = useBackend();
+ const [selectedAtom, setSelectedAtom] = useState(null);
+ const [contentSearchText, setContentSearchText] = useState('');
const {
contents = [],
@@ -34,6 +42,15 @@ export const VoreContentsPanel = (props: {
icon_overflow,
} = props;
+ useEffect(() => {
+ if (
+ selectedAtom &&
+ !contents?.some((item) => item.ref === selectedAtom.ref)
+ ) {
+ setSelectedAtom(null);
+ }
+ }, [contents]);
+
function bellyValueToName(value: string) {
const bellyName = bellyDropdownNames
?.map((entry) => {
@@ -49,111 +66,270 @@ export const VoreContentsPanel = (props: {
return '';
}
+ const contentSearch = createSearch(
+ contentSearchText,
+ (content: contentData) => content.name,
+ );
+ const displayedContents = contents?.filter(contentSearch);
+
return (
- <>
- {!!outside && (
+
-
-
- act('pick_from_outside', { pickall: true, intent: 'eject_all' })
- }
- >
- Eject All
-
+
+
-
-
- act('pick_from_outside', {
- pickall: true,
- intent: 'move_all',
- val: targetBelly,
- })
+
+
+ {show_pictures ? 'Picture View' : 'List View'}
+
- {!!bellyDropdownNames && !!onTargetBely && (
-
- onTargetBely(value)}
- options={bellyDropdownNames!}
- selected={bellyValueToName(targetBelly)}
- />
-
+
+ }
+ >
+
+ {!!outside && (
+
+
+
+
+ act('pick_from_outside', {
+ pickall: true,
+ intent: 'eject_all',
+ })
+ }
+ >
+ Eject All
+
+
+
+
+ act('pick_from_outside', {
+ pickall: true,
+ intent: 'move_all',
+ val: targetBelly,
+ })
+ }
+ >
+ Move All
+
+
+ {!!bellyDropdownNames && !!onTargetBely && (
+ <>
+ Destination:
+
+ onTargetBely(value)}
+ options={bellyDropdownNames!}
+ selected={bellyValueToName(targetBelly)}
+ />
+
+ >
+ )}
+
+
+ )}
+
+ {selectedAtom ? (
+
+ {ourTypeToOptions(
+ selectedAtom.our_type,
+ !!selectedAtom.outside,
+ bellyValueToName(targetBelly),
+ ).map((option) => (
+
+ {option.needsConfirm ? (
+
+ act(
+ selectedAtom.outside
+ ? 'pick_from_outside'
+ : 'pick_from_inside',
+ {
+ option: option.name,
+ pick: selectedAtom.ref,
+ targetBelly: targetBelly,
+ belly: belly,
+ },
+ )
+ }
+ >
+ {option.name}
+
+ ) : (
+
+ )}
+
+ ))}
+
+
+
+
+
+ ) : (
+ Select something to interact with it.
)}
-
- )}
- {(show_pictures && !icon_overflow && (
-
- {contents?.map((thing) => (
-
-
- {thing.name}
-
- ))}
-
- )) || (
-
- {contents?.map((thing) => (
-
-
-
- ))}
-
- )}
- >
+
+
+
+
+ {(show_pictures && !icon_overflow && (
+
+ {displayedContents?.map((thing) => (
+
+
+ {thing.ref === selectedAtom?.ref &&
+ (!!stats[thing.stat] || !!thing.absorbed) && (
+ <>
+
+
+ {' '}
+
+ >
+ )}
+ {thing.name}
+
+ ))}
+
+ )) || (
+
+ {displayedContents?.map((thing) => (
+
+
+
+ ))}
+
+ )}
+
+
+
+
);
};
diff --git a/tgui/packages/tgui/interfaces/VorePanel/VoreUserPreferencesTabs/VoreUserPreferencesMechanical.tsx b/tgui/packages/tgui/interfaces/VorePanel/VoreUserPreferencesTabs/VoreUserPreferencesMechanical.tsx
index 95ba10e562f..bb4e4ef97d9 100644
--- a/tgui/packages/tgui/interfaces/VorePanel/VoreUserPreferencesTabs/VoreUserPreferencesMechanical.tsx
+++ b/tgui/packages/tgui/interfaces/VorePanel/VoreUserPreferencesTabs/VoreUserPreferencesMechanical.tsx
@@ -1,41 +1,15 @@
-import { useBackend } from 'tgui/backend';
-import { Button, Section, Stack } from 'tgui-core/components';
-import type { BooleanLike } from 'tgui-core/react';
+import { Section, Stack } from 'tgui-core/components';
import type { localPrefs } from '../types';
import { VoreUserPreferenceItem } from '../VorePanelElements/VoreUserPreferenceItem';
export const VoreUserPreferencesMechanical = (props: {
- show_pictures: BooleanLike;
- icon_overflow: BooleanLike;
preferences: localPrefs;
}) => {
- const { act } = useBackend();
- const { show_pictures, icon_overflow, preferences } = props;
+ const { preferences } = props;
return (
- act('show_pictures')}
- >
- Contents Preference: {show_pictures ? 'Show Pictures' : 'Show List'}
-
- }
- >
+
= cost;
}
@@ -82,3 +84,113 @@ export async function paste_from_clipboard(
}
return ourText;
}
+
+export function ourTypeToOptions(
+ type: string,
+ outside: boolean,
+ belly?: string,
+): ActionButtonData[] {
+ const commonOption = {
+ name: 'Examine',
+ tooltip: 'Examine your current target.',
+ };
+
+ if (outside) {
+ const baseOptions = [
+ {
+ name: 'Eject',
+ color: 'yellow',
+ needsConfirm: true,
+ tooltip: 'Eject your current target at your location.',
+ },
+ {
+ name: 'Launch',
+ color: 'yellow',
+ needsConfirm: true,
+ tooltip: 'Eject your current target with some force in front of you.',
+ },
+ {
+ name: 'Move',
+ color: 'yellow',
+ needsConfirm: true,
+ disabled: !belly,
+ tooltip:
+ 'Move your current target towards your selected destination belly.',
+ },
+ {
+ name: 'Transfer',
+ color: 'yellow',
+ needsConfirm: true,
+ tooltip: 'Transfer your current target to a nearby person.',
+ },
+ ];
+ const interaction_options: ActionButtonData[] = [];
+ if (type === 'Human') {
+ interaction_options.push({
+ name: 'Transform',
+ color: 'purple',
+ needsConfirm: true,
+ tooltip: 'Transform your current target into something else.',
+ });
+ interaction_options.push({
+ name: 'Health Check',
+ tooltip: 'Check the health of your current target.',
+ });
+ }
+ if (type === 'Observer') {
+ interaction_options.push({
+ name: 'Reform',
+ color: 'green',
+ needsConfirm: true,
+ tooltip: 'Reform your current target.',
+ });
+ }
+ if (type === 'LivingC') {
+ interaction_options.push({
+ name: 'Process',
+ color: 'red',
+ needsConfirm: true,
+ tooltip: 'Process your current target instantly.',
+ });
+ interaction_options.push({
+ name: 'Health',
+ tooltip: 'Display the health of the current target.',
+ });
+ } else if (type === 'Living') {
+ interaction_options.push({
+ name: 'Health',
+ tooltip: 'Display the health of the current target.',
+ });
+ }
+ return [commonOption, ...baseOptions, ...interaction_options];
+ }
+ if (type === 'Living') {
+ return [
+ commonOption,
+ {
+ name: 'Help Out',
+ color: 'green',
+ needsConfirm: true,
+ tooltip: 'Help your current target to escape.',
+ },
+ {
+ name: 'Devour',
+ color: 'red',
+ needsConfirm: true,
+ tooltip: 'Devour your current target.',
+ },
+ ];
+ }
+ if (type === 'Item') {
+ return [
+ commonOption,
+ {
+ name: 'Use Hand',
+ color: 'yellow',
+ needsConfirm: true,
+ tooltip: 'Pick up your current target.',
+ },
+ ];
+ }
+ return [];
+}
diff --git a/tgui/packages/tgui/interfaces/VorePanel/index.tsx b/tgui/packages/tgui/interfaces/VorePanel/index.tsx
index ba17de5bc59..c282d7a441e 100644
--- a/tgui/packages/tgui/interfaces/VorePanel/index.tsx
+++ b/tgui/packages/tgui/interfaces/VorePanel/index.tsx
@@ -80,13 +80,7 @@ export const VorePanel = () => {
persist_edit_mode={persist_edit_mode}
/>
);
- tabs[3] = prefs && (
-
- );
+ tabs[3] = prefs && ;
return (
diff --git a/tgui/packages/tgui/interfaces/VorePanel/types.ts b/tgui/packages/tgui/interfaces/VorePanel/types.ts
index 1d968ab827c..b22f0c4d58e 100644
--- a/tgui/packages/tgui/interfaces/VorePanel/types.ts
+++ b/tgui/packages/tgui/interfaces/VorePanel/types.ts
@@ -204,6 +204,7 @@ export type contentData = {
ref: string;
outside: BooleanLike;
icon: string;
+ our_type: string;
};
export type bellyLiquidData = {
@@ -467,3 +468,11 @@ export type preferenceData = {
fluid?: boolean;
back_color?: { enabled: string; disabled: string };
};
+
+export type ActionButtonData = {
+ name: string;
+ tooltip: string;
+ disabled?: boolean;
+ color?: string;
+ needsConfirm?: boolean;
+};