From deaf37b8aeb424a69adbda6d982af53e37dc3637 Mon Sep 17 00:00:00 2001 From: SandPoot Date: Thu, 30 May 2024 19:43:16 -0300 Subject: [PATCH 1/2] push --- .../tgui/interfaces/MobInteraction.tsx | 906 ------------------ .../interfaces/MobInteraction/InfoSection.tsx | 73 ++ .../interfaces/MobInteraction/MainContent.tsx | 74 ++ .../tgui/interfaces/MobInteraction/index.tsx | 25 + .../MobInteraction/tabs/CharacterPrefsTab.tsx | 137 +++ .../tabs/ContentPreferencesTab.tsx | 295 ++++++ .../MobInteraction/tabs/GenitalTab.tsx | 127 +++ .../MobInteraction/tabs/InteractionsTab.tsx | 220 +++++ 8 files changed, 951 insertions(+), 906 deletions(-) delete mode 100644 tgui/packages/tgui/interfaces/MobInteraction.tsx create mode 100644 tgui/packages/tgui/interfaces/MobInteraction/InfoSection.tsx create mode 100644 tgui/packages/tgui/interfaces/MobInteraction/MainContent.tsx create mode 100644 tgui/packages/tgui/interfaces/MobInteraction/index.tsx create mode 100644 tgui/packages/tgui/interfaces/MobInteraction/tabs/CharacterPrefsTab.tsx create mode 100644 tgui/packages/tgui/interfaces/MobInteraction/tabs/ContentPreferencesTab.tsx create mode 100644 tgui/packages/tgui/interfaces/MobInteraction/tabs/GenitalTab.tsx create mode 100644 tgui/packages/tgui/interfaces/MobInteraction/tabs/InteractionsTab.tsx diff --git a/tgui/packages/tgui/interfaces/MobInteraction.tsx b/tgui/packages/tgui/interfaces/MobInteraction.tsx deleted file mode 100644 index 3b3826972d..0000000000 --- a/tgui/packages/tgui/interfaces/MobInteraction.tsx +++ /dev/null @@ -1,906 +0,0 @@ -import { filter, map, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; -import { clamp } from 'common/math'; -import { createSearch } from 'common/string'; -import { useBackend, useLocalState } from '../backend'; -import { BlockQuote, Button, Flex, Icon, Input, LabeledList, ProgressBar, Section, Table, Tabs, Stack } from '../components'; -import { TableCell, TableRow } from '../components/Table'; -import { Window } from '../layouts'; - -type HeaderInfo = { - isTargetSelf: boolean; - interactingWith: string; - lust: number; - maxLust: number; - selfAttributes: string[]; - theirAttributes: string[]; - theirLust: number; - theirMaxLust: number; -} - -type ContentInfo = { - interactions: InteractionData[]; - user_is_blacklisted: boolean; - target_is_blacklisted: boolean; -} - -type InteractionData = { - key: string; - desc: string; - type: number; - additionalDetails: string[]; -} - -type GenitalInfo = { - genitals: GenitalData[]; -} - -type GenitalData = { - name: string, - key: string, - visibility: string, - possible_choices: string[], - can_arouse: boolean, - arousal_state: boolean, - always_accessible: boolean, -} - -type CharacterPrefsInfo = { - erp_pref: number, - noncon_pref: number, - vore_pref: number, - extreme_pref: number, - extreme_harm: boolean, -} - -type ContentPrefsInfo = { - verb_consent: boolean, - lewd_verb_sounds: boolean, - arousable: boolean, - genital_examine: boolean, - vore_examine: boolean, - medihound_sleeper: boolean, - eating_noises: boolean, - digestion_noises: boolean, - trash_forcefeed: boolean, - forced_fem: boolean, - forced_masc: boolean, - hypno: boolean, - bimbofication: boolean, - breast_enlargement: boolean, - penis_enlargement: boolean, - butt_enlargement: boolean, - never_hypno: boolean, - no_aphro: boolean, - no_ass_slap: boolean, - no_auto_wag: boolean, -} - -const INTERACTION_NORMAL = 0; -const INTERACTION_LEWD = 1; -const INTERACTION_EXTREME = 2; - -const INTERACTION_FLAG_ADJACENT = (1<<0); -const INTERACTION_FLAG_EXTREME_CONTENT = (1<<1); -const INTERACTION_FLAG_OOC_CONSENT = (1<<2); -const INTERACTION_FLAG_TARGET_NOT_TIRED = (1<<3); -const INTERACTION_FLAG_USER_IS_TARGET = (1<<4); -const INTERACTION_FLAG_USER_NOT_TIRED = (1<<5); - -export const MobInteraction = (props, context) => { - const { act, data } = useBackend(context); - const { - isTargetSelf, - interactingWith, - lust, - maxLust, - selfAttributes, - theirAttributes, - theirLust, - theirMaxLust, - } = data; - const [ - searchText, - setSearchText, - ] = useLocalState(context, 'searchText', ''); - const [tabIndex, setTabIndex] = useLocalState(context, 'tabIndex', 0); - - return ( - - -
- -
- - -
- You...
- {selfAttributes.map(attribute => ( -
- {attribute}
-
- ))} -
-
- {!isTargetSelf ? ( - -
- They...
- {theirAttributes.map(attribute => ( -
- {attribute}
-
- ))} -
-
- ) : (null)} -
-
-
- - - - - {(!isTargetSelf && (theirLust !== null) ? ( - - - - ) : (null))} - -
-
-
-
- - setTabIndex(0)}> - Interactions - - setTabIndex(1)}> - Genital Options - - setTabIndex(2)}> - Character Prefs - - setTabIndex(3)}> - Preferences - - - - - - - - setSearchText(value)} /> - - - {tabIndex === 0 && ( - - ) || tabIndex === 1 && ( - - ) || tabIndex === 2 && ( - - ) || tabIndex === 3 && ( - - ) || ("Somehow, you've got into an invalid page, please report this.")} -
-
-
- ); -}; - -const InteractionsTab = (props, context) => { - const { act, data } = useBackend(context); - const [ - searchText, - setSearchText, - ] = useLocalState(context, 'searchText', ''); - const interactions = sortInteractions( - data.interactions, - searchText, - data) - || []; - const { user_is_blacklisted, target_is_blacklisted } = data; - return ( -
- - { - interactions.length ? ( - interactions.map((interaction) => ( - - - )))} - - - )) - ) : ( -
- { - user_is_blacklisted || target_is_blacklisted - ? `${user_is_blacklisted ? "Your" : "Their"} mob type is blacklisted from interactions` - : searchText ? "No matching results." - : "No interactions available." - } -
- ) - } -
-
- ); -}; - -/** - * Interaction sorter! also search box - */ -export const sortInteractions = (interactions, searchText = '', data) => { - const testSearch = createSearch(searchText, - interaction => interaction.desc); - const { - extreme_pref, - isTargetSelf, - target_has_active_player, - target_is_blacklisted, - theyAllowExtreme, - theyAllowLewd, - user_is_blacklisted, - verb_consent, - - - max_distance, - required_from_user, - required_from_user_exposed, - required_from_user_unexposed, - user_num_feet, - - required_from_target, - required_from_target_exposed, - required_from_target_unexposed, - target_num_feet, - } = data; - return flow([ - // Blacklists completely disable any and all interactions - filter(interaction => - !user_is_blacklisted && !target_is_blacklisted), - - // Optional search term, do before the others so we don't even run the tests - searchText && filter(testSearch), - - // Filter off interactions depending on pref - filter(interaction => - // Regular interaction - (interaction.type === INTERACTION_NORMAL ? true - // Lewd interaction - : interaction.type === INTERACTION_LEWD ? verb_consent - // Extreme interaction - : verb_consent && extreme_pref)), - - // Filter off interactions depending on target's pref - filter(interaction => - // If it's ourself, we've just checked it above, ignore - ((isTargetSelf || (target_has_active_player === 0)) ? true - // Regular interaction - : interaction.type === INTERACTION_NORMAL ? true - // Lewd interaction - : interaction.type === INTERACTION_LEWD ? theyAllowLewd - // Extreme interaction - : theyAllowLewd && theyAllowExtreme)), - - // Is self - filter(interaction => - (isTargetSelf ? (INTERACTION_FLAG_USER_IS_TARGET - & interaction.interactionFlags) - : !(INTERACTION_FLAG_USER_IS_TARGET & interaction.interactionFlags))), - // Has a player or none at all - filter(interaction => - (!isTargetSelf && (target_has_active_player === 1) - ? !(INTERACTION_FLAG_OOC_CONSENT - & interaction.interactionFlags) : true)), - // Distance - filter(interaction => - max_distance <= interaction.maxDistance), - // User requirements - filter(interaction => - interaction.required_from_user - ? !!((required_from_user & interaction.required_from_user) - === interaction.required_from_user) : true), - - filter(interaction => { - // User requires exposed - const exposed = !interaction.required_from_user_exposed - || ((interaction.required_from_user_exposed - & required_from_user_exposed) - === interaction.required_from_user_exposed); - // User requires unexposed - const unexposed = !interaction.required_from_user_unexposed - || ((interaction.required_from_user_unexposed - & required_from_user_unexposed) - === interaction.required_from_user_unexposed); - - if (interaction.required_from_user_exposed - && interaction.required_from_user_unexposed) { - return exposed || unexposed; - } - else { - return exposed && unexposed; - } - }), - - // User required feet amount - filter(interaction => interaction.user_num_feet - ? (interaction.user_num_feet <= user_num_feet) : true), - // Target requirements - filter(interaction => interaction.required_from_target - ? !!((required_from_target - & interaction.required_from_target) - === interaction.required_from_target) : true), - filter(interaction => { - // Target requires exposed - const exposed = !interaction.required_from_target_exposed - || ((interaction.required_from_target_exposed - & required_from_target_exposed) - === interaction.required_from_target_exposed); - // Target requires unexposed - const unexposed = !interaction.required_from_target_unexposed - || ((interaction.required_from_target_unexposed - & required_from_target_unexposed) - === interaction.required_from_target_unexposed); - - if (interaction.required_from_target_exposed - && interaction.required_from_target_unexposed) { - return exposed || unexposed; - } - else { - return exposed && unexposed; - } - }), - // Target required feet amount - filter(interaction => interaction.target_num_feet - ? (interaction.target_num_feet <= target_num_feet) : true), - - // Searching by "desc" - sortBy(interaction => interaction.desc), - // Searching by type - sortBy(interaction => interaction.type), - ])(interactions); -}; - -/** - * Genital sorter! - */ -export const sortGenitals = (genitals, searchText = '') => { - const testSearch = createSearch(searchText, - genital => genital.name); - return flow([ - // Optional search term - searchText && filter(testSearch), - // Slightly expensive, but way better than sorting in BYOND - sortBy(genital => genital.name), - ])(genitals); -}; - -// Self explanatory -const ModeToIcon = { - "Always visible": "eye", - "Hidden by clothes": "tshirt", - "Hidden by underwear": "low-vision", - "Always hidden": "eye-slash", -}; - -/* - Greetings you, yes you, adding more stuff to actions, - To not have as much headache as i did, - do not attempt to make a sum of 100% with the buttons - as it will mess up math somewhere and overflow. - - Also this is adjusted only for the current size, - if anyone feels like shrinking, - their window it will overflow anyways. - Single items is fine. -*/ -const GenitalTab = (props, context) => { - const { act, data } = useBackend(context); - const [ - searchText, - setSearchText, - ] = useLocalState(context, 'searchText', ''); - const genitals = sortGenitals(data.genitals, searchText) || []; - return ( -
- {genitals.length ? ( - - {genitals.map(genital => ( -
- - - Visibility
- {genital.possible_choices.map(choice => ( -
-
- ))} -
- ) : ( -
- { - searchText ? ( - "No matching results." - ) : ( - "You don't seem to have any genitals...\ - Or any that you could modify." - ) - } -
- )} -
- ); -}; - -const CharacterPrefsTab = (props, context) => { - const { act, data } = useBackend(context); - const { - erp_pref, - noncon_pref, - vore_pref, - extreme_pref, - extreme_harm, - } = data; - return ( -
- - - -
- ); -}; - -const ContentPreferencesTab = (props, context) => { - const { act, data } = useBackend(context); - const { - verb_consent, - lewd_verb_sounds, - arousable, - genital_examine, - vore_examine, - medihound_sleeper, - eating_noises, - digestion_noises, - trash_forcefeed, - forced_fem, - forced_masc, - hypno, - bimbofication, - breast_enlargement, - penis_enlargement, - butt_enlargement, - never_hypno, - no_aphro, - no_ass_slap, - no_auto_wag, - } = data; - return ( -
- - -
-
- ); -}; diff --git a/tgui/packages/tgui/interfaces/MobInteraction/InfoSection.tsx b/tgui/packages/tgui/interfaces/MobInteraction/InfoSection.tsx new file mode 100644 index 0000000000..d17cbbbf43 --- /dev/null +++ b/tgui/packages/tgui/interfaces/MobInteraction/InfoSection.tsx @@ -0,0 +1,73 @@ +import { useBackend } from '../../backend'; +import { BlockQuote, Icon, ProgressBar, Section, Stack } from '../../components'; + +type HeaderInfo = { + isTargetSelf: boolean; + interactingWith: string; + lust: number; + maxLust: number; + selfAttributes: string[]; + theirAttributes: string[]; + theirLust: number; + theirMaxLust: number; +} + +export const InfoSection = (props, context) => { + const { act, data } = useBackend(context); + const { + isTargetSelf, + interactingWith, + lust, + maxLust, + selfAttributes, + theirAttributes, + theirLust, + theirMaxLust, + } = data; + return ( +
+ + +
+ + +
+ You...
+ {selfAttributes.map(attribute => ( +
+ {attribute}
+
+ ))} +
+
+ {!isTargetSelf ? ( + +
+ They...
+ {theirAttributes.map(attribute => ( +
+ {attribute}
+
+ ))} +
+
+ ) : (null)} +
+
+
+ + + + + + {(!isTargetSelf && (theirLust !== null) ? ( + + + + ) : (null))} + + +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/MobInteraction/MainContent.tsx b/tgui/packages/tgui/interfaces/MobInteraction/MainContent.tsx new file mode 100644 index 0000000000..a3b3fa2346 --- /dev/null +++ b/tgui/packages/tgui/interfaces/MobInteraction/MainContent.tsx @@ -0,0 +1,74 @@ +import { useLocalState } from '../../backend'; +import { Icon, Input, Section, Tabs, Stack } from '../../components'; + +import { InteractionsTab } from './tabs/InteractionsTab'; +import { GenitalTab } from './tabs/GenitalTab'; +import { CharacterPrefsTab } from './tabs/CharacterPrefsTab'; +import { ContentPreferencesTab } from './tabs/ContentPreferencesTab'; + +export const MainContent = (props, context) => { + + const [ + searchText, + setSearchText, + ] = useLocalState(context, 'searchText', ''); + + const [tabIndex, setTabIndex] = useLocalState(context, 'tabIndex', 0); + + return ( +
+ + + + setTabIndex(0)}> + Interactions + + setTabIndex(1)}> + Genital Options + + setTabIndex(2)}> + Character Prefs + + setTabIndex(3)}> + Preferences + + + + + + + + + + setSearchText(value)} + /> + + + + +
+ {(() => { + switch (tabIndex) { + default: + return ; + case 1: + return ; + case 2: + return ; + case 3: + return ; + } + })()} +
+
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/MobInteraction/index.tsx b/tgui/packages/tgui/interfaces/MobInteraction/index.tsx new file mode 100644 index 0000000000..2b4e30b168 --- /dev/null +++ b/tgui/packages/tgui/interfaces/MobInteraction/index.tsx @@ -0,0 +1,25 @@ +import { Stack } from '../../components'; +import { Window } from '../../layouts'; + +import { InfoSection } from './InfoSection'; +import { MainContent } from './MainContent'; + +export const MobInteraction = () => { + return ( + + + + + + + + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/MobInteraction/tabs/CharacterPrefsTab.tsx b/tgui/packages/tgui/interfaces/MobInteraction/tabs/CharacterPrefsTab.tsx new file mode 100644 index 0000000000..1e1119232d --- /dev/null +++ b/tgui/packages/tgui/interfaces/MobInteraction/tabs/CharacterPrefsTab.tsx @@ -0,0 +1,137 @@ +import { useBackend } from '../../../backend'; +import { Button, Flex, LabeledList } from '../../../components'; + +type CharacterPrefsInfo = { + erp_pref: number, + noncon_pref: number, + vore_pref: number, + extreme_pref: number, + extreme_harm: boolean, +} + +export const CharacterPrefsTab = (props, context) => { + const { act, data } = useBackend(context); + const { + erp_pref, + noncon_pref, + vore_pref, + extreme_pref, + extreme_harm, + } = data; + return ( + + + + + + )) + ) : ( +
+ { + user_is_blacklisted || target_is_blacklisted + ? `${user_is_blacklisted ? "Your" : "Their"} mob type is blacklisted from interactions` + : searchText ? "No matching results." + : "No interactions available." + } +
+ ) + } + + ); +}; + +/** + * Interaction sorter! also search box + */ +export const sortInteractions = (interactions, searchText = '', data) => { + const testSearch = createSearch(searchText, + interaction => interaction.desc); + const { + extreme_pref, + isTargetSelf, + target_has_active_player, + target_is_blacklisted, + theyAllowExtreme, + theyAllowLewd, + user_is_blacklisted, + verb_consent, + + + max_distance, + required_from_user, + required_from_user_exposed, + required_from_user_unexposed, + user_num_feet, + + required_from_target, + required_from_target_exposed, + required_from_target_unexposed, + target_num_feet, + } = data; + return flow([ + // Blacklists completely disable any and all interactions + filter(interaction => + !user_is_blacklisted && !target_is_blacklisted), + + // Optional search term, do before the others so we don't even run the tests + searchText && filter(testSearch), + + // Filter off interactions depending on pref + filter(interaction => + // Regular interaction + (interaction.type === INTERACTION_NORMAL ? true + // Lewd interaction + : interaction.type === INTERACTION_LEWD ? verb_consent + // Extreme interaction + : verb_consent && extreme_pref)), + + // Filter off interactions depending on target's pref + filter(interaction => + // If it's ourself, we've just checked it above, ignore + ((isTargetSelf || (target_has_active_player === 0)) ? true + // Regular interaction + : interaction.type === INTERACTION_NORMAL ? true + // Lewd interaction + : interaction.type === INTERACTION_LEWD ? theyAllowLewd + // Extreme interaction + : theyAllowLewd && theyAllowExtreme)), + + // Is self + filter(interaction => + (isTargetSelf ? (INTERACTION_FLAG_USER_IS_TARGET + & interaction.interactionFlags) + : !(INTERACTION_FLAG_USER_IS_TARGET & interaction.interactionFlags))), + // Has a player or none at all + filter(interaction => + (!isTargetSelf && (target_has_active_player === 1) + ? !(INTERACTION_FLAG_OOC_CONSENT + & interaction.interactionFlags) : true)), + // Distance + filter(interaction => + max_distance <= interaction.maxDistance), + // User requirements + filter(interaction => + interaction.required_from_user + ? !!((required_from_user & interaction.required_from_user) + === interaction.required_from_user) : true), + + filter(interaction => { + // User requires exposed + const exposed = !interaction.required_from_user_exposed + || ((interaction.required_from_user_exposed + & required_from_user_exposed) + === interaction.required_from_user_exposed); + // User requires unexposed + const unexposed = !interaction.required_from_user_unexposed + || ((interaction.required_from_user_unexposed + & required_from_user_unexposed) + === interaction.required_from_user_unexposed); + + if (interaction.required_from_user_exposed + && interaction.required_from_user_unexposed) { + return exposed || unexposed; + } + else { + return exposed && unexposed; + } + }), + + // User required feet amount + filter(interaction => interaction.user_num_feet + ? (interaction.user_num_feet <= user_num_feet) : true), + // Target requirements + filter(interaction => interaction.required_from_target + ? !!((required_from_target + & interaction.required_from_target) + === interaction.required_from_target) : true), + filter(interaction => { + // Target requires exposed + const exposed = !interaction.required_from_target_exposed + || ((interaction.required_from_target_exposed + & required_from_target_exposed) + === interaction.required_from_target_exposed); + // Target requires unexposed + const unexposed = !interaction.required_from_target_unexposed + || ((interaction.required_from_target_unexposed + & required_from_target_unexposed) + === interaction.required_from_target_unexposed); + + if (interaction.required_from_target_exposed + && interaction.required_from_target_unexposed) { + return exposed || unexposed; + } + else { + return exposed && unexposed; + } + }), + // Target required feet amount + filter(interaction => interaction.target_num_feet + ? (interaction.target_num_feet <= target_num_feet) : true), + + // Searching by "desc" + sortBy(interaction => interaction.desc), + // Searching by type + sortBy(interaction => interaction.type), + ])(interactions); +}; From 2e9c9e771ce13b90538e4a7254508ff9251bae3a Mon Sep 17 00:00:00 2001 From: SandPoot Date: Thu, 30 May 2024 20:00:13 -0300 Subject: [PATCH 2/2] get everything at once --- .../tgui/interfaces/MobInteraction/MainContent.tsx | 13 ++++++------- .../tgui/interfaces/MobInteraction/tabs/index.tsx | 4 ++++ 2 files changed, 10 insertions(+), 7 deletions(-) create mode 100644 tgui/packages/tgui/interfaces/MobInteraction/tabs/index.tsx diff --git a/tgui/packages/tgui/interfaces/MobInteraction/MainContent.tsx b/tgui/packages/tgui/interfaces/MobInteraction/MainContent.tsx index a3b3fa2346..cd0edacb58 100644 --- a/tgui/packages/tgui/interfaces/MobInteraction/MainContent.tsx +++ b/tgui/packages/tgui/interfaces/MobInteraction/MainContent.tsx @@ -1,20 +1,19 @@ import { useLocalState } from '../../backend'; import { Icon, Input, Section, Tabs, Stack } from '../../components'; -import { InteractionsTab } from './tabs/InteractionsTab'; -import { GenitalTab } from './tabs/GenitalTab'; -import { CharacterPrefsTab } from './tabs/CharacterPrefsTab'; -import { ContentPreferencesTab } from './tabs/ContentPreferencesTab'; +import { + InteractionsTab, + GenitalTab, + CharacterPrefsTab, + ContentPreferencesTab, +} from './tabs'; export const MainContent = (props, context) => { - const [ searchText, setSearchText, ] = useLocalState(context, 'searchText', ''); - const [tabIndex, setTabIndex] = useLocalState(context, 'tabIndex', 0); - return (
diff --git a/tgui/packages/tgui/interfaces/MobInteraction/tabs/index.tsx b/tgui/packages/tgui/interfaces/MobInteraction/tabs/index.tsx new file mode 100644 index 0000000000..22ab6b5816 --- /dev/null +++ b/tgui/packages/tgui/interfaces/MobInteraction/tabs/index.tsx @@ -0,0 +1,4 @@ +export { InteractionsTab } from './InteractionsTab'; +export { GenitalTab } from './GenitalTab'; +export { CharacterPrefsTab } from './CharacterPrefsTab'; +export { ContentPreferencesTab } from './ContentPreferencesTab';