Skip to content

Commit

Permalink
[MIRROR] Adds a little button to quirks that allows for relatively ea…
Browse files Browse the repository at this point in the history
…sy customization [MDB IGNORE] (#600)

* Adds a little button to quirks that allows for relatively easy customization

* Fixing diffs

---------

Co-authored-by: SkyratBot <[email protected]>
Co-authored-by: nikothedude <[email protected]>
Co-authored-by: Giz <[email protected]>
  • Loading branch information
4 people authored Nov 13, 2023
1 parent e2b3aac commit 3893e83
Show file tree
Hide file tree
Showing 20 changed files with 294 additions and 45 deletions.
3 changes: 3 additions & 0 deletions code/__DEFINES/preferences.dm
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,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
Expand Down
72 changes: 72 additions & 0 deletions code/datums/quirks/_quirk_constant_data.dm
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions code/datums/quirks/negative_quirks/food_allergy.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions code/datums/quirks/negative_quirks/nearsighted.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions code/datums/quirks/negative_quirks/prosthetic_limb.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions code/datums/quirks/neutral_quirks/phobia.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions code/datums/quirks/positive_quirks/bilingual.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions code/datums/quirks/positive_quirks/tagger.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
4 changes: 2 additions & 2 deletions code/modules/client/preferences.dm
Original file line number Diff line number Diff line change
Expand Up @@ -409,13 +409,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))
Expand Down
2 changes: 1 addition & 1 deletion code/modules/client/preferences/food_allergy.dm
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/modules/client/preferences/glasses.dm
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/modules/client/preferences/language.dm
Original file line number Diff line number Diff line change
@@ -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

Expand Down
5 changes: 5 additions & 0 deletions code/modules/client/preferences/middleware/quirks.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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,
"veteran_only" = initial(quirk.veteran_only), // SKYRAT EDIT - Veteran quirks
)

Expand Down
2 changes: 1 addition & 1 deletion code/modules/client/preferences/paint_color.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion code/modules/client/preferences/phobia.dm
Original file line number Diff line number Diff line change
@@ -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

Expand Down
2 changes: 1 addition & 1 deletion code/modules/client/preferences/prosthetic.dm
Original file line number Diff line number Diff line change
@@ -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

Expand Down
1 change: 1 addition & 0 deletions tgstation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -1681,6 +1681,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"
Expand Down
90 changes: 55 additions & 35 deletions tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { classes } from 'common/react';
import { sendAct, useBackend, useLocalState } from '../../backend';
import { Autofocus, Box, Button, Flex, LabeledList, Popper, Stack, TrackOutsideClicks, Dropdown } from '../../components'; // SKYRAT EDIT CHANGE
import { createSetPreference, PreferencesMenuData, RandomSetting } from './data';
import { Autofocus, Box, Button, Flex, LabeledList, Popper, Stack, TrackOutsideClicks, Dropdown } from '../../components'; // SKYRAT EDIT CHANGE - Adds Dropdown
import { createSetPreference, PreferencesMenuData, RandomSetting, ServerData } from './data';
import { CharacterPreview } from '../common/CharacterPreview';
import { RandomizationButton } from './RandomizationButton';
import { ServerPreferencesFetcher } from './ServerPreferencesFetcher';
Expand Down Expand Up @@ -358,10 +358,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
Expand All @@ -372,7 +373,8 @@ const PreferenceList = (props: {
padding: '4px',
}}
overflowX="hidden"
overflowY="scroll">
overflowY="auto"
maxHeight={props.maxHeight}>
<LabeledList>
{sortPreferences(Object.entries(props.preferences)).map(
([featureId, value]) => {
Expand Down Expand Up @@ -421,6 +423,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;
Expand Down Expand Up @@ -467,36 +500,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 = {
Expand Down Expand Up @@ -631,14 +639,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>
Expand Down
Loading

0 comments on commit 3893e83

Please sign in to comment.