Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MIRROR] Adds a little button to quirks that allows for relatively easy customization #600

Merged
merged 1 commit into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading