diff --git a/modular_skyrat/modules/interaction_menu/code/interaction_component.dm b/modular_skyrat/modules/interaction_menu/code/interaction_component.dm index e7a7b78a917..32770b81dba 100644 --- a/modular_skyrat/modules/interaction_menu/code/interaction_component.dm +++ b/modular_skyrat/modules/interaction_menu/code/interaction_component.dm @@ -67,7 +67,7 @@ /datum/component/interactable/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "InteractionMenu") + ui = new(user, src, "InteractionPanel") ui.open() /datum/component/interactable/ui_status(mob/user, datum/ui_state/state) @@ -76,6 +76,11 @@ return UI_INTERACTIVE // This UI is always interactive as we handle distance flags via can_interact +/datum/component/interactable/ui_static_data(mob/user) + var/list/data = list() + data["arousalLimit"] = AROUSAL_LIMIT + return data + /datum/component/interactable/ui_data(mob/user) var/list/data = list() var/list/descriptions = list() @@ -106,6 +111,34 @@ data["self"] = body_relay.name data["block_interact"] = interact_next >= world.time data["interactions"] = categories + data["erp_interaction"] = self.client?.prefs?.read_preference(/datum/preference/toggle/erp) + + var/mob/living/carbon/human/human_user = user + + data["isTargetSelf"] = (user == self) + + // user (the one who opened the ui) + var/user_pleasure = 0 + var/user_arousal = 0 + var/user_pain = 0 + + if(user) + user_pleasure = human_user.pleasure + user_arousal = human_user.arousal + user_pain = human_user.pain + + data["pleasure"] = user_pleasure + data["arousal"] = user_arousal + data["pain"] = user_pain + data["yourName"] = human_user.real_name + + + // self - the one who the interaction component belongs to, aka who it's opened on (confusing var name yep) + if(user != self) + data["theirPleasure"] = self.pleasure + data["theirArousal"] = self.arousal + data["theirPain"] = self.pain + data["theirName"] = self.real_name var/list/parts = list() diff --git a/tgui/packages/tgui/interfaces/InteractionMenu.tsx b/tgui/packages/tgui/interfaces/InteractionMenu.tsx deleted file mode 100644 index 03e12c6a4ac..00000000000 --- a/tgui/packages/tgui/interfaces/InteractionMenu.tsx +++ /dev/null @@ -1,139 +0,0 @@ -// THIS IS A SKYRAT UI FILE -import { - Box, - Button, - Collapsible, - Icon, - NoticeBox, - Section, - Stack, -} from 'tgui-core/components'; - -import { useBackend } from '../backend'; -import { Window } from '../layouts'; - -class Interaction { - categories; - interactions; - descriptions; - colors; - lewd_slots: LewdSlot[]; - self; - ref_self; - ref_user; - block_interact; -} - -class LewdSlot { - img; - name; -} - -export const InteractionMenu = (props) => { - const { act, data } = useBackend(); - const { - categories = [], - interactions, - descriptions, - colors, - lewd_slots, - self, - ref_self, - ref_user, - block_interact, - } = data; - - return ( - - - {(block_interact && Unable to Interact) || ( - Able to Interact - )} - -
- {categories.map((category) => ( - -
- - {interactions[category].map((interaction) => ( -
-
- ))} -
- {lewd_slots.length > 0 ? ( -
- - {lewd_slots.map((element: LewdSlot) => { - return ( - - - - ); - })} - -
- ) : ( - '' - )} -
-
-
- ); -}; diff --git a/tgui/packages/tgui/interfaces/InteractionPanel/InfoSection.tsx b/tgui/packages/tgui/interfaces/InteractionPanel/InfoSection.tsx new file mode 100644 index 00000000000..defd1899120 --- /dev/null +++ b/tgui/packages/tgui/interfaces/InteractionPanel/InfoSection.tsx @@ -0,0 +1,110 @@ +// THIS IS A BUBBER UI FILE +import { Icon, ProgressBar, Section, Stack } from 'tgui-core/components'; +import type { BooleanLike } from 'tgui-core/react'; + +import { useBackend } from '../../backend'; + +type HeaderInfo = { + isTargetSelf: BooleanLike; + pleasure: number; + arousal: number; + pain: number; + theirPleasure: number; + theirArousal: number; + theirPain: number; + arousalLimit: number; + theirName: string; + yourName: string; +}; + +export const InfoSection = () => { + const { data } = useBackend(); + const { + isTargetSelf, + pleasure, + arousal, + pain, + theirPleasure, + theirArousal, + theirPain, + yourName, + theirName, + arousalLimit, + } = data; + return ( +
+ + + + {yourName} + {!isTargetSelf ? {theirName} : null} + + + + + + + + + Pleasure + + + + + Arousal + + + + + Pain + + + + + {!isTargetSelf ? ( + + + + + Pleasure + + + + + Arousal + + + + + Pain + + + + + ) : null} + + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/InteractionPanel/MainContent.tsx b/tgui/packages/tgui/interfaces/InteractionPanel/MainContent.tsx new file mode 100644 index 00000000000..435f2ee936b --- /dev/null +++ b/tgui/packages/tgui/interfaces/InteractionPanel/MainContent.tsx @@ -0,0 +1,94 @@ +// THIS IS A BUBBER UI FILE + +import { useState } from 'react'; +import { + Button, + Icon, + Input, + Section, + Stack, + Tabs, +} from 'tgui-core/components'; +import type { BooleanLike } from 'tgui-core/react'; +import { useBackend } from '../../backend'; + +type Interaction = { + erp_interaction: BooleanLike; +}; + +import { InteractionsTab, LewdItemsTab } from './tabs'; + +export const MainContent = () => { + const [searchText, setSearchText] = useState(''); + const [tabIndex, setTabIndex] = useState(0); + const [showCategories, setShowCategories] = useState(true); + const { data } = useBackend(); + const { erp_interaction } = data; + const placeholder = + tabIndex === 0 + ? 'Search for an interaction' + : tabIndex === 1 + ? 'Search for an item' + : 'Searching is unavailable for this tab'; + + return ( +
+ + + + setTabIndex(0)}> + Interactions + + {erp_interaction && ( + setTabIndex(1)} + > + Lewd Items + + )} + + + + + + + + + setSearchText(value)} + /> + + {tabIndex === 0 && ( + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/InteractionPanel/index.tsx b/tgui/packages/tgui/interfaces/InteractionPanel/index.tsx new file mode 100644 index 00000000000..2a937bf9150 --- /dev/null +++ b/tgui/packages/tgui/interfaces/InteractionPanel/index.tsx @@ -0,0 +1,39 @@ +// THIS IS A NOVA SECTOR UI FILE +import { Section, Stack } from 'tgui-core/components'; +import type { BooleanLike } from 'tgui-core/react'; +import { useBackend } from '../../backend'; +import { Window } from '../../layouts'; +import { InfoSection } from './InfoSection'; +import { MainContent } from './MainContent'; + +type Interaction = { + self; + erp_interaction: BooleanLike; +}; + +export function InteractionPanel() { + const { act, data } = useBackend(); + const { self, erp_interaction } = data; + + return ( + + + {erp_interaction && ( +
+ + + + + +
+ )} + + + + + + +
+
+ ); +} diff --git a/tgui/packages/tgui/interfaces/InteractionPanel/tabs/InteractionsTab.tsx b/tgui/packages/tgui/interfaces/InteractionPanel/tabs/InteractionsTab.tsx new file mode 100644 index 00000000000..5a5e1a3400e --- /dev/null +++ b/tgui/packages/tgui/interfaces/InteractionPanel/tabs/InteractionsTab.tsx @@ -0,0 +1,135 @@ +// THIS IS A BUBBER UI FILE +import { useMemo } from 'react'; +import { + Box, + Button, + Collapsible, + NoticeBox, + Section, + Stack, +} from 'tgui-core/components'; +import type { BooleanLike } from 'tgui-core/react'; + +import { useBackend } from '../../../backend'; + +type Interaction = { + categories: string[]; + interactions: Record; + descriptions: Record; + colors: Record; + self: string; + ref_self: string; + ref_user: string; + block_interact: BooleanLike; +}; + +type InteractionsTabProps = { + searchText: string; + showCategories: boolean; +}; + +export const InteractionsTab = (props: InteractionsTabProps) => { + const { act, data } = useBackend(); + const { + categories = [], + interactions = {}, + descriptions = {}, + colors = {}, + ref_self, + ref_user, + block_interact, + } = data; + const { searchText, showCategories } = props; + + const searchLower = searchText.toLowerCase(); + + const renderInteractionButton = (interaction: string) => { + return ( + + ); + }; + + const filterInteractions = (category: string) => { + let categoryInteractions = interactions[category] || []; + if (searchText) { + categoryInteractions = categoryInteractions.filter((interaction) => + interaction.toLowerCase().includes(searchLower), + ); + } + return categoryInteractions; + }; + + const allInteractions = useMemo(() => { + return categories.flatMap((category) => + filterInteractions(category).map((interaction) => ({ + name: interaction, + category, + })), + ); + }, [categories, searchLower]); + + return ( + + + {block_interact ? 'Unable to Interact' : 'Able to Interact'} + + + {showCategories ? ( + categories.map((category) => { + const filteredInteractions = filterInteractions(category); + if (filteredInteractions.length === 0) return null; + return ( + + {filteredInteractions.length} + {' interactions'} + + } + > +
+ + {filteredInteractions.map((interaction) => + renderInteractionButton(interaction), + )} + +
+
+ ); + }) + ) : ( +
+ + {allInteractions.map(({ name, category }) => + renderInteractionButton(name), + )} + +
+ )} +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/InteractionPanel/tabs/LewdItemsTab.tsx b/tgui/packages/tgui/interfaces/InteractionPanel/tabs/LewdItemsTab.tsx new file mode 100644 index 00000000000..f67ed97c317 --- /dev/null +++ b/tgui/packages/tgui/interfaces/InteractionPanel/tabs/LewdItemsTab.tsx @@ -0,0 +1,85 @@ +// THIS IS A BUBBER UI FILE +import { Box, Button, Icon, Stack } from 'tgui-core/components'; + +import { useBackend } from '../../../backend'; + +type LewdSlot = { + img: string; + name: string; + item_name: string; + lewd_slots: LewdSlot[]; + ref_self: string; + ref_user: string; +}; + +type LewdItemsTabPropsData = { + searchText: string; +}; + +export const LewdItemsTab = (props: LewdItemsTabPropsData) => { + const { act, data } = useBackend(); + const { lewd_slots = [], ref_self, ref_user } = data; + const { searchText } = props; + const searchLower = searchText.toLowerCase(); + + const filteredSlots = lewd_slots.filter((slot) => { + return ( + slot.name.toLowerCase().includes(searchLower) || + slot.item_name?.toLowerCase().includes(searchLower) + ); + }); + + return ( + + {filteredSlots.length > 0 && ( + + {filteredSlots.map((slot) => { + return ( + + + + ); + })} + + )} + + ); +}; diff --git a/tgui/packages/tgui/interfaces/InteractionPanel/tabs/index.tsx b/tgui/packages/tgui/interfaces/InteractionPanel/tabs/index.tsx new file mode 100644 index 00000000000..39e189eeaa8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/InteractionPanel/tabs/index.tsx @@ -0,0 +1,3 @@ +// THIS IS A BUBBER UI FILE +export { InteractionsTab } from './InteractionsTab'; +export { LewdItemsTab } from './LewdItemsTab';