diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm index f3300726029..25d4489e4a8 100644 --- a/code/controllers/subsystem/processing/quirks.dm +++ b/code/controllers/subsystem/processing/quirks.dm @@ -52,6 +52,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks) wait = 1 SECONDS var/list/quirks = list() //Assoc. list of all roundstart quirk datum types; "name" = /path/ + var/list/datum/quirk/quirk_prototypes = list() var/list/quirk_points = list() //Assoc. list of quirk names and their "point cost"; positive numbers are good traits, and negative ones are bad ///An assoc list of quirks that can be obtained as a hardcore character, and their hardcore value. var/list/hardcore_quirks = list() @@ -78,6 +79,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks) if(initial(quirk_type.abstract_parent_type) == type) continue + quirk_prototypes[type] = new type quirks[initial(quirk_type.name)] = quirk_type quirk_points[initial(quirk_type.name)] = initial(quirk_type.value) diff --git a/code/datums/quirks/_quirk.dm b/code/datums/quirks/_quirk.dm index 13e3ac56b8d..8042dde75aa 100644 --- a/code/datums/quirks/_quirk.dm +++ b/code/datums/quirks/_quirk.dm @@ -196,6 +196,12 @@ SIGNAL_HANDLER update_process() +/// If a quirk is able to be selected for the mob's species +/datum/quirk/proc/is_species_appropriate(datum/species/mob_species) + if(mob_trait in GLOB.species_prototypes[mob_species].inherent_traits) + return FALSE + return TRUE + /// Subtype quirk that has some bonus logic to spawn items for the player. /datum/quirk/item_quirk /// Lazylist of strings describing where all the quirk items have been spawned. diff --git a/code/datums/quirks/negative_quirks/blood_deficiency.dm b/code/datums/quirks/negative_quirks/blood_deficiency.dm index a5ac11714bd..b0fa4cfeae4 100644 --- a/code/datums/quirks/negative_quirks/blood_deficiency.dm +++ b/code/datums/quirks/negative_quirks/blood_deficiency.dm @@ -21,6 +21,14 @@ /datum/quirk/blooddeficiency/remove() UnregisterSignal(quirk_holder, list(COMSIG_HUMAN_ON_HANDLE_BLOOD, COMSIG_SPECIES_GAIN)) +/datum/quirk/blooddeficiency/is_species_appropriate(datum/species/mob_species) + var/datum/species_traits = GLOB.species_prototypes[mob_species].inherent_traits + if(TRAIT_NOBLOOD in species_traits) + return FALSE + if(TRAIT_NOBREATH in species_traits) + return FALSE + return ..() + /datum/quirk/blooddeficiency/proc/lose_blood(datum/source, seconds_per_tick, times_fired) SIGNAL_HANDLER diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index a3a847062df..903b26da4cc 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -231,6 +231,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) if (istype(requested_preference, /datum/preference/name)) tainted_character_profiles = TRUE + for(var/datum/preference_middleware/preference_middleware as anything in middleware) + preference_middleware.post_set_preference(ui.user, requested_preference_key, value) return TRUE if ("set_color_preference") var/requested_preference_key = params["preference"] @@ -419,10 +421,24 @@ GLOBAL_LIST_EMPTY(preferences_datums) .++ /datum/preferences/proc/validate_quirks() - if(CONFIG_GET(flag/disable_quirk_points)) - return - if(GetQuirkBalance() < 0) + var/datum/species/species_type = read_preference(/datum/preference/choiced/species) + var/list/quirks_removed + for(var/quirk_name in all_quirks) + var/quirk_path = SSquirks.quirks[quirk_name] + var/datum/quirk/quirk_prototype = SSquirks.quirk_prototypes[quirk_path] + if(!quirk_prototype.is_species_appropriate(species_type)) + all_quirks -= quirk_name + LAZYADD(quirks_removed, quirk_name) + var/list/feedback + if(LAZYLEN(quirks_removed)) + LAZYADD(feedback, "The following quirks are incompatible with your species:") + LAZYADD(feedback, quirks_removed) + if(!CONFIG_GET(flag/disable_quirk_points) && GetQuirkBalance() < 0) + LAZYADD(feedback, "Your quirks have been reset.") all_quirks = list() + if(LAZYLEN(feedback)) + to_chat(parent, boxed_message(span_greentext(feedback.Join("\n")))) + /** * Safely read a given preference datum from a given client. diff --git a/code/modules/client/preferences/middleware/_middleware.dm b/code/modules/client/preferences/middleware/_middleware.dm index 8f47f73642c..01813fd15e1 100644 --- a/code/modules/client/preferences/middleware/_middleware.dm +++ b/code/modules/client/preferences/middleware/_middleware.dm @@ -50,3 +50,7 @@ /// Called when a character is changed. /datum/preference_middleware/proc/on_new_character(mob/user) return + +/// Called after every update_preference +/datum/preference_middleware/proc/post_set_preference(mob/user, preference, value) + return diff --git a/code/modules/client/preferences/middleware/quirks.dm b/code/modules/client/preferences/middleware/quirks.dm index 075b9e10a1e..8c9f35ec376 100644 --- a/code/modules/client/preferences/middleware/quirks.dm +++ b/code/modules/client/preferences/middleware/quirks.dm @@ -6,6 +6,41 @@ "give_quirk" = PROC_REF(give_quirk), "remove_quirk" = PROC_REF(remove_quirk), ) +/datum/preference_middleware/quirks/pre_set_preference(mob/user, preference, value) + if(preference != "species") + return + var/list/incompatible_quirks + var/selected_species_type = GLOB.species_list[value] + for(var/quirk_name in preferences.all_quirks) + var/quirk_path = SSquirks.quirks[quirk_name] + var/datum/quirk/quirk_prototype = SSquirks.quirk_prototypes[quirk_path] + if(!quirk_prototype.is_species_appropriate(selected_species_type)) + LAZYADD(incompatible_quirks, quirk_name) + if(!LAZYLEN(incompatible_quirks)) + return + var/list/message = list("The following quirks are incompatible with your selected species and will be removed: [incompatible_quirks.Join(", ")].") + if(CONFIG_GET(flag/disable_quirk_points)) + message += "Would you like to continue?" + else + message += "If you do not have enough points to cover the removed quirks, your quirks will be reset. Would you like to continue?" + var/response = tgui_alert(user, message.Join(" "), "Quirks Incompatible", list("Yes", "No")) + if(response != "Yes") + return TRUE + + +/datum/preference_middleware/quirks/post_set_preference(mob/user, preference, value) + if(preference != "species") + return + tainted = TRUE + preferences.validate_quirks() + +/datum/preference_middleware/quirks/proc/get_species_compatibility() + var/list/species_blacklist = list() + var/datum/species/mob_species = preferences.read_preference(/datum/preference/choiced/species) + for(var/datum/quirk/quirk_type as anything in SSquirks.quirk_prototypes) + if(!SSquirks.quirk_prototypes[quirk_type].is_species_appropriate(mob_species)) + species_blacklist += quirk_type::name + return species_blacklist /datum/preference_middleware/quirks/get_ui_static_data(mob/user) if (preferences.current_window != PREFERENCE_TAB_CHARACTER_PREFERENCES) @@ -14,6 +49,7 @@ var/list/data = list() data["selected_quirks"] = get_selected_quirks() + data["species_disallowed_quirks"] = get_species_compatibility() return data @@ -23,6 +59,7 @@ if (tainted) tainted = FALSE data["selected_quirks"] = get_selected_quirks() + data["species_disallowed_quirks"] = get_species_compatibility() return data @@ -63,6 +100,7 @@ /datum/preference_middleware/quirks/proc/give_quirk(list/params, mob/user) var/quirk_name = params["quirk"] + preferences.validate_quirks() var/list/new_quirks = preferences.all_quirks | quirk_name if (SSquirks.filter_invalid_quirks(new_quirks) != new_quirks) // If the client is sending an invalid give_quirk, that means that diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm index fb3b97066e0..ce8773811ab 100644 --- a/code/modules/mob/dead/new_player/preferences_setup.dm +++ b/code/modules/mob/dead/new_player/preferences_setup.dm @@ -23,7 +23,7 @@ ///Setup the random hardcore quirks and give the character the new score prize. /datum/preferences/proc/hardcore_random_setup(mob/living/carbon/human/character) - var/next_hardcore_score = select_hardcore_quirks() + var/next_hardcore_score = select_hardcore_quirks(character.dna.species.type) character.hardcore_survival_score = next_hardcore_score ** 1.2 //30 points would be about 60 score log_game("[character] started hardcore random with [english_list(all_quirks)], for a score of [next_hardcore_score].") @@ -35,7 +35,7 @@ * Goes through all quirks that can be used in hardcore mode and select some based on a random budget. * Returns the new value to be gained with this setup, plus the previously earned score. **/ -/datum/preferences/proc/select_hardcore_quirks() +/datum/preferences/proc/select_hardcore_quirks(species) . = 0 var/quirk_budget = rand(8, 35) @@ -45,10 +45,10 @@ var/list/available_hardcore_quirks = SSquirks.hardcore_quirks.Copy() while(quirk_budget > 0) - for(var/i in available_hardcore_quirks) //Remove from available quirks if its too expensive. - var/datum/quirk/available_quirk = i - if(available_hardcore_quirks[available_quirk] > quirk_budget) - available_hardcore_quirks -= available_quirk + for(var/quirk in available_hardcore_quirks) //Remove from available quirks if its too expensive. + var/datum/quirk/quirk_prototype = SSquirks.quirk_prototypes[quirk] + if(available_hardcore_quirks[quirk] > quirk_budget || !quirk_prototype.is_species_appropriate(species)) + available_hardcore_quirks -= quirk if(!available_hardcore_quirks.len) break diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/QuirksPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/QuirksPage.tsx index ae70de0a4ef..9d63de2dac3 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/QuirksPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/QuirksPage.tsx @@ -383,7 +383,9 @@ export function QuirksPage(props) { } } } - + if (data.species_disallowed_quirks.includes(quirk.name)) { + return 'This quirk is incompatible with your selected species.'; + } return; } diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/types.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/types.ts index 0e21297fa86..03f2641665e 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/types.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/types.ts @@ -174,6 +174,7 @@ export type PreferencesMenuData = { keybindings: Record; overflow_role: string; selected_quirks: string[]; + species_disallowed_quirks: string[]; antag_bans?: string[]; antag_days_left?: Record;