mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 09:42:29 +00:00
Adds a little button to quirks that allows for relatively easy customization (#79251)
## About The Pull Request Title. Adds a little cog button that expands a popper with a preference list of relevant preferences. https://github.com/tgstation/tgstation/assets/59709059/5718ad5d-fadb-489f-9a31-9e7173c6f35a ## Why It's Good For The Game Customizable quirks are cool. Having a proper framework for customizable quirks is even cooler. Good UX is even COOLER. ## Changelog 🆑 code: Quirks are now customizable on the quirks page instead of on the character prefs page /🆑 --------- Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com>
This commit is contained in:
@@ -131,6 +131,9 @@
|
||||
/// such as hair color being affixed to hair.
|
||||
#define PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES "supplemental_features"
|
||||
|
||||
/// These preferences will not be rendered on the preferences page, and are practically invisible unless specifically rendered. Used for quirks, currently.
|
||||
#define PREFERENCE_CATEGORY_MANUALLY_RENDERED "manually_rendered_features"
|
||||
|
||||
// Playtime is tracked in minutes
|
||||
/// The time needed to unlock hardcore random mode in preferences
|
||||
#define PLAYTIME_HARDCORE_RANDOM 120 // 2 hours
|
||||
|
||||
72
code/datums/quirks/_quirk_constant_data.dm
Normal file
72
code/datums/quirks/_quirk_constant_data.dm
Normal file
@@ -0,0 +1,72 @@
|
||||
GLOBAL_LIST_INIT_TYPED(all_quirk_constant_data, /datum/quirk_constant_data, generate_quirk_constant_data())
|
||||
|
||||
/// Constructs [GLOB.all_quirk_constant_data] by iterating through a typecache of pregen data, ignoring abstract types, and instantiating the rest.
|
||||
/proc/generate_quirk_constant_data()
|
||||
RETURN_TYPE(/list/datum/quirk_constant_data)
|
||||
|
||||
var/list/datum/quirk_constant_data/all_constant_data = list()
|
||||
|
||||
for (var/datum/quirk_constant_data/iterated_path as anything in typecacheof(path = /datum/quirk_constant_data, ignore_root_path = TRUE))
|
||||
if (initial(iterated_path.abstract_type) == iterated_path)
|
||||
continue
|
||||
|
||||
if (!isnull(all_constant_data[initial(iterated_path.associated_typepath)]))
|
||||
stack_trace("pre-existing pregen data for [initial(iterated_path.associated_typepath)] when [iterated_path] was being considered: [all_constant_data[initial(iterated_path.associated_typepath)]]. \
|
||||
this is definitely a bug, and is probably because one of the two pregen data have the wrong quirk typepath defined. [iterated_path] will not be instantiated")
|
||||
|
||||
continue
|
||||
|
||||
var/datum/quirk_constant_data/pregen_data = new iterated_path
|
||||
all_constant_data[pregen_data.associated_typepath] = pregen_data
|
||||
|
||||
return all_constant_data
|
||||
|
||||
/// A singleton datum representing constant data and procs used by quirks.
|
||||
/datum/quirk_constant_data
|
||||
/// Abstract in OOP terms. If this is our type, we will not be instantiated.
|
||||
var/abstract_type = /datum/quirk_constant_data
|
||||
|
||||
/// The typepath of the quirk we will be associated with in the global list. This is what we represent.
|
||||
var/datum/quirk/associated_typepath
|
||||
|
||||
/// A lazylist of preference datum typepaths. Any character pref put in here will be rendered in the quirks page under a dropdown.
|
||||
var/list/datum/preference/customization_options
|
||||
|
||||
/datum/quirk_constant_data/New()
|
||||
. = ..()
|
||||
|
||||
ASSERT(abstract_type != type && !isnull(associated_typepath), "associated_typepath null - please set it! occured on: [src.type]")
|
||||
|
||||
/// Returns a list of savefile_keys derived from the preference typepaths in [customization_options]. Used in quirks middleware to supply the preferences to render.
|
||||
/datum/quirk_constant_data/proc/get_customization_data()
|
||||
RETURN_TYPE(/list)
|
||||
|
||||
var/list/customization_data = list()
|
||||
|
||||
for (var/datum/preference/pref_type as anything in customization_options)
|
||||
var/datum/preference/pref_instance = GLOB.preference_entries[pref_type]
|
||||
if (isnull(pref_instance))
|
||||
stack_trace("get_customization_data was called before instantiation of [pref_type]!")
|
||||
continue // just in case its a fluke and its only this one thats not instantiated, we'll check the other pref entries
|
||||
|
||||
customization_data += pref_instance.savefile_key
|
||||
|
||||
return customization_data
|
||||
|
||||
/// Is this quirk customizable? If true, a button will appear within the quirk's description box in the quirks page, and upon clicking it,
|
||||
/// will open a customization menu for the quirk.
|
||||
/datum/quirk_constant_data/proc/is_customizable()
|
||||
return LAZYLEN(customization_options) > 0
|
||||
|
||||
/datum/quirk_constant_data/Destroy(force, ...)
|
||||
var/error_message = "[src], a singleton quirk constant data instance, was destroyed! This should not happen!"
|
||||
if (force)
|
||||
error_message += " NOTE: This Destroy() was called with force == TRUE. This instance will be deleted and replaced with a new one."
|
||||
stack_trace(error_message)
|
||||
|
||||
if (!force)
|
||||
return QDEL_HINT_LETMELIVE
|
||||
|
||||
. = ..()
|
||||
|
||||
GLOB.all_quirk_constant_data[associated_typepath] = new src.type //recover
|
||||
@@ -25,6 +25,10 @@ GLOBAL_LIST_INIT(possible_food_allergies, list(
|
||||
/// Footype flags that will trigger the allergy
|
||||
var/target_foodtypes = NONE
|
||||
|
||||
/datum/quirk_constant_data/food_allergy
|
||||
associated_typepath = /datum/quirk/item_quirk/food_allergic
|
||||
customization_options = list(/datum/preference/choiced/food_allergy)
|
||||
|
||||
/datum/quirk/item_quirk/food_allergic/add(client/client_source)
|
||||
if(target_foodtypes != NONE) // Already set, don't care
|
||||
return
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
quirk_flags = QUIRK_HUMAN_ONLY|QUIRK_CHANGES_APPEARANCE
|
||||
mail_goodies = list(/obj/item/clothing/glasses/regular) // extra pair if orginal one gets broken by somebody mean
|
||||
|
||||
/datum/quirk_constant_data/nearsighted
|
||||
associated_typepath = /datum/quirk/item_quirk/nearsighted
|
||||
customization_options = list(/datum/preference/choiced/glasses)
|
||||
|
||||
/datum/quirk/item_quirk/nearsighted/add_unique(client/client_source)
|
||||
var/glasses_name = client_source?.prefs.read_preference(/datum/preference/choiced/glasses) || "Regular"
|
||||
var/obj/item/clothing/glasses/glasses_type
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
/// the original limb from before the prosthetic was applied
|
||||
var/obj/item/bodypart/old_limb
|
||||
|
||||
/datum/quirk_constant_data/prosthetic_limb
|
||||
associated_typepath = /datum/quirk/prosthetic_limb
|
||||
customization_options = list(/datum/preference/choiced/prosthetic)
|
||||
|
||||
/datum/quirk/prosthetic_limb/add_unique(client/client_source)
|
||||
var/limb_type = GLOB.limb_choice[client_source?.prefs?.read_preference(/datum/preference/choiced/prosthetic)]
|
||||
if(isnull(limb_type)) //Client gone or they chose a random prosthetic
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
medical_record_text = "Patient has an irrational fear of something."
|
||||
mail_goodies = list(/obj/item/clothing/glasses/blindfold, /obj/item/storage/pill_bottle/psicodine)
|
||||
|
||||
/datum/quirk_constant_data/phobia
|
||||
associated_typepath = /datum/quirk/phobia
|
||||
customization_options = list(/datum/preference/choiced/phobia)
|
||||
|
||||
// Phobia will follow you between transfers
|
||||
/datum/quirk/phobia/add(client/client_source)
|
||||
var/phobia = client_source?.prefs.read_preference(/datum/preference/choiced/phobia)
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
medical_record_text = "Patient speaks multiple languages."
|
||||
mail_goodies = list(/obj/item/taperecorder, /obj/item/clothing/head/frenchberet, /obj/item/clothing/mask/fakemoustache/italian)
|
||||
|
||||
/datum/quirk_constant_data/bilingual
|
||||
associated_typepath = /datum/quirk/bilingual
|
||||
customization_options = list(/datum/preference/choiced/language)
|
||||
|
||||
/datum/quirk/bilingual/add_unique(client/client_source)
|
||||
var/wanted_language = client_source?.prefs.read_preference(/datum/preference/choiced/language)
|
||||
var/datum/language/language_type
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
/obj/item/canvas/twentythree_twentythree
|
||||
)
|
||||
|
||||
/datum/quirk_constant_data/tagger
|
||||
associated_typepath = /datum/quirk/item_quirk/tagger
|
||||
customization_options = list(/datum/preference/color/paint_color)
|
||||
|
||||
/datum/quirk/item_quirk/tagger/add_unique(client/client_source)
|
||||
var/obj/item/toy/crayon/spraycan/can = new
|
||||
can.set_painting_tool_color(client_source?.prefs.read_preference(/datum/preference/color/paint_color))
|
||||
|
||||
@@ -314,13 +314,13 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
if (!preference.is_accessible(src))
|
||||
continue
|
||||
|
||||
LAZYINITLIST(preferences[preference.category])
|
||||
|
||||
var/value = read_preference(preference.type)
|
||||
var/data = preference.compile_ui_data(user, value)
|
||||
|
||||
LAZYINITLIST(preferences[preference.category])
|
||||
preferences[preference.category][preference.savefile_key] = data
|
||||
|
||||
|
||||
for (var/datum/preference_middleware/preference_middleware as anything in middleware)
|
||||
var/list/append_character_preferences = preference_middleware.get_character_preferences(user)
|
||||
if (isnull(append_character_preferences))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/preference/choiced/food_allergy
|
||||
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
|
||||
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
|
||||
savefile_key = "food_allergy"
|
||||
savefile_identifier = PREFERENCE_CHARACTER
|
||||
can_randomize = FALSE
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/preference/choiced/glasses
|
||||
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
|
||||
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
|
||||
savefile_key = "glasses"
|
||||
savefile_identifier = PREFERENCE_CHARACTER
|
||||
should_generate_icons = TRUE
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/preference/choiced/language
|
||||
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
|
||||
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
|
||||
savefile_key = "language"
|
||||
savefile_identifier = PREFERENCE_CHARACTER
|
||||
|
||||
|
||||
@@ -33,11 +33,16 @@
|
||||
|
||||
for (var/quirk_name in quirks)
|
||||
var/datum/quirk/quirk = quirks[quirk_name]
|
||||
var/datum/quirk_constant_data/constant_data = GLOB.all_quirk_constant_data[quirk]
|
||||
var/list/datum/preference/customization_options = constant_data?.get_customization_data()
|
||||
|
||||
quirk_info[sanitize_css_class_name(quirk_name)] = list(
|
||||
"description" = initial(quirk.desc),
|
||||
"icon" = initial(quirk.icon),
|
||||
"name" = quirk_name,
|
||||
"value" = initial(quirk.value),
|
||||
"customizable" = constant_data?.is_customizable(),
|
||||
"customization_options" = customization_options,
|
||||
)
|
||||
|
||||
return list(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/datum/preference/color/paint_color
|
||||
savefile_key = "paint_color"
|
||||
savefile_identifier = PREFERENCE_CHARACTER
|
||||
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
|
||||
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
|
||||
|
||||
/datum/preference/color/paint_color/is_accessible(datum/preferences/preferences)
|
||||
if (!..(preferences))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/preference/choiced/phobia
|
||||
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
|
||||
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
|
||||
savefile_key = "phobia"
|
||||
savefile_identifier = PREFERENCE_CHARACTER
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/preference/choiced/prosthetic
|
||||
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
|
||||
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
|
||||
savefile_key = "prosthetic"
|
||||
savefile_identifier = PREFERENCE_CHARACTER
|
||||
|
||||
|
||||
@@ -1583,6 +1583,7 @@
|
||||
#include "code\datums\proximity_monitor\fields\projectile_dampener.dm"
|
||||
#include "code\datums\proximity_monitor\fields\timestop.dm"
|
||||
#include "code\datums\quirks\_quirk.dm"
|
||||
#include "code\datums\quirks\_quirk_constant_data.dm"
|
||||
#include "code\datums\quirks\negative_quirks\allergic.dm"
|
||||
#include "code\datums\quirks\negative_quirks\bad_back.dm"
|
||||
#include "code\datums\quirks\negative_quirks\bad_touch.dm"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { classes } from 'common/react';
|
||||
import { sendAct, useBackend, useLocalState } from '../../backend';
|
||||
import { Autofocus, Box, Button, Flex, LabeledList, Popper, Stack, TrackOutsideClicks } from '../../components';
|
||||
import { createSetPreference, PreferencesMenuData, RandomSetting } from './data';
|
||||
import { createSetPreference, PreferencesMenuData, RandomSetting, ServerData } from './data';
|
||||
import { CharacterPreview } from '../common/CharacterPreview';
|
||||
import { RandomizationButton } from './RandomizationButton';
|
||||
import { ServerPreferencesFetcher } from './ServerPreferencesFetcher';
|
||||
@@ -345,10 +345,11 @@ const sortPreferences = sortBy<[string, unknown]>(([featureId, _]) => {
|
||||
return feature?.name;
|
||||
});
|
||||
|
||||
const PreferenceList = (props: {
|
||||
export const PreferenceList = (props: {
|
||||
act: typeof sendAct;
|
||||
preferences: Record<string, unknown>;
|
||||
randomizations: Record<string, RandomSetting>;
|
||||
maxHeight: string;
|
||||
}) => {
|
||||
return (
|
||||
<Stack.Item
|
||||
@@ -359,7 +360,8 @@ const PreferenceList = (props: {
|
||||
padding: '4px',
|
||||
}}
|
||||
overflowX="hidden"
|
||||
overflowY="scroll">
|
||||
overflowY="auto"
|
||||
maxHeight={props.maxHeight}>
|
||||
<LabeledList>
|
||||
{sortPreferences(Object.entries(props.preferences)).map(
|
||||
([featureId, value]) => {
|
||||
@@ -408,6 +410,37 @@ const PreferenceList = (props: {
|
||||
);
|
||||
};
|
||||
|
||||
export const getRandomization = (
|
||||
preferences: Record<string, unknown>,
|
||||
serverData: ServerData | undefined,
|
||||
randomBodyEnabled: boolean,
|
||||
context
|
||||
): Record<string, RandomSetting> => {
|
||||
if (!serverData) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const { data } = useBackend<PreferencesMenuData>(context);
|
||||
|
||||
return Object.fromEntries(
|
||||
filterMap(Object.keys(preferences), (preferenceKey) => {
|
||||
if (serverData.random.randomizable.indexOf(preferenceKey) === -1) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!randomBodyEnabled) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return [
|
||||
preferenceKey,
|
||||
data.character_preferences.randomization[preferenceKey] ||
|
||||
RandomSetting.Disabled,
|
||||
];
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const MainPage = (
|
||||
props: {
|
||||
openSpecies: () => void;
|
||||
@@ -454,36 +487,11 @@ export const MainPage = (
|
||||
data.character_preferences.non_contextual.random_body !==
|
||||
RandomSetting.Disabled || randomToggleEnabled;
|
||||
|
||||
const getRandomization = (
|
||||
preferences: Record<string, unknown>
|
||||
): Record<string, RandomSetting> => {
|
||||
if (!serverData) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
filterMap(Object.keys(preferences), (preferenceKey) => {
|
||||
if (
|
||||
serverData.random.randomizable.indexOf(preferenceKey) === -1
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!randomBodyEnabled) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return [
|
||||
preferenceKey,
|
||||
data.character_preferences.randomization[preferenceKey] ||
|
||||
RandomSetting.Disabled,
|
||||
];
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const randomizationOfMainFeatures = getRandomization(
|
||||
Object.fromEntries(mainFeatures)
|
||||
Object.fromEntries(mainFeatures),
|
||||
serverData,
|
||||
randomBodyEnabled,
|
||||
context
|
||||
);
|
||||
|
||||
const nonContextualPreferences = {
|
||||
@@ -600,14 +608,26 @@ export const MainPage = (
|
||||
<Stack vertical fill>
|
||||
<PreferenceList
|
||||
act={act}
|
||||
randomizations={getRandomization(contextualPreferences)}
|
||||
randomizations={getRandomization(
|
||||
contextualPreferences,
|
||||
serverData,
|
||||
randomBodyEnabled,
|
||||
context
|
||||
)}
|
||||
preferences={contextualPreferences}
|
||||
maxHeight="auto"
|
||||
/>
|
||||
|
||||
<PreferenceList
|
||||
act={act}
|
||||
randomizations={getRandomization(nonContextualPreferences)}
|
||||
randomizations={getRandomization(
|
||||
nonContextualPreferences,
|
||||
serverData,
|
||||
randomBodyEnabled,
|
||||
context
|
||||
)}
|
||||
preferences={nonContextualPreferences}
|
||||
maxHeight="auto"
|
||||
/>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { StatelessComponent } from 'inferno';
|
||||
import { Box, Icon, Stack, Tooltip } from '../../components';
|
||||
import { PreferencesMenuData, Quirk } from './data';
|
||||
import { Box, Button, Icon, Popper, Stack, Tooltip } from '../../components';
|
||||
import { PreferencesMenuData, Quirk, RandomSetting, ServerData } from './data';
|
||||
import { useBackend, useLocalState } from '../../backend';
|
||||
import { ServerPreferencesFetcher } from './ServerPreferencesFetcher';
|
||||
import { filterMap } from 'common/collections';
|
||||
import { getRandomization, PreferenceList } from './MainPage';
|
||||
import { useRandomToggleState } from './useRandomToggleState';
|
||||
|
||||
const getValueClass = (value: number): string => {
|
||||
if (value > 0) {
|
||||
@@ -14,6 +17,21 @@ const getValueClass = (value: number): string => {
|
||||
}
|
||||
};
|
||||
|
||||
const getCorrespondingPreferences = (
|
||||
customization_options: string[],
|
||||
relevant_preferences: Record<string, string>
|
||||
): Record<string, unknown> => {
|
||||
return Object.fromEntries(
|
||||
filterMap(Object.keys(relevant_preferences), (key) => {
|
||||
if (!customization_options.includes(key)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return [key, relevant_preferences[key]];
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const QuirkList = (props: {
|
||||
quirks: [
|
||||
string,
|
||||
@@ -22,13 +40,33 @@ const QuirkList = (props: {
|
||||
}
|
||||
][];
|
||||
onClick: (quirkName: string, quirk: Quirk) => void;
|
||||
selected: boolean;
|
||||
serverData: ServerData;
|
||||
randomBodyEnabled: boolean;
|
||||
context;
|
||||
}) => {
|
||||
const { act, data } = useBackend<PreferencesMenuData>(props.context);
|
||||
|
||||
return (
|
||||
// Stack is not used here for a variety of IE flex bugs
|
||||
<Box className="PreferencesMenu__Quirks__QuirkList">
|
||||
{props.quirks.map(([quirkKey, quirk]) => {
|
||||
const [customizationExpanded, setCustomizationExpanded] =
|
||||
useLocalState<boolean>(
|
||||
props.context,
|
||||
quirk.name + ' customization',
|
||||
false
|
||||
);
|
||||
|
||||
const className = 'PreferencesMenu__Quirks__QuirkList__quirk';
|
||||
|
||||
const hasExpandableCustomization =
|
||||
quirk.customizable &&
|
||||
props.selected &&
|
||||
customizationExpanded &&
|
||||
quirk.customization_options &&
|
||||
Object.entries(quirk.customization_options).length > 0;
|
||||
|
||||
const child = (
|
||||
<Box
|
||||
className={className}
|
||||
@@ -36,6 +74,9 @@ const QuirkList = (props: {
|
||||
role="button"
|
||||
tabIndex="1"
|
||||
onClick={() => {
|
||||
if (props.selected) {
|
||||
setCustomizationExpanded(false);
|
||||
}
|
||||
props.onClick(quirkKey, quirk);
|
||||
}}>
|
||||
<Stack fill>
|
||||
@@ -95,6 +136,72 @@ const QuirkList = (props: {
|
||||
'padding': '3px',
|
||||
}}>
|
||||
{quirk.description}
|
||||
{!!quirk.customizable && (
|
||||
<Popper
|
||||
options={{
|
||||
placement: 'bottom-end',
|
||||
}}
|
||||
popperContent={
|
||||
<Box>
|
||||
{!!quirk.customization_options &&
|
||||
hasExpandableCustomization && (
|
||||
<Box
|
||||
mt="1px"
|
||||
style={{
|
||||
'box-shadow':
|
||||
'0px 4px 8px 3px rgba(0, 0, 0, 0.7)',
|
||||
}}>
|
||||
<Stack
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
maxWidth="300px"
|
||||
backgroundColor="black"
|
||||
px="5px"
|
||||
py="3px">
|
||||
<Stack.Item>
|
||||
<PreferenceList
|
||||
act={act}
|
||||
preferences={getCorrespondingPreferences(
|
||||
quirk.customization_options,
|
||||
data.character_preferences
|
||||
.manually_rendered_features
|
||||
)}
|
||||
randomizations={getRandomization(
|
||||
getCorrespondingPreferences(
|
||||
quirk.customization_options,
|
||||
data.character_preferences
|
||||
.manually_rendered_features
|
||||
),
|
||||
props.serverData,
|
||||
props.randomBodyEnabled,
|
||||
props.context
|
||||
)}
|
||||
maxHeight="100px"
|
||||
/>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
}>
|
||||
{props.selected && (
|
||||
<Button
|
||||
selected={customizationExpanded}
|
||||
icon="cog"
|
||||
tooltip="Customize"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
setCustomizationExpanded(!customizationExpanded);
|
||||
}}
|
||||
style={{
|
||||
'float': 'right',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Popper>
|
||||
)}
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
@@ -129,6 +236,12 @@ const StatDisplay: StatelessComponent<{}> = (props) => {
|
||||
export const QuirksPage = (props, context) => {
|
||||
const { act, data } = useBackend<PreferencesMenuData>(context);
|
||||
|
||||
// this is mainly just here to copy from MainPage.tsx
|
||||
const [randomToggleEnabled] = useRandomToggleState(context);
|
||||
const randomBodyEnabled =
|
||||
data.character_preferences.non_contextual.random_body !==
|
||||
RandomSetting.Disabled || randomToggleEnabled;
|
||||
|
||||
const [selectedQuirks, setSelectedQuirks] = useLocalState(
|
||||
context,
|
||||
`selectedQuirks_${data.active_slot}`,
|
||||
@@ -137,8 +250,8 @@ export const QuirksPage = (props, context) => {
|
||||
|
||||
return (
|
||||
<ServerPreferencesFetcher
|
||||
render={(data) => {
|
||||
if (!data) {
|
||||
render={(server_data) => {
|
||||
if (!server_data) {
|
||||
return <Box>Loading quirks...</Box>;
|
||||
}
|
||||
|
||||
@@ -146,7 +259,7 @@ export const QuirksPage = (props, context) => {
|
||||
max_positive_quirks: maxPositiveQuirks,
|
||||
quirk_blacklist: quirkBlacklist,
|
||||
quirk_info: quirkInfo,
|
||||
} = data.quirks;
|
||||
} = server_data.quirks;
|
||||
|
||||
const quirks = Object.entries(quirkInfo);
|
||||
quirks.sort(([_, quirkA], [__, quirkB]) => {
|
||||
@@ -238,6 +351,7 @@ export const QuirksPage = (props, context) => {
|
||||
|
||||
<Stack.Item grow width="100%">
|
||||
<QuirkList
|
||||
selected={false}
|
||||
onClick={(quirkName, quirk) => {
|
||||
if (getReasonToNotAdd(quirkName) !== undefined) {
|
||||
return;
|
||||
@@ -260,6 +374,9 @@ export const QuirksPage = (props, context) => {
|
||||
},
|
||||
];
|
||||
})}
|
||||
serverData={server_data}
|
||||
randomBodyEnabled={randomBodyEnabled}
|
||||
context={context}
|
||||
/>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
@@ -287,6 +404,7 @@ export const QuirksPage = (props, context) => {
|
||||
|
||||
<Stack.Item grow width="100%">
|
||||
<QuirkList
|
||||
selected
|
||||
onClick={(quirkName, quirk) => {
|
||||
if (getReasonToNotRemove(quirkName) !== undefined) {
|
||||
return;
|
||||
@@ -313,6 +431,9 @@ export const QuirksPage = (props, context) => {
|
||||
},
|
||||
];
|
||||
})}
|
||||
serverData={server_data}
|
||||
randomBodyEnabled={randomBodyEnabled}
|
||||
context={context}
|
||||
/>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
|
||||
@@ -82,6 +82,8 @@ export type Quirk = {
|
||||
icon: string;
|
||||
name: string;
|
||||
value: number;
|
||||
customizable: boolean;
|
||||
customization_options?: string[];
|
||||
};
|
||||
|
||||
export type QuirkInfo = {
|
||||
@@ -135,6 +137,7 @@ export type PreferencesMenuData = {
|
||||
};
|
||||
secondary_features: Record<string, unknown>;
|
||||
supplemental_features: Record<string, unknown>;
|
||||
manually_rendered_features: Record<string, string>;
|
||||
|
||||
names: Record<string, string>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user